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