]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * "$Id: ipp.c,v 1.11 1999/10/10 15:40:07 mike Exp $" | |
3 | * | |
4 | * IPP backend for the Common UNIX Printing System (CUPS). | |
5 | * | |
6 | * Copyright 1997-1999 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 | */ | |
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 | ||
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 */ | |
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 */ | |
71 | FILE *fp; /* File to print */ | |
72 | http_t *http; /* HTTP connection */ | |
73 | ipp_t *request, /* IPP request */ | |
74 | *response; /* IPP response */ | |
75 | ipp_attribute_t *job_id; /* job-id attribute */ | |
76 | cups_lang_t *language; /* Default language */ | |
77 | struct stat fileinfo; /* File statistics */ | |
78 | size_t nbytes, /* Number of bytes written */ | |
79 | tbytes; /* Total bytes written */ | |
80 | char buffer[8192]; /* Output buffer */ | |
81 | ||
82 | ||
83 | if (argc < 6 || argc > 7) | |
84 | { | |
85 | fprintf(stderr, "Usage: %s job-id user title copies options [file]\n", | |
86 | argv[0]); | |
87 | return (1); | |
88 | } | |
89 | ||
90 | /* | |
91 | * If we have 7 arguments, print the file named on the command-line. | |
92 | * Otherwise, print stdin... | |
93 | */ | |
94 | ||
95 | if (argc == 6) | |
96 | fp = stdin; | |
97 | else if ((fp = fopen(argv[6], "rb")) == NULL) | |
98 | { | |
99 | perror("ERROR: Unable to open print file"); | |
100 | return (1); | |
101 | } | |
102 | else | |
103 | stat(argv[6], &fileinfo); | |
104 | ||
105 | /* | |
106 | * Extract the hostname and printer name from the URI... | |
107 | */ | |
108 | ||
109 | httpSeparate(argv[0], method, username, hostname, &port, resource); | |
110 | ||
111 | /* | |
112 | * Try connecting to the remote server... | |
113 | */ | |
114 | ||
115 | fprintf(stderr, "INFO: Connecting to %s...\n", hostname); | |
116 | ||
117 | if ((http = httpConnect(hostname, port)) == NULL) | |
118 | { | |
119 | perror("ERROR: Unable to connect to IPP host"); | |
120 | ||
121 | if (fp != stdin) | |
122 | fclose(fp); | |
123 | return (1); | |
124 | } | |
125 | ||
126 | /* | |
127 | * Build a URI for the printer and fill the standard IPP attributes for | |
128 | * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it | |
129 | * might contain username:password information... | |
130 | */ | |
131 | ||
132 | request = ippNew(); | |
133 | request->request.op.operation_id = IPP_PRINT_JOB; | |
134 | request->request.op.request_id = 1; | |
135 | ||
136 | snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource); | |
137 | ||
138 | language = cupsLangDefault(); | |
139 | ||
140 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, | |
141 | "attributes-charset", NULL, cupsLangEncoding(language)); | |
142 | ||
143 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, | |
144 | "attributes-natural-language", NULL, | |
145 | language != NULL ? language->language : "C"); | |
146 | ||
147 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
148 | NULL, uri); | |
149 | ||
150 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", | |
151 | NULL, argv[2]); | |
152 | ||
153 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, argv[3]); | |
154 | ||
155 | /* | |
156 | * Handle options on the command-line... | |
157 | */ | |
158 | ||
159 | options = NULL; | |
160 | num_options = cupsParseOptions(argv[5], 0, &options); | |
161 | ||
162 | if (cupsGetOption("raw", num_options, options)) | |
163 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", | |
164 | NULL, "application/vnd.cups-raw"); | |
165 | else | |
166 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", | |
167 | NULL, "application/octet-stream"); | |
168 | ||
169 | ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", atoi(argv[4])); | |
170 | ||
171 | for (i = 0; i < num_options; i ++) | |
172 | { | |
173 | /* | |
174 | * Skip the "raw" option - handled above... | |
175 | */ | |
176 | ||
177 | if (strcmp(options[i].name, "raw") == 0) | |
178 | continue; | |
179 | ||
180 | /* | |
181 | * See what the option value is; for compatibility with older interface | |
182 | * scripts, we have to support single-argument options as well as | |
183 | * option=value, option=low-high, and option=MxN. | |
184 | */ | |
185 | ||
186 | option = options[i].name; | |
187 | val = options[i].value; | |
188 | ||
189 | if (*val == '\0') | |
190 | val = NULL; | |
191 | ||
192 | if (val != NULL) | |
193 | { | |
194 | if (strcasecmp(val, "true") == 0 || | |
195 | strcasecmp(val, "on") == 0 || | |
196 | strcasecmp(val, "yes") == 0) | |
197 | { | |
198 | /* | |
199 | * Boolean value - true... | |
200 | */ | |
201 | ||
202 | n = 1; | |
203 | val = ""; | |
204 | } | |
205 | else if (strcasecmp(val, "false") == 0 || | |
206 | strcasecmp(val, "off") == 0 || | |
207 | strcasecmp(val, "no") == 0) | |
208 | { | |
209 | /* | |
210 | * Boolean value - false... | |
211 | */ | |
212 | ||
213 | n = 0; | |
214 | val = ""; | |
215 | } | |
216 | ||
217 | n = strtol(val, &s, 0); | |
218 | } | |
219 | else | |
220 | { | |
221 | if (strncmp(option, "no", 2) == 0) | |
222 | { | |
223 | option += 2; | |
224 | n = 0; | |
225 | } | |
226 | else | |
227 | n = 1; | |
228 | ||
229 | s = ""; | |
230 | } | |
231 | ||
232 | if (*s != '\0' && *s != '-' && (*s != 'x' || s == val)) | |
233 | /* | |
234 | * String value(s)... | |
235 | */ | |
236 | ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val); | |
237 | else if (val != NULL) | |
238 | { | |
239 | /* | |
240 | * Numeric value, range, or resolution... | |
241 | */ | |
242 | ||
243 | if (*s == '-') | |
244 | { | |
245 | n2 = strtol(s + 1, NULL, 0); | |
246 | ippAddRange(request, IPP_TAG_JOB, option, n, n2); | |
247 | } | |
248 | else if (*s == 'x') | |
249 | { | |
250 | n2 = strtol(s + 1, &s, 0); | |
251 | ||
252 | if (strcmp(s, "dpc") == 0) | |
253 | ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_CM, n, n2); | |
254 | else if (strcmp(s, "dpi") == 0) | |
255 | ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_INCH, n, n2); | |
256 | else | |
257 | ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val); | |
258 | } | |
259 | else | |
260 | ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, option, n); | |
261 | } | |
262 | else | |
263 | /* | |
264 | * Boolean value... | |
265 | */ | |
266 | ippAddBoolean(request, IPP_TAG_JOB, option, (char)n); | |
267 | } | |
268 | ||
269 | /* | |
270 | * Now fill in the HTTP request stuff... | |
271 | */ | |
272 | ||
273 | httpClearFields(http); | |
274 | httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); | |
275 | httpEncode64(password, username); | |
276 | httpSetField(http, HTTP_FIELD_AUTHORIZATION, password); | |
277 | ||
278 | if (fp != stdin) | |
279 | { | |
280 | sprintf(buffer, "%u", ippLength(request) + (size_t)fileinfo.st_size); | |
281 | httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, buffer); | |
282 | } | |
283 | else | |
284 | httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked"); | |
285 | ||
286 | /* | |
287 | * Do the request... | |
288 | */ | |
289 | ||
290 | for (;;) | |
291 | { | |
292 | /* | |
293 | * POST the request, retrying as needed... | |
294 | */ | |
295 | ||
296 | if (httpPost(http, resource)) | |
297 | { | |
298 | fputs("INFO: Unable to POST print request; retrying...\n", stderr); | |
299 | sleep(10); | |
300 | httpReconnect(http); | |
301 | continue; | |
302 | } | |
303 | ||
304 | fputs("INFO: POST successful, sending IPP request...\n", stderr); | |
305 | ||
306 | /* | |
307 | * Send the IPP request... | |
308 | */ | |
309 | ||
310 | request->state = IPP_IDLE; | |
311 | ||
312 | if (ippWrite(http, request) == IPP_ERROR) | |
313 | { | |
314 | fputs("ERROR: Unable to send IPP request!\n", stderr); | |
315 | status = HTTP_ERROR; | |
316 | break; | |
317 | } | |
318 | ||
319 | fputs("INFO: IPP request sent, sending print file...\n", stderr); | |
320 | ||
321 | /* | |
322 | * Then send the file... | |
323 | */ | |
324 | ||
325 | tbytes = 0; | |
326 | while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) | |
327 | { | |
328 | tbytes += nbytes; | |
329 | fprintf(stderr, "INFO: Sending print file, %uk...\n", tbytes / 1024); | |
330 | ||
331 | if (httpWrite(http, buffer, nbytes) < nbytes) | |
332 | { | |
333 | perror("ERROR: Unable to send print file to printer"); | |
334 | status = HTTP_ERROR; | |
335 | break; | |
336 | } | |
337 | } | |
338 | ||
339 | /* | |
340 | * If we are chunking the output from stdin, make sure we end up with | |
341 | * a 0-length chunk at the end... | |
342 | */ | |
343 | ||
344 | if (fp == stdin) | |
345 | httpWrite(http, buffer, 0); | |
346 | ||
347 | fputs("INFO: Print file sent; checking status...\n", stderr); | |
348 | ||
349 | /* | |
350 | * Finally, check the status from the HTTP server... | |
351 | */ | |
352 | ||
353 | while ((status = httpUpdate(http)) == HTTP_CONTINUE); | |
354 | ||
355 | if (status == HTTP_OK) | |
356 | { | |
357 | response = ippNew(); | |
358 | ippRead(http, response); | |
359 | ||
360 | if (response->request.status.status_code > IPP_OK_CONFLICT) | |
361 | fprintf(stderr, "ERROR: Print file was not accepted (%04x)!\n", | |
362 | response->request.status.status_code); | |
363 | else if ((job_id = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL) | |
364 | fputs("INFO: Print file accepted - job ID unknown.\n", stderr); | |
365 | else | |
366 | fprintf(stderr, "INFO: Print file accepted - job ID %d.\n", | |
367 | job_id->values[0].integer); | |
368 | } | |
369 | else | |
370 | { | |
371 | response = NULL; | |
372 | httpFlush(http); | |
373 | ||
374 | fprintf(stderr, "ERROR: Print request was not accepted (%d)!\n", status); | |
375 | } | |
376 | ||
377 | break; | |
378 | } | |
379 | ||
380 | /* | |
381 | * Free memory... | |
382 | */ | |
383 | ||
384 | httpClose(http); | |
385 | if (request != NULL) | |
386 | ippDelete(request); | |
387 | if (response != NULL) | |
388 | ippDelete(response); | |
389 | ||
390 | /* | |
391 | * Close the print file as needed... | |
392 | */ | |
393 | ||
394 | if (fp != stdin) | |
395 | fclose(fp); | |
396 | ||
397 | /* | |
398 | * Return the queue status... | |
399 | */ | |
400 | ||
401 | return (status != HTTP_OK); | |
402 | } | |
403 | ||
404 | ||
405 | /* | |
406 | * End of "$Id: ipp.c,v 1.11 1999/10/10 15:40:07 mike Exp $". | |
407 | */ |