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