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