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