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