s3-build: avoid to globally include printing and spoolss headers.
[vlendec/samba-autobuild/.git] / source3 / printing / printing.c
index ebb91876fc97933a5fc74dbc64e77411270ab14a..a2737105c4e0698ced30fe39745920bada419f47 100644 (file)
@@ -22,6 +22,8 @@
 #include "includes.h"
 #include "printing.h"
 #include "librpc/gen_ndr/messaging.h"
+#include "../librpc/gen_ndr/ndr_spoolss.h"
+#include "nt_printing.h"
 
 extern struct current_user current_user;
 extern userdom_struct current_user_info;
@@ -275,6 +277,89 @@ static TDB_DATA print_key(uint32 jobid, uint32 *tmp)
        return ret;
 }
 
+/****************************************************************************
+ Pack the devicemode to store it in a tdb.
+****************************************************************************/
+static int pack_devicemode(struct spoolss_DeviceMode *devmode, uint8 *buf, int buflen)
+{
+       enum ndr_err_code ndr_err;
+       DATA_BLOB blob;
+       int len = 0;
+
+       if (devmode) {
+               ndr_err = ndr_push_struct_blob(&blob, talloc_tos(),
+                                              devmode,
+                                              (ndr_push_flags_fn_t)
+                                              ndr_push_spoolss_DeviceMode);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       DEBUG(10, ("pack_devicemode: "
+                                  "error encoding spoolss_DeviceMode\n"));
+                       goto done;
+               }
+       } else {
+               ZERO_STRUCT(blob);
+       }
+
+       len = tdb_pack(buf, buflen, "B", blob.length, blob.data);
+
+       if (devmode) {
+               DEBUG(8, ("Packed devicemode [%s]\n", devmode->formname));
+       }
+
+done:
+       return len;
+}
+
+/****************************************************************************
+ Unpack the devicemode to store it in a tdb.
+****************************************************************************/
+static int unpack_devicemode(TALLOC_CTX *mem_ctx,
+                     const uint8 *buf, int buflen,
+                     struct spoolss_DeviceMode **devmode)
+{
+       struct spoolss_DeviceMode *dm;
+       enum ndr_err_code ndr_err;
+       char *data = NULL;
+       int data_len = 0;
+       DATA_BLOB blob;
+       int len = 0;
+
+       *devmode = NULL;
+
+       len = tdb_unpack(buf, buflen, "B", &data_len, &data);
+       if (!data) {
+               return len;
+       }
+
+       dm = talloc_zero(mem_ctx, struct spoolss_DeviceMode);
+       if (!dm) {
+               goto done;
+       }
+
+       blob = data_blob_const(data, data_len);
+
+       ndr_err = ndr_pull_struct_blob(&blob, dm, dm,
+                       (ndr_pull_flags_fn_t)ndr_pull_spoolss_DeviceMode);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(10, ("unpack_devicemode: "
+                          "error parsing spoolss_DeviceMode\n"));
+               goto done;
+       }
+
+       DEBUG(8, ("Unpacked devicemode [%s](%s)\n",
+                 dm->devicename, dm->formname));
+       if (dm->driverextra_data.data) {
+               DEBUG(8, ("with a private section of %d bytes\n",
+                         dm->__driverextra_length));
+       }
+
+       *devmode = dm;
+
+done:
+       SAFE_FREE(data);
+       return len;
+}
+
 /***********************************************************************
  unpack a pjob from a tdb buffer
 ***********************************************************************/
@@ -1814,21 +1899,6 @@ bool print_job_exists(const char* sharename, uint32 jobid)
        return ret;
 }
 
