]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Use Create-Job/Send-Document when the printer supports it for nicer job cancels.
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
c9d3f842 2 * "$Id$"
c8f9565c 3 *
6875feac 4 * IPP backend for CUPS.
c8f9565c 5 *
5ed93a63 6 * Copyright 2007-2012 by Apple Inc.
bb3ff448 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
c8f9565c 8 *
9 * These coded instructions, statements, and computer programs are the
4e8d321f 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
c8f9565c 12 * "LICENSE" which should have been included with this file. If this
4e8d321f 13 * file is missing or damaged, see the license at "http://www.cups.org/".
c8f9565c 14 *
dab1a4d8 15 * This file is subject to the Apple OS-Developed Software exception.
16 *
c8f9565c 17 * Contents:
18 *
d1c2727f 19 * main() - Send a file to the printer or server.
1230a8a0 20 * cancel_job() - Cancel a print job.
b9738d7c 21 * check_printer_state() - Check the printer state.
959a6a28 22 * compress_files() - Compress print files.
23 * monitor_printer() - Monitor the printer state.
2ec940b5 24 * new_request() - Create a new print creation or validation request.
d1c2727f 25 * password_cb() - Disable the password prompt for
26 * cupsDoFileRequest().
7c7997c3 27 * report_attr() - Report an IPP attribute value.
d1c2727f 28 * report_printer_state() - Report the printer state.
959a6a28 29 * run_as_user() - Run the IPP backend as the printing user.
1179bc03 30 * timeout_cb() - Handle HTTP timeouts.
8f4595eb 31 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
c8f9565c 32 */
33
34/*
35 * Include necessary headers.
36 */
37
eab5b04e 38#include "backend-private.h"
7b1ef0fc 39#include <cups/array-private.h>
c8f9565c 40#include <sys/types.h>
41#include <sys/stat.h>
1c85e5c0 42#include <sys/wait.h>
959a6a28 43#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
44# include <xpc/xpc.h>
45# define kPMPrintUIToolAgent "com.apple.printuitool.agent"
46# define kPMStartJob 100
47# define kPMWaitForJob 101
48extern void xpc_connection_set_target_uid(xpc_connection_t connection,
49 uid_t uid);
50#endif /* HAVE_GSSAPI && HAVE_XPC */
c8f9565c 51
b9738d7c 52
53/*
54 * Types...
55 */
56
57typedef struct _cups_monitor_s /**** Monitoring data ****/
58{
59 const char *uri, /* Printer URI */
60 *hostname, /* Hostname */
61 *user, /* Username */
62 *resource; /* Resource path */
63 int port, /* Port number */
64 version, /* IPP version */
65 job_id; /* Job ID for submitted job */
5ed93a63 66 const char *job_name; /* Job name for submitted job */
b9738d7c 67 http_encryption_t encryption; /* Use encryption? */
68 ipp_jstate_t job_state; /* Current job state */
69 ipp_pstate_t printer_state; /* Current printer state */
70} _cups_monitor_t;
71
72
8f4595eb 73/*
74 * Globals...
75 */
76
7b1ef0fc 77static const char *auth_info_required;
4233257e 78 /* New auth-info-required value */
959a6a28 79#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
7b1ef0fc 80static int child_pid = 0; /* Child process ID */
959a6a28 81#endif /* HAVE_GSSAPI && HAVE_XPC */
b9738d7c 82static const char * const jattrs[] = /* Job attributes we want */
83{
f8c0cc95 84 "job-impressions-completed",
b9738d7c 85 "job-media-sheets-completed",
5ed93a63 86 "job-name",
87 "job-originating-user-name",
b9738d7c 88 "job-state",
89 "job-state-reasons"
90};
7b1ef0fc 91static int job_canceled = 0;
92 /* Job cancelled? */
93static char *password = NULL;
94 /* Password for device URI */
95static int password_tries = 0;
96 /* Password tries */
b9738d7c 97static const char * const pattrs[] = /* Printer attributes we want */
98{
99 "copies-supported",
100 "cups-version",
101 "document-format-supported",
102 "marker-colors",
103 "marker-high-levels",
104 "marker-levels",
105 "marker-low-levels",
106 "marker-message",
107 "marker-names",
108 "marker-types",
109 "media-col-supported",
adbcb02a 110 "multiple-document-handling-supported",
e2fe9bbd 111 "operations-supported",
b9738d7c 112 "printer-alert",
113 "printer-alert-description",
114 "printer-is-accepting-jobs",
115 "printer-state",
116 "printer-state-message",
2a46c0fe 117 "printer-state-reasons"
b9738d7c 118};
6c1b8723 119static const char * const remote_job_states[] =
120{ /* Remote job state keywords */
dbc9263b 121 "+cups-remote-pending",
122 "+cups-remote-pending-held",
123 "+cups-remote-processing",
124 "+cups-remote-stopped",
125 "+cups-remote-canceled",
126 "+cups-remote-aborted",
127 "+cups-remote-completed"
6c1b8723 128};
6ef97027 129static _cups_mutex_t report_mutex = _CUPS_MUTEX_INITIALIZER;
7b1ef0fc 130 /* Mutex to control access */
6ef97027 131static int num_attr_cache = 0;
132 /* Number of cached attributes */
133static cups_option_t *attr_cache = NULL;
134 /* Cached attributes */
135static cups_array_t *state_reasons; /* Array of printe-state-reasons keywords */
7b1ef0fc 136static char tmpfilename[1024] = "";
137 /* Temporary spool file name */
8f4595eb 138
139
b5cb0608 140/*
141 * Local functions...
142 */
143
b9738d7c 144static void cancel_job(http_t *http, const char *uri, int id,
145 const char *resource, const char *user,
146 int version);
147static ipp_pstate_t check_printer_state(http_t *http, const char *uri,
148 const char *resource,
4a241564 149 const char *user, int version);
1230a8a0 150#ifdef HAVE_LIBZ
b9738d7c 151static void compress_files(int num_files, char **files);
1230a8a0 152#endif /* HAVE_LIBZ */
b9738d7c 153static void *monitor_printer(_cups_monitor_t *monitor);
2ec940b5 154static ipp_t *new_request(ipp_op_t op, int version, const char *uri,
155 const char *user, const char *title,
156 int num_options, cups_option_t *options,
157 const char *compression, int copies,
8211508e 158 const char *format, _ppd_cache_t *pc,
adbcb02a 159 ipp_attribute_t *media_col_sup,
160 ipp_attribute_t *doc_handling_sup);
b9738d7c 161static const char *password_cb(const char *);
162static void report_attr(ipp_attribute_t *attr);
4a241564 163static void report_printer_state(ipp_t *ipp);
959a6a28 164#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
165static int run_as_user(int argc, char *argv[], uid_t uid,
166 const char *device_uri, int fd);
167#endif /* HAVE_GSSAPI && HAVE_XPC */
b9738d7c 168static void sigterm_handler(int sig);
1179bc03 169static int timeout_cb(http_t *http, void *user_data);
7b1ef0fc 170static void update_reasons(ipp_attribute_t *attr, const char *s);
a684a3b0 171
b5cb0608 172
c8f9565c 173/*
174 * 'main()' - Send a file to the printer or server.
175 *
176 * Usage:
177 *
178 * printer-uri job-id user title copies options [file]
179 */
180
74ef1ffb 181int /* O - Exit status */
072c4872 182main(int argc, /* I - Number of command-line args */
74ef1ffb 183 char *argv[]) /* I - Command-line arguments */
c8f9565c 184{
74ef1ffb 185 int i; /* Looping var */
cad2b75f 186 int send_options; /* Send job options? */
74ef1ffb 187 int num_options; /* Number of printer options */
188 cups_option_t *options; /* Printer options */
cda2b561 189 const char *device_uri; /* Device URI */
9fa9ba8f 190 char scheme[255], /* Scheme in URI */
74ef1ffb 191 hostname[1024], /* Hostname */
192 username[255], /* Username info */
193 resource[1024], /* Resource info (printer name) */
a0c0bbe7 194 addrname[256], /* Address name */
74ef1ffb 195 *optptr, /* Pointer to URI options */
abd1f85f 196 *name, /* Name of option */
197 *value, /* Value of option */
198 sep; /* Separator character */
cce0044f 199 http_addrlist_t *addrlist; /* Address of printer */
eab5b04e 200 int snmp_fd, /* SNMP socket */
201 start_count, /* Page count via SNMP at start */
6bffe4a6 202 page_count, /* Page count via SNMP */
203 have_supplies; /* Printer supports supply levels? */
072c4872 204 int num_files; /* Number of files to print */
940afb4b 205 char **files, /* Files to print */
206 *compatfile = NULL; /* Compatibility filename */
207 off_t compatsize = 0; /* Size of compatibility file */
74ef1ffb 208 int port; /* Port number (not used) */
cce0044f 209 char portname[255]; /* Port name */
74ef1ffb 210 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
5ed93a63 211 char print_job_name[1024]; /* Update job-name for Print-Job */
6875feac 212 http_status_t http_status; /* Status of HTTP request */
74ef1ffb 213 ipp_status_t ipp_status; /* Status of IPP request */
214 http_t *http; /* HTTP connection */
215 ipp_t *request, /* IPP request */
216 *response, /* IPP response */
217 *supported; /* get-printer-attributes response */
ff0295f0 218 time_t start_time; /* Time of first connect */
ff0295f0 219 int contimeout; /* Connection timeout */
fc596e79 220 int delay, /* Delay for retries */
221 prev_delay; /* Previous delay */
2ec940b5 222 const char *compression; /* Compression mode */
223 int waitjob, /* Wait for job complete? */
74ef1ffb 224 waitprinter; /* Wait for printer ready? */
b9738d7c 225 _cups_monitor_t monitor; /* Monitoring data */
74ef1ffb 226 ipp_attribute_t *job_id_attr; /* job-id attribute */
227 int job_id; /* job-id value */
072c4872 228 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
229 ipp_attribute_t *job_state; /* job-state */
230 ipp_attribute_t *copies_sup; /* copies-supported */
6875feac 231 ipp_attribute_t *cups_version; /* cups-version */
072c4872 232 ipp_attribute_t *format_sup; /* document-format-supported */
6875feac 233 ipp_attribute_t *media_col_sup; /* media-col-supported */
2ec940b5 234 ipp_attribute_t *operations_sup; /* operations-supported */
adbcb02a 235 ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */
74ef1ffb 236 ipp_attribute_t *printer_state; /* printer-state attribute */
072c4872 237 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
5ed93a63 238 int create_job = 0, /* Does printer support Create-Job? */
239 validate_job = 0; /* Does printer support Validate-Job? */
37de726f 240 int copies, /* Number of copies for job */
241 copies_remaining; /* Number of copies remaining */
b43a3371 242 const char *content_type, /* CONTENT_TYPE environment variable */
2ec940b5 243 *final_content_type, /* FINAL_CONTENT_TYPE environment var */
244 *document_format; /* document-format value */
6875feac 245 int fd; /* File descriptor */
0663fa1c 246 off_t bytes = 0; /* Bytes copied */
6875feac 247 char buffer[16384]; /* Copy buffer */
4ff40357 248#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
74ef1ffb 249 struct sigaction action; /* Actions for POSIX signals */
4ff40357 250#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
74ef1ffb 251 int version; /* IPP version */
2ec940b5 252 ppd_file_t *ppd; /* PPD file */
8211508e 253 _ppd_cache_t *pc; /* PPD cache and mapping data */
0663fa1c 254 fd_set input; /* Input set for select() */
c8f9565c 255
4b23f3b3 256
257 /*
258 * Make sure status messages are not buffered...
259 */
260
2922de55 261 setbuf(stderr, NULL);
c8f9565c 262
98904cd6 263 /*
8f4595eb 264 * Ignore SIGPIPE and catch SIGTERM signals...
98904cd6 265 */
266
267#ifdef HAVE_SIGSET
268 sigset(SIGPIPE, SIG_IGN);
8f4595eb 269 sigset(SIGTERM, sigterm_handler);
98904cd6 270#elif defined(HAVE_SIGACTION)
271 memset(&action, 0, sizeof(action));
272 action.sa_handler = SIG_IGN;
273 sigaction(SIGPIPE, &action, NULL);
8f4595eb 274
275 sigemptyset(&action.sa_mask);
276 sigaddset(&action.sa_mask, SIGTERM);
277 action.sa_handler = sigterm_handler;
278 sigaction(SIGTERM, &action, NULL);
98904cd6 279#else
280 signal(SIGPIPE, SIG_IGN);
8f4595eb 281 signal(SIGTERM, sigterm_handler);
98904cd6 282#endif /* HAVE_SIGSET */
283
4b23f3b3 284 /*
285 * Check command-line...
286 */
287
68edc300 288 if (argc == 1)
289 {
183914a3 290 char *s;
291
d4c438d4 292 if ((s = strrchr(argv[0], '/')) != NULL)
293 s ++;
294 else
295 s = argv[0];
296
3037604c 297 printf("network %s \"Unknown\" \"%s (%s)\"\n",
298 s, _cupsLangString(cupsLangDefault(),
299 _("Internet Printing Protocol")), s);
6248387b 300 return (CUPS_BACKEND_OK);
68edc300 301 }
072c4872 302 else if (argc < 6)
c8f9565c 303 {
472af6f3 304 _cupsLangPrintf(stderr,
4c4eea89 305 _("Usage: %s job-id user title copies options [file]"),
472af6f3 306 argv[0]);
6248387b 307 return (CUPS_BACKEND_STOP);
c8f9565c 308 }
309
959a6a28 310 /*
311 * Get the device URI...
312 */
313
314 while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
315 {
316 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
317 sleep(10);
318
319 if (getenv("CLASS") != NULL)
320 return (CUPS_BACKEND_FAILED);
321 }
322
7b1ef0fc 323 if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) == NULL)
324 auth_info_required = "none";
325
326 state_reasons = _cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"));
327
959a6a28 328#ifdef HAVE_GSSAPI
329 /*
330 * For Kerberos, become the printing user (if we can) to get the credentials
331 * that way.
332 */
333
334 if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
335 {
336 uid_t uid = (uid_t)atoi(value);
337 /* User ID */
338
339# ifdef HAVE_XPC
340 if (uid > 0)
341 {
342 if (argc == 6)
343 return (run_as_user(argc, argv, uid, device_uri, 0));
344 else
345 {
346 int status = 0; /* Exit status */
347
348 for (i = 6; i < argc && !status && !job_canceled; i ++)
349 {
350 if ((fd = open(argv[i], O_RDONLY)) >= 0)
351 {
352 status = run_as_user(argc, argv, uid, device_uri, fd);
353 close(fd);
354 }
355 else
356 {
357 _cupsLangPrintError("ERROR", _("Unable to open print file"));
358 status = CUPS_BACKEND_FAILED;
359 }
360 }
361
362 return (status);
363 }
364 }
365
366# else /* No XPC, just try to run as the user ID */
367 if (uid > 0)
368 seteuid(uid);
369# endif /* HAVE_XPC */
370 }
371#endif /* HAVE_GSSAPI */
372
a684a3b0 373 /*
072c4872 374 * Get the (final) content type...
a684a3b0 375 */
376
b43a3371 377 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
378 content_type = "application/octet-stream";
a684a3b0 379
b43a3371 380 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
381 {
382 final_content_type = content_type;
383
384 if (!strncmp(final_content_type, "printer/", 8))
385 final_content_type = "application/vnd.cups-raw";
386 }
12d8a513 387
7abb7137 388 /*
389 * Extract the hostname and printer name from the URI...
390 */
391
9fa9ba8f 392 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
393 username, sizeof(username), hostname, sizeof(hostname), &port,
394 resource, sizeof(resource));
7abb7137 395
6df6deaf 396 if (!port)
397 port = IPP_PORT; /* Default to port 631 */
398
9fa9ba8f 399 if (!strcmp(scheme, "https"))
7abb7137 400 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
692bbbae 401 else
402 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
7abb7137 403
897922a9 404 /*
405 * See if there are any options...
406 */
407
2ec940b5 408 compression = NULL;
6875feac 409 version = 20;
897922a9 410 waitjob = 1;
411 waitprinter = 1;
ff0295f0 412 contimeout = 7 * 24 * 60 * 60;
897922a9 413
414 if ((optptr = strchr(resource, '?')) != NULL)
415 {
416 /*
417 * Yup, terminate the device name string and move to the first
418 * character of the optptr...
419 */
420
421 *optptr++ = '\0';
422
423 /*
424 * Then parse the optptr...
425 */
426
427 while (*optptr)
428 {
429 /*
430 * Get the name...
431 */
432
abd1f85f 433 name = optptr;
897922a9 434
abd1f85f 435 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
436 optptr ++;
437
438 if ((sep = *optptr) != '\0')
439 *optptr++ = '\0';
440
441 if (sep == '=')
897922a9 442 {
443 /*
444 * Get the value...
445 */
446
abd1f85f 447 value = optptr;
897922a9 448
abd1f85f 449 while (*optptr && *optptr != '+' && *optptr != '&')
897922a9 450 optptr ++;
abd1f85f 451
452 if (*optptr)
453 *optptr++ = '\0';
897922a9 454 }
455 else
abd1f85f 456 value = (char *)"";
897922a9 457
458 /*
459 * Process the option...
460 */
461
c6fab96f 462 if (!_cups_strcasecmp(name, "waitjob"))
897922a9 463 {
464 /*
465 * Wait for job completion?
466 */
467
c6fab96f 468 waitjob = !_cups_strcasecmp(value, "on") ||
469 !_cups_strcasecmp(value, "yes") ||
470 !_cups_strcasecmp(value, "true");
897922a9 471 }
c6fab96f 472 else if (!_cups_strcasecmp(name, "waitprinter"))
897922a9 473 {
474 /*
475 * Wait for printer idle?
476 */
477
c6fab96f 478 waitprinter = !_cups_strcasecmp(value, "on") ||
479 !_cups_strcasecmp(value, "yes") ||
480 !_cups_strcasecmp(value, "true");
897922a9 481 }
c6fab96f 482 else if (!_cups_strcasecmp(name, "encryption"))
897922a9 483 {
484 /*
485 * Enable/disable encryption?
486 */
487
c6fab96f 488 if (!_cups_strcasecmp(value, "always"))
897922a9 489 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
c6fab96f 490 else if (!_cups_strcasecmp(value, "required"))
897922a9 491 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
c6fab96f 492 else if (!_cups_strcasecmp(value, "never"))
897922a9 493 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
c6fab96f 494 else if (!_cups_strcasecmp(value, "ifrequested"))
897922a9 495 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
496 else
497 {
4c4eea89 498 _cupsLangPrintFilter(stderr, "ERROR",
499 _("Unknown encryption option value: \"%s\"."),
500 value);
897922a9 501 }
502 }
c6fab96f 503 else if (!_cups_strcasecmp(name, "version"))
a708af2a 504 {
505 if (!strcmp(value, "1.0"))
22d59a30 506 version = 10;
a708af2a 507 else if (!strcmp(value, "1.1"))
22d59a30 508 version = 11;
509 else if (!strcmp(value, "2.0"))
510 version = 20;
511 else if (!strcmp(value, "2.1"))
512 version = 21;
4c4eea89 513 else if (!strcmp(value, "2.2"))
514 version = 22;
a708af2a 515 else
516 {
4c4eea89 517 _cupsLangPrintFilter(stderr, "ERROR",
518 _("Unknown version option value: \"%s\"."),
519 value);
a708af2a 520 }
521 }
1230a8a0 522#ifdef HAVE_LIBZ
c6fab96f 523 else if (!_cups_strcasecmp(name, "compression"))
1230a8a0 524 {
c6fab96f 525 if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes") ||
526 !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "gzip"))
2ec940b5 527 compression = "gzip";
1230a8a0 528 }
529#endif /* HAVE_LIBZ */
c6fab96f 530 else if (!_cups_strcasecmp(name, "contimeout"))
ff0295f0 531 {
532 /*
533 * Set the connection timeout...
534 */
535
536 if (atoi(value) > 0)
537 contimeout = atoi(value);
538 }
897922a9 539 else
540 {
541 /*
542 * Unknown option...
543 */
544
4c4eea89 545 _cupsLangPrintFilter(stderr, "ERROR",
546 _("Unknown option \"%s\" with value \"%s\"."),
547 name, value);
897922a9 548 }
549 }
550 }
551
1230a8a0 552 /*
553 * If we have 7 arguments, print the file named on the command-line.
554 * Otherwise, copy stdin to a temporary file and print the temporary
555 * file.
556 */
557
558 if (argc == 6)
559 {
6875feac 560 num_files = 0;
7b73f614 561 files = NULL;
c6fab96f 562 send_options = !_cups_strcasecmp(final_content_type, "application/pdf") ||
563 !_cups_strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
564 !_cups_strncasecmp(final_content_type, "image/", 6);
6875feac 565
566 fputs("DEBUG: Sending stdin for job...\n", stderr);
1230a8a0 567 }
568 else
569 {
570 /*
571 * Point to the files on the command-line...
572 */
573
b43a3371 574 num_files = argc - 6;
575 files = argv + 6;
576 send_options = 1;
cad2b75f 577
1230a8a0 578#ifdef HAVE_LIBZ
579 if (compression)
580 compress_files(num_files, files);
581#endif /* HAVE_LIBZ */
1230a8a0 582
6875feac 583 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
584 }
1230a8a0 585
c8f9565c 586 /*
b5cb0608 587 * Set the authentication info, if any...
c8f9565c 588 */
589
b5cb0608 590 cupsSetPasswordCB(password_cb);
591
592 if (username[0])
593 {
48e211f3 594 /*
595 * Use authenticaion information in the device URI...
596 */
597
b5cb0608 598 if ((password = strchr(username, ':')) != NULL)
599 *password++ = '\0';
600
601 cupsSetUser(username);
602 }
cbedee47 603 else
48e211f3 604 {
605 /*
fa1fc704 606 * Try loading authentication information from the environment.
48e211f3 607 */
608
abd1f85f 609 const char *ptr = getenv("AUTH_USERNAME");
610
611 if (ptr)
fa1fc704 612 cupsSetUser(ptr);
48e211f3 613
fa1fc704 614 password = getenv("AUTH_PASSWORD");
48e211f3 615 }
c8f9565c 616
617 /*
cce0044f 618 * Try finding the remote server...
c8f9565c 619 */
620
f831f3d8 621 start_time = time(NULL);
ff0295f0 622
cce0044f 623 sprintf(portname, "%d", port);
624
7b1ef0fc 625 update_reasons(NULL, "+connecting-to-device");
cce0044f 626 fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
627
628 while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
629 {
630 _cupsLangPrintFilter(stderr, "INFO",
631 _("Unable to locate printer \"%s\"."), hostname);
632 sleep(10);
633
634 if (getenv("CLASS") != NULL)
635 {
7b1ef0fc 636 update_reasons(NULL, "-connecting-to-device");
cce0044f 637 return (CUPS_BACKEND_STOP);
638 }
639 }
640
641 http = _httpCreate(hostname, port, addrlist, cupsEncryption(), AF_UNSPEC);
1179bc03 642 httpSetTimeout(http, 30.0, timeout_cb, NULL);
cce0044f 643
644 /*
645 * See if the printer supports SNMP...
646 */
647
648 if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0)
649 {
650 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
651 &start_count, NULL);
652 }
653 else
654 have_supplies = start_count = 0;
655
656 /*
657 * Wait for data from the filter...
658 */
659
660 if (num_files == 0)
0663fa1c 661 {
ad6d7ec9 662 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
cce0044f 663 return (CUPS_BACKEND_OK);
0663fa1c 664 else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
665 return (CUPS_BACKEND_OK);
666 }
cce0044f 667
668 /*
669 * Try connecting to the remote server...
670 */
671
fc596e79 672 delay = _cupsNextDelay(0, &prev_delay);
22a980cc 673
97fcaf92 674 do
c8f9565c 675 {
9fa9ba8f 676 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
4c4eea89 677 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
c8f9565c 678
cce0044f 679 if (httpReconnect(http))
d21a7597 680 {
03a8680a 681 int error = errno; /* Connection error */
682
cce0044f 683 if (http->status == HTTP_PKI_ERROR)
7b1ef0fc 684 update_reasons(NULL, "+cups-certificate-error");
cce0044f 685
6875feac 686 if (job_canceled)
bb3ff448 687 break;
688
997cf8b0 689 if (getenv("CLASS") != NULL)
690 {
691 /*
692 * If the CLASS environment variable is set, the job was submitted
693 * to a class and not to a specific queue. In this case, we want
694 * to abort immediately so that the job can be requeued on the next
695 * available printer in the class.
696 */
697
4c4eea89 698 _cupsLangPrintFilter(stderr, "INFO",
699 _("Unable to contact printer, queuing on next "
700 "printer in class."));
997cf8b0 701
997cf8b0 702 /*
703 * Sleep 5 seconds to keep the job from requeuing too rapidly...
704 */
705
706 sleep(5);
707
7b1ef0fc 708 update_reasons(NULL, "-connecting-to-device");
cce0044f 709
6248387b 710 return (CUPS_BACKEND_FAILED);
997cf8b0 711 }
712
03a8680a 713 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
714
4c2096b8 715 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
716 errno == EHOSTUNREACH)
97fcaf92 717 {
ff0295f0 718 if (contimeout && (time(NULL) - start_time) > contimeout)
719 {
4c4eea89 720 _cupsLangPrintFilter(stderr, "ERROR",
721 _("The printer is not responding."));
7b1ef0fc 722 update_reasons(NULL, "-connecting-to-device");
ff0295f0 723 return (CUPS_BACKEND_FAILED);
724 }
725
03a8680a 726 switch (error)
727 {
728 case EHOSTDOWN :
4c4eea89 729 _cupsLangPrintFilter(stderr, "WARNING",
7cfa02ab 730 _("The printer may not exist or "
731 "is unavailable at this time."));
03a8680a 732 break;
733
734 case EHOSTUNREACH :
4c4eea89 735 _cupsLangPrintFilter(stderr, "WARNING",
7cfa02ab 736 _("The printer is unreachable at this "
737 "time."));
03a8680a 738 break;
739
740 case ECONNREFUSED :
741 default :
4c4eea89 742 _cupsLangPrintFilter(stderr, "WARNING",
7cfa02ab 743 _("The printer is busy."));
03a8680a 744 break;
745 }
ff0295f0 746
747 sleep(delay);
748
fc596e79 749 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 750 }
751 else
752 {
4c4eea89 753 _cupsLangPrintFilter(stderr, "ERROR",
7cfa02ab 754 _("The printer is not responding."));
2cc18dd2 755 sleep(30);
97fcaf92 756 }
bb3ff448 757
6875feac 758 if (job_canceled)
bb3ff448 759 break;
d21a7597 760 }
cce0044f 761 else
7b1ef0fc 762 update_reasons(NULL, "-cups-certificate-error");
c8f9565c 763 }
cce0044f 764 while (http->fd < 0);
c8f9565c 765
6875feac 766 if (job_canceled || !http)
bb3ff448 767 return (CUPS_BACKEND_FAILED);
bb3ff448 768
7b1ef0fc 769 update_reasons(NULL, "-connecting-to-device");
4c4eea89 770 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
a4e23897 771
f0aa54a1 772 fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
773 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
774 _httpAddrPort(http->hostaddr));
a0c0bbe7 775
c8f9565c 776 /*
777 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 778 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
779 * might contain username:password information...
c8f9565c 780 */
781
11fdb546 782 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
783 port, resource);
c8f9565c 784
785 /*
97fcaf92 786 * First validate the destination and see if the device supports multiple
6875feac 787 * copies...
c8f9565c 788 */
789
adbcb02a 790 copies_sup = NULL;
791 cups_version = NULL;
792 format_sup = NULL;
793 media_col_sup = NULL;
794 supported = NULL;
795 operations_sup = NULL;
796 doc_handling_sup = NULL;
c8f9565c 797
97fcaf92 798 do
c8f9565c 799 {
56f7a531 800 /*
801 * Check for side-channel requests...
802 */
803
804 backendCheckSideChannel(snmp_fd, http->hostaddr);
805
c8f9565c 806 /*
97fcaf92 807 * Build the IPP request...
c8f9565c 808 */
809
e12df069 810 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 811 request->request.op.version[0] = version / 10;
812 request->request.op.version[1] = version % 10;
c8f9565c 813
97fcaf92 814 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
815 NULL, uri);
c8f9565c 816
8f4595eb 817 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
818 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
819 NULL, pattrs);
820
97fcaf92 821 /*
822 * Do the request...
823 */
c8f9565c 824
2054e655 825 fputs("DEBUG: Getting supported attributes...\n", stderr);
a4e23897 826
acd7eb22 827 if (http->version < HTTP_1_1)
6875feac 828 {
8962df79 829 fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
830 http->version / 100, http->version % 100);
7b1ef0fc 831 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
832 "cups-ipp-wrong-http-version");
6875feac 833 }
acd7eb22 834
b9738d7c 835 supported = cupsDoRequest(http, request, resource);
836 ipp_status = cupsLastError();
b5cb0608 837
5fb23099 838 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
839 ippErrorString(ipp_status), cupsLastErrorString());
840
b5cb0608 841 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 842 {
b9738d7c 843 fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
844 ippErrorString(ipp_status));
845
b5cb0608 846 if (ipp_status == IPP_PRINTER_BUSY ||
847 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 848 {
ff0295f0 849 if (contimeout && (time(NULL) - start_time) > contimeout)
850 {
4c4eea89 851 _cupsLangPrintFilter(stderr, "ERROR",
852 _("The printer is not responding."));
ff0295f0 853 return (CUPS_BACKEND_FAILED);
854 }
855
5fb23099 856 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
ff0295f0 857
4a241564 858 report_printer_state(supported);
ff0295f0 859
860 sleep(delay);
861
fc596e79 862 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 863 }
b5cb0608 864 else if ((ipp_status == IPP_BAD_REQUEST ||
22d59a30 865 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
c8f9565c 866 {
b5cb0608 867 /*
6875feac 868 * Switch to IPP/1.1 or IPP/1.0...
b5cb0608 869 */
97fcaf92 870
6875feac 871 if (version >= 20)
872 {
4c4eea89 873 _cupsLangPrintFilter(stderr, "INFO",
874 _("Printer does not support IPP/%d.%d, trying "
875 "IPP/%s."), version / 10, version % 10, "1.1");
6875feac 876 version = 11;
877 }
878 else
879 {
4c4eea89 880 _cupsLangPrintFilter(stderr, "INFO",
881 _("Printer does not support IPP/%d.%d, trying "
882 "IPP/%s."), version / 10, version % 10, "1.0");
6875feac 883 version = 10;
884 }
885
a4e23897 886 httpReconnect(http);
c8f9565c 887 }
67474245 888 else if (ipp_status == IPP_NOT_FOUND)
889 {
4c4eea89 890 _cupsLangPrintFilter(stderr, "ERROR",
891 _("The printer URI is incorrect or no longer "
892 "exists."));
67474245 893
5fb23099 894 ippDelete(supported);
67474245 895
6248387b 896 return (CUPS_BACKEND_STOP);
67474245 897 }
fc7e68cf 898 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
899 {
900 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
901 "Negotiate", 9))
902 auth_info_required = "negotiate";
903
904 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
905 return (CUPS_BACKEND_AUTH_REQUIRED);
906 }
97fcaf92 907 else
67474245 908 {
4c4eea89 909 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 910 _("Unable to get printer status."));
67474245 911 sleep(10);
912 }
d1c2727f 913
7cfa02ab 914 ippDelete(supported);
5fb23099 915 supported = NULL;
d1c2727f 916 continue;
97fcaf92 917 }
6875feac 918
f8c0cc95 919 if (!getenv("CLASS"))
7cfa02ab 920 {
f8c0cc95 921 /*
922 * Check printer-is-accepting-jobs = false and printer-state-reasons for the
923 * "spool-area-full" keyword...
924 */
925
926 int busy = 0;
927
928 if ((printer_accepting = ippFindAttribute(supported,
929 "printer-is-accepting-jobs",
930 IPP_TAG_BOOLEAN)) != NULL &&
931 !printer_accepting->values[0].boolean)
932 busy = 1;
9e88f2d2 933 else if (!printer_accepting)
7b1ef0fc 934 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
935 "cups-ipp-missing-printer-is-accepting-jobs");
c6fab96f 936
9e88f2d2 937 if ((printer_state = ippFindAttribute(supported,
938 "printer-state-reasons",
939 IPP_TAG_KEYWORD)) != NULL && !busy)
f8c0cc95 940 {
941 for (i = 0; i < printer_state->num_values; i ++)
9e88f2d2 942 if (!strcmp(printer_state->values[0].string.text,
943 "spool-area-full") ||
f8c0cc95 944 !strncmp(printer_state->values[0].string.text, "spool-area-full-",
945 16))
946 {
947 busy = 1;
948 break;
949 }
950 }
951 else
7b1ef0fc 952 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
953 "cups-ipp-missing-printer-state-reasons");
7cfa02ab 954
f8c0cc95 955 if (busy)
7cfa02ab 956 {
5fb23099 957 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
7cfa02ab 958
4a241564 959 report_printer_state(supported);
7cfa02ab 960
961 sleep(delay);
962
f8c0cc95 963 delay = _cupsNextDelay(delay, &prev_delay);
7cfa02ab 964
965 ippDelete(supported);
5fb23099 966 supported = NULL;
7cfa02ab 967 continue;
968 }
969 }
7cfa02ab 970
6875feac 971 /*
972 * Check for supported attributes...
973 */
974
975 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
976 IPP_TAG_RANGE)) != NULL)
97fcaf92 977 {
b5cb0608 978 /*
979 * Has the "copies-supported" attribute - does it have an upper
980 * bound > 1?
981 */
97fcaf92 982
6875feac 983 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
984 copies_sup->values[0].range.lower,
985 copies_sup->values[0].range.upper);
986
b5cb0608 987 if (copies_sup->values[0].range.upper <= 1)
988 copies_sup = NULL; /* No */
989 }
97fcaf92 990
6875feac 991 cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
b5cb0608 992
6875feac 993 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
994 IPP_TAG_MIMETYPE)) != NULL)
b5cb0608 995 {
996 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
997 format_sup->num_values);
998 for (i = 0; i < format_sup->num_values; i ++)
999 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1000 format_sup->values[i].string.text);
c8f9565c 1001 }
d1c2727f 1002
6875feac 1003 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
1004 IPP_TAG_KEYWORD)) != NULL)
1005 {
1006 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
1007 media_col_sup->num_values);
1008 for (i = 0; i < media_col_sup->num_values; i ++)
1009 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1010 media_col_sup->values[i].string.text);
1011 }
1012
2ec940b5 1013 if ((operations_sup = ippFindAttribute(supported, "operations-supported",
1014 IPP_TAG_ENUM)) != NULL)
1015 {
9e88f2d2 1016 for (i = 0; i < operations_sup->num_values; i ++)
1017 if (operations_sup->values[i].integer == IPP_PRINT_JOB)
1018 break;
1019
1020 if (i >= operations_sup->num_values)
7b1ef0fc 1021 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1022 "cups-ipp-missing-print-job");
9e88f2d2 1023
1024 for (i = 0; i < operations_sup->num_values; i ++)
1025 if (operations_sup->values[i].integer == IPP_CANCEL_JOB)
1026 break;
1027
1028 if (i >= operations_sup->num_values)
7b1ef0fc 1029 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1030 "cups-ipp-missing-cancel-job");
9e88f2d2 1031
1032 for (i = 0; i < operations_sup->num_values; i ++)
1033 if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
1034 break;
1035
1036 if (i >= operations_sup->num_values)
7b1ef0fc 1037 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1038 "cups-ipp-missing-get-job-attributes");
9e88f2d2 1039
1040 for (i = 0; i < operations_sup->num_values; i ++)
1041 if (operations_sup->values[i].integer == IPP_GET_PRINTER_ATTRIBUTES)
1042 break;
1043
1044 if (i >= operations_sup->num_values)
7b1ef0fc 1045 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1046 "cups-ipp-missing-get-printer-attributes");
9e88f2d2 1047
2ec940b5 1048 for (i = 0; i < operations_sup->num_values; i ++)
5ed93a63 1049 {
2ec940b5 1050 if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
1051 {
1052 validate_job = 1;
5ed93a63 1053 if (create_job)
1054 break;
2ec940b5 1055 }
5ed93a63 1056 else if (operations_sup->values[i].integer == IPP_CREATE_JOB)
1057 {
1058 create_job = 1;
1059 if (validate_job)
1060 break;
1061 }
1062 }
2ec940b5 1063
1064 if (!validate_job)
7b1ef0fc 1065 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1066 "cups-ipp-missing-validate-job");
2ec940b5 1067 }
1068 else
7b1ef0fc 1069 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1070 "cups-ipp-missing-operations-supported");
2ec940b5 1071
adbcb02a 1072 doc_handling_sup = ippFindAttribute(supported,
1073 "multiple-document-handling-supported",
1074 IPP_TAG_KEYWORD);
1075
4a241564 1076 report_printer_state(supported);
c8f9565c 1077 }
97fcaf92 1078 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 1079
997cf8b0 1080 /*
1081 * See if the printer is accepting jobs and is not stopped; if either
1082 * condition is true and we are printing to a class, requeue the job...
1083 */
1084
1085 if (getenv("CLASS") != NULL)
1086 {
1087 printer_state = ippFindAttribute(supported, "printer-state",
1088 IPP_TAG_ENUM);
1089 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
1090 IPP_TAG_BOOLEAN);
1091
1092 if (printer_state == NULL ||
072c4872 1093 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
1094 waitprinter) ||
997cf8b0 1095 printer_accepting == NULL ||
1096 !printer_accepting->values[0].boolean)
1097 {
1098 /*
1099 * If the CLASS environment variable is set, the job was submitted
1100 * to a class and not to a specific queue. In this case, we want
1101 * to abort immediately so that the job can be requeued on the next
1102 * available printer in the class.
1103 */
1104
4c4eea89 1105 _cupsLangPrintFilter(stderr, "INFO",
1106 _("Unable to contact printer, queuing on next "
1107 "printer in class."));
997cf8b0 1108
1109 ippDelete(supported);
1110 httpClose(http);
1111
997cf8b0 1112 /*
1113 * Sleep 5 seconds to keep the job from requeuing too rapidly...
1114 */
1115
1116 sleep(5);
1117
6248387b 1118 return (CUPS_BACKEND_FAILED);
997cf8b0 1119 }
1120 }
1121
c8f9565c 1122 /*
97fcaf92 1123 * See if the printer supports multiple copies...
c8f9565c 1124 */
1125
37de726f 1126 copies = atoi(argv[4]);
1127
d1c2727f 1128 if (copies_sup || argc < 7)
7d52601e 1129 {
37de726f 1130 copies_remaining = 1;
7d52601e 1131
982776b0 1132 if (argc < 7 && !_cups_strncasecmp(final_content_type, "image/", 6))
7d52601e 1133 copies = 1;
1134 }
0c5b0932 1135 else
37de726f 1136 copies_remaining = copies;
0c5b0932 1137
2ec940b5 1138 /*
1139 * Prepare remaining printing options...
1140 */
1141
1142 options = NULL;
8211508e 1143 pc = NULL;
2ec940b5 1144
1145 if (send_options)
1146 {
1147 num_options = cupsParseOptions(argv[5], 0, &options);
1148
1149 if (!cups_version && media_col_sup)
1150 {
1151 /*
1152 * Load the PPD file and generate PWG attribute mapping information...
1153 */
1154
1155 ppd = ppdOpenFile(getenv("PPD"));
8211508e 1156 pc = _ppdCacheCreateWithPPD(ppd);
2ec940b5 1157
1158 ppdClose(ppd);
1159 }
1160 }
1161 else
1162 num_options = 0;
1163
1164 document_format = NULL;
1165
1166 if (format_sup != NULL)
1167 {
1168 for (i = 0; i < format_sup->num_values; i ++)
c6fab96f 1169 if (!_cups_strcasecmp(final_content_type, format_sup->values[i].string.text))
2ec940b5 1170 {
1171 document_format = final_content_type;
1172 break;
1173 }
cce0044f 1174
1175 if (!document_format)
1176 {
1177 for (i = 0; i < format_sup->num_values; i ++)
c6fab96f 1178 if (!_cups_strcasecmp("application/octet-stream",
cce0044f 1179 format_sup->values[i].string.text))
1180 {
1181 document_format = "application/octet-stream";
1182 break;
1183 }
1184 }
2ec940b5 1185 }
1186
940afb4b 1187 /*
1188 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1189 * to a temporary file so that we can do a HTTP/1.0 submission...
1190 *
1191 * (I hate compatibility hacks!)
1192 */
1193
1194 if (http->version < HTTP_1_1 && num_files == 0)
1195 {
1196 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1197 {
1198 perror("DEBUG: Unable to create temporary file");
1199 return (CUPS_BACKEND_FAILED);
1200 }
1201
1202 _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1203
1204 compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1205 backendNetworkSideCB);
1206
1207 close(fd);
1208
1209 compatfile = tmpfilename;
1210 files = &compatfile;
1211 num_files = 1;
1212 }
1213 else if (http->version < HTTP_1_1 && num_files == 1)
1214 {
1215 struct stat fileinfo; /* File information */
1216
1217 if (!stat(files[0], &fileinfo))
1218 compatsize = fileinfo.st_size;
1219 }
1220
b9738d7c 1221 /*
1222 * Start monitoring the printer in the background...
1223 */
1224
1225 monitor.uri = uri;
1226 monitor.hostname = hostname;
1227 monitor.user = argv[2];
1228 monitor.resource = resource;
1229 monitor.port = port;
1230 monitor.version = version;
1231 monitor.job_id = 0;
1232 monitor.encryption = cupsEncryption();
1233 monitor.job_state = IPP_JOB_PENDING;
1234 monitor.printer_state = IPP_PRINTER_IDLE;
1235
5ed93a63 1236 if (create_job)
1237 {
1238 monitor.job_name = argv[3];
1239 }
1240 else
1241 {
1242 snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1],
1243 argv[3]);
1244 monitor.job_name = print_job_name;
1245 }
1246
b9738d7c 1247 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1248
c8f9565c 1249 /*
2ec940b5 1250 * Validate access to the printer...
c8f9565c 1251 */
1252
5fb23099 1253 while (!job_canceled && validate_job)
c8f9565c 1254 {
5ed93a63 1255 request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2],
1256 monitor.job_name, num_options, options, compression,
8211508e 1257 copies_sup ? copies : 1, document_format, pc,
adbcb02a 1258 media_col_sup, doc_handling_sup);
97fcaf92 1259
2ec940b5 1260 ippDelete(cupsDoRequest(http, request, resource));
8ce7c000 1261
2ec940b5 1262 ipp_status = cupsLastError();
97fcaf92 1263
5fb23099 1264 fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1265 ippErrorString(ipp_status), cupsLastErrorString());
6875feac 1266
5fb23099 1267 if (job_canceled)
1268 break;
e7186d00 1269
5fb23099 1270 if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1271 {
1272 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1273 sleep(10);
1274 }
548f3b66 1275 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1276 ipp_status == IPP_AUTHENTICATION_CANCELED)
5fb23099 1277 {
1278 /*
1279 * Update auth-info-required as needed...
1280 */
b8f3410f 1281
5fb23099 1282 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1283 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
97fcaf92 1284
5fb23099 1285 /*
1286 * Normal authentication goes through the password callback, which sets
1287 * auth_info_required to "username,password". Kerberos goes directly
1288 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1289 * here and set auth_info_required as needed...
1290 */
6875feac 1291
5fb23099 1292 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1293 "Negotiate", 9))
1294 auth_info_required = "negotiate";
753453e4 1295
5fb23099 1296 goto cleanup;
2ec940b5 1297 }
5fb23099 1298 else if (ipp_status == IPP_OPERATION_NOT_SUPPORTED)
1299 {
0663fa1c 1300 /*
1301 * This is all too common...
1302 */
1303
7b1ef0fc 1304 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1305 "cups-ipp-missing-validate-job");
5fb23099 1306 break;
1307 }
1308 else if (ipp_status < IPP_REDIRECTION_OTHER_SITE)
2ec940b5 1309 break;
1310 }
37de726f 1311
2ec940b5 1312 /*
1313 * Then issue the print-job request...
1314 */
6875feac 1315
2ec940b5 1316 job_id = 0;
6875feac 1317
2ec940b5 1318 while (!job_canceled && copies_remaining > 0)
1319 {
1320 /*
1321 * Check for side-channel requests...
1322 */
b9738d7c 1323
2ec940b5 1324 backendCheckSideChannel(snmp_fd, http->hostaddr);
b9738d7c 1325
2ec940b5 1326 /*
1327 * Build the IPP job creation request...
1328 */
6875feac 1329
2ec940b5 1330 if (job_canceled)
1331 break;
753453e4 1332
5ed93a63 1333 request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB :
1334 IPP_PRINT_JOB,
1335 version, uri, argv[2], monitor.job_name, num_options,
1336 options, compression, copies_sup ? copies : 1,
1337 document_format, pc, media_col_sup, doc_handling_sup);
c8f9565c 1338
cb555bcf 1339 /*
b5cb0608 1340 * Do the request...
cb555bcf 1341 */
1342
5ed93a63 1343 if (num_files > 1 || create_job)
072c4872 1344 response = cupsDoRequest(http, request, resource);
b5cb0608 1345 else
6875feac 1346 {
940afb4b 1347 size_t length = 0; /* Length of request */
1348
1349 if (compatsize > 0)
1350 {
1351 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1352 length = ippLength(request) + (size_t)compatsize;
1353 }
1354 else
1355 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1356
1357 http_status = cupsSendRequest(http, request, resource, length);
6875feac 1358 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1359 {
1360 if (num_files == 1)
7b64ad64 1361 fd = open(files[0], O_RDONLY);
6875feac 1362 else
6875feac 1363 {
7b64ad64 1364 fd = 0;
1365 http_status = cupsWriteRequestData(http, buffer, bytes);
1366 }
6875feac 1367
06efaeda 1368 while (http_status == HTTP_CONTINUE &&
1369 (!job_canceled || compatsize > 0))
7b64ad64 1370 {
0663fa1c 1371 /*
1372 * Check for side-channel requests and more print data...
1373 */
1374
1375 FD_ZERO(&input);
1376 FD_SET(fd, &input);
1377 FD_SET(snmp_fd, &input);
1378
1379 while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
1380 NULL) <= 0 && !job_canceled);
1381
1382 if (FD_ISSET(snmp_fd, &input))
6875feac 1383 backendCheckSideChannel(snmp_fd, http->hostaddr);
0663fa1c 1384
1385 if (FD_ISSET(fd, &input))
7b64ad64 1386 {
1387 if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1388 {
1389 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1390
1391 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1392 break;
1393 }
1394 else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
1395 break;
1396 }
6875feac 1397 }
1398
1399 if (num_files == 1)
1400 close(fd);
1401 }
1402
1403 response = cupsGetResponse(http, resource);
1404 ippDelete(request);
1405 }
072c4872 1406
1407 ipp_status = cupsLastError();
b5cb0608 1408
5fb23099 1409 fprintf(stderr, "DEBUG: %s: %s (%s)\n",
5ed93a63 1410 (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
5fb23099 1411 ippErrorString(ipp_status), cupsLastErrorString());
1412
b5cb0608 1413 if (ipp_status > IPP_OK_CONFLICT)
1414 {
753453e4 1415 job_id = 0;
1416
6875feac 1417 if (job_canceled)
072c4872 1418 break;
1419
b5cb0608 1420 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
084c8c5c 1421 ipp_status == IPP_NOT_POSSIBLE ||
b5cb0608 1422 ipp_status == IPP_PRINTER_BUSY)
1423 {
5fb23099 1424 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
b5cb0608 1425 sleep(10);
7cfa02ab 1426
1427 if (num_files == 0)
1428 {
1429 /*
1430 * We can't re-submit when we have no files to print, so exit
1431 * immediately with the right status code...
1432 */
1433
1434 goto cleanup;
1435 }
b5cb0608 1436 }
72bdcfc1 1437 else if (ipp_status == IPP_ERROR_JOB_CANCELED)
1438 goto cleanup;
b5cb0608 1439 else
ebac5c9b 1440 {
1441 /*
1442 * Update auth-info-required as needed...
1443 */
1444
4c4eea89 1445 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1446 _("Print file was not accepted."));
ebac5c9b 1447
4233257e 1448 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
ebac5c9b 1449 {
1450 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1451 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1452
4233257e 1453 /*
1454 * Normal authentication goes through the password callback, which sets
1455 * auth_info_required to "username,password". Kerberos goes directly
1456 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1457 * here and set auth_info_required as needed...
1458 */
1459
ebac5c9b 1460 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1461 "Negotiate", 9))
4233257e 1462 auth_info_required = "negotiate";
ebac5c9b 1463 }
5fb23099 1464 else
1465 sleep(10);
1466
1467 if (num_files == 0)
1468 {
1469 /*
1470 * We can't re-submit when we have no files to print, so exit
1471 * immediately with the right status code...
1472 */
1473
1474 goto cleanup;
1475 }
ebac5c9b 1476 }
b5cb0608 1477 }
1478 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1479 IPP_TAG_INTEGER)) == NULL)
97fcaf92 1480 {
4c4eea89 1481 _cupsLangPrintFilter(stderr, "INFO",
1482 _("Print file accepted - job ID unknown."));
7b1ef0fc 1483 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1484 "cups-ipp-missing-job-id");
b5cb0608 1485 job_id = 0;
1486 }
1487 else
1488 {
b9738d7c 1489 monitor.job_id = job_id = job_id_attr->values[0].integer;
4c4eea89 1490 _cupsLangPrintFilter(stderr, "INFO",
1491 _("Print file accepted - job ID %d."), job_id);
97fcaf92 1492 }
cb555bcf 1493
5ed93a63 1494 fprintf(stderr, "DEBUG: job-id=%d\n", job_id);
072c4872 1495 ippDelete(response);
1496
6875feac 1497 if (job_canceled)
072c4872 1498 break;
1499
5ed93a63 1500 if (job_id && (num_files > 1 || create_job))
072c4872 1501 {
5ed93a63 1502 for (i = 0; num_files == 0 || i < num_files; i ++)
072c4872 1503 {
56f7a531 1504 /*
1505 * Check for side-channel requests...
1506 */
1507
1508 backendCheckSideChannel(snmp_fd, http->hostaddr);
1509
1510 /*
1511 * Send the next file in the job...
1512 */
1513
072c4872 1514 request = ippNewRequest(IPP_SEND_DOCUMENT);
22d59a30 1515 request->request.op.version[0] = version / 10;
1516 request->request.op.version[1] = version % 10;
072c4872 1517
1518 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1519 NULL, uri);
1520
1521 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1522 job_id);
1523
1524 if (argv[2][0])
1525 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1526 "requesting-user-name", NULL, argv[2]);
1527
5ed93a63 1528 if ((i + 1) >= num_files)
072c4872 1529 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1530
1531 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
5ed93a63 1532 "document-format", NULL, document_format);
072c4872 1533
6875feac 1534 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1535 http_status = cupsSendRequest(http, request, resource, 0);
5ed93a63 1536 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1537 {
1538 if (num_files == 0)
1539 {
1540 fd = 0;
1541 http_status = cupsWriteRequestData(http, buffer, bytes);
1542 }
1543 else
1544 fd = open(files[i], O_RDONLY);
1545 }
1546 else
1547 fd = -1;
1548
1549 if (fd >= 0)
6875feac 1550 {
06efaeda 1551 while (!job_canceled &&
1552 (bytes = read(fd, buffer, sizeof(buffer))) > 0)
6875feac 1553 {
e6af41cc 1554 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
6875feac 1555 break;
1556 else
1557 {
1558 /*
1559 * Check for side-channel requests...
1560 */
1561
1562 backendCheckSideChannel(snmp_fd, http->hostaddr);
1563 }
1564 }
1565
5ed93a63 1566 if (fd > 0)
1567 close(fd);
6875feac 1568 }
acd7eb22 1569
6875feac 1570 ippDelete(cupsGetResponse(http, resource));
1571 ippDelete(request);
072c4872 1572
5fb23099 1573 fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1574 ippErrorString(cupsLastError()), cupsLastErrorString());
1575
072c4872 1576 if (cupsLastError() > IPP_OK_CONFLICT)
1577 {
1578 ipp_status = cupsLastError();
1579
4c4eea89 1580 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1581 _("Unable to add document to print job."));
072c4872 1582 break;
1583 }
5ed93a63 1584 else if (num_files == 0 || fd < 0)
1585 break;
072c4872 1586 }
1587 }
b5cb0608 1588
753453e4 1589 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 1590 {
1591 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
37de726f 1592 copies_remaining --;
b5cb0608 1593 }
ab827512 1594 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
084c8c5c 1595 ipp_status == IPP_NOT_POSSIBLE ||
ab827512 1596 ipp_status == IPP_PRINTER_BUSY)
a04d2365 1597 continue;
ab827512 1598 else
37de726f 1599 copies_remaining --;
8ce7c000 1600
c8f9565c 1601 /*
b5cb0608 1602 * Wait for the job to complete...
c8f9565c 1603 */
1604
897922a9 1605 if (!job_id || !waitjob)
b5cb0608 1606 continue;
1607
4c4eea89 1608 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
b5cb0608 1609
fc596e79 1610 for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
c8f9565c 1611 {
56f7a531 1612 /*
1613 * Check for side-channel requests...
1614 */
1615
1616 backendCheckSideChannel(snmp_fd, http->hostaddr);
1617
97fcaf92 1618 /*
b5cb0608 1619 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 1620 */
1621
e12df069 1622 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
22d59a30 1623 request->request.op.version[0] = version / 10;
1624 request->request.op.version[1] = version % 10;
97fcaf92 1625
b5cb0608 1626 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1627 NULL, uri);
3f9cb6c6 1628
b5cb0608 1629 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1630 job_id);
c8f9565c 1631
ee8b7dd3 1632 if (argv[2][0])
897922a9 1633 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1634 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 1635
8f4595eb 1636 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1637 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1638 NULL, jattrs);
97fcaf92 1639
1640 /*
b5cb0608 1641 * Do the request...
97fcaf92 1642 */
1643
fc596e79 1644 httpReconnect(http);
072c4872 1645 response = cupsDoRequest(http, request, resource);
1646 ipp_status = cupsLastError();
97fcaf92 1647
b5cb0608 1648 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 1649 {
b5cb0608 1650 /*
d1c2727f 1651 * Job has gone away and/or the server has no job history...
b5cb0608 1652 */
97fcaf92 1653
7b1ef0fc 1654 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1655 "cups-ipp-missing-job-history");
b5cb0608 1656 ippDelete(response);
d1c2727f 1657
1658 ipp_status = IPP_OK;
b5cb0608 1659 break;
97fcaf92 1660 }
1661
5fb23099 1662 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1663 ippErrorString(ipp_status), cupsLastErrorString());
1664
b5cb0608 1665 if (ipp_status > IPP_OK_CONFLICT)
6a536282 1666 {
b5cb0608 1667 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
084c8c5c 1668 ipp_status != IPP_NOT_POSSIBLE &&
b5cb0608 1669 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 1670 {
072c4872 1671 ippDelete(response);
b5cb0608 1672
4c4eea89 1673 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1674 _("Unable to get print job status."));
b5cb0608 1675 break;
97fcaf92 1676 }
6a536282 1677 }
8f4595eb 1678
072c4872 1679 if (response)
97fcaf92 1680 {
8f4595eb 1681 if ((job_state = ippFindAttribute(response, "job-state",
1682 IPP_TAG_ENUM)) != NULL)
97fcaf92 1683 {
6c1b8723 1684 /*
1685 * Reflect the remote job state in the local queue...
1686 */
1687
7b1ef0fc 1688 if (cups_version &&
1689 job_state->values[0].integer >= IPP_JOB_PENDING &&
1690 job_state->values[0].integer <= IPP_JOB_COMPLETED)
1691 update_reasons(NULL,
1692 remote_job_states[job_state->values[0].integer -
1693 IPP_JOB_PENDING]);
f8c0cc95 1694
1695 if ((job_sheets = ippFindAttribute(response,
1696 "job-media-sheets-completed",
1697 IPP_TAG_INTEGER)) == NULL)
1698 job_sheets = ippFindAttribute(response,
1699 "job-impressions-completed",
1700 IPP_TAG_INTEGER);
1701
1702 if (job_sheets)
1703 fprintf(stderr, "PAGE: total %d\n",
1704 job_sheets->values[0].integer);
6c1b8723 1705
8f4595eb 1706 /*
1707 * Stop polling if the job is finished or pending-held...
1708 */
1709
7951315f 1710 if (job_state->values[0].integer > IPP_JOB_STOPPED)
8f4595eb 1711 {
1712 ippDelete(response);
1713 break;
1714 }
97fcaf92 1715 }
084c8c5c 1716 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1717 ipp_status != IPP_NOT_POSSIBLE &&
1718 ipp_status != IPP_PRINTER_BUSY)
f3fdb6b2 1719 {
1720 /*
1721 * If the printer does not return a job-state attribute, it does not
1722 * conform to the IPP specification - break out immediately and fail
1723 * the job...
1724 */
1725
7b1ef0fc 1726 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1727 "cups-ipp-missing-job-state");
f3fdb6b2 1728 ipp_status = IPP_INTERNAL_ERROR;
1729 break;
1730 }
97fcaf92 1731 }
1732
072c4872 1733 ippDelete(response);
d1c2727f 1734
b5cb0608 1735 /*
fc596e79 1736 * Wait before polling again...
d1c2727f 1737 */
97fcaf92 1738
2ce8588b 1739 sleep(delay);
1740
fc596e79 1741 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 1742 }
c8f9565c 1743 }
1744
6bb5a528 1745 /*
072c4872 1746 * Cancel the job as needed...
6bb5a528 1747 */
1748
6875feac 1749 if (job_canceled && job_id)
072c4872 1750 cancel_job(http, uri, job_id, resource, argv[2], version);
1751
1752 /*
1753 * Check the printer state and report it if necessary...
1754 */
6bb5a528 1755
4a241564 1756 check_printer_state(http, uri, resource, argv[2], version);
6bb5a528 1757
eab5b04e 1758 /*
1759 * Collect the final page count as needed...
1760 */
1761
aa141f0d 1762 if (have_supplies &&
eab5b04e 1763 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1764 page_count > start_count)
1765 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1766
4233257e 1767#ifdef HAVE_GSSAPI
1768 /*
1769 * See if we used Kerberos at all...
1770 */
1771
1772 if (http->gssctx)
1773 auth_info_required = "negotiate";
1774#endif /* HAVE_GSSAPI */
1775
c8f9565c 1776 /*
1777 * Free memory...
1778 */
1779
2ec940b5 1780 cleanup:
1781
1782 cupsFreeOptions(num_options, options);
8211508e 1783 _ppdCacheDestroy(pc);
2ec940b5 1784
c8f9565c 1785 httpClose(http);
c8f9565c 1786
072c4872 1787 ippDelete(supported);
2922de55 1788
c8f9565c 1789 /*
b8f3410f 1790 * Remove the temporary file(s) if necessary...
c8f9565c 1791 */
1792
940afb4b 1793 if (tmpfilename[0])
1794 unlink(tmpfilename);
1795
1230a8a0 1796#ifdef HAVE_LIBZ
1797 if (compression)
1798 {
1799 for (i = 0; i < num_files; i ++)
1800 unlink(files[i]);
1801 }
1802#endif /* HAVE_LIBZ */
1803
c8f9565c 1804 /*
1805 * Return the queue status...
1806 */
1807
c55dc24c 1808 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
d0eaf635 1809 ipp_status == IPP_AUTHENTICATION_CANCELED ||
c55dc24c 1810 ipp_status <= IPP_OK_CONFLICT)
1811 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
4233257e 1812
d0eaf635 1813 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1814 ipp_status == IPP_AUTHENTICATION_CANCELED)
3fe655a3 1815 return (CUPS_BACKEND_AUTH_REQUIRED);
f3fdb6b2 1816 else if (ipp_status == IPP_INTERNAL_ERROR)
1817 return (CUPS_BACKEND_STOP);
623108b2 1818 else if (ipp_status == IPP_DOCUMENT_FORMAT ||
1819 ipp_status == IPP_CONFLICT)
1820 return (CUPS_BACKEND_FAILED);
e0d12789 1821 else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
5fb23099 1822 return (CUPS_BACKEND_RETRY_CURRENT);
3fe655a3 1823 else
83d83f7a 1824 {
4c4eea89 1825 _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
3fe655a3 1826 return (CUPS_BACKEND_OK);
83d83f7a 1827 }
b5cb0608 1828}
1829
1830
072c4872 1831/*
1832 * 'cancel_job()' - Cancel a print job.
1833 */
1834
1835static void
1836cancel_job(http_t *http, /* I - HTTP connection */
1837 const char *uri, /* I - printer-uri */
1838 int id, /* I - job-id */
1839 const char *resource, /* I - Resource path */
1840 const char *user, /* I - requesting-user-name */
1841 int version) /* I - IPP version */
1842{
1843 ipp_t *request; /* Cancel-Job request */
1844
1845
4c4eea89 1846 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
072c4872 1847
1848 request = ippNewRequest(IPP_CANCEL_JOB);
22d59a30 1849 request->request.op.version[0] = version / 10;
1850 request->request.op.version[1] = version % 10;
072c4872 1851
1852 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1853 NULL, uri);
1854 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1855
1856 if (user && user[0])
1857 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1858 "requesting-user-name", NULL, user);
1859
1860 /*
1861 * Do the request...
1862 */
1863
1864 ippDelete(cupsDoRequest(http, request, resource));
1865
1866 if (cupsLastError() > IPP_OK_CONFLICT)
5fb23099 1867 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
072c4872 1868}
1869
1870
6bb5a528 1871/*
b9738d7c 1872 * 'check_printer_state()' - Check the printer state.
6bb5a528 1873 */
1874
b9738d7c 1875static ipp_pstate_t /* O - Current printer-state */
e12df069 1876check_printer_state(
1877 http_t *http, /* I - HTTP connection */
1878 const char *uri, /* I - Printer URI */
1879 const char *resource, /* I - Resource path */
1880 const char *user, /* I - Username, if any */
4a241564 1881 int version) /* I - IPP version */
1882 {
b9738d7c 1883 ipp_t *request, /* IPP request */
1884 *response; /* IPP response */
1885 ipp_attribute_t *attr; /* Attribute in response */
1886 ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
1887 /* Current printer-state */
6bb5a528 1888
1889
1890 /*
b9738d7c 1891 * Send a Get-Printer-Attributes request and log the results...
6bb5a528 1892 */
1893
e12df069 1894 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 1895 request->request.op.version[0] = version / 10;
1896 request->request.op.version[1] = version % 10;
6bb5a528 1897
1898 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
b9738d7c 1899 NULL, uri);
6bb5a528 1900
1901 if (user && user[0])
1902 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
b9738d7c 1903 "requesting-user-name", NULL, user);
6bb5a528 1904
a6ccc6e8 1905 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
b9738d7c 1906 "requested-attributes",
1907 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
6bb5a528 1908
1909 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1910 {
4a241564 1911 report_printer_state(response);
b9738d7c 1912
1913 if ((attr = ippFindAttribute(response, "printer-state",
1914 IPP_TAG_ENUM)) != NULL)
1915 printer_state = (ipp_pstate_t)attr->values[0].integer;
1916
6bb5a528 1917 ippDelete(response);
1918 }
b9738d7c 1919
5fb23099 1920 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1921 ippErrorString(cupsLastError()), cupsLastErrorString());
1922
b9738d7c 1923 /*
1924 * Return the printer-state value...
1925 */
1926
1927 return (printer_state);
6bb5a528 1928}
1929
1930
1230a8a0 1931#ifdef HAVE_LIBZ
1932/*
959a6a28 1933 * 'compress_files()' - Compress print files.
1230a8a0 1934 */
1935
1936static void
1937compress_files(int num_files, /* I - Number of files */
1938 char **files) /* I - Files */
1939{
1940 int i, /* Looping var */
1941 fd; /* Temporary file descriptor */
1942 ssize_t bytes; /* Bytes read/written */
1943 size_t total; /* Total bytes read */
1944 cups_file_t *in, /* Input file */
1945 *out; /* Output file */
1946 struct stat outinfo; /* Output file information */
1947 char filename[1024], /* Temporary filename */
1fb9f3e8 1948 buffer[32768]; /* Copy buffer */
1230a8a0 1949
1950
1951 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1952 for (i = 0; i < num_files; i ++)
1953 {
1954 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1955 {
4c4eea89 1956 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1230a8a0 1957 exit(CUPS_BACKEND_FAILED);
1958 }
1959
1960 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1961 {
4c4eea89 1962 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1230a8a0 1963 exit(CUPS_BACKEND_FAILED);
1964 }
1965
1966 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1967 {
4c4eea89 1968 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1230a8a0 1969 cupsFileClose(out);
1970 exit(CUPS_BACKEND_FAILED);
1971 }
1972
1973 total = 0;
1974 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1975 if (cupsFileWrite(out, buffer, bytes) < bytes)
1976 {
4c4eea89 1977 _cupsLangPrintError("ERROR",
1978 _("Unable to generate compressed print file"));
1230a8a0 1979 cupsFileClose(in);
1980 cupsFileClose(out);
1981 exit(CUPS_BACKEND_FAILED);
1982 }
1983 else
1984 total += bytes;
1985
1986 cupsFileClose(out);
1987 cupsFileClose(in);
1988
1989 files[i] = strdup(filename);
1990
1991 if (!stat(filename, &outinfo))
ff0295f0 1992 fprintf(stderr,
1993 "DEBUG: File %d compressed to %.1f%% of original size, "
1994 CUPS_LLFMT " bytes...\n",
1995 i + 1, 100.0 * outinfo.st_size / total,
1996 CUPS_LLCAST outinfo.st_size);
1230a8a0 1997 }
1998}
1999#endif /* HAVE_LIBZ */
2000
2001
b9738d7c 2002/*
959a6a28 2003 * 'monitor_printer()' - Monitor the printer state.
b9738d7c 2004 */
2005
2006static void * /* O - Thread exit code */
2007monitor_printer(
2008 _cups_monitor_t *monitor) /* I - Monitoring data */
2009{
2010 http_t *http; /* Connection to printer */
2011 ipp_t *request, /* IPP request */
2012 *response; /* IPP response */
2013 ipp_attribute_t *attr; /* Attribute in response */
2014 int delay, /* Current delay */
8211508e 2015 prev_delay; /* Previous delay */
5ed93a63 2016 ipp_op_t job_op; /* Operation to use */
2017 int job_id; /* Job ID */
2018 const char *job_name; /* Job name */
2019 ipp_jstate_t job_state; /* Job state */
2020 const char *job_user; /* Job originating user name */
b9738d7c 2021
2022
2023 /*
2024 * Make a copy of the printer connection...
2025 */
2026
cce0044f 2027 http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
16f98e36 2028 AF_UNSPEC);
1179bc03 2029 httpSetTimeout(http, 30.0, timeout_cb, NULL);
b9738d7c 2030 cupsSetPasswordCB(password_cb);
2031
2032 /*
2033 * Loop until the job is canceled, aborted, or completed.
2034 */
2035
fc596e79 2036 delay = _cupsNextDelay(0, &prev_delay);
b9738d7c 2037
2038 while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
2039 {
2040 /*
2041 * Reconnect to the printer...
2042 */
2043
2044 if (!httpReconnect(http))
2045 {
2046 /*
2047 * Connected, so check on the printer state...
2048 */
2049
2050 monitor->printer_state = check_printer_state(http, monitor->uri,
2051 monitor->resource,
2052 monitor->user,
4a241564 2053 monitor->version);
b9738d7c 2054
5ed93a63 2055 /*
2056 * Check the status of the job itself...
2057 */
b9738d7c 2058
5ed93a63 2059 job_op = monitor->job_id > 0 ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
2060 request = ippNewRequest(job_op);
2061 request->request.op.version[0] = monitor->version / 10;
2062 request->request.op.version[1] = monitor->version % 10;
b9738d7c 2063
5ed93a63 2064 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2065 NULL, monitor->uri);
2066 if (job_op == IPP_GET_JOB_ATTRIBUTES)
b9738d7c 2067 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
5ed93a63 2068 monitor->job_id);
b9738d7c 2069
5ed93a63 2070 if (monitor->user && monitor->user[0])
2071 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2072 "requesting-user-name", NULL, monitor->user);
b9738d7c 2073
5ed93a63 2074 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2075 "requested-attributes",
2076 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
b9738d7c 2077
5ed93a63 2078 /*
2079 * Do the request...
2080 */
b9738d7c 2081
5ed93a63 2082 response = cupsDoRequest(http, request, monitor->resource);
b9738d7c 2083
5ed93a63 2084 fprintf(stderr, "DEBUG: %s: %s (%s)\n", ippOpString(job_op),
2085 ippErrorString(cupsLastError()), cupsLastErrorString());
5fb23099 2086
5ed93a63 2087 if (job_op == IPP_GET_JOB_ATTRIBUTES)
2088 {
b9738d7c 2089 if ((attr = ippFindAttribute(response, "job-state",
2090 IPP_TAG_ENUM)) != NULL)
2091 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
2092 else
2093 monitor->job_state = IPP_JOB_COMPLETED;
5ed93a63 2094 }
2095 else
2096 {
2097 for (attr = response->attrs; attr; attr = attr->next)
2098 {
2099 job_id = 0;
2100 job_name = NULL;
2101 job_state = IPP_JOB_PENDING;
2102 job_user = NULL;
2103
2104 while (attr && attr->group_tag != IPP_TAG_JOB)
2105 attr = attr->next;
b9738d7c 2106
5ed93a63 2107 if (!attr)
2108 break;
2109
2110 while (attr && attr->group_tag == IPP_TAG_JOB)
2111 {
2112 if (!strcmp(attr->name, "job-id") &&
2113 attr->value_tag == IPP_TAG_INTEGER)
2114 job_id = attr->values[0].integer;
2115 else if (!strcmp(attr->name, "job-name") &&
2116 (attr->value_tag == IPP_TAG_NAME ||
2117 attr->value_tag == IPP_TAG_NAMELANG))
2118 job_name = attr->values[0].string.text;
2119 else if (!strcmp(attr->name, "job-state") &&
2120 attr->value_tag == IPP_TAG_ENUM)
2121 job_state = attr->values[0].integer;
2122 else if (!strcmp(attr->name, "job-originating-user-name") &&
2123 (attr->value_tag == IPP_TAG_NAME ||
2124 attr->value_tag == IPP_TAG_NAMELANG))
2125 job_user = attr->values[0].string.text;
2126
2127 attr = attr->next;
2128 }
2129
2130 if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) &&
2131 job_user && monitor->user && !strcmp(job_user, monitor->user))
2132 {
2133 monitor->job_id = job_id;
2134 monitor->job_state = job_state;
2135 break;
2136 }
2137
2138 if (!attr)
2139 break;
2140 }
b9738d7c 2141 }
2142
5ed93a63 2143 ippDelete(response);
2144
b9738d7c 2145 /*
2146 * Disconnect from the printer - we'll reconnect on the next poll...
2147 */
2148
2149 _httpDisconnect(http);
2150 }
2151
2152 /*
fc596e79 2153 * Sleep for N seconds...
b9738d7c 2154 */
2155
2156 sleep(delay);
2157
fc596e79 2158 delay = _cupsNextDelay(delay, &prev_delay);
b9738d7c 2159 }
2160
5ed93a63 2161 /*
2162 * Cancel the job if necessary...
2163 */
2164
2165 if (job_canceled && monitor->job_id > 0)
2166 if (!httpReconnect(http))
2167 cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
2168 monitor->user, monitor->version);
2169
b9738d7c 2170 /*
2171 * Cleanup and return...
2172 */
2173
2174 httpClose(http);
2175
2176 return (NULL);
2177}
2178
2179
2ec940b5 2180/*
2181 * 'new_request()' - Create a new print creation or validation request.
2182 */
2183
2184static ipp_t * /* O - Request data */
2185new_request(
2186 ipp_op_t op, /* I - IPP operation code */
2187 int version, /* I - IPP version number */
2188 const char *uri, /* I - printer-uri value */
2189 const char *user, /* I - requesting-user-name value */
2190 const char *title, /* I - job-name value */
2191 int num_options, /* I - Number of options to send */
2192 cups_option_t *options, /* I - Options to send */
2193 const char *compression, /* I - compression value or NULL */
aa141f0d 2194 int copies, /* I - copies value or 0 */
03ab99b4 2195 const char *format, /* I - document-format value or NULL */
8211508e 2196 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
adbcb02a 2197 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
2198 ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */
2ec940b5 2199{
2200 int i; /* Looping var */
2201 ipp_t *request; /* Request data */
2202 const char *keyword; /* PWG keyword */
2203 _pwg_size_t *size; /* PWG media size */
2204 ipp_t *media_col, /* media-col value */
2205 *media_size; /* media-size value */
2206 const char *media_source, /* media-source value */
adbcb02a 2207 *media_type, /* media-type value */
2208 *collate_str; /* multiple-document-handling value */
2ec940b5 2209
2210
2211 /*
2212 * Create the IPP request...
2213 */
2214
2215 request = ippNewRequest(op);
2216 request->request.op.version[0] = version / 10;
2217 request->request.op.version[1] = version % 10;
2218
2219 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
2220 ippOpString(request->request.op.operation_id),
2221 request->request.op.version[0],
2222 request->request.op.version[1]);
2223
2224 /*
2225 * Add standard attributes...
2226 */
2227
2228 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2229 NULL, uri);
2230 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
2231
2232 if (user && *user)
2233 {
2234 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2235 "requesting-user-name", NULL, user);
2236 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
2237 }
2238
2239 if (title && *title)
2240 {
2241 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
2242 title);
2243 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
2244 }
2245
2246 if (format)
2247 {
2248 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
2249 "document-format", NULL, format);
2250 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
2251 }
2252
2253#ifdef HAVE_LIBZ
2254 if (compression)
2255 {
2256 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2257 "compression", NULL, compression);
2258 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2259 }
2260#endif /* HAVE_LIBZ */
2261
2262 /*
2263 * Handle options on the command-line...
2264 */
2265
2266 if (num_options > 0)
2267 {
8211508e 2268 if (pc)
2ec940b5 2269 {
10a541ae 2270 int num_finishings = 0, /* Number of finishing values */
2271 finishings[10]; /* Finishing enum values */
2272
2ec940b5 2273 /*
2274 * Send standard IPP attributes...
2275 */
2276
2277 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2278 keyword = cupsGetOption("media", num_options, options);
2279
8211508e 2280 if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2ec940b5 2281 {
2282 /*
2283 * Add a media-col value...
2284 */
2285
2286 media_size = ippNew();
2287 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2288 "x-dimension", size->width);
2289 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2290 "y-dimension", size->length);
2291
2292 media_col = ippNew();
2293 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2294
8211508e 2295 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2296 num_options,
2297 options));
2298 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2299 num_options,
2300 options));
2ec940b5 2301
2302 for (i = 0; i < media_col_sup->num_values; i ++)
2303 {
2304 if (!strcmp(media_col_sup->values[i].string.text,
2305 "media-left-margin"))
2306 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2307 "media-left-margin", size->left);
2308 else if (!strcmp(media_col_sup->values[i].string.text,
2309 "media-bottom-margin"))
2310 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
59075862 2311 "media-bottom-margin", size->bottom);
2ec940b5 2312 else if (!strcmp(media_col_sup->values[i].string.text,
2313 "media-right-margin"))
2314 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
59075862 2315 "media-right-margin", size->right);
2ec940b5 2316 else if (!strcmp(media_col_sup->values[i].string.text,
2317 "media-top-margin"))
2318 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
59075862 2319 "media-top-margin", size->top);
2ec940b5 2320 else if (!strcmp(media_col_sup->values[i].string.text,
2321 "media-source") && media_source)
2322 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2323 "media-source", NULL, media_source);
2324 else if (!strcmp(media_col_sup->values[i].string.text,
2325 "media-type") && media_type)
2326 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2327 "media-type", NULL, media_type);
2328 }
2329
2330 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2331 }
2332
2333 if ((keyword = cupsGetOption("output-bin", num_options,
2334 options)) == NULL)
8211508e 2335 keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2336 options));
2ec940b5 2337
2338 if (keyword)
2339 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2340 NULL, keyword);
2341
2342 if ((keyword = cupsGetOption("output-mode", num_options,
2343 options)) != NULL)
2344 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2345 NULL, keyword);
2346 else if ((keyword = cupsGetOption("ColorModel", num_options,
2347 options)) != NULL)
2348 {
c6fab96f 2349 if (!_cups_strcasecmp(keyword, "Gray"))
2ec940b5 2350 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2351 NULL, "monochrome");
2352 else
2353 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2354 NULL, "color");
2355 }
2356
2357 if ((keyword = cupsGetOption("print-quality", num_options,
2358 options)) != NULL)
2359 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2360 atoi(keyword));
2361 else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2362 options)) != NULL)
2363 {
c6fab96f 2364 if (!_cups_strcasecmp(keyword, "draft"))
2ec940b5 2365 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2366 IPP_QUALITY_DRAFT);
c6fab96f 2367 else if (!_cups_strcasecmp(keyword, "normal"))
2ec940b5 2368 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2369 IPP_QUALITY_NORMAL);
c6fab96f 2370 else if (!_cups_strcasecmp(keyword, "high"))
2ec940b5 2371 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2372 IPP_QUALITY_HIGH);
2373 }
2374
2375 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2376 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2377 NULL, keyword);
8211508e 2378 else if (pc->sides_option &&
2379 (keyword = cupsGetOption(pc->sides_option, num_options,
2ec940b5 2380 options)) != NULL)
2381 {
c6fab96f 2382 if (!_cups_strcasecmp(keyword, pc->sides_1sided))
2ec940b5 2383 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2384 NULL, "one-sided");
c6fab96f 2385 else if (!_cups_strcasecmp(keyword, pc->sides_2sided_long))
2ec940b5 2386 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2387 NULL, "two-sided-long-edge");
c6fab96f 2388 if (!_cups_strcasecmp(keyword, pc->sides_2sided_short))
2ec940b5 2389 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2390 NULL, "two-sided-short-edge");
2391 }
adbcb02a 2392
2393 if (doc_handling_sup &&
d0cc23b7 2394 (!format || _cups_strncasecmp(format, "image/", 6)) &&
adbcb02a 2395 (keyword = cupsGetOption("collate", num_options, options)) != NULL)
2396 {
c6fab96f 2397 if (!_cups_strcasecmp(keyword, "true"))
adbcb02a 2398 collate_str = "separate-documents-collated-copies";
2399 else
2400 collate_str = "separate-documents-uncollated-copies";
7b64ad64 2401
adbcb02a 2402 for (i = 0; i < doc_handling_sup->num_values; i ++)
2403 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2404 {
2405 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2406 "multiple-document-handling", NULL, collate_str);
2407 break;
2408 }
2409 }
32f5cbae 2410
10a541ae 2411 /*
2412 * Map finishing options...
2413 */
2414
2415 num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options,
2416 (int)(sizeof(finishings) /
2417 sizeof(finishings[0])),
2418 finishings);
2419 if (num_finishings > 0)
2420 ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings",
2421 num_finishings, finishings);
2ec940b5 2422 }
2423 else
2424 {
2425 /*
2426 * When talking to another CUPS server, send all options...
2427 */
2428
2429 cupsEncodeOptions(request, num_options, options);
2430 }
2431
2432 if (copies > 1)
2433 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2434 }
2435
2436 return (request);
2437}
2438
2439
b5cb0608 2440/*
2441 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2442 */
2443
072c4872 2444static const char * /* O - Password */
e12df069 2445password_cb(const char *prompt) /* I - Prompt (not used) */
b5cb0608 2446{
2447 (void)prompt;
2448
4233257e 2449 /*
2450 * Remember that we need to authenticate...
2451 */
2452
2453 auth_info_required = "username,password";
2454
acbc1533 2455 if (password && *password && password_tries < 3)
9c85dfbf 2456 {
2457 password_tries ++;
2458
6248387b 2459 return (password);
9c85dfbf 2460 }
6248387b 2461 else
2462 {
2463 /*
4233257e 2464 * Give up after 3 tries or if we don't have a password to begin with...
6248387b 2465 */
2466
4233257e 2467 return (NULL);
6248387b 2468 }
c8f9565c 2469}
2470
2471
7c7997c3 2472/*
2473 * 'report_attr()' - Report an IPP attribute value.
2474 */
2475
2476static void
2477report_attr(ipp_attribute_t *attr) /* I - Attribute */
2478{
7b1ef0fc 2479 int i; /* Looping var */
2480 char value[1024], /* Value string */
2481 *valptr, /* Pointer into value string */
2482 *attrptr; /* Pointer into attribute value */
2483 const char *cached; /* Cached attribute */
7c7997c3 2484
2485
2486 /*
2487 * Convert the attribute values into quoted strings...
2488 */
2489
2490 for (i = 0, valptr = value;
2491 i < attr->num_values && valptr < (value + sizeof(value) - 10);
2492 i ++)
2493 {
2494 if (i > 0)
2495 *valptr++ = ',';
2496
2497 switch (attr->value_tag)
2498 {
2499 case IPP_TAG_INTEGER :
2500 case IPP_TAG_ENUM :
2501 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2502 attr->values[i].integer);
2503 valptr += strlen(valptr);
2504 break;
2505
2506 case IPP_TAG_TEXT :
2507 case IPP_TAG_NAME :
2508 case IPP_TAG_KEYWORD :
2509 *valptr++ = '\"';
2510 for (attrptr = attr->values[i].string.text;
2511 *attrptr && valptr < (value + sizeof(value) - 10);
2512 attrptr ++)
2513 {
2514 if (*attrptr == '\\' || *attrptr == '\"')
2515 *valptr++ = '\\';
2516
2517 *valptr++ = *attrptr;
2518 }
2519 *valptr++ = '\"';
2520 break;
2521
2522 default :
2523 /*
2524 * Unsupported value type...
2525 */
2526
2527 return;
2528 }
2529 }
2530
2531 *valptr = '\0';
2532
6ef97027 2533 _cupsMutexLock(&report_mutex);
2534
7b1ef0fc 2535 if ((cached = cupsGetOption(attr->name, num_attr_cache,
2536 attr_cache)) == NULL || strcmp(cached, value))
2537 {
2538 /*
2539 * Tell the scheduler about the new values...
2540 */
7c7997c3 2541
7b1ef0fc 2542 num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
2543 &attr_cache);
2544 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2545 }
6ef97027 2546
2547 _cupsMutexUnlock(&report_mutex);
7c7997c3 2548}
2549
2550
c8f9565c 2551/*
d1c2727f 2552 * 'report_printer_state()' - Report the printer state.
2553 */
2554
90d0da15 2555static void
4a241564 2556report_printer_state(ipp_t *ipp) /* I - IPP response */
d1c2727f 2557{
b9738d7c 2558 ipp_attribute_t *pa, /* printer-alert */
2559 *pam, /* printer-alert-message */
2560 *psm, /* printer-state-message */
7c7997c3 2561 *reasons, /* printer-state-reasons */
2562 *marker; /* marker-* attributes */
b9738d7c 2563 char value[1024], /* State/message string */
2564 *valptr; /* Pointer into string */
aa141f0d 2565 static int ipp_supplies = -1;
2566 /* Report supply levels? */
d1c2727f 2567
2568
b9738d7c 2569 /*
2570 * Report alerts and messages...
2571 */
2572
2573 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2574 report_attr(pa);
2575
2576 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2577 IPP_TAG_TEXT)) != NULL)
2578 report_attr(pam);
2579
a6ccc6e8 2580 if ((psm = ippFindAttribute(ipp, "printer-state-message",
2581 IPP_TAG_TEXT)) != NULL)
b9738d7c 2582 {
2583 char *ptr; /* Pointer into message */
2584
2585
2586 strlcpy(value, "INFO: ", sizeof(value));
2587 for (ptr = psm->values[0].string.text, valptr = value + 6;
2588 *ptr && valptr < (value + sizeof(value) - 6);
2589 ptr ++)
2590 {
2591 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2592 {
2593 /*
2594 * Substitute "<XX>" for the control character; sprintf is safe because
2595 * we always leave 6 chars free at the end...
2596 */
2597
2598 sprintf(valptr, "<%02X>", *ptr);
2599 valptr += 4;
2600 }
2601 else
2602 *valptr++ = *ptr;
2603 }
2604
2605 *valptr++ = '\n';
57487c71 2606 *valptr = '\0';
b9738d7c 2607
2608 fputs(value, stderr);
2609 }
2610
2611 /*
2612 * Now report printer-state-reasons, filtering out some of the reasons we never
2613 * want to set...
2614 */
a6ccc6e8 2615
d1c2727f 2616 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2617 IPP_TAG_KEYWORD)) == NULL)
90d0da15 2618 return;
d1c2727f 2619
7b1ef0fc 2620 update_reasons(reasons, NULL);
8f4595eb 2621
7c7997c3 2622 /*
2623 * Relay the current marker-* attribute values...
2624 */
2625
aa141f0d 2626 if (ipp_supplies < 0)
2627 {
2628 ppd_file_t *ppd; /* PPD file */
2629 ppd_attr_t *ppdattr; /* Attribute in PPD file */
2630
2631 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2632 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
c6fab96f 2633 ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
aa141f0d 2634 ipp_supplies = 0;
2635 else
2636 ipp_supplies = 1;
2637
2638 ppdClose(ppd);
2639 }
2640
2641 if (ipp_supplies > 0)
2642 {
2643 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2644 report_attr(marker);
2645 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2646 IPP_TAG_INTEGER)) != NULL)
2647 report_attr(marker);
2648 if ((marker = ippFindAttribute(ipp, "marker-levels",
2649 IPP_TAG_INTEGER)) != NULL)
2650 report_attr(marker);
2651 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2652 IPP_TAG_INTEGER)) != NULL)
2653 report_attr(marker);
2654 if ((marker = ippFindAttribute(ipp, "marker-message",
2655 IPP_TAG_TEXT)) != NULL)
2656 report_attr(marker);
2657 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2658 report_attr(marker);
2659 if ((marker = ippFindAttribute(ipp, "marker-types",
2660 IPP_TAG_KEYWORD)) != NULL)
2661 report_attr(marker);
2662 }
d1c2727f 2663}
2664
2665
959a6a28 2666#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2667/*
2668 * 'run_as_user()' - Run the IPP backend as the printing user.
2669 *
2670 * This function uses an XPC-based user agent to run the backend as the printing
2671 * user. We need to do this in order to have access to the user's Kerberos
2672 * credentials.
2673 */
2674
2675static int /* O - Exit status */
2676run_as_user(int argc, /* I - Number of command-line args */
2677 char *argv[], /* I - Command-line arguments */
2678 uid_t uid, /* I - User ID */
2679 const char *device_uri, /* I - Device URI */
2680 int fd) /* I - File to print */
2681{
3d5f60d1 2682 const char *auth_negotiate;/* AUTH_NEGOTIATE env var */
959a6a28 2683 xpc_connection_t conn; /* Connection to XPC service */
2684 xpc_object_t request; /* Request message dictionary */
2685 __block xpc_object_t response; /* Response message dictionary */
2686 dispatch_semaphore_t sem; /* Semaphore for waiting for response */
2687 int status = CUPS_BACKEND_FAILED;
2688 /* Status of request */
2689
2690
2691 fprintf(stderr, "DEBUG: Running IPP backend as UID %d.\n", (int)uid);
2692
2693 /*
2694 * Connect to the user agent for the specified UID...
2695 */
2696
2697 conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
2698 dispatch_get_global_queue(0, 0), 0);
2699 if (!conn)
2700 {
2701 _cupsLangPrintFilter(stderr, "ERROR",
2702 _("Unable to start backend process."));
2703 fputs("DEBUG: Unable to create connection to agent.\n", stderr);
2704 goto cleanup;
2705 }
2706
2707 xpc_connection_set_event_handler(conn,
2708 ^(xpc_object_t event)
2709 {
2710 xpc_type_t messageType = xpc_get_type(event);
2711
2712 if (messageType == XPC_TYPE_ERROR)
2713 {
2714 if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
2715 fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
2716 xpc_connection_get_name(conn));
2717 else if (event == XPC_ERROR_CONNECTION_INVALID)
2718 fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
2719 xpc_connection_get_name(conn));
2720 else
2721 fprintf(stderr, "DEBUG: Unxpected error for service %s: %s\n",
2722 xpc_connection_get_name(conn),
2723 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
2724 }
2725 });
2726 xpc_connection_set_target_uid(conn, uid);
2727 xpc_connection_resume(conn);
2728
2729 /*
2730 * Try starting the backend...
2731 */
2732
2733 request = xpc_dictionary_create(NULL, NULL, 0);
2734 xpc_dictionary_set_int64(request, "command", kPMStartJob);
2735 xpc_dictionary_set_string(request, "device-uri", device_uri);
2736 xpc_dictionary_set_string(request, "job-id", argv[1]);
2737 xpc_dictionary_set_string(request, "user", argv[2]);
2738 xpc_dictionary_set_string(request, "title", argv[3]);
2739 xpc_dictionary_set_string(request, "copies", argv[4]);
2740 xpc_dictionary_set_string(request, "options", argv[5]);
2741 xpc_dictionary_set_string(request, "auth-info-required",
2742 getenv("AUTH_INFO_REQUIRED"));
3d5f60d1 2743 if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
2744 xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
959a6a28 2745 xpc_dictionary_set_fd(request, "stdin", fd);
2746 xpc_dictionary_set_fd(request, "stderr", 2);
2747 xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
2748
2749 sem = dispatch_semaphore_create(0);
2750 response = NULL;
2751
2752 xpc_connection_send_message_with_reply(conn, request,
2753 dispatch_get_global_queue(0,0),
2754 ^(xpc_object_t reply)
2755 {
2756 /* Save the response and wake up */
2757 if (xpc_get_type(reply)
2758 == XPC_TYPE_DICTIONARY)
2759 response = xpc_retain(reply);
2760
2761 dispatch_semaphore_signal(sem);
2762 });
2763
2764 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2765 xpc_release(request);
2766 dispatch_release(sem);
2767
2768 if (response)
2769 {
2770 child_pid = xpc_dictionary_get_int64(response, "child-pid");
2771
2772 xpc_release(response);
2773
2774 if (child_pid)
2775 fprintf(stderr, "DEBUG: Child PID=%d.\n", child_pid);
2776 else
2777 {
2778 _cupsLangPrintFilter(stderr, "ERROR",
2779 _("Unable to start backend process."));
2780 fputs("DEBUG: No child PID.\n", stderr);
2781 goto cleanup;
2782 }
2783 }
2784 else
2785 {
2786 _cupsLangPrintFilter(stderr, "ERROR",
2787 _("Unable to start backend process."));
2788 fputs("DEBUG: No reply from agent.\n", stderr);
2789 goto cleanup;
2790 }
2791
2792 /*
2793 * Then wait for the backend to finish...
2794 */
2795
2796 request = xpc_dictionary_create(NULL, NULL, 0);
2797 xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
2798 xpc_dictionary_set_fd(request, "stderr", 2);
2799
2800 sem = dispatch_semaphore_create(0);
2801 response = NULL;
2802
2803 xpc_connection_send_message_with_reply(conn, request,
2804 dispatch_get_global_queue(0,0),
2805 ^(xpc_object_t reply)
2806 {
2807 /* Save the response and wake up */
2808 if (xpc_get_type(reply)
2809 == XPC_TYPE_DICTIONARY)
2810 response = xpc_retain(reply);
2811
2812 dispatch_semaphore_signal(sem);
2813 });
2814
2815 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2816 xpc_release(request);
2817 dispatch_release(sem);
2818
2819 if (response)
2820 {
2821 status = xpc_dictionary_get_int64(response, "status");
2822
2823 if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
2824 {
2825 fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
2826 status = CUPS_BACKEND_FAILED;
2827 }
2828 else if (WIFSIGNALED(status))
2829 {
2830 fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
2831 status = CUPS_BACKEND_STOP;
2832 }
2833 else if (WIFEXITED(status))
2834 {
2835 status = WEXITSTATUS(status);
2836 fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
2837 }
2838
2839 xpc_release(response);
2840 }
2841 else
2842 _cupsLangPrintFilter(stderr, "ERROR",
2843 _("Unable to get backend exit status."));
2844
2845 cleanup:
2846
2847 if (conn)
2848 {
2849 xpc_connection_suspend(conn);
2850 xpc_connection_cancel(conn);
2851 xpc_release(conn);
2852 }
2853
2854 return (status);
2855}
2856#endif /* HAVE_GSSAPI && HAVE_XPC */
2857
2858
d1c2727f 2859/*
8f4595eb 2860 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2861 */
2862
2863static void
2864sigterm_handler(int sig) /* I - Signal */
2865{
2866 (void)sig; /* remove compiler warnings... */
2867
959a6a28 2868#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2869 if (child_pid)
2870 {
2871 kill(child_pid, sig);
2872 child_pid = 0;
2873 }
2874#endif /* HAVE_GSSAPI && HAVE_XPC */
2875
6875feac 2876 if (!job_canceled)
072c4872 2877 {
2878 /*
959a6a28 2879 * Flag that the job should be canceled...
072c4872 2880 */
2881
6875feac 2882 job_canceled = 1;
072c4872 2883 return;
2884 }
2885
940afb4b 2886 /*
2887 * The scheduler already tried to cancel us once, now just terminate
7b1ef0fc 2888 * after removing our temp file!
940afb4b 2889 */
2890
2891 if (tmpfilename[0])
2892 unlink(tmpfilename);
2893
8f4595eb 2894 exit(1);
2895}
2896
2897
1179bc03 2898/*
2899 * 'timeout_cb()' - Handle HTTP timeouts.
2900 */
2901
2902static int /* O - 1 to continue, 0 to cancel */
2903timeout_cb(http_t *http, /* I - Connection to server (unused) */
2904 void *user_data) /* I - User data (unused) */
2905{
2906 (void)http;
2907 (void)user_data;
2908
2909 return (!job_canceled);
2910}
2911
2912
7b1ef0fc 2913/*
2914 * 'update_reasons()' - Update the printer-state-reasons values.
2915 */
2916
2917static void
2918update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */
2919 const char *s) /* I - STATE: string or NULL */
2920{
2921 char op; /* Add (+), remove (-), replace (\0) */
2922 cups_array_t *new_reasons; /* New reasons array */
2923 char *reason, /* Current reason */
2924 add[2048], /* Reasons added string */
2925 *addptr, /* Pointer into add string */
2926 rem[2048], /* Reasons removed string */
2927 *remptr; /* Pointer into remove string */
2928 const char *addprefix, /* Current add string prefix */
2929 *remprefix; /* Current remove string prefix */
2930
2931
2932 fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
2933 attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
2934 attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
2935
2936 /*
2937 * Create an array of new reason keyword strings...
2938 */
2939
2940 if (attr)
2941 {
2942 int i; /* Looping var */
2943
2944 new_reasons = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2945 op = '\0';
2946
2947 for (i = 0; i < attr->num_values; i ++)
2948 {
2949 reason = attr->values[i].string.text;
2950
2951 if (strcmp(reason, "none") &&
2952 strcmp(reason, "none-report") &&
2953 strcmp(reason, "paused") &&
77dd74a0 2954 strncmp(reason, "spool-area-full", 15) &&
7b1ef0fc 2955 strcmp(reason, "com.apple.print.recoverable-warning") &&
2956 strncmp(reason, "cups-", 5))
2957 cupsArrayAdd(new_reasons, reason);
2958 }
2959 }
2960 else if (s)
2961 {
2962 if (*s == '+' || *s == '-')
2963 op = *s++;
2964 else
2965 op = '\0';
2966
2967 new_reasons = _cupsArrayNewStrings(s);
2968 }
2969 else
2970 return;
2971
2972 /*
2973 * Compute the changes...
2974 */
2975
2976 add[0] = '\0';
2977 addprefix = "STATE: +";
2978 addptr = add;
2979 rem[0] = '\0';
2980 remprefix = "STATE: -";
2981 remptr = rem;
2982
2983 fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
2984 op ? op : ' ', cupsArrayCount(new_reasons),
2985 cupsArrayCount(state_reasons));
2986
6ef97027 2987 _cupsMutexLock(&report_mutex);
7b1ef0fc 2988
2989 if (op == '+')
2990 {
2991 /*
2992 * Add reasons...
2993 */
2994
2995 for (reason = (char *)cupsArrayFirst(new_reasons);
2996 reason;
2997 reason = (char *)cupsArrayNext(new_reasons))
2998 {
2999 if (!cupsArrayFind(state_reasons, reason))
3000 {
3001 if (!strncmp(reason, "cups-remote-", 12))
3002 {
3003 /*
3004 * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
3005 * keywords...
3006 */
3007
3008 char *temp; /* Current reason in state_reasons */
3009
3010 cupsArraySave(state_reasons);
3011
3012 for (temp = (char *)cupsArrayFirst(state_reasons);
3013 temp;
3014 temp = (char *)cupsArrayNext(state_reasons))
3015 if (!strncmp(temp, "cups-remote-", 12))
3016 {
3017 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3018 temp);
3019 remptr += strlen(remptr);
3020 remprefix = ",";
3021
3022 cupsArrayRemove(state_reasons, temp);
3023 break;
3024 }
3025
3026 cupsArrayRestore(state_reasons);
3027 }
3028
3029 cupsArrayAdd(state_reasons, reason);
3030
3031 snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3032 reason);
3033 addptr += strlen(addptr);
3034 addprefix = ",";
3035 }
3036 }
3037 }
3038 else if (op == '-')
3039 {
3040 /*
3041 * Remove reasons...
3042 */
3043
3044 for (reason = (char *)cupsArrayFirst(new_reasons);
3045 reason;
3046 reason = (char *)cupsArrayNext(new_reasons))
3047 {
3048 if (cupsArrayFind(state_reasons, reason))
3049 {
3050 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3051 reason);
3052 remptr += strlen(remptr);
3053 remprefix = ",";
3054
3055 cupsArrayRemove(state_reasons, reason);
3056 }
3057 }
3058 }
3059 else
3060 {
3061 /*
3062 * Replace reasons...
3063 */
3064
3065 for (reason = (char *)cupsArrayFirst(state_reasons);
3066 reason;
3067 reason = (char *)cupsArrayNext(state_reasons))
3068 {
3069 if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
3070 {
3071 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3072 reason);
3073 remptr += strlen(remptr);
3074 remprefix = ",";
3075
3076 cupsArrayRemove(state_reasons, reason);
3077 }
3078 }
3079
3080 for (reason = (char *)cupsArrayFirst(new_reasons);
3081 reason;
3082 reason = (char *)cupsArrayNext(new_reasons))
3083 {
3084 if (!cupsArrayFind(state_reasons, reason))
3085 {
3086 cupsArrayAdd(state_reasons, reason);
3087
3088 snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3089 reason);
3090 addptr += strlen(addptr);
3091 addprefix = ",";
3092 }
3093 }
3094 }
3095
6ef97027 3096 _cupsMutexUnlock(&report_mutex);
7b1ef0fc 3097
3098 /*
3099 * Report changes and return...
3100 */
3101
3102 if (add[0] && rem[0])
3103 fprintf(stderr, "%s\n%s\n", add, rem);
3104 else if (add[0])
3105 fprintf(stderr, "%s\n", add);
3106 else if (rem[0])
3107 fprintf(stderr, "%s\n", rem);
3108}
3109
8f4595eb 3110/*
c9d3f842 3111 * End of "$Id$".
c8f9565c 3112 */