2 * "$Id: util.c 6649 2007-07-11 21:46:42Z mike $"
4 * Printing utilities for the Common UNIX Printing System (CUPS).
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products.
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/".
15 * This file is subject to the Apple OS-Developed Software exception.
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
23 * cupsGetDefault() - Get the default printer or class from the default
25 * cupsGetDefault2() - Get the default printer or class from the
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
31 * cupsGetPPD2() - Get the PPD file for a printer on the specified
33 * cupsGetPrinters() - Get a list of printers from the default server.
34 * cupsGetServerPPD() - Get an available PPD file from the server.
35 * cupsLastError() - Return the last IPP status code.
36 * cupsLastErrorString() - Return the last IPP status-message.
37 * cupsPrintFile() - Print a file to a printer or class on the default
39 * cupsPrintFile2() - Print a file to a printer or class on the
41 * cupsPrintFiles() - Print one or more files to a printer or class on
43 * cupsPrintFiles2() - Print one or more files to a printer or class on
44 * the specified server.
45 * cups_connect() - Connect to the specified host...
46 * cups_get_printer_uri() - Get the printer-uri-supported attribute for the
47 * first printer in a class.
51 * Include necessary headers...
60 #if defined(WIN32) || defined(__EMX__)
64 #endif /* WIN32 || __EMX__ */
71 static char *cups_connect(const char *name
, char *printer
, char *hostname
);
72 static int cups_get_printer_uri(http_t
*http
, const char *name
,
73 char *host
, int hostsize
, int *port
,
74 char *resource
, int resourcesize
,
79 * 'cupsCancelJob()' - Cancel a print job on the default server.
81 * Use the cupsLastError() and cupsLastErrorString() functions to get
82 * the cause of any failure.
85 int /* O - 1 on success, 0 on failure */
86 cupsCancelJob(const char *name
, /* I - Name of printer or class */
87 int job
) /* I - Job ID */
89 char printer
[HTTP_MAX_URI
], /* Printer name */
90 hostname
[HTTP_MAX_URI
], /* Hostname */
91 uri
[HTTP_MAX_URI
]; /* Printer URI */
92 ipp_t
*request
, /* IPP request */
93 *response
; /* IPP response */
94 cups_lang_t
*language
; /* Language info */
95 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
99 * See if we can connect to the server...
102 if (!cups_connect(name
, printer
, hostname
))
104 DEBUG_puts("Unable to connect to server!");
110 * Create a printer URI...
113 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
114 "localhost", 0, "/printers/%s", printer
) != HTTP_URI_OK
)
116 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
122 * Build an IPP_CANCEL_JOB request, which requires the following
126 * attributes-natural-language
129 * [requesting-user-name]
134 request
->request
.op
.operation_id
= IPP_CANCEL_JOB
;
135 request
->request
.op
.request_id
= 1;
137 language
= cupsLangDefault();
139 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
140 "attributes-charset", NULL
, cupsLangEncoding(language
));
142 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
143 "attributes-natural-language", NULL
,
144 language
!= NULL
? language
->language
: "C");
146 cupsLangFree(language
);
148 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
151 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", job
);
153 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
160 if ((response
= cupsDoRequest(cg
->http
, request
, "/jobs/")) != NULL
)
163 return (cg
->last_error
< IPP_REDIRECTION_OTHER_SITE
);
168 * 'cupsFreeJobs()' - Free memory used by job data.
172 cupsFreeJobs(int num_jobs
, /* I - Number of jobs */
173 cups_job_t
*jobs
) /* I - Jobs */
175 int i
; /* Looping var */
178 if (num_jobs
<= 0 || jobs
== NULL
)
181 for (i
= 0; i
< num_jobs
; i
++)
185 free(jobs
[i
].format
);
194 * 'cupsGetClasses()' - Get a list of printer classes from the default server.
196 * This function is deprecated - use cupsGetDests() instead.
201 int /* O - Number of classes */
202 cupsGetClasses(char ***classes
) /* O - Classes */
204 int n
; /* Number of classes */
205 ipp_t
*request
, /* IPP Request */
206 *response
; /* IPP Response */
207 ipp_attribute_t
*attr
; /* Current attribute */
208 cups_lang_t
*language
; /* Default language */
209 char **temp
; /* Temporary pointer */
210 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
215 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
221 * Try to connect to the server...
224 if (!cups_connect("default", NULL
, NULL
))
226 DEBUG_puts("Unable to connect to server!");
232 * Build a CUPS_GET_CLASSES request, which requires the following
236 * attributes-natural-language
237 * requested-attributes
242 request
->request
.op
.operation_id
= CUPS_GET_CLASSES
;
243 request
->request
.op
.request_id
= 1;
245 language
= cupsLangDefault();
247 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
248 "attributes-charset", NULL
, cupsLangEncoding(language
));
250 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
251 "attributes-natural-language", NULL
, language
->language
);
253 cupsLangFree(language
);
255 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
256 "requested-attributes", NULL
, "printer-name");
259 * Do the request and get back a response...
265 if ((response
= cupsDoRequest(cg
->http
, request
, "/")) != NULL
)
267 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
268 if (attr
->name
!= NULL
&&
269 strcasecmp(attr
->name
, "printer-name") == 0 &&
270 attr
->value_tag
== IPP_TAG_NAME
)
273 temp
= malloc(sizeof(char *));
275 temp
= realloc(*classes
, sizeof(char *) * (n
+ 1));
295 temp
[n
] = strdup(attr
->values
[0].string
.text
);
307 * 'cupsGetDefault()' - Get the default printer or class for the default server.
309 * This function returns the default printer or class as defined by
310 * the LPDEST or PRINTER environment variables. If these environment
311 * variables are not set, the server default destination is returned.
312 * Applications should use the cupsGetDests() and cupsGetDest() functions
313 * to get the user-defined default printer, as this function does not
314 * support the lpoptions-defined default printer.
317 const char * /* O - Default printer or NULL */
320 const char *var
; /* Environment variable */
321 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
325 * First see if the LPDEST or PRINTER environment variables are
326 * set... However, if PRINTER is set to "lp", ignore it to work
327 * around a "feature" in most Linux distributions - the default
328 * user login scripts set PRINTER to "lp"...
331 if ((var
= getenv("LPDEST")) != NULL
)
333 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
337 * Try to connect to the server...
340 if (!cups_connect("default", NULL
, NULL
))
342 DEBUG_puts("Unable to connect to server!");
348 * Return the default printer...
351 return (cupsGetDefault2(cg
->http
));
356 * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
358 * This function returns the default printer or class as defined by
359 * the LPDEST or PRINTER environment variables. If these environment
360 * variables are not set, the server default destination is returned.
361 * Applications should use the cupsGetDests() and cupsGetDest() functions
362 * to get the user-defined default printer, as this function does not
363 * support the lpoptions-defined default printer.
365 * @since CUPS 1.1.21@
368 const char * /* O - Default printer or NULL */
369 cupsGetDefault2(http_t
*http
) /* I - HTTP connection */
371 ipp_t
*request
, /* IPP Request */
372 *response
; /* IPP Response */
373 ipp_attribute_t
*attr
; /* Current attribute */
374 cups_lang_t
*language
; /* Default language */
375 const char *var
; /* Environment variable */
376 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
380 * First see if the LPDEST or PRINTER environment variables are
381 * set... However, if PRINTER is set to "lp", ignore it to work
382 * around a "feature" in most Linux distributions - the default
383 * user login scripts set PRINTER to "lp"...
386 if ((var
= getenv("LPDEST")) != NULL
)
388 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
392 * Range check input...
399 * Build a CUPS_GET_DEFAULT request, which requires the following
403 * attributes-natural-language
408 request
->request
.op
.operation_id
= CUPS_GET_DEFAULT
;
409 request
->request
.op
.request_id
= 1;
411 language
= cupsLangDefault();
413 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
414 "attributes-charset", NULL
, cupsLangEncoding(language
));
416 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
417 "attributes-natural-language", NULL
, language
->language
);
419 cupsLangFree(language
);
422 * Do the request and get back a response...
425 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
427 if ((attr
= ippFindAttribute(response
, "printer-name", IPP_TAG_NAME
)) != NULL
)
429 strlcpy(cg
->def_printer
, attr
->values
[0].string
.text
, sizeof(cg
->def_printer
));
431 return (cg
->def_printer
);
442 * 'cupsGetJobs()' - Get the jobs from the default server.
445 int /* O - Number of jobs */
446 cupsGetJobs(cups_job_t
**jobs
, /* O - Job data */
447 const char *mydest
, /* I - NULL = all destinations, *
448 * otherwise show jobs for mydest */
449 int myjobs
, /* I - 0 = all users, 1 = mine */
450 int completed
) /* I - -1 = show all, 0 = active, *
451 * 1 = completed jobs */
453 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
456 * Try to connect to the server...
459 if (!cups_connect("default", NULL
, NULL
))
461 DEBUG_puts("Unable to connect to server!");
470 return (cupsGetJobs2(cg
->http
, jobs
, mydest
, myjobs
, completed
));
476 * 'cupsGetJobs2()' - Get the jobs from the specified server.
478 * @since CUPS 1.1.21@
481 int /* O - Number of jobs */
482 cupsGetJobs2(http_t
*http
, /* I - HTTP connection */
483 cups_job_t
**jobs
, /* O - Job data */
484 const char *mydest
, /* I - NULL = all destinations, *
485 * otherwise show jobs for mydest */
486 int myjobs
, /* I - 0 = all users, 1 = mine */
487 int completed
) /* I - -1 = show all, 0 = active, *
488 * 1 = completed jobs */
490 int n
; /* Number of jobs */
491 ipp_t
*request
, /* IPP Request */
492 *response
; /* IPP Response */
493 ipp_attribute_t
*attr
; /* Current attribute */
494 cups_lang_t
*language
; /* Default language */
495 cups_job_t
*temp
; /* Temporary pointer */
497 priority
, /* job-priority */
498 size
; /* job-k-octets */
499 ipp_jstate_t state
; /* job-state */
500 time_t completed_time
, /* time-at-completed */
501 creation_time
, /* time-at-creation */
502 processing_time
; /* time-at-processing */
503 const char *dest
, /* job-printer-uri */
504 *format
, /* document-format */
505 *title
, /* job-name */
506 *user
; /* job-originating-user-name */
507 char uri
[HTTP_MAX_URI
]; /* URI for jobs */
508 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
509 static const char * const attrs
[] = /* Requested attributes */
517 "time-at-processing",
521 "job-originating-user-name"
526 * Range check input...
531 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
537 * Get the right URI...
542 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
543 "localhost", 0, "/printers/%s", mydest
) != HTTP_URI_OK
)
545 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
551 strcpy(uri
, "ipp://localhost/jobs");
555 * Build an IPP_GET_JOBS request, which requires the following
559 * attributes-natural-language
561 * requesting-user-name
564 * requested-attributes
569 request
->request
.op
.operation_id
= IPP_GET_JOBS
;
570 request
->request
.op
.request_id
= 1;
572 language
= cupsLangDefault();
574 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
575 "attributes-charset", NULL
, cupsLangEncoding(language
));
577 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
578 "attributes-natural-language", NULL
, language
->language
);
580 cupsLangFree(language
);
582 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
583 "printer-uri", NULL
, uri
);
585 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
586 "requesting-user-name", NULL
, cupsUser());
589 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
592 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
593 "which-jobs", NULL
, "completed");
594 else if (completed
< 0)
595 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
596 "which-jobs", NULL
, "all");
598 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
599 "requested-attributes", sizeof(attrs
) / sizeof(attrs
[0]),
603 * Do the request and get back a response...
609 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
611 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
614 * Skip leading attributes until we hit a job...
617 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_JOB
)
624 * Pull the needed attributes from this job...
630 state
= IPP_JOB_PENDING
;
633 format
= "application/octet-stream";
639 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_JOB
)
641 if (strcmp(attr
->name
, "job-id") == 0 &&
642 attr
->value_tag
== IPP_TAG_INTEGER
)
643 id
= attr
->values
[0].integer
;
644 else if (strcmp(attr
->name
, "job-state") == 0 &&
645 attr
->value_tag
== IPP_TAG_ENUM
)
646 state
= (ipp_jstate_t
)attr
->values
[0].integer
;
647 else if (strcmp(attr
->name
, "job-priority") == 0 &&
648 attr
->value_tag
== IPP_TAG_INTEGER
)
649 priority
= attr
->values
[0].integer
;
650 else if (strcmp(attr
->name
, "job-k-octets") == 0 &&
651 attr
->value_tag
== IPP_TAG_INTEGER
)
652 size
= attr
->values
[0].integer
;
653 else if (strcmp(attr
->name
, "time-at-completed") == 0 &&
654 attr
->value_tag
== IPP_TAG_INTEGER
)
655 completed_time
= attr
->values
[0].integer
;
656 else if (strcmp(attr
->name
, "time-at-creation") == 0 &&
657 attr
->value_tag
== IPP_TAG_INTEGER
)
658 creation_time
= attr
->values
[0].integer
;
659 else if (strcmp(attr
->name
, "time-at-processing") == 0 &&
660 attr
->value_tag
== IPP_TAG_INTEGER
)
661 processing_time
= attr
->values
[0].integer
;
662 else if (strcmp(attr
->name
, "job-printer-uri") == 0 &&
663 attr
->value_tag
== IPP_TAG_URI
)
665 if ((dest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
668 else if (strcmp(attr
->name
, "job-originating-user-name") == 0 &&
669 attr
->value_tag
== IPP_TAG_NAME
)
670 user
= attr
->values
[0].string
.text
;
671 else if (strcmp(attr
->name
, "document-format") == 0 &&
672 attr
->value_tag
== IPP_TAG_MIMETYPE
)
673 format
= attr
->values
[0].string
.text
;
674 else if (strcmp(attr
->name
, "job-name") == 0 &&
675 (attr
->value_tag
== IPP_TAG_TEXT
||
676 attr
->value_tag
== IPP_TAG_NAME
))
677 title
= attr
->values
[0].string
.text
;
683 * See if we have everything needed...
686 if (dest
== NULL
|| id
== 0)
695 * Allocate memory for the job...
699 temp
= malloc(sizeof(cups_job_t
));
701 temp
= realloc(*jobs
, sizeof(cups_job_t
) * (n
+ 1));
709 cupsFreeJobs(n
, *jobs
);
721 * Copy the data over...
724 temp
->dest
= strdup(dest
);
725 temp
->user
= strdup(user
);
726 temp
->format
= strdup(format
);
727 temp
->title
= strdup(title
);
729 temp
->priority
= priority
;
732 temp
->completed_time
= completed_time
;
733 temp
->creation_time
= creation_time
;
734 temp
->processing_time
= processing_time
;
743 if (n
== 0 && cg
->last_error
>= IPP_BAD_REQUEST
)
751 * 'cupsGetPPD()' - Get the PPD file for a printer on the default server.
753 * For classes, cupsGetPPD() returns the PPD file for the first printer
757 const char * /* O - Filename for PPD file */
758 cupsGetPPD(const char *name
) /* I - Printer name */
760 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
763 * See if we can connect to the server...
766 if (!cups_connect(name
, NULL
, NULL
))
768 DEBUG_puts("Unable to connect to server!");
774 * Return the PPD file...
777 return (cupsGetPPD2(cg
->http
, name
));
782 * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
784 * For classes, cupsGetPPD2() returns the PPD file for the first printer
787 * @since CUPS 1.1.21@
790 const char * /* O - Filename for PPD file */
791 cupsGetPPD2(http_t
*http
, /* I - HTTP connection */
792 const char *name
) /* I - Printer name */
794 int http_port
; /* Port number */
795 char http_hostname
[HTTP_MAX_HOST
];
796 /* Hostname associated with connection */
797 http_t
*http2
; /* Alternate HTTP connection */
798 int fd
; /* PPD file */
799 char localhost
[HTTP_MAX_URI
],/* Local hostname */
800 hostname
[HTTP_MAX_URI
], /* Hostname */
801 resource
[HTTP_MAX_URI
]; /* Resource name */
802 int port
; /* Port number */
803 http_status_t status
; /* HTTP status from server */
804 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
808 * Range check input...
811 DEBUG_printf(("cupsGetPPD2(http=%p, name=\"%s\")\n", http
,
812 name
? name
: "(null)"));
817 _cupsSetError(IPP_INTERNAL_ERROR
, "No HTTP connection!");
819 _cupsSetError(IPP_INTERNAL_ERROR
, "No printer name!");
825 * Try finding a printer URI for this printer...
828 if (!cups_get_printer_uri(http
, name
, hostname
, sizeof(hostname
), &port
,
829 resource
, sizeof(resource
), 0))
832 DEBUG_printf(("Printer hostname=\"%s\", port=%d\n", hostname
, port
));
835 * Remap local hostname to localhost...
838 httpGetHostname(NULL
, localhost
, sizeof(localhost
));
840 DEBUG_printf(("Local hostname=\"%s\"\n", localhost
));
842 if (!strcasecmp(localhost
, hostname
))
843 strcpy(hostname
, "localhost");
846 * Get the hostname and port number we are connected to...
849 httpGetHostname(http
, http_hostname
, sizeof(http_hostname
));
852 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
853 http_port
= ntohs(http
->hostaddr
->ipv6
.sin6_port
);
855 #endif /* AF_INET6 */
856 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
857 http_port
= ntohs(http
->hostaddr
->ipv4
.sin_port
);
859 http_port
= ippPort();
861 DEBUG_printf(("Connection hostname=\"%s\", port=%d\n", http_hostname
,
865 * Reconnect to the correct server as needed...
868 if (!strcasecmp(http_hostname
, hostname
) && port
== http_port
)
870 else if ((http2
= httpConnectEncrypt(hostname
, port
,
871 cupsEncryption())) == NULL
)
873 DEBUG_puts("Unable to connect to server!");
882 if ((fd
= cupsTempFd(cg
->ppd_filename
, sizeof(cg
->ppd_filename
))) < 0)
885 * Can't open file; close the server connection and return NULL...
888 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
));
897 * And send a request to the HTTP server...
900 strlcat(resource
, ".ppd", sizeof(resource
));
902 status
= cupsGetFd(http2
, resource
, fd
);
910 * See if we actually got the file or an error...
913 if (status
!= HTTP_OK
)
917 case HTTP_NOT_FOUND
:
918 _cupsSetError(IPP_NOT_FOUND
, httpStatus(status
));
921 case HTTP_UNAUTHORIZED
:
922 _cupsSetError(IPP_NOT_AUTHORIZED
, httpStatus(status
));
926 DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
928 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, httpStatus(status
));
932 unlink(cg
->ppd_filename
);
938 * Return the PPD file...
941 return (cg
->ppd_filename
);
946 * 'cupsGetPrinters()' - Get a list of printers from the default server.
948 * This function is deprecated - use cupsGetDests() instead.
953 int /* O - Number of printers */
954 cupsGetPrinters(char ***printers
) /* O - Printers */
956 int n
; /* Number of printers */
957 ipp_t
*request
, /* IPP Request */
958 *response
; /* IPP Response */
959 ipp_attribute_t
*attr
; /* Current attribute */
960 cups_lang_t
*language
; /* Default language */
961 char **temp
; /* Temporary pointer */
962 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
965 if (printers
== NULL
)
967 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
973 * Try to connect to the server...
976 if (!cups_connect("default", NULL
, NULL
))
978 DEBUG_puts("Unable to connect to server!");
984 * Build a CUPS_GET_PRINTERS request, which requires the following
988 * attributes-natural-language
989 * requested-attributes
994 request
->request
.op
.operation_id
= CUPS_GET_PRINTERS
;
995 request
->request
.op
.request_id
= 1;
997 language
= cupsLangDefault();
999 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1000 "attributes-charset", NULL
, cupsLangEncoding(language
));
1002 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1003 "attributes-natural-language", NULL
, language
->language
);
1005 cupsLangFree(language
);
1007 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1008 "requested-attributes", NULL
, "printer-name");
1010 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
1013 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
1014 "printer-type-mask", CUPS_PRINTER_CLASS
);
1017 * Do the request and get back a response...
1023 if ((response
= cupsDoRequest(cg
->http
, request
, "/")) != NULL
)
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
)
1031 temp
= malloc(sizeof(char *));
1033 temp
= realloc(*printers
, sizeof(char *) * (n
+ 1));
1038 * Ran out of memory!
1044 free((*printers
)[n
]);
1048 ippDelete(response
);
1053 temp
[n
] = strdup(attr
->values
[0].string
.text
);
1057 ippDelete(response
);
1065 * 'cupsGetServerPPD()' - Get an available PPD file from the server.
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
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().
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") */
1083 int fd
; /* PPD file descriptor */
1084 ipp_t
*request
; /* IPP request */
1085 _cups_globals_t
*cg
= _cupsGlobals();
1086 /* Pointer to library globals */
1090 * Range check input...
1096 _cupsSetError(IPP_INTERNAL_ERROR
, "No HTTP connection!");
1098 _cupsSetError(IPP_INTERNAL_ERROR
, "No PPD name!");
1104 * Get a temp file...
1107 if ((fd
= cupsTempFd(cg
->ppd_filename
, sizeof(cg
->ppd_filename
))) < 0)
1110 * Can't open file; close the server connection and return NULL...
1113 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
));
1119 * Get the PPD file...
1122 request
= ippNewRequest(CUPS_GET_PPD
);
1123 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "ppd-name", NULL
,
1126 ippDelete(cupsDoIORequest(http
, request
, "/", -1, fd
));
1130 if (cupsLastError() != IPP_OK
)
1132 unlink(cg
->ppd_filename
);
1136 return (cg
->ppd_filename
);
1141 * 'cupsLastError()' - Return the last IPP status code.
1144 ipp_status_t
/* O - IPP status code from last request */
1147 return (_cupsGlobals()->last_error
);
1152 * 'cupsLastErrorString()' - Return the last IPP status-message.
1157 const char * /* O - status-message text from last request */
1158 cupsLastErrorString(void)
1160 return (_cupsGlobals()->last_status_message
);
1165 * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
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 */
1175 DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", "
1176 "title=\"%s\", num_options=%d, options=%p)\n",
1177 name
, filename
, title
, num_options
, options
));
1179 return (cupsPrintFiles(name
, 1, &filename
, title
, num_options
, options
));
1184 * 'cupsPrintFile2()' - Print a file to a printer or class on the specified server.
1186 * @since CUPS 1.1.21@
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 */
1195 /* I - Number of options */
1196 cups_option_t
*options
) /* I - Options */
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
));
1202 return (cupsPrintFiles2(http
, name
, 1, &filename
, title
, num_options
, options
));
1207 * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
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 */
1217 /* I - Number of options */
1218 cups_option_t
*options
) /* I - Options */
1220 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
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
));
1228 * Setup a connection and request data...
1231 if (!cups_connect(name
, NULL
, NULL
))
1233 DEBUG_printf(("cupsPrintFiles: Unable to open connection - %s.\n",
1235 DEBUG_puts("Unable to connect to server!");
1241 * Print the file(s)...
1244 return (cupsPrintFiles2(cg
->http
, name
, num_files
, files
, title
,
1245 num_options
, options
));
1251 * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
1254 * @since CUPS 1.1.21@
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 */
1264 /* I - Number of options */
1265 cups_option_t
*options
) /* I - Options */
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 cups_lang_t
*language
; /* Language to use */
1274 int jobid
; /* New job ID */
1275 const char *base
; /* Basename of current filename */
1278 DEBUG_printf(("cupsPrintFiles(http=%p, name=\"%s\", num_files=%d, "
1279 "files=%p, title=\"%s\", num_options=%d, options=%p)\n",
1280 http
, name
, num_files
, files
, title
, num_options
, options
));
1283 * Range check input...
1286 if (!http
|| !name
|| num_files
< 1 || files
== NULL
)
1288 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1294 * Setup the printer URI...
1297 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1298 "localhost", 0, "/printers/%s", name
) != HTTP_URI_OK
)
1300 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1306 * Setup the request data...
1309 language
= cupsLangDefault();
1312 * Build a standard CUPS URI for the printer and fill the standard IPP
1316 if ((request
= ippNew()) == NULL
)
1318 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1323 request
->request
.op
.operation_id
= num_files
== 1 ? IPP_PRINT_JOB
:
1325 request
->request
.op
.request_id
= 1;
1327 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1328 "attributes-charset", NULL
, cupsLangEncoding(language
));
1330 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1331 "attributes-natural-language", NULL
,
1332 language
!= NULL
? language
->language
: "C");
1334 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1337 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1341 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
1345 * Then add all options...
1348 cupsEncodeOptions(request
, num_options
, options
);
1354 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
1357 response
= cupsDoFileRequest(http
, request
, uri
, *files
);
1359 response
= cupsDoRequest(http
, request
, uri
);
1361 if (response
== NULL
)
1363 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1365 DEBUG_printf(("IPP response code was 0x%x!\n",
1366 response
->request
.status
.status_code
));
1369 else if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1371 DEBUG_puts("No job ID!");
1373 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1378 jobid
= attr
->values
[0].integer
;
1380 if (response
!= NULL
)
1381 ippDelete(response
);
1384 * Handle multiple file jobs if the create-job operation worked...
1387 if (jobid
> 0 && num_files
> 1)
1388 for (i
= 0; i
< num_files
; i
++)
1391 * Build a standard CUPS URI for the job and fill the standard IPP
1395 if ((request
= ippNew()) == NULL
)
1398 request
->request
.op
.operation_id
= IPP_SEND_DOCUMENT
;
1399 request
->request
.op
.request_id
= 1;
1401 snprintf(uri
, sizeof(uri
), "ipp://localhost/jobs/%d", jobid
);
1403 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1404 "attributes-charset", NULL
, cupsLangEncoding(language
));
1406 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1407 "attributes-natural-language", NULL
,
1408 language
!= NULL
? language
->language
: "C");
1410 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri",
1414 * Handle raw print files...
1417 if (cupsGetOption("raw", num_options
, options
))
1418 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1419 "document-format", NULL
, "application/vnd.cups-raw");
1420 else if ((val
= cupsGetOption("document-format", num_options
,
1422 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1423 "document-format", NULL
, val
);
1425 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1426 "document-format", NULL
, "application/octet-stream");
1428 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1429 "requesting-user-name", NULL
, cupsUser());
1432 * Add the original document filename...
1435 if ((base
= strrchr(files
[i
], '/')) != NULL
)
1440 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "document-name",
1444 * Is this the last document?
1447 if (i
== (num_files
- 1))
1448 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1454 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
1456 if ((response
= cupsDoFileRequest(http
, request
, uri
,
1458 ippDelete(response
);
1461 cupsLangFree(language
);
1468 * 'cups_connect()' - Connect to the specified host...
1471 static char * /* I - Printer name or NULL */
1472 cups_connect(const char *name
, /* I - Destination (printer[@host]) */
1473 char *printer
, /* O - Printer name [HTTP_MAX_URI] */
1474 char *hostname
) /* O - Hostname [HTTP_MAX_URI] */
1476 char hostbuf
[HTTP_MAX_URI
], /* Name of host */
1477 http_hostname
[HTTP_MAX_HOST
]; /* Hostname associated with connection */
1478 _cups_globals_t
*cg
= _cupsGlobals();/* Pointer to library globals */
1481 DEBUG_printf(("cups_connect(\"%s\", %p, %p)\n", name
, printer
, hostname
));
1485 _cupsSetError(IPP_BAD_REQUEST
, NULL
);
1491 * All jobs are now queued to cupsServer() to avoid hostname
1492 * resolution problems and to ensure that the user sees all
1493 * locally queued jobs locally.
1496 strlcpy(hostbuf
, cupsServer(), sizeof(hostbuf
));
1498 httpGetHostname(cg
->http
, http_hostname
, sizeof(http_hostname
));
1500 if (hostname
!= NULL
)
1501 strlcpy(hostname
, hostbuf
, HTTP_MAX_URI
);
1505 if (printer
!= NULL
)
1506 strlcpy(printer
, name
, HTTP_MAX_URI
);
1508 printer
= (char *)name
;
1510 if (cg
->http
!= NULL
)
1512 if (!strcasecmp(http_hostname
, hostname
))
1515 httpClose(cg
->http
);
1518 DEBUG_printf(("connecting to %s on port %d...\n", hostname
, ippPort()));
1520 if ((cg
->http
= httpConnectEncrypt(hostname
, ippPort(),
1521 cupsEncryption())) == NULL
)
1523 DEBUG_puts("Unable to connect to server!");
1525 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, strerror(errno
));
1535 * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the first printer in a class.
1538 static int /* O - 1 on success, 0 on failure */
1539 cups_get_printer_uri(
1540 http_t
*http
, /* I - HTTP connection */
1541 const char *name
, /* I - Name of printer or class */
1542 char *host
, /* I - Hostname buffer */
1543 int hostsize
, /* I - Size of hostname buffer */
1544 int *port
, /* O - Port number */
1545 char *resource
, /* I - Resource buffer */
1546 int resourcesize
, /* I - Size of resource buffer */
1547 int depth
) /* I - Depth of query */
1549 int i
; /* Looping var */
1550 int http_port
; /* Port number */
1551 http_t
*http2
; /* Alternate HTTP connection */
1552 ipp_t
*request
, /* IPP request */
1553 *response
; /* IPP response */
1554 ipp_attribute_t
*attr
; /* Current attribute */
1555 char uri
[HTTP_MAX_URI
], /* printer-uri attribute */
1556 scheme
[HTTP_MAX_URI
], /* Scheme name */
1557 username
[HTTP_MAX_URI
], /* Username:password */
1558 classname
[255], /* Temporary class name */
1559 http_hostname
[HTTP_MAX_HOST
];
1560 /* Hostname associated with connection */
1561 static const char * const requested_attrs
[] =
1562 { /* Requested attributes */
1563 "printer-uri-supported",
1569 DEBUG_printf(("cups_get_printer_uri(http=%p, name=\"%s\", host=%p, "
1570 "hostsize=%d, resource=%p, resourcesize=%d, depth=%d)\n",
1571 http
, name
? name
: "(null)", host
, hostsize
,
1572 resource
, resourcesize
, depth
));
1575 * Setup the printer URI...
1578 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1579 "localhost", 0, "/printers/%s", name
) != HTTP_URI_OK
)
1581 _cupsSetError(IPP_INTERNAL_ERROR
, "Unable to create printer-uri!");
1589 DEBUG_printf(("cups_get_printer_uri: printer-uri=\"%s\"\n", uri
));
1592 * Get the hostname and port number we are connected to...
1595 httpGetHostname(http
, http_hostname
, sizeof(http_hostname
));
1598 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
1599 http_port
= ntohs(http
->hostaddr
->ipv6
.sin6_port
);
1601 #endif /* AF_INET6 */
1602 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
1603 http_port
= ntohs(http
->hostaddr
->ipv4
.sin_port
);
1605 http_port
= ippPort();
1608 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1611 * attributes-charset
1612 * attributes-natural-language
1614 * requested-attributes
1617 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1619 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1622 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1623 "requested-attributes",
1624 sizeof(requested_attrs
) / sizeof(requested_attrs
[0]),
1625 NULL
, requested_attrs
);
1628 * Do the request and get back a response...
1631 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1633 if ((attr
= ippFindAttribute(response
, "member-uris", IPP_TAG_URI
)) != NULL
)
1636 * Get the first actual printer name in the class...
1639 for (i
= 0; i
< attr
->num_values
; i
++)
1641 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[i
].string
.text
,
1642 scheme
, sizeof(scheme
), username
, sizeof(username
),
1643 host
, hostsize
, port
, resource
, resourcesize
);
1644 if (!strncmp(resource
, "/printers/", 10))
1650 ippDelete(response
);
1657 * No printers in this class - try recursively looking for a printer,
1658 * but not more than 3 levels deep...
1663 for (i
= 0; i
< attr
->num_values
; i
++)
1665 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[i
].string
.text
,
1666 scheme
, sizeof(scheme
), username
, sizeof(username
),
1667 host
, hostsize
, port
, resource
, resourcesize
);
1668 if (!strncmp(resource
, "/classes/", 9))
1671 * Found a class! Connect to the right server...
1674 if (!strcasecmp(http_hostname
, host
) && *port
== http_port
)
1676 else if ((http2
= httpConnectEncrypt(host
, *port
,
1677 cupsEncryption())) == NULL
)
1679 DEBUG_puts("Unable to connect to server!");
1685 * Look up printers on that server...
1688 strlcpy(classname
, resource
+ 9, sizeof(classname
));
1690 cups_get_printer_uri(http2
, classname
, host
, hostsize
, port
,
1691 resource
, resourcesize
, depth
+ 1);
1694 * Close the connection as needed...
1706 else if ((attr
= ippFindAttribute(response
, "printer-uri-supported",
1707 IPP_TAG_URI
)) != NULL
)
1709 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[0].string
.text
,
1710 scheme
, sizeof(scheme
), username
, sizeof(username
),
1711 host
, hostsize
, port
, resource
, resourcesize
);
1712 ippDelete(response
);
1717 ippDelete(response
);
1720 if (cupsLastError() != IPP_NOT_FOUND
)
1721 _cupsSetError(IPP_INTERNAL_ERROR
, "No printer-uri found!");
1731 * End of "$Id: util.c 6649 2007-07-11 21:46:42Z mike $".