Add bcast_msg_flags to connection struct. Allows sender to filter when
[samba.git] / source / printing / printing.c
index e1f58ba6a5b4e5519c686dd2b1bc934d8717b576..0dacfe820cf55594d406fdb6d0b8278cae4a5675 100644 (file)
@@ -1,9 +1,9 @@
-#define OLD_NTDOMAIN 1
 /* 
    Unix SMB/Netbios implementation.
    Version 3.0
    printing backend routines
    Copyright (C) Andrew Tridgell 1992-2000
+   Copyright (C) Jeremy Allison 2002
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include "includes.h"
-extern int DEBUGLEVEL;
+#include "printing.h"
+
+/* Current printer interface */
+static struct printif *current_printif = &generic_printif;
 
 /* 
    the printing backend revolves around a tdb database that stores the
@@ -37,63 +39,270 @@ extern int DEBUGLEVEL;
    jobids are assigned when a job starts spooling. 
 */
 
-struct printjob {
-       pid_t pid; /* which process launched the job */
-       int sysjob; /* the system (lp) job number */
-       int fd; /* file descriptor of open file if open */
-       time_t starttime; /* when the job started spooling */
-       int status; /* the status of this job */
-       size_t size; /* the size of the job so far */
-       BOOL spooled; /* has it been sent to the spooler yet? */
-       BOOL smbjob; /* set if the job is a SMB job */
-       fstring filename; /* the filename used to spool the file */
-       fstring jobname; /* the job name given to us by the client */
-       fstring user; /* the user who started the job */
-       fstring qname; /* name of the print queue the job was sent to */
-};
+/***************************************************************************
+ Nightmare. LANMAN jobid's are 16 bit numbers..... We must map them to 32
+ bit RPC jobids.... JRA.
+***************************************************************************/
+
+static TDB_CONTEXT *rap_tdb;
+static uint16 next_rap_jobid;
+
+uint16 pjobid_to_rap(int snum, uint32 jobid)
+{
+       uint16 rap_jobid;
+       TDB_DATA data, key;
+       char jinfo[8];
+
+       if (!rap_tdb) {
+               /* Create the in-memory tdb. */
+               rap_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, (O_RDWR|O_CREAT), 0644);
+               if (!rap_tdb)
+                       return 0;
+       }
+
+       SIVAL(&jinfo,0,(int32)snum);
+       SIVAL(&jinfo,4,jobid);
+
+       key.dptr = (char *)&jinfo;
+       key.dsize = sizeof(jinfo);
+       data = tdb_fetch(rap_tdb, key);
+       if (data.dptr && data.dsize == sizeof(uint16)) {
+               memcpy(&rap_jobid, data.dptr, sizeof(uint16));
+               SAFE_FREE(data.dptr);
+               return rap_jobid;
+       }
+       /* Not found - create and store mapping. */
+       rap_jobid = ++next_rap_jobid;
+       if (rap_jobid == 0)
+               rap_jobid = ++next_rap_jobid;
+       data.dptr = (char *)&rap_jobid;
+       data.dsize = sizeof(rap_jobid);
+       tdb_store(rap_tdb, key, data, TDB_REPLACE);
+       tdb_store(rap_tdb, data, key, TDB_REPLACE);
+       return rap_jobid;
+}
+
+BOOL rap_to_pjobid(uint16 rap_jobid, int *psnum, uint32 *pjobid)
+{
+       TDB_DATA data, key;
+       char jinfo[8];
+
+       if (!rap_tdb)
+               return False;
+
+       key.dptr = (char *)&rap_jobid;
+       key.dsize = sizeof(rap_jobid);
+       data = tdb_fetch(rap_tdb, key);
+       if (data.dptr && data.dsize == sizeof(jinfo)) {
+               *psnum = IVAL(&jinfo,0);
+               *pjobid = IVAL(&jinfo,4);
+               SAFE_FREE(data.dptr);
+               return True;
+       }
+       return False;
+}
+
+static void rap_jobid_delete(int snum, uint32 jobid)
+{
+       TDB_DATA key, data;
+       uint16 rap_jobid;
+       char jinfo[8];
+
+       if (!rap_tdb)
+               return;
+
+       SIVAL(&jinfo,0,(int32)snum);
+       SIVAL(&jinfo,4,jobid);
+
+       key.dptr = (char *)&jinfo;
+       key.dsize = sizeof(jinfo);
+       data = tdb_fetch(rap_tdb, key);
+       if (!data.dptr || (data.dsize != sizeof(uint16)))
+               return;
+
+       memcpy(&rap_jobid, data.dptr, sizeof(uint16));
+       SAFE_FREE(data.dptr);
+       data.dptr = (char *)&rap_jobid;
+       data.dsize = sizeof(rap_jobid);
+       tdb_delete(rap_tdb, key);
+       tdb_delete(rap_tdb, data);
+}
 
-/* the open printing.tdb database */
-static TDB_CONTEXT *tdb;
 static pid_t local_pid;
 
-#define PRINT_MAX_JOBID 10000
-#define UNIX_JOB_START PRINT_MAX_JOBID
+static int get_queue_status(int, print_status_struct *);
+
+/* There can be this many printing tdb's open, plus any locked ones. */
+#define MAX_PRINT_DBS_OPEN 1
+
+struct tdb_print_db {
+       struct tdb_print_db *next, *prev;
+       TDB_CONTEXT *tdb;
+       int ref_count;
+       fstring printer_name;
+};
 
-#define PRINT_SPOOL_PREFIX "smbprn."
-#define PRINT_DATABASE_VERSION 2
+static struct tdb_print_db *print_db_head;
 
 /****************************************************************************
-initialise the printing backend. Called once at startup. 
-Does not survive a fork
+  Function to find or create the printer specific job tdb given a printername.
+  Limits the number of tdb's open to MAX_PRINT_DBS_OPEN.
 ****************************************************************************/
+
+static struct tdb_print_db *get_print_db_byname(const char *printername)
+{
+       struct tdb_print_db *p = NULL, *last_entry = NULL;
+       int num_open = 0;
+       pstring printdb_path;
+
+       for (p = print_db_head, last_entry = print_db_head; p; p = p->next) {
+               if (p->tdb && strequal(p->printer_name, printername)) {
+                       DLIST_PROMOTE(print_db_head, p);
+                       p->ref_count++;
+                       return p;
+               }
+               num_open++;
+               last_entry = p;
+       }
+
+       /* Not found. */
+       if (num_open >= MAX_PRINT_DBS_OPEN) {
+               /* Try and recycle the last entry. */
+               DLIST_PROMOTE(print_db_head, last_entry);
+
+               for (p = print_db_head; p; p = p->next) {
+                       if (p->ref_count)
+                               continue;
+                       if (p->tdb) {
+                               if (tdb_close(print_db_head->tdb)) {
+                                       DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n",
+                                                               print_db_head->printer_name ));
+                                       return NULL;
+                               }
+                       }
+                       ZERO_STRUCTP(p);
+                       break;
+               }
+               if (p) {
+                       DLIST_PROMOTE(print_db_head, p);
+                       p = print_db_head;
+               }
+       }
+       
+       if (!p) {
+               /* Create one. */
+               p = (struct tdb_print_db *)malloc(sizeof(struct tdb_print_db));
+               if (!p) {
+                       DEBUG(0,("get_print_db: malloc fail !\n"));
+                       return NULL;
+               }
+               ZERO_STRUCTP(p);
+               DLIST_ADD(print_db_head, p);
+       }
+
+       pstrcpy(printdb_path, lock_path("printing/"));
+       pstrcat(printdb_path, printername);
+       pstrcat(printdb_path, ".tdb");
+
+       become_root();
+       p->tdb = tdb_open_log(printdb_path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       unbecome_root();
+
+       if (!p->tdb) {
+               DEBUG(0,("get_print_db: Failed to open printer backend database %s.\n",
+                                       printdb_path ));
+               DLIST_REMOVE(print_db_head, p);
+               SAFE_FREE(p);
+               return NULL;
+       }
+       fstrcpy(p->printer_name, printername);
+       p->ref_count++;
+       return p;
+}
+
+static void release_print_db( struct tdb_print_db *pdb)
+{
+       pdb->ref_count--;
+       SMB_ASSERT(pdb->ref_count >= 0);
+}
+
+/****************************************************************************
+ Initialise the printing backend. Called once at startup. 
+ Does not survive a fork
+****************************************************************************/
+
 BOOL print_backend_init(void)
 {
        char *sversion = "INFO/version";
+       pstring printing_path;
+       int services = lp_numservices();
+       int snum;
+
+       if (local_pid == sys_getpid())
+               return True;
+
+       unlink(lock_path("printing.tdb"));
+       pstrcpy(printing_path,lock_path("printing"));
+       mkdir(printing_path,0755);
 
-       if (tdb && local_pid == sys_getpid()) return True;
-       tdb = tdb_open(lock_path("printing.tdb"), 0, 0, O_RDWR|O_CREAT, 0600);
-       if (!tdb) {
-               DEBUG(0,("Failed to open printing backend database\n"));
-       }
        local_pid = sys_getpid();
 
        /* handle a Samba upgrade */
-       tdb_lock_bystring(tdb, sversion);
-       if (tdb_fetch_int(tdb, sversion) != PRINT_DATABASE_VERSION) {
-               tdb_traverse(tdb, (tdb_traverse_func)tdb_delete, NULL);
-               tdb_store_int(tdb, sversion, PRINT_DATABASE_VERSION);
+
+       for (snum = 0; snum < services; snum++) {
+               struct tdb_print_db *pdb;
+               if (!lp_print_ok(snum))
+                       continue;
+
+               pdb = get_print_db_byname(lp_const_servicename(snum));
+               if (!pdb)
+                       continue;
+               if (tdb_lock_bystring(pdb->tdb, sversion) == -1) {
+                       DEBUG(0,("print_backend_init: Failed to open printer %s database\n", lp_const_servicename(snum) ));
+                       return False;
+               }
+               if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) {
+                       tdb_traverse(pdb->tdb, tdb_traverse_delete_fn, NULL);
+                       tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION);
+               }
+               tdb_unlock_bystring(pdb->tdb, sversion);
        }
