1 /*
2  * Printing utilities for CUPS.
3  *
4  * Copyright © 2007-2018 by Apple Inc.
5  * Copyright © 1997-2006 by Easy Software Products.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include "cups-private.h"
16 #include "debug-internal.h"
17 #include <fcntl.h>
18 #include <sys/stat.h>
19 #if defined(_WIN32) || defined(__EMX__)
20 #  include <io.h>
21 #else
22 #  include <unistd.h>
23 #endif /* _WIN32 || __EMX__ */
24 
25 
26 /*
27  * 'cupsCancelJob()' - Cancel a print job on the default server.
28  *
29  * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
30  * to cancel the current job on the named destination.
31  *
32  * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
33  * the cause of any failure.
34  *
35  * @exclude all@
36  */
37 
38 int					/* O - 1 on success, 0 on failure */
cupsCancelJob(const char * name,int job_id)39 cupsCancelJob(const char *name,		/* I - Name of printer or class */
40               int        job_id)	/* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
41 {
42   return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0)
43               < IPP_STATUS_REDIRECTION_OTHER_SITE);
44 }
45 
46 
47 /*
48  * 'cupsCancelJob2()' - Cancel or purge a print job.
49  *
50  * Canceled jobs remain in the job history while purged jobs are removed
51  * from the job history.
52  *
53  * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
54  * to cancel the current job on the named destination.
55  *
56  * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
57  * the cause of any failure.
58  *
59  * @since CUPS 1.4/macOS 10.6@ @exclude all@
60  */
61 
62 ipp_status_t				/* O - IPP status */
cupsCancelJob2(http_t * http,const char * name,int job_id,int purge)63 cupsCancelJob2(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
64                const char *name,	/* I - Name of printer or class */
65                int        job_id,	/* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
66 	       int        purge)	/* I - 1 to purge, 0 to cancel */
67 {
68   char		uri[HTTP_MAX_URI];	/* Job/printer URI */
69   ipp_t		*request;		/* IPP request */
70 
71 
72  /*
73   * Range check input...
74   */
75 
76   if (job_id < -1 || (!name && job_id == 0))
77   {
78     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
79     return (0);
80   }
81 
82  /*
83   * Connect to the default server as needed...
84   */
85 
86   if (!http)
87     if ((http = _cupsConnect()) == NULL)
88       return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
89 
90  /*
91   * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following
92   * attributes:
93   *
94   *    attributes-charset
95   *    attributes-natural-language
96   *    job-uri or printer-uri + job-id
97   *    requesting-user-name
98   *    [purge-job] or [purge-jobs]
99   */
100 
101   request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB);
102 
103   if (name)
104   {
105     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
106                      "localhost", ippPort(), "/printers/%s", name);
107 
108     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
109                  uri);
110     ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
111                   job_id);
112   }
113   else if (job_id > 0)
114   {
115     snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
116 
117     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
118   }
119 
120   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
121                NULL, cupsUser());
122 
123   if (purge && job_id >= 0)
124     ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1);
125   else if (!purge && job_id < 0)
126     ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0);
127 
128  /*
129   * Do the request...
130   */
131 
132   ippDelete(cupsDoRequest(http, request, "/jobs/"));
133 
134   return (cupsLastError());
135 }
136 
137 
138 /*
139  * 'cupsCreateJob()' - Create an empty job for streaming.
140  *
141  * Use this function when you want to stream print data using the
142  * @link cupsStartDocument@, @link cupsWriteRequestData@, and
143  * @link cupsFinishDocument@ functions.  If you have one or more files to
144  * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function
145  * instead.
146  *
147  * @since CUPS 1.4/macOS 10.6@ @exclude all@
148  */
149 
150 int					/* O - Job ID or 0 on error */
cupsCreateJob(http_t * http,const char * name,const char * title,int num_options,cups_option_t * options)151 cupsCreateJob(
152     http_t        *http,		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
153     const char    *name,		/* I - Destination name */
154     const char    *title,		/* I - Title of job */
155     int           num_options,		/* I - Number of options */
156     cups_option_t *options)		/* I - Options */
157 {
158   int		job_id = 0;		/* job-id value */
159   ipp_status_t  status;                 /* Create-Job status */
160   cups_dest_t	*dest;			/* Destination */
161   cups_dinfo_t  *info;                  /* Destination information */
162 
163 
164   DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", num_options=%d, options=%p)", (void *)http, name, title, num_options, (void *)options));
165 
166  /*
167   * Range check input...
168   */
169 
170   if (!name)
171   {
172     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
173     return (0);
174   }
175 
176  /*
177   * Lookup the destination...
178   */
179 
180   if ((dest = cupsGetNamedDest(http, name, NULL)) == NULL)
181   {
182     DEBUG_puts("1cupsCreateJob: Destination not found.");
183     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
184     return (0);
185   }
186 
187  /*
188   * Query dest information and create the job...
189   */
190 
191   DEBUG_puts("1cupsCreateJob: Querying destination info.");
192   if ((info = cupsCopyDestInfo(http, dest)) == NULL)
193   {
194     DEBUG_puts("1cupsCreateJob: Query failed.");
195     cupsFreeDests(1, dest);
196     return (0);
197   }
198 
199   status = cupsCreateDestJob(http, dest, info, &job_id, title, num_options, options);
200   DEBUG_printf(("1cupsCreateJob: cupsCreateDestJob returned %04x (%s)", status, ippErrorString(status)));
201 
202   cupsFreeDestInfo(info);
203   cupsFreeDests(1, dest);
204 
205  /*
206   * Return the job...
207   */
208 
209   if (status >= IPP_STATUS_REDIRECTION_OTHER_SITE)
210     return (0);
211   else
212     return (job_id);
213 }
214 
215 
216 /*
217  * 'cupsFinishDocument()' - Finish sending a document.
218  *
219  * The document must have been started using @link cupsStartDocument@.
220  *
221  * @since CUPS 1.4/macOS 10.6@ @exclude all@
222  */
223 
224 ipp_status_t				/* O - Status of document submission */
cupsFinishDocument(http_t * http,const char * name)225 cupsFinishDocument(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
226                    const char *name)	/* I - Destination name */
227 {
228   char	resource[1024];			/* Printer resource */
229 
230 
231   snprintf(resource, sizeof(resource), "/printers/%s", name);
232 
233   ippDelete(cupsGetResponse(http, resource));
234 
235   return (cupsLastError());
236 }
237 
238 
239 /*
240  * 'cupsFreeJobs()' - Free memory used by job data.
241  */
242 
243 void
cupsFreeJobs(int num_jobs,cups_job_t * jobs)244 cupsFreeJobs(int        num_jobs,	/* I - Number of jobs */
245              cups_job_t *jobs)		/* I - Jobs */
246 {
247   int		i;			/* Looping var */
248   cups_job_t	*job;			/* Current job */
249 
250 
251   if (num_jobs <= 0 || !jobs)
252     return;
253 
254   for (i = num_jobs, job = jobs; i > 0; i --, job ++)
255   {
256     _cupsStrFree(job->dest);
257     _cupsStrFree(job->user);
258     _cupsStrFree(job->format);
259     _cupsStrFree(job->title);
260   }
261 
262   free(jobs);
263 }
264 
265 
266 /*
267  * 'cupsGetClasses()' - Get a list of printer classes from the default server.
268  *
269  * This function is deprecated and no longer returns a list of printer
270  * classes - use @link cupsGetDests@ instead.
271  *
272  * @deprecated@ @exclude all@
273  */
274 
275 int					/* O - Number of classes */
cupsGetClasses(char *** classes)276 cupsGetClasses(char ***classes)		/* O - Classes */
277 {
278   if (classes)
279     *classes = NULL;
280 
281   return (0);
282 }
283 
284 
285 /*
286  * 'cupsGetDefault()' - Get the default printer or class for the default server.
287  *
288  * This function returns the default printer or class as defined by
289  * the LPDEST or PRINTER environment variables. If these environment
290  * variables are not set, the server default destination is returned.
291  * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
292  * functions to get the user-defined default printer, as this function does
293  * not support the lpoptions-defined default printer.
294  *
295  * @exclude all@
296  */
297 
298 const char *				/* O - Default printer or @code NULL@ */
cupsGetDefault(void)299 cupsGetDefault(void)
300 {
301  /*
302   * Return the default printer...
303   */
304 
305   return (cupsGetDefault2(CUPS_HTTP_DEFAULT));
306 }
307 
308 
309 /*
310  * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
311  *
312  * This function returns the default printer or class as defined by
313  * the LPDEST or PRINTER environment variables. If these environment
314  * variables are not set, the server default destination is returned.
315  * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
316  * functions to get the user-defined default printer, as this function does
317  * not support the lpoptions-defined default printer.
318  *
319  * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
320  */
321 
322 const char *				/* O - Default printer or @code NULL@ */
cupsGetDefault2(http_t * http)323 cupsGetDefault2(http_t *http)		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
324 {
325   ipp_t		*request,		/* IPP Request */
326 		*response;		/* IPP Response */
327   ipp_attribute_t *attr;		/* Current attribute */
328   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
329 
330 
331  /*
332   * See if we have a user default printer set...
333   */
334 
335   if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer)))
336     return (cg->def_printer);
337 
338  /*
339   * Connect to the server as needed...
340   */
341 
342   if (!http)
343     if ((http = _cupsConnect()) == NULL)
344       return (NULL);
345 
346  /*
347   * Build a CUPS_GET_DEFAULT request, which requires the following
348   * attributes:
349   *
350   *    attributes-charset
351   *    attributes-natural-language
352   */
353 
354   request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT);
355 
356  /*
357   * Do the request and get back a response...
358   */
359 
360   if ((response = cupsDoRequest(http, request, "/")) != NULL)
361   {
362     if ((attr = ippFindAttribute(response, "printer-name",
363                                  IPP_TAG_NAME)) != NULL)
364     {
365       strlcpy(cg->def_printer, attr->values[0].string.text,
366               sizeof(cg->def_printer));
367       ippDelete(response);
368       return (cg->def_printer);
369     }
370 
371     ippDelete(response);
372   }
373 
374   return (NULL);
375 }
376 
377 
378 /*
379  * 'cupsGetJobs()' - Get the jobs from the default server.
380  *
381  * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
382  * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
383  * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
384  * jobs that are stopped, canceled, aborted, or completed.
385  *
386  * @exclude all@
387  */
388 
389 int					/* O - Number of jobs */
cupsGetJobs(cups_job_t ** jobs,const char * name,int myjobs,int whichjobs)390 cupsGetJobs(cups_job_t **jobs,		/* O - Job data */
391             const char *name,		/* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
392             int        myjobs,		/* I - 0 = all users, 1 = mine */
393 	    int        whichjobs)	/* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
394 {
395  /*
396   * Return the jobs...
397   */
398 
399   return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, name, myjobs, whichjobs));
400 }
401 
402 
403 
404 /*
405  * 'cupsGetJobs2()' - Get the jobs from the specified server.
406  *
407  * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
408  * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
409  * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
410  * jobs that are stopped, canceled, aborted, or completed.
411  *
412  * @since CUPS 1.1.21/macOS 10.4@
413  */
414 
415 int					/* O - Number of jobs */
cupsGetJobs2(http_t * http,cups_job_t ** jobs,const char * name,int myjobs,int whichjobs)416 cupsGetJobs2(http_t     *http,		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
417              cups_job_t **jobs,		/* O - Job data */
418              const char *name,		/* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
419              int        myjobs,		/* I - 0 = all users, 1 = mine */
420 	     int        whichjobs)	/* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
421 {
422   int		n;			/* Number of jobs */
423   ipp_t		*request,		/* IPP Request */
424 		*response;		/* IPP Response */
425   ipp_attribute_t *attr;		/* Current attribute */
426   cups_job_t	*temp;			/* Temporary pointer */
427   int		id,			/* job-id */
428 		priority,		/* job-priority */
429 		size;			/* job-k-octets */
430   ipp_jstate_t	state;			/* job-state */
431   time_t	completed_time,		/* time-at-completed */
432 		creation_time,		/* time-at-creation */
433 		processing_time;	/* time-at-processing */
434   const char	*dest,			/* job-printer-uri */
435 		*format,		/* document-format */
436 		*title,			/* job-name */
437 		*user;			/* job-originating-user-name */
438   char		uri[HTTP_MAX_URI];	/* URI for jobs */
439   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
440   static const char * const attrs[] =	/* Requested attributes */
441 		{
442 		  "document-format",
443 		  "job-id",
444 		  "job-k-octets",
445 		  "job-name",
446 		  "job-originating-user-name",
447 		  "job-printer-uri",
448 		  "job-priority",
449 		  "job-state",
450 		  "time-at-completed",
451 		  "time-at-creation",
452 		  "time-at-processing"
453 		};
454 
455 
456  /*
457   * Range check input...
458   */
459 
460   if (!jobs)
461   {
462     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
463 
464     return (-1);
465   }
466 
467  /*
468   * Get the right URI...
469   */
470 
471   if (name)
472   {
473     if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
474                          "localhost", 0, "/printers/%s",
475                          name) < HTTP_URI_STATUS_OK)
476     {
477       _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
478                     _("Unable to create printer-uri"), 1);
479 
480       return (-1);
481     }
482   }
483   else
484     strlcpy(uri, "ipp://localhost/", sizeof(uri));
485 
486   if (!http)
487     if ((http = _cupsConnect()) == NULL)
488       return (-1);
489 
490  /*
491   * Build an IPP_GET_JOBS request, which requires the following
492   * attributes:
493   *
494   *    attributes-charset
495   *    attributes-natural-language
496   *    printer-uri
497   *    requesting-user-name
498   *    which-jobs
499   *    my-jobs
500   *    requested-attributes
501   */
502 
503   request = ippNewRequest(IPP_OP_GET_JOBS);
504 
505   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
506                "printer-uri", NULL, uri);
507 
508   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
509                "requesting-user-name", NULL, cupsUser());
510 
511   if (myjobs)
512     ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
513 
514   if (whichjobs == CUPS_WHICHJOBS_COMPLETED)
515     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
516                  "which-jobs", NULL, "completed");
517   else if (whichjobs == CUPS_WHICHJOBS_ALL)
518     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
519                  "which-jobs", NULL, "all");
520 
521   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
522                 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
523 		NULL, attrs);
524 
525  /*
526   * Do the request and get back a response...
527   */
528 
529   n     = 0;
530   *jobs = NULL;
531 
532   if ((response = cupsDoRequest(http, request, "/")) != NULL)
533   {
534     for (attr = response->attrs; attr; attr = attr->next)
535     {
536      /*
537       * Skip leading attributes until we hit a job...
538       */
539 
540       while (attr && attr->group_tag != IPP_TAG_JOB)
541         attr = attr->next;
542 
543       if (!attr)
544         break;
545 
546      /*
547       * Pull the needed attributes from this job...
548       */
549 
550       id              = 0;
551       size            = 0;
552       priority        = 50;
553       state           = IPP_JSTATE_PENDING;
554       user            = "unknown";
555       dest            = NULL;
556       format          = "application/octet-stream";
557       title           = "untitled";
558       creation_time   = 0;
559       completed_time  = 0;
560       processing_time = 0;
561 
562       while (attr && attr->group_tag == IPP_TAG_JOB)
563       {
564         if (!strcmp(attr->name, "job-id") &&
565 	    attr->value_tag == IPP_TAG_INTEGER)
566 	  id = attr->values[0].integer;
567         else if (!strcmp(attr->name, "job-state") &&
568 	         attr->value_tag == IPP_TAG_ENUM)
569 	  state = (ipp_jstate_t)attr->values[0].integer;
570         else if (!strcmp(attr->name, "job-priority") &&
571 	         attr->value_tag == IPP_TAG_INTEGER)
572 	  priority = attr->values[0].integer;
573         else if (!strcmp(attr->name, "job-k-octets") &&
574 	         attr->value_tag == IPP_TAG_INTEGER)
575 	  size = attr->values[0].integer;
576         else if (!strcmp(attr->name, "time-at-completed") &&
577 	         attr->value_tag == IPP_TAG_INTEGER)
578 	  completed_time = attr->values[0].integer;
579         else if (!strcmp(attr->name, "time-at-creation") &&
580 	         attr->value_tag == IPP_TAG_INTEGER)
581 	  creation_time = attr->values[0].integer;
582         else if (!strcmp(attr->name, "time-at-processing") &&
583 	         attr->value_tag == IPP_TAG_INTEGER)
584 	  processing_time = attr->values[0].integer;
585         else if (!strcmp(attr->name, "job-printer-uri") &&
586 	         attr->value_tag == IPP_TAG_URI)
587 	{
588 	  if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
589 	    dest ++;
590         }
591         else if (!strcmp(attr->name, "job-originating-user-name") &&
592 	         attr->value_tag == IPP_TAG_NAME)
593 	  user = attr->values[0].string.text;
594         else if (!strcmp(attr->name, "document-format") &&
595 	         attr->value_tag == IPP_TAG_MIMETYPE)
596 	  format = attr->values[0].string.text;
597         else if (!strcmp(attr->name, "job-name") &&
598 	         (attr->value_tag == IPP_TAG_TEXT ||
599 		  attr->value_tag == IPP_TAG_NAME))
600 	  title = attr->values[0].string.text;
601 
602         attr = attr->next;
603       }
604 
605      /*
606       * See if we have everything needed...
607       */
608 
609       if (!dest || !id)
610       {
611         if (!attr)
612 	  break;
613 	else
614           continue;
615       }
616 
617      /*
618       * Allocate memory for the job...
619       */
620 
621       if (n == 0)
622         temp = malloc(sizeof(cups_job_t));
623       else
624 	temp = realloc(*jobs, sizeof(cups_job_t) * (size_t)(n + 1));
625 
626       if (!temp)
627       {
628        /*
629         * Ran out of memory!
630         */
631 
632         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
633 
634 	cupsFreeJobs(n, *jobs);
635 	*jobs = NULL;
636 
637         ippDelete(response);
638 
639 	return (-1);
640       }
641 
642       *jobs = temp;
643       temp  += n;
644       n ++;
645 
646      /*
647       * Copy the data over...
648       */
649 
650       temp->dest            = _cupsStrAlloc(dest);
651       temp->user            = _cupsStrAlloc(user);
652       temp->format          = _cupsStrAlloc(format);
653       temp->title           = _cupsStrAlloc(title);
654       temp->id              = id;
655       temp->priority        = priority;
656       temp->state           = state;
657       temp->size            = size;
658       temp->completed_time  = completed_time;
659       temp->creation_time   = creation_time;
660       temp->processing_time = processing_time;
661 
662       if (!attr)
663         break;
664     }
665 
666     ippDelete(response);
667   }
668 
669   if (n == 0 && cg->last_error >= IPP_STATUS_ERROR_BAD_REQUEST)
670     return (-1);
671   else
672     return (n);
673 }
674 
675 
676 /*
677  * 'cupsGetPrinters()' - Get a list of printers from the default server.
678  *
679  * This function is deprecated and no longer returns a list of printers - use
680  * @link cupsGetDests@ instead.
681  *
682  * @deprecated@ @exclude all@
683  */
684 
685 int					/* O - Number of printers */
cupsGetPrinters(char *** printers)686 cupsGetPrinters(char ***printers)	/* O - Printers */
687 {
688   if (printers)
689     *printers = NULL;
690 
691   return (0);
692 }
693 
694 
695 /*
696  * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
697  *
698  * @exclude all@
699  */
700 
701 int					/* O - Job ID or 0 on error */
cupsPrintFile(const char * name,const char * filename,const char * title,int num_options,cups_option_t * options)702 cupsPrintFile(const char    *name,	/* I - Destination name */
703               const char    *filename,	/* I - File to print */
704 	      const char    *title,	/* I - Title of job */
705               int           num_options,/* I - Number of options */
706 	      cups_option_t *options)	/* I - Options */
707 {
708   DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", title=\"%s\", num_options=%d, options=%p)", name, filename, title, num_options, (void *)options));
709 
710   return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title,
711                           num_options, options));
712 }
713 
714 
715 /*
716  * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
717  *                      server.
718  *
719  * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
720  */
721 
722 int					/* O - Job ID or 0 on error */
cupsPrintFile2(http_t * http,const char * name,const char * filename,const char * title,int num_options,cups_option_t * options)723 cupsPrintFile2(
724     http_t        *http,		/* I - Connection to server */
725     const char    *name,		/* I - Destination name */
726     const char    *filename,		/* I - File to print */
727     const char    *title,		/* I - Title of job */
728     int           num_options,		/* I - Number of options */
729     cups_option_t *options)		/* I - Options */
730 {
731   DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\",  title=\"%s\", num_options=%d, options=%p)", (void *)http, name, filename, title, num_options, (void *)options));
732 
733   return (cupsPrintFiles2(http, name, 1, &filename, title, num_options,
734                           options));
735 }
736 
737 
738 /*
739  * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
740  *                      default server.
741  *
742  * @exclude all@
743  */
744 
745 int					/* O - Job ID or 0 on error */
cupsPrintFiles(const char * name,int num_files,const char ** files,const char * title,int num_options,cups_option_t * options)746 cupsPrintFiles(
747     const char    *name,		/* I - Destination name */
748     int           num_files,		/* I - Number of files */
749     const char    **files,		/* I - File(s) to print */
750     const char    *title,		/* I - Title of job */
751     int           num_options,		/* I - Number of options */
752     cups_option_t *options)		/* I - Options */
753 {
754   DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, files=%p, title=\"%s\", num_options=%d, options=%p)", name, num_files, (void *)files, title, num_options, (void *)options));
755 
756  /*
757   * Print the file(s)...
758   */
759 
760   return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title,
761                           num_options, options));
762 }
763 
764 
765 /*
766  * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
767  *                       specified server.
768  *
769  * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
770  */
771 
772 int					/* O - Job ID or 0 on error */
cupsPrintFiles2(http_t * http,const char * name,int num_files,const char ** files,const char * title,int num_options,cups_option_t * options)773 cupsPrintFiles2(
774     http_t        *http,		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
775     const char    *name,		/* I - Destination name */
776     int           num_files,		/* I - Number of files */
777     const char    **files,		/* I - File(s) to print */
778     const char    *title,		/* I - Title of job */
779     int           num_options,		/* I - Number of options */
780     cups_option_t *options)		/* I - Options */
781 {
782   int		i;			/* Looping var */
783   int		job_id;			/* New job ID */
784   const char	*docname;		/* Basename of current filename */
785   const char	*format;		/* Document format */
786   cups_file_t	*fp;			/* Current file */
787   char		buffer[8192];		/* Copy buffer */
788   ssize_t	bytes;			/* Bytes in buffer */
789   http_status_t	status;			/* Status of write */
790   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
791   ipp_status_t	cancel_status;		/* Status code to preserve */
792   char		*cancel_message;	/* Error message to preserve */
793 
794 
795   DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, files=%p, title=\"%s\", num_options=%d, options=%p)", (void *)http, name, num_files, (void *)files, title, num_options, (void *)options));
796 
797  /*
798   * Range check input...
799   */
800 
801   if (!name || num_files < 1 || !files)
802   {
803     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
804 
805     return (0);
806   }
807 
808  /*
809   * Create the print job...
810   */
811 
812   if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0)
813     return (0);
814 
815  /*
816   * Send each of the files...
817   */
818 
819   if (cupsGetOption("raw", num_options, options))
820     format = CUPS_FORMAT_RAW;
821   else if ((format = cupsGetOption("document-format", num_options,
822 				   options)) == NULL)
823     format = CUPS_FORMAT_AUTO;
824 
825   for (i = 0; i < num_files; i ++)
826   {
827    /*
828     * Start the next file...
829     */
830 
831     if ((docname = strrchr(files[i], '/')) != NULL)
832       docname ++;
833     else
834       docname = files[i];
835 
836     if ((fp = cupsFileOpen(files[i], "rb")) == NULL)
837     {
838      /*
839       * Unable to open print file, cancel the job and return...
840       */
841 
842       _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0);
843       goto cancel_job;
844     }
845 
846     status = cupsStartDocument(http, name, job_id, docname, format,
847 			       i == (num_files - 1));
848 
849     while (status == HTTP_STATUS_CONTINUE &&
850 	   (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
851       status = cupsWriteRequestData(http, buffer, (size_t)bytes);
852 
853     cupsFileClose(fp);
854 
855     if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK)
856     {
857      /*
858       * Unable to queue, cancel the job and return...
859       */
860 
861       goto cancel_job;
862     }
863   }
864 
865   return (job_id);
866 
867  /*
868   * If we get here, something happened while sending the print job so we need
869   * to cancel the job without setting the last error (since we need to preserve
870   * the current error...
871   */
872 
873   cancel_job:
874 
875   cancel_status  = cg->last_error;
876   cancel_message = cg->last_status_message ?
877                        _cupsStrRetain(cg->last_status_message) : NULL;
878 
879   cupsCancelJob2(http, name, job_id, 0);
880 
881   cg->last_error          = cancel_status;
882   cg->last_status_message = cancel_message;
883 
884   return (0);
885 }
886 
887 
888 /*
889  * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob().
890  *
891  * Use @link cupsWriteRequestData@ to write data for the document and
892  * @link cupsFinishDocument@ to finish the document and get the submission status.
893  *
894  * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@,
895  * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and
896  * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although
897  * any supported MIME type string can be supplied.
898  *
899  * @since CUPS 1.4/macOS 10.6@ @exclude all@
900  */
901 
902 http_status_t				/* O - HTTP status of request */
cupsStartDocument(http_t * http,const char * name,int job_id,const char * docname,const char * format,int last_document)903 cupsStartDocument(
904     http_t     *http,			/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
905     const char *name,			/* I - Destination name */
906     int        job_id,			/* I - Job ID from @link cupsCreateJob@ */
907     const char *docname,		/* I - Name of document */
908     const char *format,			/* I - MIME type or @code CUPS_FORMAT_foo@ */
909     int        last_document)		/* I - 1 for last document in job, 0 otherwise */
910 {
911   char		resource[1024],		/* Resource for destinatio */
912 		printer_uri[1024];	/* Printer URI */
913   ipp_t		*request;		/* Send-Document request */
914   http_status_t	status;			/* HTTP status */
915 
916 
917  /*
918   * Create a Send-Document request...
919   */
920 
921   if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
922   {
923     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
924     return (HTTP_STATUS_ERROR);
925   }
926 
927   httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
928                    NULL, "localhost", ippPort(), "/printers/%s", name);
929   snprintf(resource, sizeof(resource), "/printers/%s", name);
930 
931   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
932                NULL, printer_uri);
933   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
934   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
935                NULL, cupsUser());
936   if (docname)
937     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
938                  NULL, docname);
939   if (format)
940     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
941                  "document-format", NULL, format);
942   ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document);
943 
944  /*
945   * Send and delete the request, then return the status...
946   */
947 
948   status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE);
949 
950   ippDelete(request);
951 
952   return (status);
953 }
954 
955