]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/util.c
Full sweep of all Clang warnings, plus some bug fixes for incorrect memcpy usage.
[thirdparty/cups.git] / cups / util.c
1 /*
2 * "$Id$"
3 *
4 * Printing utilities for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18 /*
19 * Include necessary headers...
20 */
21
22 #include "cups-private.h"
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #if defined(WIN32) || defined(__EMX__)
26 # include <io.h>
27 #else
28 # include <unistd.h>
29 #endif /* WIN32 || __EMX__ */
30
31
32 /*
33 * Local functions...
34 */
35
36 static int cups_get_printer_uri(http_t *http, const char *name,
37 char *host, int hostsize, int *port,
38 char *resource, int resourcesize,
39 int depth);
40
41
42 /*
43 * 'cupsCancelJob()' - Cancel a print job on the default server.
44 *
45 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
46 * to cancel the current job on the named destination.
47 *
48 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
49 * the cause of any failure.
50 */
51
52 int /* O - 1 on success, 0 on failure */
53 cupsCancelJob(const char *name, /* I - Name of printer or class */
54 int job_id) /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
55 {
56 return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0)
57 < IPP_STATUS_REDIRECTION_OTHER_SITE);
58 }
59
60
61 /*
62 * 'cupsCancelJob2()' - Cancel or purge a print job.
63 *
64 * Canceled jobs remain in the job history while purged jobs are removed
65 * from the job history.
66 *
67 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
68 * to cancel the current job on the named destination.
69 *
70 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
71 * the cause of any failure.
72 *
73 * @since CUPS 1.4/OS X 10.6@
74 */
75
76 ipp_status_t /* O - IPP status */
77 cupsCancelJob2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
78 const char *name, /* I - Name of printer or class */
79 int job_id, /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
80 int purge) /* I - 1 to purge, 0 to cancel */
81 {
82 char uri[HTTP_MAX_URI]; /* Job/printer URI */
83 ipp_t *request; /* IPP request */
84
85
86 /*
87 * Range check input...
88 */
89
90 if (job_id < -1 || (!name && job_id == 0))
91 {
92 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
93 return (0);
94 }
95
96 /*
97 * Connect to the default server as needed...
98 */
99
100 if (!http)
101 if ((http = _cupsConnect()) == NULL)
102 return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
103
104 /*
105 * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following
106 * attributes:
107 *
108 * attributes-charset
109 * attributes-natural-language
110 * job-uri or printer-uri + job-id
111 * requesting-user-name
112 * [purge-job] or [purge-jobs]
113 */
114
115 request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB);
116
117 if (name)
118 {
119 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
120 "localhost", ippPort(), "/printers/%s", name);
121
122 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
123 uri);
124 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
125 job_id);
126 }
127 else if (job_id > 0)
128 {
129 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
130
131 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
132 }
133
134 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
135 NULL, cupsUser());
136
137 if (purge && job_id >= 0)
138 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1);
139 else if (!purge && job_id < 0)
140 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0);
141
142 /*
143 * Do the request...
144 */
145
146 ippDelete(cupsDoRequest(http, request, "/jobs/"));
147
148 return (cupsLastError());
149 }
150
151
152 /*
153 * 'cupsCreateJob()' - Create an empty job for streaming.
154 *
155 * Use this function when you want to stream print data using the
156 * @link cupsStartDocument@, @link cupsWriteRequestData@, and
157 * @link cupsFinishDocument@ functions. If you have one or more files to
158 * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function
159 * instead.
160 *
161 * @since CUPS 1.4/OS X 10.6@
162 */
163
164 int /* O - Job ID or 0 on error */
165 cupsCreateJob(
166 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
167 const char *name, /* I - Destination name */
168 const char *title, /* I - Title of job */
169 int num_options, /* I - Number of options */
170 cups_option_t *options) /* I - Options */
171 {
172 char printer_uri[1024], /* Printer URI */
173 resource[1024]; /* Printer resource */
174 ipp_t *request, /* Create-Job request */
175 *response; /* Create-Job response */
176 ipp_attribute_t *attr; /* job-id attribute */
177 int job_id = 0; /* job-id value */
178
179
180 DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", "
181 "num_options=%d, options=%p)",
182 http, name, title, num_options, options));
183
184 /*
185 * Range check input...
186 */
187
188 if (!name)
189 {
190 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
191 return (0);
192 }
193
194 /*
195 * Build a Create-Job request...
196 */
197
198 if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
199 {
200 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
201 return (0);
202 }
203
204 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
205 NULL, "localhost", ippPort(), "/printers/%s", name);
206 snprintf(resource, sizeof(resource), "/printers/%s", name);
207
208 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
209 NULL, printer_uri);
210 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
211 NULL, cupsUser());
212 if (title)
213 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
214 title);
215 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
216 cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
217 cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
218
219 /*
220 * Send the request and get the job-id...
221 */
222
223 response = cupsDoRequest(http, request, resource);
224
225 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
226 job_id = attr->values[0].integer;
227
228 ippDelete(response);
229
230 /*
231 * Return it...
232 */
233
234 return (job_id);
235 }
236
237
238 /*
239 * 'cupsFinishDocument()' - Finish sending a document.
240 *
241 * The document must have been started using @link cupsStartDocument@.
242 *
243 * @since CUPS 1.4/OS X 10.6@
244 */
245
246 ipp_status_t /* O - Status of document submission */
247 cupsFinishDocument(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
248 const char *name) /* I - Destination name */
249 {
250 char resource[1024]; /* Printer resource */
251
252
253 snprintf(resource, sizeof(resource), "/printers/%s", name);
254
255 ippDelete(cupsGetResponse(http, resource));
256
257 return (cupsLastError());
258 }
259
260
261 /*
262 * 'cupsFreeJobs()' - Free memory used by job data.
263 */
264
265 void
266 cupsFreeJobs(int num_jobs, /* I - Number of jobs */
267 cups_job_t *jobs) /* I - Jobs */
268 {
269 int i; /* Looping var */
270 cups_job_t *job; /* Current job */
271
272
273 if (num_jobs <= 0 || !jobs)
274 return;
275
276 for (i = num_jobs, job = jobs; i > 0; i --, job ++)
277 {
278 _cupsStrFree(job->dest);
279 _cupsStrFree(job->user);
280 _cupsStrFree(job->format);
281 _cupsStrFree(job->title);
282 }
283
284 free(jobs);
285 }
286
287
288 /*
289 * 'cupsGetClasses()' - Get a list of printer classes from the default server.
290 *
291 * This function is deprecated - use @link cupsGetDests@ instead.
292 *
293 * @deprecated@
294 */
295
296 int /* O - Number of classes */
297 cupsGetClasses(char ***classes) /* O - Classes */
298 {
299 int n; /* Number of classes */
300 ipp_t *request, /* IPP Request */
301 *response; /* IPP Response */
302 ipp_attribute_t *attr; /* Current attribute */
303 char **temp; /* Temporary pointer */
304 http_t *http; /* Connection to server */
305
306
307 if (!classes)
308 {
309 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
310
311 return (0);
312 }
313
314 *classes = NULL;
315
316 if ((http = _cupsConnect()) == NULL)
317 return (0);
318
319 /*
320 * Build a CUPS_GET_CLASSES request, which requires the following
321 * attributes:
322 *
323 * attributes-charset
324 * attributes-natural-language
325 * requested-attributes
326 */
327
328 request = ippNewRequest(IPP_OP_CUPS_GET_CLASSES);
329
330 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
331 "requested-attributes", NULL, "printer-name");
332
333 /*
334 * Do the request and get back a response...
335 */
336
337 n = 0;
338
339 if ((response = cupsDoRequest(http, request, "/")) != NULL)
340 {
341 for (attr = response->attrs; attr != NULL; attr = attr->next)
342 if (attr->name != NULL &&
343 _cups_strcasecmp(attr->name, "printer-name") == 0 &&
344 attr->value_tag == IPP_TAG_NAME)
345 {
346 if (n == 0)
347 temp = malloc(sizeof(char *));
348 else
349 temp = realloc(*classes, sizeof(char *) * (size_t)(n + 1));
350
351 if (temp == NULL)
352 {
353 /*
354 * Ran out of memory!
355 */
356
357 while (n > 0)
358 {
359 n --;
360 free((*classes)[n]);
361 }
362
363 free(*classes);
364 ippDelete(response);
365 return (0);
366 }
367
368 *classes = temp;
369 temp[n] = strdup(attr->values[0].string.text);
370 n ++;
371 }
372
373 ippDelete(response);
374 }
375
376 return (n);
377 }
378
379
380 /*
381 * 'cupsGetDefault()' - Get the default printer or class for the default server.
382 *
383 * This function returns the default printer or class as defined by
384 * the LPDEST or PRINTER environment variables. If these environment
385 * variables are not set, the server default destination is returned.
386 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
387 * functions to get the user-defined default printer, as this function does
388 * not support the lpoptions-defined default printer.
389 */
390
391 const char * /* O - Default printer or @code NULL@ */
392 cupsGetDefault(void)
393 {
394 /*
395 * Return the default printer...
396 */
397
398 return (cupsGetDefault2(CUPS_HTTP_DEFAULT));
399 }
400
401
402 /*
403 * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
404 *
405 * This function returns the default printer or class as defined by
406 * the LPDEST or PRINTER environment variables. If these environment
407 * variables are not set, the server default destination is returned.
408 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
409 * functions to get the user-defined default printer, as this function does
410 * not support the lpoptions-defined default printer.
411 *
412 * @since CUPS 1.1.21/OS X 10.4@
413 */
414
415 const char * /* O - Default printer or @code NULL@ */
416 cupsGetDefault2(http_t *http) /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
417 {
418 ipp_t *request, /* IPP Request */
419 *response; /* IPP Response */
420 ipp_attribute_t *attr; /* Current attribute */
421 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
422
423
424 /*
425 * See if we have a user default printer set...
426 */
427
428 if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer)))
429 return (cg->def_printer);
430
431 /*
432 * Connect to the server as needed...
433 */
434
435 if (!http)
436 if ((http = _cupsConnect()) == NULL)
437 return (NULL);
438
439 /*
440 * Build a CUPS_GET_DEFAULT request, which requires the following
441 * attributes:
442 *
443 * attributes-charset
444 * attributes-natural-language
445 */
446
447 request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT);
448
449 /*
450 * Do the request and get back a response...
451 */
452
453 if ((response = cupsDoRequest(http, request, "/")) != NULL)
454 {
455 if ((attr = ippFindAttribute(response, "printer-name",
456 IPP_TAG_NAME)) != NULL)
457 {
458 strlcpy(cg->def_printer, attr->values[0].string.text,
459 sizeof(cg->def_printer));
460 ippDelete(response);
461 return (cg->def_printer);
462 }
463
464 ippDelete(response);
465 }
466
467 return (NULL);
468 }
469
470
471 /*
472 * 'cupsGetJobs()' - Get the jobs from the default server.
473 *
474 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
475 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
476 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
477 * jobs that are stopped, canceled, aborted, or completed.
478 */
479
480 int /* O - Number of jobs */
481 cupsGetJobs(cups_job_t **jobs, /* O - Job data */
482 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
483 int myjobs, /* I - 0 = all users, 1 = mine */
484 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
485 {
486 /*
487 * Return the jobs...
488 */
489
490 return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, name, myjobs, whichjobs));
491 }
492
493
494
495 /*
496 * 'cupsGetJobs2()' - Get the jobs from the specified server.
497 *
498 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
499 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
500 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
501 * jobs that are stopped, canceled, aborted, or completed.
502 *
503 * @since CUPS 1.1.21/OS X 10.4@
504 */
505
506 int /* O - Number of jobs */
507 cupsGetJobs2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
508 cups_job_t **jobs, /* O - Job data */
509 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
510 int myjobs, /* I - 0 = all users, 1 = mine */
511 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
512 {
513 int n; /* Number of jobs */
514 ipp_t *request, /* IPP Request */
515 *response; /* IPP Response */
516 ipp_attribute_t *attr; /* Current attribute */
517 cups_job_t *temp; /* Temporary pointer */
518 int id, /* job-id */
519 priority, /* job-priority */
520 size; /* job-k-octets */
521 ipp_jstate_t state; /* job-state */
522 time_t completed_time, /* time-at-completed */
523 creation_time, /* time-at-creation */
524 processing_time; /* time-at-processing */
525 const char *dest, /* job-printer-uri */
526 *format, /* document-format */
527 *title, /* job-name */
528 *user; /* job-originating-user-name */
529 char uri[HTTP_MAX_URI]; /* URI for jobs */
530 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
531 static const char * const attrs[] = /* Requested attributes */
532 {
533 "document-format",
534 "job-id",
535 "job-k-octets",
536 "job-name",
537 "job-originating-user-name",
538 "job-printer-uri",
539 "job-priority",
540 "job-state",
541 "time-at-completed",
542 "time-at-creation",
543 "time-at-processing"
544 };
545
546
547 /*
548 * Range check input...
549 */
550
551 if (!jobs)
552 {
553 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
554
555 return (-1);
556 }
557
558 /*
559 * Get the right URI...
560 */
561
562 if (name)
563 {
564 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
565 "localhost", 0, "/printers/%s",
566 name) < HTTP_URI_STATUS_OK)
567 {
568 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
569 _("Unable to create printer-uri"), 1);
570
571 return (-1);
572 }
573 }
574 else
575 strlcpy(uri, "ipp://localhost/", sizeof(uri));
576
577 if (!http)
578 if ((http = _cupsConnect()) == NULL)
579 return (-1);
580
581 /*
582 * Build an IPP_GET_JOBS request, which requires the following
583 * attributes:
584 *
585 * attributes-charset
586 * attributes-natural-language
587 * printer-uri
588 * requesting-user-name
589 * which-jobs
590 * my-jobs
591 * requested-attributes
592 */
593
594 request = ippNewRequest(IPP_OP_GET_JOBS);
595
596 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
597 "printer-uri", NULL, uri);
598
599 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
600 "requesting-user-name", NULL, cupsUser());
601
602 if (myjobs)
603 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
604
605 if (whichjobs == CUPS_WHICHJOBS_COMPLETED)
606 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
607 "which-jobs", NULL, "completed");
608 else if (whichjobs == CUPS_WHICHJOBS_ALL)
609 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
610 "which-jobs", NULL, "all");
611
612 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
613 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
614 NULL, attrs);
615
616 /*
617 * Do the request and get back a response...
618 */
619
620 n = 0;
621 *jobs = NULL;
622
623 if ((response = cupsDoRequest(http, request, "/")) != NULL)
624 {
625 for (attr = response->attrs; attr; attr = attr->next)
626 {
627 /*
628 * Skip leading attributes until we hit a job...
629 */
630
631 while (attr && attr->group_tag != IPP_TAG_JOB)
632 attr = attr->next;
633
634 if (!attr)
635 break;
636
637 /*
638 * Pull the needed attributes from this job...
639 */
640
641 id = 0;
642 size = 0;
643 priority = 50;
644 state = IPP_JSTATE_PENDING;
645 user = "unknown";
646 dest = NULL;
647 format = "application/octet-stream";
648 title = "untitled";
649 creation_time = 0;
650 completed_time = 0;
651 processing_time = 0;
652
653 while (attr && attr->group_tag == IPP_TAG_JOB)
654 {
655 if (!strcmp(attr->name, "job-id") &&
656 attr->value_tag == IPP_TAG_INTEGER)
657 id = attr->values[0].integer;
658 else if (!strcmp(attr->name, "job-state") &&
659 attr->value_tag == IPP_TAG_ENUM)
660 state = (ipp_jstate_t)attr->values[0].integer;
661 else if (!strcmp(attr->name, "job-priority") &&
662 attr->value_tag == IPP_TAG_INTEGER)
663 priority = attr->values[0].integer;
664 else if (!strcmp(attr->name, "job-k-octets") &&
665 attr->value_tag == IPP_TAG_INTEGER)
666 size = attr->values[0].integer;
667 else if (!strcmp(attr->name, "time-at-completed") &&
668 attr->value_tag == IPP_TAG_INTEGER)
669 completed_time = attr->values[0].integer;
670 else if (!strcmp(attr->name, "time-at-creation") &&
671 attr->value_tag == IPP_TAG_INTEGER)
672 creation_time = attr->values[0].integer;
673 else if (!strcmp(attr->name, "time-at-processing") &&
674 attr->value_tag == IPP_TAG_INTEGER)
675 processing_time = attr->values[0].integer;
676 else if (!strcmp(attr->name, "job-printer-uri") &&
677 attr->value_tag == IPP_TAG_URI)
678 {
679 if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
680 dest ++;
681 }
682 else if (!strcmp(attr->name, "job-originating-user-name") &&
683 attr->value_tag == IPP_TAG_NAME)
684 user = attr->values[0].string.text;
685 else if (!strcmp(attr->name, "document-format") &&
686 attr->value_tag == IPP_TAG_MIMETYPE)
687 format = attr->values[0].string.text;
688 else if (!strcmp(attr->name, "job-name") &&
689 (attr->value_tag == IPP_TAG_TEXT ||
690 attr->value_tag == IPP_TAG_NAME))
691 title = attr->values[0].string.text;
692
693 attr = attr->next;
694 }
695
696 /*
697 * See if we have everything needed...
698 */
699
700 if (!dest || !id)
701 {
702 if (!attr)
703 break;
704 else
705 continue;
706 }
707
708 /*
709 * Allocate memory for the job...
710 */
711
712 if (n == 0)
713 temp = malloc(sizeof(cups_job_t));
714 else
715 temp = realloc(*jobs, sizeof(cups_job_t) * (size_t)(n + 1));
716
717 if (!temp)
718 {
719 /*
720 * Ran out of memory!
721 */
722
723 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
724
725 cupsFreeJobs(n, *jobs);
726 *jobs = NULL;
727
728 ippDelete(response);
729
730 return (-1);
731 }
732
733 *jobs = temp;
734 temp += n;
735 n ++;
736
737 /*
738 * Copy the data over...
739 */
740
741 temp->dest = _cupsStrAlloc(dest);
742 temp->user = _cupsStrAlloc(user);
743 temp->format = _cupsStrAlloc(format);
744 temp->title = _cupsStrAlloc(title);
745 temp->id = id;
746 temp->priority = priority;
747 temp->state = state;
748 temp->size = size;
749 temp->completed_time = completed_time;
750 temp->creation_time = creation_time;
751 temp->processing_time = processing_time;
752
753 if (!attr)
754 break;
755 }
756
757 ippDelete(response);
758 }
759
760 if (n == 0 && cg->last_error >= IPP_STATUS_ERROR_BAD_REQUEST)
761 return (-1);
762 else
763 return (n);
764 }
765
766
767 /*
768 * 'cupsGetPPD()' - Get the PPD file for a printer on the default server.
769 *
770 * For classes, @code cupsGetPPD@ returns the PPD file for the first printer
771 * in the class.
772 *
773 * The returned filename is stored in a static buffer and is overwritten with
774 * each call to @code cupsGetPPD@ or @link cupsGetPPD2@. The caller "owns" the
775 * file that is created and must @code unlink@ the returned filename.
776 */
777
778 const char * /* O - Filename for PPD file */
779 cupsGetPPD(const char *name) /* I - Destination name */
780 {
781 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
782 time_t modtime = 0; /* Modification time */
783
784
785 /*
786 * Return the PPD file...
787 */
788
789 cg->ppd_filename[0] = '\0';
790
791 if (cupsGetPPD3(CUPS_HTTP_DEFAULT, name, &modtime, cg->ppd_filename,
792 sizeof(cg->ppd_filename)) == HTTP_STATUS_OK)
793 return (cg->ppd_filename);
794 else
795 return (NULL);
796 }
797
798
799 /*
800 * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
801 *
802 * For classes, @code cupsGetPPD2@ returns the PPD file for the first printer
803 * in the class.
804 *
805 * The returned filename is stored in a static buffer and is overwritten with
806 * each call to @link cupsGetPPD@ or @code cupsGetPPD2@. The caller "owns" the
807 * file that is created and must @code unlink@ the returned filename.
808 *
809 * @since CUPS 1.1.21/OS X 10.4@
810 */
811
812 const char * /* O - Filename for PPD file */
813 cupsGetPPD2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
814 const char *name) /* I - Destination name */
815 {
816 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
817 time_t modtime = 0; /* Modification time */
818
819
820 cg->ppd_filename[0] = '\0';
821
822 if (cupsGetPPD3(http, name, &modtime, cg->ppd_filename,
823 sizeof(cg->ppd_filename)) == HTTP_STATUS_OK)
824 return (cg->ppd_filename);
825 else
826 return (NULL);
827 }
828
829
830 /*
831 * 'cupsGetPPD3()' - Get the PPD file for a printer on the specified
832 * server if it has changed.
833 *
834 * The "modtime" parameter contains the modification time of any
835 * locally-cached content and is updated with the time from the PPD file on
836 * the server.
837 *
838 * The "buffer" parameter contains the local PPD filename. If it contains
839 * the empty string, a new temporary file is created, otherwise the existing
840 * file will be overwritten as needed. The caller "owns" the file that is
841 * created and must @code unlink@ the returned filename.
842 *
843 * On success, @code HTTP_STATUS_OK@ is returned for a new PPD file and
844 * @code HTTP_STATUS_NOT_MODIFIED@ if the existing PPD file is up-to-date. Any other
845 * status is an error.
846 *
847 * For classes, @code cupsGetPPD3@ returns the PPD file for the first printer
848 * in the class.
849 *
850 * @since CUPS 1.4/OS X 10.6@
851 */
852
853 http_status_t /* O - HTTP status */
854 cupsGetPPD3(http_t *http, /* I - HTTP connection or @code CUPS_HTTP_DEFAULT@ */
855 const char *name, /* I - Destination name */
856 time_t *modtime, /* IO - Modification time */
857 char *buffer, /* I - Filename buffer */
858 size_t bufsize) /* I - Size of filename buffer */
859 {
860 int http_port; /* Port number */
861 char http_hostname[HTTP_MAX_HOST];
862 /* Hostname associated with connection */
863 http_t *http2; /* Alternate HTTP connection */
864 int fd; /* PPD file */
865 char localhost[HTTP_MAX_URI],/* Local hostname */
866 hostname[HTTP_MAX_URI], /* Hostname */
867 resource[HTTP_MAX_URI]; /* Resource name */
868 int port; /* Port number */
869 http_status_t status; /* HTTP status from server */
870 char tempfile[1024] = ""; /* Temporary filename */
871 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
872
873
874 /*
875 * Range check input...
876 */
877
878 DEBUG_printf(("cupsGetPPD3(http=%p, name=\"%s\", modtime=%p(%d), buffer=%p, "
879 "bufsize=%d)", http, name, modtime,
880 modtime ? (int)*modtime : 0, buffer, (int)bufsize));
881
882 if (!name)
883 {
884 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer name"), 1);
885 return (HTTP_STATUS_NOT_ACCEPTABLE);
886 }
887
888 if (!modtime)
889 {
890 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No modification time"), 1);
891 return (HTTP_STATUS_NOT_ACCEPTABLE);
892 }
893
894 if (!buffer || bufsize <= 1)
895 {
896 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad filename buffer"), 1);
897 return (HTTP_STATUS_NOT_ACCEPTABLE);
898 }
899
900 #ifndef WIN32
901 /*
902 * See if the PPD file is available locally...
903 */
904
905 if (http)
906 httpGetHostname(http, hostname, sizeof(hostname));
907 else
908 {
909 strlcpy(hostname, cupsServer(), sizeof(hostname));
910 if (hostname[0] == '/')
911 strlcpy(hostname, "localhost", sizeof(hostname));
912 }
913
914 if (!_cups_strcasecmp(hostname, "localhost"))
915 {
916 char ppdname[1024]; /* PPD filename */
917 struct stat ppdinfo; /* PPD file information */
918
919
920 snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->cups_serverroot,
921 name);
922 if (!stat(ppdname, &ppdinfo))
923 {
924 /*
925 * OK, the file exists, use it!
926 */
927
928 if (buffer[0])
929 {
930 unlink(buffer);
931
932 if (symlink(ppdname, buffer) && errno != EEXIST)
933 {
934 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
935
936 return (HTTP_STATUS_SERVER_ERROR);
937 }
938 }
939 else
940 {
941 int tries; /* Number of tries */
942 const char *tmpdir; /* TMPDIR environment variable */
943 struct timeval curtime; /* Current time */
944
945 /*
946 * Previously we put root temporary files in the default CUPS temporary
947 * directory under /var/spool/cups. However, since the scheduler cleans
948 * out temporary files there and runs independently of the user apps, we
949 * don't want to use it unless specifically told to by cupsd.
950 */
951
952 if ((tmpdir = getenv("TMPDIR")) == NULL)
953 # ifdef __APPLE__
954 tmpdir = "/private/tmp"; /* /tmp is a symlink to /private/tmp */
955 # else
956 tmpdir = "/tmp";
957 # endif /* __APPLE__ */
958
959 /*
960 * Make the temporary name using the specified directory...
961 */
962
963 tries = 0;
964
965 do
966 {
967 /*
968 * Get the current time of day...
969 */
970
971 gettimeofday(&curtime, NULL);
972
973 /*
974 * Format a string using the hex time values...
975 */
976
977 snprintf(buffer, bufsize, "%s/%08lx%05lx", tmpdir,
978 (unsigned long)curtime.tv_sec,
979 (unsigned long)curtime.tv_usec);
980
981 /*
982 * Try to make a symlink...
983 */
984
985 if (!symlink(ppdname, buffer))
986 break;
987
988 tries ++;
989 }
990 while (tries < 1000);
991
992 if (tries >= 1000)
993 {
994 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
995
996 return (HTTP_STATUS_SERVER_ERROR);
997 }
998 }
999
1000 if (*modtime >= ppdinfo.st_mtime)
1001 return (HTTP_STATUS_NOT_MODIFIED);
1002 else
1003 {
1004 *modtime = ppdinfo.st_mtime;
1005 return (HTTP_STATUS_OK);
1006 }
1007 }
1008 }
1009 #endif /* !WIN32 */
1010
1011 /*
1012 * Try finding a printer URI for this printer...
1013 */
1014
1015 if (!http)
1016 if ((http = _cupsConnect()) == NULL)
1017 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
1018
1019 if (!cups_get_printer_uri(http, name, hostname, sizeof(hostname), &port,
1020 resource, sizeof(resource), 0))
1021 return (HTTP_STATUS_NOT_FOUND);
1022
1023 DEBUG_printf(("2cupsGetPPD3: Printer hostname=\"%s\", port=%d", hostname,
1024 port));
1025
1026 /*
1027 * Remap local hostname to localhost...
1028 */
1029
1030 httpGetHostname(NULL, localhost, sizeof(localhost));
1031
1032 DEBUG_printf(("2cupsGetPPD3: Local hostname=\"%s\"", localhost));
1033
1034 if (!_cups_strcasecmp(localhost, hostname))
1035 strlcpy(hostname, "localhost", sizeof(hostname));
1036
1037 /*
1038 * Get the hostname and port number we are connected to...
1039 */
1040
1041 httpGetHostname(http, http_hostname, sizeof(http_hostname));
1042 http_port = httpAddrPort(http->hostaddr);
1043
1044 DEBUG_printf(("2cupsGetPPD3: Connection hostname=\"%s\", port=%d",
1045 http_hostname, http_port));
1046
1047 /*
1048 * Reconnect to the correct server as needed...
1049 */
1050
1051 if (!_cups_strcasecmp(http_hostname, hostname) && port == http_port)
1052 http2 = http;
1053 else if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC,
1054 cupsEncryption(), 1, 30000, NULL)) == NULL)
1055 {
1056 DEBUG_puts("1cupsGetPPD3: Unable to connect to server");
1057
1058 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
1059 }
1060
1061 /*
1062 * Get a temp file...
1063 */
1064
1065 if (buffer[0])
1066 fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600);
1067 else
1068 fd = cupsTempFd(tempfile, sizeof(tempfile));
1069
1070 if (fd < 0)
1071 {
1072 /*
1073 * Can't open file; close the server connection and return NULL...
1074 */
1075
1076 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
1077
1078 if (http2 != http)
1079 httpClose(http2);
1080
1081 return (HTTP_STATUS_SERVER_ERROR);
1082 }
1083
1084 /*
1085 * And send a request to the HTTP server...
1086 */
1087
1088 strlcat(resource, ".ppd", sizeof(resource));
1089
1090 if (*modtime > 0)
1091 httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE,
1092 httpGetDateString(*modtime));
1093
1094 status = cupsGetFd(http2, resource, fd);
1095
1096 close(fd);
1097
1098 /*
1099 * See if we actually got the file or an error...
1100 */
1101
1102 if (status == HTTP_STATUS_OK)
1103 {
1104 *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE));
1105
1106 if (tempfile[0])
1107 strlcpy(buffer, tempfile, bufsize);
1108 }
1109 else if (status != HTTP_STATUS_NOT_MODIFIED)
1110 {
1111 _cupsSetHTTPError(status);
1112
1113 if (buffer[0])
1114 unlink(buffer);
1115 else if (tempfile[0])
1116 unlink(tempfile);
1117 }
1118 else if (tempfile[0])
1119 unlink(tempfile);
1120
1121 if (http2 != http)
1122 httpClose(http2);
1123
1124 /*
1125 * Return the PPD file...
1126 */
1127
1128 DEBUG_printf(("1cupsGetPPD3: Returning status %d", status));
1129
1130 return (status);
1131 }
1132
1133
1134 /*
1135 * 'cupsGetPrinters()' - Get a list of printers from the default server.
1136 *
1137 * This function is deprecated - use @link cupsGetDests@ instead.
1138 *
1139 * @deprecated@
1140 */
1141
1142 int /* O - Number of printers */
1143 cupsGetPrinters(char ***printers) /* O - Printers */
1144 {
1145 int n; /* Number of printers */
1146 ipp_t *request, /* IPP Request */
1147 *response; /* IPP Response */
1148 ipp_attribute_t *attr; /* Current attribute */
1149 char **temp; /* Temporary pointer */
1150 http_t *http; /* Connection to server */
1151
1152
1153 /*
1154 * Range check input...
1155 */
1156
1157 if (!printers)
1158 {
1159 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1160
1161 return (0);
1162 }
1163
1164 *printers = NULL;
1165
1166 /*
1167 * Try to connect to the server...
1168 */
1169
1170 if ((http = _cupsConnect()) == NULL)
1171 return (0);
1172
1173 /*
1174 * Build a CUPS_GET_PRINTERS request, which requires the following
1175 * attributes:
1176 *
1177 * attributes-charset
1178 * attributes-natural-language
1179 * requested-attributes
1180 */
1181
1182 request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS);
1183
1184 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1185 "requested-attributes", NULL, "printer-name");
1186
1187 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
1188 "printer-type", 0);
1189
1190 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
1191 "printer-type-mask", CUPS_PRINTER_CLASS);
1192
1193 /*
1194 * Do the request and get back a response...
1195 */
1196
1197 n = 0;
1198
1199 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1200 {
1201 for (attr = response->attrs; attr != NULL; attr = attr->next)
1202 if (attr->name != NULL &&
1203 _cups_strcasecmp(attr->name, "printer-name") == 0 &&
1204 attr->value_tag == IPP_TAG_NAME)
1205 {
1206 if (n == 0)
1207 temp = malloc(sizeof(char *));
1208 else
1209 temp = realloc(*printers, sizeof(char *) * (size_t)(n + 1));
1210
1211 if (temp == NULL)
1212 {
1213 /*
1214 * Ran out of memory!
1215 */
1216
1217 while (n > 0)
1218 {
1219 n --;
1220 free((*printers)[n]);
1221 }
1222
1223 free(*printers);
1224 ippDelete(response);
1225 return (0);
1226 }
1227
1228 *printers = temp;
1229 temp[n] = strdup(attr->values[0].string.text);
1230 n ++;
1231 }
1232
1233 ippDelete(response);
1234 }
1235
1236 return (n);
1237 }
1238
1239
1240 /*
1241 * 'cupsGetServerPPD()' - Get an available PPD file from the server.
1242 *
1243 * This function returns the named PPD file from the server. The
1244 * list of available PPDs is provided by the IPP @code CUPS_GET_PPDS@
1245 * operation.
1246 *
1247 * You must remove (unlink) the PPD file when you are finished with
1248 * it. The PPD filename is stored in a static location that will be
1249 * overwritten on the next call to @link cupsGetPPD@, @link cupsGetPPD2@,
1250 * or @link cupsGetServerPPD@.
1251 *
1252 * @since CUPS 1.3/OS X 10.5@
1253 */
1254
1255 char * /* O - Name of PPD file or @code NULL@ on error */
1256 cupsGetServerPPD(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1257 const char *name) /* I - Name of PPD file ("ppd-name") */
1258 {
1259 int fd; /* PPD file descriptor */
1260 ipp_t *request; /* IPP request */
1261 _cups_globals_t *cg = _cupsGlobals();
1262 /* Pointer to library globals */
1263
1264
1265 /*
1266 * Range check input...
1267 */
1268
1269 if (!name)
1270 {
1271 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No PPD name"), 1);
1272
1273 return (NULL);
1274 }
1275
1276 if (!http)
1277 if ((http = _cupsConnect()) == NULL)
1278 return (NULL);
1279
1280 /*
1281 * Get a temp file...
1282 */
1283
1284 if ((fd = cupsTempFd(cg->ppd_filename, sizeof(cg->ppd_filename))) < 0)
1285 {
1286 /*
1287 * Can't open file; close the server connection and return NULL...
1288 */
1289
1290 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
1291
1292 return (NULL);
1293 }
1294
1295 /*
1296 * Get the PPD file...
1297 */
1298
1299 request = ippNewRequest(IPP_OP_CUPS_GET_PPD);
1300 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", NULL,
1301 name);
1302
1303 ippDelete(cupsDoIORequest(http, request, "/", -1, fd));
1304
1305 close(fd);
1306
1307 if (cupsLastError() != IPP_STATUS_OK)
1308 {
1309 unlink(cg->ppd_filename);
1310 return (NULL);
1311 }
1312 else
1313 return (cg->ppd_filename);
1314 }
1315
1316
1317 /*
1318 * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
1319 */
1320
1321 int /* O - Job ID or 0 on error */
1322 cupsPrintFile(const char *name, /* I - Destination name */
1323 const char *filename, /* I - File to print */
1324 const char *title, /* I - Title of job */
1325 int num_options,/* I - Number of options */
1326 cups_option_t *options) /* I - Options */
1327 {
1328 DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", "
1329 "title=\"%s\", num_options=%d, options=%p)",
1330 name, filename, title, num_options, options));
1331
1332 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title,
1333 num_options, options));
1334 }
1335
1336
1337 /*
1338 * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
1339 * server.
1340 *
1341 * @since CUPS 1.1.21/OS X 10.4@
1342 */
1343
1344 int /* O - Job ID or 0 on error */
1345 cupsPrintFile2(
1346 http_t *http, /* I - Connection to server */
1347 const char *name, /* I - Destination name */
1348 const char *filename, /* I - File to print */
1349 const char *title, /* I - Title of job */
1350 int num_options, /* I - Number of options */
1351 cups_option_t *options) /* I - Options */
1352 {
1353 DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", "
1354 "title=\"%s\", num_options=%d, options=%p)",
1355 http, name, filename, title, num_options, options));
1356
1357 return (cupsPrintFiles2(http, name, 1, &filename, title, num_options,
1358 options));
1359 }
1360
1361
1362 /*
1363 * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
1364 * default server.
1365 */
1366
1367 int /* O - Job ID or 0 on error */
1368 cupsPrintFiles(
1369 const char *name, /* I - Destination name */
1370 int num_files, /* I - Number of files */
1371 const char **files, /* I - File(s) to print */
1372 const char *title, /* I - Title of job */
1373 int num_options, /* I - Number of options */
1374 cups_option_t *options) /* I - Options */
1375 {
1376 DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, "
1377 "files=%p, title=\"%s\", num_options=%d, options=%p)",
1378 name, num_files, files, title, num_options, options));
1379
1380 /*
1381 * Print the file(s)...
1382 */
1383
1384 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title,
1385 num_options, options));
1386 }
1387
1388
1389 /*
1390 * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
1391 * specified server.
1392 *
1393 * @since CUPS 1.1.21/OS X 10.4@
1394 */
1395
1396 int /* O - Job ID or 0 on error */
1397 cupsPrintFiles2(
1398 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1399 const char *name, /* I - Destination name */
1400 int num_files, /* I - Number of files */
1401 const char **files, /* I - File(s) to print */
1402 const char *title, /* I - Title of job */
1403 int num_options, /* I - Number of options */
1404 cups_option_t *options) /* I - Options */
1405 {
1406 int i; /* Looping var */
1407 int job_id; /* New job ID */
1408 const char *docname; /* Basename of current filename */
1409 const char *format; /* Document format */
1410 cups_file_t *fp; /* Current file */
1411 char buffer[8192]; /* Copy buffer */
1412 ssize_t bytes; /* Bytes in buffer */
1413 http_status_t status; /* Status of write */
1414 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1415 ipp_status_t cancel_status; /* Status code to preserve */
1416 char *cancel_message; /* Error message to preserve */
1417
1418
1419 DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, "
1420 "files=%p, title=\"%s\", num_options=%d, options=%p)",
1421 http, name, num_files, files, title, num_options, options));
1422
1423 /*
1424 * Range check input...
1425 */
1426
1427 if (!name || num_files < 1 || !files)
1428 {
1429 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1430
1431 return (0);
1432 }
1433
1434 /*
1435 * Create the print job...
1436 */
1437
1438 if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0)
1439 return (0);
1440
1441 /*
1442 * Send each of the files...
1443 */
1444
1445 if (cupsGetOption("raw", num_options, options))
1446 format = CUPS_FORMAT_RAW;
1447 else if ((format = cupsGetOption("document-format", num_options,
1448 options)) == NULL)
1449 format = CUPS_FORMAT_AUTO;
1450
1451 for (i = 0; i < num_files; i ++)
1452 {
1453 /*
1454 * Start the next file...
1455 */
1456
1457 if ((docname = strrchr(files[i], '/')) != NULL)
1458 docname ++;
1459 else
1460 docname = files[i];
1461
1462 if ((fp = cupsFileOpen(files[i], "rb")) == NULL)
1463 {
1464 /*
1465 * Unable to open print file, cancel the job and return...
1466 */
1467
1468 _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0);
1469 goto cancel_job;
1470 }
1471
1472 status = cupsStartDocument(http, name, job_id, docname, format,
1473 i == (num_files - 1));
1474
1475 while (status == HTTP_STATUS_CONTINUE &&
1476 (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
1477 status = cupsWriteRequestData(http, buffer, (size_t)bytes);
1478
1479 cupsFileClose(fp);
1480
1481 if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK)
1482 {
1483 /*
1484 * Unable to queue, cancel the job and return...
1485 */
1486
1487 goto cancel_job;
1488 }
1489 }
1490
1491 return (job_id);
1492
1493 /*
1494 * If we get here, something happened while sending the print job so we need
1495 * to cancel the job without setting the last error (since we need to preserve
1496 * the current error...
1497 */
1498
1499 cancel_job:
1500
1501 cancel_status = cg->last_error;
1502 cancel_message = cg->last_status_message ?
1503 _cupsStrRetain(cg->last_status_message) : NULL;
1504
1505 cupsCancelJob2(http, name, job_id, 0);
1506
1507 cg->last_error = cancel_status;
1508 cg->last_status_message = cancel_message;
1509
1510 return (0);
1511 }
1512
1513
1514 /*
1515 * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob().
1516 *
1517 * Use @link cupsWriteRequestData@ to write data for the document and
1518 * @link cupsFinishDocument@ to finish the document and get the submission status.
1519 *
1520 * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@,
1521 * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and
1522 * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although
1523 * any supported MIME type string can be supplied.
1524 *
1525 * @since CUPS 1.4/OS X 10.6@
1526 */
1527
1528 http_status_t /* O - HTTP status of request */
1529 cupsStartDocument(
1530 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1531 const char *name, /* I - Destination name */
1532 int job_id, /* I - Job ID from @link cupsCreateJob@ */
1533 const char *docname, /* I - Name of document */
1534 const char *format, /* I - MIME type or @code CUPS_FORMAT_foo@ */
1535 int last_document) /* I - 1 for last document in job, 0 otherwise */
1536 {
1537 char resource[1024], /* Resource for destinatio */
1538 printer_uri[1024]; /* Printer URI */
1539 ipp_t *request; /* Send-Document request */
1540 http_status_t status; /* HTTP status */
1541
1542
1543 /*
1544 * Create a Send-Document request...
1545 */
1546
1547 if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
1548 {
1549 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
1550 return (HTTP_STATUS_ERROR);
1551 }
1552
1553 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
1554 NULL, "localhost", ippPort(), "/printers/%s", name);
1555 snprintf(resource, sizeof(resource), "/printers/%s", name);
1556
1557 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1558 NULL, printer_uri);
1559 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
1560 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1561 NULL, cupsUser());
1562 if (docname)
1563 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
1564 NULL, docname);
1565 if (format)
1566 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1567 "document-format", NULL, format);
1568 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document);
1569
1570 /*
1571 * Send and delete the request, then return the status...
1572 */
1573
1574 status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE);
1575
1576 ippDelete(request);
1577
1578 return (status);
1579 }
1580
1581
1582 /*
1583 * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the
1584 * first printer in a class.
1585 */
1586
1587 static int /* O - 1 on success, 0 on failure */
1588 cups_get_printer_uri(
1589 http_t *http, /* I - Connection to server */
1590 const char *name, /* I - Name of printer or class */
1591 char *host, /* I - Hostname buffer */
1592 int hostsize, /* I - Size of hostname buffer */
1593 int *port, /* O - Port number */
1594 char *resource, /* I - Resource buffer */
1595 int resourcesize, /* I - Size of resource buffer */
1596 int depth) /* I - Depth of query */
1597 {
1598 int i; /* Looping var */
1599 int http_port; /* Port number */
1600 http_t *http2; /* Alternate HTTP connection */
1601 ipp_t *request, /* IPP request */
1602 *response; /* IPP response */
1603 ipp_attribute_t *attr; /* Current attribute */
1604 char uri[HTTP_MAX_URI], /* printer-uri attribute */
1605 scheme[HTTP_MAX_URI], /* Scheme name */
1606 username[HTTP_MAX_URI], /* Username:password */
1607 classname[255], /* Temporary class name */
1608 http_hostname[HTTP_MAX_HOST];
1609 /* Hostname associated with connection */
1610 static const char * const requested_attrs[] =
1611 { /* Requested attributes */
1612 "device-uri",
1613 "member-uris",
1614 "printer-uri-supported",
1615 "printer-type"
1616 };
1617
1618
1619 DEBUG_printf(("7cups_get_printer_uri(http=%p, name=\"%s\", host=%p, "
1620 "hostsize=%d, resource=%p, resourcesize=%d, depth=%d)",
1621 http, name, host, hostsize, resource, resourcesize, depth));
1622
1623 /*
1624 * Setup the printer URI...
1625 */
1626
1627 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1628 "localhost", 0, "/printers/%s",
1629 name) < HTTP_URI_STATUS_OK)
1630 {
1631 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer-uri"),
1632 1);
1633
1634 *host = '\0';
1635 *resource = '\0';
1636
1637 return (0);
1638 }
1639
1640 DEBUG_printf(("9cups_get_printer_uri: printer-uri=\"%s\"", uri));
1641
1642 /*
1643 * Get the hostname and port number we are connected to...
1644 */
1645
1646 httpGetHostname(http, http_hostname, sizeof(http_hostname));
1647 http_port = httpAddrPort(http->hostaddr);
1648
1649 /*
1650 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1651 * attributes:
1652 *
1653 * attributes-charset
1654 * attributes-natural-language
1655 * printer-uri
1656 * requested-attributes
1657 */
1658
1659 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
1660
1661 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1662 NULL, uri);
1663
1664 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1665 "requested-attributes",
1666 sizeof(requested_attrs) / sizeof(requested_attrs[0]),
1667 NULL, requested_attrs);
1668
1669 /*
1670 * Do the request and get back a response...
1671 */
1672
1673 snprintf(resource, resourcesize, "/printers/%s", name);
1674
1675 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1676 {
1677 const char *device_uri = NULL; /* device-uri value */
1678
1679 if ((attr = ippFindAttribute(response, "device-uri",
1680 IPP_TAG_URI)) != NULL)
1681 device_uri = attr->values[0].string.text;
1682
1683 if (device_uri &&
1684 (!strncmp(device_uri, "ipp://", 6) ||
1685 !strncmp(device_uri, "ipps://", 7) ||
1686 ((strstr(device_uri, "._ipp.") != NULL ||
1687 strstr(device_uri, "._ipps.") != NULL) &&
1688 !strcmp(device_uri + strlen(device_uri) - 5, "/cups"))))
1689 {
1690 /*
1691 * Statically-configured shared printer.
1692 */
1693
1694 httpSeparateURI(HTTP_URI_CODING_ALL,
1695 _httpResolveURI(device_uri, uri, sizeof(uri),
1696 _HTTP_RESOLVE_DEFAULT, NULL, NULL),
1697 scheme, sizeof(scheme), username, sizeof(username),
1698 host, hostsize, port, resource, resourcesize);
1699 ippDelete(response);
1700
1701 return (1);
1702 }
1703 else if ((attr = ippFindAttribute(response, "member-uris",
1704 IPP_TAG_URI)) != NULL)
1705 {
1706 /*
1707 * Get the first actual printer name in the class...
1708 */
1709
1710 for (i = 0; i < attr->num_values; i ++)
1711 {
1712 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
1713 scheme, sizeof(scheme), username, sizeof(username),
1714 host, hostsize, port, resource, resourcesize);
1715 if (!strncmp(resource, "/printers/", 10))
1716 {
1717 /*
1718 * Found a printer!
1719 */
1720
1721 ippDelete(response);
1722
1723 return (1);
1724 }
1725 }
1726
1727 /*
1728 * No printers in this class - try recursively looking for a printer,
1729 * but not more than 3 levels deep...
1730 */
1731
1732 if (depth < 3)
1733 {
1734 for (i = 0; i < attr->num_values; i ++)
1735 {
1736 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
1737 scheme, sizeof(scheme), username, sizeof(username),
1738 host, hostsize, port, resource, resourcesize);
1739 if (!strncmp(resource, "/classes/", 9))
1740 {
1741 /*
1742 * Found a class! Connect to the right server...
1743 */
1744
1745 if (!_cups_strcasecmp(http_hostname, host) && *port == http_port)
1746 http2 = http;
1747 else if ((http2 = httpConnect2(host, *port, NULL, AF_UNSPEC,
1748 cupsEncryption(), 1, 30000,
1749 NULL)) == NULL)
1750 {
1751 DEBUG_puts("8cups_get_printer_uri: Unable to connect to server");
1752
1753 continue;
1754 }
1755
1756 /*
1757 * Look up printers on that server...
1758 */
1759
1760 strlcpy(classname, resource + 9, sizeof(classname));
1761
1762 cups_get_printer_uri(http2, classname, host, hostsize, port,
1763 resource, resourcesize, depth + 1);
1764
1765 /*
1766 * Close the connection as needed...
1767 */
1768
1769 if (http2 != http)
1770 httpClose(http2);
1771
1772 if (*host)
1773 return (1);
1774 }
1775 }
1776 }
1777 }
1778 else if ((attr = ippFindAttribute(response, "printer-uri-supported",
1779 IPP_TAG_URI)) != NULL)
1780 {
1781 httpSeparateURI(HTTP_URI_CODING_ALL,
1782 _httpResolveURI(attr->values[0].string.text, uri,
1783 sizeof(uri), _HTTP_RESOLVE_DEFAULT,
1784 NULL, NULL),
1785 scheme, sizeof(scheme), username, sizeof(username),
1786 host, hostsize, port, resource, resourcesize);
1787 ippDelete(response);
1788
1789 if (!strncmp(resource, "/classes/", 9))
1790 {
1791 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
1792 _("No printer-uri found for class"), 1);
1793
1794 *host = '\0';
1795 *resource = '\0';
1796
1797 return (0);
1798 }
1799
1800 return (1);
1801 }
1802
1803 ippDelete(response);
1804 }
1805
1806 if (cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND)
1807 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri found"), 1);
1808
1809 *host = '\0';
1810 *resource = '\0';
1811
1812 return (0);
1813 }
1814
1815
1816 /*
1817 * End of "$Id$".
1818 */