-       tdb_unlock_bystring(tdb, sversion);
 
+       /* select the appropriate printing interface... */
+#ifdef HAVE_CUPS
+       if (strcmp(lp_printcapname(), "cups") == 0)
+               current_printif = &cups_printif;
+#endif /* HAVE_CUPS */
+
+       /* do NT print initialization... */
        return nt_printing_init();
 }
 
 /****************************************************************************
-useful function to generate a tdb key
+ Shut down printing backend. Called once at shutdown to close the tdb.
 ****************************************************************************/
-static TDB_DATA print_key(int jobid)
+
+void printing_end(void)
 {
-       static int j;
+       struct tdb_print_db *p;
+
+       for (p = print_db_head; p; ) {
+               struct tdb_print_db *next_p = p->next;
+               if (p->tdb)
+                       tdb_close(p->tdb);
+               DLIST_REMOVE(print_db_head, p);
+               SAFE_FREE(p);
+               p = next_p;
+       }
+}
+
+/****************************************************************************
+ Useful function to generate a tdb key.
+****************************************************************************/
+
+static TDB_DATA print_key(uint32 jobid)
+{
+       static uint32 j;
        TDB_DATA ret;
 
        j = jobid;
@@ -103,147 +312,317 @@ static TDB_DATA print_key(int jobid)
 }
 
 /****************************************************************************
-useful function to find a print job in the database
+ Useful function to find a print job in the database.
 ****************************************************************************/
-static struct printjob *print_job_find(int jobid)
+
+static struct printjob *print_job_find(int snum, uint32 jobid)
 {
        static struct printjob pjob;
        TDB_DATA ret;
+       struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
 
-       ret = tdb_fetch(tdb, print_key(jobid));
-       if (!ret.dptr || ret.dsize != sizeof(pjob)) return NULL;
+       if (!pdb)
+               return NULL;
+
+       ret = tdb_fetch(pdb->tdb, print_key(jobid));
+       release_print_db(pdb);
+
+       if (!ret.dptr || ret.dsize != sizeof(pjob))
+               return NULL;
 
        memcpy(&pjob, ret.dptr, sizeof(pjob));
-       free(ret.dptr);
+       SAFE_FREE(ret.dptr);
        return &pjob;
 }
 
+/* Convert a unix jobid to a smb jobid */
+
+static uint32 sysjob_to_jobid_value;
+
+static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key,
+                              TDB_DATA data, void *state)
+{
+       struct printjob *pjob = (struct printjob *)data.dptr;
+       int *sysjob = (int *)state;
+
+       if (key.dsize != sizeof(uint32))
+               return 0;
+
+       if (*sysjob == pjob->sysjob) {
+               uint32 *jobid = (uint32 *)key.dptr;
+
+               sysjob_to_jobid_value = *jobid;
+               return 1;
+       }
+
+       return 0;
+}
+
 /****************************************************************************
-store a job structure back to the database
+ This is a *horribly expensive call as we have to iterate through all the
+ current printer tdb's. Don't do this often ! JRA.
 ****************************************************************************/
-static BOOL print_job_store(int jobid, struct printjob *pjob)
+
+uint32 sysjob_to_jobid(int unix_jobid)
 {
-       TDB_DATA d;
-       d.dptr = (void *)pjob;
-       d.dsize = sizeof(*pjob);
-       return (0 == tdb_store(tdb, print_key(jobid), d, TDB_REPLACE));
+       int services = lp_numservices();
+       int snum;
+
+       sysjob_to_jobid_value = (uint32)-1;
+
+       for (snum = 0; snum < services; snum++) {
+               struct tdb_print_db *pdb;
+               if (!lp_print_ok(snum))
+                       continue;
+               pdb = get_print_db_byname(lp_const_servicename(snum));
+               if (pdb)
+                       tdb_traverse(pdb->tdb, unixjob_traverse_fn, &unix_jobid);
+               release_print_db(pdb);
+               if (sysjob_to_jobid_value != (uint32)-1)
+                       return sysjob_to_jobid_value;
+       }
+       return (uint32)-1;
 }
 
 /****************************************************************************
-run a given print command 
-a null terminated list of value/substitute pairs is provided
-for local substitution strings
+ Send notifications based on what has changed after a pjob_store.
 ****************************************************************************/
-static int print_run_command(int snum,char *command, 
-                            char *outfile,
-                            ...)
+
+static struct {
+       uint32 lpq_status;
+       uint32 spoolss_status;
+} lpq_to_spoolss_status_map[] = {
+       { LPQ_QUEUED, JOB_STATUS_QUEUED },
+       { LPQ_PAUSED, JOB_STATUS_PAUSED },
+       { LPQ_SPOOLING, JOB_STATUS_SPOOLING },
+       { LPQ_PRINTING, JOB_STATUS_PRINTING },
+       { LPQ_DELETING, JOB_STATUS_DELETING },
+       { LPQ_OFFLINE, JOB_STATUS_OFFLINE },
+       { LPQ_PAPEROUT, JOB_STATUS_PAPEROUT },
+       { LPQ_PRINTED, JOB_STATUS_PRINTED },
+       { LPQ_DELETED, JOB_STATUS_DELETED },
+       { LPQ_BLOCKED, JOB_STATUS_BLOCKED },
+       { LPQ_USER_INTERVENTION, JOB_STATUS_USER_INTERVENTION },
+       { -1, 0 }
+};
+
+/* Convert a lpq status value stored in printing.tdb into the
+   appropriate win32 API constant. */
+
+static uint32 map_to_spoolss_status(uint32 lpq_status)
 {
-       pstring syscmd;
-       char *p, *arg;
-       int ret;
-       va_list ap;
+       int i = 0;
 
-       if (!command || !*command) return -1;
+       while (lpq_to_spoolss_status_map[i].lpq_status != -1) {
+               if (lpq_to_spoolss_status_map[i].lpq_status == lpq_status)
+                       return lpq_to_spoolss_status_map[i].spoolss_status;
+               i++;
+       }
 
-       if (!VALID_SNUM(snum)) {
-               DEBUG(0,("Invalid snum %d for command %s\n", snum, command));
-               return -1;
+       return 0;
+}
+
+static void pjob_store_notify(int snum, uint32 jobid, struct printjob *old_data,
+                             struct printjob *new_data)
+{
+       BOOL new_job = False;
+
+       if (!old_data)
+               new_job = True;
+
+       /* Notify the job name first */
+
+       if (new_job || !strequal(old_data->jobname, new_data->jobname))
+               notify_job_name(snum, jobid, new_data->jobname);
+
+       /* Job attributes that can't be changed.  We only send
+          notification for these on a new job. */
+
+       if (new_job) {
+               notify_job_submitted(snum, jobid, new_data->starttime);
+               notify_job_username(snum, jobid, new_data->user);
        }
 
-       pstrcpy(syscmd, command);
+       /* Job attributes of a new job or attributes that can be
+          modified. */
+
+       if (new_job || old_data->status != new_data->status)
+               notify_job_status(snum, jobid, map_to_spoolss_status(new_data->status));
+
+       if (new_job || old_data->size != new_data->size)
+               notify_job_total_bytes(snum, jobid, new_data->size);
+
+       if (new_job || old_data->page_count != new_data->page_count)
+               notify_job_total_pages(snum, jobid, new_data->page_count);
+}
+
+/****************************************************************************
+ Store a job structure back to the database.
+****************************************************************************/
+
+static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob, BOOL donotify)
+{
+       TDB_DATA old_data, new_data;
+       BOOL ret;
+       struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+
+       if (!pdb)
+               return False;
+
+       /* Get old data */
+
+       old_data = tdb_fetch(pdb->tdb, print_key(jobid));
+
+       /* Store new data */
+
+       new_data.dptr = (void *)pjob;
+       new_data.dsize = sizeof(*pjob);
+       ret = (tdb_store(pdb->tdb, print_key(jobid), new_data, TDB_REPLACE) == 0);
+
+       release_print_db(pdb);
 
-       va_start(ap, outfile);
-       while ((arg = va_arg(ap, char *))) {
-               char *value = va_arg(ap,char *);
-               pstring_sub(syscmd, arg, value);
+       /* Send notify updates for what has changed */
+
+       if (donotify && ret && (old_data.dsize == 0 || old_data.dsize == sizeof(*pjob))) {
+               pjob_store_notify(
+                       snum, jobid, (struct printjob *)old_data.dptr,
+                       (struct printjob *)new_data.dptr);
+               free(old_data.dptr);
        }
-       va_end(ap);
-  
-       p = PRINTERNAME(snum);
-       if (!p || !*p) p = SERVICE(snum);
-  
-       pstring_sub(syscmd, "%p", p);
-       standard_sub_snum(snum,syscmd);
-  
-       ret = smbrun(syscmd,outfile,False);
-
-       DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+
        return ret;
 }
 
+/****************************************************************************
+ Remove a job structure from the database.
+****************************************************************************/
+
+static void pjob_delete(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       uint32 job_status = 0;
+       struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+
+       if (!pdb)
+               return;
+
+       if (!pjob) {
+               DEBUG(5, ("pjob_delete(): we were asked to delete nonexistent job %u\n",
+                                       (unsigned int)jobid));
+               release_print_db(pdb);
+               return;
+       }
+
+       /* Send a notification that a job has been deleted */
+
+       job_status = map_to_spoolss_status(pjob->status);
+
+       /* We must cycle through JOB_STATUS_DELETING and
+           JOB_STATUS_DELETED for the port monitor to delete the job
+           properly. */
+       
+       job_status |= JOB_STATUS_DELETING;
+       notify_job_status(snum, jobid, job_status);
+       
+       job_status |= JOB_STATUS_DELETED;
+       notify_job_status(snum, jobid, job_status);
+
+       /* Remove from printing.tdb */
+
+       tdb_delete(pdb->tdb, print_key(jobid));
+       release_print_db(pdb);
+       rap_jobid_delete(snum, jobid);
+}
 
 /****************************************************************************
-parse a file name from the system spooler to generate a jobid
+ Parse a file name from the system spooler to generate a jobid.
 ****************************************************************************/
-static int print_parse_jobid(char *fname)
+
+static uint32 print_parse_jobid(char *fname)
 {
        int jobid;
 
-       if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0) return -1;
+       if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0)
+               return (uint32)-1;
        fname += strlen(PRINT_SPOOL_PREFIX);
 
        jobid = atoi(fname);
