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