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