-       if (jobid <= 0) return -1;
+       if (jobid <= 0)
+               return (uint32)-1;
 
-       return jobid;
+       return (uint32)jobid;
 }
 
-
 /****************************************************************************
-list a unix job in the print database
+ List a unix job in the print database.
 ****************************************************************************/
+
 static void print_unix_job(int snum, print_queue_struct *q)
 {
-       int jobid = q->job + UNIX_JOB_START;
-       struct printjob pj;
+       uint32 jobid = q->job + UNIX_JOB_START;
+       struct printjob pj, *old_pj;
+
+       /* Preserve the timestamp on an existing unix print job */
+
+       old_pj = print_job_find(snum, jobid);
 
        ZERO_STRUCT(pj);
 
        pj.pid = (pid_t)-1;
        pj.sysjob = q->job;
        pj.fd = -1;
-       pj.starttime = q->time;
+       pj.starttime = old_pj ? old_pj->starttime : q->time;
        pj.status = q->status;
        pj.size = q->size;
        pj.spooled = True;
        pj.smbjob = False;
        fstrcpy(pj.filename, "");
-       fstrcpy(pj.jobname, q->file);
-       fstrcpy(pj.user, q->user);
-       fstrcpy(pj.qname, lp_servicename(snum));
+       fstrcpy(pj.jobname, q->fs_file);
+       fstrcpy(pj.user, q->fs_user);
+       fstrcpy(pj.queuename, lp_const_servicename(snum));
 
-       print_job_store(jobid, &pj);
+       pjob_store(snum, jobid, &pj, True);
 }
 
 
 struct traverse_struct {
        print_queue_struct *queue;
-       int qcount, snum, maxcount;
+       int qcount, snum, maxcount, total_jobs;
 };
 
-/* utility fn to delete any jobs that are no longer active */
+/****************************************************************************
+ Utility fn to delete any jobs that are no longer active.
+****************************************************************************/
+
 static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
 {
        struct traverse_struct *ts = (struct traverse_struct *)state;
        struct printjob pjob;
-       int i, jobid;
+       uint32 jobid;
+       int i;
 
-       if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
+       if (data.dsize != sizeof(pjob) || key.dsize != sizeof(jobid))
+               return 0;
        memcpy(&jobid, key.dptr, sizeof(jobid));
        memcpy(&pjob,  data.dptr, sizeof(pjob));
 
-       if (strcmp(lp_servicename(ts->snum), pjob.qname)) {
-               /* this isn't for the queue we are looking at */
+       if (ts->snum != lp_servicenumber(pjob.queuename)) {
+               /* this isn't for the queue we are looking at - this cannot happen with the split tdb's. JRA */
                return 0;
        }
 
        if (!pjob.smbjob) {
-               /* remove a unix job if it isn't in the system queue
-                   any more */
+               /* remove a unix job if it isn't in the system queue any more */
+
                for (i=0;i<ts->qcount;i++) {
-                       if (jobid == ts->queue[i].job + UNIX_JOB_START) break;
+                       uint32 u_jobid = (ts->queue[i].job + UNIX_JOB_START);
+                       if (jobid == u_jobid)
+                               break;
                }
-               if (i == ts->qcount) tdb_delete(tdb, key);
+               if (i == ts->qcount)
+                       pjob_delete(ts->snum, jobid);
+               else
+                       ts->total_jobs++;
                return 0;
        }
 
@@ -252,88 +631,210 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void
                /* if a job is not spooled and the process doesn't
                    exist then kill it. This cleans up after smbd
                    deaths */
-               if (!process_exists(pjob.pid)) {
-                       tdb_delete(tdb, key);
-               }
+               if (!process_exists(pjob.pid))
+                       pjob_delete(ts->snum, jobid);
+               else
+                       ts->total_jobs++;
                return 0;
        }
 
        for (i=0;i<ts->qcount;i++) {
-               int qid = print_parse_jobid(ts->queue[i].file);
-               if (jobid == qid) break;
+               uint32 curr_jobid = print_parse_jobid(ts->queue[i].fs_file);
+               if (jobid == curr_jobid)
+                       break;
        }
        
+       /* The job isn't in the system queue - we have to assume it has
+          completed, so delete the database entry. */
+
        if (i == ts->qcount) {
-               /* the job isn't in the system queue - we have to
-                   assume it has completed, so delete the database
-                   entry */
-               tdb_delete(t, key);
+               time_t cur_t = time(NULL);
+
+               /* A race can occur between the time a job is spooled and
+                  when it appears in the lpq output.  This happens when
+                  the job is added to printing.tdb when another smbd
+                  running print_queue_update() has completed a lpq and
+                  is currently traversing the printing tdb and deleting jobs.
+                  A workaround is to not delete the job if it has been 
+                  submitted less than lp_lpqcachetime() seconds ago. */
+
+               if ((cur_t - pjob.starttime) > lp_lpqcachetime())
+                       pjob_delete(ts->snum, jobid);
+               else
+                       ts->total_jobs++;
        }
+       else
+               ts->total_jobs++;
 
        return 0;
 }
 
 /****************************************************************************
-check if the print queue has been updated recently enough
+ Check if the print queue has been updated recently enough.
 ****************************************************************************/
