2 * "$Id: ipp.c,v 1.15 1999/12/08 14:59:49 mike Exp $"
4 * IPP backend for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-1999 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.
30 * Include necessary headers.
36 #include <sys/types.h>
38 #include <cups/cups.h>
39 #include <cups/language.h>
40 #include <cups/string.h>
44 * 'main()' - Send a file to the printer or server.
48 * printer-uri job-id user title copies options [file]
51 int /* O - Exit status */
52 main(int argc
, /* I - Number of command-line arguments (6 or 7) */
53 char *argv
[]) /* I - Command-line arguments */
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 */
71 ipp_status_t ipp_status
; /* Status of IPP request */
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 */
77 ipp_attribute_t
*copies_sup
; /* copies-supported attribute */
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 */
83 int copies
; /* Number of copies remaining */
86 if (argc
< 6 || argc
> 7)
88 fprintf(stderr
, "Usage: %s job-id user title copies options [file]\n",
94 * If we have 7 arguments, print the file named on the command-line.
95 * Otherwise, copy stdin to a temporary file and print the temporary
102 * Copy stdin to a temporary file...
105 FILE *fp
; /* Temporary file */
106 char buffer
[8192]; /* Buffer for copying */
107 int bytes
; /* Number of bytes read */
110 if ((fp
= fopen(cupsTempFile(filename
, sizeof(filename
)), "w")) == NULL
)
112 perror("ERROR: unable to create temporary file");
116 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0)
117 if (fwrite(buffer
, 1, bytes
, fp
) < bytes
)
119 perror("ERROR: unable to write to temporary file");
129 strncpy(filename
, argv
[6], sizeof(filename
) - 1);
130 filename
[sizeof(filename
) - 1] = '\0';
134 * Open the print file...
137 if ((fp
= fopen(filename
, "rb")) == NULL
)
139 perror("ERROR: Unable to open print file");
143 stat(filename
, &fileinfo
);
146 * Extract the hostname and printer name from the URI...
149 httpSeparate(argv
[0], method
, username
, hostname
, &port
, resource
);
152 * Try connecting to the remote server...
157 fprintf(stderr
, "INFO: Connecting to %s...\n", hostname
);
159 if ((http
= httpConnect(hostname
, port
)) == NULL
)
160 if (errno
== ECONNREFUSED
)
162 fprintf(stderr
, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
168 perror("ERROR: Unable to connect to IPP host");
175 while (http
== NULL
);
178 * Build a URI for the printer and fill the standard IPP attributes for
179 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
180 * might contain username:password information...
183 snprintf(uri
, sizeof(uri
), "%s://%s:%d%s", method
, hostname
, port
, resource
);
186 * First validate the destination and see if the device supports multiple
187 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
188 * don't support the copies attribute...
191 language
= cupsLangDefault();
197 * Build the IPP request...
201 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
202 request
->request
.op
.request_id
= 1;
204 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
205 "attributes-charset", NULL
, cupsLangEncoding(language
));
207 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
208 "attributes-natural-language", NULL
,
209 language
!= NULL
? language
->language
: "C");
211 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
215 * Now fill in the HTTP request stuff...
218 httpClearFields(http
);
219 httpSetField(http
, HTTP_FIELD_CONTENT_TYPE
, "application/ipp");
222 httpEncode64(password
, username
);
223 httpSetField(http
, HTTP_FIELD_AUTHORIZATION
, password
);
226 sprintf(buffer
, "%u", ippLength(request
));
227 httpSetField(http
, HTTP_FIELD_CONTENT_LENGTH
, buffer
);
236 * POST the request, retrying as needed...
239 if (httpPost(http
, resource
))
241 fputs("INFO: Unable to POST get-printer-attributes request; retrying...\n", stderr
);
247 fputs("INFO: POST successful, sending IPP request...\n", stderr
);
250 * Send the IPP request...
253 request
->state
= IPP_IDLE
;
255 if (ippWrite(http
, request
) == IPP_ERROR
)
257 fputs("ERROR: Unable to send IPP request!\n", stderr
);
262 fputs("INFO: IPP request sent, getting status...\n", stderr
);
265 * Finally, check the status from the HTTP server...
268 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
270 if (status
== HTTP_OK
)
273 ippRead(http
, response
);
275 ipp_status
= response
->request
.status
.status_code
;
277 if (ipp_status
> IPP_OK_CONFLICT
)
279 if (ipp_status
== IPP_PRINTER_BUSY
||
280 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
282 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr
);
287 fprintf(stderr
, "ERROR: Printer will not accept print file (%x)!\n",
292 else if ((copies_sup
= ippFindAttribute(response
, "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 */
308 if (status
== HTTP_ERROR
)
310 fprintf(stderr
, "WARNING: Did not receive the IPP response (%d)\n",
313 ipp_status
= IPP_PRINTER_BUSY
;
316 fprintf(stderr
, "ERROR: Validate request was not accepted (%d)!\n", status
);
324 if (status
!= HTTP_OK
)
334 while (ipp_status
> IPP_OK_CONFLICT
);
337 * See if the printer supports multiple copies...
343 copies
= atoi(argv
[4]);
346 * Then issue the print-job request...
352 * Build the IPP request...
356 request
->request
.op
.operation_id
= IPP_PRINT_JOB
;
357 request
->request
.op
.request_id
= 1;
359 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
360 "attributes-charset", NULL
, cupsLangEncoding(language
));
362 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
363 "attributes-natural-language", NULL
,
364 language
!= NULL
? language
->language
: "C");
366 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
369 fprintf(stderr
, "DEBUG: printer-uri = \"%s\"\n", uri
);
371 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
374 fprintf(stderr
, "DEBUG: requesting-user-name = \"%s\"\n", argv
[2]);
376 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
379 fprintf(stderr
, "DEBUG: job-name = \"%s\"\n", argv
[3]);
382 * Handle options on the command-line...
386 num_options
= cupsParseOptions(argv
[5], 0, &options
);
388 if (cupsGetOption("raw", num_options
, options
))
389 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
390 NULL
, "application/vnd.cups-raw");
392 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format",
393 NULL
, "application/octet-stream");
396 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies", atoi(argv
[4]));
398 for (i
= 0; i
< num_options
; i
++)
401 * Skip the "raw" option - handled above...
404 if (strcasecmp(options
[i
].name
, "raw") == 0)
408 * See what the option value is; for compatibility with older interface
409 * scripts, we have to support single-argument options as well as
410 * option=value, option=low-high, and option=MxN.
413 option
= options
[i
].name
;
414 val
= options
[i
].value
;
421 if (strcasecmp(val
, "true") == 0 ||
422 strcasecmp(val
, "on") == 0 ||
423 strcasecmp(val
, "yes") == 0)
426 * Boolean value - true...
432 else if (strcasecmp(val
, "false") == 0 ||
433 strcasecmp(val
, "off") == 0 ||
434 strcasecmp(val
, "no") == 0)
437 * Boolean value - false...
444 n
= strtol(val
, &s
, 0);
448 if (strncasecmp(option
, "no", 2) == 0)
459 if (*s
!= '\0' && *s
!= '-' && (*s
!= 'x' || s
== val
))
463 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, option
, NULL
, val
);
464 else if (val
!= NULL
)
467 * Numeric value, range, or resolution...
472 n2
= strtol(s
+ 1, NULL
, 0);
473 ippAddRange(request
, IPP_TAG_JOB
, option
, n
, n2
);
477 n2
= strtol(s
+ 1, &s
, 0);
479 if (strcasecmp(s
, "dpc") == 0)
480 ippAddResolution(request
, IPP_TAG_JOB
, option
, IPP_RES_PER_CM
, n
, n2
);
481 else if (strcasecmp(s
, "dpi") == 0)
482 ippAddResolution(request
, IPP_TAG_JOB
, option
, IPP_RES_PER_INCH
, n
, n2
);
484 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, option
, NULL
, val
);
487 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, option
, n
);
493 ippAddBoolean(request
, IPP_TAG_JOB
, option
, (char)n
);
497 * Now fill in the HTTP request stuff...
500 httpClearFields(http
);
501 httpSetField(http
, HTTP_FIELD_CONTENT_TYPE
, "application/ipp");
504 httpEncode64(password
, username
);
505 httpSetField(http
, HTTP_FIELD_AUTHORIZATION
, password
);
508 sprintf(buffer
, "%u", ippLength(request
) + (size_t)fileinfo
.st_size
);
509 httpSetField(http
, HTTP_FIELD_CONTENT_LENGTH
, buffer
);
518 * POST the request, retrying as needed...
523 if (httpPost(http
, resource
))
525 fputs("INFO: Unable to POST print request; retrying...\n", stderr
);
530 fputs("INFO: POST successful, sending IPP request...\n", stderr
);
533 * Send the IPP request...
536 request
->state
= IPP_IDLE
;
538 if (ippWrite(http
, request
) == IPP_ERROR
)
540 fputs("ERROR: Unable to send IPP request!\n", stderr
);
545 fputs("INFO: IPP request sent, sending print file...\n", stderr
);
548 * Then send the file...
554 while ((nbytes
= fread(buffer
, 1, sizeof(buffer
), fp
)) > 0)
557 fprintf(stderr
, "INFO: Sending print file, %uk...\n", tbytes
/ 1024);
559 if (httpWrite(http
, buffer
, nbytes
) < nbytes
)
561 perror("ERROR: Unable to send print file to printer");
567 fputs("INFO: Print file sent; checking status...\n", stderr
);
570 * Finally, check the status from the HTTP server...
573 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
575 if (status
== HTTP_OK
)
578 ippRead(http
, response
);
580 if ((ipp_status
= response
->request
.status
.status_code
) > IPP_OK_CONFLICT
)
582 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
583 ipp_status
== IPP_PRINTER_BUSY
)
585 fputs("INFO: Printer is busy; retrying print job...\n", stderr
);
589 fprintf(stderr
, "ERROR: Print file was not accepted (%04x)!\n",
590 response
->request
.status
.status_code
);
592 else if ((job_id
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
593 fputs("INFO: Print file accepted - job ID unknown.\n", stderr
);
595 fprintf(stderr
, "INFO: Print file accepted - job ID %d.\n",
596 job_id
->values
[0].integer
);
601 ipp_status
= IPP_PRINTER_BUSY
;
603 if (status
== HTTP_ERROR
)
605 fprintf(stderr
, "WARNING: Did not receive the IPP response (%d)\n",
610 fprintf(stderr
, "ERROR: Print request was not accepted (%d)!\n", status
);
620 if (response
!= NULL
)
623 if (ipp_status
<= IPP_OK_CONFLICT
)
625 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
637 * Close and remove the temporary file if necessary...
646 * Return the queue status...
649 return (status
!= HTTP_OK
);
654 * End of "$Id: ipp.c,v 1.15 1999/12/08 14:59:49 mike Exp $".