]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ipp.c
Copyright update...
[thirdparty/cups.git] / backend / ipp.c
1 /*
2 * "$Id: ipp.c,v 1.54 2002/01/02 17:58:34 mike Exp $"
3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2002 by Easy Software Products, all rights reserved.
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
17 * 44141 Airport View Drive, Suite 204
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 *
26 * main() - Send a file to the printer or server.
27 * password_cb() - Disable the password prompt for cupsDoFileRequest().
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>
42 #include <signal.h>
43
44
45 /*
46 * Local functions...
47 */
48
49 const char *password_cb(const char *);
50
51
52 /*
53 * Local globals...
54 */
55
56 char *password = NULL;
57
58
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
67 int /* O - Exit status */
68 main(int argc, /* I - Number of command-line arguments (6 or 7) */
69 char *argv[]) /* I - Command-line arguments */
70 {
71 int i; /* Looping var */
72 int num_options; /* Number of printer options */
73 cups_option_t *options; /* Printer options */
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) */
80 char uri[HTTP_MAX_URI];/* Updated URI without user/pass */
81 ipp_status_t ipp_status; /* Status of IPP request */
82 http_t *http; /* HTTP connection */
83 ipp_t *request, /* IPP request */
84 *response, /* IPP response */
85 *supported; /* get-printer-attributes response */
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 */
89 ipp_attribute_t *copies_sup; /* copies-supported attribute */
90 ipp_attribute_t *charset_sup; /* charset-supported attribute */
91 ipp_attribute_t *format_sup; /* document-format-supported attribute */
92 const char *charset; /* Character set to use */
93 cups_lang_t *language; /* Default language */
94 int copies; /* Number of copies remaining */
95 const char *content_type; /* CONTENT_TYPE environment variable */
96 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
97 struct sigaction action; /* Actions for POSIX signals */
98 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
99 int version; /* IPP version */
100
101
102 /*
103 * Make sure status messages are not buffered...
104 */
105
106 setbuf(stderr, NULL);
107
108 /*
109 * Check command-line...
110 */
111
112 if (argc == 1)
113 {
114 char *s;
115
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);
122 return (0);
123 }
124 else if (argc < 6 || argc > 7)
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.
133 * Otherwise, copy stdin to a temporary file and print the temporary
134 * file.
135 */
136
137 if (argc == 6)
138 {
139 /*
140 * Copy stdin to a temporary file...
141 */
142
143 int fd; /* Temporary file */
144 char buffer[8192]; /* Buffer for copying */
145 int bytes; /* Number of bytes read */
146
147
148 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
149 {
150 perror("ERROR: unable to create temporary file");
151 return (1);
152 }
153
154 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
155 if (write(fd, buffer, bytes) < bytes)
156 {
157 perror("ERROR: unable to write to temporary file");
158 close(fd);
159 unlink(filename);
160 return (1);
161 }
162
163 close(fd);
164 }
165 else
166 {
167 strncpy(filename, argv[6], sizeof(filename) - 1);
168 filename[sizeof(filename) - 1] = '\0';
169 }
170
171 /*
172 * Extract the hostname and printer name from the URI...
173 */
174
175 httpSeparate(argv[0], method, username, hostname, &port, resource);
176
177 /*
178 * Set the authentication info, if any...
179 */
180
181 cupsSetPasswordCB(password_cb);
182
183 if (username[0])
184 {
185 if ((password = strchr(username, ':')) != NULL)
186 *password++ = '\0';
187
188 cupsSetUser(username);
189 }
190
191 /*
192 * Try connecting to the remote server...
193 */
194
195 do
196 {
197 fprintf(stderr, "INFO: Connecting to %s...\n", hostname);
198
199 if ((http = httpConnect(hostname, port)) == NULL)
200 {
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");
210 sleep(30);
211 }
212 }
213 }
214 while (http == NULL);
215
216 /*
217 * Build a URI for the printer and fill the standard IPP attributes for
218 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
219 * might contain username:password information...
220 */
221
222 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
223
224 /*
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...
228 */
229
230 language = cupsLangDefault();
231 charset_sup = NULL;
232 copies_sup = NULL;
233 format_sup = NULL;
234 version = 1;
235 supported = NULL;
236
237 do
238 {
239 /*
240 * Build the IPP request...
241 */
242
243 request = ippNew();
244 request->request.op.version[1] = version;
245 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
246 request->request.op.request_id = 1;
247
248 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
249 "attributes-charset", NULL, "utf-8");
250
251 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
252 "attributes-natural-language", NULL,
253 language != NULL ? language->language : "en");
254
255 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
256 NULL, uri);
257
258 /*
259 * Do the request...
260 */
261
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)
268 {
269 if (supported)
270 ippDelete(supported);
271
272 if (ipp_status == IPP_PRINTER_BUSY ||
273 ipp_status == IPP_SERVICE_UNAVAILABLE)
274 {
275 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
276 sleep(10);
277 }
278 else if ((ipp_status == IPP_BAD_REQUEST ||
279 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
280 {
281 /*
282 * Switch to IPP/1.0...
283 */
284
285 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr);
286 version = 0;
287 }
288 else
289 fprintf(stderr, "ERROR: Printer will not accept print file (%s)!\n",
290 ippErrorString(ipp_status));
291 }
292 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
293 IPP_TAG_RANGE)) != NULL)
294 {
295 /*
296 * Has the "copies-supported" attribute - does it have an upper
297 * bound > 1?
298 */
299
300 if (copies_sup->values[0].range.upper <= 1)
301 copies_sup = NULL; /* No */
302 }
303
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);
316 }
317 }
318 while (ipp_status > IPP_OK_CONFLICT);
319
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... Only ignore SIGTERM if we are printing data from
324 * stdin (otherwise you can't cancel raw jobs...)
325 */
326
327 if (argc < 7)
328 {
329 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
330 sigset(SIGTERM, SIG_IGN);
331 #elif defined(HAVE_SIGACTION)
332 memset(&action, 0, sizeof(action));
333
334 sigemptyset(&action.sa_mask);
335 action.sa_handler = SIG_IGN;
336 sigaction(SIGTERM, &action, NULL);
337 #else
338 signal(SIGTERM, SIG_IGN);
339 #endif /* HAVE_SIGSET */
340 }
341
342 /*
343 * See if the printer supports multiple copies...
344 */
345
346 if (copies_sup || argc < 7)
347 copies = 1;
348 else
349 copies = atoi(argv[4]);
350
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
388 /*
389 * Then issue the print-job request...
390 */
391
392 while (copies > 0)
393 {
394 /*
395 * Build the IPP request...
396 */
397
398 request = ippNew();
399 request->request.op.version[1] = version;
400 request->request.op.operation_id = IPP_PRINT_JOB;
401 request->request.op.request_id = 1;
402
403 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
404 "attributes-charset", NULL, charset);
405
406 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
407 "attributes-natural-language", NULL,
408 language != NULL ? language->language : "en");
409
410 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
411 NULL, uri);
412
413 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
414
415 if (argv[2][0])
416 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
417 NULL, argv[2]);
418
419 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
420
421 if (argv[3][0])
422 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
423 argv[3]);
424
425 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
426
427 /*
428 * Handle options on the command-line...
429 */
430
431 options = NULL;
432 num_options = cupsParseOptions(argv[5], 0, &options);
433
434 if (argc > 6)
435 content_type = getenv("CONTENT_TYPE");
436 else
437 content_type = "application/vnd.cups-raw";
438
439 if (content_type != NULL && format_sup != NULL)
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 }
449
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
466 cupsFreeOptions(num_options, options);
467
468
469 /*
470 * Do the request...
471 */
472
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 {
480 job_id = 0;
481
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)
494 {
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);
502 }
503
504 if (response)
505 ippDelete(response);
506
507 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
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;
515
516 /*
517 * Wait for the job to complete...
518 */
519
520 if (!job_id)
521 continue;
522
523 fputs("INFO: Waiting for job to complete...\n", stderr);
524
525 for (;;)
526 {
527 /*
528 * Build an IPP_GET_JOB_ATTRIBUTES request...
529 */
530
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;
535
536 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
537 "attributes-charset", NULL, charset);
538
539 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
540 "attributes-natural-language", NULL,
541 language != NULL ? language->language : "en");
542
543 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
544 NULL, uri);
545
546 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
547 job_id);
548
549 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
550 "requested-attributes", NULL, "job-state");
551
552 /*
553 * Do the request...
554 */
555
556 if ((response = cupsDoRequest(http, request, resource)) == NULL)
557 ipp_status = cupsLastError();
558 else
559 ipp_status = response->request.status.status_code;
560
561 if (ipp_status == IPP_NOT_FOUND)
562 {
563 /*
564 * Job has gone away and/or the server has no job history...
565 */
566
567 ippDelete(response);
568
569 ipp_status = IPP_OK;
570 break;
571 }
572
573 if (ipp_status > IPP_OK_CONFLICT)
574 {
575 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
576 ipp_status != IPP_PRINTER_BUSY)
577 {
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));
583 break;
584 }
585 }
586 else if ((job_state = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
587 {
588 /*
589 * Stop polling if the job is finished or pending-held...
590 */
591
592 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
593 job_state->values[0].integer == IPP_JOB_HELD)
594 {
595 ippDelete(response);
596 break;
597 }
598 }
599
600 /*
601 * Wait 10 seconds before polling again...
602 */
603
604 if (response)
605 ippDelete(response);
606
607 sleep(10);
608 }
609 }
610
611 /*
612 * Free memory...
613 */
614
615 httpClose(http);
616
617 if (supported)
618 ippDelete(supported);
619
620 /*
621 * Close and remove the temporary file if necessary...
622 */
623
624 if (argc < 7)
625 unlink(filename);
626
627 /*
628 * Return the queue status...
629 */
630
631 if (ipp_status <= IPP_OK_CONFLICT)
632 fputs("INFO: Ready to print.\n", stderr);
633
634 return (ipp_status > IPP_OK_CONFLICT);
635 }
636
637
638 /*
639 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
640 */
641
642 const char * /* O - Password */
643 password_cb(const char *prompt) /* I - Prompt (not used) */
644 {
645 (void)prompt;
646
647 return (password);
648 }
649
650
651 /*
652 * End of "$Id: ipp.c,v 1.54 2002/01/02 17:58:34 mike Exp $".
653 */