]> 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/*
1c85e5c0 2 * "$Id: ipp.c,v 1.38.2.22 2003/01/31 21:24:22 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 }
97fcaf92 405 else
d1c2727f 406 fprintf(stderr, "ERROR: Unable to get printer status (%s)!\n",
b5cb0608 407 ippErrorString(ipp_status));
d1c2727f 408
409 if (supported)
410 ippDelete(supported);
411
412 continue;
97fcaf92 413 }
b5cb0608 414 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
415 IPP_TAG_RANGE)) != NULL)
97fcaf92 416 {
b5cb0608 417 /*
418 * Has the "copies-supported" attribute - does it have an upper
419 * bound > 1?
420 */
97fcaf92 421
b5cb0608 422 if (copies_sup->values[0].range.upper <= 1)
423 copies_sup = NULL; /* No */
424 }
97fcaf92 425
b5cb0608 426 charset_sup = ippFindAttribute(supported, "charset-supported",
427 IPP_TAG_CHARSET);
428 format_sup = ippFindAttribute(supported, "document-format-supported",
429 IPP_TAG_MIMETYPE);
430
431 if (format_sup)
432 {
433 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
434 format_sup->num_values);
435 for (i = 0; i < format_sup->num_values; i ++)
436 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
437 format_sup->values[i].string.text);
c8f9565c 438 }
d1c2727f 439
440 report_printer_state(supported);
c8f9565c 441 }
97fcaf92 442 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 443
997cf8b0 444 /*
445 * See if the printer is accepting jobs and is not stopped; if either
446 * condition is true and we are printing to a class, requeue the job...
447 */
448
449 if (getenv("CLASS") != NULL)
450 {
451 printer_state = ippFindAttribute(supported, "printer-state",
452 IPP_TAG_ENUM);
453 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
454 IPP_TAG_BOOLEAN);
455
456 if (printer_state == NULL ||
457 printer_state->values[0].integer > IPP_PRINTER_PROCESSING ||
458 printer_accepting == NULL ||
459 !printer_accepting->values[0].boolean)
460 {
461 /*
462 * If the CLASS environment variable is set, the job was submitted
463 * to a class and not to a specific queue. In this case, we want
464 * to abort immediately so that the job can be requeued on the next
465 * available printer in the class.
466 */
467
468 fprintf(stderr, "INFO: Unable to queue job on %s, queuing on next printer in class...\n",
469 hostname);
470
471 ippDelete(supported);
472 httpClose(http);
473
a684a3b0 474 if (argc == 6 || strcmp(filename, argv[6]))
997cf8b0 475 unlink(filename);
476
477 /*
478 * Sleep 5 seconds to keep the job from requeuing too rapidly...
479 */
480
481 sleep(5);
482
483 return (1);
484 }
485 }
486
4ff40357 487 /*
488 * Now that we are "connected" to the port, ignore SIGTERM so that we
489 * can finish out any page data the driver sends (e.g. to eject the
d1c2727f 490 * current page... Only ignore SIGTERM if we are printing data from
491 * stdin (otherwise you can't cancel raw jobs...)
4ff40357 492 */
493
d1c2727f 494 if (argc < 7)
495 {
4ff40357 496#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
d1c2727f 497 sigset(SIGTERM, SIG_IGN);
4ff40357 498#elif defined(HAVE_SIGACTION)
d1c2727f 499 memset(&action, 0, sizeof(action));
4ff40357 500
d1c2727f 501 sigemptyset(&action.sa_mask);
502 action.sa_handler = SIG_IGN;
503 sigaction(SIGTERM, &action, NULL);
4ff40357 504#else
d1c2727f 505 signal(SIGTERM, SIG_IGN);
4ff40357 506#endif /* HAVE_SIGSET */
d1c2727f 507 }
4ff40357 508
c8f9565c 509 /*
97fcaf92 510 * See if the printer supports multiple copies...
c8f9565c 511 */
512
d1c2727f 513 if (copies_sup || argc < 7)
97fcaf92 514 copies = 1;
0c5b0932 515 else
97fcaf92 516 copies = atoi(argv[4]);
0c5b0932 517
25512894 518 /*
519 * Figure out the character set to use...
520 */
521
522 charset = language ? cupsLangEncoding(language) : "us-ascii";
523
524 if (charset_sup)
525 {
526 /*
527 * See if IPP server supports the requested character set...
528 */
529
530 for (i = 0; i < charset_sup->num_values; i ++)
531 if (strcasecmp(charset, charset_sup->values[i].string.text) == 0)
532 break;
533
534 /*
535 * If not, choose us-ascii or utf-8...
536 */
537
538 if (i >= charset_sup->num_values)
539 {
540 /*
541 * See if us-ascii is supported...
542 */
543
544 for (i = 0; i < charset_sup->num_values; i ++)
545 if (strcasecmp("us-ascii", charset_sup->values[i].string.text) == 0)
546 break;
547
548 if (i < charset_sup->num_values)
549 charset = "us-ascii";
550 else
551 charset = "utf-8";
552 }
553 }
554
c8f9565c 555 /*
97fcaf92 556 * Then issue the print-job request...
c8f9565c 557 */
558
d1c2727f 559 reasons = 0;
560
97fcaf92 561 while (copies > 0)
c8f9565c 562 {
563 /*
97fcaf92 564 * Build the IPP request...
c8f9565c 565 */
566
97fcaf92 567 request = ippNew();
0a3ac972 568 request->request.op.version[1] = version;
569 request->request.op.operation_id = IPP_PRINT_JOB;
570 request->request.op.request_id = 1;
c8f9565c 571
97fcaf92 572 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
25512894 573 "attributes-charset", NULL, charset);
8ce7c000 574
97fcaf92 575 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
576 "attributes-natural-language", NULL,
67436e05 577 language != NULL ? language->language : "en");
c8f9565c 578
97fcaf92 579 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
580 NULL, uri);
c8f9565c 581
97fcaf92 582 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
583
753453e4 584 if (argv[2][0])
585 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
586 NULL, argv[2]);
97fcaf92 587
588 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
589
753453e4 590 if (argv[3][0])
591 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
592 argv[3]);
c8f9565c 593
97fcaf92 594 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 595
c8f9565c 596 /*
97fcaf92 597 * Handle options on the command-line...
c8f9565c 598 */
599
97fcaf92 600 options = NULL;
601 num_options = cupsParseOptions(argv[5], 0, &options);
602
753453e4 603 if (content_type != NULL && format_sup != NULL)
36aa99bb 604 {
605 for (i = 0; i < format_sup->num_values; i ++)
606 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
607 break;
608
609 if (i < format_sup->num_values)
610 num_options = cupsAddOption("document-format", content_type,
611 num_options, &options);
612 }
97fcaf92 613
753453e4 614 if (copies_sup)
615 {
616 /*
617 * Only send options if the destination printer supports the copies
618 * attribute. This is a hack for the HP JetDirect implementation of
619 * IPP, which does not accept extension attributes and incorrectly
620 * reports a client-error-bad-request error instead of the
621 * successful-ok-unsupported-attributes status. In short, at least
622 * some HP implementations of IPP are non-compliant.
623 */
624
625 cupsEncodeOptions(request, num_options, options);
626 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
627 atoi(argv[4]));
628 }
629
183914a3 630 cupsFreeOptions(num_options, options);
c8f9565c 631
a4e23897 632 /*
633 * If copies aren't supported, then we are likely dealing with an HP
634 * JetDirect. The HP IPP implementation seems to close the connection
635 * after every request (that is, it does *not* implement HTTP Keep-
636 * Alive, which is REQUIRED by HTTP/1.1...
637 */
638
639 if (!copies_sup)
640 httpReconnect(http);
641
cb555bcf 642 /*
b5cb0608 643 * Do the request...
cb555bcf 644 */
645
b5cb0608 646 if ((response = cupsDoFileRequest(http, request, resource, filename)) == NULL)
647 ipp_status = cupsLastError();
648 else
0a3ac972 649 ipp_status = response->request.status.status_code;
b5cb0608 650
651 if (ipp_status > IPP_OK_CONFLICT)
652 {
753453e4 653 job_id = 0;
654
b5cb0608 655 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
656 ipp_status == IPP_PRINTER_BUSY)
657 {
658 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
659 sleep(10);
660 }
661 else
662 fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
663 ippErrorString(ipp_status));
664 }
665 else if ((job_id_attr = ippFindAttribute(response, "job-id",
666 IPP_TAG_INTEGER)) == NULL)
97fcaf92 667 {
b5cb0608 668 fputs("INFO: Print file accepted - job ID unknown.\n", stderr);
669 job_id = 0;
670 }
671 else
672 {
673 job_id = job_id_attr->values[0].integer;
674 fprintf(stderr, "INFO: Print file accepted - job ID %d.\n", job_id);
97fcaf92 675 }
cb555bcf 676
b5cb0608 677 if (response)
678 ippDelete(response);
679
753453e4 680 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 681 {
682 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
683 copies --;
684 }
685 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
686 ipp_status != IPP_PRINTER_BUSY)
687 break;
8ce7c000 688
c8f9565c 689 /*
b5cb0608 690 * Wait for the job to complete...
c8f9565c 691 */
692
b5cb0608 693 if (!job_id)
694 continue;
695
696 fputs("INFO: Waiting for job to complete...\n", stderr);
697
97fcaf92 698 for (;;)
c8f9565c 699 {
97fcaf92 700 /*
b5cb0608 701 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 702 */
703
b5cb0608 704 request = ippNew();
0a3ac972 705 request->request.op.version[1] = version;
706 request->request.op.operation_id = IPP_GET_JOB_ATTRIBUTES;
707 request->request.op.request_id = 1;
97fcaf92 708
b5cb0608 709 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
710 "attributes-charset", NULL, charset);
3f9cb6c6 711
b5cb0608 712 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
713 "attributes-natural-language", NULL,
714 language != NULL ? language->language : "en");
97fcaf92 715
b5cb0608 716 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
717 NULL, uri);
3f9cb6c6 718
b5cb0608 719 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
720 job_id);
c8f9565c 721
ee8b7dd3 722 if (argv[2][0])
723 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
724 NULL, argv[2]);
725
b5cb0608 726 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
727 "requested-attributes", NULL, "job-state");
97fcaf92 728
729 /*
b5cb0608 730 * Do the request...
97fcaf92 731 */
732
a4e23897 733 if (!copies_sup)
734 httpReconnect(http);
735
b5cb0608 736 if ((response = cupsDoRequest(http, request, resource)) == NULL)
737 ipp_status = cupsLastError();
738 else
0a3ac972 739 ipp_status = response->request.status.status_code;
97fcaf92 740
b5cb0608 741 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 742 {
b5cb0608 743 /*
d1c2727f 744 * Job has gone away and/or the server has no job history...
b5cb0608 745 */
97fcaf92 746
b5cb0608 747 ippDelete(response);
d1c2727f 748
749 ipp_status = IPP_OK;
b5cb0608 750 break;
97fcaf92 751 }
752
b5cb0608 753 if (ipp_status > IPP_OK_CONFLICT)
6a536282 754 {
b5cb0608 755 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
756 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 757 {
b5cb0608 758 if (response)
759 ippDelete(response);
760
761 fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
762 job_id, ippErrorString(ipp_status));
763 break;
97fcaf92 764 }
6a536282 765 }
b5cb0608 766 else if ((job_state = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
97fcaf92 767 {
b5cb0608 768 /*
753453e4 769 * Stop polling if the job is finished or pending-held...
b5cb0608 770 */
97fcaf92 771
753453e4 772 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
773 job_state->values[0].integer == IPP_JOB_HELD)
97fcaf92 774 {
b5cb0608 775 ippDelete(response);
776 break;
97fcaf92 777 }
97fcaf92 778 }
779
d1c2727f 780 if (response)
781 ippDelete(response);
782
b5cb0608 783 /*
d1c2727f 784 * Now check on the printer state...
b5cb0608 785 */
c8f9565c 786
d1c2727f 787 request = ippNew();
0a3ac972 788 request->request.op.version[1] = version;
789 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
790 request->request.op.request_id = 1;
d1c2727f 791
792 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
793 "attributes-charset", NULL, charset);
794
795 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
796 "attributes-natural-language", NULL,
797 language != NULL ? language->language : "en");
798
799 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
800 NULL, uri);
801
ee8b7dd3 802 if (argv[2][0])
803 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
804 NULL, argv[2]);
805
d1c2727f 806 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
ee8b7dd3 807 "requested-attributes", NULL, "printer-state-reasons");
d1c2727f 808
809 /*
810 * Do the request...
811 */
812
a4e23897 813 if (!copies_sup)
814 httpReconnect(http);
815
d1c2727f 816 if ((response = cupsDoRequest(http, request, resource)) != NULL)
817 {
818 reasons = report_printer_state(response);
b5cb0608 819 ippDelete(response);
d1c2727f 820 }
821
822 /*
823 * Wait 10 seconds before polling again...
824 */
97fcaf92 825
b5cb0608 826 sleep(10);
97fcaf92 827 }
c8f9565c 828 }
829
830 /*
831 * Free memory...
832 */
833
834 httpClose(http);
c8f9565c 835
2922de55 836 if (supported)
837 ippDelete(supported);
838
c8f9565c 839 /*
97fcaf92 840 * Close and remove the temporary file if necessary...
c8f9565c 841 */
842
a684a3b0 843 if (argc == 6 || strcmp(filename, argv[6]))
97fcaf92 844 unlink(filename);
c8f9565c 845
846 /*
847 * Return the queue status...
848 */
849
d1c2727f 850 if (ipp_status <= IPP_OK_CONFLICT && reasons == 0)
753453e4 851 fputs("INFO: Ready to print.\n", stderr);
b5cb0608 852
853 return (ipp_status > IPP_OK_CONFLICT);
854}
855
856
857/*
858 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
859 */
860
861const char * /* O - Password */
862password_cb(const char *prompt) /* I - Prompt (not used) */
863{
864 (void)prompt;
865
866 return (password);
c8f9565c 867}
868
869
870/*
d1c2727f 871 * 'report_printer_state()' - Report the printer state.
872 */
873
d8076497 874int /* O - Number of reasons shown */
d1c2727f 875report_printer_state(ipp_t *ipp) /* I - IPP response */
876{
877 int i; /* Looping var */
d8076497 878 int count; /* Count of reasons shown... */
d1c2727f 879 ipp_attribute_t *reasons; /* printer-state-reasons */
880 const char *message; /* Message to show */
d8076497 881 char unknown[1024]; /* Unknown message string */
d1c2727f 882
883
884 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
885 IPP_TAG_KEYWORD)) == NULL)
886 return (0);
887
d8076497 888 for (i = 0, count = 0; i < reasons->num_values; i ++)
d1c2727f 889 {
890 message = NULL;
891
892 if (strncmp(reasons->values[i].string.text, "media-needed", 12) == 0)
893 message = "Media tray needs to be filled.";
894 else if (strncmp(reasons->values[i].string.text, "media-jam", 9) == 0)
895 message = "Media jam!";
896 else if (strncmp(reasons->values[i].string.text, "moving-to-paused", 16) == 0 ||
897 strncmp(reasons->values[i].string.text, "paused", 6) == 0 ||
898 strncmp(reasons->values[i].string.text, "shutdown", 8) == 0)
899 message = "Printer off-line.";
900 else if (strncmp(reasons->values[i].string.text, "toner-low", 9) == 0)
901 message = "Toner low.";
902 else if (strncmp(reasons->values[i].string.text, "toner-empty", 11) == 0)
903 message = "Out of toner!";
904 else if (strncmp(reasons->values[i].string.text, "cover-open", 10) == 0)
905 message = "Cover open.";
906 else if (strncmp(reasons->values[i].string.text, "interlock-open", 14) == 0)
907 message = "Interlock open.";
908 else if (strncmp(reasons->values[i].string.text, "door-open", 9) == 0)
909 message = "Door open.";
910 else if (strncmp(reasons->values[i].string.text, "input-tray-missing", 18) == 0)
911 message = "Media tray missing!";
912 else if (strncmp(reasons->values[i].string.text, "media-low", 9) == 0)
913 message = "Media tray almost empty.";
914 else if (strncmp(reasons->values[i].string.text, "media-empty", 11) == 0)
915 message = "Media tray empty!";
916 else if (strncmp(reasons->values[i].string.text, "output-tray-missing", 19) == 0)
917 message = "Output tray missing!";
918 else if (strncmp(reasons->values[i].string.text, "output-area-almost-full", 23) == 0)
919 message = "Output bin almost full.";
920 else if (strncmp(reasons->values[i].string.text, "output-area-full", 16) == 0)
921 message = "Output bin full!";
922 else if (strncmp(reasons->values[i].string.text, "marker-supply-low", 17) == 0)
923 message = "Ink/toner almost empty.";
924 else if (strncmp(reasons->values[i].string.text, "marker-supply-empty", 19) == 0)
925 message = "Ink/toner empty!";
926 else if (strncmp(reasons->values[i].string.text, "marker-waste-almost-full", 24) == 0)
927 message = "Ink/toner waste bin almost full.";
928 else if (strncmp(reasons->values[i].string.text, "marker-waste-full", 17) == 0)
929 message = "Ink/toner waste bin full!";
930 else if (strncmp(reasons->values[i].string.text, "fuser-over-temp", 15) == 0)
931 message = "Fuser temperature high!";
932 else if (strncmp(reasons->values[i].string.text, "fuser-under-temp", 16) == 0)
933 message = "Fuser temperature low!";
934 else if (strncmp(reasons->values[i].string.text, "opc-near-eol", 12) == 0)
935 message = "OPC almost at end-of-life.";
936 else if (strncmp(reasons->values[i].string.text, "opc-life-over", 13) == 0)
937 message = "OPC at end-of-life!";
938 else if (strncmp(reasons->values[i].string.text, "developer-low", 13) == 0)
939 message = "Developer almost empty.";
940 else if (strncmp(reasons->values[i].string.text, "developer-empty", 15) == 0)
941 message = "Developer empty!";
d8076497 942 else if (strstr(reasons->values[i].string.text, "error") != NULL)
943 {
944 message = unknown;
945
946 snprintf(unknown, sizeof(unknown), "Unknown printer error (%s)!",
947 reasons->values[i].string.text);
948 }
d1c2727f 949
950 if (message)
951 {
d8076497 952 count ++;
d1c2727f 953 if (strstr(reasons->values[i].string.text, "error"))
954 fprintf(stderr, "ERROR: %s\n", message);
955 else if (strstr(reasons->values[i].string.text, "warning"))
956 fprintf(stderr, "WARNING: %s\n", message);
957 else
958 fprintf(stderr, "INFO: %s\n", message);
959 }
960 }
961
d8076497 962 return (count);
d1c2727f 963}
964
965
a684a3b0 966#ifdef __APPLE__
967/*
968 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
969 * remotely.
970 *
971 * This step is required because the PICT format is not documented and
972 * subject to change, so developing a filter for other OS's is infeasible.
973 * Also, fonts required by the PICT file need to be embedded on the
974 * client side (which has the fonts), so we run the filter to get a
975 * PostScript file for printing...
976 */
977
978int /* O - Exit status of filter */
979run_pictwps_filter(char **argv, /* I - Command-line arguments */
980 char *filename, /* I - Filename buffer */
981 int length) /* I - Size of filename buffer */
982{
983 struct stat fileinfo; /* Print file information */
984 const char *ppdfile; /* PPD file for destination printer */
985 int pid; /* Child process ID */
986 int fd; /* Temporary file descriptor */
987 int status; /* Exit status of filter */
988 const char *printer; /* PRINTER env var */
989 static char ppdenv[1024]; /* PPD environment variable */
990
991
992 /*
993 * First get the PPD file for the printer...
994 */
995
996 printer = getenv("PRINTER");
997 if (!printer)
998 {
999 fputs("ERROR: PRINTER environment variable not defined!\n", stderr);
1000 return (-1);
1001 }
1002
1003 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1004 {
1005 fprintf(stderr, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
1006 printer, ippErrorString(cupsLastError()));
1c85e5c0 1007 /*return (-1);*/
1008 }
1009 else
1010 {
1011 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1012 putenv(ppdenv);
a684a3b0 1013 }
a684a3b0 1014
1015 /*
1016 * Then create a temporary file for printing...
1017 */
1018
1019 if ((fd = cupsTempFd(filename, length)) < 0)
1020 {
1021 fprintf(stderr, "ERROR: Unable to create temporary file - %s.\n",
1c85e5c0 1022 strerror(errno));
1023 if (ppdfile)
1024 unlink(ppdfile);
a684a3b0 1025 return (-1);
1026 }
1027
1028 /*
1029 * Get the owner of the spool file - it is owned by the user we want to run
1030 * as...
1031 */
1032
1033 stat(argv[6], &fileinfo);
1034
1c85e5c0 1035 if (ppdfile)
1036 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1037
1038 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1039
a684a3b0 1040 /*
1041 * Finally, run the filter to convert the file...
1042 */
1043
1044 if ((pid = fork()) == 0)
1045 {
1046 /*
1047 * Child process for pictwpstops... Redirect output of pictwpstops to a
1048 * file...
1049 */
1050
1051 close(1);
1052 dup(fd);
1053 close(fd);
1054
1055 if (!getuid())
1056 {
1057 /*
1058 * Change to an unpriviledged user...
1059 */
1060
1061 setgid(fileinfo.st_gid);
1062 setuid(fileinfo.st_uid);
1063 }
1064
1065 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1066 argv[6], NULL);
1067 perror("ERROR: Unable to exec pictwpstops");
1068 return (errno);
1069 }
1c85e5c0 1070
1071 close(fd);
1072
1073 if (pid < 0)
a684a3b0 1074 {
1075 /*
1076 * Error!
1077 */
1078
1079 perror("ERROR: Unable to fork pictwpstops");
a684a3b0 1080 unlink(filename);
1c85e5c0 1081 if (ppdfile)
1082 unlink(ppdfile);
a684a3b0 1083 return (-1);
1084 }
1085
1086 /*
1087 * Now wait for the filter to complete...
1088 */
1089
1090 if (wait(&status) < 0)
1091 {
1092 perror("ERROR: Unable to wait for pictwpstops");
1093 close(fd);
1094 unlink(filename);
1c85e5c0 1095 if (ppdfile)
1096 unlink(ppdfile);
a684a3b0 1097 return (-1);
1098 }
1099
1c85e5c0 1100 if (ppdfile)
1101 unlink(ppdfile);
1102
a684a3b0 1103 close(fd);
1104
1105 if (status)
1106 {
1107 if (status >= 256)
1108 fprintf(stderr, "ERROR: pictwpstops exited with status %d!\n",
1109 status / 256);
1110 else
1111 fprintf(stderr, "ERROR: pictwpstops exited on signal %d!\n",
1112 status);
1113
1114 unlink(filename);
1115 return (status);
1116 }
1117
1118 /*
1119 * Return with no errors..
1120 */
1121
1122 return (0);
1123}
1124#endif /* __APPLE__ */
1125
d1c2727f 1126
1127/*
1c85e5c0 1128 * End of "$Id: ipp.c,v 1.38.2.22 2003/01/31 21:24:22 mike Exp $".
c8f9565c 1129 */