+
 static void print_cache_flush(int snum)
 {
        fstring key;
-       slprintf(key, sizeof(key), "CACHE/%s", lp_servicename(snum));
-       tdb_store_int(tdb, key, -1);
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+       if (!pdb)
+               return;
+       slprintf(key, sizeof(key)-1, "CACHE/%s", printername);
+       tdb_store_int32(pdb->tdb, key, -1);
+       release_print_db(pdb);
+}
+
+/****************************************************************************
+ Check if someone already thinks they are doing the update.
+****************************************************************************/
+
+static pid_t get_updating_pid(fstring printer_name)
+{
+       fstring keystr;
+       TDB_DATA data, key;
+       pid_t updating_pid;
+       struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+       if (!pdb)
+               return (pid_t)-1;
+       slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
+
+       data = tdb_fetch(pdb->tdb, key);
+       release_print_db(pdb);
+       if (!data.dptr || data.dsize != sizeof(pid_t))
+               return (pid_t)-1;
+
+       memcpy(&updating_pid, data.dptr, sizeof(pid_t));
+       SAFE_FREE(data.dptr);
+
+       if (process_exists(updating_pid))
+               return updating_pid;
+
+       return (pid_t)-1;
+}
+
+/****************************************************************************
+ Set the fact that we're doing the update, or have finished doing the update
+ in the tdb.
+****************************************************************************/
+
+static void set_updating_pid(const fstring printer_name, BOOL delete)
+{
+       fstring keystr;
+       TDB_DATA key;
+       TDB_DATA data;
+       pid_t updating_pid = sys_getpid();
+       struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+       if (!pdb)
+               return;
+
+       slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
+
+       if (delete) {
+               tdb_delete(pdb->tdb, key);
+               release_print_db(pdb);
+               return;
+       }
+       
+       data.dptr = (void *)&updating_pid;
+       data.dsize = sizeof(pid_t);
+
+       tdb_store(pdb->tdb, key, data, TDB_REPLACE);    
+       release_print_db(pdb);
 }
 
 /****************************************************************************
-update the internal database from the system print queue for a queue
+ Update the internal database from the system print queue for a queue.
 ****************************************************************************/
+
 static void print_queue_update(int snum)
 {
-       char *path = lp_pathname(snum);
-       char *cmd = lp_lpqcommand(snum);
-       char **qlines;
-       pstring tmp_file;
-       int numlines, i, qcount;
+       int i, qcount;
        print_queue_struct *queue = NULL;
        print_status_struct status;
+       print_status_struct old_status;
        struct printjob *pjob;
        struct traverse_struct tstruct;
-       fstring keystr;
+       fstring keystr, printer_name, cachestr;
        TDB_DATA data, key;
+       struct tdb_print_db *pdb;
+
+       fstrcpy(printer_name, lp_const_servicename(snum));
+       pdb = get_print_db_byname(printer_name);
+       if (!pdb)
+               return;
+
        /*
-        * Update the cache time FIRST ! Stops others doing this
-        * if the lpq takes a long time.
+        * Check to see if someone else is doing this update.
+        * This is essentially a mutex on the update.
         */
 
-       slprintf(keystr, sizeof(keystr), "CACHE/%s", lp_servicename(snum));
-       tdb_store_int(tdb, keystr, (int)time(NULL));
+       if (get_updating_pid(printer_name) != -1) {
+               release_print_db(pdb);
+               return;
+       }
 
-       slprintf(tmp_file, sizeof(tmp_file), "%s/smblpq.%d", path, local_pid);
+       /* Lock the queue for the database update */
 
-       unlink(tmp_file);
-       print_run_command(snum, cmd, tmp_file,
-                         NULL);
+       slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name);
+       if (tdb_lock_bystring(pdb->tdb, keystr) == -1) {
+               DEBUG(0,("print_queue_update: Failed to lock printer %s database\n", printer_name));
+               release_print_db(pdb);
+               return;
+       }
 
-       numlines = 0;
-       qlines = file_lines_load(tmp_file, &numlines);
-       unlink(tmp_file);
+       /*
+        * Ensure that no one else got in here.
+        * If the updating pid is still -1 then we are
+        * the winner.
+        */
 
-       /* turn the lpq output into a series of job structures */
-       qcount = 0;
-       ZERO_STRUCT(status);
-       if (numlines)
-               queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(numlines+1));
-
-       if (queue) {
-               for (i=0; i<numlines; i++) {
-                       /* parse the line */
-                       if (parse_lpq_entry(snum,qlines[i],
-                                           &queue[qcount],&status,qcount==0)) {
-                               qcount++;
-                       }
-               }               
+       if (get_updating_pid(printer_name) != -1) {
+               /*
+                * Someone else is doing the update, exit.
+                */
+               tdb_unlock_bystring(pdb->tdb, keystr);
+               release_print_db(pdb);
+               return;
        }
-       file_lines_free(qlines);
+
+       /*
+        * We're going to do the update ourselves.
+        */
+
+       /* Tell others we're doing the update. */
+       set_updating_pid(printer_name, False);
+
+       /*
+        * Allow others to enter and notice we're doing
+        * the update.
+        */
+
+       tdb_unlock_bystring(pdb->tdb, keystr);
+
+       /*
+        * Update the cache time FIRST ! Stops others even
+        * attempting to get the lock and doing this
+        * if the lpq takes a long time.
+        */
+
+       slprintf(cachestr, sizeof(cachestr)-1, "CACHE/%s", printer_name);
+       tdb_store_int32(pdb->tdb, cachestr, (int)time(NULL));
+
+        /* get the current queue using the appropriate interface */
+       ZERO_STRUCT(status);
+
+       qcount = (*(current_printif->queue_get))(snum, &queue, &status);
+
+       DEBUG(3, ("%d job%s in queue for %s\n", qcount, (qcount != 1) ?
+               "s" : "", printer_name));
 
        /*
          any job in the internal database that is marked as spooled
@@ -346,16 +847,16 @@ static void print_queue_update(int snum)
          fill in any system job numbers as we go
        */
        for (i=0; i<qcount; i++) {
-               int jobid = print_parse_jobid(queue[i].file);
+               uint32 jobid = print_parse_jobid(queue[i].fs_file);
 
-               if (jobid == -1) {
+               if (jobid == (uint32)-1) {
                        /* assume its a unix print job */
                        print_unix_job(snum, &queue[i]);
                        continue;
                }
 
                /* we have an active SMB print job - update its status */
-               pjob = print_job_find(jobid);
+               pjob = print_job_find(snum, jobid);
                if (!pjob) {
                        /* err, somethings wrong. Probably smbd was restarted
                           with jobs in the queue. All we can do is treat them
@@ -367,7 +868,7 @@ static void print_queue_update(int snum)
                pjob->sysjob = queue[i].job;
                pjob->status = queue[i].status;
 
-               print_job_store(jobid, pjob);
+               pjob_store(snum, jobid, pjob, True);
        }
 
        /* now delete any queued entries that don't appear in the
@@ -375,133 +876,164 @@ static void print_queue_update(int snum)
        tstruct.queue = queue;
        tstruct.qcount = qcount;
        tstruct.snum = snum;
+       tstruct.total_jobs = 0;
+
+       tdb_traverse(pdb->tdb, traverse_fn_delete, (void *)&tstruct);
+
+       SAFE_FREE(tstruct.queue);
 
-       tdb_traverse(tdb, traverse_fn_delete, (void *)&tstruct);
+       tdb_store_int32(pdb->tdb, "INFO/total_jobs", tstruct.total_jobs);
 
-       safe_free(tstruct.queue);
+       if( qcount != get_queue_status(snum, &old_status))
+               DEBUG(10,("print_queue_update: queue status change %d jobs -> %d jobs for printer %s\n",
+                                       old_status.qcount, qcount, printer_name ));
+
+       /* store the new queue status structure */
+       slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printer_name);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
 
-       /* store the queue status structure */
        status.qcount = qcount;
-       slprintf(keystr, sizeof(keystr), "STATUS/%s", lp_servicename(snum));
        data.dptr = (void *)&status;
        data.dsize = sizeof(status);
-       key.dptr = keystr;
-       key.dsize = strlen(keystr);
-       tdb_store(tdb, key, data, TDB_REPLACE); 
+       tdb_store(pdb->tdb, key, data, TDB_REPLACE);    
 
        /*
         * Update the cache time again. We want to do this call
         * as little as possible...
         */
 
-       slprintf(keystr, sizeof(keystr), "CACHE/%s", lp_servicename(snum));
-       tdb_store_int(tdb, keystr, (int)time(NULL));
-}
+       slprintf(keystr, sizeof(keystr)-1, "CACHE/%s", printer_name);
+       tdb_store_int32(pdb->tdb, keystr, (int32)time(NULL));
 
-/****************************************************************************
-check if a jobid is valid. It is valid if it exists in the database
-****************************************************************************/
-BOOL print_job_exists(int jobid)
-{
-       return tdb_exists(tdb, print_key(jobid));
+       /* Delete our pid from the db. */
+       set_updating_pid(printer_name, True);
+       release_print_db(pdb);
 }
 
-
 /****************************************************************************
-work out which service a jobid is for
-note that we have to look up by queue name to ensure that it works for 
-other than the process that started the job
+ Check if a jobid is valid. It is valid if it exists in the database.
 ****************************************************************************/
-int print_job_snum(int jobid)
+
+BOOL print_job_exists(int snum, uint32 jobid)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       if (!pjob) return -1;
+       struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+       BOOL ret;
 
-       return lp_servicenumber(pjob->qname);
+       if (!pdb)
+               return False;
+       ret = tdb_exists(pdb->tdb, print_key(jobid));
+       release_print_db(pdb);
+       return ret;
 }
 
 /****************************************************************************
-give the fd used for a jobid
+ Give the fd used for a jobid.
 ****************************************************************************/
-int print_job_fd(int jobid)
+
+int print_job_fd(int snum, uint32 jobid)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       if (!pjob) return -1;
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob)
+               return -1;
        /* don't allow another process to get this info - it is meaningless */
