1 /*
2  * Printing utilities for CUPS.
3  *
4  * Copyright 2007-2015 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 
40 int					/* O - 1 on success, 0 on failure */
cupsCancelJob(const char * name,int job_id)41 cupsCancelJob(const char *name,		/* I - Name of printer or class */
42               int        job_id)	/* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
43 {
44   return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0)
45               < IPP_STATUS_REDIRECTION_OTHER_SITE);
46 }
47 
48 
49 /*
50  * 'cupsCancelJob2()' - Cancel or purge a print job.
51  *
52  * Canceled jobs remain in the job history while purged jobs are removed
53  * from the job history.
54  *
55  * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
56  * to cancel the current job on the named destination.
57  *
58  * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
59  * the cause of any failure.
60  *
61  * @since CUPS 1.4/macOS 10.6@
62  */
63 
64 ipp_status_t				/* O - IPP status */
cupsCancelJob2(http_t * http,const char * name,int job_id,int purge)65 cupsCancelJob2(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
66                const char *name,	/* I - Name of printer or class */
67                int        job_id,	/* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
68 	       int        purge)	/* I - 1 to purge, 0 to cancel */
69 {
70   char		uri[HTTP_MAX_URI];	/* Job/printer URI */
71   ipp_t		*request;		/* IPP request */
72 
73 
74  /*
75   * Range check input...
76   */
77 
78   if (job_id < -1 || (!name && job_id == 0))
79   {
80     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
81     return (0);
82   }
83 
84  /*
85   * Connect to the default server as needed...
86   */
87 
88   if (!http)
89     if ((http = _cupsConnect()) == NULL)
90       return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
91 
92  /*
93   * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following
94   * attributes:
95   *
96   *    attributes-charset
97   *    attributes-natural-language
98   *    job-uri or printer-uri + job-id
99   *    requesting-user-name
100   *    [purge-job] or [purge-jobs]
101   */
102 
103   request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB);
104 
105   if (name)
106   {
107     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
108                      "localhost", ippPort(), "/printers/%s", name);
109 
110     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
111                  uri);
112     ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
113                   job_id);
114   }
115   else if (job_id > 0)
116   {
117     snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
118 
119     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
120   }
121 
122   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
123                NULL, cupsUser());
124 
125   if (purge && job_id >= 0)
126     ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1);
127   else if (!purge && job_id < 0)
128     ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0);
129 
130  /*
131   * Do the request...
132   */
133 
134   ippDelete(cupsDoRequest(http, request, "/jobs/"));
135 
136   return (cupsLastError());
137 }
138 
139 
140 /*
141  * 'cupsCreateJob()' - Create an empty job for streaming.
142  *
143  * Use this function when you want to stream print data using the
144  * @link cupsStartDocument@, @link cupsWriteRequestData@, and
145  * @link cupsFinishDocument@ functions.  If you have one or more files to
146  * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function
147  * instead.
148  *
149  * @since CUPS 1.4/macOS 10.6@
150  */
151 
152 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)153 cupsCreateJob(
154     http_t        *http,		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
155     const char    *name,		/* I - Destination name */
156     const char    *title,		/* I - Title of job */
157     int           num_options,		/* I - Number of options */
158     cups_option_t *options)		/* I - Options */
159 {
160   char		printer_uri[1024],	/* Printer URI */
161 		resource[1024];		/* Printer resource */
162   ipp_t		*request,		/* Create-Job request */
163 		*response;		/* Create-Job response */
164   ipp_attribute_t *attr;		/* job-id attribute */
165   int		job_id = 0;		/* job-id value */
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   * Build a Create-Job request...
182   */
183 
184   if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
185   {
186     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
187     return (0);
188   }
189 
190   httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
191                    NULL, "localhost", ippPort(), "/printers/%s", name);
192   snprintf(resource, sizeof(resource), "/printers/%s", name);
193 
194   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
195                NULL, printer_uri);
196   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
197                NULL, cupsUser());
198   if (title)
199     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
200                  title);
201   cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
202   cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
203   cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
204 
205  /*
206   * Send the request and get the job-id...
207   */
208 
209   response = cupsDoRequest(http, request, resource);
210 
211   if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
212     job_id = attr->values[0].integer;
213 
214   ippDelete(response);
215 
216  /*
217   * Return it...
218   */
219 
220   return (job_id);
221 }
222 
223 
224 /*
225  * 'cupsFinishDocument()' - Finish sending a document.
226  *
227  * The document must have been started using @link cupsStartDocument@.
228  *
229  * @since CUPS 1.4/macOS 10.6@
230  */
231 
232 ipp_status_t				/* O - Status of document submission */
cupsFinishDocument(http_t * http,const char * name)233 cupsFinishDocument(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
234                    const char *name)	/* I - Destination name */
235 {
236   char	resource[1024];			/* Printer resource */
237 
238 
239   snprintf(resource, sizeof(resource), "/printers/%s", name);
240 
241   ippDelete(cupsGetResponse(http, resource));
242 
243   return (cupsLastError());
244 }
245 
246 
247 /*
248  * 'cupsFreeJobs()' - Free memory used by job data.
249  */
250 
251 void
cupsFreeJobs(int num_jobs,cups_job_t * jobs)252 cupsFreeJobs(int        num_jobs,	/* I - Number of jobs */
253              cups_job_t *jobs)		/* I - Jobs */
254 {
255   int		i;			/* Looping var */
256   cups_job_t	*job;			/* Current job */
257 
258 
259   if (num_jobs <= 0 || !jobs)
260     return;
261 
262   for (i = num_jobs, job = jobs; i > 0; i --, job ++)
263   {
264     _cupsStrFree(job->dest);
265     _cupsStrFree(job->user);
266     _cupsStrFree(job->format);
267     _cupsStrFree(job->title);
268   }
269 
270   free(jobs);
271 }
272 
273 
274 /*
275  * 'cupsGetClasses()' - Get a list of printer classes from the default server.
276  *
277  * This function is deprecated and no longer returns a list of printer
278  * classes - use @link cupsGetDests@ instead.
279  *
280  * @deprecated@
281  */
282 
283 int					/* O - Number of classes */
cupsGetClasses(char *** classes)284 cupsGetClasses(char ***classes)		/* O - Classes */
285 {
286   if (classes)
287     *classes = NULL;
288 
289   return (0);
290 }
291 
292 
293 /*
294  * 'cupsGetDefault()' - Get the default printer or class for the default server.
295  *
296  * This function returns the default printer or class as defined by
297  * the LPDEST or PRINTER environment variables. If these environment
298  * variables are not set, the server default destination is returned.
299  * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
300  * functions to get the user-defined default printer, as this function does
301  * not support the lpoptions-defined default printer.
302  */
303 
304 const char *				/* O - Default printer or @code NULL@ */
cupsGetDefault(void)305 cupsGetDefault(void)
306 {
307  /*
308   * Return the default printer...
309   */
310 
311   return (cupsGetDefault2(CUPS_HTTP_DEFAULT));
312 }
313 
314 
315 /*
316  * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
317  *
318  * This function returns the default printer or class as defined by
319  * the LPDEST or PRINTER environment variables. If these environment
320  * variables are not set, the server default destination is returned.
321  * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
322  * functions to get the user-defined default printer, as this function does
323  * not support the lpoptions-defined default printer.
324  *
325  * @since CUPS 1.1.21/macOS 10.4@
326  */
327 
328 const char *				/* O - Default printer or @code NULL@ */
cupsGetDefault2(http_t * http)329 cupsGetDefault2(http_t *http)		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
330 {
331   ipp_t		*request,		/* IPP Request */
332 		*response;		/* IPP Response */
333   ipp_attribute_t *attr;		/* Current attribute */
334   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
335 
336 
337  /*
338   * See if we have a user default printer set...
339   */
340 
341   if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer)))
342     return (cg->def_printer);
343 
344  /*
345   * Connect to the server as needed...
346   */
347 
348   if (!http)
349     if ((http = _cupsConnect()) == NULL)
350       return (NULL);
351 
352  /*
353   * Build a CUPS_GET_DEFAULT request, which requires the following
354   * attributes:
355   *
356   *    attributes-charset
357   *    attributes-natural-language
358   */
359 
360   request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT);
361 
362  /*
363   * Do the request and get back a response...
364   */
365 
366   if ((response = cupsDoRequest(http, request, "/")) != NULL)
367   {
368     if ((attr = ippFindAttribute(response, "printer-name",
369                                  IPP_TAG_NAME)) != NULL)
370     {
371       strlcpy(cg->def_printer, attr->values[0].string.text,
372               sizeof(cg->def_printer));
373       ippDelete(response);
374       return (cg->def_printer);
375     }
376 
377     ippDelete(response);
378   }
379 
380   return (NULL);
381 }
382 
383 
384 /*
385  * 'cupsGetJobs()' - Get the jobs from the default server.
386  *
387  * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
388  * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
389  * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
390  * jobs that are stopped, canceled, aborted, or completed.
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@
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 
703 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)704 cupsPrintFile(const char    *name,	/* I - Destination name */
705               const char    *filename,	/* I - File to print */
706 	      const char    *title,	/* I - Title of job */
707               int           num_options,/* I - Number of options */
708 	      cups_option_t *options)	/* I - Options */
709 {
710   DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", title=\"%s\", num_options=%d, options=%p)", name, filename, title, num_options, (void *)options));
711 
712   return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title,
713                           num_options, options));
714 }
715 
716 
717 /*
718  * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
719  *                      server.
720  *
721  * @since CUPS 1.1.21/macOS 10.4@
722  */
723 
724 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)725 cupsPrintFile2(
726     http_t        *http,		/* I - Connection to server */
727     const char    *name,		/* I - Destination name */
728     const char    *filename,		/* I - File to print */
729     const char    *title,		/* I - Title of job */
730     int           num_options,		/* I - Number of options */
731     cups_option_t *options)		/* I - Options */
732 {
733   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));
734 
735   return (cupsPrintFiles2(http, name, 1, &filename, title, num_options,
736                           options));
737 }
738 
739 
740 /*
741  * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
742  *                      default server.
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@
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@
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