2 * "$Id: util.c 6506 2007-05-03 18:12:35Z mike $"
4 * Printing utilities for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 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 USA
20 * Voice: (301) 373-9600
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 on the default server.
29 * cupsFreeJobs() - Free memory used by job data.
30 * cupsGetClasses() - Get a list of printer classes from the default
32 * cupsGetDefault() - Get the default printer or class from the default
34 * cupsGetDefault2() - Get the default printer or class from the
36 * cupsGetJobs() - Get the jobs from the default server.
37 * cupsGetJobs2() - Get the jobs from the specified server.
38 * cupsGetPPD() - Get the PPD file for a printer on the default
40 * cupsGetPPD2() - Get the PPD file for a printer on the specified
42 * cupsGetPrinters() - Get a list of printers from the default server.
43 * cupsGetServerPPD() - Get an available PPD file from the server.
44 * cupsLastError() - Return the last IPP status code.
45 * cupsLastErrorString() - Return the last IPP status-message.
46 * cupsPrintFile() - Print a file to a printer or class on the default
48 * cupsPrintFile2() - Print a file to a printer or class on the
50 * cupsPrintFiles() - Print one or more files to a printer or class on
52 * cupsPrintFiles2() - Print one or more files to a printer or class on
53 * the specified server.
54 * cups_connect() - Connect to the specified host...
55 * cups_get_printer_uri() - Get the printer-uri-supported attribute for the
56 * first printer in a class.
60 * Include necessary headers...
69 #if defined(WIN32) || defined(__EMX__)
73 #endif /* WIN32 || __EMX__ */
80 static char *cups_connect(const char *name
, char *printer
, char *hostname
);
81 static int cups_get_printer_uri(http_t
*http
, const char *name
,
82 char *host
, int hostsize
, int *port
,
83 char *resource
, int resourcesize
,
88 * 'cupsCancelJob()' - Cancel a print job on the default server.
90 * Use the cupsLastError() and cupsLastErrorString() functions to get
91 * the cause of any failure.
94 int /* O - 1 on success, 0 on failure */
95 cupsCancelJob(const char *name
, /* I - Name of printer or class */
96 int job
) /* I - Job ID */
98 char printer
[HTTP_MAX_URI
], /* Printer name */
99 hostname
[HTTP_MAX_URI
], /* Hostname */
100 uri
[HTTP_MAX_URI
]; /* Printer URI */
101 ipp_t
*request
, /* IPP request */
102 *response
; /* IPP response */
103 cups_lang_t
*language
; /* Language info */
104 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
108 * See if we can connect to the server...
111 if (!cups_connect(name
, printer
, hostname
))
113 DEBUG_puts("Unable to connect to server!");
119 * Create a printer URI...
122 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
123 "localhost", 0, "/printers/%s", printer
) != HTTP_URI_OK
)
125 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
131 * Build an IPP_CANCEL_JOB request, which requires the following
135 * attributes-natural-language
138 * [requesting-user-name]
143 request
->request
.op
.operation_id
= IPP_CANCEL_JOB
;
144 request
->request
.op
.request_id
= 1;
146 language
= cupsLangDefault();
148 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
149 "attributes-charset", NULL
, cupsLangEncoding(language
));
151 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
152 "attributes-natural-language", NULL
,
153 language
!= NULL
? language
->language
: "C");
155 cupsLangFree(language
);
157 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
160 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", job
);
162 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
169 if ((response
= cupsDoRequest(cg
->http
, request
, "/jobs/")) != NULL
)
172 return (cg
->last_error
< IPP_REDIRECTION_OTHER_SITE
);
177 * 'cupsFreeJobs()' - Free memory used by job data.
181 cupsFreeJobs(int num_jobs
, /* I - Number of jobs */
182 cups_job_t
*jobs
) /* I - Jobs */
184 int i
; /* Looping var */
187 if (num_jobs
<= 0 || jobs
== NULL
)
190 for (i
= 0; i
< num_jobs
; i
++)
194 free(jobs
[i
].format
);
203 * 'cupsGetClasses()' - Get a list of printer classes from the default server.
205 * This function is deprecated - use cupsGetDests() instead.
210 int /* O - Number of classes */
211 cupsGetClasses(char ***classes
) /* O - Classes */
213 int n
; /* Number of classes */
214 ipp_t
*request
, /* IPP Request */
215 *response
; /* IPP Response */
216 ipp_attribute_t
*attr
; /* Current attribute */
217 cups_lang_t
*language
; /* Default language */
218 char **temp
; /* Temporary pointer */
219 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
224 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
230 * Try to connect to the server...
233 if (!cups_connect("default", NULL
, NULL
))
235 DEBUG_puts("Unable to connect to server!");
241 * Build a CUPS_GET_CLASSES request, which requires the following
245 * attributes-natural-language
246 * requested-attributes
251 request
->request
.op
.operation_id
= CUPS_GET_CLASSES
;
252 request
->request
.op
.request_id
= 1;
254 language
= cupsLangDefault();
256 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
257 "attributes-charset", NULL
, cupsLangEncoding(language
));
259 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
260 "attributes-natural-language", NULL
, language
->language
);
262 cupsLangFree(language
);
264 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
265 "requested-attributes", NULL
, "printer-name");
268 * Do the request and get back a response...
274 if ((response
= cupsDoRequest(cg
->http
, request
, "/")) != NULL
)
276 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
277 if (attr
->name
!= NULL
&&
278 strcasecmp(attr
->name
, "printer-name") == 0 &&
279 attr
->value_tag
== IPP_TAG_NAME
)
282 temp
= malloc(sizeof(char *));
284 temp
= realloc(*classes
, sizeof(char *) * (n
+ 1));
304 temp
[n
] = strdup(attr
->values
[0].string
.text
);
316 * 'cupsGetDefault()' - Get the default printer or class for the default server.
318 * This function returns the default printer or class as defined by
319 * the LPDEST or PRINTER environment variables. If these environment
320 * variables are not set, the server default destination is returned.
321 * Applications should use the cupsGetDests() and cupsGetDest() functions
322 * to get the user-defined default printer, as this function does not
323 * support the lpoptions-defined default printer.
326 const char * /* O - Default printer or NULL */
329 const char *var
; /* Environment variable */
330 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
334 * First see if the LPDEST or PRINTER environment variables are
335 * set... However, if PRINTER is set to "lp", ignore it to work
336 * around a "feature" in most Linux distributions - the default
337 * user login scripts set PRINTER to "lp"...
340 if ((var
= getenv("LPDEST")) != NULL
)
342 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
346 * Try to connect to the server...
349 if (!cups_connect("default", NULL
, NULL
))
351 DEBUG_puts("Unable to connect to server!");
357 * Return the default printer...
360 return (cupsGetDefault2(cg
->http
));
365 * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
367 * This function returns the default printer or class as defined by
368 * the LPDEST or PRINTER environment variables. If these environment
369 * variables are not set, the server default destination is returned.
370 * Applications should use the cupsGetDests() and cupsGetDest() functions
371 * to get the user-defined default printer, as this function does not
372 * support the lpoptions-defined default printer.
374 * @since CUPS 1.1.21@
377 const char * /* O - Default printer or NULL */
378 cupsGetDefault2(http_t
*http
) /* I - HTTP connection */
380 ipp_t
*request
, /* IPP Request */
381 *response
; /* IPP Response */
382 ipp_attribute_t
*attr
; /* Current attribute */
383 cups_lang_t
*language
; /* Default language */
384 const char *var
; /* Environment variable */
385 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
389 * First see if the LPDEST or PRINTER environment variables are
390 * set... However, if PRINTER is set to "lp", ignore it to work
391 * around a "feature" in most Linux distributions - the default
392 * user login scripts set PRINTER to "lp"...
395 if ((var
= getenv("LPDEST")) != NULL
)
397 else if ((var
= getenv("PRINTER")) != NULL
&& strcmp(var
, "lp") != 0)
401 * Range check input...
408 * Build a CUPS_GET_DEFAULT request, which requires the following
412 * attributes-natural-language
417 request
->request
.op
.operation_id
= CUPS_GET_DEFAULT
;
418 request
->request
.op
.request_id
= 1;
420 language
= cupsLangDefault();
422 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
423 "attributes-charset", NULL
, cupsLangEncoding(language
));
425 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
426 "attributes-natural-language", NULL
, language
->language
);
428 cupsLangFree(language
);
431 * Do the request and get back a response...
434 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
436 if ((attr
= ippFindAttribute(response
, "printer-name", IPP_TAG_NAME
)) != NULL
)
438 strlcpy(cg
->def_printer
, attr
->values
[0].string
.text
, sizeof(cg
->def_printer
));
440 return (cg
->def_printer
);
451 * 'cupsGetJobs()' - Get the jobs from the default server.
454 int /* O - Number of jobs */
455 cupsGetJobs(cups_job_t
**jobs
, /* O - Job data */
456 const char *mydest
, /* I - NULL = all destinations, *
457 * otherwise show jobs for mydest */
458 int myjobs
, /* I - 0 = all users, 1 = mine */
459 int completed
) /* I - -1 = show all, 0 = active, *
460 * 1 = completed jobs */
462 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
465 * Try to connect to the server...
468 if (!cups_connect("default", NULL
, NULL
))
470 DEBUG_puts("Unable to connect to server!");
479 return (cupsGetJobs2(cg
->http
, jobs
, mydest
, myjobs
, completed
));
485 * 'cupsGetJobs2()' - Get the jobs from the specified server.
487 * @since CUPS 1.1.21@
490 int /* O - Number of jobs */
491 cupsGetJobs2(http_t
*http
, /* I - HTTP connection */
492 cups_job_t
**jobs
, /* O - Job data */
493 const char *mydest
, /* I - NULL = all destinations, *
494 * otherwise show jobs for mydest */
495 int myjobs
, /* I - 0 = all users, 1 = mine */
496 int completed
) /* I - -1 = show all, 0 = active, *
497 * 1 = completed jobs */
499 int n
; /* Number of jobs */
500 ipp_t
*request
, /* IPP Request */
501 *response
; /* IPP Response */
502 ipp_attribute_t
*attr
; /* Current attribute */
503 cups_lang_t
*language
; /* Default language */
504 cups_job_t
*temp
; /* Temporary pointer */
506 priority
, /* job-priority */
507 size
; /* job-k-octets */
508 ipp_jstate_t state
; /* job-state */
509 time_t completed_time
, /* time-at-completed */
510 creation_time
, /* time-at-creation */
511 processing_time
; /* time-at-processing */
512 const char *dest
, /* job-printer-uri */
513 *format
, /* document-format */
514 *title
, /* job-name */
515 *user
; /* job-originating-user-name */
516 char uri
[HTTP_MAX_URI
]; /* URI for jobs */
517 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
518 static const char * const attrs
[] = /* Requested attributes */
526 "time-at-processing",
530 "job-originating-user-name"
535 * Range check input...
540 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
546 * Get the right URI...
551 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
552 "localhost", 0, "/printers/%s", mydest
) != HTTP_URI_OK
)
554 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
560 strcpy(uri
, "ipp://localhost/jobs");
564 * Build an IPP_GET_JOBS request, which requires the following
568 * attributes-natural-language
570 * requesting-user-name
573 * requested-attributes
578 request
->request
.op
.operation_id
= IPP_GET_JOBS
;
579 request
->request
.op
.request_id
= 1;
581 language
= cupsLangDefault();
583 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
584 "attributes-charset", NULL
, cupsLangEncoding(language
));
586 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
587 "attributes-natural-language", NULL
, language
->language
);
589 cupsLangFree(language
);
591 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
592 "printer-uri", NULL
, uri
);
594 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
595 "requesting-user-name", NULL
, cupsUser());
598 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
601 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
602 "which-jobs", NULL
, "completed");
603 else if (completed
< 0)
604 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
605 "which-jobs", NULL
, "all");
607 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
608 "requested-attributes", sizeof(attrs
) / sizeof(attrs
[0]),
612 * Do the request and get back a response...
618 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
620 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
623 * Skip leading attributes until we hit a job...
626 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_JOB
)
633 * Pull the needed attributes from this job...
639 state
= IPP_JOB_PENDING
;
642 format
= "application/octet-stream";
648 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_JOB
)
650 if (strcmp(attr
->name
, "job-id") == 0 &&
651 attr
->value_tag
== IPP_TAG_INTEGER
)
652 id
= attr
->values
[0].integer
;
653 else if (strcmp(attr
->name
, "job-state") == 0 &&
654 attr
->value_tag
== IPP_TAG_ENUM
)
655 state
= (ipp_jstate_t
)attr
->values
[0].integer
;
656 else if (strcmp(attr
->name
, "job-priority") == 0 &&
657 attr
->value_tag
== IPP_TAG_INTEGER
)
658 priority
= attr
->values
[0].integer
;
659 else if (strcmp(attr
->name
, "job-k-octets") == 0 &&
660 attr
->value_tag
== IPP_TAG_INTEGER
)
661 size
= attr
->values
[0].integer
;
662 else if (strcmp(attr
->name
, "time-at-completed") == 0 &&
663 attr
->value_tag
== IPP_TAG_INTEGER
)
664 completed_time
= attr
->values
[0].integer
;
665 else if (strcmp(attr
->name
, "time-at-creation") == 0 &&
666 attr
->value_tag
== IPP_TAG_INTEGER
)
667 creation_time
= attr
->values
[0].integer
;
668 else if (strcmp(attr
->name
, "time-at-processing") == 0 &&
669 attr
->value_tag
== IPP_TAG_INTEGER
)
670 processing_time
= attr
->values
[0].integer
;
671 else if (strcmp(attr
->name
, "job-printer-uri") == 0 &&
672 attr
->value_tag
== IPP_TAG_URI
)
674 if ((dest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
677 else if (strcmp(attr
->name
, "job-originating-user-name") == 0 &&
678 attr
->value_tag
== IPP_TAG_NAME
)
679 user
= attr
->values
[0].string
.text
;
680 else if (strcmp(attr
->name
, "document-format") == 0 &&
681 attr
->value_tag
== IPP_TAG_MIMETYPE
)
682 format
= attr
->values
[0].string
.text
;
683 else if (strcmp(attr
->name
, "job-name") == 0 &&
684 (attr
->value_tag
== IPP_TAG_TEXT
||
685 attr
->value_tag
== IPP_TAG_NAME
))
686 title
= attr
->values
[0].string
.text
;
692 * See if we have everything needed...
695 if (dest
== NULL
|| id
== 0)
704 * Allocate memory for the job...
708 temp
= malloc(sizeof(cups_job_t
));
710 temp
= realloc(*jobs
, sizeof(cups_job_t
) * (n
+ 1));
718 cupsFreeJobs(n
, *jobs
);
730 * Copy the data over...
733 temp
->dest
= strdup(dest
);
734 temp
->user
= strdup(user
);
735 temp
->format
= strdup(format
);
736 temp
->title
= strdup(title
);
738 temp
->priority
= priority
;
741 temp
->completed_time
= completed_time
;
742 temp
->creation_time
= creation_time
;
743 temp
->processing_time
= processing_time
;
752 if (n
== 0 && cg
->last_error
>= IPP_BAD_REQUEST
)
760 * 'cupsGetPPD()' - Get the PPD file for a printer on the default server.
762 * For classes, cupsGetPPD() returns the PPD file for the first printer
766 const char * /* O - Filename for PPD file */
767 cupsGetPPD(const char *name
) /* I - Printer name */
769 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
772 * See if we can connect to the server...
775 if (!cups_connect(name
, NULL
, NULL
))
777 DEBUG_puts("Unable to connect to server!");
783 * Return the PPD file...
786 return (cupsGetPPD2(cg
->http
, name
));
791 * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
793 * For classes, cupsGetPPD2() returns the PPD file for the first printer
796 * @since CUPS 1.1.21@
799 const char * /* O - Filename for PPD file */
800 cupsGetPPD2(http_t
*http
, /* I - HTTP connection */
801 const char *name
) /* I - Printer name */
803 int http_port
; /* Port number */
804 char http_hostname
[HTTP_MAX_HOST
];
805 /* Hostname associated with connection */
806 http_t
*http2
; /* Alternate HTTP connection */
807 int fd
; /* PPD file */
808 char localhost
[HTTP_MAX_URI
],/* Local hostname */
809 hostname
[HTTP_MAX_URI
], /* Hostname */
810 resource
[HTTP_MAX_URI
]; /* Resource name */
811 int port
; /* Port number */
812 http_status_t status
; /* HTTP status from server */
813 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
817 * Range check input...
820 DEBUG_printf(("cupsGetPPD2(http=%p, name=\"%s\")\n", http
,
821 name
? name
: "(null)"));
826 _cupsSetError(IPP_INTERNAL_ERROR
, "No HTTP connection!");
828 _cupsSetError(IPP_INTERNAL_ERROR
, "No printer name!");
834 * Try finding a printer URI for this printer...
837 if (!cups_get_printer_uri(http
, name
, hostname
, sizeof(hostname
), &port
,
838 resource
, sizeof(resource
), 0))
841 DEBUG_printf(("Printer hostname=\"%s\", port=%d\n", hostname
, port
));
844 * Remap local hostname to localhost...
847 httpGetHostname(NULL
, localhost
, sizeof(localhost
));
849 DEBUG_printf(("Local hostname=\"%s\"\n", localhost
));
851 if (!strcasecmp(localhost
, hostname
))
852 strcpy(hostname
, "localhost");
855 * Get the hostname and port number we are connected to...
858 httpGetHostname(http
, http_hostname
, sizeof(http_hostname
));
861 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
862 http_port
= ntohs(http
->hostaddr
->ipv6
.sin6_port
);
864 #endif /* AF_INET6 */
865 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
866 http_port
= ntohs(http
->hostaddr
->ipv4
.sin_port
);
868 http_port
= ippPort();
870 DEBUG_printf(("Connection hostname=\"%s\", port=%d\n", http_hostname
,
874 * Reconnect to the correct server as needed...
877 if (!strcasecmp(http_hostname
, hostname
) && port
== http_port
)
879 else if ((http2
= httpConnectEncrypt(hostname
, port
,
880 cupsEncryption())) == NULL
)
882 DEBUG_puts("Unable to connect to server!");
891 if ((fd
= cupsTempFd(cg
->ppd_filename
, sizeof(cg
->ppd_filename
))) < 0)
894 * Can't open file; close the server connection and return NULL...
897 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
));
906 * And send a request to the HTTP server...
909 strlcat(resource
, ".ppd", sizeof(resource
));
911 status
= cupsGetFd(http2
, resource
, fd
);
919 * See if we actually got the file or an error...
922 if (status
!= HTTP_OK
)
926 case HTTP_NOT_FOUND
:
927 _cupsSetError(IPP_NOT_FOUND
, httpStatus(status
));
930 case HTTP_UNAUTHORIZED
:
931 _cupsSetError(IPP_NOT_AUTHORIZED
, httpStatus(status
));
935 DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
937 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, httpStatus(status
));
941 unlink(cg
->ppd_filename
);
947 * Return the PPD file...
950 return (cg
->ppd_filename
);
955 * 'cupsGetPrinters()' - Get a list of printers from the default server.
957 * This function is deprecated - use cupsGetDests() instead.
962 int /* O - Number of printers */
963 cupsGetPrinters(char ***printers
) /* O - Printers */
965 int n
; /* Number of printers */
966 ipp_t
*request
, /* IPP Request */
967 *response
; /* IPP Response */
968 ipp_attribute_t
*attr
; /* Current attribute */
969 cups_lang_t
*language
; /* Default language */
970 char **temp
; /* Temporary pointer */
971 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
974 if (printers
== NULL
)
976 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
982 * Try to connect to the server...
985 if (!cups_connect("default", NULL
, NULL
))
987 DEBUG_puts("Unable to connect to server!");
993 * Build a CUPS_GET_PRINTERS request, which requires the following
997 * attributes-natural-language
998 * requested-attributes
1003 request
->request
.op
.operation_id
= CUPS_GET_PRINTERS
;
1004 request
->request
.op
.request_id
= 1;
1006 language
= cupsLangDefault();
1008 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1009 "attributes-charset", NULL
, cupsLangEncoding(language
));
1011 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1012 "attributes-natural-language", NULL
, language
->language
);
1014 cupsLangFree(language
);
1016 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1017 "requested-attributes", NULL
, "printer-name");
1019 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
1022 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
1023 "printer-type-mask", CUPS_PRINTER_CLASS
);
1026 * Do the request and get back a response...
1032 if ((response
= cupsDoRequest(cg
->http
, request
, "/")) != NULL
)
1034 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1035 if (attr
->name
!= NULL
&&
1036 strcasecmp(attr
->name
, "printer-name") == 0 &&
1037 attr
->value_tag
== IPP_TAG_NAME
)
1040 temp
= malloc(sizeof(char *));
1042 temp
= realloc(*printers
, sizeof(char *) * (n
+ 1));
1047 * Ran out of memory!
1053 free((*printers
)[n
]);
1057 ippDelete(response
);
1062 temp
[n
] = strdup(attr
->values
[0].string
.text
);
1066 ippDelete(response
);
1074 * 'cupsGetServerPPD()' - Get an available PPD file from the server.
1076 * This function returns the named PPD file from the server. The
1077 * list of available PPDs is provided by the IPP CUPS_GET_PPDS
1080 * You must remove (unlink) the PPD file when you are finished with
1081 * it. The PPD filename is stored in a static location that will be
1082 * overwritten on the next call to cupsGetPPD(), cupsGetPPD2(), or
1083 * cupsGetServerPPD().
1088 char * /* O - Name of PPD file or NULL on error */
1089 cupsGetServerPPD(http_t
*http
, /* I - HTTP connection */
1090 const char *name
) /* I - Name of PPD file ("ppd-name") */
1092 int fd
; /* PPD file descriptor */
1093 ipp_t
*request
; /* IPP request */
1094 _cups_globals_t
*cg
= _cupsGlobals();
1095 /* Pointer to library globals */
1099 * Range check input...
1105 _cupsSetError(IPP_INTERNAL_ERROR
, "No HTTP connection!");
1107 _cupsSetError(IPP_INTERNAL_ERROR
, "No PPD name!");
1113 * Get a temp file...
1116 if ((fd
= cupsTempFd(cg
->ppd_filename
, sizeof(cg
->ppd_filename
))) < 0)
1119 * Can't open file; close the server connection and return NULL...
1122 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
));
1128 * Get the PPD file...
1131 request
= ippNewRequest(CUPS_GET_PPD
);
1132 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "ppd-name", NULL
,
1135 ippDelete(cupsDoIORequest(http
, request
, "/", -1, fd
));
1139 if (cupsLastError() != IPP_OK
)
1141 unlink(cg
->ppd_filename
);
1145 return (cg
->ppd_filename
);
1150 * 'cupsLastError()' - Return the last IPP status code.
1153 ipp_status_t
/* O - IPP status code from last request */
1156 return (_cupsGlobals()->last_error
);
1161 * 'cupsLastErrorString()' - Return the last IPP status-message.
1166 const char * /* O - status-message text from last request */
1167 cupsLastErrorString(void)
1169 return (_cupsGlobals()->last_status_message
);
1174 * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
1177 int /* O - Job ID */
1178 cupsPrintFile(const char *name
, /* I - Printer or class name */
1179 const char *filename
, /* I - File to print */
1180 const char *title
, /* I - Title of job */
1181 int num_options
,/* I - Number of options */
1182 cups_option_t
*options
) /* I - Options */
1184 DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", "
1185 "title=\"%s\", num_options=%d, options=%p)\n",
1186 name
, filename
, title
, num_options
, options
));
1188 return (cupsPrintFiles(name
, 1, &filename
, title
, num_options
, options
));
1193 * 'cupsPrintFile2()' - Print a file to a printer or class on the specified server.
1195 * @since CUPS 1.1.21@
1198 int /* O - Job ID */
1199 cupsPrintFile2(http_t
*http
, /* I - HTTP connection */
1200 const char *name
, /* I - Printer or class name */
1201 const char *filename
, /* I - File to print */
1202 const char *title
, /* I - Title of job */
1204 /* I - Number of options */
1205 cups_option_t
*options
) /* I - Options */
1207 DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", "
1208 "title=\"%s\", num_options=%d, options=%p)\n",
1209 http
, name
, filename
, title
, num_options
, options
));
1211 return (cupsPrintFiles2(http
, name
, 1, &filename
, title
, num_options
, options
));
1216 * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
1220 int /* O - Job ID */
1221 cupsPrintFiles(const char *name
, /* I - Printer or class name */
1222 int num_files
, /* I - Number of files */
1223 const char **files
, /* I - File(s) to print */
1224 const char *title
, /* I - Title of job */
1226 /* I - Number of options */
1227 cups_option_t
*options
) /* I - Options */
1229 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
1231 DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, "
1232 "files=%p, title=\"%s\", num_options=%d, options=%p)\n",
1233 name
, num_files
, files
, title
, num_options
, options
));
1237 * Setup a connection and request data...
1240 if (!cups_connect(name
, NULL
, NULL
))
1242 DEBUG_printf(("cupsPrintFiles: Unable to open connection - %s.\n",
1244 DEBUG_puts("Unable to connect to server!");
1250 * Print the file(s)...
1253 return (cupsPrintFiles2(cg
->http
, name
, num_files
, files
, title
,
1254 num_options
, options
));
1260 * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
1263 * @since CUPS 1.1.21@
1266 int /* O - Job ID */
1267 cupsPrintFiles2(http_t
*http
, /* I - HTTP connection */
1268 const char *name
, /* I - Printer or class name */
1269 int num_files
,/* I - Number of files */
1270 const char **files
, /* I - File(s) to print */
1271 const char *title
, /* I - Title of job */
1273 /* I - Number of options */
1274 cups_option_t
*options
) /* I - Options */
1276 int i
; /* Looping var */
1277 const char *val
; /* Pointer to option value */
1278 ipp_t
*request
; /* IPP request */
1279 ipp_t
*response
; /* IPP response */
1280 ipp_attribute_t
*attr
; /* IPP job-id attribute */
1281 char uri
[HTTP_MAX_URI
]; /* Printer URI */
1282 cups_lang_t
*language
; /* Language to use */
1283 int jobid
; /* New job ID */
1284 const char *base
; /* Basename of current filename */
1287 DEBUG_printf(("cupsPrintFiles(http=%p, name=\"%s\", num_files=%d, "
1288 "files=%p, title=\"%s\", num_options=%d, options=%p)\n",
1289 http
, name
, num_files
, files
, title
, num_options
, options
));
1292 * Range check input...
1295 if (!http
|| !name
|| num_files
< 1 || files
== NULL
)
1297 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1303 * Setup the printer URI...
1306 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1307 "localhost", 0, "/printers/%s", name
) != HTTP_URI_OK
)
1309 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1315 * Setup the request data...
1318 language
= cupsLangDefault();
1321 * Build a standard CUPS URI for the printer and fill the standard IPP
1325 if ((request
= ippNew()) == NULL
)
1327 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1332 request
->request
.op
.operation_id
= num_files
== 1 ? IPP_PRINT_JOB
:
1334 request
->request
.op
.request_id
= 1;
1336 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1337 "attributes-charset", NULL
, cupsLangEncoding(language
));
1339 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1340 "attributes-natural-language", NULL
,
1341 language
!= NULL
? language
->language
: "C");
1343 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1346 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1350 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
1354 * Then add all options...
1357 cupsEncodeOptions(request
, num_options
, options
);
1363 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
1366 response
= cupsDoFileRequest(http
, request
, uri
, *files
);
1368 response
= cupsDoRequest(http
, request
, uri
);
1370 if (response
== NULL
)
1372 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1374 DEBUG_printf(("IPP response code was 0x%x!\n",
1375 response
->request
.status
.status_code
));
1378 else if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1380 DEBUG_puts("No job ID!");
1382 _cupsSetError(IPP_INTERNAL_ERROR
, NULL
);
1387 jobid
= attr
->values
[0].integer
;
1389 if (response
!= NULL
)
1390 ippDelete(response
);
1393 * Handle multiple file jobs if the create-job operation worked...
1396 if (jobid
> 0 && num_files
> 1)
1397 for (i
= 0; i
< num_files
; i
++)
1400 * Build a standard CUPS URI for the job and fill the standard IPP
1404 if ((request
= ippNew()) == NULL
)
1407 request
->request
.op
.operation_id
= IPP_SEND_DOCUMENT
;
1408 request
->request
.op
.request_id
= 1;
1410 snprintf(uri
, sizeof(uri
), "ipp://localhost/jobs/%d", jobid
);
1412 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1413 "attributes-charset", NULL
, cupsLangEncoding(language
));
1415 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1416 "attributes-natural-language", NULL
,
1417 language
!= NULL
? language
->language
: "C");
1419 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri",
1423 * Handle raw print files...
1426 if (cupsGetOption("raw", num_options
, options
))
1427 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1428 "document-format", NULL
, "application/vnd.cups-raw");
1429 else if ((val
= cupsGetOption("document-format", num_options
,
1431 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1432 "document-format", NULL
, val
);
1434 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1435 "document-format", NULL
, "application/octet-stream");
1437 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1438 "requesting-user-name", NULL
, cupsUser());
1441 * Add the original document filename...
1444 if ((base
= strrchr(files
[i
], '/')) != NULL
)
1449 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "document-name",
1453 * Is this the last document?
1456 if (i
== (num_files
- 1))
1457 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1463 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
1465 if ((response
= cupsDoFileRequest(http
, request
, uri
,
1467 ippDelete(response
);
1470 cupsLangFree(language
);
1477 * 'cups_connect()' - Connect to the specified host...
1480 static char * /* I - Printer name or NULL */
1481 cups_connect(const char *name
, /* I - Destination (printer[@host]) */
1482 char *printer
, /* O - Printer name [HTTP_MAX_URI] */
1483 char *hostname
) /* O - Hostname [HTTP_MAX_URI] */
1485 char hostbuf
[HTTP_MAX_URI
], /* Name of host */
1486 http_hostname
[HTTP_MAX_HOST
]; /* Hostname associated with connection */
1487 _cups_globals_t
*cg
= _cupsGlobals();/* Pointer to library globals */
1490 DEBUG_printf(("cups_connect(\"%s\", %p, %p)\n", name
, printer
, hostname
));
1494 _cupsSetError(IPP_BAD_REQUEST
, NULL
);
1500 * All jobs are now queued to cupsServer() to avoid hostname
1501 * resolution problems and to ensure that the user sees all
1502 * locally queued jobs locally.
1505 strlcpy(hostbuf
, cupsServer(), sizeof(hostbuf
));
1507 httpGetHostname(cg
->http
, http_hostname
, sizeof(http_hostname
));
1509 if (hostname
!= NULL
)
1510 strlcpy(hostname
, hostbuf
, HTTP_MAX_URI
);
1514 if (printer
!= NULL
)
1515 strlcpy(printer
, name
, HTTP_MAX_URI
);
1517 printer
= (char *)name
;
1519 if (cg
->http
!= NULL
)
1521 if (!strcasecmp(http_hostname
, hostname
))
1524 httpClose(cg
->http
);
1527 DEBUG_printf(("connecting to %s on port %d...\n", hostname
, ippPort()));
1529 if ((cg
->http
= httpConnectEncrypt(hostname
, ippPort(),
1530 cupsEncryption())) == NULL
)
1532 DEBUG_puts("Unable to connect to server!");
1534 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, strerror(errno
));
1544 * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the first printer in a class.
1547 static int /* O - 1 on success, 0 on failure */
1548 cups_get_printer_uri(
1549 http_t
*http
, /* I - HTTP connection */
1550 const char *name
, /* I - Name of printer or class */
1551 char *host
, /* I - Hostname buffer */
1552 int hostsize
, /* I - Size of hostname buffer */
1553 int *port
, /* O - Port number */
1554 char *resource
, /* I - Resource buffer */
1555 int resourcesize
, /* I - Size of resource buffer */
1556 int depth
) /* I - Depth of query */
1558 int i
; /* Looping var */
1559 int http_port
; /* Port number */
1560 http_t
*http2
; /* Alternate HTTP connection */
1561 ipp_t
*request
, /* IPP request */
1562 *response
; /* IPP response */
1563 ipp_attribute_t
*attr
; /* Current attribute */
1564 char uri
[HTTP_MAX_URI
], /* printer-uri attribute */
1565 scheme
[HTTP_MAX_URI
], /* Scheme name */
1566 username
[HTTP_MAX_URI
], /* Username:password */
1567 classname
[255], /* Temporary class name */
1568 http_hostname
[HTTP_MAX_HOST
];
1569 /* Hostname associated with connection */
1570 static const char * const requested_attrs
[] =
1571 { /* Requested attributes */
1572 "printer-uri-supported",
1578 DEBUG_printf(("cups_get_printer_uri(http=%p, name=\"%s\", host=%p, "
1579 "hostsize=%d, resource=%p, resourcesize=%d, depth=%d)\n",
1580 http
, name
? name
: "(null)", host
, hostsize
,
1581 resource
, resourcesize
, depth
));
1584 * Setup the printer URI...
1587 if (httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1588 "localhost", 0, "/printers/%s", name
) != HTTP_URI_OK
)
1590 _cupsSetError(IPP_INTERNAL_ERROR
, "Unable to create printer-uri!");
1598 DEBUG_printf(("cups_get_printer_uri: printer-uri=\"%s\"\n", uri
));
1601 * Get the hostname and port number we are connected to...
1604 httpGetHostname(http
, http_hostname
, sizeof(http_hostname
));
1607 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
1608 http_port
= ntohs(http
->hostaddr
->ipv6
.sin6_port
);
1610 #endif /* AF_INET6 */
1611 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
1612 http_port
= ntohs(http
->hostaddr
->ipv4
.sin_port
);
1614 http_port
= ippPort();
1617 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1620 * attributes-charset
1621 * attributes-natural-language
1623 * requested-attributes
1626 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1628 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1631 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1632 "requested-attributes",
1633 sizeof(requested_attrs
) / sizeof(requested_attrs
[0]),
1634 NULL
, requested_attrs
);
1637 * Do the request and get back a response...
1640 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1642 if ((attr
= ippFindAttribute(response
, "member-uris", IPP_TAG_URI
)) != NULL
)
1645 * Get the first actual printer name in the class...
1648 for (i
= 0; i
< attr
->num_values
; i
++)
1650 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[i
].string
.text
,
1651 scheme
, sizeof(scheme
), username
, sizeof(username
),
1652 host
, hostsize
, port
, resource
, resourcesize
);
1653 if (!strncmp(resource
, "/printers/", 10))
1659 ippDelete(response
);
1666 * No printers in this class - try recursively looking for a printer,
1667 * but not more than 3 levels deep...
1672 for (i
= 0; i
< attr
->num_values
; i
++)
1674 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[i
].string
.text
,
1675 scheme
, sizeof(scheme
), username
, sizeof(username
),
1676 host
, hostsize
, port
, resource
, resourcesize
);
1677 if (!strncmp(resource
, "/classes/", 9))
1680 * Found a class! Connect to the right server...
1683 if (!strcasecmp(http_hostname
, host
) && *port
== http_port
)
1685 else if ((http2
= httpConnectEncrypt(host
, *port
,
1686 cupsEncryption())) == NULL
)
1688 DEBUG_puts("Unable to connect to server!");
1694 * Look up printers on that server...
1697 strlcpy(classname
, resource
+ 9, sizeof(classname
));
1699 cups_get_printer_uri(http2
, classname
, host
, hostsize
, port
,
1700 resource
, resourcesize
, depth
+ 1);
1703 * Close the connection as needed...
1715 else if ((attr
= ippFindAttribute(response
, "printer-uri-supported",
1716 IPP_TAG_URI
)) != NULL
)
1718 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[0].string
.text
,
1719 scheme
, sizeof(scheme
), username
, sizeof(username
),
1720 host
, hostsize
, port
, resource
, resourcesize
);
1721 ippDelete(response
);
1726 ippDelete(response
);
1729 if (cupsLastError() != IPP_NOT_FOUND
)
1730 _cupsSetError(IPP_INTERNAL_ERROR
, "No printer-uri found!");
1740 * End of "$Id: util.c 6506 2007-05-03 18:12:35Z mike $".