]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Mirror 1.1.x changes.
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
d8e1f369 2 * "$Id: ipp.c,v 1.38.2.27 2003/04/16 19:36:31 mike Exp $"
c8f9565c 3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
1d9595ab 6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
c8f9565c 7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
8784b6a6 17 * 44141 Airport View Drive, Suite 204
c8f9565c 18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
dab1a4d8 24 * This file is subject to the Apple OS-Developed Software exception.
25 *
c8f9565c 26 * Contents:
27 *
d1c2727f 28 * main() - Send a file to the printer or server.
29 * password_cb() - Disable the password prompt for
30 * cupsDoFileRequest().
31 * report_printer_state() - Report the printer state.
8f4595eb 32 * run_pictwps_filter() - Convert PICT files to PostScript when printing
33 * remotely.
34 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
c8f9565c 35 */
36
37/*
38 * Include necessary headers.
39 */
40
41#include <stdio.h>
42#include <stdlib.h>
43#include <errno.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <cups/cups.h>
47#include <cups/language.h>
48#include <cups/string.h>
4ff40357 49#include <signal.h>
1c85e5c0 50#include <sys/wait.h>
c8f9565c 51
52
8f4595eb 53/*
54 * Globals...
55 */
56
57static char tmpfilename[1024] = ""; /* Temporary spool file name */
58
59
dfb69f7a 60/*
61 * Some OS's don't have hstrerror(), most notably Solaris...
62 */
63
64#ifndef HAVE_HSTRERROR
65# define hstrerror cups_hstrerror
66
67const char * /* O - Error string */
68cups_hstrerror(int error) /* I - Error number */
69{
70 static const char * const errors[] =
71 {
72 "OK",
73 "Host not found.",
74 "Try again.",
75 "Unrecoverable lookup error.",
76 "No data associated with name."
77 };
78
79
80 if (error < 0 || error > 4)
81 return ("Unknown hostname lookup error.");
82 else
83 return (errors[error]);
84}
adf75dba 85#elif defined(_AIX)
86/*
87 * AIX doesn't provide a prototype but does provide the function...
88 */
89extern const char *hstrerror(int);
dfb69f7a 90#endif /* !HAVE_HSTRERROR */
91
92
b5cb0608 93/*
94 * Local functions...
95 */
96
97const char *password_cb(const char *);
d1c2727f 98int report_printer_state(ipp_t *ipp);
b5cb0608 99
a684a3b0 100#ifdef __APPLE__
101int run_pictwps_filter(char **argv, char *filename, int length);
102#endif /* __APPLE__ */
8f4595eb 103static void sigterm_handler(int sig);
a684a3b0 104
b5cb0608 105
106/*
107 * Local globals...
108 */
109
110char *password = NULL;
111
112
c8f9565c 113/*
114 * 'main()' - Send a file to the printer or server.
115 *
116 * Usage:
117 *
118 * printer-uri job-id user title copies options [file]
119 */
120
121int /* O - Exit status */
122main(int argc, /* I - Number of command-line arguments (6 or 7) */
123 char *argv[]) /* I - Command-line arguments */
124{
25512894 125 int i; /* Looping var */
c8f9565c 126 int num_options; /* Number of printer options */
3079988e 127 cups_option_t *options; /* Printer options */
c8f9565c 128 char method[255], /* Method in URI */
129 hostname[1024], /* Hostname */
130 username[255], /* Username info */
8f4595eb 131 resource[1024]; /* Resource info (printer name) */
d8e1f369 132 char *filename; /* File to print */
c8f9565c 133 int port; /* Port number (not used) */
b5cb0608 134 char uri[HTTP_MAX_URI];/* Updated URI without user/pass */
97fcaf92 135 ipp_status_t ipp_status; /* Status of IPP request */
c8f9565c 136 http_t *http; /* HTTP connection */
137 ipp_t *request, /* IPP request */
2922de55 138 *response, /* IPP response */
139 *supported; /* get-printer-attributes response */
b5cb0608 140 ipp_attribute_t *job_id_attr; /* job-id attribute */
141 int job_id; /* job-id value */
8f4595eb 142 ipp_attribute_t *job_sheets; /* job-media-sheets-completed attribute */
b5cb0608 143 ipp_attribute_t *job_state; /* job-state attribute */
97fcaf92 144 ipp_attribute_t *copies_sup; /* copies-supported attribute */
25512894 145 ipp_attribute_t *charset_sup; /* charset-supported attribute */
36aa99bb 146 ipp_attribute_t *format_sup; /* document-format-supported attribute */
997cf8b0 147 ipp_attribute_t *printer_state;
148 /* printer-state attribute */
149 ipp_attribute_t *printer_accepting;
150 /* printer-is-accepting-jobs attribute */
25512894 151 const char *charset; /* Character set to use */
c8f9565c 152 cups_lang_t *language; /* Default language */
97fcaf92 153 int copies; /* Number of copies remaining */
0a3944d6 154 const char *content_type; /* CONTENT_TYPE environment variable */
4ff40357 155#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
156 struct sigaction action; /* Actions for POSIX signals */
157#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
5a2c7855 158 int version; /* IPP version */
d1c2727f 159 int reasons; /* Number of printer-state-reasons shown */
8f4595eb 160 static const char * const pattrs[] =
161 { /* Printer attributes we want */
162 "copies-supported",
163 "charset-supported",
164 "document-format-supported",
165 "printer-is-accepting-jobs",
166 "printer-state",
167 "printer-state-reasons",
168 };
169 static const char * const jattrs[] =
170 { /* Job attributes we want */
171 "job-media-sheets-completed",
172 "job-state"
173 };
c8f9565c 174
4b23f3b3 175
176 /*
177 * Make sure status messages are not buffered...
178 */
179
2922de55 180 setbuf(stderr, NULL);
c8f9565c 181
98904cd6 182 /*
8f4595eb 183 * Ignore SIGPIPE and catch SIGTERM signals...
98904cd6 184 */
185
186#ifdef HAVE_SIGSET
187 sigset(SIGPIPE, SIG_IGN);
8f4595eb 188 sigset(SIGTERM, sigterm_handler);
98904cd6 189#elif defined(HAVE_SIGACTION)
190 memset(&action, 0, sizeof(action));
191 action.sa_handler = SIG_IGN;
192 sigaction(SIGPIPE, &action, NULL);
8f4595eb 193
194 sigemptyset(&action.sa_mask);
195 sigaddset(&action.sa_mask, SIGTERM);
196 action.sa_handler = sigterm_handler;
197 sigaction(SIGTERM, &action, NULL);
98904cd6 198#else
199 signal(SIGPIPE, SIG_IGN);
8f4595eb 200 signal(SIGTERM, sigterm_handler);
98904cd6 201#endif /* HAVE_SIGSET */
202
4b23f3b3 203 /*
204 * Check command-line...
205 */
206
68edc300 207 if (argc == 1)
208 {
183914a3 209 char *s;
210
d4c438d4 211 if ((s = strrchr(argv[0], '/')) != NULL)
212 s ++;
213 else
214 s = argv[0];
215
7c587866 216 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n", s, s);
68edc300 217 return (0);
218 }
219 else if (argc < 6 || argc > 7)
c8f9565c 220 {
221 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
222 argv[0]);
223 return (1);
224 }
225
a684a3b0 226 /*
227 * Get the content type...
228 */
229
230 if (argc > 6)
231 content_type = getenv("CONTENT_TYPE");
232 else
233 content_type = "application/vnd.cups-raw";
234
235 if (content_type == NULL)
236 content_type = "application/octet-stream";
237
c8f9565c 238 /*
239 * If we have 7 arguments, print the file named on the command-line.
97fcaf92 240 * Otherwise, copy stdin to a temporary file and print the temporary
241 * file.
c8f9565c 242 */
243
244 if (argc == 6)
97fcaf92 245 {
246 /*
247 * Copy stdin to a temporary file...
248 */
249
1b5bf964 250 int fd; /* Temporary file */
97fcaf92 251 char buffer[8192]; /* Buffer for copying */
252 int bytes; /* Number of bytes read */
253
254
8f4595eb 255 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
97fcaf92 256 {
257 perror("ERROR: unable to create temporary file");
258 return (1);
259 }
260
261 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
1b5bf964 262 if (write(fd, buffer, bytes) < bytes)
97fcaf92 263 {
264 perror("ERROR: unable to write to temporary file");
1b5bf964 265 close(fd);
8f4595eb 266 unlink(tmpfilename);
97fcaf92 267 return (1);
268 }
269
1b5bf964 270 close(fd);
8f4595eb 271 filename = tmpfilename;
97fcaf92 272 }
273 else
8f4595eb 274 filename = argv[6];
97fcaf92 275
276 /*
b5cb0608 277 * Extract the hostname and printer name from the URI...
97fcaf92 278 */
279
b5cb0608 280 httpSeparate(argv[0], method, username, hostname, &port, resource);
c8f9565c 281
282 /*
b5cb0608 283 * Set the authentication info, if any...
c8f9565c 284 */
285
b5cb0608 286 cupsSetPasswordCB(password_cb);
287
288 if (username[0])
289 {
290 if ((password = strchr(username, ':')) != NULL)
291 *password++ = '\0';
292
293 cupsSetUser(username);
294 }
c8f9565c 295
296 /*
297 * Try connecting to the remote server...
298 */
299
97fcaf92 300 do
c8f9565c 301 {
a4e23897 302 fprintf(stderr, "INFO: Connecting to %s on port %d...\n", hostname, port);
c8f9565c 303
7914a6a1 304 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
d21a7597 305 {
997cf8b0 306 if (getenv("CLASS") != NULL)
307 {
308 /*
309 * If the CLASS environment variable is set, the job was submitted
310 * to a class and not to a specific queue. In this case, we want
311 * to abort immediately so that the job can be requeued on the next
312 * available printer in the class.
313 */
314
315 fprintf(stderr, "INFO: Unable to queue job on %s, queuing on next printer in class...\n",
316 hostname);
317
a684a3b0 318 if (argc == 6 || strcmp(filename, argv[6]))
997cf8b0 319 unlink(filename);
320
321 /*
322 * Sleep 5 seconds to keep the job from requeuing too rapidly...
323 */
324
325 sleep(5);
326
327 return (1);
328 }
329
4c2096b8 330 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
331 errno == EHOSTUNREACH)
97fcaf92 332 {
333 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
334 hostname);
335 sleep(30);
336 }
7e3ba0af 337 else if (h_errno)
338 {
339 fprintf(stderr, "INFO: Unable to lookup host \'%s\' - %s\n",
340 hostname, hstrerror(h_errno));
341 sleep(30);
342 }
97fcaf92 343 else
344 {
345 perror("ERROR: Unable to connect to IPP host");
2cc18dd2 346 sleep(30);
97fcaf92 347 }
d21a7597 348 }
c8f9565c 349 }
97fcaf92 350 while (http == NULL);
c8f9565c 351
a4e23897 352 fprintf(stderr, "INFO: Connected to %s...\n", hostname);
353
c8f9565c 354 /*
355 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 356 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
357 * might contain username:password information...
c8f9565c 358 */
359
3f9cb6c6 360 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
c8f9565c 361
362 /*
97fcaf92 363 * First validate the destination and see if the device supports multiple
364 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
365 * don't support the copies attribute...
c8f9565c 366 */
367
25512894 368 language = cupsLangDefault();
369 charset_sup = NULL;
370 copies_sup = NULL;
2922de55 371 format_sup = NULL;
25512894 372 version = 1;
2922de55 373 supported = NULL;
c8f9565c 374
97fcaf92 375 do
c8f9565c 376 {
377 /*
97fcaf92 378 * Build the IPP request...
c8f9565c 379 */
380
97fcaf92 381 request = ippNew();
0a3ac972 382 request->request.op.version[1] = version;
383 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
384 request->request.op.request_id = 1;
c8f9565c 385
97fcaf92 386 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
67436e05 387 "attributes-charset", NULL, "utf-8");
97fcaf92 388
389 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
390 "attributes-natural-language", NULL,
67436e05 391 language != NULL ? language->language : "en");
c8f9565c 392
97fcaf92 393 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
394 NULL, uri);
c8f9565c 395
8f4595eb 396 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
397 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
398 NULL, pattrs);
399
97fcaf92 400 /*
401 * Do the request...
402 */
c8f9565c 403
a4e23897 404 fputs("DEBUG: Getting supported attributes...\n", stderr);
405
b5cb0608 406 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
407 ipp_status = cupsLastError();
408 else
0a3ac972 409 ipp_status = supported->request.status.status_code;
b5cb0608 410
411 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 412 {
b5cb0608 413 if (ipp_status == IPP_PRINTER_BUSY ||
414 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 415 {
b5cb0608 416 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
d1c2727f 417 report_printer_state(supported);
97fcaf92 418 sleep(10);
97fcaf92 419 }
b5cb0608 420 else if ((ipp_status == IPP_BAD_REQUEST ||
421 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
c8f9565c 422 {
b5cb0608 423 /*
424 * Switch to IPP/1.0...
425 */
97fcaf92 426
b5cb0608 427 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr);
428 version = 0;
a4e23897 429 httpReconnect(http);
c8f9565c 430 }
67474245 431 else if (ipp_status == IPP_NOT_FOUND)
432 {
433 fputs("ERROR: Destination printer does not exist!\n", stderr);
434
435 if (supported)
436 ippDelete(supported);
437
438 return (1);
439 }
97fcaf92 440 else
67474245 441 {
d1c2727f 442 fprintf(stderr, "ERROR: Unable to get printer status (%s)!\n",
b5cb0608 443 ippErrorString(ipp_status));
67474245 444 sleep(10);
445 }
d1c2727f 446
447 if (supported)
448 ippDelete(supported);
449
450 continue;
97fcaf92 451 }
b5cb0608 452 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
453 IPP_TAG_RANGE)) != NULL)
97fcaf92 454 {
b5cb0608 455 /*
456 * Has the "copies-supported" attribute - does it have an upper
457 * bound > 1?
458 */
97fcaf92 459
b5cb0608 460 if (copies_sup->values[0].range.upper <= 1)
461 copies_sup = NULL; /* No */
462 }
97fcaf92 463
b5cb0608 464 charset_sup = ippFindAttribute(supported, "charset-supported",
465 IPP_TAG_CHARSET);
466 format_sup = ippFindAttribute(supported, "document-format-supported",
467 IPP_TAG_MIMETYPE);
468
469 if (format_sup)
470 {
471 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
472 format_sup->num_values);
473 for (i = 0; i < format_sup->num_values; i ++)
474 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
475 format_sup->values[i].string.text);
c8f9565c 476 }
d1c2727f 477
478 report_printer_state(supported);
c8f9565c 479 }
97fcaf92 480 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 481
997cf8b0 482 /*
483 * See if the printer is accepting jobs and is not stopped; if either
484 * condition is true and we are printing to a class, requeue the job...
485 */
486
487 if (getenv("CLASS") != NULL)
488 {
489 printer_state = ippFindAttribute(supported, "printer-state",
490 IPP_TAG_ENUM);
491 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
492 IPP_TAG_BOOLEAN);
493
494 if (printer_state == NULL ||
495 printer_state->values[0].integer > IPP_PRINTER_PROCESSING ||
496 printer_accepting == NULL ||
497 !printer_accepting->values[0].boolean)
498 {
499 /*
500 * If the CLASS environment variable is set, the job was submitted
501 * to a class and not to a specific queue. In this case, we want
502 * to abort immediately so that the job can be requeued on the next
503 * available printer in the class.
504 */
505
506 fprintf(stderr, "INFO: Unable to queue job on %s, queuing on next printer in class...\n",
507 hostname);
508
509 ippDelete(supported);
510 httpClose(http);
511
a684a3b0 512 if (argc == 6 || strcmp(filename, argv[6]))
997cf8b0 513 unlink(filename);
514
515 /*
516 * Sleep 5 seconds to keep the job from requeuing too rapidly...
517 */
518
519 sleep(5);
520
521 return (1);
522 }
523 }
524
c8f9565c 525 /*
97fcaf92 526 * See if the printer supports multiple copies...
c8f9565c 527 */
528
d1c2727f 529 if (copies_sup || argc < 7)
97fcaf92 530 copies = 1;
0c5b0932 531 else
97fcaf92 532 copies = atoi(argv[4]);
0c5b0932 533
25512894 534 /*
535 * Figure out the character set to use...
536 */
537
538 charset = language ? cupsLangEncoding(language) : "us-ascii";
539
540 if (charset_sup)
541 {
542 /*
543 * See if IPP server supports the requested character set...
544 */
545
546 for (i = 0; i < charset_sup->num_values; i ++)
547 if (strcasecmp(charset, charset_sup->values[i].string.text) == 0)
548 break;
549
550 /*
551 * If not, choose us-ascii or utf-8...
552 */
553
554 if (i >= charset_sup->num_values)
555 {
556 /*
557 * See if us-ascii is supported...
558 */
559
560 for (i = 0; i < charset_sup->num_values; i ++)
561 if (strcasecmp("us-ascii", charset_sup->values[i].string.text) == 0)
562 break;
563
564 if (i < charset_sup->num_values)
565 charset = "us-ascii";
566 else
567 charset = "utf-8";
568 }
569 }
570
c8f9565c 571 /*
97fcaf92 572 * Then issue the print-job request...
c8f9565c 573 */
574
d1c2727f 575 reasons = 0;
576
97fcaf92 577 while (copies > 0)
c8f9565c 578 {
579 /*
97fcaf92 580 * Build the IPP request...
c8f9565c 581 */
582
97fcaf92 583 request = ippNew();
0a3ac972 584 request->request.op.version[1] = version;
585 request->request.op.operation_id = IPP_PRINT_JOB;
586 request->request.op.request_id = 1;
c8f9565c 587
97fcaf92 588 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
25512894 589 "attributes-charset", NULL, charset);
8ce7c000 590
97fcaf92 591 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
592 "attributes-natural-language", NULL,
67436e05 593 language != NULL ? language->language : "en");
c8f9565c 594
97fcaf92 595 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
596 NULL, uri);
c8f9565c 597
97fcaf92 598 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
599
753453e4 600 if (argv[2][0])
601 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
602 NULL, argv[2]);
97fcaf92 603
604 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
605
753453e4 606 if (argv[3][0])
607 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
608 argv[3]);
c8f9565c 609
97fcaf92 610 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 611
c8f9565c 612 /*
97fcaf92 613 * Handle options on the command-line...
c8f9565c 614 */
615
97fcaf92 616 options = NULL;
617 num_options = cupsParseOptions(argv[5], 0, &options);
618
e7186d00 619#ifdef __APPLE__
620 if (content_type != NULL && strcasecmp(content_type, "application/pictwps") == 0)
621 {
622 if (format_sup != NULL)
623 {
624 for (i = 0; i < format_sup->num_values; i ++)
625 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
626 break;
627 }
628
629 if (format_sup == NULL || i >= format_sup->num_values)
630 {
631 /*
632 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
633 * so convert the document to PostScript...
634 */
635
636 if (run_pictwps_filter(argv, filename, sizeof(filename)))
637 return (1);
638
639 /*
640 * Change the MIME type to application/postscript...
641 */
642
643 content_type = "application/postscript";
644 }
645 }
646#endif /* __APPLE__ */
647
753453e4 648 if (content_type != NULL && format_sup != NULL)
36aa99bb 649 {
650 for (i = 0; i < format_sup->num_values; i ++)
651 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
652 break;
653
654 if (i < format_sup->num_values)
655 num_options = cupsAddOption("document-format", content_type,
656 num_options, &options);
657 }
97fcaf92 658
753453e4 659 if (copies_sup)
660 {
661 /*
662 * Only send options if the destination printer supports the copies
663 * attribute. This is a hack for the HP JetDirect implementation of
664 * IPP, which does not accept extension attributes and incorrectly
665 * reports a client-error-bad-request error instead of the
666 * successful-ok-unsupported-attributes status. In short, at least
667 * some HP implementations of IPP are non-compliant.
668 */
669
670 cupsEncodeOptions(request, num_options, options);
671 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
672 atoi(argv[4]));
673 }
674
183914a3 675 cupsFreeOptions(num_options, options);
c8f9565c 676
a4e23897 677 /*
678 * If copies aren't supported, then we are likely dealing with an HP
679 * JetDirect. The HP IPP implementation seems to close the connection
680 * after every request (that is, it does *not* implement HTTP Keep-
681 * Alive, which is REQUIRED by HTTP/1.1...
682 */
683
684 if (!copies_sup)
685 httpReconnect(http);
686
cb555bcf 687 /*
b5cb0608 688 * Do the request...
cb555bcf 689 */
690
b5cb0608 691 if ((response = cupsDoFileRequest(http, request, resource, filename)) == NULL)
692 ipp_status = cupsLastError();
693 else
0a3ac972 694 ipp_status = response->request.status.status_code;
b5cb0608 695
696 if (ipp_status > IPP_OK_CONFLICT)
697 {
753453e4 698 job_id = 0;
699
b5cb0608 700 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
701 ipp_status == IPP_PRINTER_BUSY)
702 {
703 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
704 sleep(10);
705 }
706 else
707 fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
708 ippErrorString(ipp_status));
709 }
710 else if ((job_id_attr = ippFindAttribute(response, "job-id",
711 IPP_TAG_INTEGER)) == NULL)
97fcaf92 712 {
b5cb0608 713 fputs("INFO: Print file accepted - job ID unknown.\n", stderr);
714 job_id = 0;
715 }
716 else
717 {
718 job_id = job_id_attr->values[0].integer;
719 fprintf(stderr, "INFO: Print file accepted - job ID %d.\n", job_id);
97fcaf92 720 }
cb555bcf 721
b5cb0608 722 if (response)
723 ippDelete(response);
724
753453e4 725 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 726 {
727 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
728 copies --;
729 }
730 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
731 ipp_status != IPP_PRINTER_BUSY)
732 break;
8ce7c000 733
c8f9565c 734 /*
b5cb0608 735 * Wait for the job to complete...
c8f9565c 736 */
737
b5cb0608 738 if (!job_id)
739 continue;
740
741 fputs("INFO: Waiting for job to complete...\n", stderr);
742
97fcaf92 743 for (;;)
c8f9565c 744 {
97fcaf92 745 /*
b5cb0608 746 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 747 */
748
b5cb0608 749 request = ippNew();
0a3ac972 750 request->request.op.version[1] = version;
751 request->request.op.operation_id = IPP_GET_JOB_ATTRIBUTES;
752 request->request.op.request_id = 1;
97fcaf92 753
b5cb0608 754 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
755 "attributes-charset", NULL, charset);
3f9cb6c6 756
b5cb0608 757 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
758 "attributes-natural-language", NULL,
759 language != NULL ? language->language : "en");
97fcaf92 760
b5cb0608 761 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
762 NULL, uri);
3f9cb6c6 763
b5cb0608 764 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
765 job_id);
c8f9565c 766
ee8b7dd3 767 if (argv[2][0])
768 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
769 NULL, argv[2]);
770
8f4595eb 771 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
772 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
773 NULL, jattrs);
97fcaf92 774
775 /*
b5cb0608 776 * Do the request...
97fcaf92 777 */
778
a4e23897 779 if (!copies_sup)
780 httpReconnect(http);
781
b5cb0608 782 if ((response = cupsDoRequest(http, request, resource)) == NULL)
783 ipp_status = cupsLastError();
784 else
0a3ac972 785 ipp_status = response->request.status.status_code;
97fcaf92 786
b5cb0608 787 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 788 {
b5cb0608 789 /*
d1c2727f 790 * Job has gone away and/or the server has no job history...
b5cb0608 791 */
97fcaf92 792
b5cb0608 793 ippDelete(response);
d1c2727f 794
795 ipp_status = IPP_OK;
b5cb0608 796 break;
97fcaf92 797 }
798
b5cb0608 799 if (ipp_status > IPP_OK_CONFLICT)
6a536282 800 {
b5cb0608 801 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
802 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 803 {
b5cb0608 804 if (response)
805 ippDelete(response);
806
807 fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
808 job_id, ippErrorString(ipp_status));
809 break;
97fcaf92 810 }
6a536282 811 }
8f4595eb 812
813 if (response != NULL)
97fcaf92 814 {
8f4595eb 815 if ((job_sheets = ippFindAttribute(response, "job-media-sheets-completed",
816 IPP_TAG_INTEGER)) != NULL)
817 fprintf(stderr, "PAGE: total %d\n", job_sheets->values[0].integer);
97fcaf92 818
8f4595eb 819 if ((job_state = ippFindAttribute(response, "job-state",
820 IPP_TAG_ENUM)) != NULL)
97fcaf92 821 {
8f4595eb 822 /*
823 * Stop polling if the job is finished or pending-held...
824 */
825
826 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
827 job_state->values[0].integer == IPP_JOB_HELD)
828 {
829 ippDelete(response);
830 break;
831 }
97fcaf92 832 }
97fcaf92 833 }
834
d1c2727f 835 if (response)
836 ippDelete(response);
837
b5cb0608 838 /*
d1c2727f 839 * Now check on the printer state...
b5cb0608 840 */
c8f9565c 841
d1c2727f 842 request = ippNew();
0a3ac972 843 request->request.op.version[1] = version;
844 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
845 request->request.op.request_id = 1;
d1c2727f 846
847 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
848 "attributes-charset", NULL, charset);
849
850 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
851 "attributes-natural-language", NULL,
852 language != NULL ? language->language : "en");
853
854 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
855 NULL, uri);
856
ee8b7dd3 857 if (argv[2][0])
858 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
859 NULL, argv[2]);
860
d1c2727f 861 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
ee8b7dd3 862 "requested-attributes", NULL, "printer-state-reasons");
d1c2727f 863
864 /*
865 * Do the request...
866 */
867
a4e23897 868 if (!copies_sup)
869 httpReconnect(http);
870
d1c2727f 871 if ((response = cupsDoRequest(http, request, resource)) != NULL)
872 {
873 reasons = report_printer_state(response);
b5cb0608 874 ippDelete(response);
d1c2727f 875 }
876
877 /*
878 * Wait 10 seconds before polling again...
879 */
97fcaf92 880
b5cb0608 881 sleep(10);
97fcaf92 882 }
c8f9565c 883 }
884
885 /*
886 * Free memory...
887 */
888
889 httpClose(http);
c8f9565c 890
2922de55 891 if (supported)
892 ippDelete(supported);
893
c8f9565c 894 /*
8f4595eb 895 * Remove the temporary file if necessary...
c8f9565c 896 */
897
d8e1f369 898 if (tmpfilename[0])
8f4595eb 899 unlink(tmpfilename);
c8f9565c 900
901 /*
902 * Return the queue status...
903 */
904
d1c2727f 905 if (ipp_status <= IPP_OK_CONFLICT && reasons == 0)
753453e4 906 fputs("INFO: Ready to print.\n", stderr);
b5cb0608 907
908 return (ipp_status > IPP_OK_CONFLICT);
909}
910
911
912/*
913 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
914 */
915
916const char * /* O - Password */
917password_cb(const char *prompt) /* I - Prompt (not used) */
918{
919 (void)prompt;
920
921 return (password);
c8f9565c 922}
923
924
925/*
d1c2727f 926 * 'report_printer_state()' - Report the printer state.
927 */
928
d8076497 929int /* O - Number of reasons shown */
d1c2727f 930report_printer_state(ipp_t *ipp) /* I - IPP response */
931{
932 int i; /* Looping var */
d8076497 933 int count; /* Count of reasons shown... */
d1c2727f 934 ipp_attribute_t *reasons; /* printer-state-reasons */
8f4595eb 935 const char *reason; /* Current reason */
d1c2727f 936 const char *message; /* Message to show */
d8076497 937 char unknown[1024]; /* Unknown message string */
8f4595eb 938 const char *prefix; /* Prefix for STATE: line */
939 char state[1024]; /* State string */
d1c2727f 940
941
942 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
943 IPP_TAG_KEYWORD)) == NULL)
944 return (0);
945
8f4595eb 946 state[0] = '\0';
947 prefix = "STATE: ";
948
d8076497 949 for (i = 0, count = 0; i < reasons->num_values; i ++)
d1c2727f 950 {
8f4595eb 951 reason = reasons->values[i].string.text;
952
953 strlcat(state, prefix, sizeof(state));
954 strlcat(state, reason, sizeof(state));
955
956 prefix = ",";
d1c2727f 957 message = NULL;
958
8f4595eb 959 if (strncmp(reason, "media-needed", 12) == 0)
d1c2727f 960 message = "Media tray needs to be filled.";
8f4595eb 961 else if (strncmp(reason, "media-jam", 9) == 0)
d1c2727f 962 message = "Media jam!";
8f4595eb 963 else if (strncmp(reason, "moving-to-paused", 16) == 0 ||
964 strncmp(reason, "paused", 6) == 0 ||
965 strncmp(reason, "shutdown", 8) == 0)
d1c2727f 966 message = "Printer off-line.";
8f4595eb 967 else if (strncmp(reason, "toner-low", 9) == 0)
d1c2727f 968 message = "Toner low.";
8f4595eb 969 else if (strncmp(reason, "toner-empty", 11) == 0)
d1c2727f 970 message = "Out of toner!";
8f4595eb 971 else if (strncmp(reason, "cover-open", 10) == 0)
d1c2727f 972 message = "Cover open.";
8f4595eb 973 else if (strncmp(reason, "interlock-open", 14) == 0)
d1c2727f 974 message = "Interlock open.";
8f4595eb 975 else if (strncmp(reason, "door-open", 9) == 0)
d1c2727f 976 message = "Door open.";
8f4595eb 977 else if (strncmp(reason, "input-tray-missing", 18) == 0)
d1c2727f 978 message = "Media tray missing!";
8f4595eb 979 else if (strncmp(reason, "media-low", 9) == 0)
d1c2727f 980 message = "Media tray almost empty.";
8f4595eb 981 else if (strncmp(reason, "media-empty", 11) == 0)
d1c2727f 982 message = "Media tray empty!";
8f4595eb 983 else if (strncmp(reason, "output-tray-missing", 19) == 0)
d1c2727f 984 message = "Output tray missing!";
8f4595eb 985 else if (strncmp(reason, "output-area-almost-full", 23) == 0)
d1c2727f 986 message = "Output bin almost full.";
8f4595eb 987 else if (strncmp(reason, "output-area-full", 16) == 0)
d1c2727f 988 message = "Output bin full!";
8f4595eb 989 else if (strncmp(reason, "marker-supply-low", 17) == 0)
d1c2727f 990 message = "Ink/toner almost empty.";
8f4595eb 991 else if (strncmp(reason, "marker-supply-empty", 19) == 0)
d1c2727f 992 message = "Ink/toner empty!";
8f4595eb 993 else if (strncmp(reason, "marker-waste-almost-full", 24) == 0)
d1c2727f 994 message = "Ink/toner waste bin almost full.";
8f4595eb 995 else if (strncmp(reason, "marker-waste-full", 17) == 0)
d1c2727f 996 message = "Ink/toner waste bin full!";
8f4595eb 997 else if (strncmp(reason, "fuser-over-temp", 15) == 0)
d1c2727f 998 message = "Fuser temperature high!";
8f4595eb 999 else if (strncmp(reason, "fuser-under-temp", 16) == 0)
d1c2727f 1000 message = "Fuser temperature low!";
8f4595eb 1001 else if (strncmp(reason, "opc-near-eol", 12) == 0)
d1c2727f 1002 message = "OPC almost at end-of-life.";
8f4595eb 1003 else if (strncmp(reason, "opc-life-over", 13) == 0)
d1c2727f 1004 message = "OPC at end-of-life!";
8f4595eb 1005 else if (strncmp(reason, "developer-low", 13) == 0)
d1c2727f 1006 message = "Developer almost empty.";
8f4595eb 1007 else if (strncmp(reason, "developer-empty", 15) == 0)
d1c2727f 1008 message = "Developer empty!";
8f4595eb 1009 else if (strstr(reason, "error") != NULL)
d8076497 1010 {
1011 message = unknown;
1012
1013 snprintf(unknown, sizeof(unknown), "Unknown printer error (%s)!",
8f4595eb 1014 reason);
d8076497 1015 }
d1c2727f 1016
1017 if (message)
1018 {
d8076497 1019 count ++;
d1c2727f 1020 if (strstr(reasons->values[i].string.text, "error"))
1021 fprintf(stderr, "ERROR: %s\n", message);
1022 else if (strstr(reasons->values[i].string.text, "warning"))
1023 fprintf(stderr, "WARNING: %s\n", message);
1024 else
1025 fprintf(stderr, "INFO: %s\n", message);
1026 }
1027 }
1028
8f4595eb 1029 fprintf(stderr, "%s\n", state);
1030
d8076497 1031 return (count);
d1c2727f 1032}
1033
1034
a684a3b0 1035#ifdef __APPLE__
1036/*
1037 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1038 * remotely.
1039 *
1040 * This step is required because the PICT format is not documented and
1041 * subject to change, so developing a filter for other OS's is infeasible.
1042 * Also, fonts required by the PICT file need to be embedded on the
1043 * client side (which has the fonts), so we run the filter to get a
1044 * PostScript file for printing...
1045 */
1046
1047int /* O - Exit status of filter */
1048run_pictwps_filter(char **argv, /* I - Command-line arguments */
1049 char *filename, /* I - Filename buffer */
1050 int length) /* I - Size of filename buffer */
1051{
1052 struct stat fileinfo; /* Print file information */
1053 const char *ppdfile; /* PPD file for destination printer */
1054 int pid; /* Child process ID */
1055 int fd; /* Temporary file descriptor */
1056 int status; /* Exit status of filter */
1057 const char *printer; /* PRINTER env var */
1058 static char ppdenv[1024]; /* PPD environment variable */
1059
1060
1061 /*
1062 * First get the PPD file for the printer...
1063 */
1064
1065 printer = getenv("PRINTER");
1066 if (!printer)
1067 {
1068 fputs("ERROR: PRINTER environment variable not defined!\n", stderr);
1069 return (-1);
1070 }
1071
1072 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1073 {
1074 fprintf(stderr, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
1075 printer, ippErrorString(cupsLastError()));
1c85e5c0 1076 /*return (-1);*/
1077 }
1078 else
1079 {
1080 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1081 putenv(ppdenv);
a684a3b0 1082 }
a684a3b0 1083
1084 /*
1085 * Then create a temporary file for printing...
1086 */
1087
1088 if ((fd = cupsTempFd(filename, length)) < 0)
1089 {
1090 fprintf(stderr, "ERROR: Unable to create temporary file - %s.\n",
1c85e5c0 1091 strerror(errno));
1092 if (ppdfile)
1093 unlink(ppdfile);
a684a3b0 1094 return (-1);
1095 }
1096
1097 /*
1098 * Get the owner of the spool file - it is owned by the user we want to run
1099 * as...
1100 */
1101
1102 stat(argv[6], &fileinfo);
1103
1c85e5c0 1104 if (ppdfile)
1105 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1106
1107 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1108
a684a3b0 1109 /*
1110 * Finally, run the filter to convert the file...
1111 */
1112
1113 if ((pid = fork()) == 0)
1114 {
1115 /*
1116 * Child process for pictwpstops... Redirect output of pictwpstops to a
1117 * file...
1118 */
1119
1120 close(1);
1121 dup(fd);
1122 close(fd);
1123
1124 if (!getuid())
1125 {
1126 /*
1127 * Change to an unpriviledged user...
1128 */
1129
1130 setgid(fileinfo.st_gid);
1131 setuid(fileinfo.st_uid);
1132 }
1133
1134 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1135 argv[6], NULL);
1136 perror("ERROR: Unable to exec pictwpstops");
1137 return (errno);
1138 }
1c85e5c0 1139
1140 close(fd);
1141
1142 if (pid < 0)
a684a3b0 1143 {
1144 /*
1145 * Error!
1146 */
1147
1148 perror("ERROR: Unable to fork pictwpstops");
a684a3b0 1149 unlink(filename);
1c85e5c0 1150 if (ppdfile)
1151 unlink(ppdfile);
a684a3b0 1152 return (-1);
1153 }
1154
1155 /*
1156 * Now wait for the filter to complete...
1157 */
1158
1159 if (wait(&status) < 0)
1160 {
1161 perror("ERROR: Unable to wait for pictwpstops");
1162 close(fd);
1163 unlink(filename);
1c85e5c0 1164 if (ppdfile)
1165 unlink(ppdfile);
a684a3b0 1166 return (-1);
1167 }
1168
1c85e5c0 1169 if (ppdfile)
1170 unlink(ppdfile);
1171
a684a3b0 1172 close(fd);
1173
1174 if (status)
1175 {
1176 if (status >= 256)
1177 fprintf(stderr, "ERROR: pictwpstops exited with status %d!\n",
1178 status / 256);
1179 else
1180 fprintf(stderr, "ERROR: pictwpstops exited on signal %d!\n",
1181 status);
1182
1183 unlink(filename);
1184 return (status);
1185 }
1186
1187 /*
1188 * Return with no errors..
1189 */
1190
1191 return (0);
1192}
1193#endif /* __APPLE__ */
1194
d1c2727f 1195
1196/*
8f4595eb 1197 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1198 */
1199
1200static void
1201sigterm_handler(int sig) /* I - Signal */
1202{
1203 (void)sig; /* remove compiler warnings... */
1204
1205 /*
1206 * Remove the temporary file if necessary...
1207 */
1208
1209 if (tmpfilename[0])
1210 unlink(tmpfilename);
1211
1212 exit(1);
1213}
1214
1215
1216/*
d8e1f369 1217 * End of "$Id: ipp.c,v 1.38.2.27 2003/04/16 19:36:31 mike Exp $".
c8f9565c 1218 */