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