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