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