]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Didn't disable browsing if browse socket could not be created.
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
3079988e 2 * "$Id: ipp.c,v 1.26 2000/05/11 15:16:23 mike Exp $"
c8f9565c 3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
71fe22b7 6 * Copyright 1997-2000 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 *
6d0582d2 26 * main() - Send a file to the printer or server.
c8f9565c 27 */
28
29/*
30 * Include necessary headers.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <errno.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <cups/cups.h>
39#include <cups/language.h>
40#include <cups/string.h>
4ff40357 41#include <signal.h>
c8f9565c 42
43
44/*
45 * 'main()' - Send a file to the printer or server.
46 *
47 * Usage:
48 *
49 * printer-uri job-id user title copies options [file]
50 */
51
52int /* O - Exit status */
53main(int argc, /* I - Number of command-line arguments (6 or 7) */
54 char *argv[]) /* I - Command-line arguments */
55{
56 int i; /* Looping var */
57 int n, n2; /* Attribute values */
58 char *option, /* Name of option */
c8f9565c 59 *s; /* Pointer into option value */
3079988e 60 const char *val; /* Pointer to option value */
c8f9565c 61 int num_options; /* Number of printer options */
3079988e 62 cups_option_t *options; /* Printer options */
c8f9565c 63 char method[255], /* Method in URI */
64 hostname[1024], /* Hostname */
65 username[255], /* Username info */
66 resource[1024], /* Resource info (printer name) */
67 filename[1024]; /* File to print */
68 int port; /* Port number (not used) */
69 char password[255], /* Password info */
70 uri[HTTP_MAX_URI];/* Updated URI without user/pass */
71 http_status_t status; /* Status of HTTP job */
97fcaf92 72 ipp_status_t ipp_status; /* Status of IPP request */
c8f9565c 73 FILE *fp; /* File to print */
74 http_t *http; /* HTTP connection */
75 ipp_t *request, /* IPP request */
76 *response; /* IPP response */
77 ipp_attribute_t *job_id; /* job-id attribute */
97fcaf92 78 ipp_attribute_t *copies_sup; /* copies-supported attribute */
c8f9565c 79 cups_lang_t *language; /* Default language */
80 struct stat fileinfo; /* File statistics */
81 size_t nbytes, /* Number of bytes written */
82 tbytes; /* Total bytes written */
83 char buffer[8192]; /* Output buffer */
97fcaf92 84 int copies; /* Number of copies remaining */
0a3944d6 85 const char *content_type; /* CONTENT_TYPE environment variable */
4ff40357 86#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
87 struct sigaction action; /* Actions for POSIX signals */
88#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
c8f9565c 89
90
68edc300 91 if (argc == 1)
92 {
d4c438d4 93 if ((s = strrchr(argv[0], '/')) != NULL)
94 s ++;
95 else
96 s = argv[0];
97
98 printf("network %s \"Unknown\" \"Internet Printing Protocol\"\n", s);
68edc300 99 return (0);
100 }
101 else if (argc < 6 || argc > 7)
c8f9565c 102 {
103 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
104 argv[0]);
105 return (1);
106 }
107
108 /*
109 * If we have 7 arguments, print the file named on the command-line.
97fcaf92 110 * Otherwise, copy stdin to a temporary file and print the temporary
111 * file.
c8f9565c 112 */
113
114 if (argc == 6)
97fcaf92 115 {
116 /*
117 * Copy stdin to a temporary file...
118 */
119
120 FILE *fp; /* Temporary file */
121 char buffer[8192]; /* Buffer for copying */
122 int bytes; /* Number of bytes read */
123
124
125 if ((fp = fopen(cupsTempFile(filename, sizeof(filename)), "w")) == NULL)
126 {
127 perror("ERROR: unable to create temporary file");
128 return (1);
129 }
130
131 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
132 if (fwrite(buffer, 1, bytes, fp) < bytes)
133 {
134 perror("ERROR: unable to write to temporary file");
135 fclose(fp);
136 unlink(filename);
137 return (1);
138 }
139
140 fclose(fp);
141 }
142 else
143 {
144 strncpy(filename, argv[6], sizeof(filename) - 1);
145 filename[sizeof(filename) - 1] = '\0';
146 }
147
148 /*
149 * Open the print file...
150 */
151
152 if ((fp = fopen(filename, "rb")) == NULL)
c8f9565c 153 {
154 perror("ERROR: Unable to open print file");
155 return (1);
156 }
157 else
97fcaf92 158 stat(filename, &fileinfo);
c8f9565c 159
160 /*
161 * Extract the hostname and printer name from the URI...
162 */
163
164 httpSeparate(argv[0], method, username, hostname, &port, resource);
165
166 /*
167 * Try connecting to the remote server...
168 */
169
97fcaf92 170 do
c8f9565c 171 {
97fcaf92 172 fprintf(stderr, "INFO: Connecting to %s...\n", hostname);
c8f9565c 173
97fcaf92 174 if ((http = httpConnect(hostname, port)) == NULL)
d21a7597 175 {
97fcaf92 176 if (errno == ECONNREFUSED)
177 {
178 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
179 hostname);
180 sleep(30);
181 }
182 else
183 {
184 perror("ERROR: Unable to connect to IPP host");
2cc18dd2 185 sleep(30);
97fcaf92 186 }
d21a7597 187 }
c8f9565c 188 }
97fcaf92 189 while (http == NULL);
c8f9565c 190
191 /*
192 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 193 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
194 * might contain username:password information...
c8f9565c 195 */
196
3f9cb6c6 197 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
c8f9565c 198
199 /*
97fcaf92 200 * First validate the destination and see if the device supports multiple
201 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
202 * don't support the copies attribute...
c8f9565c 203 */
204
97fcaf92 205 language = cupsLangDefault();
206 copies_sup = NULL;
c8f9565c 207
97fcaf92 208 do
c8f9565c 209 {
210 /*
97fcaf92 211 * Build the IPP request...
c8f9565c 212 */
213
97fcaf92 214 request = ippNew();
215 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
216 request->request.op.request_id = 1;
c8f9565c 217
97fcaf92 218 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
219 "attributes-charset", NULL, cupsLangEncoding(language));
220
221 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
222 "attributes-natural-language", NULL,
223 language != NULL ? language->language : "C");
c8f9565c 224
97fcaf92 225 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
226 NULL, uri);
c8f9565c 227
97fcaf92 228 /*
229 * Now fill in the HTTP request stuff...
230 */
c8f9565c 231
97fcaf92 232 httpClearFields(http);
233 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
234 if (username[0])
c8f9565c 235 {
97fcaf92 236 httpEncode64(password, username);
237 httpSetField(http, HTTP_FIELD_AUTHORIZATION, password);
238 }
c8f9565c 239
97fcaf92 240 sprintf(buffer, "%u", ippLength(request));
241 httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, buffer);
c8f9565c 242
97fcaf92 243 /*
244 * Do the request...
245 */
c8f9565c 246
97fcaf92 247 for (;;)
c8f9565c 248 {
97fcaf92 249 /*
250 * POST the request, retrying as needed...
251 */
252
253 if (httpPost(http, resource))
c8f9565c 254 {
97fcaf92 255 fputs("INFO: Unable to POST get-printer-attributes request; retrying...\n", stderr);
256 sleep(10);
257 httpReconnect(http);
258 continue;
c8f9565c 259 }
c8f9565c 260
97fcaf92 261 fputs("INFO: POST successful, sending IPP request...\n", stderr);
c8f9565c 262
c8f9565c 263 /*
97fcaf92 264 * Send the IPP request...
c8f9565c 265 */
97fcaf92 266
267 request->state = IPP_IDLE;
268
269 if (ippWrite(http, request) == IPP_ERROR)
270 {
271 fputs("ERROR: Unable to send IPP request!\n", stderr);
272 status = HTTP_ERROR;
273 break;
274 }
275
276 fputs("INFO: IPP request sent, getting status...\n", stderr);
277
c8f9565c 278 /*
97fcaf92 279 * Finally, check the status from the HTTP server...
c8f9565c 280 */
281
97fcaf92 282 while ((status = httpUpdate(http)) == HTTP_CONTINUE);
283
284 if (status == HTTP_OK)
c8f9565c 285 {
97fcaf92 286 response = ippNew();
287 ippRead(http, response);
288
289 ipp_status = response->request.status.status_code;
d21a7597 290
97fcaf92 291 if (ipp_status > IPP_OK_CONFLICT)
292 {
293 if (ipp_status == IPP_PRINTER_BUSY ||
294 ipp_status == IPP_SERVICE_UNAVAILABLE)
295 {
296 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
297 sleep(10);
298 }
299 else
300 {
301 fprintf(stderr, "ERROR: Printer will not accept print file (%x)!\n",
302 ipp_status);
303 status = HTTP_ERROR;
304 }
305 }
306 else if ((copies_sup = ippFindAttribute(response, "copies-supported",
307 IPP_TAG_RANGE)) != NULL)
308 {
309 /*
310 * Has the "copies-supported" attribute - does it have an upper
311 * bound > 1?
312 */
313
314 if (copies_sup->values[0].range.upper <= 1)
315 copies_sup = NULL; /* No */
316 }
317
318 ippDelete(response);
c8f9565c 319 }
97fcaf92 320 else
c8f9565c 321 {
d21a7597 322 response = NULL;
323
97fcaf92 324 if (status == HTTP_ERROR)
325 {
326 fprintf(stderr, "WARNING: Did not receive the IPP response (%d)\n",
327 errno);
328 status = HTTP_OK;
329 ipp_status = IPP_PRINTER_BUSY;
330 }
331 else
d21a7597 332 {
97fcaf92 333 fprintf(stderr, "ERROR: Validate request was not accepted (%d)!\n", status);
d21a7597 334 ipp_status = IPP_FORBIDDEN;
335 }
c8f9565c 336 }
97fcaf92 337
338 httpFlush(http);
339
340 break;
341 }
342
343 if (status != HTTP_OK)
344 {
345 if (fp != stdin)
346 fclose(fp);
347
348 httpClose(http);
349
350 return (1);
c8f9565c 351 }
c8f9565c 352 }
97fcaf92 353 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 354
4ff40357 355 /*
356 * Now that we are "connected" to the port, ignore SIGTERM so that we
357 * can finish out any page data the driver sends (e.g. to eject the
358 * current page...
359 */
360
361#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
362 sigset(SIGTERM, SIG_IGN);
363#elif defined(HAVE_SIGACTION)
364 memset(&action, 0, sizeof(action));
365
366 sigemptyset(&action.sa_mask);
367 action.sa_handler = SIG_IGN;
368 sigaction(SIGTERM, &action, NULL);
369#else
370 signal(SIGTERM, SIG_IGN);
371#endif /* HAVE_SIGSET */
372
c8f9565c 373 /*
97fcaf92 374 * See if the printer supports multiple copies...
c8f9565c 375 */
376
97fcaf92 377 if (copies_sup)
378 copies = 1;
0c5b0932 379 else
97fcaf92 380 copies = atoi(argv[4]);
0c5b0932 381
c8f9565c 382 /*
97fcaf92 383 * Then issue the print-job request...
c8f9565c 384 */
385
97fcaf92 386 while (copies > 0)
c8f9565c 387 {
388 /*
97fcaf92 389 * Build the IPP request...
c8f9565c 390 */
391
97fcaf92 392 request = ippNew();
393 request->request.op.operation_id = IPP_PRINT_JOB;
394 request->request.op.request_id = 1;
c8f9565c 395
97fcaf92 396 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
397 "attributes-charset", NULL, cupsLangEncoding(language));
8ce7c000 398
97fcaf92 399 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
400 "attributes-natural-language", NULL,
401 language != NULL ? language->language : "C");
c8f9565c 402
97fcaf92 403 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
404 NULL, uri);
c8f9565c 405
97fcaf92 406 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
407
408 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
409 NULL, argv[2]);
410
411 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
412
413 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
414 argv[3]);
c8f9565c 415
97fcaf92 416 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 417
c8f9565c 418 /*
97fcaf92 419 * Handle options on the command-line...
c8f9565c 420 */
421
97fcaf92 422 options = NULL;
423 num_options = cupsParseOptions(argv[5], 0, &options);
424
0a3944d6 425 if (cupsGetOption("raw", num_options, options) ||
426 ((content_type = getenv("CONTENT_TYPE")) != NULL &&
427 strcasecmp(content_type, "application/vnd.cups-raw") == 0))
97fcaf92 428 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
429 NULL, "application/vnd.cups-raw");
3079988e 430 else if ((val = cupsGetOption("document-format", num_options, options)) != NULL)
866ee248 431 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
3079988e 432 NULL, val);
97fcaf92 433 else
434 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
435 NULL, "application/octet-stream");
436
437 if (copies_sup)
438 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", atoi(argv[4]));
439
440 for (i = 0; i < num_options; i ++)
c8f9565c 441 {
97fcaf92 442 /*
443 * Skip the "raw" option - handled above...
444 */
445
866ee248 446 if (strcasecmp(options[i].name, "raw") == 0 ||
447 strcasecmp(options[i].name, "document-format") == 0)
97fcaf92 448 continue;
449
450 /*
451 * See what the option value is; for compatibility with older interface
452 * scripts, we have to support single-argument options as well as
453 * option=value, option=low-high, and option=MxN.
454 */
455
456 option = options[i].name;
457 val = options[i].value;
c8f9565c 458
97fcaf92 459 if (*val == '\0')
460 val = NULL;
461
462 if (val != NULL)
c8f9565c 463 {
97fcaf92 464 if (strcasecmp(val, "true") == 0 ||
465 strcasecmp(val, "on") == 0 ||
466 strcasecmp(val, "yes") == 0)
467 {
468 /*
469 * Boolean value - true...
470 */
471
472 n = 1;
473 val = "";
474 }
475 else if (strcasecmp(val, "false") == 0 ||
476 strcasecmp(val, "off") == 0 ||
477 strcasecmp(val, "no") == 0)
478 {
479 /*
480 * Boolean value - false...
481 */
482
483 n = 0;
484 val = "";
485 }
486
487 n = strtol(val, &s, 0);
488 }
489 else
490 {
491 if (strncasecmp(option, "no", 2) == 0)
492 {
493 option += 2;
494 n = 0;
495 }
496 else
497 n = 1;
498
499 s = "";
c8f9565c 500 }
97fcaf92 501
502 if (*s != '\0' && *s != '-' && (*s != 'x' || s == val))
503 /*
504 * String value(s)...
505 */
506 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val);
507 else if (val != NULL)
508 {
509 /*
510 * Numeric value, range, or resolution...
511 */
512
513 if (*s == '-')
514 {
515 n2 = strtol(s + 1, NULL, 0);
516 ippAddRange(request, IPP_TAG_JOB, option, n, n2);
517 }
518 else if (*s == 'x')
519 {
520 n2 = strtol(s + 1, &s, 0);
521
522 if (strcasecmp(s, "dpc") == 0)
523 ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_CM, n, n2);
524 else if (strcasecmp(s, "dpi") == 0)
525 ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_INCH, n, n2);
526 else
527 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val);
528 }
529 else
530 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, option, n);
531 }
532 else
533 /*
534 * Boolean value...
535 */
536 ippAddBoolean(request, IPP_TAG_JOB, option, (char)n);
c8f9565c 537 }
538
cb555bcf 539 /*
97fcaf92 540 * Now fill in the HTTP request stuff...
cb555bcf 541 */
542
97fcaf92 543 httpClearFields(http);
544 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
545 if (username[0])
546 {
547 httpEncode64(password, username);
548 httpSetField(http, HTTP_FIELD_AUTHORIZATION, password);
549 }
cb555bcf 550
97fcaf92 551 sprintf(buffer, "%u", ippLength(request) + (size_t)fileinfo.st_size);
552 httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, buffer);
8ce7c000 553
c8f9565c 554 /*
97fcaf92 555 * Do the request...
c8f9565c 556 */
557
97fcaf92 558 for (;;)
c8f9565c 559 {
97fcaf92 560 /*
561 * POST the request, retrying as needed...
562 */
563
564 httpReconnect(http);
c8f9565c 565
97fcaf92 566 if (httpPost(http, resource))
3f9cb6c6 567 {
97fcaf92 568 fputs("INFO: Unable to POST print request; retrying...\n", stderr);
569 sleep(10);
570 continue;
571 }
572
573 fputs("INFO: POST successful, sending IPP request...\n", stderr);
3f9cb6c6 574
97fcaf92 575 /*
576 * Send the IPP request...
577 */
578
579 request->state = IPP_IDLE;
3f9cb6c6 580
97fcaf92 581 if (ippWrite(http, request) == IPP_ERROR)
582 {
583 fputs("ERROR: Unable to send IPP request!\n", stderr);
584 status = HTTP_ERROR;
585 break;
3f9cb6c6 586 }
c8f9565c 587
97fcaf92 588 fputs("INFO: IPP request sent, sending print file...\n", stderr);
589
590 /*
591 * Then send the file...
592 */
593
594 rewind(fp);
595
596 tbytes = 0;
597 while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
598 {
599 tbytes += nbytes;
600 fprintf(stderr, "INFO: Sending print file, %uk...\n", tbytes / 1024);
601
602 if (httpWrite(http, buffer, nbytes) < nbytes)
603 {
604 perror("ERROR: Unable to send print file to printer");
605 status = HTTP_ERROR;
606 break;
607 }
608 }
609
610 fputs("INFO: Print file sent; checking status...\n", stderr);
611
612 /*
613 * Finally, check the status from the HTTP server...
614 */
615
616 while ((status = httpUpdate(http)) == HTTP_CONTINUE);
617
618 if (status == HTTP_OK)
6a536282 619 {
97fcaf92 620 response = ippNew();
621 ippRead(http, response);
622
623 if ((ipp_status = response->request.status.status_code) > IPP_OK_CONFLICT)
624 {
625 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
626 ipp_status == IPP_PRINTER_BUSY)
627 {
628 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
629 sleep(10);
630 }
631 else
632 fprintf(stderr, "ERROR: Print file was not accepted (%04x)!\n",
633 response->request.status.status_code);
634 }
635 else if ((job_id = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
636 fputs("INFO: Print file accepted - job ID unknown.\n", stderr);
637 else
638 fprintf(stderr, "INFO: Print file accepted - job ID %d.\n",
639 job_id->values[0].integer);
6a536282 640 }
641 else
97fcaf92 642 {
643 response = NULL;
644 ipp_status = IPP_PRINTER_BUSY;
645
646 if (status == HTTP_ERROR)
647 {
648 fprintf(stderr, "WARNING: Did not receive the IPP response (%d)\n",
649 errno);
650 status = HTTP_OK;
651 }
652 else
653 fprintf(stderr, "ERROR: Print request was not accepted (%d)!\n", status);
654 }
655
656 httpFlush(http);
657
658 break;
c8f9565c 659 }
660
97fcaf92 661 if (request != NULL)
662 ippDelete(request);
663 if (response != NULL)
664 ippDelete(response);
665
666 if (ipp_status <= IPP_OK_CONFLICT)
667 {
668 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
669 copies --;
670 }
48419d23 671 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
672 ipp_status != IPP_PRINTER_BUSY)
673 break;
c8f9565c 674 }
675
676 /*
677 * Free memory...
678 */
679
680 httpClose(http);
c8f9565c 681
682 /*
97fcaf92 683 * Close and remove the temporary file if necessary...
c8f9565c 684 */
685
97fcaf92 686 fclose(fp);
3f9cb6c6 687
97fcaf92 688 if (argc < 7)
689 unlink(filename);
c8f9565c 690
691 /*
692 * Return the queue status...
693 */
694
695 return (status != HTTP_OK);
696}
697
698
699/*
3079988e 700 * End of "$Id: ipp.c,v 1.26 2000/05/11 15:16:23 mike Exp $".
c8f9565c 701 */