-       if (pjob->pid != local_pid) return -1;
+       if (pjob->pid != local_pid)
+               return -1;
        return pjob->fd;
 }
 
 /****************************************************************************
-give the filename used for a jobid
-only valid for the process doing the spooling and when the job
-has not been spooled
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
 ****************************************************************************/
-char *print_job_fname(int jobid)
+
+char *print_job_fname(int snum, uint32 jobid)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       if (!pjob || pjob->spooled || pjob->pid != local_pid) return NULL;
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob || pjob->spooled || pjob->pid != local_pid)
+               return NULL;
        return pjob->filename;
 }
 
-
 /****************************************************************************
-set the place in the queue for a job
+ Set the place in the queue for a job.
 ****************************************************************************/
-BOOL print_job_set_place(int jobid, int place)
+
+BOOL print_job_set_place(int snum, uint32 jobid, int place)
 {
        DEBUG(2,("print_job_set_place not implemented yet\n"));
        return False;
 }
 
 /****************************************************************************
-set the name of a job. Only possible for owner
+ Set the name of a job. Only possible for owner.
 ****************************************************************************/
-BOOL print_job_set_name(int jobid, char *name)
+
+BOOL print_job_set_name(int snum, uint32 jobid, char *name)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       if (!pjob || pjob->pid != local_pid) return False;
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob || pjob->pid != local_pid)
+               return False;
 
        fstrcpy(pjob->jobname, name);
-       return print_job_store(jobid, pjob);
+       return pjob_store(snum, jobid, pjob, True);
 }
 
-
 /****************************************************************************
-delete a print job - don't update queue
+ Delete a print job - don't update queue.
 ****************************************************************************/
-static BOOL print_job_delete1(int jobid)
+
+static BOOL print_job_delete1(int snum, uint32 jobid)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       int snum;
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int result = 0;
 
-       if (!pjob) return False;
+       if (!pjob)
+               return False;
 
-       snum = print_job_snum(jobid);
+       /*
+        * If already deleting just return.
+        */
 
-       if (pjob->spooled && pjob->sysjob != -1) {
-               /* need to delete the spooled entry */
-               fstring jobstr;
-               slprintf(jobstr, sizeof(jobstr), "%d", pjob->sysjob);
-               print_run_command(snum, 
-                                 lp_lprmcommand(snum), NULL,
-                                 "%j", jobstr,
-                                 "%T", http_timestring(pjob->starttime),
-                                 NULL);
+       if (pjob->status == LPQ_DELETING)
+               return True;
+
+       /* Hrm - we need to be able to cope with deleting a job before it
+          has reached the spooler. */
+
+       if (pjob->sysjob == -1) {
+               DEBUG(5, ("attempt to delete job %u not seen by lpr\n", (unsigned int)jobid));
        }
 
-       return True;
+       /* Set the tdb entry to be deleting. */
+
+       pjob->status = LPQ_DELETING;
+       pjob_store(snum, jobid, pjob, True);
+
+       if (pjob->spooled && pjob->sysjob != -1)
+               result = (*(current_printif->job_delete))(snum, pjob);
+
+       /* Delete the tdb entry if the delete suceeded or the job hasn't
+          been spooled. */
+
+       if (result == 0)
+               pjob_delete(snum, jobid);
+
+       return (result == 0);
 }
 
 /****************************************************************************
-return true if the current user owns the print job
+ Return true if the current user owns the print job.
 ****************************************************************************/
-static BOOL is_owner(struct current_user *user, int jobid)
+
+static BOOL is_owner(struct current_user *user, int snum, uint32 jobid)
 {
-       struct printjob *pjob = print_job_find(jobid);
+       struct printjob *pjob = print_job_find(snum, jobid);
        user_struct *vuser;
 
-       if (!pjob || !user) return False;
+       if (!pjob || !user)
+               return False;
 
        if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
                return strequal(pjob->user, vuser->user.smb_name);
@@ -511,14 +1043,14 @@ static BOOL is_owner(struct current_user *user, int jobid)
 }
 
 /****************************************************************************
-delete a print job
+ Delete a print job.
 ****************************************************************************/
-BOOL print_job_delete(struct current_user *user, int jobid)
+
+BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
 {
-       int snum = print_job_snum(jobid);
        BOOL owner;
-       
-       owner = is_owner(user, jobid);
+
+       owner = is_owner(user, snum, jobid);
        
        /* Check access against security descriptor or whether the user
           owns their job. */
@@ -526,105 +1058,123 @@ BOOL print_job_delete(struct current_user *user, int jobid)
        if (!owner && 
            !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
                DEBUG(3, ("delete denied by security descriptor\n"));
+               *errcode = WERR_ACCESS_DENIED;
                return False;
        }
 
-       if (!print_job_delete1(jobid)) return False;
+       if (!print_job_delete1(snum, jobid)) 
+               return False;
 
        /* force update the database and say the delete failed if the
            job still exists */
 
        print_queue_update(snum);
 
-       return !print_job_exists(jobid);
+       return !print_job_exists(snum, jobid);
 }
 
-
 /****************************************************************************
-pause a job
+ Pause a job.
 ****************************************************************************/
-BOOL print_job_pause(struct current_user *user, int jobid)
+
+BOOL print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       int snum, ret = -1;
-       fstring jobstr;
-       BOOL owner;
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int ret = -1;
        
+       if (!pjob || !user) 
+               return False;
 
-       if (!pjob || !user) return False;
-
-       if (!pjob->spooled || pjob->sysjob == -1) return False;
-
-       snum = print_job_snum(jobid);
-       owner = is_owner(user, jobid);
+       if (!pjob->spooled || pjob->sysjob == -1) 
+               return False;
 
-       if (!owner &&
+       if (!is_owner(user, snum, jobid) &&
            !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
                DEBUG(3, ("pause denied by security descriptor\n"));
+               *errcode = WERR_ACCESS_DENIED;
                return False;
        }
 
        /* need to pause the spooled entry */
-       slprintf(jobstr, sizeof(jobstr), "%d", pjob->sysjob);
-       ret = print_run_command(snum, 
-                               lp_lppausecommand(snum), NULL,
-                               "%j", jobstr,
-                               NULL);
+       ret = (*(current_printif->job_pause))(snum, pjob);
+
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
 
        /* force update the database */
        print_cache_flush(snum);
 
+       /* Send a printer notify message */
+
+       notify_job_status(snum, jobid, JOB_STATUS_PAUSED);
+
        /* how do we tell if this succeeded? */
-       return ret == 0;
+
+       return True;
 }
 
 /****************************************************************************
-resume a job
+ Resume a job.
 ****************************************************************************/
-BOOL print_job_resume(struct current_user *user, int jobid)
+
+BOOL print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       int snum, ret;
-       fstring jobstr;
-       BOOL owner;
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int ret;
        
-       if (!pjob || !user) return False;
-
-       if (!pjob->spooled || pjob->sysjob == -1) return False;
+       if (!pjob || !user)
+               return False;
 
-       snum = print_job_snum(jobid);
-       owner = is_owner(user, jobid);
+       if (!pjob->spooled || pjob->sysjob == -1)
+               return False;
 
-       if (!is_owner(user, jobid) &&
+       if (!is_owner(user, snum, jobid) &&
            !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
                DEBUG(3, ("resume denied by security descriptor\n"));
+               *errcode = WERR_ACCESS_DENIED;
                return False;
        }
 
-       slprintf(jobstr, sizeof(jobstr), "%d", pjob->sysjob);
-       ret = print_run_command(snum, 
-                               lp_lpresumecommand(snum), NULL,
-                               "%j", jobstr,
-                               NULL);
+       ret = (*(current_printif->job_resume))(snum, pjob);
+
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
 
        /* force update the database */
        print_cache_flush(snum);
 
-       /* how do we tell if this succeeded? */
-       return ret == 0;
+       /* Send a printer notify message */
+
+       notify_job_status(snum, jobid, JOB_STATUS_QUEUED);
+
+       return True;
 }
 
 /****************************************************************************
-write to a print file
+ Write to a print file.
 ****************************************************************************/