-/****************************************************************************
- Give the fd used for a jobid.
-****************************************************************************/
-
-int print_job_fd(const char* sharename, uint32 jobid)
-{
-       struct printjob *pjob = print_job_find(sharename, jobid);
-       if (!pjob)
-               return -1;
-       /* don't allow another process to get this info - it is meaningless */
-       if (pjob->pid != sys_getpid())
-               return -1;
-       return pjob->fd;
-}
-
 /****************************************************************************
  Give the filename used for a jobid.
  Only valid for the process doing the spooling and when the job
@@ -2040,16 +2110,14 @@ static bool is_owner(struct auth_serversupplied_info *server_info,
  Delete a print job.
 ****************************************************************************/
 
-bool print_job_delete(struct auth_serversupplied_info *server_info, int snum,
-                     uint32 jobid, WERROR *errcode)
+WERROR print_job_delete(struct auth_serversupplied_info *server_info,
+                       int snum, uint32_t jobid)
 {
-       const char* sharename = lp_const_servicename( snum );
+       const char* sharename = lp_const_servicename(snum);
        struct printjob *pjob;
        bool    owner;
        char    *fname;
 
-       *errcode = WERR_OK;
-
        owner = is_owner(server_info, lp_const_servicename(snum), jobid);
 
        /* Check access against security descriptor or whether the user
@@ -2058,7 +2126,6 @@ bool print_job_delete(struct auth_serversupplied_info *server_info, int snum,
        if (!owner &&
            !print_access_check(server_info, snum, JOB_ACCESS_ADMINISTER)) {
                DEBUG(3, ("delete denied by security descriptor\n"));
-               *errcode = WERR_ACCESS_DENIED;
 
                /* BEGIN_ADMIN_LOG */
                sys_adminlog( LOG_ERR,
@@ -2068,7 +2135,7 @@ pause, or resume print job. User name: %s. Printer name: %s.",
                              lp_printername(snum) );
                /* END_ADMIN_LOG */
 
-               return False;
+               return WERR_ACCESS_DENIED;
        }
 
        /*
@@ -2078,19 +2145,18 @@ pause, or resume print job. User name: %s. Printer name: %s.",
         * spool file & return.
         */
 
