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