]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Copyright update...
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
efb2f309 2 * "$Id: ipp.c,v 1.54 2002/01/02 17:58:34 mike Exp $"
c8f9565c 3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
efb2f309 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 *
d5c0dc3e 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
d5c0dc3e 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) */
d5c0dc3e 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 */
d5c0dc3e 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 /*
d5c0dc3e 172 * Extract the hostname and printer name from the URI...
97fcaf92 173 */
174
d5c0dc3e 175 httpSeparate(argv[0], method, username, hostname, &port, resource);
c8f9565c 176
177 /*
d5c0dc3e 178 * Set the authentication info, if any...
c8f9565c 179 */
180
d5c0dc3e 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
d5c0dc3e 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 {
d5c0dc3e 269 if (supported)
270 ippDelete(supported);
97fcaf92 271
d5c0dc3e 272 if (ipp_status == IPP_PRINTER_BUSY ||
273 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 274 {
d5c0dc3e 275 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
97fcaf92 276 sleep(10);
97fcaf92 277 }
d5c0dc3e 278 else if ((ipp_status == IPP_BAD_REQUEST ||
279 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
c8f9565c 280 {
d5c0dc3e 281 /*
282 * Switch to IPP/1.0...
283 */
97fcaf92 284
d5c0dc3e 285 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr);
286 version = 0;
c8f9565c 287 }
97fcaf92 288 else
d5c0dc3e 289 fprintf(stderr, "ERROR: Printer will not accept print file (%s)!\n",
290 ippErrorString(ipp_status));
97fcaf92 291 }
d5c0dc3e 292 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
293 IPP_TAG_RANGE)) != NULL)
97fcaf92 294 {
d5c0dc3e 295 /*
296 * Has the "copies-supported" attribute - does it have an upper
297 * bound > 1?
298 */
97fcaf92 299
d5c0dc3e 300 if (copies_sup->values[0].range.upper <= 1)
301 copies_sup = NULL; /* No */
302 }
97fcaf92 303
d5c0dc3e 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
62f24dbf 323 * current page... Only ignore SIGTERM if we are printing data from
324 * stdin (otherwise you can't cancel raw jobs...)
4ff40357 325 */
326
62f24dbf 327 if (argc < 7)
328 {
4ff40357 329#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
62f24dbf 330 sigset(SIGTERM, SIG_IGN);
4ff40357 331#elif defined(HAVE_SIGACTION)
62f24dbf 332 memset(&action, 0, sizeof(action));
4ff40357 333
62f24dbf 334 sigemptyset(&action.sa_mask);
335 action.sa_handler = SIG_IGN;
336 sigaction(SIGTERM, &action, NULL);
4ff40357 337#else
62f24dbf 338 signal(SIGTERM, SIG_IGN);
4ff40357 339#endif /* HAVE_SIGSET */
62f24dbf 340 }
4ff40357 341
c8f9565c 342 /*
97fcaf92 343 * See if the printer supports multiple copies...
c8f9565c 344 */
345
2c7475b0 346 if (copies_sup || argc < 7)
97fcaf92 347 copies = 1;
0c5b0932 348 else
97fcaf92 349 copies = atoi(argv[4]);
0c5b0932 350
25512894 351 /*
352 * Figure out the character set to use...
353 */
354
355 charset = language ? cupsLangEncoding(language) : "us-ascii";
356
357 if (charset_sup)
358 {
359 /*
360 * See if IPP server supports the requested character set...
361 */
362
363 for (i = 0; i < charset_sup->num_values; i ++)
364 if (strcasecmp(charset, charset_sup->values[i].string.text) == 0)
365 break;
366
367 /*
368 * If not, choose us-ascii or utf-8...
369 */
370
371 if (i >= charset_sup->num_values)
372 {
373 /*
374 * See if us-ascii is supported...
375 */
376
377 for (i = 0; i < charset_sup->num_values; i ++)
378 if (strcasecmp("us-ascii", charset_sup->values[i].string.text) == 0)
379 break;
380
381 if (i < charset_sup->num_values)
382 charset = "us-ascii";
383 else
384 charset = "utf-8";
385 }
386 }
387
c8f9565c 388 /*
97fcaf92 389 * Then issue the print-job request...
c8f9565c 390 */
391
97fcaf92 392 while (copies > 0)
c8f9565c 393 {
394 /*
97fcaf92 395 * Build the IPP request...
c8f9565c 396 */
397
97fcaf92 398 request = ippNew();
5a2c7855 399 request->request.op.version[1] = version;
97fcaf92 400 request->request.op.operation_id = IPP_PRINT_JOB;
401 request->request.op.request_id = 1;
c8f9565c 402
97fcaf92 403 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
25512894 404 "attributes-charset", NULL, charset);
8ce7c000 405
97fcaf92 406 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
407 "attributes-natural-language", NULL,
67436e05 408 language != NULL ? language->language : "en");
c8f9565c 409
97fcaf92 410 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
411 NULL, uri);
c8f9565c 412
97fcaf92 413 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
414
32c8c89f 415 if (argv[2][0])
416 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
417 NULL, argv[2]);
97fcaf92 418
419 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
420
32c8c89f 421 if (argv[3][0])
422 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
423 argv[3]);
c8f9565c 424
97fcaf92 425 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 426
c8f9565c 427 /*
97fcaf92 428 * Handle options on the command-line...
c8f9565c 429 */
430
97fcaf92 431 options = NULL;
432 num_options = cupsParseOptions(argv[5], 0, &options);
433
47ad9e1d 434 if (argc > 6)
435 content_type = getenv("CONTENT_TYPE");
436 else
96f778ef 437 content_type = "application/vnd.cups-raw";
47ad9e1d 438
439 if (content_type != NULL && format_sup != NULL)
36aa99bb 440 {
441 for (i = 0; i < format_sup->num_values; i ++)
442 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
443 break;
444
445 if (i < format_sup->num_values)
446 num_options = cupsAddOption("document-format", content_type,
447 num_options, &options);
448 }
97fcaf92 449
4ce7a5bf 450 if (copies_sup)
451 {
452 /*
453 * Only send options if the destination printer supports the copies
454 * attribute. This is a hack for the HP JetDirect implementation of
455 * IPP, which does not accept extension attributes and incorrectly
456 * reports a client-error-bad-request error instead of the
457 * successful-ok-unsupported-attributes status. In short, at least
458 * some HP implementations of IPP are non-compliant.
459 */
460
461 cupsEncodeOptions(request, num_options, options);
462 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
463 atoi(argv[4]));
464 }
465
183914a3 466 cupsFreeOptions(num_options, options);
c8f9565c 467
168c50b6 468
cb555bcf 469 /*
d5c0dc3e 470 * Do the request...
cb555bcf 471 */
472
d5c0dc3e 473 if ((response = cupsDoFileRequest(http, request, resource, filename)) == NULL)
474 ipp_status = cupsLastError();
475 else
476 ipp_status = response->request.status.status_code;
477
478 if (ipp_status > IPP_OK_CONFLICT)
479 {
af3c0b53 480 job_id = 0;
481
d5c0dc3e 482 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
483 ipp_status == IPP_PRINTER_BUSY)
484 {
485 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
486 sleep(10);
487 }
488 else
489 fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
490 ippErrorString(ipp_status));
491 }
492 else if ((job_id_attr = ippFindAttribute(response, "job-id",
493 IPP_TAG_INTEGER)) == NULL)
97fcaf92 494 {
d5c0dc3e 495 fputs("INFO: Print file accepted - job ID unknown.\n", stderr);
496 job_id = 0;
497 }
498 else
499 {
500 job_id = job_id_attr->values[0].integer;
501 fprintf(stderr, "INFO: Print file accepted - job ID %d.\n", job_id);
97fcaf92 502 }
cb555bcf 503
d5c0dc3e 504 if (response)
505 ippDelete(response);
506
6df8e5ef 507 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
d5c0dc3e 508 {
509 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
510 copies --;
511 }
512 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
513 ipp_status != IPP_PRINTER_BUSY)
514 break;
8ce7c000 515
c8f9565c 516 /*
d5c0dc3e 517 * Wait for the job to complete...
c8f9565c 518 */
519
d5c0dc3e 520 if (!job_id)
521 continue;
522
523 fputs("INFO: Waiting for job to complete...\n", stderr);
524
97fcaf92 525 for (;;)
c8f9565c 526 {
97fcaf92 527 /*
d5c0dc3e 528 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 529 */
530
d5c0dc3e 531 request = ippNew();
532 request->request.op.version[1] = version;
533 request->request.op.operation_id = IPP_GET_JOB_ATTRIBUTES;
534 request->request.op.request_id = 1;
97fcaf92 535
d5c0dc3e 536 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
537 "attributes-charset", NULL, charset);
3f9cb6c6 538
d5c0dc3e 539 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
540 "attributes-natural-language", NULL,
541 language != NULL ? language->language : "en");
97fcaf92 542
d5c0dc3e 543 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
544 NULL, uri);
3f9cb6c6 545
d5c0dc3e 546 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
547 job_id);
c8f9565c 548
d5c0dc3e 549 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
550 "requested-attributes", NULL, "job-state");
97fcaf92 551
552 /*
d5c0dc3e 553 * Do the request...
97fcaf92 554 */
555
d5c0dc3e 556 if ((response = cupsDoRequest(http, request, resource)) == NULL)
557 ipp_status = cupsLastError();
558 else
559 ipp_status = response->request.status.status_code;
97fcaf92 560
4f2b7367 561 if (ipp_status == IPP_NOT_FOUND)
562 {
563 /*
cc6c9645 564 * Job has gone away and/or the server has no job history...
4f2b7367 565 */
566
567 ippDelete(response);
cc6c9645 568
569 ipp_status = IPP_OK;
4f2b7367 570 break;
571 }
572
d5c0dc3e 573 if (ipp_status > IPP_OK_CONFLICT)
97fcaf92 574 {
d5c0dc3e 575 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
576 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 577 {
d5c0dc3e 578 if (response)
579 ippDelete(response);
580
581 fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
582 job_id, ippErrorString(ipp_status));
97fcaf92 583 break;
584 }
585 }
d5c0dc3e 586 else if ((job_state = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
6a536282 587 {
d5c0dc3e 588 /*
92f20b12 589 * Stop polling if the job is finished or pending-held...
d5c0dc3e 590 */
97fcaf92 591
92f20b12 592 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
593 job_state->values[0].integer == IPP_JOB_HELD)
97fcaf92 594 {
d5c0dc3e 595 ippDelete(response);
596 break;
97fcaf92 597 }
6a536282 598 }
97fcaf92 599
d5c0dc3e 600 /*
601 * Wait 10 seconds before polling again...
602 */
97fcaf92 603
d5c0dc3e 604 if (response)
605 ippDelete(response);
97fcaf92 606
d5c0dc3e 607 sleep(10);
c8f9565c 608 }
c8f9565c 609 }
610
611 /*
612 * Free memory...
613 */
614
615 httpClose(http);
c8f9565c 616
2922de55 617 if (supported)
618 ippDelete(supported);
619
c8f9565c 620 /*
97fcaf92 621 * Close and remove the temporary file if necessary...
c8f9565c 622 */
623
97fcaf92 624 if (argc < 7)
625 unlink(filename);
c8f9565c 626
627 /*
628 * Return the queue status...
629 */
630
74405f61 631 if (ipp_status <= IPP_OK_CONFLICT)
632 fputs("INFO: Ready to print.\n", stderr);
633
d5c0dc3e 634 return (ipp_status > IPP_OK_CONFLICT);
635}
636
637
638/*
639 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
640 */
641
642const char * /* O - Password */
643password_cb(const char *prompt) /* I - Prompt (not used) */
644{
645 (void)prompt;
646
647 return (password);
c8f9565c 648}
649
650
651/*
efb2f309 652 * End of "$Id: ipp.c,v 1.54 2002/01/02 17:58:34 mike Exp $".
c8f9565c 653 */