-int print_job_write(int jobid, const char *buf, int size)
+
+int print_job_write(int snum, uint32 jobid, const char *buf, int size)
 {
-       int fd;
+       int return_code;
+       struct printjob *pjob = print_job_find(snum, jobid);
 
-       fd = print_job_fd(jobid);
-       if (fd == -1) return -1;
+       if (!pjob)
+               return -1;
+       /* don't allow another process to get this info - it is meaningless */
+       if (pjob->pid != local_pid)
+               return -1;
 
-       return write(fd, buf, size);
+       return_code = write(pjob->fd, buf, size);
+       if (return_code>0) {
+               pjob->size += size;
+               pjob_store(snum, jobid, pjob, True);
+       }
+       return return_code;
 }
 
 /****************************************************************************
@@ -634,64 +1184,153 @@ int print_job_write(int jobid, const char *buf, int size)
 static BOOL print_cache_expired(int snum)
 {
        fstring key;
-       time_t t2, t = time(NULL);
-       slprintf(key, sizeof(key), "CACHE/%s", lp_servicename(snum));
-       t2 = tdb_fetch_int(tdb, key);
-       if (t2 == ((time_t)-1) || (t - t2) >= lp_lpqcachetime()) {
+       time_t last_qscan_time, time_now = time(NULL);
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+       if (!pdb)
+               return False;
+
+       slprintf(key, sizeof(key), "CACHE/%s", printername);
+       last_qscan_time = (time_t)tdb_fetch_int32(pdb->tdb, key);
+
+       /*
+        * Invalidate the queue for 3 reasons.
+        * (1). last queue scan time == -1.
+        * (2). Current time - last queue scan time > allowed cache time.
+        * (3). last queue scan time > current time + MAX_CACHE_VALID_TIME (1 hour by default).
+        * This last test picks up machines for which the clock has been moved
+        * forward, an lpq scan done and then the clock moved back. Otherwise
+        * that last lpq scan would stay around for a loooong loooong time... :-). JRA.
+        */
+
+       if (last_qscan_time == ((time_t)-1) || (time_now - last_qscan_time) >= lp_lpqcachetime() ||
+                       last_qscan_time > (time_now + MAX_CACHE_VALID_TIME)) {
+               DEBUG(3, ("print cache expired for queue %s \
+(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", printername,
+                       (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() ));
+               release_print_db(pdb);
                return True;
        }
+       release_print_db(pdb);
        return False;
 }
 
 /****************************************************************************
Determine the number of jobs in a queue.
Get the queue status - do not update if db is out of date.
 ****************************************************************************/
 
-static int print_queue_length(int snum)
+static int get_queue_status(int snum, print_status_struct *status)
 {
        fstring keystr;
        TDB_DATA data, key;
-       print_status_struct status;
-
-       /* make sure the database is up to date */
-       if (print_cache_expired(snum)) print_queue_update(snum);
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
+       if (!pdb)
+               return 0;
 
-       /* also fetch the queue status */
-       ZERO_STRUCTP(&status);
-       slprintf(keystr, sizeof(keystr), "STATUS/%s", lp_servicename(snum));
+       ZERO_STRUCTP(status);
+       slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
        key.dptr = keystr;
        key.dsize = strlen(keystr);
-       data = tdb_fetch(tdb, key);
+       data = tdb_fetch(pdb->tdb, key);
+       release_print_db(pdb);
        if (data.dptr) {
-               if (data.dsize == sizeof(status)) {
-                       memcpy(&status, data.dptr, sizeof(status));
+               if (data.dsize == sizeof(print_status_struct)) {
+                       memcpy(status, data.dptr, sizeof(print_status_struct));
                }
-               free(data.dptr);
+               SAFE_FREE(data.dptr);
        }
-       return status.qcount;
+       return status->qcount;
+}
+
+/****************************************************************************
+ Determine the number of jobs in a queue.
+****************************************************************************/
+
+int print_queue_length(int snum, print_status_struct *pstatus)
+{
+       print_status_struct status;
+       int len;
+       /* make sure the database is up to date */
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
+       /* also fetch the queue status */
+       memset(&status, 0, sizeof(status));
+       len = get_queue_status(snum, &status);
+       if (pstatus)
+               *pstatus = status;
+       return len;
 }
 
+#if 0 /* JRATEST */
+/****************************************************************************
+ Determine the number of jobs in all queues. This is very expensive. Don't
+ call ! JRA.
+****************************************************************************/
+
+static int get_total_jobs(void)
+{
+       int total_jobs = 0;
+       int snum;
+       int services = lp_numservices();
+
+       for (snum = 0; snum < services; snum++) {
+               struct tdb_print_db *pdb;
+               int jobs;
+
+               if (!lp_print_ok(snum))
+                       continue;
+
+               pdb = get_print_db_byname(lp_const_servicename(snum));
+               if (!pdb)
+                       continue;
+
+               /* make sure the database is up to date */
+               if (print_cache_expired(snum))
+                       print_queue_update(snum);
+
+               jobs = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs");
+               if (jobs > 0)
+                       total_jobs += jobs;
+               release_print_db(pdb);
+       }
+       return total_jobs;
+}
+#endif /* JRATEST */
+
 /***************************************************************************
-start spooling a job - return the jobid
+ Start spooling a job - return the jobid.
 ***************************************************************************/
-int print_job_start(struct current_user *user, int snum, char *jobname)
+
+uint32 print_job_start(struct current_user *user, int snum, char *jobname)
 {
-       int jobid;
+       uint32 jobid;
        char *path;
        struct printjob pjob;
        int next_jobid;
        user_struct *vuser;
+       int njobs = 0;
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
 
        errno = 0;
 
+       if (!pdb)
+               return (uint32)-1;
+
        if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) {
                DEBUG(3, ("print_job_start: job start denied by security descriptor\n"));
-               return -1;
+               release_print_db(pdb);
+               return (uint32)-1;
        }
 
        if (!print_time_access_check(snum)) {
                DEBUG(3, ("print_job_start: job start denied by time check\n"));
-               return -1;
+               release_print_db(pdb);
+               return (uint32)-1;
        }
 
        path = lp_pathname(snum);
@@ -701,29 +1340,48 @@ int print_job_start(struct current_user *user, int snum, char *jobname)
                SMB_BIG_UINT dspace, dsize;
                if (sys_fsusage(path, &dspace, &dsize) == 0 &&
                    dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) {
+                       DEBUG(3, ("print_job_start: disk space check failed.\n"));
+                       release_print_db(pdb);
                        errno = ENOSPC;
-                       return -1;
+                       return (uint32)-1;
                }
        }
 
        /* for autoloaded printers, check that the printcap entry still exists */
-       if (lp_autoloaded(snum) && !pcap_printername_ok(lp_servicename(snum), NULL)) {
+       if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum), NULL)) {
+               DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) ));
+               release_print_db(pdb);
                errno = ENOENT;
-               return -1;
+               return (uint32)-1;
        }
 
