2 * "$Id: util.c 7014 2007-10-10 21:57:43Z 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 * 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
41 * cupsPrintFile2() - Print a file to a printer or class on the
43 * cupsPrintFiles() - Print one or more files to a printer or class on
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.
53 * Include necessary headers...
62 #if defined(WIN32) || defined(__EMX__)
66 #endif /* WIN32 || __EMX__ */
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
,
81 * 'cupsCancelJob()' - Cancel a print job on the default server.
83 * Use the cupsLastError() and cupsLastErrorString() functions to get
84 * the cause of any failure.
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 */
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 */
100 * See if we can connect to the server...
103 if (!cups_connect(name
, printer
, hostname
))
105 DEBUG_puts("Unable to connect to server!");
111 * Create a printer URI...
114 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
115 "localhost", 0, "/printers/%s", printer
) != HTTP_URI_OK
)
117 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
123 * Build an IPP_CANCEL_JOB request, which requires the following
127 * attributes-natural-language
130 * [requesting-user-name]
133 request
= ippNewRequest(IPP_CANCEL_JOB
);
135 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
138 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", job
);
140 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
147 if ((response
= cupsDoRequest(cg
->http
, request
, "/jobs/")) != NULL
)
150 return (cg
->last_error
< IPP_REDIRECTION_OTHER_SITE
);
155 * 'cupsFreeJobs()' - Free memory used by job data.
159 cupsFreeJobs(int num_jobs
, /* I - Number of jobs */
160 cups_job_t
*jobs
) /* I - Jobs */
162 int i
; /* Looping var */
165 if (num_jobs
<= 0 || jobs
== NULL
)
168 for (i
= 0; i
< num_jobs
; i
++)
172 free(jobs
[i
].format
);
181 * 'cupsGetClasses()' - Get a list of printer classes from the default server.
183 * This function is deprecated - use cupsGetDests() instead.
188 int /* O - Number of classes */
189 cupsGetClasses(char ***classes
) /* O - Classes */
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 */
201 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
207 * Try to connect to the server...
210 if (!cups_connect("default", NULL
, NULL
))
212 DEBUG_puts("Unable to connect to server!");
218 * Build a CUPS_GET_CLASSES request, which requires the following
222 * attributes-natural-language
223 * requested-attributes
226 request
= ippNewRequest(CUPS_GET_CLASSES
);
228 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
229 "requested-attributes", NULL
, "printer-name");
232 * Do the request and get back a response...
238 if ((response
= cupsDoRequest(cg
->http
, request
, "/")) != NULL
)
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
)
246 temp
= malloc(sizeof(char *));
248 temp
= realloc(*classes
, sizeof(char *) * (n
+ 1));
268 temp
[n
] = strdup(attr
->values
[0].string
.text
);
280 * 'cupsGetDefault()' - Get the default printer or class for the default server.
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.
290 const char * /* O - Default printer or NULL */
293 const char *var
; /* Environment variable */
294 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
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"...
304 if ((var
= getenv("LPDEST")) != NULL
)
306 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
310 * Try to connect to the server...
313 if (!cups_connect("default", NULL
, NULL
))
315 DEBUG_puts("Unable to connect to server!");
321 * Return the default printer...
324 return (cupsGetDefault2(cg
->http
));
329 * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
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.
338 * @since CUPS 1.1.21@
341 const char * /* O - Default printer or NULL */
342 cupsGetDefault2(http_t
*http
) /* I - HTTP connection */
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 */
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"...
358 if ((var
= getenv("LPDEST")) != NULL
)
360 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
364 * Range check input...
371 * Build a CUPS_GET_DEFAULT request, which requires the following
375 * attributes-natural-language
378 request
= ippNewRequest(CUPS_GET_DEFAULT
);
381 * Do the request and get back a response...
384 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
386 if ((attr
= ippFindAttribute(response
, "printer-name", IPP_TAG_NAME
)) != NULL
)
388 strlcpy(cg
->def_printer
, attr
->values
[0].string
.text
, sizeof(cg
->def_printer
));
390 return (cg
->def_printer
);
401 * 'cupsGetJobs()' - Get the jobs from the default server.
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 */
412 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
415 * Try to connect to the server...
418 if (!cups_connect("default", NULL
, NULL
))
420 DEBUG_puts("Unable to connect to server!");
429 return (cupsGetJobs2(cg
->http
, jobs
, mydest
, myjobs
, completed
));
435 * 'cupsGetJobs2()' - Get the jobs from the specified server.
437 * @since CUPS 1.1.21@
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 */
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 */
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 */
475 "time-at-processing",
479 "job-originating-user-name"
484 * Range check input...
489 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
495 * Get the right URI...
500 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
501 "localhost", 0, "/printers/%s", mydest
) != HTTP_URI_OK
)
503 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
509 strcpy(uri
, "ipp://localhost/jobs");
513 * Build an IPP_GET_JOBS request, which requires the following
517 * attributes-natural-language
519 * requesting-user-name
522 * requested-attributes
525 request
= ippNewRequest(IPP_GET_JOBS
);
527 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
528 "printer-uri", NULL
, uri
);
530 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
531 "requesting-user-name", NULL
, cupsUser());
534 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
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");
543 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
544 "requested-attributes", sizeof(attrs
) / sizeof(attrs
[0]),
548 * Do the request and get back a response...
554 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
556 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
559 * Skip leading attributes until we hit a job...
562 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_JOB
)
569 * Pull the needed attributes from this job...
575 state
= IPP_JOB_PENDING
;
578 format
= "application/octet-stream";
584 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_JOB
)
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
)
610 if ((dest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
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
;
628 * See if we have everything needed...
631 if (dest
== NULL
|| id
== 0)
640 * Allocate memory for the job...
644 temp
= malloc(sizeof(cups_job_t
));
646 temp
= realloc(*jobs
, sizeof(cups_job_t
) * (n
+ 1));
654 cupsFreeJobs(n
, *jobs
);
666 * Copy the data over...
669 temp
->dest
= strdup(dest
);
670 temp
->user
= strdup(user
);
671 temp
->format
= strdup(format
);
672 temp
->title
= strdup(title
);
674 temp
->priority
= priority
;
677 temp
->completed_time
= completed_time
;
678 temp
->creation_time
= creation_time
;
679 temp
->processing_time
= processing_time
;
688 if (n
== 0 && cg
->last_error
>= IPP_BAD_REQUEST
)
696 * 'cupsGetPPD()' - Get the PPD file for a printer on the default server.
698 * For classes, cupsGetPPD() returns the PPD file for the first printer
702 const char * /* O - Filename for PPD file */
703 cupsGetPPD(const char *name
) /* I - Printer name */
705 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
706 time_t modtime
= 0; /* Modification time */
710 * See if we can connect to the server...
713 if (!cups_connect(name
, NULL
, NULL
))
715 DEBUG_puts("Unable to connect to server!");
721 * Return the PPD file...
724 cg
->ppd_filename
[0] = '\0';
726 if (cupsGetPPD3(cg
->http
, name
, &modtime
, cg
->ppd_filename
,
727 sizeof(cg
->ppd_filename
)) == HTTP_OK
)
728 return (cg
->ppd_filename
);
735 * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
737 * For classes, cupsGetPPD2() returns the PPD file for the first printer
740 * @since CUPS 1.1.21@
743 const char * /* O - Filename for PPD file */
744 cupsGetPPD2(http_t
*http
, /* I - HTTP connection */
745 const char *name
) /* I - Printer name */
747 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
748 time_t modtime
= 0; /* Modification time */
751 cg
->ppd_filename
[0] = '\0';
753 if (cupsGetPPD3(http
, name
, &modtime
, cg
->ppd_filename
,
754 sizeof(cg
->ppd_filename
)) == HTTP_OK
)
755 return (cg
->ppd_filename
);
762 * 'cupsGetPPD3()' - Get the PPD file for a printer on the specified
763 * server if it has changed.
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
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.
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.
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 */
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 */
798 * Range check input...
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
));
807 _cupsSetError(IPP_INTERNAL_ERROR
, "No HTTP connection!");
808 return (HTTP_NOT_ACCEPTABLE
);
813 _cupsSetError(IPP_INTERNAL_ERROR
, "No printer name!");
814 return (HTTP_NOT_ACCEPTABLE
);
819 _cupsSetError(IPP_INTERNAL_ERROR
, "No modification time!");
820 return (HTTP_NOT_ACCEPTABLE
);
823 if (!buffer
|| bufsize
<= 1)
825 _cupsSetError(IPP_INTERNAL_ERROR
, "Bad filename buffer!");
826 return (HTTP_NOT_ACCEPTABLE
);
830 * Try finding a printer URI for this printer...
833 if (!cups_get_printer_uri(http
, name
, hostname
, sizeof(hostname
), &port
,
834 resource
, sizeof(resource
), 0))
835 return (HTTP_NOT_FOUND
);
837 DEBUG_printf(("Printer hostname=\"%s\", port=%d\n", hostname
, port
));
840 * Remap local hostname to localhost...
843 httpGetHostname(NULL
, localhost
, sizeof(localhost
));
845 DEBUG_printf(("Local hostname=\"%s\"\n", localhost
));
847 if (!strcasecmp(localhost
, hostname
))
848 strcpy(hostname
, "localhost");
851 * Get the hostname and port number we are connected to...
854 httpGetHostname(http
, http_hostname
, sizeof(http_hostname
));
857 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
858 http_port
= ntohs(http
->hostaddr
->ipv6
.sin6_port
);
860 #endif /* AF_INET6 */
861 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
862 http_port
= ntohs(http
->hostaddr
->ipv4
.sin_port
);
864 http_port
= ippPort();
866 DEBUG_printf(("Connection hostname=\"%s\", port=%d\n", http_hostname
,
870 * Reconnect to the correct server as needed...
873 if (!strcasecmp(http_hostname
, hostname
) && port
== http_port
)
875 else if ((http2
= httpConnectEncrypt(hostname
, port
,
876 cupsEncryption())) == NULL
)
878 DEBUG_puts("Unable to connect to server!");
880 return (HTTP_SERVICE_UNAVAILABLE
);
888 fd
= open(buffer
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0600);
890 fd
= cupsTempFd(buffer
, bufsize
);
895 * Can't open file; close the server connection and return NULL...
898 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
));
903 return (HTTP_SERVER_ERROR
);
907 * And send a request to the HTTP server...
910 strlcat(resource
, ".ppd", sizeof(resource
));
913 httpSetField(http2
, HTTP_FIELD_IF_MODIFIED_SINCE
,
914 httpGetDateString(*modtime
));
916 status
= cupsGetFd(http2
, resource
, fd
);
924 * See if we actually got the file or an error...
927 if (status
== HTTP_OK
)
928 *modtime
= httpGetDateTime(httpGetField(http2
, HTTP_FIELD_DATE
));
929 else if (status
!= HTTP_NOT_MODIFIED
)
933 case HTTP_NOT_FOUND
:
934 _cupsSetError(IPP_NOT_FOUND
, httpStatus(status
));
937 case HTTP_UNAUTHORIZED
:
938 _cupsSetError(IPP_NOT_AUTHORIZED
, httpStatus(status
));
942 DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
944 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, httpStatus(status
));
948 unlink(cg
->ppd_filename
);
952 * Return the PPD file...
960 * 'cupsGetPrinters()' - Get a list of printers from the default server.
962 * This function is deprecated - use cupsGetDests() instead.
967 int /* O - Number of printers */
968 cupsGetPrinters(char ***printers
) /* O - Printers */
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 */
978 if (printers
== NULL
)
980 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
986 * Try to connect to the server...
989 if (!cups_connect("default", NULL
, NULL
))
991 DEBUG_puts("Unable to connect to server!");
997 * Build a CUPS_GET_PRINTERS request, which requires the following
1000 * attributes-charset
1001 * attributes-natural-language
1002 * requested-attributes
1005 request
= ippNewRequest(CUPS_GET_PRINTERS
);
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 int jobid
; /* New job ID */
1274 const char *base
; /* Basename of current filename */
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
));
1282 * Range check input...
1285 if (!http
|| !name
|| num_files
< 1 || files
== NULL
)
1287 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1293 * Setup the printer URI...
1296 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1297 "localhost", 0, "/printers/%s", name
) != HTTP_URI_OK
)
1299 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1305 * Build a standard CUPS URI for the printer and fill the standard IPP
1309 if ((request
= ippNewRequest(num_files
== 1 ? IPP_PRINT_JOB
:
1310 IPP_CREATE_JOB
)) == NULL
)
1312 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1317 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1320 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1324 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
1328 * Then add all options...
1331 cupsEncodeOptions(request
, num_options
, options
);
1337 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
1340 response
= cupsDoFileRequest(http
, request
, uri
, *files
);
1342 response
= cupsDoRequest(http
, request
, uri
);
1344 if (response
== NULL
)
1346 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1348 DEBUG_printf(("IPP response code was 0x%x!\n",
1349 response
->request
.status
.status_code
));
1352 else if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1354 DEBUG_puts("No job ID!");
1356 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1361 jobid
= attr
->values
[0].integer
;
1363 if (response
!= NULL
)
1364 ippDelete(response
);
1367 * Handle multiple file jobs if the create-job operation worked...
1370 if (jobid
> 0 && num_files
> 1)
1371 for (i
= 0; i
< num_files
; i
++)
1374 * Build a standard CUPS URI for the job and fill the standard IPP
1378 if ((request
= ippNewRequest(IPP_SEND_DOCUMENT
)) == NULL
)
1381 snprintf(uri
, sizeof(uri
), "ipp://localhost/jobs/%d", jobid
);
1383 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri",
1387 * Handle raw print files...
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
,
1395 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1396 "document-format", NULL
, val
);
1398 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1399 "document-format", NULL
, "application/octet-stream");
1401 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1402 "requesting-user-name", NULL
, cupsUser());
1405 * Add the original document filename...
1408 if ((base
= strrchr(files
[i
], '/')) != NULL
)
1413 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "document-name",
1417 * Is this the last document?
1420 if (i
== (num_files
- 1))
1421 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1427 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
1429 if ((response
= cupsDoFileRequest(http
, request
, uri
,
1431 ippDelete(response
);
1439 * 'cups_connect()' - Connect to the specified host...
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] */
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 */
1452 DEBUG_printf(("cups_connect(\"%s\", %p, %p)\n", name
, printer
, hostname
));
1456 _cupsSetError(IPP_BAD_REQUEST
, NULL
);
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.
1467 strlcpy(hostbuf
, cupsServer(), sizeof(hostbuf
));
1469 httpGetHostname(cg
->http
, http_hostname
, sizeof(http_hostname
));
1471 if (hostname
!= NULL
)
1472 strlcpy(hostname
, hostbuf
, HTTP_MAX_URI
);
1476 if (printer
!= NULL
)
1477 strlcpy(printer
, name
, HTTP_MAX_URI
);
1479 printer
= (char *)name
;
1481 if (cg
->http
!= NULL
)
1483 if (!strcasecmp(http_hostname
, hostname
))
1486 httpClose(cg
->http
);
1489 DEBUG_printf(("connecting to %s on port %d...\n", hostname
, ippPort()));
1491 if ((cg
->http
= httpConnectEncrypt(hostname
, ippPort(),
1492 cupsEncryption())) == NULL
)
1494 DEBUG_puts("Unable to connect to server!");
1496 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, strerror(errno
));
1506 * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the first printer in a class.
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 */
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",
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
));
1546 * Setup the printer URI...
1549 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1550 "localhost", 0, "/printers/%s", name
) != HTTP_URI_OK
)
1552 _cupsSetError(IPP_INTERNAL_ERROR
, "Unable to create printer-uri!");
1560 DEBUG_printf(("cups_get_printer_uri: printer-uri=\"%s\"\n", uri
));
1563 * Get the hostname and port number we are connected to...
1566 httpGetHostname(http
, http_hostname
, sizeof(http_hostname
));
1569 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
1570 http_port
= ntohs(http
->hostaddr
->ipv6
.sin6_port
);
1572 #endif /* AF_INET6 */
1573 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
1574 http_port
= ntohs(http
->hostaddr
->ipv4
.sin_port
);
1576 http_port
= ippPort();
1579 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1582 * attributes-charset
1583 * attributes-natural-language
1585 * requested-attributes
1588 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1590 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1593 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1594 "requested-attributes",
1595 sizeof(requested_attrs
) / sizeof(requested_attrs
[0]),
1596 NULL
, requested_attrs
);
1599 * Do the request and get back a response...
1602 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1604 if ((attr
= ippFindAttribute(response
, "member-uris", IPP_TAG_URI
)) != NULL
)
1607 * Get the first actual printer name in the class...
1610 for (i
= 0; i
< attr
->num_values
; i
++)
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))
1621 ippDelete(response
);
1628 * No printers in this class - try recursively looking for a printer,
1629 * but not more than 3 levels deep...
1634 for (i
= 0; i
< attr
->num_values
; i
++)
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))
1642 * Found a class! Connect to the right server...
1645 if (!strcasecmp(http_hostname
, host
) && *port
== http_port
)
1647 else if ((http2
= httpConnectEncrypt(host
, *port
,
1648 cupsEncryption())) == NULL
)
1650 DEBUG_puts("Unable to connect to server!");
1656 * Look up printers on that server...
1659 strlcpy(classname
, resource
+ 9, sizeof(classname
));
1661 cups_get_printer_uri(http2
, classname
, host
, hostsize
, port
,
1662 resource
, resourcesize
, depth
+ 1);
1665 * Close the connection as needed...
1677 else if ((attr
= ippFindAttribute(response
, "printer-uri-supported",
1678 IPP_TAG_URI
)) != NULL
)
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
);
1688 ippDelete(response
);
1691 if (cupsLastError() != IPP_NOT_FOUND
)
1692 _cupsSetError(IPP_INTERNAL_ERROR
, "No printer-uri found!");
1702 * End of "$Id: util.c 7014 2007-10-10 21:57:43Z mike $".