2 * "$Id: util.c,v 1.93 2002/03/01 19:53:30 mike Exp $"
4 * Printing utilities for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2002 by Easy Software Products.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
24 * This file is subject to the Apple OS-Developed Software exception.
28 * cupsCancelJob() - Cancel a print job.
29 * cupsDoFileRequest() - Do an IPP request...
30 * cupsFreeJobs() - Free memory used by job data.
31 * cupsGetClasses() - Get a list of printer classes.
32 * cupsGetDefault() - Get the default printer or class.
33 * cupsGetJobs() - Get the jobs from the server.
34 * cupsGetPPD() - Get the PPD file for a printer.
35 * cupsGetPrinters() - Get a list of printers.
36 * cupsLastError() - Return the last IPP error that occurred.
37 * cupsPrintFile() - Print a file to a printer or class.
38 * cupsPrintFiles() - Print one or more files to a printer or class.
39 * cups_connect() - Connect to the specified host...
40 * cups_local_auth() - Get the local authorization certificate if
41 * available/applicable...
45 * Include necessary headers...
58 #if defined(WIN32) || defined(__EMX__)
62 #endif /* WIN32 || __EMX__ */
69 static http_t
*cups_server
= NULL
; /* Current server connection */
70 static ipp_status_t last_error
= IPP_OK
; /* Last IPP error */
71 static char authstring
[HTTP_MAX_VALUE
] = "";
72 /* Authorization string */
73 static char pwdstring
[33] = ""; /* Last password string */
80 static char *cups_connect(const char *name
, char *printer
, char *hostname
);
81 static int cups_local_auth(http_t
*http
);
85 * 'cupsCancelJob()' - Cancel a print job.
88 int /* O - 1 on success, 0 on failure */
89 cupsCancelJob(const char *name
, /* I - Name of printer or class */
90 int job
) /* I - Job ID */
92 char printer
[HTTP_MAX_URI
], /* Printer name */
93 hostname
[HTTP_MAX_URI
], /* Hostname */
94 uri
[HTTP_MAX_URI
]; /* Printer URI */
95 ipp_t
*request
, /* IPP request */
96 *response
; /* IPP response */
97 cups_lang_t
*language
; /* Language info */
101 * See if we can connect to the server...
104 if (!cups_connect(name
, printer
, hostname
))
106 last_error
= IPP_SERVICE_UNAVAILABLE
;
111 * Build an IPP_CANCEL_JOB request, which requires the following
115 * attributes-natural-language
118 * [requesting-user-name]
123 request
->request
.op
.operation_id
= IPP_CANCEL_JOB
;
124 request
->request
.op
.request_id
= 1;
126 language
= cupsLangDefault();
128 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
129 "attributes-charset", NULL
, cupsLangEncoding(language
));
131 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
132 "attributes-natural-language", NULL
,
133 language
!= NULL
? language
->language
: "C");
135 snprintf(uri
, sizeof(uri
), "ipp://%s:%d/printers/%s", hostname
, ippPort(), printer
);
136 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
139 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", job
);
141 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
148 if ((response
= cupsDoRequest(cups_server
, request
, "/jobs/")) == NULL
)
150 last_error
= IPP_BAD_REQUEST
;
155 last_error
= response
->request
.status
.status_code
;
164 * 'cupsDoFileRequest()' - Do an IPP request...
167 ipp_t
* /* O - Response data */
168 cupsDoFileRequest(http_t
*http
, /* I - HTTP connection to server */
169 ipp_t
*request
, /* I - IPP request */
170 const char *resource
, /* I - HTTP resource for POST */
171 const char *filename
) /* I - File to send or NULL */
173 ipp_t
*response
; /* IPP response data */
174 char length
[255]; /* Content-Length field */
175 http_status_t status
; /* Status of HTTP request */
176 FILE *file
; /* File to send */
177 struct stat fileinfo
; /* File information */
178 int bytes
; /* Number of bytes read/written */
179 char buffer
[32768]; /* Output buffer */
180 const char *password
; /* Password string */
181 char realm
[HTTP_MAX_VALUE
], /* realm="xyz" string */
182 nonce
[HTTP_MAX_VALUE
], /* nonce="xyz" string */
183 plain
[255], /* Plaintext username:password */
184 encode
[512]; /* Encoded username:password */
185 char prompt
[1024]; /* Prompt string */
186 int digest_tries
; /* Number of tries with Digest */
189 if (http
== NULL
|| request
== NULL
|| resource
== NULL
)
194 last_error
= IPP_INTERNAL_ERROR
;
198 DEBUG_printf(("cupsDoFileRequest(%p, %08x, \'%s\', \'%s\')\n",
199 http
, request
, resource
, filename
? filename
: "(null)"));
202 * See if we have a file to send...
205 if (filename
!= NULL
)
207 if (stat(filename
, &fileinfo
))
210 * Can't get file information!
214 last_error
= IPP_NOT_FOUND
;
218 if ((file
= fopen(filename
, "rb")) == NULL
)
225 last_error
= IPP_NOT_FOUND
;
233 * Loop until we can send the request without authorization problems.
240 while (response
== NULL
)
242 DEBUG_puts("cupsDoFileRequest: setup...");
245 * Setup the HTTP variables needed...
248 if (filename
!= NULL
)
249 sprintf(length
, "%lu", (unsigned long)(ippLength(request
) +
250 (size_t)fileinfo
.st_size
));
252 sprintf(length
, "%lu", (unsigned long)ippLength(request
));
254 httpClearFields(http
);
255 httpSetField(http
, HTTP_FIELD_CONTENT_LENGTH
, length
);
256 httpSetField(http
, HTTP_FIELD_CONTENT_TYPE
, "application/ipp");
257 httpSetField(http
, HTTP_FIELD_AUTHORIZATION
, authstring
);
263 DEBUG_puts("cupsDoFileRequest: post...");
265 if (httpPost(http
, resource
))
267 if (httpReconnect(http
))
277 * Send the IPP data and wait for the response...
280 DEBUG_puts("cupsDoFileRequest: ipp write...");
282 request
->state
= IPP_IDLE
;
283 if (ippWrite(http
, request
) != IPP_ERROR
)
284 if (filename
!= NULL
)
286 DEBUG_puts("cupsDoFileRequest: file write...");
294 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), file
)) > 0)
295 if (httpWrite(http
, buffer
, bytes
) < bytes
)
300 * Get the server's return status...
303 DEBUG_puts("cupsDoFileRequest: update...");
305 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
307 if (status
== HTTP_UNAUTHORIZED
)
309 DEBUG_puts("cupsDoFileRequest: unauthorized...");
312 * Flush any error message...
318 * See if we can do local authentication...
321 if (cups_local_auth(http
))
325 * See if we should retry the current digest password...
328 if (strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Basic", 5) == 0 ||
329 digest_tries
> 1 || !pwdstring
[0])
332 * Nope - get a password from the user...
335 snprintf(prompt
, sizeof(prompt
), "Password for %s on %s? ", cupsUser(),
338 if ((password
= cupsGetPassword(prompt
)) == NULL
)
343 strncpy(pwdstring
, password
, sizeof(pwdstring
) - 1);
344 pwdstring
[sizeof(pwdstring
) - 1] = '\0';
352 * Got a password; encode it for the server...
355 if (strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Basic", 5) == 0)
358 * Basic authentication...
361 snprintf(plain
, sizeof(plain
), "%s:%s", cupsUser(), pwdstring
);
362 httpEncode64(encode
, plain
);
363 snprintf(authstring
, sizeof(authstring
), "Basic %s", encode
);
368 * Digest authentication...
371 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
, "realm", realm
);
372 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
, "nonce", nonce
);
374 httpMD5(cupsUser(), realm
, pwdstring
, encode
);
375 httpMD5Final(nonce
, "POST", resource
, encode
);
376 snprintf(authstring
, sizeof(authstring
),
377 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
378 "response=\"%s\"", cupsUser(), realm
, nonce
, encode
);
383 else if (status
== HTTP_ERROR
)
386 if (http
->error
!= WSAENETDOWN
&& http
->error
!= WSAENETUNREACH
)
388 if (http
->error
!= ENETDOWN
&& http
->error
!= ENETUNREACH
)
395 else if (status
== HTTP_UPGRADE_REQUIRED
)
398 * Flush any error message...
404 * Upgrade with encryption...
407 httpEncryption(http
, HTTP_ENCRYPT_REQUIRED
);
410 * Try again, this time with encryption enabled...
415 #endif /* HAVE_LIBSSL */
416 else if (status
!= HTTP_OK
)
418 DEBUG_printf(("cupsDoFileRequest: error %d...\n", status
));
421 * Flush any error message...
430 * Read the response...
433 DEBUG_puts("cupsDoFileRequest: response...");
437 if (ippRead(http
, response
) == IPP_ERROR
)
440 * Delete the response...
446 last_error
= IPP_SERVICE_UNAVAILABLE
;
453 * Close the file if needed...
456 if (filename
!= NULL
)
460 * Flush any remaining data...
466 * Delete the original request and return the response...
472 last_error
= response
->request
.status
.status_code
;
473 else if (status
== HTTP_NOT_FOUND
)
474 last_error
= IPP_NOT_FOUND
;
475 else if (status
== HTTP_UNAUTHORIZED
)
476 last_error
= IPP_NOT_AUTHORIZED
;
477 else if (status
!= HTTP_OK
)
478 last_error
= IPP_SERVICE_UNAVAILABLE
;
485 * 'cupsFreeJobs()' - Free memory used by job data.
489 cupsFreeJobs(int num_jobs
,/* I - Number of jobs */
490 cups_job_t
*jobs
) /* I - Jobs */
492 int i
; /* Looping var */
495 if (num_jobs
<= 0 || jobs
== NULL
)
498 for (i
= 0; i
< num_jobs
; i
++)
502 free(jobs
[i
].format
);
511 * 'cupsGetClasses()' - Get a list of printer classes.
514 int /* O - Number of classes */
515 cupsGetClasses(char ***classes
) /* O - Classes */
517 int n
; /* Number of classes */
518 ipp_t
*request
, /* IPP Request */
519 *response
; /* IPP Response */
520 ipp_attribute_t
*attr
; /* Current attribute */
521 cups_lang_t
*language
; /* Default language */
522 char **temp
; /* Temporary pointer */
527 last_error
= IPP_INTERNAL_ERROR
;
532 * Try to connect to the server...
535 if (!cups_connect("default", NULL
, NULL
))
537 last_error
= IPP_SERVICE_UNAVAILABLE
;
542 * Build a CUPS_GET_CLASSES request, which requires the following
546 * attributes-natural-language
547 * requested-attributes
552 request
->request
.op
.operation_id
= CUPS_GET_CLASSES
;
553 request
->request
.op
.request_id
= 1;
555 language
= cupsLangDefault();
557 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
558 "attributes-charset", NULL
, cupsLangEncoding(language
));
560 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
561 "attributes-natural-language", NULL
, language
->language
);
563 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
564 "requested-attributes", NULL
, "printer-name");
567 * Do the request and get back a response...
573 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
575 last_error
= response
->request
.status
.status_code
;
577 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
578 if (attr
->name
!= NULL
&&
579 strcasecmp(attr
->name
, "printer-name") == 0 &&
580 attr
->value_tag
== IPP_TAG_NAME
)
583 temp
= malloc(sizeof(char *));
585 temp
= realloc(*classes
, sizeof(char *) * (n
+ 1));
605 temp
[n
] = strdup(attr
->values
[0].string
.text
);
612 last_error
= IPP_BAD_REQUEST
;
619 * 'cupsGetDefault()' - Get the default printer or class.
622 const char * /* O - Default printer or NULL */
625 ipp_t
*request
, /* IPP Request */
626 *response
; /* IPP Response */
627 ipp_attribute_t
*attr
; /* Current attribute */
628 cups_lang_t
*language
; /* Default language */
629 const char *var
; /* Environment variable */
630 static char def_printer
[256];/* Default printer */
634 * First see if the LPDEST or PRINTER environment variables are
635 * set... However, if PRINTER is set to "lp", ignore it to work
636 * around a "feature" in most Linux distributions - the default
637 * user login scripts set PRINTER to "lp"...
640 if ((var
= getenv("LPDEST")) != NULL
)
642 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
646 * Try to connect to the server...
649 if (!cups_connect("default", NULL
, NULL
))
651 last_error
= IPP_SERVICE_UNAVAILABLE
;
656 * Build a CUPS_GET_DEFAULT request, which requires the following
660 * attributes-natural-language
665 request
->request
.op
.operation_id
= CUPS_GET_DEFAULT
;
666 request
->request
.op
.request_id
= 1;
668 language
= cupsLangDefault();
670 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
671 "attributes-charset", NULL
, cupsLangEncoding(language
));
673 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
674 "attributes-natural-language", NULL
, language
->language
);
677 * Do the request and get back a response...
680 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
682 last_error
= response
->request
.status
.status_code
;
684 if ((attr
= ippFindAttribute(response
, "printer-name", IPP_TAG_NAME
)) != NULL
)
686 strncpy(def_printer
, attr
->values
[0].string
.text
, sizeof(def_printer
) - 1);
687 def_printer
[sizeof(def_printer
) - 1] = '\0';
689 return (def_printer
);
695 last_error
= IPP_BAD_REQUEST
;
702 * 'cupsGetJobs()' - Get the jobs from the server.
705 int /* O - Number of jobs */
706 cupsGetJobs(cups_job_t
**jobs
, /* O - Job data */
707 const char *mydest
, /* I - Only show jobs for dest? */
708 int myjobs
, /* I - Only show my jobs? */
709 int completed
) /* I - Only show completed jobs? */
711 int n
; /* Number of jobs */
712 ipp_t
*request
, /* IPP Request */
713 *response
; /* IPP Response */
714 ipp_attribute_t
*attr
; /* Current attribute */
715 cups_lang_t
*language
; /* Default language */
716 cups_job_t
*temp
; /* Temporary pointer */
718 priority
, /* job-priority */
719 size
; /* job-k-octets */
720 ipp_jstate_t state
; /* job-state */
721 time_t completed_time
, /* time-at-completed */
722 creation_time
, /* time-at-creation */
723 processing_time
; /* time-at-processing */
724 const char *dest
, /* job-printer-uri */
725 *format
, /* document-format */
726 *title
, /* job-name */
727 *user
; /* job-originating-user-name */
728 char uri
[HTTP_MAX_URI
]; /* URI for jobs */
729 static const char *attrs
[] = /* Requested attributes */
737 "time-at-processing",
741 "job-originating-user-name"
747 last_error
= IPP_INTERNAL_ERROR
;
752 * Try to connect to the server...
755 if (!cups_connect("default", NULL
, NULL
))
757 last_error
= IPP_SERVICE_UNAVAILABLE
;
762 * Build an IPP_GET_JOBS request, which requires the following
766 * attributes-natural-language
768 * requesting-user-name
771 * requested-attributes
776 request
->request
.op
.operation_id
= IPP_GET_JOBS
;
777 request
->request
.op
.request_id
= 1;
779 language
= cupsLangDefault();
781 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
782 "attributes-charset", NULL
, cupsLangEncoding(language
));
784 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
785 "attributes-natural-language", NULL
, language
->language
);
788 snprintf(uri
, sizeof(uri
), "ipp://localhost/printers/%s", mydest
);
790 strcpy(uri
, "ipp://localhost/jobs");
792 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
793 "printer-uri", NULL
, uri
);
795 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
796 "requesting-user-name", NULL
, cupsUser());
799 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
802 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
803 "which-jobs", NULL
, "completed");
805 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
806 "requested-attributes", sizeof(attrs
) / sizeof(attrs
[0]),
810 * Do the request and get back a response...
816 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
818 last_error
= response
->request
.status
.status_code
;
820 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
823 * Skip leading attributes until we hit a job...
826 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_JOB
)
833 * Pull the needed attributes from this job...
839 state
= IPP_JOB_PENDING
;
848 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_JOB
)
850 if (strcmp(attr
->name
, "job-id") == 0 &&
851 attr
->value_tag
== IPP_TAG_INTEGER
)
852 id
= attr
->values
[0].integer
;
853 else if (strcmp(attr
->name
, "job-state") == 0 &&
854 attr
->value_tag
== IPP_TAG_ENUM
)
855 state
= (ipp_jstate_t
)attr
->values
[0].integer
;
856 else if (strcmp(attr
->name
, "job-priority") == 0 &&
857 attr
->value_tag
== IPP_TAG_INTEGER
)
858 priority
= attr
->values
[0].integer
;
859 else if (strcmp(attr
->name
, "job-k-octets") == 0 &&
860 attr
->value_tag
== IPP_TAG_INTEGER
)
861 size
= attr
->values
[0].integer
;
862 else if (strcmp(attr
->name
, "time-at-completed") == 0 &&
863 attr
->value_tag
== IPP_TAG_INTEGER
)
864 completed_time
= attr
->values
[0].integer
;
865 else if (strcmp(attr
->name
, "time-at-creation") == 0 &&
866 attr
->value_tag
== IPP_TAG_INTEGER
)
867 creation_time
= attr
->values
[0].integer
;
868 else if (strcmp(attr
->name
, "time-at-processing") == 0 &&
869 attr
->value_tag
== IPP_TAG_INTEGER
)
870 processing_time
= attr
->values
[0].integer
;
871 else if (strcmp(attr
->name
, "job-printer-uri") == 0 &&
872 attr
->value_tag
== IPP_TAG_URI
)
874 if ((dest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
877 else if (strcmp(attr
->name
, "job-originating-user-name") == 0 &&
878 attr
->value_tag
== IPP_TAG_NAME
)
879 user
= attr
->values
[0].string
.text
;
880 else if (strcmp(attr
->name
, "document-format") == 0 &&
881 attr
->value_tag
== IPP_TAG_MIMETYPE
)
882 format
= attr
->values
[0].string
.text
;
883 else if (strcmp(attr
->name
, "job-name") == 0 &&
884 (attr
->value_tag
== IPP_TAG_TEXT
||
885 attr
->value_tag
== IPP_TAG_NAME
))
886 title
= attr
->values
[0].string
.text
;
892 * See if we have everything needed...
895 if (dest
== NULL
|| title
== NULL
|| user
== NULL
|| id
== 0)
904 format
= "application/octet-stream";
907 * Allocate memory for the job...
911 temp
= malloc(sizeof(cups_job_t
));
913 temp
= realloc(*jobs
, sizeof(cups_job_t
) * (n
+ 1));
921 cupsFreeJobs(n
, *jobs
);
933 * Copy the data over...
936 temp
->dest
= strdup(dest
);
937 temp
->user
= strdup(user
);
938 temp
->format
= strdup(format
);
939 temp
->title
= strdup(title
);
941 temp
->priority
= priority
;
944 temp
->completed_time
= completed_time
;
945 temp
->creation_time
= creation_time
;
946 temp
->processing_time
= processing_time
;
955 last_error
= IPP_BAD_REQUEST
;
962 * 'cupsGetPPD()' - Get the PPD file for a printer.
965 const char * /* O - Filename for PPD file */
966 cupsGetPPD(const char *name
) /* I - Printer name */
968 int i
; /* Looping var */
969 ipp_t
*request
, /* IPP request */
970 *response
; /* IPP response */
971 ipp_attribute_t
*attr
; /* Current attribute */
972 cups_lang_t
*language
; /* Local language */
973 int fd
; /* PPD file */
974 int bytes
; /* Number of bytes read */
975 char buffer
[8192]; /* Buffer for file */
976 char printer
[HTTP_MAX_URI
], /* Printer name */
977 method
[HTTP_MAX_URI
], /* Method/scheme name */
978 username
[HTTP_MAX_URI
], /* Username:password */
979 hostname
[HTTP_MAX_URI
], /* Hostname */
980 resource
[HTTP_MAX_URI
]; /* Resource name */
981 int port
; /* Port number */
982 const char *password
; /* Password string */
983 char realm
[HTTP_MAX_VALUE
], /* realm="xyz" string */
984 nonce
[HTTP_MAX_VALUE
], /* nonce="xyz" string */
985 plain
[255], /* Plaintext username:password */
986 encode
[512]; /* Encoded username:password */
987 http_status_t status
; /* HTTP status from server */
988 char prompt
[1024]; /* Prompt string */
989 int digest_tries
; /* Number of tries with Digest */
990 static char filename
[HTTP_MAX_URI
]; /* Local filename */
991 static const char *requested_attrs
[] =/* Requested attributes */
993 "printer-uri-supported",
1001 last_error
= IPP_INTERNAL_ERROR
;
1006 * See if we can connect to the server...
1009 if (!cups_connect(name
, printer
, hostname
))
1011 last_error
= IPP_SERVICE_UNAVAILABLE
;
1016 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1019 * attributes-charset
1020 * attributes-natural-language
1022 * requested-attributes
1027 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
1028 request
->request
.op
.request_id
= 1;
1030 language
= cupsLangDefault();
1032 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1033 "attributes-charset", NULL
, cupsLangEncoding(language
));
1035 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1036 "attributes-natural-language", NULL
, language
->language
);
1038 snprintf(buffer
, sizeof(buffer
), "ipp://localhost/printers/%s", printer
);
1039 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
1040 "printer-uri", NULL
, buffer
);
1042 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1043 "requested-attributes",
1044 sizeof(requested_attrs
) / sizeof(requested_attrs
[0]),
1045 NULL
, requested_attrs
);
1048 * Do the request and get back a response...
1051 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
1053 last_error
= response
->request
.status
.status_code
;
1057 if ((attr
= ippFindAttribute(response
, "member-uris", IPP_TAG_URI
)) != NULL
)
1060 * Get the first actual server and printer name in the class...
1063 for (i
= 0; i
< attr
->num_values
; i
++)
1065 httpSeparate(attr
->values
[0].string
.text
, method
, username
, hostname
,
1067 if (strncmp(resource
, "/printers/", 10) == 0)
1073 strncpy(printer
, resource
+ 10, sizeof(printer
) - 1);
1074 printer
[sizeof(printer
) - 1] = '\0';
1079 else if ((attr
= ippFindAttribute(response
, "printer-uri-supported",
1080 IPP_TAG_URI
)) != NULL
)
1083 * Get the actual server and printer names...
1086 httpSeparate(attr
->values
[0].string
.text
, method
, username
, hostname
,
1088 strncpy(printer
, strrchr(resource
, '/') + 1, sizeof(printer
) - 1);
1089 printer
[sizeof(printer
) - 1] = '\0';
1092 ippDelete(response
);
1095 * Remap local hostname to localhost...
1098 gethostname(buffer
, sizeof(buffer
));
1100 if (strcasecmp(buffer
, hostname
) == 0)
1101 strcpy(hostname
, "localhost");
1104 cupsLangFree(language
);
1110 * Reconnect to the correct server as needed...
1113 if (strcasecmp(cups_server
->hostname
, hostname
) != 0)
1115 httpClose(cups_server
);
1117 if ((cups_server
= httpConnectEncrypt(hostname
, ippPort(),
1118 cupsEncryption())) == NULL
)
1120 last_error
= IPP_SERVICE_UNAVAILABLE
;
1126 * Get a temp file...
1129 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1132 * Can't open file; close the server connection and return NULL...
1135 httpFlush(cups_server
);
1136 httpClose(cups_server
);
1142 * And send a request to the HTTP server...
1145 snprintf(resource
, sizeof(resource
), "/printers/%s.ppd", printer
);
1151 httpClearFields(cups_server
);
1152 httpSetField(cups_server
, HTTP_FIELD_HOST
, hostname
);
1153 httpSetField(cups_server
, HTTP_FIELD_AUTHORIZATION
, authstring
);
1155 if (httpGet(cups_server
, resource
))
1157 if (httpReconnect(cups_server
))
1159 status
= HTTP_ERROR
;
1164 status
= HTTP_UNAUTHORIZED
;
1169 while ((status
= httpUpdate(cups_server
)) == HTTP_CONTINUE
);
1171 if (status
== HTTP_UNAUTHORIZED
)
1173 DEBUG_puts("cupsGetPPD: unauthorized...");
1176 * Flush any error message...
1179 httpFlush(cups_server
);
1182 * See if we can do local authentication...
1185 if (cups_local_auth(cups_server
))
1189 * See if we should retry the current digest password...
1192 if (strncmp(cups_server
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Basic", 5) == 0 ||
1193 digest_tries
> 1 || !pwdstring
[0])
1196 * Nope - get a password from the user...
1199 snprintf(prompt
, sizeof(prompt
), "Password for %s on %s? ", cupsUser(),
1200 cups_server
->hostname
);
1202 if ((password
= cupsGetPassword(prompt
)) == NULL
)
1207 strncpy(pwdstring
, password
, sizeof(pwdstring
) - 1);
1208 pwdstring
[sizeof(pwdstring
) - 1] = '\0';
1216 * Got a password; encode it for the server...
1219 if (strncmp(cups_server
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Basic", 5) == 0)
1222 * Basic authentication...
1225 snprintf(plain
, sizeof(plain
), "%s:%s", cupsUser(), pwdstring
);
1226 httpEncode64(encode
, plain
);
1227 snprintf(authstring
, sizeof(authstring
), "Basic %s", encode
);
1232 * Digest authentication...
1235 httpGetSubField(cups_server
, HTTP_FIELD_WWW_AUTHENTICATE
, "realm", realm
);
1236 httpGetSubField(cups_server
, HTTP_FIELD_WWW_AUTHENTICATE
, "nonce", nonce
);
1238 httpMD5(cupsUser(), realm
, pwdstring
, encode
);
1239 httpMD5Final(nonce
, "GET", resource
, encode
);
1240 snprintf(authstring
, sizeof(authstring
),
1241 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
1242 "response=\"%s\"", cupsUser(), realm
, nonce
, encode
);
1248 else if (status
== HTTP_UPGRADE_REQUIRED
)
1251 * Flush any error message...
1254 httpFlush(cups_server
);
1257 * Upgrade with encryption...
1260 httpEncryption(cups_server
, HTTP_ENCRYPT_REQUIRED
);
1263 * Try again, this time with encryption enabled...
1268 #endif /* HAVE_LIBSSL */
1270 while (status
== HTTP_UNAUTHORIZED
|| status
== HTTP_UPGRADE_REQUIRED
);
1273 * See if we actually got the file or an error...
1276 if (status
!= HTTP_OK
)
1279 httpFlush(cups_server
);
1280 httpClose(cups_server
);
1286 * OK, we need to copy the file...
1289 while ((bytes
= httpRead(cups_server
, buffer
, sizeof(buffer
))) > 0)
1290 write(fd
, buffer
, bytes
);
1299 * 'cupsGetPrinters()' - Get a list of printers.
1302 int /* O - Number of printers */
1303 cupsGetPrinters(char ***printers
) /* O - Printers */
1305 int n
; /* Number of printers */
1306 ipp_t
*request
, /* IPP Request */
1307 *response
; /* IPP Response */
1308 ipp_attribute_t
*attr
; /* Current attribute */
1309 cups_lang_t
*language
; /* Default language */
1310 char **temp
; /* Temporary pointer */
1313 if (printers
== NULL
)
1315 last_error
= IPP_INTERNAL_ERROR
;
1320 * Try to connect to the server...
1323 if (!cups_connect("default", NULL
, NULL
))
1325 last_error
= IPP_SERVICE_UNAVAILABLE
;
1330 * Build a CUPS_GET_PRINTERS request, which requires the following
1333 * attributes-charset
1334 * attributes-natural-language
1335 * requested-attributes
1340 request
->request
.op
.operation_id
= CUPS_GET_PRINTERS
;
1341 request
->request
.op
.request_id
= 1;
1343 language
= cupsLangDefault();
1345 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1346 "attributes-charset", NULL
, cupsLangEncoding(language
));
1348 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1349 "attributes-natural-language", NULL
, language
->language
);
1351 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1352 "requested-attributes", NULL
, "printer-name");
1355 * Do the request and get back a response...
1361 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
1363 last_error
= response
->request
.status
.status_code
;
1365 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1366 if (attr
->name
!= NULL
&&
1367 strcasecmp(attr
->name
, "printer-name") == 0 &&
1368 attr
->value_tag
== IPP_TAG_NAME
)
1371 temp
= malloc(sizeof(char *));
1373 temp
= realloc(*printers
, sizeof(char *) * (n
+ 1));
1378 * Ran out of memory!
1384 free((*printers
)[n
]);
1388 ippDelete(response
);
1393 temp
[n
] = strdup(attr
->values
[0].string
.text
);
1397 ippDelete(response
);
1400 last_error
= IPP_BAD_REQUEST
;
1407 * 'cupsLastError()' - Return the last IPP error that occurred.
1410 ipp_status_t
/* O - IPP error code */
1413 return (last_error
);
1418 * 'cupsPrintFile()' - Print a file to a printer or class.
1421 int /* O - Job ID */
1422 cupsPrintFile(const char *name
, /* I - Printer or class name */
1423 const char *filename
, /* I - File to print */
1424 const char *title
, /* I - Title of job */
1425 int num_options
,/* I - Number of options */
1426 cups_option_t
*options
) /* I - Options */
1428 DEBUG_printf(("cupsPrintFile(\'%s\', \'%s\', %d, %p)\n",
1429 name
, filename
, num_options
, options
));
1431 return (cupsPrintFiles(name
, 1, &filename
, title
, num_options
, options
));
1436 * 'cupsPrintFiles()' - Print one or more files to a printer or class.
1439 int /* O - Job ID */
1440 cupsPrintFiles(const char *name
, /* I - Printer or class name */
1441 int num_files
, /* I - Number of files */
1442 const char **files
, /* I - File(s) to print */
1443 const char *title
, /* I - Title of job */
1444 int num_options
,/* I - Number of options */
1445 cups_option_t
*options
) /* I - Options */
1447 int i
; /* Looping var */
1448 const char *val
; /* Pointer to option value */
1449 ipp_t
*request
; /* IPP request */
1450 ipp_t
*response
; /* IPP response */
1451 ipp_attribute_t
*attr
; /* IPP job-id attribute */
1452 char hostname
[HTTP_MAX_URI
], /* Hostname */
1453 printer
[HTTP_MAX_URI
], /* Printer or class name */
1454 uri
[HTTP_MAX_URI
]; /* Printer URI */
1455 cups_lang_t
*language
; /* Language to use */
1456 int jobid
; /* New job ID */
1459 DEBUG_printf(("cupsPrintFiles(\'%s\', %d, %p, %d, %p)\n",
1460 name
, num_files
, files
, num_options
, options
));
1462 if (name
== NULL
|| num_files
< 1 || files
== NULL
)
1466 * Setup a connection and request data...
1469 if (!cups_connect(name
, printer
, hostname
))
1471 DEBUG_printf(("cupsPrintFile: Unable to open connection - %s.\n",
1473 last_error
= IPP_SERVICE_UNAVAILABLE
;
1477 language
= cupsLangDefault();
1480 * Build a standard CUPS URI for the printer and fill the standard IPP
1484 if ((request
= ippNew()) == NULL
)
1487 request
->request
.op
.operation_id
= num_files
== 1 ? IPP_PRINT_JOB
:
1489 request
->request
.op
.request_id
= 1;
1491 snprintf(uri
, sizeof(uri
), "ipp://%s:%d/printers/%s", hostname
, ippPort(), printer
);
1493 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1494 "attributes-charset", NULL
, cupsLangEncoding(language
));
1496 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1497 "attributes-natural-language", NULL
,
1498 language
!= NULL
? language
->language
: "C");
1500 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1503 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1507 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
, title
);
1510 * Then add all options...
1513 cupsEncodeOptions(request
, num_options
, options
);
1519 snprintf(uri
, sizeof(uri
), "/printers/%s", printer
);
1522 response
= cupsDoFileRequest(cups_server
, request
, uri
, *files
);
1524 response
= cupsDoRequest(cups_server
, request
, uri
);
1526 if (response
== NULL
)
1528 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1530 DEBUG_printf(("IPP response code was 0x%x!\n",
1531 response
->request
.status
.status_code
));
1534 else if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1536 DEBUG_puts("No job ID!");
1540 jobid
= attr
->values
[0].integer
;
1542 if (response
!= NULL
)
1543 ippDelete(response
);
1546 * Handle multiple file jobs if the create-job operation worked...
1549 if (jobid
> 0 && num_files
> 1)
1550 for (i
= 0; i
< num_files
; i
++)
1553 * Build a standard CUPS URI for the job and fill the standard IPP
1557 if ((request
= ippNew()) == NULL
)
1560 request
->request
.op
.operation_id
= IPP_SEND_DOCUMENT
;
1561 request
->request
.op
.request_id
= 1;
1563 snprintf(uri
, sizeof(uri
), "ipp://%s:%d/jobs/%d", hostname
, ippPort(),
1566 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1567 "attributes-charset", NULL
, cupsLangEncoding(language
));
1569 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1570 "attributes-natural-language", NULL
,
1571 language
!= NULL
? language
->language
: "C");
1573 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri",
1577 * Handle raw print files...
1580 if (cupsGetOption("raw", num_options
, options
))
1581 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
1582 NULL
, "application/vnd.cups-raw");
1583 else if ((val
= cupsGetOption("document-format", num_options
, options
)) != NULL
)
1584 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
1587 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
1588 NULL
, "application/octet-stream");
1590 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1594 * Is this the last document?
1597 if (i
== (num_files
- 1))
1598 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1604 snprintf(uri
, sizeof(uri
), "/printers/%s", printer
);
1606 if ((response
= cupsDoFileRequest(cups_server
, request
, uri
,
1608 ippDelete(response
);
1616 * 'cups_connect()' - Connect to the specified host...
1619 static char * /* I - Printer name or NULL */
1620 cups_connect(const char *name
, /* I - Destination (printer[@host]) */
1621 char *printer
, /* O - Printer name [HTTP_MAX_URI] */
1622 char *hostname
) /* O - Hostname [HTTP_MAX_URI] */
1624 char hostbuf
[HTTP_MAX_URI
]; /* Name of host */
1625 static char printerbuf
[HTTP_MAX_URI
];
1626 /* Name of printer or class */
1629 DEBUG_printf(("cups_connect(\"%s\", %p, %p)\n", name
, printer
, hostname
));
1633 last_error
= IPP_BAD_REQUEST
;
1637 if (sscanf(name
, "%1023[^@]@%1023s", printerbuf
, hostbuf
) == 1)
1639 strncpy(hostbuf
, cupsServer(), sizeof(hostbuf
) - 1);
1640 hostbuf
[sizeof(hostbuf
) - 1] = '\0';
1643 if (hostname
!= NULL
)
1645 strncpy(hostname
, hostbuf
, HTTP_MAX_URI
- 1);
1646 hostname
[HTTP_MAX_URI
- 1] = '\0';
1651 if (printer
!= NULL
)
1653 strncpy(printer
, printerbuf
, HTTP_MAX_URI
- 1);
1654 printer
[HTTP_MAX_URI
- 1] = '\0';
1657 printer
= printerbuf
;
1659 if (cups_server
!= NULL
)
1661 if (strcasecmp(cups_server
->hostname
, hostname
) == 0)
1664 httpClose(cups_server
);
1667 DEBUG_printf(("connecting to %s on port %d...\n", hostname
, ippPort()));
1669 if ((cups_server
= httpConnectEncrypt(hostname
, ippPort(),
1670 cupsEncryption())) == NULL
)
1672 last_error
= IPP_SERVICE_UNAVAILABLE
;
1681 * 'cups_local_auth()' - Get the local authorization certificate if
1682 * available/applicable...
1685 static int /* O - 1 if available, 0 if not */
1686 cups_local_auth(http_t
*http
) /* I - Connection */
1688 #if defined(WIN32) || defined(__EMX__)
1690 * Currently WIN32 and OS-2 do not support the CUPS server...
1695 int pid
; /* Current process ID */
1696 FILE *fp
; /* Certificate file */
1697 char filename
[1024], /* Certificate filename */
1698 certificate
[33];/* Certificate string */
1699 const char *root
; /* Server root directory */
1703 * See if we are accessing localhost...
1706 if (ntohl(http
->hostaddr
.sin_addr
.s_addr
) != 0x7f000001 &&
1707 strcasecmp(http
->hostname
, "localhost") != 0)
1711 * Try opening a certificate file for this PID. If that fails,
1712 * try the root certificate...
1715 if ((root
= getenv("CUPS_SERVERROOT")) == NULL
)
1716 root
= CUPS_SERVERROOT
;
1719 snprintf(filename
, sizeof(filename
), "%s/certs/%d", root
, pid
);
1720 if ((fp
= fopen(filename
, "r")) == NULL
&& pid
> 0)
1722 snprintf(filename
, sizeof(filename
), "%s/certs/0", root
);
1723 fp
= fopen(filename
, "r");
1730 * Read the certificate from the file...
1733 fgets(certificate
, sizeof(certificate
), fp
);
1737 * Set the authorization string and return...
1740 snprintf(authstring
, sizeof(authstring
), "Local %s", certificate
);
1743 #endif /* WIN32 || __EMX__ */
1748 * End of "$Id: util.c,v 1.93 2002/03/01 19:53:30 mike Exp $".