]> git.ipfire.org Git - thirdparty/cups.git/blame_incremental - backend/ipp.c
Mirror lpstat change from 1.1.
[thirdparty/cups.git] / backend / ipp.c
... / ...
CommitLineData
1/*
2 * "$Id: ipp.c,v 1.38.2.3 2002/01/02 18:04:17 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
49const char *password_cb(const char *);
50
51
52/*
53 * Local globals...
54 */
55
56char *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
67int /* O - Exit status */
68main(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...
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
338 /*
339 * See if the printer supports multiple copies...
340 */
341
342 if (copies_sup)
343 copies = 1;
344 else
345 copies = atoi(argv[4]);
346
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
384 /*
385 * Then issue the print-job request...
386 */
387
388 while (copies > 0)
389 {
390 /*
391 * Build the IPP request...
392 */
393
394 request = ippNew();
395 request->request.op.version[1] = version;
396 request->request.op.operation_id = IPP_PRINT_JOB;
397 request->request.op.request_id = 1;
398
399 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
400 "attributes-charset", NULL, charset);
401
402 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
403 "attributes-natural-language", NULL,
404 language != NULL ? language->language : "en");
405
406 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
407 NULL, uri);
408
409 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
410
411 if (argv[2][0])
412 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
413 NULL, argv[2]);
414
415 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
416
417 if (argv[3][0])
418 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
419 argv[3]);
420
421 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
422
423 /*
424 * Handle options on the command-line...
425 */
426
427 options = NULL;
428 num_options = cupsParseOptions(argv[5], 0, &options);
429
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)
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 }
445
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
462 cupsFreeOptions(num_options, options);
463
464
465 /*
466 * Do the request...
467 */
468
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 {
476 job_id = 0;
477
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)
490 {
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);
498 }
499
500 if (response)
501 ippDelete(response);
502
503 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
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;
511
512 /*
513 * Wait for the job to complete...
514 */
515
516 if (!job_id)
517 continue;
518
519 fputs("INFO: Waiting for job to complete...\n", stderr);
520
521 for (;;)
522 {
523 /*
524 * Build an IPP_GET_JOB_ATTRIBUTES request...
525 */
526
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;
531
532 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
533 "attributes-charset", NULL, charset);
534
535 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
536 "attributes-natural-language", NULL,
537 language != NULL ? language->language : "en");
538
539 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
540 NULL, uri);
541
542 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
543 job_id);
544
545 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
546 "requested-attributes", NULL, "job-state");
547
548 /*
549 * Do the request...
550 */
551
552 if ((response = cupsDoRequest(http, request, resource)) == NULL)
553 ipp_status = cupsLastError();
554 else
555 ipp_status = response->request.status.status_code;
556
557 if (ipp_status == IPP_NOT_FOUND)
558 {
559 /*
560 * Job has gone away and the server has no job history...
561 */
562
563 ippDelete(response);
564 break;
565 }
566
567 if (ipp_status > IPP_OK_CONFLICT)
568 {
569 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
570 ipp_status != IPP_PRINTER_BUSY)
571 {
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;
578 }
579 }
580 else if ((job_state = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
581 {
582 /*
583 * Stop polling if the job is finished or pending-held...
584 */
585
586 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
587 job_state->values[0].integer == IPP_JOB_HELD)
588 {
589 ippDelete(response);
590 break;
591 }
592 }
593
594 /*
595 * Wait 10 seconds before polling again...
596 */
597
598 if (response)
599 ippDelete(response);
600
601 sleep(10);
602 }
603 }
604
605 /*
606 * Free memory...
607 */
608
609 httpClose(http);
610
611 if (supported)
612 ippDelete(supported);
613
614 /*
615 * Close and remove the temporary file if necessary...
616 */
617
618 if (argc < 7)
619 unlink(filename);
620
621 /*
622 * Return the queue status...
623 */
624
625 if (ipp_status <= IPP_OK_CONFLICT)
626 fputs("INFO: Ready to print.\n", stderr);
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);
642}
643
644
645/*
646 * End of "$Id: ipp.c,v 1.38.2.3 2002/01/02 18:04:17 mike Exp $".
647 */