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