2 * "$Id: util.c,v 1.81.2.3 2001/12/26 16:52:13 mike Exp $"
4 * Printing utilities for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2001 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
26 * cupsCancelJob() - Cancel a print job.
27 * cupsDoFileRequest() - Do an IPP request...
28 * cupsFreeJobs() - Free memory used by job data.
29 * cupsGetClasses() - Get a list of printer classes.
30 * cupsGetDefault() - Get the default printer or class.
31 * cupsGetJobs() - Get the jobs from the server.
32 * cupsGetPPD() - Get the PPD file for a printer.
33 * cupsGetPrinters() - Get a list of printers.
34 * cupsLastError() - Return the last IPP error that occurred.
35 * cupsPrintFile() - Print a file to a printer or class.
36 * cupsPrintFiles() - Print one or more files to a printer or class.
37 * cups_connect() - Connect to the specified host...
38 * cups_local_auth() - Get the local authorization certificate if
39 * available/applicable...
43 * Include necessary headers...
56 #if defined(WIN32) || defined(__EMX__)
60 #endif /* WIN32 || __EMX__ */
67 static http_t
*cups_server
= NULL
; /* Current server connection */
68 static ipp_status_t last_error
= IPP_OK
; /* Last IPP error */
69 static char authstring
[1024] = ""; /* Authorization string */
76 static char *cups_connect(const char *name
, char *printer
, char *hostname
);
77 static int cups_local_auth(http_t
*http
);
81 * 'cupsCancelJob()' - Cancel a print job.
84 int /* O - 1 on success, 0 on failure */
85 cupsCancelJob(const char *name
, /* I - Name of printer or class */
86 int job
) /* I - Job ID */
88 char printer
[HTTP_MAX_URI
], /* Printer name */
89 hostname
[HTTP_MAX_URI
], /* Hostname */
90 uri
[HTTP_MAX_URI
]; /* Printer URI */
91 ipp_t
*request
, /* IPP request */
92 *response
; /* IPP response */
93 cups_lang_t
*language
; /* Language info */
97 * See if we can connect to the server...
100 if (!cups_connect(name
, printer
, hostname
))
102 last_error
= IPP_SERVICE_UNAVAILABLE
;
107 * Build an IPP_CANCEL_JOB request, which requires the following
111 * attributes-natural-language
114 * [requesting-user-name]
119 request
->request
.op
.operation_id
= IPP_CANCEL_JOB
;
120 request
->request
.op
.request_id
= 1;
122 language
= cupsLangDefault();
124 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
125 "attributes-charset", NULL
, cupsLangEncoding(language
));
127 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
128 "attributes-natural-language", NULL
,
129 language
!= NULL
? language
->language
: "C");
131 snprintf(uri
, sizeof(uri
), "ipp://%s:%d/printers/%s", hostname
, ippPort(), printer
);
132 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
135 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", job
);
137 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
144 if ((response
= cupsDoRequest(cups_server
, request
, "/jobs/")) == NULL
)
146 last_error
= IPP_BAD_REQUEST
;
151 last_error
= response
->request
.status
.status_code
;
160 * 'cupsDoFileRequest()' - Do an IPP request...
163 ipp_t
* /* O - Response data */
164 cupsDoFileRequest(http_t
*http
, /* I - HTTP connection to server */
165 ipp_t
*request
, /* I - IPP request */
166 const char *resource
, /* I - HTTP resource for POST */
167 const char *filename
) /* I - File to send or NULL */
169 ipp_t
*response
; /* IPP response data */
170 char length
[255]; /* Content-Length field */
171 http_status_t status
; /* Status of HTTP request */
172 FILE *file
; /* File to send */
173 struct stat fileinfo
; /* File information */
174 int bytes
; /* Number of bytes read/written */
175 char buffer
[32768]; /* Output buffer */
176 const char *password
; /* Password string */
177 char realm
[HTTP_MAX_VALUE
], /* realm="xyz" string */
178 nonce
[HTTP_MAX_VALUE
], /* nonce="xyz" string */
179 plain
[255], /* Plaintext username:password */
180 encode
[512]; /* Encoded username:password */
181 char prompt
[1024]; /* Prompt string */
184 if (http
== NULL
|| request
== NULL
|| resource
== NULL
)
189 last_error
= IPP_INTERNAL_ERROR
;
193 DEBUG_printf(("cupsDoFileRequest(%p, %08x, \'%s\', \'%s\')\n",
194 http
, request
, resource
, filename
? filename
: "(null)"));
197 * See if we have a file to send...
200 if (filename
!= NULL
)
202 if (stat(filename
, &fileinfo
))
205 * Can't get file information!
209 last_error
= IPP_NOT_FOUND
;
213 if ((file
= fopen(filename
, "rb")) == NULL
)
220 last_error
= IPP_NOT_FOUND
;
228 * Loop until we can send the request without authorization problems.
234 while (response
== NULL
)
236 DEBUG_puts("cupsDoFileRequest: setup...");
239 * Setup the HTTP variables needed...
242 if (filename
!= NULL
)
243 sprintf(length
, "%lu", (unsigned long)(ippLength(request
) +
244 (size_t)fileinfo
.st_size
));
246 sprintf(length
, "%lu", (unsigned long)ippLength(request
));
248 httpClearFields(http
);
249 httpSetField(http
, HTTP_FIELD_CONTENT_LENGTH
, length
);
250 httpSetField(http
, HTTP_FIELD_CONTENT_TYPE
, "application/ipp");
251 httpSetField(http
, HTTP_FIELD_AUTHORIZATION
, authstring
);
257 DEBUG_puts("cupsDoFileRequest: post...");
259 if (httpPost(http
, resource
))
261 if (httpReconnect(http
))
271 * Send the IPP data and wait for the response...
274 DEBUG_puts("cupsDoFileRequest: ipp write...");
276 request
->state
= IPP_IDLE
;
277 if (ippWrite(http
, request
) != IPP_ERROR
)
278 if (filename
!= NULL
)
280 DEBUG_puts("cupsDoFileRequest: file write...");
288 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), file
)) > 0)
289 if (httpWrite(http
, buffer
, bytes
) < bytes
)
294 * Get the server's return status...
297 DEBUG_puts("cupsDoFileRequest: update...");
299 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
301 if (status
== HTTP_UNAUTHORIZED
)
303 DEBUG_puts("cupsDoFileRequest: unauthorized...");
306 * Flush any error message...
312 * See if we can do local authentication...
315 if (cups_local_auth(http
))
319 * Nope - get a password from the user...
322 snprintf(prompt
, sizeof(prompt
), "Password for %s on %s? ", cupsUser(),
325 if ((password
= cupsGetPassword(prompt
)) != NULL
)
328 * Got a password; send it to the server...
334 if (strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Basic", 5) == 0)
337 * Basic authentication...
340 snprintf(plain
, sizeof(plain
), "%s:%s", cupsUser(), password
);
341 httpEncode64(encode
, plain
);
342 snprintf(authstring
, sizeof(authstring
), "Basic %s", encode
);
347 * Digest authentication...
350 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
, "realm", realm
);
351 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
, "nonce", nonce
);
353 httpMD5(cupsUser(), realm
, password
, encode
);
354 httpMD5Final(nonce
, "POST", resource
, encode
);
355 snprintf(authstring
, sizeof(authstring
),
356 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
357 "response=\"%s\"", cupsUser(), realm
, nonce
, encode
);
364 else if (status
== HTTP_ERROR
)
366 #if defined(WIN32) || defined(__EMX__)
367 if (http
->error
!= WSAENETDOWN
&& http
->error
!= WSAENETUNREACH
)
369 if (http
->error
!= ENETDOWN
&& http
->error
!= ENETUNREACH
)
370 #endif /* WIN32 || __EMX__ */
376 else if (status
== HTTP_UPGRADE_REQUIRED
)
379 * Flush any error message...
385 * Try again, this time with encryption enabled...
390 #endif /* HAVE_LIBSSL */
391 else if (status
!= HTTP_OK
)
393 DEBUG_printf(("cupsDoFileRequest: error %d...\n", status
));
396 * Flush any error message...
405 * Read the response...
408 DEBUG_puts("cupsDoFileRequest: response...");
412 if (ippRead(http
, response
) == IPP_ERROR
)
415 * Delete the response...
421 last_error
= IPP_SERVICE_UNAVAILABLE
;
428 * Close the file if needed...
431 if (filename
!= NULL
)
435 * Flush any remaining data...
441 * Delete the original request and return the response...
447 last_error
= response
->request
.status
.status_code
;
448 else if (status
== HTTP_NOT_FOUND
)
449 last_error
= IPP_NOT_FOUND
;
450 else if (status
== HTTP_UNAUTHORIZED
)
451 last_error
= IPP_NOT_AUTHORIZED
;
452 else if (status
!= HTTP_OK
)
453 last_error
= IPP_SERVICE_UNAVAILABLE
;
460 * 'cupsFreeJobs()' - Free memory used by job data.
464 cupsFreeJobs(int num_jobs
,/* I - Number of jobs */
465 cups_job_t
*jobs
) /* I - Jobs */
467 int i
; /* Looping var */
470 if (num_jobs
<= 0 || jobs
== NULL
)
473 for (i
= 0; i
< num_jobs
; i
++)
477 free(jobs
[i
].format
);
486 * 'cupsGetClasses()' - Get a list of printer classes.
489 int /* O - Number of classes */
490 cupsGetClasses(char ***classes
) /* O - Classes */
492 int n
; /* Number of classes */
493 ipp_t
*request
, /* IPP Request */
494 *response
; /* IPP Response */
495 ipp_attribute_t
*attr
; /* Current attribute */
496 cups_lang_t
*language
; /* Default language */
497 char **temp
; /* Temporary pointer */
502 last_error
= IPP_INTERNAL_ERROR
;
507 * Try to connect to the server...
510 if (!cups_connect("default", NULL
, NULL
))
512 last_error
= IPP_SERVICE_UNAVAILABLE
;
517 * Build a CUPS_GET_CLASSES request, which requires the following
521 * attributes-natural-language
522 * requested-attributes
527 request
->request
.op
.operation_id
= CUPS_GET_CLASSES
;
528 request
->request
.op
.request_id
= 1;
530 language
= cupsLangDefault();
532 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
533 "attributes-charset", NULL
, cupsLangEncoding(language
));
535 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
536 "attributes-natural-language", NULL
, language
->language
);
538 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
539 "requested-attributes", NULL
, "printer-name");
542 * Do the request and get back a response...
548 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
550 last_error
= response
->request
.status
.status_code
;
552 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
553 if (attr
->name
!= NULL
&&
554 strcasecmp(attr
->name
, "printer-name") == 0 &&
555 attr
->value_tag
== IPP_TAG_NAME
)
558 temp
= malloc(sizeof(char *));
560 temp
= realloc(*classes
, sizeof(char *) * (n
+ 1));
580 temp
[n
] = strdup(attr
->values
[0].string
.text
);
587 last_error
= IPP_BAD_REQUEST
;
594 * 'cupsGetDefault()' - Get the default printer or class.
597 const char * /* O - Default printer or NULL */
600 ipp_t
*request
, /* IPP Request */
601 *response
; /* IPP Response */
602 ipp_attribute_t
*attr
; /* Current attribute */
603 cups_lang_t
*language
; /* Default language */
604 const char *var
; /* Environment variable */
605 static char def_printer
[256];/* Default printer */
609 * First see if the LPDEST or PRINTER environment variables are
610 * set... However, if PRINTER is set to "lp", ignore it to work
611 * around a "feature" in most Linux distributions - the default
612 * user login scripts set PRINTER to "lp"...
615 if ((var
= getenv("LPDEST")) != NULL
)
617 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
621 * Try to connect to the server...
624 if (!cups_connect("default", NULL
, NULL
))
626 last_error
= IPP_SERVICE_UNAVAILABLE
;
631 * Build a CUPS_GET_DEFAULT request, which requires the following
635 * attributes-natural-language
640 request
->request
.op
.operation_id
= CUPS_GET_DEFAULT
;
641 request
->request
.op
.request_id
= 1;
643 language
= cupsLangDefault();
645 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
646 "attributes-charset", NULL
, cupsLangEncoding(language
));
648 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
649 "attributes-natural-language", NULL
, language
->language
);
652 * Do the request and get back a response...
655 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
657 last_error
= response
->request
.status
.status_code
;
659 if ((attr
= ippFindAttribute(response
, "printer-name", IPP_TAG_NAME
)) != NULL
)
661 strncpy(def_printer
, attr
->values
[0].string
.text
, sizeof(def_printer
) - 1);
662 def_printer
[sizeof(def_printer
) - 1] = '\0';
664 return (def_printer
);
670 last_error
= IPP_BAD_REQUEST
;
677 * 'cupsGetJobs()' - Get the jobs from the server.
680 int /* O - Number of jobs */
681 cupsGetJobs(cups_job_t
**jobs
, /* O - Job data */
682 const char *mydest
, /* I - Only show jobs for dest? */
683 int myjobs
, /* I - Only show my jobs? */
684 int completed
) /* I - Only show completed jobs? */
686 int n
; /* Number of jobs */
687 ipp_t
*request
, /* IPP Request */
688 *response
; /* IPP Response */
689 ipp_attribute_t
*attr
; /* Current attribute */
690 cups_lang_t
*language
; /* Default language */
691 cups_job_t
*temp
; /* Temporary pointer */
693 priority
, /* job-priority */
694 size
; /* job-k-octets */
695 ipp_jstate_t state
; /* job-state */
696 time_t completed_time
, /* time-at-completed */
697 creation_time
, /* time-at-creation */
698 processing_time
; /* time-at-processing */
699 const char *dest
, /* job-printer-uri */
700 *format
, /* document-format */
701 *title
, /* job-name */
702 *user
; /* job-originating-user-name */
703 char uri
[HTTP_MAX_URI
]; /* URI for jobs */
704 static const char *attrs
[] = /* Requested attributes */
712 "time-at-processing",
716 "job-originating-user-name"
722 last_error
= IPP_INTERNAL_ERROR
;
727 * Try to connect to the server...
730 if (!cups_connect("default", NULL
, NULL
))
732 last_error
= IPP_SERVICE_UNAVAILABLE
;
737 * Build an IPP_GET_JOBS request, which requires the following
741 * attributes-natural-language
743 * requesting-user-name
746 * requested-attributes
751 request
->request
.op
.operation_id
= IPP_GET_JOBS
;
752 request
->request
.op
.request_id
= 1;
754 language
= cupsLangDefault();
756 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
757 "attributes-charset", NULL
, cupsLangEncoding(language
));
759 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
760 "attributes-natural-language", NULL
, language
->language
);
763 snprintf(uri
, sizeof(uri
), "ipp://localhost/printers/%s", mydest
);
765 strcpy(uri
, "ipp://localhost/jobs");
767 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
768 "printer-uri", NULL
, uri
);
770 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
771 "requesting-user-name", NULL
, cupsUser());
774 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
777 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
778 "which-jobs", NULL
, "completed");
780 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
781 "requested-attributes", sizeof(attrs
) / sizeof(attrs
[0]),
785 * Do the request and get back a response...
791 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
793 last_error
= response
->request
.status
.status_code
;
795 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
798 * Skip leading attributes until we hit a job...
801 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_JOB
)
808 * Pull the needed attributes from this job...
814 state
= IPP_JOB_PENDING
;
823 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_JOB
)
825 if (strcmp(attr
->name
, "job-id") == 0 &&
826 attr
->value_tag
== IPP_TAG_INTEGER
)
827 id
= attr
->values
[0].integer
;
828 else if (strcmp(attr
->name
, "job-state") == 0 &&
829 attr
->value_tag
== IPP_TAG_ENUM
)
830 state
= (ipp_jstate_t
)attr
->values
[0].integer
;
831 else if (strcmp(attr
->name
, "job-priority") == 0 &&
832 attr
->value_tag
== IPP_TAG_INTEGER
)
833 priority
= attr
->values
[0].integer
;
834 else if (strcmp(attr
->name
, "job-k-octets") == 0 &&
835 attr
->value_tag
== IPP_TAG_INTEGER
)
836 size
= attr
->values
[0].integer
;
837 else if (strcmp(attr
->name
, "time-at-completed") == 0 &&
838 attr
->value_tag
== IPP_TAG_INTEGER
)
839 completed_time
= attr
->values
[0].integer
;
840 else if (strcmp(attr
->name
, "time-at-creation") == 0 &&
841 attr
->value_tag
== IPP_TAG_INTEGER
)
842 creation_time
= attr
->values
[0].integer
;
843 else if (strcmp(attr
->name
, "time-at-processing") == 0 &&
844 attr
->value_tag
== IPP_TAG_INTEGER
)
845 processing_time
= attr
->values
[0].integer
;
846 else if (strcmp(attr
->name
, "job-printer-uri") == 0 &&
847 attr
->value_tag
== IPP_TAG_URI
)
849 if ((dest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
852 else if (strcmp(attr
->name
, "job-originating-user-name") == 0 &&
853 attr
->value_tag
== IPP_TAG_NAME
)
854 user
= attr
->values
[0].string
.text
;
855 else if (strcmp(attr
->name
, "document-format") == 0 &&
856 attr
->value_tag
== IPP_TAG_MIMETYPE
)
857 format
= attr
->values
[0].string
.text
;
858 else if (strcmp(attr
->name
, "job-name") == 0 &&
859 attr
->value_tag
== IPP_TAG_TEXT
)
860 title
= attr
->values
[0].string
.text
;
866 * See if we have everything needed...
869 if (dest
== NULL
|| format
== NULL
|| title
== NULL
|| user
== NULL
||
879 * Allocate memory for the job...
883 temp
= malloc(sizeof(cups_job_t
));
885 temp
= realloc(*jobs
, sizeof(cups_job_t
) * (n
+ 1));
893 cupsFreeJobs(n
, *jobs
);
905 * Copy the data over...
908 temp
->dest
= strdup(dest
);
909 temp
->user
= strdup(user
);
910 temp
->format
= strdup(format
);
911 temp
->title
= strdup(title
);
913 temp
->priority
= priority
;
916 temp
->completed_time
= completed_time
;
917 temp
->creation_time
= creation_time
;
918 temp
->processing_time
= processing_time
;
924 last_error
= IPP_BAD_REQUEST
;
931 * 'cupsGetPPD()' - Get the PPD file for a printer.
934 const char * /* O - Filename for PPD file */
935 cupsGetPPD(const char *name
) /* I - Printer name */
937 int i
; /* Looping var */
938 ipp_t
*request
, /* IPP request */
939 *response
; /* IPP response */
940 ipp_attribute_t
*attr
; /* Current attribute */
941 cups_lang_t
*language
; /* Local language */
942 int fd
; /* PPD file */
943 int bytes
; /* Number of bytes read */
944 char buffer
[8192]; /* Buffer for file */
945 char printer
[HTTP_MAX_URI
], /* Printer name */
946 method
[HTTP_MAX_URI
], /* Method/scheme name */
947 username
[HTTP_MAX_URI
], /* Username:password */
948 hostname
[HTTP_MAX_URI
], /* Hostname */
949 resource
[HTTP_MAX_URI
]; /* Resource name */
950 int port
; /* Port number */
951 const char *password
; /* Password string */
952 char realm
[HTTP_MAX_VALUE
], /* realm="xyz" string */
953 nonce
[HTTP_MAX_VALUE
], /* nonce="xyz" string */
954 plain
[255], /* Plaintext username:password */
955 encode
[512]; /* Encoded username:password */
956 http_status_t status
; /* HTTP status from server */
957 char prompt
[1024]; /* Prompt string */
958 static char filename
[HTTP_MAX_URI
]; /* Local filename */
959 static const char *requested_attrs
[] =/* Requested attributes */
961 "printer-uri-supported",
969 last_error
= IPP_INTERNAL_ERROR
;
974 * See if we can connect to the server...
977 if (!cups_connect(name
, printer
, hostname
))
979 last_error
= IPP_SERVICE_UNAVAILABLE
;
984 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
988 * attributes-natural-language
990 * requested-attributes
995 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
996 request
->request
.op
.request_id
= 1;
998 language
= cupsLangDefault();
1000 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1001 "attributes-charset", NULL
, cupsLangEncoding(language
));
1003 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1004 "attributes-natural-language", NULL
, language
->language
);
1006 snprintf(buffer
, sizeof(buffer
), "ipp://localhost/printers/%s", name
);
1007 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
1008 "printer-uri", NULL
, buffer
);
1010 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1011 "requested-attributes",
1012 sizeof(requested_attrs
) / sizeof(requested_attrs
[0]),
1013 NULL
, requested_attrs
);
1016 * Do the request and get back a response...
1019 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
1021 last_error
= response
->request
.status
.status_code
;
1025 if ((attr
= ippFindAttribute(response
, "member-uris", IPP_TAG_URI
)) != NULL
)
1028 * Get the first actual server and printer name in the class...
1031 for (i
= 0; i
< attr
->num_values
; i
++)
1033 httpSeparate(attr
->values
[0].string
.text
, method
, username
, hostname
,
1035 if (strncmp(resource
, "/printers/", 10) == 0)
1041 strncpy(printer
, resource
+ 10, sizeof(printer
) - 1);
1042 printer
[sizeof(printer
) - 1] = '\0';
1047 else if ((attr
= ippFindAttribute(response
, "printer-uri-supported",
1048 IPP_TAG_URI
)) != NULL
)
1051 * Get the actual server and printer names...
1054 httpSeparate(attr
->values
[0].string
.text
, method
, username
, hostname
,
1056 strncpy(printer
, strrchr(resource
, '/') + 1, sizeof(printer
) - 1);
1057 printer
[sizeof(printer
) - 1] = '\0';
1060 ippDelete(response
);
1063 * Remap local hostname to localhost...
1066 gethostname(buffer
, sizeof(buffer
));
1068 if (strcasecmp(buffer
, hostname
) == 0)
1069 strcpy(hostname
, "localhost");
1072 cupsLangFree(language
);
1078 * Reconnect to the correct server as needed...
1081 if (strcasecmp(cups_server
->hostname
, hostname
) != 0)
1083 httpClose(cups_server
);
1085 if ((cups_server
= httpConnectEncrypt(hostname
, ippPort(),
1086 cupsEncryption())) == NULL
)
1088 last_error
= IPP_SERVICE_UNAVAILABLE
;
1094 * Get a temp file...
1097 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1100 * Can't open file; close the server connection and return NULL...
1103 httpFlush(cups_server
);
1104 httpClose(cups_server
);
1110 * And send a request to the HTTP server...
1113 snprintf(resource
, sizeof(resource
), "/printers/%s.ppd", printer
);
1117 httpClearFields(cups_server
);
1118 httpSetField(cups_server
, HTTP_FIELD_HOST
, hostname
);
1119 httpSetField(cups_server
, HTTP_FIELD_AUTHORIZATION
, authstring
);
1121 if (httpGet(cups_server
, resource
))
1123 if (httpReconnect(cups_server
))
1125 status
= HTTP_ERROR
;
1130 status
= HTTP_UNAUTHORIZED
;
1135 while ((status
= httpUpdate(cups_server
)) == HTTP_CONTINUE
);
1137 if (status
== HTTP_UNAUTHORIZED
)
1139 DEBUG_puts("cupsGetPPD: unauthorized...");
1142 * Flush any error message...
1145 httpFlush(cups_server
);
1148 * See if we can do local authentication...
1151 if (cups_local_auth(cups_server
))
1155 * Nope, get a password from the user...
1158 snprintf(prompt
, sizeof(prompt
), "Password for %s on %s? ", cupsUser(),
1159 cups_server
->hostname
);
1161 if ((password
= cupsGetPassword(prompt
)) != NULL
)
1164 * Got a password; send it to the server...
1170 if (strncmp(cups_server
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Basic", 5) == 0)
1173 * Basic authentication...
1176 snprintf(plain
, sizeof(plain
), "%s:%s", cupsUser(), password
);
1177 httpEncode64(encode
, plain
);
1178 snprintf(authstring
, sizeof(authstring
), "Basic %s", encode
);
1183 * Digest authentication...
1186 httpGetSubField(cups_server
, HTTP_FIELD_WWW_AUTHENTICATE
, "realm", realm
);
1187 httpGetSubField(cups_server
, HTTP_FIELD_WWW_AUTHENTICATE
, "nonce", nonce
);
1189 httpMD5(cupsUser(), realm
, password
, encode
);
1190 httpMD5Final(nonce
, "GET", resource
, encode
);
1191 snprintf(authstring
, sizeof(authstring
),
1192 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
1193 "response=\"%s\"", cupsUser(), realm
, nonce
, encode
);
1201 while (status
== HTTP_UNAUTHORIZED
|| status
== HTTP_UPGRADE_REQUIRED
);
1204 * See if we actually got the file or an error...
1207 if (status
!= HTTP_OK
)
1210 httpFlush(cups_server
);
1211 httpClose(cups_server
);
1217 * OK, we need to copy the file...
1220 while ((bytes
= httpRead(cups_server
, buffer
, sizeof(buffer
))) > 0)
1221 write(fd
, buffer
, bytes
);
1230 * 'cupsGetPrinters()' - Get a list of printers.
1233 int /* O - Number of printers */
1234 cupsGetPrinters(char ***printers
) /* O - Printers */
1236 int n
; /* Number of printers */
1237 ipp_t
*request
, /* IPP Request */
1238 *response
; /* IPP Response */
1239 ipp_attribute_t
*attr
; /* Current attribute */
1240 cups_lang_t
*language
; /* Default language */
1241 char **temp
; /* Temporary pointer */
1244 if (printers
== NULL
)
1246 last_error
= IPP_INTERNAL_ERROR
;
1251 * Try to connect to the server...
1254 if (!cups_connect("default", NULL
, NULL
))
1256 last_error
= IPP_SERVICE_UNAVAILABLE
;
1261 * Build a CUPS_GET_PRINTERS request, which requires the following
1264 * attributes-charset
1265 * attributes-natural-language
1266 * requested-attributes
1271 request
->request
.op
.operation_id
= CUPS_GET_PRINTERS
;
1272 request
->request
.op
.request_id
= 1;
1274 language
= cupsLangDefault();
1276 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1277 "attributes-charset", NULL
, cupsLangEncoding(language
));
1279 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1280 "attributes-natural-language", NULL
, language
->language
);
1282 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1283 "requested-attributes", NULL
, "printer-name");
1286 * Do the request and get back a response...
1292 if ((response
= cupsDoRequest(cups_server
, request
, "/")) != NULL
)
1294 last_error
= response
->request
.status
.status_code
;
1296 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1297 if (attr
->name
!= NULL
&&
1298 strcasecmp(attr
->name
, "printer-name") == 0 &&
1299 attr
->value_tag
== IPP_TAG_NAME
)
1302 temp
= malloc(sizeof(char *));
1304 temp
= realloc(*printers
, sizeof(char *) * (n
+ 1));
1309 * Ran out of memory!
1315 free((*printers
)[n
]);
1319 ippDelete(response
);
1324 temp
[n
] = strdup(attr
->values
[0].string
.text
);
1328 ippDelete(response
);
1331 last_error
= IPP_BAD_REQUEST
;
1338 * 'cupsLastError()' - Return the last IPP error that occurred.
1341 ipp_status_t
/* O - IPP error code */
1344 return (last_error
);
1349 * 'cupsPrintFile()' - Print a file to a printer or class.
1352 int /* O - Job ID */
1353 cupsPrintFile(const char *name
, /* I - Printer or class name */
1354 const char *filename
, /* I - File to print */
1355 const char *title
, /* I - Title of job */
1356 int num_options
,/* I - Number of options */
1357 cups_option_t
*options
) /* I - Options */
1359 DEBUG_printf(("cupsPrintFile(\'%s\', \'%s\', %d, %p)\n",
1360 name
, filename
, num_options
, options
));
1362 return (cupsPrintFiles(name
, 1, &filename
, title
, num_options
, options
));
1367 * 'cupsPrintFiles()' - Print one or more files to a printer or class.
1370 int /* O - Job ID */
1371 cupsPrintFiles(const char *name
, /* I - Printer or class name */
1372 int num_files
, /* I - Number of files */
1373 const char **files
, /* I - File(s) to print */
1374 const char *title
, /* I - Title of job */
1375 int num_options
,/* I - Number of options */
1376 cups_option_t
*options
) /* I - Options */
1378 int i
; /* Looping var */
1379 const char *val
; /* Pointer to option value */
1380 ipp_t
*request
; /* IPP request */
1381 ipp_t
*response
; /* IPP response */
1382 ipp_attribute_t
*attr
; /* IPP job-id attribute */
1383 char hostname
[HTTP_MAX_URI
], /* Hostname */
1384 printer
[HTTP_MAX_URI
], /* Printer or class name */
1385 uri
[HTTP_MAX_URI
]; /* Printer URI */
1386 cups_lang_t
*language
; /* Language to use */
1387 int jobid
; /* New job ID */
1390 DEBUG_printf(("cupsPrintFiles(\'%s\', %d, %p, %d, %p)\n",
1391 name
, num_files
, files
, num_options
, options
));
1393 if (name
== NULL
|| num_files
< 1 || files
== NULL
)
1397 * Setup a connection and request data...
1400 if (!cups_connect(name
, printer
, hostname
))
1402 DEBUG_printf(("cupsPrintFile: Unable to open connection - %s.\n",
1404 last_error
= IPP_SERVICE_UNAVAILABLE
;
1408 language
= cupsLangDefault();
1411 * Build a standard CUPS URI for the printer and fill the standard IPP
1415 if ((request
= ippNew()) == NULL
)
1418 request
->request
.op
.operation_id
= num_files
== 1 ? IPP_PRINT_JOB
:
1420 request
->request
.op
.request_id
= 1;
1422 snprintf(uri
, sizeof(uri
), "ipp://%s:%d/printers/%s", hostname
, ippPort(), printer
);
1424 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1425 "attributes-charset", NULL
, cupsLangEncoding(language
));
1427 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1428 "attributes-natural-language", NULL
,
1429 language
!= NULL
? language
->language
: "C");
1431 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1434 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1438 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
, title
);
1441 * Then add all options...
1444 cupsEncodeOptions(request
, num_options
, options
);
1450 snprintf(uri
, sizeof(uri
), "/printers/%s", printer
);
1453 response
= cupsDoFileRequest(cups_server
, request
, uri
, *files
);
1455 response
= cupsDoRequest(cups_server
, request
, uri
);
1457 if (response
== NULL
)
1459 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1461 DEBUG_printf(("IPP response code was 0x%x!\n",
1462 response
->request
.status
.status_code
));
1465 else if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1467 DEBUG_puts("No job ID!");
1471 jobid
= attr
->values
[0].integer
;
1473 if (response
!= NULL
)
1474 ippDelete(response
);
1477 * Handle multiple file jobs if the create-job operation worked...
1480 if (jobid
> 0 && num_files
> 1)
1481 for (i
= 0; i
< num_files
; i
++)
1484 * Build a standard CUPS URI for the job and fill the standard IPP
1488 if ((request
= ippNew()) == NULL
)
1491 request
->request
.op
.operation_id
= IPP_SEND_DOCUMENT
;
1492 request
->request
.op
.request_id
= 1;
1494 snprintf(uri
, sizeof(uri
), "ipp://%s:%d/jobs/%d", hostname
, ippPort(),
1497 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1498 "attributes-charset", NULL
, cupsLangEncoding(language
));
1500 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1501 "attributes-natural-language", NULL
,
1502 language
!= NULL
? language
->language
: "C");
1504 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri",
1508 * Handle raw print files...
1511 if (cupsGetOption("raw", num_options
, options
))
1512 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
1513 NULL
, "application/vnd.cups-raw");
1514 else if ((val
= cupsGetOption("document-format", num_options
, options
)) != NULL
)
1515 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
1518 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
1519 NULL
, "application/octet-stream");
1521 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1525 * Is this the last document?
1528 if (i
== (num_files
- 1))
1529 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1535 snprintf(uri
, sizeof(uri
), "/printers/%s", printer
);
1537 if ((response
= cupsDoFileRequest(cups_server
, request
, uri
,
1539 ippDelete(response
);
1547 * 'cups_connect()' - Connect to the specified host...
1550 static char * /* I - Printer name or NULL */
1551 cups_connect(const char *name
, /* I - Destination (printer[@host]) */
1552 char *printer
, /* O - Printer name [HTTP_MAX_URI] */
1553 char *hostname
) /* O - Hostname [HTTP_MAX_URI] */
1555 char hostbuf
[HTTP_MAX_URI
]; /* Name of host */
1556 static char printerbuf
[HTTP_MAX_URI
];
1557 /* Name of printer or class */
1560 DEBUG_printf(("cups_connect(\"%s\", %p, %p)\n", name
, printer
, hostname
));
1564 last_error
= IPP_BAD_REQUEST
;
1568 if (sscanf(name
, "%1023[^@]@%1023s", printerbuf
, hostbuf
) == 1)
1570 strncpy(hostbuf
, cupsServer(), sizeof(hostbuf
) - 1);
1571 hostbuf
[sizeof(hostbuf
) - 1] = '\0';
1574 if (hostname
!= NULL
)
1576 strncpy(hostname
, hostbuf
, HTTP_MAX_URI
- 1);
1577 hostname
[HTTP_MAX_URI
- 1] = '\0';
1582 if (printer
!= NULL
)
1584 strncpy(printer
, printerbuf
, HTTP_MAX_URI
- 1);
1585 printer
[HTTP_MAX_URI
- 1] = '\0';
1588 printer
= printerbuf
;
1590 if (cups_server
!= NULL
)
1592 if (strcasecmp(cups_server
->hostname
, hostname
) == 0)
1595 httpClose(cups_server
);
1598 DEBUG_printf(("connecting to %s on port %d...\n", hostname
, ippPort()));
1600 if ((cups_server
= httpConnectEncrypt(hostname
, ippPort(),
1601 cupsEncryption())) == NULL
)
1603 last_error
= IPP_SERVICE_UNAVAILABLE
;
1612 * 'cups_local_auth()' - Get the local authorization certificate if
1613 * available/applicable...
1616 static int /* O - 1 if available, 0 if not */
1617 cups_local_auth(http_t
*http
) /* I - Connection */
1619 #if defined(WIN32) || defined(__EMX__)
1621 * Currently WIN32 and OS-2 do not support the CUPS server...
1626 int pid
; /* Current process ID */
1627 FILE *fp
; /* Certificate file */
1628 char filename
[1024], /* Certificate filename */
1629 certificate
[33];/* Certificate string */
1630 const char *root
; /* Server root directory */
1634 * See if we are accessing localhost...
1637 if (strcasecmp(http
->hostname
, "localhost") != 0)
1640 if (!httpAddrLocalhost(&(http
->hostaddr
)))
1644 * Try opening a certificate file for this PID. If that fails,
1645 * try the root certificate...
1648 if ((root
= getenv("CUPS_SERVERROOT")) == NULL
)
1649 root
= CUPS_SERVERROOT
;
1652 snprintf(filename
, sizeof(filename
), "%s/certs/%d", root
, pid
);
1653 if ((fp
= fopen(filename
, "r")) == NULL
&& pid
> 0)
1655 snprintf(filename
, sizeof(filename
), "%s/certs/0", root
);
1656 fp
= fopen(filename
, "r");
1663 * Read the certificate from the file...
1666 fgets(certificate
, sizeof(certificate
), fp
);
1670 * Set the authorization string and return...
1673 snprintf(authstring
, sizeof(authstring
), "Local %s", certificate
);
1676 #endif /* WIN32 || __EMX__ */
1681 * End of "$Id: util.c,v 1.81.2.3 2001/12/26 16:52:13 mike Exp $".