-       if (print_queue_length(snum) > lp_maxprintjobs(snum)) {
+       /* Insure the maximum queue size is not violated */
+       if (lp_maxprintjobs(snum) && (njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) {
+               DEBUG(3, ("print_job_start: number of jobs (%d) larger than max printjobs per queue (%d).\n",
+                       njobs, lp_maxprintjobs(snum) ));
+               release_print_db(pdb);
                errno = ENOSPC;
-               return -1;
+               return (uint32)-1;
        }
 
+#if 0 /* JRATEST */
+       /* Insure the maximum print jobs in the system is not violated */
+       if (lp_totalprintjobs() && get_total_jobs() > lp_totalprintjobs()) {
+               DEBUG(3, ("print_job_start: number of jobs (%d) larger than max printjobs per system (%d).\n",
+                       njobs, lp_totalprintjobs() ));
+               release_print_db(pdb);
+               errno = ENOSPC;
+               return (uint32)-1;
+       }
+#endif /* JRATEST */
+
        /* create the database entry */
        ZERO_STRUCT(pjob);
        pjob.pid = local_pid;
        pjob.sysjob = -1;
        pjob.fd = -1;
        pjob.starttime = time(NULL);
-       pjob.status = LPQ_QUEUED;
+       pjob.status = LPQ_SPOOLING;
        pjob.size = 0;
        pjob.spooled = False;
        pjob.smbjob = True;
@@ -736,44 +1394,60 @@ int print_job_start(struct current_user *user, int snum, char *jobname)
                fstrcpy(pjob.user, uidtoname(user->uid));
        }
 
-       fstrcpy(pjob.qname, lp_servicename(snum));
+       fstrcpy(pjob.queuename, lp_const_servicename(snum));
 
        /* lock the database */
-       tdb_lock_bystring(tdb, "INFO/nextjob");
+       if (tdb_lock_bystring(pdb->tdb, "INFO/nextjob") == -1) {
+               DEBUG(0,("print_job_start: failed to lock printing database %s\n", printername ));
+               release_print_db(pdb);
+               return (uint32)-1;
+       }
 
- next_jobnum:
-       next_jobid = tdb_fetch_int(tdb, "INFO/nextjob");
-       if (next_jobid == -1) next_jobid = 1;
+       next_jobid = tdb_fetch_int32(pdb->tdb, "INFO/nextjob");
+       if (next_jobid == -1)
+               next_jobid = 1;
 
-       for (jobid = next_jobid+1; jobid != next_jobid; ) {
-               if (!print_job_exists(jobid)) break;
-               jobid = (jobid + 1) % PRINT_MAX_JOBID;
-               if (jobid == 0) jobid = 1;
+       for (jobid = NEXT_JOBID(next_jobid); jobid != next_jobid; jobid = NEXT_JOBID(jobid)) {
+               if (!print_job_exists(snum, jobid))
+                       break;
        }
-       if (jobid == next_jobid || !print_job_store(jobid, &pjob)) {
+       if (jobid == next_jobid || !pjob_store(snum, jobid, &pjob, False)) {
+               DEBUG(3, ("print_job_start: either jobid (%d)==next_jobid(%d) or pjob_store failed.\n",
+                               jobid, next_jobid ));
                jobid = -1;
                goto fail;
        }
 
-       tdb_store_int(tdb, "INFO/nextjob", jobid);
-
-       /* we have a job entry - now create the spool file 
+       if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) {
+               DEBUG(3, ("print_job_start: failed to store INFO/nextjob.\n"));
+               jobid = -1;
+               goto fail;
+       }
 
-          we unlink first to cope with old spool files and also to beat
-          a symlink security hole - it allows us to use O_EXCL 
-          There may be old spool files owned by other users lying around.
-       */
-       slprintf(pjob.filename, sizeof(pjob.filename), "%s/%s%d", 
-                path, PRINT_SPOOL_PREFIX, jobid);
-       if (unlink(pjob.filename) == -1 && errno != ENOENT) {
-               goto next_jobnum;
+       /* We've finished with the INFO/nextjob lock. */
+       tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+
+       /* we have a job entry - now create the spool file */
+       slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.8u.XXXXXX", 
+                path, PRINT_SPOOL_PREFIX, (unsigned int)jobid);
+       pjob.fd = smb_mkstemp(pjob.filename);
+
+       if (pjob.fd == -1) {
+               if (errno == EACCES) {
+                       /* Common setup error, force a report. */
+                       DEBUG(0, ("print_job_start: insufficient permissions \
+to open spool file %s.\n", pjob.filename));
+               } else {
+                       /* Normal case, report at level 3 and above. */
+                       DEBUG(3, ("print_job_start: can't open spool file %s,\n", pjob.filename));
+                       DEBUGADD(3, ("errno = %d (%s).\n", errno, strerror(errno)));
+               }
+               goto fail;
        }
-       pjob.fd = sys_open(pjob.filename,O_WRONLY|O_CREAT|O_EXCL,0600);
-       if (pjob.fd == -1) goto fail;
 
-       print_job_store(jobid, &pjob);
+       pjob_store(snum, jobid, &pjob, False);
 
-       tdb_unlock_bystring(tdb, "INFO/nextjob");
+       release_print_db(pdb);
 
        /*
         * If the printer is marked as postscript output a leading
@@ -783,33 +1457,50 @@ int print_job_start(struct current_user *user, int snum, char *jobname)
         * tim@fsg.com 09/06/94
         */
        if (lp_postscript(snum)) {
-               print_job_write(jobid, "%!\n",3);
+               print_job_write(snum, jobid, "%!\n",3);
        }
 
        return jobid;
 
  fail:
-       if (jobid != -1) {
-               tdb_delete(tdb, print_key(jobid));
-       }
+       if (jobid != -1)
+               pjob_delete(snum, jobid);
 
-       tdb_unlock_bystring(tdb, "INFO/nextjob");
+       tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+       release_print_db(pdb);
+
+       DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) ));
        return -1;
 }
 
+/****************************************************************************
+ Update the number of pages spooled to jobid
+****************************************************************************/
+
+void print_job_endpage(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob)
+               return;
+       /* don't allow another process to get this info - it is meaningless */
+       if (pjob->pid != local_pid)
+               return;
+
+       pjob->page_count++;
+       pjob_store(snum, jobid, pjob, True);
+}
+
 /****************************************************************************
  Print a file - called on closing the file. This spools the job.
+ If normal close is false then we're tearing down the jobs - treat as an
+ error.
 ****************************************************************************/
 
-BOOL print_job_end(int jobid)
+BOOL print_job_end(int snum, uint32 jobid, BOOL normal_close)
 {
-       struct printjob *pjob = print_job_find(jobid);
-       int snum;
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int ret;
        SMB_STRUCT_STAT sbuf;
-       pstring current_directory;
-       pstring print_directory;
-       char *wd, *p, *printer_name;
-       pstring jobname;
 
        if (!pjob)
                return False;
@@ -817,90 +1508,93 @@ BOOL print_job_end(int jobid)
        if (pjob->spooled || pjob->pid != local_pid)
                return False;
 
-       snum = print_job_snum(jobid);
-
-       if (sys_fstat(pjob->fd, &sbuf) == 0)
+       if (normal_close && (sys_fstat(pjob->fd, &sbuf) == 0)) {
                pjob->size = sbuf.st_size;
+               close(pjob->fd);
+               pjob->fd = -1;
+       } else {
 
-       close(pjob->fd);
-       pjob->fd = -1;
+               /* 
+                * Not a normal close or we couldn't stat the job file,
+                * so something has gone wrong. Cleanup.
+                */
+               close(pjob->fd);
+               pjob->fd = -1;
+               DEBUG(3,("print_job_end: failed to stat file for jobid %d\n", jobid ));
+               goto fail;
+       }
 
-       if (pjob->size == 0) {
-               /* don't bother spooling empty files */
+       /* Technically, this is not quite right. If the printer has a separator
+        * page turned on, the NT spooler prints the separator page even if the
+        * print job is 0 bytes. 010215 JRR */
+       if (pjob->size == 0 || pjob->status == LPQ_DELETING) {
+               /* don't bother spooling empty files or something being deleted. */
+               DEBUG(5,("print_job_end: canceling spool of %s (%s)\n",
+                       pjob->filename, pjob->size ? "deleted" : "zero length" ));
                unlink(pjob->filename);
-               tdb_delete(tdb, print_key(jobid));
+               pjob_delete(snum, jobid);
                return True;
        }
 
-       /* we print from the directory path to give the best chance of
-           parsing the lpq output */
-       wd = sys_getwd(current_directory);
-       if (!wd)
-               return False;           
-
-       pstrcpy(print_directory, pjob->filename);
-       p = strrchr(print_directory,'/');
-       if (!p)
-               return False;
-       *p++ = 0;
-
-       if (chdir(print_directory) != 0)
-               return False;
-
-       pstrcpy(jobname, pjob->jobname);
-       pstring_sub(jobname, "'", "_");
+       ret = (*(current_printif->job_submit))(snum, pjob);
 
-       /* send it to the system spooler */
-       print_run_command(snum, 
-                         lp_printcommand(snum), NULL,
-                         "%s", p,
-                         "%J", jobname,
-                         "%f", p,
-                         NULL);
-
-       chdir(wd);
+       if (ret)
+               goto fail;
 
+       /* The print job has been sucessfully handed over to the back-end */
+       
        pjob->spooled = True;
-       print_job_store(jobid, pjob);
-
-       /* force update the database */
-       print_cache_flush(snum);
+       pjob->status = LPQ_QUEUED;
+       pjob_store(snum, jobid, pjob, True);
        
-       /* Send a printer notify message */
-
-       printer_name = PRINTERNAME(snum);
+       /* make sure the database is up to date */
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
+       
+       return True;
 
-       message_send_all(MSG_PRINTER_NOTIFY, printer_name, 
-                        strlen(printer_name) + 1);
+fail:
 
-       return True;
+       /* The print job was not succesfully started. Cleanup */
+       /* Still need to add proper error return propagation! 010122:JRR */
+       unlink(pjob->filename);
+       pjob_delete(snum, jobid);
+       return False;
 }
 
-/* utility fn to enumerate the print queue */
+/****************************************************************************
+ Utility fn to enumerate the print queue.
+****************************************************************************/
+
 static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
 {
        struct traverse_struct *ts = (struct traverse_struct *)state;
        struct printjob pjob;
-       int i, jobid;
+       int i;
+       uint32 jobid;
 
-       if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
+       if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int))
+               return 0;
        memcpy(&jobid, key.dptr, sizeof(jobid));
        memcpy(&pjob,  data.dptr, sizeof(pjob));
 
        /* maybe it isn't for this queue */
-       if (ts->snum != print_queue_snum(pjob.qname)) return 0;
+       if (ts->snum != lp_servicenumber(pjob.queuename))
+               return 0;
 
-       if (ts->qcount >= ts->maxcount) return 0;
+       if (ts->qcount >= ts->maxcount)
+               return 0;
 
        i = ts->qcount;
 
        ts->queue[i].job = jobid;
        ts->queue[i].size = pjob.size;
+       ts->queue[i].page_count = pjob.page_count;
        ts->queue[i].status = pjob.status;
        ts->queue[i].priority = 1;
        ts->queue[i].time = pjob.starttime;
-       fstrcpy(ts->queue[i].user, pjob.user);
-       fstrcpy(ts->queue[i].file, pjob.jobname);
+       fstrcpy(ts->queue[i].fs_user, pjob.user);
+       fstrcpy(ts->queue[i].fs_file, pjob.jobname);
 
        ts->qcount++;
 
