2
* "$Id: ipp.c 10112 2011-11-07 06:08:44Z mike $"
4
* IPP backend for CUPS.
6
* Copyright 2007-2011 by Apple Inc.
7
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
9
* These coded instructions, statements, and computer programs are the
10
* property of Apple Inc. and are protected by Federal copyright
11
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
12
* "LICENSE" which should have been included with this file. If this
13
* file is missing or damaged, see the license at "http://www.cups.org/".
15
* This file is subject to the Apple OS-Developed Software exception.
19
* main() - Send a file to the printer or server.
20
* cancel_job() - Cancel a print job.
21
* check_printer_state() - Check the printer state.
22
* compress_files() - Compress print files.
23
* monitor_printer() - Monitor the printer state.
24
* new_request() - Create a new print creation or validation request.
25
* password_cb() - Disable the password prompt for
26
* cupsDoFileRequest().
27
* report_attr() - Report an IPP attribute value.
28
* report_printer_state() - Report the printer state.
29
* run_as_user() - Run the IPP backend as the printing user.
30
* timeout_cb() - Handle HTTP timeouts.
31
* sigterm_handler() - Handle 'terminate' signals that stop the backend.
35
* Include necessary headers.
38
#include "backend-private.h"
39
#include <cups/array-private.h>
40
#include <sys/types.h>
43
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
45
# define kPMPrintUIToolAgent "com.apple.printuitool.agent"
46
# define kPMStartJob 100
47
# define kPMWaitForJob 101
48
extern void xpc_connection_set_target_uid(xpc_connection_t connection,
50
#endif /* HAVE_GSSAPI && HAVE_XPC */
57
typedef struct _cups_monitor_s /**** Monitoring data ****/
59
const char *uri, /* Printer URI */
60
*hostname, /* Hostname */
62
*resource; /* Resource path */
63
int port, /* Port number */
64
version, /* IPP version */
65
job_id; /* Job ID for submitted job */
66
http_encryption_t encryption; /* Use encryption? */
67
ipp_jstate_t job_state; /* Current job state */
68
ipp_pstate_t printer_state; /* Current printer state */
76
static const char *auth_info_required;
77
/* New auth-info-required value */
78
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
79
static int child_pid = 0; /* Child process ID */
80
#endif /* HAVE_GSSAPI && HAVE_XPC */
81
static const char * const jattrs[] = /* Job attributes we want */
83
"job-impressions-completed",
84
"job-media-sheets-completed",
88
static int job_canceled = 0;
90
static char *password = NULL;
91
/* Password for device URI */
92
static int password_tries = 0;
94
static const char * const pattrs[] = /* Printer attributes we want */
98
"document-format-supported",
100
"marker-high-levels",
106
"media-col-supported",
107
"multiple-document-handling-supported",
108
"operations-supported",
110
"printer-alert-description",
111
"printer-is-accepting-jobs",
113
"printer-state-message",
114
"printer-state-reasons"
116
static const char * const remote_job_states[] =
117
{ /* Remote job state keywords */
118
"+cups-remote-pending",
119
"+cups-remote-pending-held",
120
"+cups-remote-processing",
121
"+cups-remote-stopped",
122
"+cups-remote-canceled",
123
"+cups-remote-aborted",
124
"+cups-remote-completed"
126
static _cups_mutex_t report_mutex = _CUPS_MUTEX_INITIALIZER;
127
/* Mutex to control access */
128
static int num_attr_cache = 0;
129
/* Number of cached attributes */
130
static cups_option_t *attr_cache = NULL;
131
/* Cached attributes */
132
static cups_array_t *state_reasons; /* Array of printe-state-reasons keywords */
133
static char tmpfilename[1024] = "";
134
/* Temporary spool file name */
141
static void cancel_job(http_t *http, const char *uri, int id,
142
const char *resource, const char *user,
144
static ipp_pstate_t check_printer_state(http_t *http, const char *uri,
145
const char *resource,
146
const char *user, int version);
148
static void compress_files(int num_files, char **files);
149
#endif /* HAVE_LIBZ */
150
static void *monitor_printer(_cups_monitor_t *monitor);
151
static ipp_t *new_request(ipp_op_t op, int version, const char *uri,
152
const char *user, const char *title,
153
int num_options, cups_option_t *options,
154
const char *compression, int copies,
155
const char *format, _ppd_cache_t *pc,
156
ipp_attribute_t *media_col_sup,
157
ipp_attribute_t *doc_handling_sup);
158
static const char *password_cb(const char *);
159
static void report_attr(ipp_attribute_t *attr);
160
static void report_printer_state(ipp_t *ipp);
161
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
162
static int run_as_user(int argc, char *argv[], uid_t uid,
163
const char *device_uri, int fd);
164
#endif /* HAVE_GSSAPI && HAVE_XPC */
165
static void sigterm_handler(int sig);
166
static int timeout_cb(http_t *http, void *user_data);
167
static void update_reasons(ipp_attribute_t *attr, const char *s);
171
* 'main()' - Send a file to the printer or server.
175
* printer-uri job-id user title copies options [file]
178
int /* O - Exit status */
179
main(int argc, /* I - Number of command-line args */
180
char *argv[]) /* I - Command-line arguments */
182
int i; /* Looping var */
183
int send_options; /* Send job options? */
184
int num_options; /* Number of printer options */
185
cups_option_t *options; /* Printer options */
186
const char *device_uri; /* Device URI */
187
char scheme[255], /* Scheme in URI */
188
hostname[1024], /* Hostname */
189
username[255], /* Username info */
190
resource[1024], /* Resource info (printer name) */
191
addrname[256], /* Address name */
192
*optptr, /* Pointer to URI options */
193
*name, /* Name of option */
194
*value, /* Value of option */
195
sep; /* Separator character */
196
http_addrlist_t *addrlist; /* Address of printer */
197
int snmp_fd, /* SNMP socket */
198
start_count, /* Page count via SNMP at start */
199
page_count, /* Page count via SNMP */
200
have_supplies; /* Printer supports supply levels? */
201
int num_files; /* Number of files to print */
202
char **files, /* Files to print */
203
*compatfile = NULL; /* Compatibility filename */
204
off_t compatsize = 0; /* Size of compatibility file */
205
int port; /* Port number (not used) */
206
char portname[255]; /* Port name */
207
char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
208
http_status_t http_status; /* Status of HTTP request */
209
ipp_status_t ipp_status; /* Status of IPP request */
210
http_t *http; /* HTTP connection */
211
ipp_t *request, /* IPP request */
212
*response, /* IPP response */
213
*supported; /* get-printer-attributes response */
214
time_t start_time; /* Time of first connect */
215
int contimeout; /* Connection timeout */
216
int delay, /* Delay for retries */
217
prev_delay; /* Previous delay */
218
const char *compression; /* Compression mode */
219
int waitjob, /* Wait for job complete? */
220
waitprinter; /* Wait for printer ready? */
221
_cups_monitor_t monitor; /* Monitoring data */
222
ipp_attribute_t *job_id_attr; /* job-id attribute */
223
int job_id; /* job-id value */
224
ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
225
ipp_attribute_t *job_state; /* job-state */
226
ipp_attribute_t *copies_sup; /* copies-supported */
227
ipp_attribute_t *cups_version; /* cups-version */
228
ipp_attribute_t *format_sup; /* document-format-supported */
229
ipp_attribute_t *media_col_sup; /* media-col-supported */
230
ipp_attribute_t *operations_sup; /* operations-supported */
231
ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */
232
ipp_attribute_t *printer_state; /* printer-state attribute */
233
ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
234
int validate_job; /* Does printer support Validate-Job? */
235
int copies, /* Number of copies for job */
236
copies_remaining; /* Number of copies remaining */
237
const char *content_type, /* CONTENT_TYPE environment variable */
238
*final_content_type, /* FINAL_CONTENT_TYPE environment var */
239
*document_format; /* document-format value */
240
int fd; /* File descriptor */
241
off_t bytes = 0; /* Bytes copied */
242
char buffer[16384]; /* Copy buffer */
243
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
244
struct sigaction action; /* Actions for POSIX signals */
245
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
246
int version; /* IPP version */
247
ppd_file_t *ppd; /* PPD file */
248
_ppd_cache_t *pc; /* PPD cache and mapping data */
249
fd_set input; /* Input set for select() */
253
* Make sure status messages are not buffered...
256
setbuf(stderr, NULL);
259
* Ignore SIGPIPE and catch SIGTERM signals...
263
sigset(SIGPIPE, SIG_IGN);
264
sigset(SIGTERM, sigterm_handler);
265
#elif defined(HAVE_SIGACTION)
266
memset(&action, 0, sizeof(action));
267
action.sa_handler = SIG_IGN;
268
sigaction(SIGPIPE, &action, NULL);
270
sigemptyset(&action.sa_mask);
271
sigaddset(&action.sa_mask, SIGTERM);
272
action.sa_handler = sigterm_handler;
273
sigaction(SIGTERM, &action, NULL);
275
signal(SIGPIPE, SIG_IGN);
276
signal(SIGTERM, sigterm_handler);
277
#endif /* HAVE_SIGSET */
280
* Check command-line...
287
if ((s = strrchr(argv[0], '/')) != NULL)
292
printf("network %s \"Unknown\" \"%s (%s)\"\n",
293
s, _cupsLangString(cupsLangDefault(),
294
_("Internet Printing Protocol")), s);
295
return (CUPS_BACKEND_OK);
299
_cupsLangPrintf(stderr,
300
_("Usage: %s job-id user title copies options [file]"),
302
return (CUPS_BACKEND_STOP);
306
* Get the device URI...
309
while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
311
_cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
314
if (getenv("CLASS") != NULL)
315
return (CUPS_BACKEND_FAILED);
318
if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) == NULL)
319
auth_info_required = "none";
321
state_reasons = _cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"));
325
* For Kerberos, become the printing user (if we can) to get the credentials
329
if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
331
uid_t uid = (uid_t)atoi(value);
338
return (run_as_user(argc, argv, uid, device_uri, 0));
341
int status = 0; /* Exit status */
343
for (i = 6; i < argc && !status && !job_canceled; i ++)
345
if ((fd = open(argv[i], O_RDONLY)) >= 0)
347
status = run_as_user(argc, argv, uid, device_uri, fd);
352
_cupsLangPrintError("ERROR", _("Unable to open print file"));
353
status = CUPS_BACKEND_FAILED;
361
# else /* No XPC, just try to run as the user ID */
364
# endif /* HAVE_XPC */
366
#endif /* HAVE_GSSAPI */
369
* Get the (final) content type...
372
if ((content_type = getenv("CONTENT_TYPE")) == NULL)
373
content_type = "application/octet-stream";
375
if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
377
final_content_type = content_type;
379
if (!strncmp(final_content_type, "printer/", 8))
380
final_content_type = "application/vnd.cups-raw";
384
* Extract the hostname and printer name from the URI...
387
httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
388
username, sizeof(username), hostname, sizeof(hostname), &port,
389
resource, sizeof(resource));
392
port = IPP_PORT; /* Default to port 631 */
394
if (!strcmp(scheme, "https"))
395
cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
397
cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
400
* See if there are any options...
407
contimeout = 7 * 24 * 60 * 60;
409
if ((optptr = strchr(resource, '?')) != NULL)
412
* Yup, terminate the device name string and move to the first
413
* character of the optptr...
419
* Then parse the optptr...
430
while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
433
if ((sep = *optptr) != '\0')
444
while (*optptr && *optptr != '+' && *optptr != '&')
454
* Process the option...
457
if (!_cups_strcasecmp(name, "waitjob"))
460
* Wait for job completion?
463
waitjob = !_cups_strcasecmp(value, "on") ||
464
!_cups_strcasecmp(value, "yes") ||
465
!_cups_strcasecmp(value, "true");
467
else if (!_cups_strcasecmp(name, "waitprinter"))
470
* Wait for printer idle?
473
waitprinter = !_cups_strcasecmp(value, "on") ||
474
!_cups_strcasecmp(value, "yes") ||
475
!_cups_strcasecmp(value, "true");
477
else if (!_cups_strcasecmp(name, "encryption"))
480
* Enable/disable encryption?
483
if (!_cups_strcasecmp(value, "always"))
484
cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
485
else if (!_cups_strcasecmp(value, "required"))
486
cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
487
else if (!_cups_strcasecmp(value, "never"))
488
cupsSetEncryption(HTTP_ENCRYPT_NEVER);
489
else if (!_cups_strcasecmp(value, "ifrequested"))
490
cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
493
_cupsLangPrintFilter(stderr, "ERROR",
494
_("Unknown encryption option value: \"%s\"."),
498
else if (!_cups_strcasecmp(name, "version"))
500
if (!strcmp(value, "1.0"))
502
else if (!strcmp(value, "1.1"))
504
else if (!strcmp(value, "2.0"))
506
else if (!strcmp(value, "2.1"))
508
else if (!strcmp(value, "2.2"))
512
_cupsLangPrintFilter(stderr, "ERROR",
513
_("Unknown version option value: \"%s\"."),
518
else if (!_cups_strcasecmp(name, "compression"))
520
if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes") ||
521
!_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "gzip"))
522
compression = "gzip";
524
#endif /* HAVE_LIBZ */
525
else if (!_cups_strcasecmp(name, "contimeout"))
528
* Set the connection timeout...
532
contimeout = atoi(value);
540
_cupsLangPrintFilter(stderr, "ERROR",
541
_("Unknown option \"%s\" with value \"%s\"."),
548
* If we have 7 arguments, print the file named on the command-line.
549
* Otherwise, copy stdin to a temporary file and print the temporary
557
send_options = !_cups_strcasecmp(final_content_type, "application/pdf") ||
558
!_cups_strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
559
!_cups_strncasecmp(final_content_type, "image/", 6);
561
fputs("DEBUG: Sending stdin for job...\n", stderr);
566
* Point to the files on the command-line...
569
num_files = argc - 6;
575
compress_files(num_files, files);
576
#endif /* HAVE_LIBZ */
578
fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
582
* Set the authentication info, if any...
585
cupsSetPasswordCB(password_cb);
590
* Use authenticaion information in the device URI...
593
if ((password = strchr(username, ':')) != NULL)
596
cupsSetUser(username);
601
* Try loading authentication information from the environment.
604
const char *ptr = getenv("AUTH_USERNAME");
609
password = getenv("AUTH_PASSWORD");
613
* Try finding the remote server...
616
start_time = time(NULL);
618
sprintf(portname, "%d", port);
620
update_reasons(NULL, "+connecting-to-device");
621
fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
623
while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
625
_cupsLangPrintFilter(stderr, "INFO",
626
_("Unable to locate printer \"%s\"."), hostname);
629
if (getenv("CLASS") != NULL)
631
update_reasons(NULL, "-connecting-to-device");
632
return (CUPS_BACKEND_STOP);
636
http = _httpCreate(hostname, port, addrlist, cupsEncryption(), AF_UNSPEC);
637
httpSetTimeout(http, 30.0, timeout_cb, NULL);
640
* See if the printer supports SNMP...
643
if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0)
645
have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
649
have_supplies = start_count = 0;
652
* Wait for data from the filter...
657
if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
658
return (CUPS_BACKEND_OK);
659
else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
660
return (CUPS_BACKEND_OK);
664
* Try connecting to the remote server...
667
delay = _cupsNextDelay(0, &prev_delay);
671
fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
672
_cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
674
if (httpReconnect(http))
676
int error = errno; /* Connection error */
678
if (http->status == HTTP_PKI_ERROR)
679
update_reasons(NULL, "+cups-certificate-error");
684
if (getenv("CLASS") != NULL)
687
* If the CLASS environment variable is set, the job was submitted
688
* to a class and not to a specific queue. In this case, we want
689
* to abort immediately so that the job can be requeued on the next
690
* available printer in the class.
693
_cupsLangPrintFilter(stderr, "INFO",
694
_("Unable to contact printer, queuing on next "
695
"printer in class."));
698
* Sleep 5 seconds to keep the job from requeuing too rapidly...
703
update_reasons(NULL, "-connecting-to-device");
705
return (CUPS_BACKEND_FAILED);
708
fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
710
if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
711
errno == EHOSTUNREACH)
713
if (contimeout && (time(NULL) - start_time) > contimeout)
715
_cupsLangPrintFilter(stderr, "ERROR",
716
_("The printer is not responding."));
717
update_reasons(NULL, "-connecting-to-device");
718
return (CUPS_BACKEND_FAILED);
724
_cupsLangPrintFilter(stderr, "WARNING",
725
_("The printer may not exist or "
726
"is unavailable at this time."));
730
_cupsLangPrintFilter(stderr, "WARNING",
731
_("The printer is unreachable at this "
737
_cupsLangPrintFilter(stderr, "WARNING",
738
_("The printer is busy."));
744
delay = _cupsNextDelay(delay, &prev_delay);
748
_cupsLangPrintFilter(stderr, "ERROR",
749
_("The printer is not responding."));
757
update_reasons(NULL, "-cups-certificate-error");
759
while (http->fd < 0);
761
if (job_canceled || !http)
762
return (CUPS_BACKEND_FAILED);
764
update_reasons(NULL, "-connecting-to-device");
765
_cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
767
fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
768
httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
769
_httpAddrPort(http->hostaddr));
772
* Build a URI for the printer and fill the standard IPP attributes for
773
* an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
774
* might contain username:password information...
777
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
781
* First validate the destination and see if the device supports multiple
788
media_col_sup = NULL;
790
operations_sup = NULL;
791
doc_handling_sup = NULL;
797
* Check for side-channel requests...
800
backendCheckSideChannel(snmp_fd, http->hostaddr);
803
* Build the IPP request...
806
request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
807
request->request.op.version[0] = version / 10;
808
request->request.op.version[1] = version % 10;
810
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
813
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
814
"requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
821
fputs("DEBUG: Getting supported attributes...\n", stderr);
823
if (http->version < HTTP_1_1)
825
fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
826
http->version / 100, http->version % 100);
827
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
828
"cups-ipp-wrong-http-version");
831
supported = cupsDoRequest(http, request, resource);
832
ipp_status = cupsLastError();
834
fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
835
ippErrorString(ipp_status), cupsLastErrorString());
837
if (ipp_status > IPP_OK_CONFLICT)
839
fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
840
ippErrorString(ipp_status));
842
if (ipp_status == IPP_PRINTER_BUSY ||
843
ipp_status == IPP_SERVICE_UNAVAILABLE)
845
if (contimeout && (time(NULL) - start_time) > contimeout)
847
_cupsLangPrintFilter(stderr, "ERROR",
848
_("The printer is not responding."));
849
return (CUPS_BACKEND_FAILED);
852
_cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
854
report_printer_state(supported);
858
delay = _cupsNextDelay(delay, &prev_delay);
860
else if ((ipp_status == IPP_BAD_REQUEST ||
861
ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
864
* Switch to IPP/1.1 or IPP/1.0...
869
_cupsLangPrintFilter(stderr, "INFO",
870
_("Printer does not support IPP/%d.%d, trying "
871
"IPP/%s."), version / 10, version % 10, "1.1");
876
_cupsLangPrintFilter(stderr, "INFO",
877
_("Printer does not support IPP/%d.%d, trying "
878
"IPP/%s."), version / 10, version % 10, "1.0");
884
else if (ipp_status == IPP_NOT_FOUND)
886
_cupsLangPrintFilter(stderr, "ERROR",
887
_("The printer URI is incorrect or no longer "
890
ippDelete(supported);
892
return (CUPS_BACKEND_STOP);
894
else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
896
if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
898
auth_info_required = "negotiate";
900
auth_info_required = "username,password";
902
fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
903
return (CUPS_BACKEND_AUTH_REQUIRED);
907
_cupsLangPrintFilter(stderr, "ERROR",
908
_("Unable to get printer status."));
912
ippDelete(supported);
917
if (!getenv("CLASS"))
920
* Check printer-is-accepting-jobs = false and printer-state-reasons for the
921
* "spool-area-full" keyword...
926
if ((printer_accepting = ippFindAttribute(supported,
927
"printer-is-accepting-jobs",
928
IPP_TAG_BOOLEAN)) != NULL &&
929
!printer_accepting->values[0].boolean)
931
else if (!printer_accepting)
932
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
933
"cups-ipp-missing-printer-is-accepting-jobs");
935
if ((printer_state = ippFindAttribute(supported,
936
"printer-state-reasons",
937
IPP_TAG_KEYWORD)) != NULL && !busy)
939
for (i = 0; i < printer_state->num_values; i ++)
940
if (!strcmp(printer_state->values[0].string.text,
941
"spool-area-full") ||
942
!strncmp(printer_state->values[0].string.text, "spool-area-full-",
950
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
951
"cups-ipp-missing-printer-state-reasons");
955
_cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
957
report_printer_state(supported);
961
delay = _cupsNextDelay(delay, &prev_delay);
963
ippDelete(supported);
970
* Check for supported attributes...
973
if ((copies_sup = ippFindAttribute(supported, "copies-supported",
974
IPP_TAG_RANGE)) != NULL)
977
* Has the "copies-supported" attribute - does it have an upper
981
fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
982
copies_sup->values[0].range.lower,
983
copies_sup->values[0].range.upper);
985
if (copies_sup->values[0].range.upper <= 1)
986
copies_sup = NULL; /* No */
989
cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
991
if ((format_sup = ippFindAttribute(supported, "document-format-supported",
992
IPP_TAG_MIMETYPE)) != NULL)
994
fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
995
format_sup->num_values);
996
for (i = 0; i < format_sup->num_values; i ++)
997
fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
998
format_sup->values[i].string.text);
1001
if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
1002
IPP_TAG_KEYWORD)) != NULL)
1004
fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
1005
media_col_sup->num_values);
1006
for (i = 0; i < media_col_sup->num_values; i ++)
1007
fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1008
media_col_sup->values[i].string.text);
1011
if ((operations_sup = ippFindAttribute(supported, "operations-supported",
1012
IPP_TAG_ENUM)) != NULL)
1014
for (i = 0; i < operations_sup->num_values; i ++)
1015
if (operations_sup->values[i].integer == IPP_PRINT_JOB)
1018
if (i >= operations_sup->num_values)
1019
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1020
"cups-ipp-missing-print-job");
1022
for (i = 0; i < operations_sup->num_values; i ++)
1023
if (operations_sup->values[i].integer == IPP_CANCEL_JOB)
1026
if (i >= operations_sup->num_values)
1027
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1028
"cups-ipp-missing-cancel-job");
1030
for (i = 0; i < operations_sup->num_values; i ++)
1031
if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
1034
if (i >= operations_sup->num_values)
1035
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1036
"cups-ipp-missing-get-job-attributes");
1038
for (i = 0; i < operations_sup->num_values; i ++)
1039
if (operations_sup->values[i].integer == IPP_GET_PRINTER_ATTRIBUTES)
1042
if (i >= operations_sup->num_values)
1043
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1044
"cups-ipp-missing-get-printer-attributes");
1046
for (i = 0; i < operations_sup->num_values; i ++)
1047
if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
1054
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1055
"cups-ipp-missing-validate-job");
1058
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1059
"cups-ipp-missing-operations-supported");
1061
doc_handling_sup = ippFindAttribute(supported,
1062
"multiple-document-handling-supported",
1065
report_printer_state(supported);
1067
while (ipp_status > IPP_OK_CONFLICT);
1070
* See if the printer is accepting jobs and is not stopped; if either
1071
* condition is true and we are printing to a class, requeue the job...
1074
if (getenv("CLASS") != NULL)
1076
printer_state = ippFindAttribute(supported, "printer-state",
1078
printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
1081
if (printer_state == NULL ||
1082
(printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
1084
printer_accepting == NULL ||
1085
!printer_accepting->values[0].boolean)
1088
* If the CLASS environment variable is set, the job was submitted
1089
* to a class and not to a specific queue. In this case, we want
1090
* to abort immediately so that the job can be requeued on the next
1091
* available printer in the class.
1094
_cupsLangPrintFilter(stderr, "INFO",
1095
_("Unable to contact printer, queuing on next "
1096
"printer in class."));
1098
ippDelete(supported);
1102
* Sleep 5 seconds to keep the job from requeuing too rapidly...
1107
return (CUPS_BACKEND_FAILED);
1112
* See if the printer supports multiple copies...
1115
copies = atoi(argv[4]);
1117
if (copies_sup || argc < 7)
1119
copies_remaining = 1;
1121
if (argc < 7 && !send_options)
1125
copies_remaining = copies;
1128
* Prepare remaining printing options...
1136
num_options = cupsParseOptions(argv[5], 0, &options);
1138
if (!cups_version && media_col_sup)
1141
* Load the PPD file and generate PWG attribute mapping information...
1144
ppd = ppdOpenFile(getenv("PPD"));
1145
pc = _ppdCacheCreateWithPPD(ppd);
1153
document_format = NULL;
1155
if (format_sup != NULL)
1157
for (i = 0; i < format_sup->num_values; i ++)
1158
if (!_cups_strcasecmp(final_content_type, format_sup->values[i].string.text))
1160
fprintf(stderr, "DEBUG: Selected document_type %s\n", final_content_type);
1161
document_format = final_content_type;
1165
if (!document_format)
1167
for (i = 0; i < format_sup->num_values; i ++)
1168
if (!_cups_strcasecmp("application/octet-stream",
1169
format_sup->values[i].string.text))
1171
fprintf(stderr, "DEBUG: No document_type, forcing to NULL\n");
1172
document_format = NULL;
1179
* If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1180
* to a temporary file so that we can do a HTTP/1.0 submission...
1182
* (I hate compatibility hacks!)
1185
if (http->version < HTTP_1_1 && num_files == 0)
1187
if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1189
perror("DEBUG: Unable to create temporary file");
1190
return (CUPS_BACKEND_FAILED);
1193
_cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1195
compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1196
backendNetworkSideCB);
1200
compatfile = tmpfilename;
1201
files = &compatfile;
1204
else if (http->version < HTTP_1_1 && num_files == 1)
1206
struct stat fileinfo; /* File information */
1208
if (!stat(files[0], &fileinfo))
1209
compatsize = fileinfo.st_size;
1213
* Start monitoring the printer in the background...
1217
monitor.hostname = hostname;
1218
monitor.user = argv[2];
1219
monitor.resource = resource;
1220
monitor.port = port;
1221
monitor.version = version;
1223
monitor.encryption = cupsEncryption();
1224
monitor.job_state = IPP_JOB_PENDING;
1225
monitor.printer_state = IPP_PRINTER_IDLE;
1227
_cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1230
* Validate access to the printer...
1233
while (!job_canceled && validate_job)
1235
request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], argv[3],
1236
num_options, options, compression,
1237
copies_sup ? copies : 1, document_format, pc,
1238
media_col_sup, doc_handling_sup);
1240
ippDelete(cupsDoRequest(http, request, resource));
1242
ipp_status = cupsLastError();
1244
fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1245
ippErrorString(ipp_status), cupsLastErrorString());
1250
if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1252
_cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1255
else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1256
ipp_status == IPP_AUTHENTICATION_CANCELED)
1259
* Update auth-info-required as needed...
1262
fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1263
httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1266
* Normal authentication goes through the password callback, which sets
1267
* auth_info_required to "username,password". Kerberos goes directly
1268
* through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1269
* here and set auth_info_required as needed...
1272
if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1274
auth_info_required = "negotiate";
1276
auth_info_required = "username,password";
1280
else if (ipp_status == IPP_OPERATION_NOT_SUPPORTED)
1283
* This is all too common...
1286
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1287
"cups-ipp-missing-validate-job");
1290
else if (ipp_status < IPP_REDIRECTION_OTHER_SITE)
1295
* Then issue the print-job request...
1300
while (!job_canceled && copies_remaining > 0)
1303
* Check for side-channel requests...
1306
backendCheckSideChannel(snmp_fd, http->hostaddr);
1309
* Build the IPP job creation request...
1315
request = new_request(num_files > 1 ? IPP_CREATE_JOB : IPP_PRINT_JOB,
1316
version, uri, argv[2], argv[3], num_options, options,
1317
compression, copies_sup ? copies : 1, document_format,
1318
pc, media_col_sup, doc_handling_sup);
1325
response = cupsDoRequest(http, request, resource);
1328
size_t length = 0; /* Length of request */
1332
fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1333
length = ippLength(request) + (size_t)compatsize;
1336
fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1338
http_status = cupsSendRequest(http, request, resource, length);
1339
if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1342
fd = open(files[0], O_RDONLY);
1346
http_status = cupsWriteRequestData(http, buffer, bytes);
1349
while (http_status == HTTP_CONTINUE &&
1350
(!job_canceled || compatsize > 0))
1353
* Check for side-channel requests and more print data...
1358
FD_SET(snmp_fd, &input);
1360
while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
1361
NULL) <= 0 && !job_canceled);
1363
if (FD_ISSET(snmp_fd, &input))
1364
backendCheckSideChannel(snmp_fd, http->hostaddr);
1366
if (FD_ISSET(fd, &input))
1368
if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1370
fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1372
if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1375
else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
1384
response = cupsGetResponse(http, resource);
1388
ipp_status = cupsLastError();
1390
fprintf(stderr, "DEBUG: %s: %s (%s)\n",
1391
num_files > 1 ? "Create-Job" : "Print-Job",
1392
ippErrorString(ipp_status), cupsLastErrorString());
1394
if (ipp_status > IPP_OK_CONFLICT)
1401
if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1402
ipp_status == IPP_NOT_POSSIBLE ||
1403
ipp_status == IPP_PRINTER_BUSY)
1405
_cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1411
* We can't re-submit when we have no files to print, so exit
1412
* immediately with the right status code...
1418
else if (ipp_status == IPP_ERROR_JOB_CANCELED)
1423
* Update auth-info-required as needed...
1426
_cupsLangPrintFilter(stderr, "ERROR",
1427
_("Print file was not accepted."));
1429
if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1431
fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1432
httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1435
* Normal authentication goes through the password callback, which sets
1436
* auth_info_required to "username,password". Kerberos goes directly
1437
* through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1438
* here and set auth_info_required as needed...
1441
if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1443
auth_info_required = "negotiate";
1445
auth_info_required = "username,password";
1453
* We can't re-submit when we have no files to print, so exit
1454
* immediately with the right status code...
1461
else if ((job_id_attr = ippFindAttribute(response, "job-id",
1462
IPP_TAG_INTEGER)) == NULL)
1464
_cupsLangPrintFilter(stderr, "INFO",
1465
_("Print file accepted - job ID unknown."));
1466
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1467
"cups-ipp-missing-job-id");
1472
monitor.job_id = job_id = job_id_attr->values[0].integer;
1473
_cupsLangPrintFilter(stderr, "INFO",
1474
_("Print file accepted - job ID %d."), job_id);
1477
ippDelete(response);
1482
if (job_id && num_files > 1)
1484
for (i = 0; i < num_files; i ++)
1487
* Check for side-channel requests...
1490
backendCheckSideChannel(snmp_fd, http->hostaddr);
1493
* Send the next file in the job...
1496
request = ippNewRequest(IPP_SEND_DOCUMENT);
1497
request->request.op.version[0] = version / 10;
1498
request->request.op.version[1] = version % 10;
1500
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1503
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1507
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1508
"requesting-user-name", NULL, argv[2]);
1510
if ((i + 1) == num_files)
1511
ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1513
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1514
"document-format", NULL, content_type);
1516
fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1517
http_status = cupsSendRequest(http, request, resource, 0);
1518
if (http_status == HTTP_CONTINUE && request->state == IPP_DATA &&
1519
(fd = open(files[i], O_RDONLY)) >= 0)
1521
while (!job_canceled &&
1522
(bytes = read(fd, buffer, sizeof(buffer))) > 0)
1524
if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1529
* Check for side-channel requests...
1532
backendCheckSideChannel(snmp_fd, http->hostaddr);
1539
ippDelete(cupsGetResponse(http, resource));
1542
fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1543
ippErrorString(cupsLastError()), cupsLastErrorString());
1545
if (cupsLastError() > IPP_OK_CONFLICT)
1547
ipp_status = cupsLastError();
1549
_cupsLangPrintFilter(stderr, "ERROR",
1550
_("Unable to add document to print job."));
1556
if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1558
fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1559
copies_remaining --;
1561
else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1562
ipp_status == IPP_NOT_POSSIBLE ||
1563
ipp_status == IPP_PRINTER_BUSY)
1566
copies_remaining --;
1569
* Wait for the job to complete...
1572
if (!job_id || !waitjob)
1575
_cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
1577
for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
1580
* Check for side-channel requests...
1583
backendCheckSideChannel(snmp_fd, http->hostaddr);
1586
* Build an IPP_GET_JOB_ATTRIBUTES request...
1589
request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1590
request->request.op.version[0] = version / 10;
1591
request->request.op.version[1] = version % 10;
1593
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1596
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1600
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1601
"requesting-user-name", NULL, argv[2]);
1603
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1604
"requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1611
httpReconnect(http);
1612
response = cupsDoRequest(http, request, resource);
1613
ipp_status = cupsLastError();
1615
if (ipp_status == IPP_NOT_FOUND)
1618
* Job has gone away and/or the server has no job history...
1621
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1622
"cups-ipp-missing-job-history");
1623
ippDelete(response);
1625
ipp_status = IPP_OK;
1629
fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1630
ippErrorString(ipp_status), cupsLastErrorString());
1632
if (ipp_status > IPP_OK_CONFLICT)
1634
if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1635
ipp_status != IPP_NOT_POSSIBLE &&
1636
ipp_status != IPP_PRINTER_BUSY)
1638
ippDelete(response);
1640
_cupsLangPrintFilter(stderr, "ERROR",
1641
_("Unable to get print job status."));
1648
if ((job_state = ippFindAttribute(response, "job-state",
1649
IPP_TAG_ENUM)) != NULL)
1652
* Reflect the remote job state in the local queue...
1656
job_state->values[0].integer >= IPP_JOB_PENDING &&
1657
job_state->values[0].integer <= IPP_JOB_COMPLETED)
1658
update_reasons(NULL,
1659
remote_job_states[job_state->values[0].integer -
1662
if ((job_sheets = ippFindAttribute(response,
1663
"job-media-sheets-completed",
1664
IPP_TAG_INTEGER)) == NULL)
1665
job_sheets = ippFindAttribute(response,
1666
"job-impressions-completed",
1670
fprintf(stderr, "PAGE: total %d\n",
1671
job_sheets->values[0].integer);
1674
* Stop polling if the job is finished or pending-held...
1677
if (job_state->values[0].integer > IPP_JOB_STOPPED)
1679
ippDelete(response);
1683
else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1684
ipp_status != IPP_NOT_POSSIBLE &&
1685
ipp_status != IPP_PRINTER_BUSY)
1688
* If the printer does not return a job-state attribute, it does not
1689
* conform to the IPP specification - break out immediately and fail
1693
update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1694
"cups-ipp-missing-job-state");
1695
ipp_status = IPP_INTERNAL_ERROR;
1700
ippDelete(response);
1703
* Wait before polling again...
1708
delay = _cupsNextDelay(delay, &prev_delay);
1713
* Cancel the job as needed...
1716
if (job_canceled && job_id)
1717
cancel_job(http, uri, job_id, resource, argv[2], version);
1720
* Check the printer state and report it if necessary...
1723
check_printer_state(http, uri, resource, argv[2], version);
1726
* Collect the final page count as needed...
1729
if (have_supplies &&
1730
!backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1731
page_count > start_count)
1732
fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1736
* See if we used Kerberos at all...
1740
auth_info_required = "negotiate";
1741
#endif /* HAVE_GSSAPI */
1749
cupsFreeOptions(num_options, options);
1750
_ppdCacheDestroy(pc);
1754
ippDelete(supported);
1757
* Remove the temporary file(s) if necessary...
1761
unlink(tmpfilename);
1766
for (i = 0; i < num_files; i ++)
1769
#endif /* HAVE_LIBZ */
1772
* Return the queue status...
1775
if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1776
ipp_status == IPP_AUTHENTICATION_CANCELED ||
1777
ipp_status <= IPP_OK_CONFLICT)
1778
fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1780
if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1781
ipp_status == IPP_AUTHENTICATION_CANCELED)
1782
return (CUPS_BACKEND_AUTH_REQUIRED);
1783
else if (ipp_status == IPP_INTERNAL_ERROR)
1784
return (CUPS_BACKEND_STOP);
1785
else if (ipp_status == IPP_DOCUMENT_FORMAT ||
1786
ipp_status == IPP_CONFLICT)
1787
return (CUPS_BACKEND_FAILED);
1788
else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
1789
return (CUPS_BACKEND_RETRY_CURRENT);
1792
_cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
1793
return (CUPS_BACKEND_OK);
1799
* 'cancel_job()' - Cancel a print job.
1803
cancel_job(http_t *http, /* I - HTTP connection */
1804
const char *uri, /* I - printer-uri */
1805
int id, /* I - job-id */
1806
const char *resource, /* I - Resource path */
1807
const char *user, /* I - requesting-user-name */
1808
int version) /* I - IPP version */
1810
ipp_t *request; /* Cancel-Job request */
1813
_cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
1815
request = ippNewRequest(IPP_CANCEL_JOB);
1816
request->request.op.version[0] = version / 10;
1817
request->request.op.version[1] = version % 10;
1819
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1821
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1823
if (user && user[0])
1824
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1825
"requesting-user-name", NULL, user);
1831
ippDelete(cupsDoRequest(http, request, resource));
1833
if (cupsLastError() > IPP_OK_CONFLICT)
1834
_cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
1839
* 'check_printer_state()' - Check the printer state.
1842
static ipp_pstate_t /* O - Current printer-state */
1843
check_printer_state(
1844
http_t *http, /* I - HTTP connection */
1845
const char *uri, /* I - Printer URI */
1846
const char *resource, /* I - Resource path */
1847
const char *user, /* I - Username, if any */
1848
int version) /* I - IPP version */
1850
ipp_t *request, /* IPP request */
1851
*response; /* IPP response */
1852
ipp_attribute_t *attr; /* Attribute in response */
1853
ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
1854
/* Current printer-state */
1858
* Send a Get-Printer-Attributes request and log the results...
1861
request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1862
request->request.op.version[0] = version / 10;
1863
request->request.op.version[1] = version % 10;
1865
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1868
if (user && user[0])
1869
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1870
"requesting-user-name", NULL, user);
1872
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1873
"requested-attributes",
1874
(int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
1876
if ((response = cupsDoRequest(http, request, resource)) != NULL)
1878
report_printer_state(response);
1880
if ((attr = ippFindAttribute(response, "printer-state",
1881
IPP_TAG_ENUM)) != NULL)
1882
printer_state = (ipp_pstate_t)attr->values[0].integer;
1884
ippDelete(response);
1887
fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1888
ippErrorString(cupsLastError()), cupsLastErrorString());
1891
* Return the printer-state value...
1894
return (printer_state);
1900
* 'compress_files()' - Compress print files.
1904
compress_files(int num_files, /* I - Number of files */
1905
char **files) /* I - Files */
1907
int i, /* Looping var */
1908
fd; /* Temporary file descriptor */
1909
ssize_t bytes; /* Bytes read/written */
1910
size_t total; /* Total bytes read */
1911
cups_file_t *in, /* Input file */
1912
*out; /* Output file */
1913
struct stat outinfo; /* Output file information */
1914
char filename[1024], /* Temporary filename */
1915
buffer[32768]; /* Copy buffer */
1918
fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1919
for (i = 0; i < num_files; i ++)
1921
if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1923
_cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1924
exit(CUPS_BACKEND_FAILED);
1927
if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1929
_cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1930
exit(CUPS_BACKEND_FAILED);
1933
if ((in = cupsFileOpen(files[i], "r")) == NULL)
1935
_cupsLangPrintError("ERROR", _("Unable to open print file"));
1937
exit(CUPS_BACKEND_FAILED);
1941
while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1942
if (cupsFileWrite(out, buffer, bytes) < bytes)
1944
_cupsLangPrintError("ERROR",
1945
_("Unable to generate compressed print file"));
1948
exit(CUPS_BACKEND_FAILED);
1956
files[i] = strdup(filename);
1958
if (!stat(filename, &outinfo))
1960
"DEBUG: File %d compressed to %.1f%% of original size, "
1961
CUPS_LLFMT " bytes...\n",
1962
i + 1, 100.0 * outinfo.st_size / total,
1963
CUPS_LLCAST outinfo.st_size);
1966
#endif /* HAVE_LIBZ */
1970
* 'monitor_printer()' - Monitor the printer state.
1973
static void * /* O - Thread exit code */
1975
_cups_monitor_t *monitor) /* I - Monitoring data */
1977
http_t *http; /* Connection to printer */
1978
ipp_t *request, /* IPP request */
1979
*response; /* IPP response */
1980
ipp_attribute_t *attr; /* Attribute in response */
1981
int delay, /* Current delay */
1982
prev_delay; /* Previous delay */
1986
* Make a copy of the printer connection...
1989
http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
1991
httpSetTimeout(http, 30.0, timeout_cb, NULL);
1992
cupsSetPasswordCB(password_cb);
1995
* Loop until the job is canceled, aborted, or completed.
1998
delay = _cupsNextDelay(0, &prev_delay);
2000
while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
2003
* Reconnect to the printer...
2006
if (!httpReconnect(http))
2009
* Connected, so check on the printer state...
2012
monitor->printer_state = check_printer_state(http, monitor->uri,
2017
if (monitor->job_id > 0)
2020
* Check the status of the job itself...
2023
request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
2024
request->request.op.version[0] = monitor->version / 10;
2025
request->request.op.version[1] = monitor->version % 10;
2027
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2028
NULL, monitor->uri);
2029
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2032
if (monitor->user && monitor->user[0])
2033
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2034
"requesting-user-name", NULL, monitor->user);
2036
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2037
"requested-attributes",
2038
(int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
2044
response = cupsDoRequest(http, request, monitor->resource);
2046
fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
2047
ippErrorString(cupsLastError()), cupsLastErrorString());
2049
if ((attr = ippFindAttribute(response, "job-state",
2050
IPP_TAG_ENUM)) != NULL)
2051
monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
2053
monitor->job_state = IPP_JOB_COMPLETED;
2055
ippDelete(response);
2059
* Disconnect from the printer - we'll reconnect on the next poll...
2062
_httpDisconnect(http);
2066
* Sleep for N seconds...
2071
delay = _cupsNextDelay(delay, &prev_delay);
2075
* Cleanup and return...
2085
* 'new_request()' - Create a new print creation or validation request.
2088
static ipp_t * /* O - Request data */
2090
ipp_op_t op, /* I - IPP operation code */
2091
int version, /* I - IPP version number */
2092
const char *uri, /* I - printer-uri value */
2093
const char *user, /* I - requesting-user-name value */
2094
const char *title, /* I - job-name value */
2095
int num_options, /* I - Number of options to send */
2096
cups_option_t *options, /* I - Options to send */
2097
const char *compression, /* I - compression value or NULL */
2098
int copies, /* I - copies value or 0 */
2099
const char *format, /* I - documet-format value or NULL */
2100
_ppd_cache_t *pc, /* I - PPD cache and mapping data */
2101
ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
2102
ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */
2104
int i; /* Looping var */
2105
ipp_t *request; /* Request data */
2106
const char *keyword; /* PWG keyword */
2107
_pwg_size_t *size; /* PWG media size */
2108
ipp_t *media_col, /* media-col value */
2109
*media_size; /* media-size value */
2110
const char *media_source, /* media-source value */
2111
*media_type, /* media-type value */
2112
*collate_str; /* multiple-document-handling value */
2116
* Create the IPP request...
2119
request = ippNewRequest(op);
2120
request->request.op.version[0] = version / 10;
2121
request->request.op.version[1] = version % 10;
2123
fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
2124
ippOpString(request->request.op.operation_id),
2125
request->request.op.version[0],
2126
request->request.op.version[1]);
2129
* Add standard attributes...
2132
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2134
fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
2138
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2139
"requesting-user-name", NULL, user);
2140
fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
2143
if (title && *title)
2145
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
2147
fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
2152
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
2153
"document-format", NULL, format);
2154
fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
2160
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2161
"compression", NULL, compression);
2162
fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2164
#endif /* HAVE_LIBZ */
2167
* Handle options on the command-line...
2170
if (num_options > 0)
2175
* Send standard IPP attributes...
2178
if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2179
keyword = cupsGetOption("media", num_options, options);
2181
if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2184
* Add a media-col value...
2187
media_size = ippNew();
2188
ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2189
"x-dimension", size->width);
2190
ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2191
"y-dimension", size->length);
2193
media_col = ippNew();
2194
ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2196
media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2199
media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2203
for (i = 0; i < media_col_sup->num_values; i ++)
2205
if (!strcmp(media_col_sup->values[i].string.text,
2206
"media-left-margin"))
2207
ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2208
"media-left-margin", size->left);
2209
else if (!strcmp(media_col_sup->values[i].string.text,
2210
"media-bottom-margin"))
2211
ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2212
"media-bottom-margin", size->bottom);
2213
else if (!strcmp(media_col_sup->values[i].string.text,
2214
"media-right-margin"))
2215
ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2216
"media-right-margin", size->right);
2217
else if (!strcmp(media_col_sup->values[i].string.text,
2218
"media-top-margin"))
2219
ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2220
"media-top-margin", size->top);
2221
else if (!strcmp(media_col_sup->values[i].string.text,
2222
"media-source") && media_source)
2223
ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2224
"media-source", NULL, media_source);
2225
else if (!strcmp(media_col_sup->values[i].string.text,
2226
"media-type") && media_type)
2227
ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2228
"media-type", NULL, media_type);
2231
ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2234
if ((keyword = cupsGetOption("output-bin", num_options,
2236
keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2240
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2243
if ((keyword = cupsGetOption("output-mode", num_options,
2245
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2247
else if ((keyword = cupsGetOption("ColorModel", num_options,
2250
if (!_cups_strcasecmp(keyword, "Gray"))
2251
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2252
NULL, "monochrome");
2254
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2258
if ((keyword = cupsGetOption("print-quality", num_options,
2260
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2262
else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2265
if (!_cups_strcasecmp(keyword, "draft"))
2266
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2268
else if (!_cups_strcasecmp(keyword, "normal"))
2269
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2270
IPP_QUALITY_NORMAL);
2271
else if (!_cups_strcasecmp(keyword, "high"))
2272
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2276
if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2277
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2279
else if (pc->sides_option &&
2280
(keyword = cupsGetOption(pc->sides_option, num_options,
2283
if (!_cups_strcasecmp(keyword, pc->sides_1sided))
2284
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2286
else if (!_cups_strcasecmp(keyword, pc->sides_2sided_long))
2287
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2288
NULL, "two-sided-long-edge");
2289
if (!_cups_strcasecmp(keyword, pc->sides_2sided_short))
2290
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2291
NULL, "two-sided-short-edge");
2294
if (doc_handling_sup &&
2295
(keyword = cupsGetOption("collate", num_options, options)) != NULL)
2297
if (!_cups_strcasecmp(keyword, "true"))
2298
collate_str = "separate-documents-collated-copies";
2300
collate_str = "separate-documents-uncollated-copies";
2302
for (i = 0; i < doc_handling_sup->num_values; i ++)
2303
if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2305
ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2306
"multiple-document-handling", NULL, collate_str);
2314
* When talking to another CUPS server, send all options...
2317
cupsEncodeOptions(request, num_options, options);
2321
ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2329
* 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2332
static const char * /* O - Password */
2333
password_cb(const char *prompt) /* I - Prompt (not used) */
2338
* Remember that we need to authenticate...
2341
auth_info_required = "username,password";
2343
if (password && *password && password_tries < 3)
2352
* Give up after 3 tries or if we don't have a password to begin with...
2361
* 'report_attr()' - Report an IPP attribute value.
2365
report_attr(ipp_attribute_t *attr) /* I - Attribute */
2367
int i; /* Looping var */
2368
char value[1024], /* Value string */
2369
*valptr, /* Pointer into value string */
2370
*attrptr; /* Pointer into attribute value */
2371
const char *cached; /* Cached attribute */
2375
* Convert the attribute values into quoted strings...
2378
for (i = 0, valptr = value;
2379
i < attr->num_values && valptr < (value + sizeof(value) - 10);
2385
switch (attr->value_tag)
2387
case IPP_TAG_INTEGER :
2389
snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2390
attr->values[i].integer);
2391
valptr += strlen(valptr);
2396
case IPP_TAG_KEYWORD :
2398
for (attrptr = attr->values[i].string.text;
2399
*attrptr && valptr < (value + sizeof(value) - 10);
2402
if (*attrptr == '\\' || *attrptr == '\"')
2405
*valptr++ = *attrptr;
2412
* Unsupported value type...
2421
_cupsMutexLock(&report_mutex);
2423
if ((cached = cupsGetOption(attr->name, num_attr_cache,
2424
attr_cache)) == NULL || strcmp(cached, value))
2427
* Tell the scheduler about the new values...
2430
num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
2432
fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2435
_cupsMutexUnlock(&report_mutex);
2440
* 'report_printer_state()' - Report the printer state.
2444
report_printer_state(ipp_t *ipp) /* I - IPP response */
2446
ipp_attribute_t *pa, /* printer-alert */
2447
*pam, /* printer-alert-message */
2448
*psm, /* printer-state-message */
2449
*reasons, /* printer-state-reasons */
2450
*marker; /* marker-* attributes */
2451
char value[1024], /* State/message string */
2452
*valptr; /* Pointer into string */
2453
static int ipp_supplies = -1;
2454
/* Report supply levels? */
2458
* Report alerts and messages...
2461
if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2464
if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2465
IPP_TAG_TEXT)) != NULL)
2468
if ((psm = ippFindAttribute(ipp, "printer-state-message",
2469
IPP_TAG_TEXT)) != NULL)
2471
char *ptr; /* Pointer into message */
2474
strlcpy(value, "INFO: ", sizeof(value));
2475
for (ptr = psm->values[0].string.text, valptr = value + 6;
2476
*ptr && valptr < (value + sizeof(value) - 6);
2479
if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2482
* Substitute "<XX>" for the control character; sprintf is safe because
2483
* we always leave 6 chars free at the end...
2486
sprintf(valptr, "<%02X>", *ptr);
2496
fputs(value, stderr);
2500
* Now report printer-state-reasons, filtering out some of the reasons we never
2504
if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2505
IPP_TAG_KEYWORD)) == NULL)
2508
update_reasons(reasons, NULL);
2511
* Relay the current marker-* attribute values...
2514
if (ipp_supplies < 0)
2516
ppd_file_t *ppd; /* PPD file */
2517
ppd_attr_t *ppdattr; /* Attribute in PPD file */
2519
if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2520
(ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
2521
ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
2529
if (ipp_supplies > 0)
2531
if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2532
report_attr(marker);
2533
if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2534
IPP_TAG_INTEGER)) != NULL)
2535
report_attr(marker);
2536
if ((marker = ippFindAttribute(ipp, "marker-levels",
2537
IPP_TAG_INTEGER)) != NULL)
2538
report_attr(marker);
2539
if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2540
IPP_TAG_INTEGER)) != NULL)
2541
report_attr(marker);
2542
if ((marker = ippFindAttribute(ipp, "marker-message",
2543
IPP_TAG_TEXT)) != NULL)
2544
report_attr(marker);
2545
if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2546
report_attr(marker);
2547
if ((marker = ippFindAttribute(ipp, "marker-types",
2548
IPP_TAG_KEYWORD)) != NULL)
2549
report_attr(marker);
2554
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2556
* 'run_as_user()' - Run the IPP backend as the printing user.
2558
* This function uses an XPC-based user agent to run the backend as the printing
2559
* user. We need to do this in order to have access to the user's Kerberos
2563
static int /* O - Exit status */
2564
run_as_user(int argc, /* I - Number of command-line args */
2565
char *argv[], /* I - Command-line arguments */
2566
uid_t uid, /* I - User ID */
2567
const char *device_uri, /* I - Device URI */
2568
int fd) /* I - File to print */
2570
const char *auth_negotiate;/* AUTH_NEGOTIATE env var */
2571
xpc_connection_t conn; /* Connection to XPC service */
2572
xpc_object_t request; /* Request message dictionary */
2573
__block xpc_object_t response; /* Response message dictionary */
2574
dispatch_semaphore_t sem; /* Semaphore for waiting for response */
2575
int status = CUPS_BACKEND_FAILED;
2576
/* Status of request */
2579
fprintf(stderr, "DEBUG: Running IPP backend as UID %d.\n", (int)uid);
2582
* Connect to the user agent for the specified UID...
2585
conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
2586
dispatch_get_global_queue(0, 0), 0);
2589
_cupsLangPrintFilter(stderr, "ERROR",
2590
_("Unable to start backend process."));
2591
fputs("DEBUG: Unable to create connection to agent.\n", stderr);
2595
xpc_connection_set_event_handler(conn,
2596
^(xpc_object_t event)
2598
xpc_type_t messageType = xpc_get_type(event);
2600
if (messageType == XPC_TYPE_ERROR)
2602
if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
2603
fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
2604
xpc_connection_get_name(conn));
2605
else if (event == XPC_ERROR_CONNECTION_INVALID)
2606
fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
2607
xpc_connection_get_name(conn));
2609
fprintf(stderr, "DEBUG: Unxpected error for service %s: %s\n",
2610
xpc_connection_get_name(conn),
2611
xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
2614
xpc_connection_set_target_uid(conn, uid);
2615
xpc_connection_resume(conn);
2618
* Try starting the backend...
2621
request = xpc_dictionary_create(NULL, NULL, 0);
2622
xpc_dictionary_set_int64(request, "command", kPMStartJob);
2623
xpc_dictionary_set_string(request, "device-uri", device_uri);
2624
xpc_dictionary_set_string(request, "job-id", argv[1]);
2625
xpc_dictionary_set_string(request, "user", argv[2]);
2626
xpc_dictionary_set_string(request, "title", argv[3]);
2627
xpc_dictionary_set_string(request, "copies", argv[4]);
2628
xpc_dictionary_set_string(request, "options", argv[5]);
2629
xpc_dictionary_set_string(request, "auth-info-required",
2630
getenv("AUTH_INFO_REQUIRED"));
2631
if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
2632
xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
2633
xpc_dictionary_set_fd(request, "stdin", fd);
2634
xpc_dictionary_set_fd(request, "stderr", 2);
2635
xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
2637
sem = dispatch_semaphore_create(0);
2640
xpc_connection_send_message_with_reply(conn, request,
2641
dispatch_get_global_queue(0,0),
2642
^(xpc_object_t reply)
2644
/* Save the response and wake up */
2645
if (xpc_get_type(reply)
2646
== XPC_TYPE_DICTIONARY)
2647
response = xpc_retain(reply);
2649
dispatch_semaphore_signal(sem);
2652
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2653
xpc_release(request);
2654
dispatch_release(sem);
2658
child_pid = xpc_dictionary_get_int64(response, "child-pid");
2660
xpc_release(response);
2663
fprintf(stderr, "DEBUG: Child PID=%d.\n", child_pid);
2666
_cupsLangPrintFilter(stderr, "ERROR",
2667
_("Unable to start backend process."));
2668
fputs("DEBUG: No child PID.\n", stderr);
2674
_cupsLangPrintFilter(stderr, "ERROR",
2675
_("Unable to start backend process."));
2676
fputs("DEBUG: No reply from agent.\n", stderr);
2681
* Then wait for the backend to finish...
2684
request = xpc_dictionary_create(NULL, NULL, 0);
2685
xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
2686
xpc_dictionary_set_fd(request, "stderr", 2);
2688
sem = dispatch_semaphore_create(0);
2691
xpc_connection_send_message_with_reply(conn, request,
2692
dispatch_get_global_queue(0,0),
2693
^(xpc_object_t reply)
2695
/* Save the response and wake up */
2696
if (xpc_get_type(reply)
2697
== XPC_TYPE_DICTIONARY)
2698
response = xpc_retain(reply);
2700
dispatch_semaphore_signal(sem);
2703
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2704
xpc_release(request);
2705
dispatch_release(sem);
2709
status = xpc_dictionary_get_int64(response, "status");
2711
if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
2713
fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
2714
status = CUPS_BACKEND_FAILED;
2716
else if (WIFSIGNALED(status))
2718
fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
2719
status = CUPS_BACKEND_STOP;
2721
else if (WIFEXITED(status))
2723
status = WEXITSTATUS(status);
2724
fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
2727
xpc_release(response);
2730
_cupsLangPrintFilter(stderr, "ERROR",
2731
_("Unable to get backend exit status."));
2737
xpc_connection_suspend(conn);
2738
xpc_connection_cancel(conn);
2744
#endif /* HAVE_GSSAPI && HAVE_XPC */
2748
* 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2752
sigterm_handler(int sig) /* I - Signal */
2754
(void)sig; /* remove compiler warnings... */
2756
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2759
kill(child_pid, sig);
2762
#endif /* HAVE_GSSAPI && HAVE_XPC */
2767
* Flag that the job should be canceled...
2775
* The scheduler already tried to cancel us once, now just terminate
2776
* after removing our temp file!
2780
unlink(tmpfilename);
2787
* 'timeout_cb()' - Handle HTTP timeouts.
2790
static int /* O - 1 to continue, 0 to cancel */
2791
timeout_cb(http_t *http, /* I - Connection to server (unused) */
2792
void *user_data) /* I - User data (unused) */
2797
return (!job_canceled);
2802
* 'update_reasons()' - Update the printer-state-reasons values.
2806
update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */
2807
const char *s) /* I - STATE: string or NULL */
2809
char op; /* Add (+), remove (-), replace (\0) */
2810
cups_array_t *new_reasons; /* New reasons array */
2811
char *reason, /* Current reason */
2812
add[2048], /* Reasons added string */
2813
*addptr, /* Pointer into add string */
2814
rem[2048], /* Reasons removed string */
2815
*remptr; /* Pointer into remove string */
2816
const char *addprefix, /* Current add string prefix */
2817
*remprefix; /* Current remove string prefix */
2820
fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
2821
attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
2822
attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
2825
* Create an array of new reason keyword strings...
2830
int i; /* Looping var */
2832
new_reasons = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2835
for (i = 0; i < attr->num_values; i ++)
2837
reason = attr->values[i].string.text;
2839
if (strcmp(reason, "none") &&
2840
strcmp(reason, "none-report") &&
2841
strcmp(reason, "paused") &&
2842
strncmp(reason, "spool-area-full", 15) &&
2843
strcmp(reason, "com.apple.print.recoverable-warning") &&
2844
strncmp(reason, "cups-", 5))
2845
cupsArrayAdd(new_reasons, reason);
2850
if (*s == '+' || *s == '-')
2855
new_reasons = _cupsArrayNewStrings(s);
2861
* Compute the changes...
2865
addprefix = "STATE: +";
2868
remprefix = "STATE: -";
2871
fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
2872
op ? op : ' ', cupsArrayCount(new_reasons),
2873
cupsArrayCount(state_reasons));
2875
_cupsMutexLock(&report_mutex);
2883
for (reason = (char *)cupsArrayFirst(new_reasons);
2885
reason = (char *)cupsArrayNext(new_reasons))
2887
if (!cupsArrayFind(state_reasons, reason))
2889
if (!strncmp(reason, "cups-remote-", 12))
2892
* If we are setting cups-remote-xxx, remove all other cups-remote-xxx
2896
char *temp; /* Current reason in state_reasons */
2898
cupsArraySave(state_reasons);
2900
for (temp = (char *)cupsArrayFirst(state_reasons);
2902
temp = (char *)cupsArrayNext(state_reasons))
2903
if (!strncmp(temp, "cups-remote-", 12))
2905
snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
2907
remptr += strlen(remptr);
2910
cupsArrayRemove(state_reasons, temp);
2914
cupsArrayRestore(state_reasons);
2917
cupsArrayAdd(state_reasons, reason);
2919
snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
2921
addptr += strlen(addptr);
2932
for (reason = (char *)cupsArrayFirst(new_reasons);
2934
reason = (char *)cupsArrayNext(new_reasons))
2936
if (cupsArrayFind(state_reasons, reason))
2938
snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
2940
remptr += strlen(remptr);
2943
cupsArrayRemove(state_reasons, reason);
2950
* Replace reasons...
2953
for (reason = (char *)cupsArrayFirst(state_reasons);
2955
reason = (char *)cupsArrayNext(state_reasons))
2957
if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
2959
snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
2961
remptr += strlen(remptr);
2964
cupsArrayRemove(state_reasons, reason);
2968
for (reason = (char *)cupsArrayFirst(new_reasons);
2970
reason = (char *)cupsArrayNext(new_reasons))
2972
if (!cupsArrayFind(state_reasons, reason))
2974
cupsArrayAdd(state_reasons, reason);
2976
snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
2978
addptr += strlen(addptr);
2984
_cupsMutexUnlock(&report_mutex);
2987
* Report changes and return...
2990
if (add[0] && rem[0])
2991
fprintf(stderr, "%s\n%s\n", add, rem);
2993
fprintf(stderr, "%s\n", add);
2995
fprintf(stderr, "%s\n", rem);
2999
* End of "$Id: ipp.c 10112 2011-11-07 06:08:44Z mike $".