]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Mirror lpstat change from 1.1.
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
839c43aa 2 * "$Id: ipp.c,v 1.38.2.3 2002/01/02 18:04:17 mike Exp $"
c8f9565c 3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
839c43aa 6 * Copyright 1997-2002 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 *
24 * Contents:
25 *
b5cb0608 26 * main() - Send a file to the printer or server.
27 * password_cb() - Disable the password prompt for cupsDoFileRequest().
c8f9565c 28 */
29
30/*
31 * Include necessary headers.
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <errno.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <cups/cups.h>
40#include <cups/language.h>
41#include <cups/string.h>
4ff40357 42#include <signal.h>
c8f9565c 43
44
b5cb0608 45/*
46 * Local functions...
47 */
48
49const char *password_cb(const char *);
50
51
52/*
53 * Local globals...
54 */
55
56char *password = NULL;
57
58
c8f9565c 59/*
60 * 'main()' - Send a file to the printer or server.
61 *
62 * Usage:
63 *
64 * printer-uri job-id user title copies options [file]
65 */
66
67int /* O - Exit status */
68main(int argc, /* I - Number of command-line arguments (6 or 7) */
69 char *argv[]) /* I - Command-line arguments */
70{
25512894 71 int i; /* Looping var */
c8f9565c 72 int num_options; /* Number of printer options */
3079988e 73 cups_option_t *options; /* Printer options */
c8f9565c 74 char method[255], /* Method in URI */
75 hostname[1024], /* Hostname */
76 username[255], /* Username info */
77 resource[1024], /* Resource info (printer name) */
78 filename[1024]; /* File to print */
79 int port; /* Port number (not used) */
b5cb0608 80 char uri[HTTP_MAX_URI];/* Updated URI without user/pass */
97fcaf92 81 ipp_status_t ipp_status; /* Status of IPP request */
c8f9565c 82 http_t *http; /* HTTP connection */
83 ipp_t *request, /* IPP request */
2922de55 84 *response, /* IPP response */
85 *supported; /* get-printer-attributes response */
b5cb0608 86 ipp_attribute_t *job_id_attr; /* job-id attribute */
87 int job_id; /* job-id value */
88 ipp_attribute_t *job_state; /* job-state attribute */
97fcaf92 89 ipp_attribute_t *copies_sup; /* copies-supported attribute */
25512894 90 ipp_attribute_t *charset_sup; /* charset-supported attribute */
36aa99bb 91 ipp_attribute_t *format_sup; /* document-format-supported attribute */
25512894 92 const char *charset; /* Character set to use */
c8f9565c 93 cups_lang_t *language; /* Default language */
97fcaf92 94 int copies; /* Number of copies remaining */
0a3944d6 95 const char *content_type; /* CONTENT_TYPE environment variable */
4ff40357 96#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
97 struct sigaction action; /* Actions for POSIX signals */
98#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
5a2c7855 99 int version; /* IPP version */
c8f9565c 100
4b23f3b3 101
102 /*
103 * Make sure status messages are not buffered...
104 */
105
2922de55 106 setbuf(stderr, NULL);
c8f9565c 107
4b23f3b3 108 /*
109 * Check command-line...
110 */
111
68edc300 112 if (argc == 1)
113 {
183914a3 114 char *s;
115
d4c438d4 116 if ((s = strrchr(argv[0], '/')) != NULL)
117 s ++;
118 else
119 s = argv[0];
120
121 printf("network %s \"Unknown\" \"Internet Printing Protocol\"\n", s);
68edc300 122 return (0);
123 }
124 else if (argc < 6 || argc > 7)
c8f9565c 125 {
126 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
127 argv[0]);
128 return (1);
129 }
130
131 /*
132 * If we have 7 arguments, print the file named on the command-line.
97fcaf92 133 * Otherwise, copy stdin to a temporary file and print the temporary
134 * file.
c8f9565c 135 */
136
137 if (argc == 6)
97fcaf92 138 {
139 /*
140 * Copy stdin to a temporary file...
141 */
142
1b5bf964 143 int fd; /* Temporary file */
97fcaf92 144 char buffer[8192]; /* Buffer for copying */
145 int bytes; /* Number of bytes read */
146
147
1b5bf964 148 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
97fcaf92 149 {
150 perror("ERROR: unable to create temporary file");
151 return (1);
152 }
153
154 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
1b5bf964 155 if (write(fd, buffer, bytes) < bytes)
97fcaf92 156 {
157 perror("ERROR: unable to write to temporary file");
1b5bf964 158 close(fd);
97fcaf92 159 unlink(filename);
160 return (1);
161 }
162
1b5bf964 163 close(fd);
97fcaf92 164 }
165 else
166 {
167 strncpy(filename, argv[6], sizeof(filename) - 1);
168 filename[sizeof(filename) - 1] = '\0';
169 }
170
171 /*
b5cb0608 172 * Extract the hostname and printer name from the URI...
97fcaf92 173 */
174
b5cb0608 175 httpSeparate(argv[0], method, username, hostname, &port, resource);
c8f9565c 176
177 /*
b5cb0608 178 * Set the authentication info, if any...
c8f9565c 179 */
180
b5cb0608 181 cupsSetPasswordCB(password_cb);
182
183 if (username[0])
184 {
185 if ((password = strchr(username, ':')) != NULL)
186 *password++ = '\0';
187
188 cupsSetUser(username);
189 }
c8f9565c 190
191 /*
192 * Try connecting to the remote server...
193 */
194
97fcaf92 195 do
c8f9565c 196 {
97fcaf92 197 fprintf(stderr, "INFO: Connecting to %s...\n", hostname);
c8f9565c 198
97fcaf92 199 if ((http = httpConnect(hostname, port)) == NULL)
d21a7597 200 {
97fcaf92 201 if (errno == ECONNREFUSED)
202 {
203 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
204 hostname);
205 sleep(30);
206 }
207 else
208 {
209 perror("ERROR: Unable to connect to IPP host");
2cc18dd2 210 sleep(30);
97fcaf92 211 }
d21a7597 212 }
c8f9565c 213 }
97fcaf92 214 while (http == NULL);
c8f9565c 215
216 /*
217 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 218 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
219 * might contain username:password information...
c8f9565c 220 */
221
3f9cb6c6 222 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
c8f9565c 223
224 /*
97fcaf92 225 * First validate the destination and see if the device supports multiple
226 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
227 * don't support the copies attribute...
c8f9565c 228 */
229
25512894 230 language = cupsLangDefault();
231 charset_sup = NULL;
232 copies_sup = NULL;
2922de55 233 format_sup = NULL;
25512894 234 version = 1;
2922de55 235 supported = NULL;
c8f9565c 236
97fcaf92 237 do
c8f9565c 238 {
239 /*
97fcaf92 240 * Build the IPP request...
c8f9565c 241 */
242
97fcaf92 243 request = ippNew();
5a2c7855 244 request->request.op.version[1] = version;
97fcaf92 245 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
246 request->request.op.request_id = 1;
c8f9565c 247
97fcaf92 248 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
67436e05 249 "attributes-charset", NULL, "utf-8");
97fcaf92 250
251 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
252 "attributes-natural-language", NULL,
67436e05 253 language != NULL ? language->language : "en");
c8f9565c 254
97fcaf92 255 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
256 NULL, uri);
c8f9565c 257
97fcaf92 258 /*
259 * Do the request...
260 */
c8f9565c 261
b5cb0608 262 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
263 ipp_status = cupsLastError();
264 else
265 ipp_status = supported->request.status.status_code;
266
267 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 268 {
b5cb0608 269 if (supported)
270 ippDelete(supported);
97fcaf92 271
b5cb0608 272 if (ipp_status == IPP_PRINTER_BUSY ||
273 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 274 {
b5cb0608 275 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
97fcaf92 276 sleep(10);
97fcaf92 277 }
b5cb0608 278 else if ((ipp_status == IPP_BAD_REQUEST ||
279 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
c8f9565c 280 {
b5cb0608 281 /*
282 * Switch to IPP/1.0...
283 */
97fcaf92 284
b5cb0608 285 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr);
286 version = 0;
c8f9565c 287 }
97fcaf92 288 else
b5cb0608 289 fprintf(stderr, "ERROR: Printer will not accept print file (%s)!\n",
290 ippErrorString(ipp_status));
97fcaf92 291 }
b5cb0608 292 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
293 IPP_TAG_RANGE)) != NULL)
97fcaf92 294 {
b5cb0608 295 /*
296 * Has the "copies-supported" attribute - does it have an upper
297 * bound > 1?
298 */
97fcaf92 299
b5cb0608 300 if (copies_sup->values[0].range.upper <= 1)
301 copies_sup = NULL; /* No */
302 }
97fcaf92 303
b5cb0608 304 charset_sup = ippFindAttribute(supported, "charset-supported",
305 IPP_TAG_CHARSET);
306 format_sup = ippFindAttribute(supported, "document-format-supported",
307 IPP_TAG_MIMETYPE);
308
309 if (format_sup)
310 {
311 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
312 format_sup->num_values);
313 for (i = 0; i < format_sup->num_values; i ++)
314 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
315 format_sup->values[i].string.text);
c8f9565c 316 }
c8f9565c 317 }
97fcaf92 318 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 319
4ff40357 320 /*
321 * Now that we are "connected" to the port, ignore SIGTERM so that we
322 * can finish out any page data the driver sends (e.g. to eject the
323 * current page...
324 */
325
326#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
327 sigset(SIGTERM, SIG_IGN);
328#elif defined(HAVE_SIGACTION)
329 memset(&action, 0, sizeof(action));
330
331 sigemptyset(&action.sa_mask);
332 action.sa_handler = SIG_IGN;
333 sigaction(SIGTERM, &action, NULL);
334#else
335 signal(SIGTERM, SIG_IGN);
336#endif /* HAVE_SIGSET */
337
c8f9565c 338 /*
97fcaf92 339 * See if the printer supports multiple copies...
c8f9565c 340 */
341
97fcaf92 342 if (copies_sup)
343 copies = 1;
0c5b0932 344 else
97fcaf92 345 copies = atoi(argv[4]);
0c5b0932 346
25512894 347 /*
348 * Figure out the character set to use...
349 */
350
351 charset = language ? cupsLangEncoding(language) : "us-ascii";
352
353 if (charset_sup)
354 {
355 /*
356 * See if IPP server supports the requested character set...
357 */
358
359 for (i = 0; i < charset_sup->num_values; i ++)
360 if (strcasecmp(charset, charset_sup->values[i].string.text) == 0)
361 break;
362
363 /*
364 * If not, choose us-ascii or utf-8...
365 */
366
367 if (i >= charset_sup->num_values)
368 {
369 /*
370 * See if us-ascii is supported...
371 */
372
373 for (i = 0; i < charset_sup->num_values; i ++)
374 if (strcasecmp("us-ascii", charset_sup->values[i].string.text) == 0)
375 break;
376
377 if (i < charset_sup->num_values)
378 charset = "us-ascii";
379 else
380 charset = "utf-8";
381 }
382 }
383
c8f9565c 384 /*
97fcaf92 385 * Then issue the print-job request...
c8f9565c 386 */
387
97fcaf92 388 while (copies > 0)
c8f9565c 389 {
390 /*
97fcaf92 391 * Build the IPP request...
c8f9565c 392 */
393
97fcaf92 394 request = ippNew();
5a2c7855 395 request->request.op.version[1] = version;
97fcaf92 396 request->request.op.operation_id = IPP_PRINT_JOB;
397 request->request.op.request_id = 1;
c8f9565c 398
97fcaf92 399 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
25512894 400 "attributes-charset", NULL, charset);
8ce7c000 401
97fcaf92 402 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
403 "attributes-natural-language", NULL,
67436e05 404 language != NULL ? language->language : "en");
c8f9565c 405
97fcaf92 406 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
407 NULL, uri);
c8f9565c 408
97fcaf92 409 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
410
753453e4 411 if (argv[2][0])
412 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
413 NULL, argv[2]);
97fcaf92 414
415 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
416
753453e4 417 if (argv[3][0])
418 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
419 argv[3]);
c8f9565c 420
97fcaf92 421 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 422
c8f9565c 423 /*
97fcaf92 424 * Handle options on the command-line...
c8f9565c 425 */
426
97fcaf92 427 options = NULL;
428 num_options = cupsParseOptions(argv[5], 0, &options);
429
753453e4 430 if (argc > 6)
431 content_type = getenv("CONTENT_TYPE");
432 else
433 content_type = "application/vnd.cups-raw";
434
435 if (content_type != NULL && format_sup != NULL)
36aa99bb 436 {
437 for (i = 0; i < format_sup->num_values; i ++)
438 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
439 break;
440
441 if (i < format_sup->num_values)
442 num_options = cupsAddOption("document-format", content_type,
443 num_options, &options);
444 }
97fcaf92 445
753453e4 446 if (copies_sup)
447 {
448 /*
449 * Only send options if the destination printer supports the copies
450 * attribute. This is a hack for the HP JetDirect implementation of
451 * IPP, which does not accept extension attributes and incorrectly
452 * reports a client-error-bad-request error instead of the
453 * successful-ok-unsupported-attributes status. In short, at least
454 * some HP implementations of IPP are non-compliant.
455 */
456
457 cupsEncodeOptions(request, num_options, options);
458 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
459 atoi(argv[4]));
460 }
461
183914a3 462 cupsFreeOptions(num_options, options);
c8f9565c 463
168c50b6 464
cb555bcf 465 /*
b5cb0608 466 * Do the request...
cb555bcf 467 */
468
b5cb0608 469 if ((response = cupsDoFileRequest(http, request, resource, filename)) == NULL)
470 ipp_status = cupsLastError();
471 else
472 ipp_status = response->request.status.status_code;
473
474 if (ipp_status > IPP_OK_CONFLICT)
475 {
753453e4 476 job_id = 0;
477
b5cb0608 478 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
479 ipp_status == IPP_PRINTER_BUSY)
480 {
481 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
482 sleep(10);
483 }
484 else
485 fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
486 ippErrorString(ipp_status));
487 }
488 else if ((job_id_attr = ippFindAttribute(response, "job-id",
489 IPP_TAG_INTEGER)) == NULL)
97fcaf92 490 {
b5cb0608 491 fputs("INFO: Print file accepted - job ID unknown.\n", stderr);
492 job_id = 0;
493 }
494 else
495 {
496 job_id = job_id_attr->values[0].integer;
497 fprintf(stderr, "INFO: Print file accepted - job ID %d.\n", job_id);
97fcaf92 498 }
cb555bcf 499
b5cb0608 500 if (response)
501 ippDelete(response);
502
753453e4 503 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 504 {
505 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
506 copies --;
507 }
508 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
509 ipp_status != IPP_PRINTER_BUSY)
510 break;
8ce7c000 511
c8f9565c 512 /*
b5cb0608 513 * Wait for the job to complete...
c8f9565c 514 */
515
b5cb0608 516 if (!job_id)
517 continue;
518
519 fputs("INFO: Waiting for job to complete...\n", stderr);
520
97fcaf92 521 for (;;)
c8f9565c 522 {
97fcaf92 523 /*
b5cb0608 524 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 525 */
526
b5cb0608 527 request = ippNew();
528 request->request.op.version[1] = version;
529 request->request.op.operation_id = IPP_GET_JOB_ATTRIBUTES;
530 request->request.op.request_id = 1;
97fcaf92 531
b5cb0608 532 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
533 "attributes-charset", NULL, charset);
3f9cb6c6 534
b5cb0608 535 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
536 "attributes-natural-language", NULL,
537 language != NULL ? language->language : "en");
97fcaf92 538
b5cb0608 539 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
540 NULL, uri);
3f9cb6c6 541
b5cb0608 542 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
543 job_id);
c8f9565c 544
b5cb0608 545 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
546 "requested-attributes", NULL, "job-state");
97fcaf92 547
548 /*
b5cb0608 549 * Do the request...
97fcaf92 550 */
551
b5cb0608 552 if ((response = cupsDoRequest(http, request, resource)) == NULL)
553 ipp_status = cupsLastError();
554 else
555 ipp_status = response->request.status.status_code;
97fcaf92 556
b5cb0608 557 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 558 {
b5cb0608 559 /*
560 * Job has gone away and the server has no job history...
561 */
97fcaf92 562
b5cb0608 563 ippDelete(response);
564 break;
97fcaf92 565 }
566
b5cb0608 567 if (ipp_status > IPP_OK_CONFLICT)
6a536282 568 {
b5cb0608 569 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
570 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 571 {
b5cb0608 572 if (response)
573 ippDelete(response);
574
575 fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
576 job_id, ippErrorString(ipp_status));
577 break;
97fcaf92 578 }
6a536282 579 }
b5cb0608 580 else if ((job_state = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
97fcaf92 581 {
b5cb0608 582 /*
753453e4 583 * Stop polling if the job is finished or pending-held...
b5cb0608 584 */
97fcaf92 585
753453e4 586 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
587 job_state->values[0].integer == IPP_JOB_HELD)
97fcaf92 588 {
b5cb0608 589 ippDelete(response);
590 break;
97fcaf92 591 }
97fcaf92 592 }
593
b5cb0608 594 /*
595 * Wait 10 seconds before polling again...
596 */
c8f9565c 597
b5cb0608 598 if (response)
599 ippDelete(response);
97fcaf92 600
b5cb0608 601 sleep(10);
97fcaf92 602 }
c8f9565c 603 }
604
605 /*
606 * Free memory...
607 */
608
609 httpClose(http);
c8f9565c 610
2922de55 611 if (supported)
612 ippDelete(supported);
613
c8f9565c 614 /*
97fcaf92 615 * Close and remove the temporary file if necessary...
c8f9565c 616 */
617
97fcaf92 618 if (argc < 7)
619 unlink(filename);
c8f9565c 620
621 /*
622 * Return the queue status...
623 */
624
b5cb0608 625 if (ipp_status <= IPP_OK_CONFLICT)
753453e4 626 fputs("INFO: Ready to print.\n", stderr);
b5cb0608 627
628 return (ipp_status > IPP_OK_CONFLICT);
629}
630
631
632/*
633 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
634 */
635
636const char * /* O - Password */
637password_cb(const char *prompt) /* I - Prompt (not used) */
638{
639 (void)prompt;
640
641 return (password);
c8f9565c 642}
643
644
645/*
839c43aa 646 * End of "$Id: ipp.c,v 1.38.2.3 2002/01/02 18:04:17 mike Exp $".
c8f9565c 647 */