@@ -911,44 +1605,56 @@ struct traverse_count_struct {
        int snum, count;
 };
 
-/* utility fn to count the number of entries in the print queue */
+/****************************************************************************
+ Utility fn to count the number of entries in the print queue.
+****************************************************************************/
+
 static int traverse_count_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
 {
        struct traverse_count_struct *ts = (struct traverse_count_struct *)state;
        struct printjob pjob;
-       int jobid;
+       uint32 jobid;
 
-       if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
+       if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int))
+               return 0;
        memcpy(&jobid, key.dptr, sizeof(jobid));
        memcpy(&pjob,  data.dptr, sizeof(pjob));
 
-       /* maybe it isn't for this queue */
-       if (ts->snum != print_queue_snum(pjob.qname)) return 0;
+       /* maybe it isn't for this queue - this cannot happen with the tdb/printer code. JRA */
+       if (ts->snum != lp_servicenumber(pjob.queuename))
+               return 0;
 
        ts->count++;
 
        return 0;
 }
 
-/* Sort print jobs by submittal time */
+/****************************************************************************
+ Sort print jobs by submittal time.
+****************************************************************************/
 
 static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2)
 {
        /* Silly cases */
 
-       if (!j1 && !j2) return 0;
-       if (!j1) return -1;
-       if (!j2) return 1;
+       if (!j1 && !j2)
+               return 0;
+       if (!j1)
+               return -1;
+       if (!j2)
+               return 1;
 
        /* Sort on job start time */
 
-       if (j1->time == j2->time) return 0;
+       if (j1->time == j2->time)
+               return 0;
        return (j1->time > j2->time) ? 1 : -1;
 }
 
 /****************************************************************************
-get a printer queue listing
+ Get a printer queue listing.
 ****************************************************************************/
+
 int print_queue_status(int snum, 
                       print_queue_struct **queue,
                       print_status_struct *status)
@@ -957,21 +1663,54 @@ int print_queue_status(int snum,
        struct traverse_count_struct tsc;
        fstring keystr;
        TDB_DATA data, key;
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
 
-       /* make sure the database is up to date */
-       if (print_cache_expired(snum)) print_queue_update(snum);
+       *queue = NULL;
        
+       if (!pdb)
+               return 0;
+
+       /* make sure the database is up to date */
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
+
        /*
-        * Count the number of entries.
+        * Fetch the queue status.  We must do this first, as there may
+        * be no jobs in the queue.
+        */
+       ZERO_STRUCTP(status);
+       slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
+       data = tdb_fetch(pdb->tdb, key);
+       if (data.dptr) {
+               if (data.dsize == sizeof(*status)) {
+                       memcpy(status, data.dptr, sizeof(*status));
+               }
+               SAFE_FREE(data.dptr);
+       }
+
+       /*
+        * Now, fetch the print queue information.  We first count the number
+        * of entries, and then only retrieve the queue if necessary.
         */
        tsc.count = 0;
        tsc.snum = snum;
-       tdb_traverse(tdb, traverse_count_fn_queue, (void *)&tsc);
+       
+       tdb_traverse(pdb->tdb, traverse_count_fn_queue, (void *)&tsc);
+
+       if (tsc.count == 0) {
+               release_print_db(pdb);
+               return 0;
+       }
 
        /* Allocate the queue size. */
-       if (( tstruct.queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*tsc.count))
-                               == NULL)
+       if ((tstruct.queue = (print_queue_struct *)
+            malloc(sizeof(print_queue_struct)*tsc.count)) == NULL) {
+               release_print_db(pdb);
                return 0;
+       }
 
        /*
         * Fill in the queue.
@@ -982,20 +1721,8 @@ int print_queue_status(int snum,
        tstruct.maxcount = tsc.count;
        tstruct.snum = snum;
 
-       tdb_traverse(tdb, traverse_fn_queue, (void *)&tstruct);
-
-       /* also fetch the queue status */
-       ZERO_STRUCTP(status);
-       slprintf(keystr, sizeof(keystr), "STATUS/%s", lp_servicename(snum));
-       key.dptr = keystr;
-       key.dsize = strlen(keystr);
-       data = tdb_fetch(tdb, key);
-       if (data.dptr) {
-               if (data.dsize == sizeof(*status)) {
-                       memcpy(status, data.dptr, sizeof(*status));
-               }
-               free(data.dptr);
-       }
+       tdb_traverse(pdb->tdb, traverse_fn_queue, (void *)&tstruct);
+       release_print_db(pdb);
 
        /* Sort the queue by submission time otherwise they are displayed
           in hash order. */
@@ -1007,109 +1734,105 @@ int print_queue_status(int snum,
        return tstruct.qcount;
 }
 
-
 /****************************************************************************
-turn a queue name into a snum
+ Turn a queue name into a snum.
 ****************************************************************************/
-int print_queue_snum(char *qname)
+
+int print_queue_snum(const char *qname)
 {
        int snum = lp_servicenumber(qname);
-       if (snum == -1 || !lp_print_ok(snum)) return -1;
+       if (snum == -1 || !lp_print_ok(snum))
+               return -1;
        return snum;
 }
 
-
 /****************************************************************************
- pause a queue
+ Pause a queue.
 ****************************************************************************/
-BOOL print_queue_pause(struct current_user *user, int snum, int *errcode)
+
+BOOL print_queue_pause(struct current_user *user, int snum, WERROR *errcode)
 {
        int ret;
        
        if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
-               *errcode = ERROR_ACCESS_DENIED;
+               *errcode = WERR_ACCESS_DENIED;
                return False;
        }
 
-       ret = print_run_command(snum, lp_queuepausecommand(snum), NULL, 
-                               NULL);
+       ret = (*(current_printif->queue_pause))(snum);
+
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
 
        /* force update the database */
        print_cache_flush(snum);
 
        /* Send a printer notify message */
 
-       if (ret == 0) {
-               char *printer_name;
+       notify_printer_status(snum, PRINTER_STATUS_PAUSED);
 
-               printer_name = PRINTERNAME(snum);
-
-               message_send_all(MSG_PRINTER_NOTIFY, printer_name, 
-                                strlen(printer_name) + 1);
-       }
-
-       return ret == 0;
+       return True;
 }
 
 /****************************************************************************
- resume a queue
+ Resume a queue.
 ****************************************************************************/
-BOOL print_queue_resume(struct current_user *user, int snum, int *errcode)
+
+BOOL print_queue_resume(struct current_user *user, int snum, WERROR *errcode)
 {
        int ret;
 
        if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
-               *errcode = ERROR_ACCESS_DENIED;
+               *errcode = WERR_ACCESS_DENIED;
                return False;
        }
 
-       ret = print_run_command(snum, lp_queueresumecommand(snum), NULL, 
-                               NULL);
-
-       /* force update the database */
-       print_cache_flush(snum);
+       ret = (*(current_printif->queue_resume))(snum);
 
-       /* Send a printer notify message */
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
 
-       if (ret == 0) {
-               char *printer_name;
+       /* make sure the database is up to date */
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
 
-               printer_name = PRINTERNAME(snum);
+       /* Send a printer notify message */
 
-               message_send_all(MSG_PRINTER_NOTIFY, printer_name, 
-                                strlen(printer_name) + 1);
-       }
+       notify_printer_status(snum, PRINTER_STATUS_OK);
 
-       return ret == 0;
+       return True;
 }
 
 /****************************************************************************
- purge a queue - implemented by deleting all jobs that we can delete
+ Purge a queue - implemented by deleting all jobs that we can delete.
 ****************************************************************************/
-BOOL print_queue_purge(struct current_user *user, int snum, int *errcode)
+
+BOOL print_queue_purge(struct current_user *user, int snum, WERROR *errcode)
 {
        print_queue_struct *queue;
        print_status_struct status;
-       char *printer_name;
        int njobs, i;
+       BOOL can_job_admin;
 
+       /* Force and update so the count is accurate (i.e. not a cached count) */
+       print_queue_update(snum);
+       
+       can_job_admin = print_access_check(user, snum, JOB_ACCESS_ADMINISTER);
        njobs = print_queue_status(snum, &queue, &status);
+
        for (i=0;i<njobs;i++) {
-               if (print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
-                       print_job_delete1(queue[i].job);
+               BOOL owner = is_owner(user, snum, queue[i].job);
+
+               if (owner || can_job_admin) {
+                       print_job_delete1(snum, queue[i].job);
                }
        }
 
-       print_cache_flush(snum);
-       safe_free(queue);
-
-       /* Send a printer notify message */
-
-       printer_name = PRINTERNAME(snum);
-
-       message_send_all(MSG_PRINTER_NOTIFY, printer_name, 
-                        strlen(printer_name) + 1);
+       SAFE_FREE(queue);
 
        return True;
 }
-#undef OLD_NTDOMAIN