2 * "$Id: ipp.c,v 1.54 2002/01/02 17:58:34 mike Exp $"
4 * IPP backend for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2002 by Easy Software Products, all rights reserved.
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
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * main() - Send a file to the printer or server.
27 * password_cb() - Disable the password prompt for cupsDoFileRequest().
31 * Include necessary headers.
37 #include <sys/types.h>
39 #include <cups/cups.h>
40 #include <cups/language.h>
41 #include <cups/string.h>
49 const char *password_cb(const char *);
56 char *password
= NULL
;
60 * 'main()' - Send a file to the printer or server.
64 * printer-uri job-id user title copies options [file]
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 */
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 */
103 * Make sure status messages are not buffered...
106 setbuf(stderr
, NULL
);
109 * Check command-line...
116 if ((s
= strrchr(argv
[0], '/')) != NULL
)
121 printf("network %s \"Unknown\" \"Internet Printing Protocol\"\n", s
);
124 else if (argc
< 6 || argc
> 7)
126 fprintf(stderr
, "Usage: %s job-id user title copies options [file]\n",
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
140 * Copy stdin to a temporary file...
143 int fd
; /* Temporary file */
144 char buffer
[8192]; /* Buffer for copying */
145 int bytes
; /* Number of bytes read */
148 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
150 perror("ERROR: unable to create temporary file");
154 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0)
155 if (write(fd
, buffer
, bytes
) < bytes
)
157 perror("ERROR: unable to write to temporary file");
167 strncpy(filename
, argv
[6], sizeof(filename
) - 1);
168 filename
[sizeof(filename
) - 1] = '\0';
172 * Extract the hostname and printer name from the URI...
175 httpSeparate(argv
[0], method
, username
, hostname
, &port
, resource
);
178 * Set the authentication info, if any...
181 cupsSetPasswordCB(password_cb
);
185 if ((password
= strchr(username
, ':')) != NULL
)
188 cupsSetUser(username
);
192 * Try connecting to the remote server...
197 fprintf(stderr
, "INFO: Connecting to %s...\n", hostname
);
199 if ((http
= httpConnect(hostname
, port
)) == NULL
)
201 if (errno
== ECONNREFUSED
)
203 fprintf(stderr
, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
209 perror("ERROR: Unable to connect to IPP host");
214 while (http
== NULL
);
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...
222 snprintf(uri
, sizeof(uri
), "%s://%s:%d%s", method
, hostname
, port
, resource
);
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...
230 language
= cupsLangDefault();
240 * Build the IPP request...
244 request
->request
.op
.version
[1] = version
;
245 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
246 request
->request
.op
.request_id
= 1;
248 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
249 "attributes-charset", NULL
, "utf-8");
251 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
252 "attributes-natural-language", NULL
,
253 language
!= NULL
? language
->language
: "en");
255 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
262 if ((supported
= cupsDoRequest(http
, request
, resource
)) == NULL
)
263 ipp_status
= cupsLastError();
265 ipp_status
= supported
->request
.status
.status_code
;
267 if (ipp_status
> IPP_OK_CONFLICT
)
270 ippDelete(supported
);
272 if (ipp_status
== IPP_PRINTER_BUSY
||
273 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
275 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr
);
278 else if ((ipp_status
== IPP_BAD_REQUEST
||
279 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
== 1)
282 * Switch to IPP/1.0...
285 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr
);
289 fprintf(stderr
, "ERROR: Printer will not accept print file (%s)!\n",
290 ippErrorString(ipp_status
));
292 else if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
293 IPP_TAG_RANGE
)) != NULL
)
296 * Has the "copies-supported" attribute - does it have an upper
300 if (copies_sup
->values
[0].range
.upper
<= 1)
301 copies_sup
= NULL
; /* No */
304 charset_sup
= ippFindAttribute(supported
, "charset-supported",
306 format_sup
= ippFindAttribute(supported
, "document-format-supported",
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
);
318 while (ipp_status
> IPP_OK_CONFLICT
);
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...)
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
));
334 sigemptyset(&action
.sa_mask
);
335 action
.sa_handler
= SIG_IGN
;
336 sigaction(SIGTERM
, &action
, NULL
);
338 signal(SIGTERM
, SIG_IGN
);
339 #endif /* HAVE_SIGSET */
343 * See if the printer supports multiple copies...
346 if (copies_sup
|| argc
< 7)
349 copies
= atoi(argv
[4]);
352 * Figure out the character set to use...
355 charset
= language
? cupsLangEncoding(language
) : "us-ascii";
360 * See if IPP server supports the requested character set...
363 for (i
= 0; i
< charset_sup
->num_values
; i
++)
364 if (strcasecmp(charset
, charset_sup
->values
[i
].string
.text
) == 0)
368 * If not, choose us-ascii or utf-8...
371 if (i
>= charset_sup
->num_values
)
374 * See if us-ascii is supported...
377 for (i
= 0; i
< charset_sup
->num_values
; i
++)
378 if (strcasecmp("us-ascii", charset_sup
->values
[i
].string
.text
) == 0)
381 if (i
< charset_sup
->num_values
)
382 charset
= "us-ascii";
389 * Then issue the print-job request...
395 * Build the IPP request...
399 request
->request
.op
.version
[1] = version
;
400 request
->request
.op
.operation_id
= IPP_PRINT_JOB
;
401 request
->request
.op
.request_id
= 1;
403 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
404 "attributes-charset", NULL
, charset
);
406 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
407 "attributes-natural-language", NULL
,
408 language
!= NULL
? language
->language
: "en");
410 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
413 fprintf(stderr
, "DEBUG: printer-uri = \"%s\"\n", uri
);
416 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
419 fprintf(stderr
, "DEBUG: requesting-user-name = \"%s\"\n", argv
[2]);
422 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
425 fprintf(stderr
, "DEBUG: job-name = \"%s\"\n", argv
[3]);
428 * Handle options on the command-line...
432 num_options
= cupsParseOptions(argv
[5], 0, &options
);
435 content_type
= getenv("CONTENT_TYPE");
437 content_type
= "application/vnd.cups-raw";
439 if (content_type
!= NULL
&& format_sup
!= NULL
)
441 for (i
= 0; i
< format_sup
->num_values
; i
++)
442 if (strcasecmp(content_type
, format_sup
->values
[i
].string
.text
) == 0)
445 if (i
< format_sup
->num_values
)
446 num_options
= cupsAddOption("document-format", content_type
,
447 num_options
, &options
);
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.
461 cupsEncodeOptions(request
, num_options
, options
);
462 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies",
466 cupsFreeOptions(num_options
, options
);
473 if ((response
= cupsDoFileRequest(http
, request
, resource
, filename
)) == NULL
)
474 ipp_status
= cupsLastError();
476 ipp_status
= response
->request
.status
.status_code
;
478 if (ipp_status
> IPP_OK_CONFLICT
)
482 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
483 ipp_status
== IPP_PRINTER_BUSY
)
485 fputs("INFO: Printer is busy; retrying print job...\n", stderr
);
489 fprintf(stderr
, "ERROR: Print file was not accepted (%s)!\n",
490 ippErrorString(ipp_status
));
492 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
493 IPP_TAG_INTEGER
)) == NULL
)
495 fputs("INFO: Print file accepted - job ID unknown.\n", stderr
);
500 job_id
= job_id_attr
->values
[0].integer
;
501 fprintf(stderr
, "INFO: Print file accepted - job ID %d.\n", job_id
);
507 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
509 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
512 else if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
513 ipp_status
!= IPP_PRINTER_BUSY
)
517 * Wait for the job to complete...
523 fputs("INFO: Waiting for job to complete...\n", stderr
);
528 * Build an IPP_GET_JOB_ATTRIBUTES request...
532 request
->request
.op
.version
[1] = version
;
533 request
->request
.op
.operation_id
= IPP_GET_JOB_ATTRIBUTES
;
534 request
->request
.op
.request_id
= 1;
536 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
537 "attributes-charset", NULL
, charset
);
539 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
540 "attributes-natural-language", NULL
,
541 language
!= NULL
? language
->language
: "en");
543 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
546 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
549 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
550 "requested-attributes", NULL
, "job-state");
556 if ((response
= cupsDoRequest(http
, request
, resource
)) == NULL
)
557 ipp_status
= cupsLastError();
559 ipp_status
= response
->request
.status
.status_code
;
561 if (ipp_status
== IPP_NOT_FOUND
)
564 * Job has gone away and/or the server has no job history...
573 if (ipp_status
> IPP_OK_CONFLICT
)
575 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
576 ipp_status
!= IPP_PRINTER_BUSY
)
581 fprintf(stderr
, "ERROR: Unable to get job %d attributes (%s)!\n",
582 job_id
, ippErrorString(ipp_status
));
586 else if ((job_state
= ippFindAttribute(response
, "job-state", IPP_TAG_ENUM
)) != NULL
)
589 * Stop polling if the job is finished or pending-held...
592 if (job_state
->values
[0].integer
> IPP_JOB_PROCESSING
||
593 job_state
->values
[0].integer
== IPP_JOB_HELD
)
601 * Wait 10 seconds before polling again...
618 ippDelete(supported
);
621 * Close and remove the temporary file if necessary...
628 * Return the queue status...
631 if (ipp_status
<= IPP_OK_CONFLICT
)
632 fputs("INFO: Ready to print.\n", stderr
);
634 return (ipp_status
> IPP_OK_CONFLICT
);
639 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
642 const char * /* O - Password */
643 password_cb(const char *prompt
) /* I - Prompt (not used) */
652 * End of "$Id: ipp.c,v 1.54 2002/01/02 17:58:34 mike Exp $".