-       if ( (fname = print_job_fname( sharename, jobid )) != NULL )
-       {
+       fname = print_job_fname(sharename, jobid);
+       if (fname != NULL) {
                /* remove the spool file */
-               DEBUG(10,("print_job_delete: Removing spool file [%s]\n", fname ));
-               if ( unlink( fname ) == -1 ) {
-                       *errcode = map_werror_from_unix(errno);
-                       return False;
+               DEBUG(10, ("print_job_delete: "
+                          "Removing spool file [%s]\n", fname));
+               if (unlink(fname) == -1) {
+                       return map_werror_from_unix(errno);
                }
        }
 
        if (!print_job_delete1(snum, jobid)) {
-               *errcode = WERR_ACCESS_DENIED;
-               return False;
+               return WERR_ACCESS_DENIED;
        }
 
        /* force update the database and say the delete failed if the
@@ -2099,10 +2165,11 @@ pause, or resume print job. User name: %s. Printer name: %s.",
        print_queue_update(snum, True);
 
        pjob = print_job_find(sharename, jobid);
-       if ( pjob && (pjob->status != LPQ_DELETING) )
-               *errcode = WERR_ACCESS_DENIED;
+       if (pjob && (pjob->status != LPQ_DELETING)) {
+               return WERR_ACCESS_DENIED;
+       }
 
-       return (pjob == NULL );
+       return WERR_PRINTER_HAS_JOBS_QUEUED;
 }
 
 /****************************************************************************
@@ -2243,6 +2310,11 @@ ssize_t print_job_write(int snum, uint32 jobid, const char *buf, SMB_OFF_T pos,
        if (pjob->pid != sys_getpid())
                return -1;
 
+       /* if SMBD is spooling this can't be allowed */
+       if (pjob->status == PJOB_SMBD_SPOOLING) {
+               return -1;
+       }
+
        return_code = write_data_at_offset(pjob->fd, buf, size, pos);
 
        if (return_code>0) {
@@ -2483,25 +2555,64 @@ static WERROR print_job_checks(struct auth_serversupplied_info *server_info,
 ***************************************************************************/
 
 static WERROR print_job_spool_file(int snum, uint32_t jobid,
-                                  fstring *filename, int *fd)
+                                  const char *output_file,
+                                  struct printjob *pjob)
 {
        WERROR werr;
+       SMB_STRUCT_STAT st;
+       const char *path;
+       int len;
+
+       /* if this file is within the printer path, it means that smbd
+        * is spooling it and will pass us control when it is finished.
+        * Verify that the file name is ok, within path, and it is
+        * already already there */
+       if (output_file) {
+               path = lp_pathname(snum);
+               len = strlen(path);
+               if (strncmp(output_file, path, len) == 0 &&
+                   (output_file[len - 1] == '/' || output_file[len] == '/')) {
+
+                       /* verify path is not too long */
+                       if (strlen(output_file) >= sizeof(pjob->filename)) {
+                               return WERR_INVALID_NAME;
+                       }
 
-       slprintf(*filename, sizeof(*filename)-1, "%s/%s%.8u.XXXXXX",
-                lp_pathname(snum), PRINT_SPOOL_PREFIX, (unsigned int)jobid);
-       *fd = mkstemp(*filename);
+                       /* verify that the file exists */
+                       if (sys_stat(output_file, &st, false) != 0) {
+                               return WERR_INVALID_NAME;
+                       }
+
+                       fstrcpy(pjob->filename, output_file);
+
+                       DEBUG(3, ("print_job_spool_file:"
+                                 "External spooling activated"));
 
-       if (*fd == -1) {
+                       /* we do not open the file until spooling is done */
+                       pjob->fd = -1;
+                       pjob->status = PJOB_SMBD_SPOOLING;
+
+                       return WERR_OK;
+               }
+       }
+
+       slprintf(pjob->filename, sizeof(pjob->filename)-1,
+                "%s/%s%.8u.XXXXXX", lp_pathname(snum),
+                PRINT_SPOOL_PREFIX, (unsigned int)jobid);
+       pjob->fd = mkstemp(pjob->filename);
+
+       if (pjob->fd == -1) {
                werr = map_werror_from_unix(errno);
                if (W_ERROR_EQUAL(werr, WERR_ACCESS_DENIED)) {
                        /* Common setup error, force a report. */
                        DEBUG(0, ("print_job_spool_file: "
                                  "insufficient permissions to open spool "
-                                 "file %s.\n", *filename));
+                                 "file %s.\n", pjob->filename));
                } else {
                        /* Normal case, report at level 3 and above. */
                        DEBUG(3, ("print_job_spool_file: "
-                                 "can't open spool file %s\n", *filename));
+                                 "can't open spool file %s\n",
+                                 pjob->filename));
                }
                return werr;
        }
@@ -2537,8 +2648,9 @@ WERROR print_job_start(struct auth_serversupplied_info *server_info,
                return werr;
        }
 
-       DEBUG(10,("print_job_start: Queue %s number of jobs (%d), max printjobs = %d\n",
-               sharename, njobs, lp_maxprintjobs(snum) ));
+       DEBUG(10, ("print_job_start: "
+                  "Queue %s number of jobs (%d), max printjobs = %d\n",
+                  sharename, njobs, lp_maxprintjobs(snum)));
 
        werr = allocate_print_jobid(pdb, snum, sharename, &jobid);
        if (!W_ERROR_IS_OK(werr)) {
@@ -2573,7 +2685,7 @@ WERROR print_job_start(struct auth_serversupplied_info *server_info,
        fstrcpy(pjob.queuename, lp_const_servicename(snum));
 
        /* we have a job entry - now create the spool file */
-       werr = print_job_spool_file(snum, jobid, &pjob.filename, &pjob.fd);
+       werr = print_job_spool_file(snum, jobid, filename, &pjob);
        if (!W_ERROR_IS_OK(werr)) {
                goto fail;
        }
@@ -2591,7 +2703,7 @@ WERROR print_job_start(struct auth_serversupplied_info *server_info,
        *_jobid = jobid;
        return WERR_OK;
 
- fail:
+fail:
        if (jobid != -1) {
                pjob_delete(sharename, jobid);
        }
@@ -2629,36 +2741,61 @@ void print_job_endpage(int snum, uint32 jobid)
  error.
 ****************************************************************************/
 
-bool print_job_end(int snum, uint32 jobid, enum file_close_type close_type)
+NTSTATUS print_job_end(int snum, uint32 jobid, enum file_close_type close_type)
 {
        const char* sharename = lp_const_servicename(snum);
        struct printjob *pjob;
        int ret;
        SMB_STRUCT_STAT sbuf;
        struct printif *current_printif = get_printer_fns( snum );
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
 
        pjob = print_job_find(sharename, jobid);
 
-       if (!pjob)
-               return False;
+       if (!pjob) {
+               return NT_STATUS_PRINT_CANCELLED;
+       }
 
-       if (pjob->spooled || pjob->pid != sys_getpid())
-               return False;
+       if (pjob->spooled || pjob->pid != sys_getpid()) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) {
+               if (pjob->status == PJOB_SMBD_SPOOLING) {
+                       /* take over the file now, smbd is done */
+                       if (sys_stat(pjob->filename, &sbuf, false) != 0) {
+                               status = map_nt_error_from_unix(errno);
+                               DEBUG(3, ("print_job_end: "
+                                         "stat file failed for jobid %d\n",
+                                         jobid));
+                               goto fail;
+                       }
+
+                       pjob->status = LPQ_SPOOLING;
+
+               } else {
+
+                       if ((sys_fstat(pjob->fd, &sbuf, false) != 0)) {
+                               status = map_nt_error_from_unix(errno);
+                               close(pjob->fd);
+                               DEBUG(3, ("print_job_end: "
+                                         "stat file failed for jobid %d\n",
+                                         jobid));
+                               goto fail;
+                       }
+
+                       close(pjob->fd);
+               }
 
-       if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) &&
-           (sys_fstat(pjob->fd, &sbuf, false) == 0)) {
                pjob->size = sbuf.st_ex_size;
-               close(pjob->fd);
-               pjob->fd = -1;
        } else {
 
                /*
-                * Not a normal close or we couldn't stat the job file,
-                * so something has gone wrong. Cleanup.
+                * Not a normal close, 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 ));
+               if (pjob->fd != -1) {
+                       close(pjob->fd);
+               }
                goto fail;
        }
 
@@ -2671,13 +2808,15 @@ bool print_job_end(int snum, uint32 jobid, enum file_close_type close_type)
                        pjob->filename, pjob->size ? "deleted" : "zero length" ));
                unlink(pjob->filename);
                pjob_delete(sharename, jobid);
-               return True;
+               return NT_STATUS_OK;
        }
 
        ret = (*(current_printif->job_submit))(snum, pjob);
 
-       if (ret)
+       if (ret) {
+               status = NT_STATUS_PRINT_CANCELLED;
                goto fail;
+       }
 
        /* The print job has been successfully handed over to the back-end */
 
@@ -2689,15 +2828,16 @@ bool print_job_end(int snum, uint32 jobid, enum file_close_type close_type)
        if (print_cache_expired(lp_const_servicename(snum), True))
                print_queue_update(snum, False);
 
-       return True;
+       return NT_STATUS_OK;
 
 fail:
 
        /* The print job was not successfully started. Cleanup */
        /* Still need to add proper error return propagation! 010122:JRR */
+       pjob->fd = -1;
        unlink(pjob->filename);
        pjob_delete(sharename, jobid);
-       return False;
+       return status;
 }
 
 /****************************************************************************