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