]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/request.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / request.c
1 /*
2 * "$Id: request.c 6416 2007-03-30 18:30:33Z mike $"
3 *
4 * IPP utilities for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2007 by Easy Software Products.
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.txt" 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 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
28 * cupsDoFileRequest() - Do an IPP request with a file.
29 * cupsDoRequest() - Do an IPP request.
30 * _cupsSetError() - Set the last IPP status code and status-message.
31 */
32
33 /*
34 * Include necessary headers...
35 */
36
37 #include "globals.h"
38 #include "debug.h"
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #if defined(WIN32) || defined(__EMX__)
44 # include <io.h>
45 #else
46 # include <unistd.h>
47 #endif /* WIN32 || __EMX__ */
48
49
50 /*
51 * 'cupsDoFileRequest()' - Do an IPP request with a file.
52 *
53 * This function sends the IPP request to the specified server, retrying
54 * and authenticating as necessary. The request is freed with ippDelete()
55 * after receiving a valid IPP response.
56 */
57
58 ipp_t * /* O - Response data */
59 cupsDoFileRequest(http_t *http, /* I - HTTP connection to server */
60 ipp_t *request, /* I - IPP request */
61 const char *resource, /* I - HTTP resource for POST */
62 const char *filename) /* I - File to send or NULL for none */
63 {
64 ipp_t *response; /* IPP response data */
65 size_t length; /* Content-Length value */
66 http_status_t status; /* Status of HTTP request */
67 int got_status; /* Did we get the status? */
68 ipp_state_t state; /* State of IPP processing */
69 FILE *file; /* File to send */
70 struct stat fileinfo; /* File information */
71 int bytes; /* Number of bytes read/written */
72 char buffer[32768]; /* Output buffer */
73 http_status_t expect; /* Expect: header to use */
74 #ifdef HAVE_AUTHORIZATION_H
75 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
76 #endif /* HAVE_AUTHORIZATION_H */
77
78
79 DEBUG_printf(("cupsDoFileRequest(%p, %p, \'%s\', \'%s\')\n",
80 http, request, resource ? resource : "(null)",
81 filename ? filename : "(null)"));
82
83 if (http == NULL || request == NULL || resource == NULL)
84 {
85 if (request != NULL)
86 ippDelete(request);
87
88 _cupsSetError(IPP_INTERNAL_ERROR, NULL);
89
90 return (NULL);
91 }
92
93 /*
94 * See if we have a file to send...
95 */
96
97 if (filename != NULL)
98 {
99 if (stat(filename, &fileinfo))
100 {
101 /*
102 * Can't get file information!
103 */
104
105 _cupsSetError(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
106 strerror(errno));
107
108 ippDelete(request);
109
110 return (NULL);
111 }
112
113 #ifdef WIN32
114 if (fileinfo.st_mode & _S_IFDIR)
115 #else
116 if (S_ISDIR(fileinfo.st_mode))
117 #endif /* WIN32 */
118 {
119 /*
120 * Can't send a directory...
121 */
122
123 ippDelete(request);
124
125 _cupsSetError(IPP_NOT_POSSIBLE, strerror(EISDIR));
126
127 return (NULL);
128 }
129
130 if ((file = fopen(filename, "rb")) == NULL)
131 {
132 /*
133 * Can't open file!
134 */
135
136 _cupsSetError(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
137 strerror(errno));
138
139 ippDelete(request);
140
141 return (NULL);
142 }
143 }
144 else
145 file = NULL;
146
147 #ifdef HAVE_SSL
148 /*
149 * See if we have an auth-info attribute and are communicating over
150 * a non-local link. If so, encrypt the link so that we can pass
151 * the authentication information securely...
152 */
153
154 if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
155 !httpAddrLocalhost(http->hostaddr) && !http->tls &&
156 httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
157 return (NULL);
158 #endif /* HAVE_SSL */
159
160 /*
161 * Loop until we can send the request without authorization problems.
162 */
163
164 response = NULL;
165 status = HTTP_ERROR;
166 expect = HTTP_CONTINUE;
167
168 while (response == NULL)
169 {
170 DEBUG_puts("cupsDoFileRequest: setup...");
171
172 /*
173 * Setup the HTTP variables needed...
174 */
175
176 length = ippLength(request);
177 if (filename)
178 length += fileinfo.st_size;
179
180 httpClearFields(http);
181 httpSetLength(http, length);
182 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
183 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
184 httpSetExpect(http, expect);
185
186 DEBUG_printf(("cupsDoFileRequest: authstring=\"%s\"\n", http->authstring));
187
188 /*
189 * Try the request...
190 */
191
192 DEBUG_puts("cupsDoFileRequest: post...");
193
194 if (httpPost(http, resource))
195 {
196 if (httpReconnect(http))
197 {
198 status = HTTP_ERROR;
199 break;
200 }
201 else
202 continue;
203 }
204
205 /*
206 * Send the IPP data...
207 */
208
209 DEBUG_puts("cupsDoFileRequest: ipp write...");
210
211 request->state = IPP_IDLE;
212 status = HTTP_CONTINUE;
213 got_status = 0;
214
215 while ((state = ippWrite(http, request)) != IPP_DATA)
216 if (state == IPP_ERROR)
217 break;
218 else if (httpCheck(http))
219 {
220 got_status = 1;
221
222 if ((status = httpUpdate(http)) != HTTP_CONTINUE)
223 break;
224 }
225
226 if (!got_status)
227 {
228 /*
229 * Wait up to 1 second to get the 100-continue response...
230 */
231
232 if (httpWait(http, 1000))
233 status = httpUpdate(http);
234 }
235 else if (httpCheck(http))
236 status = httpUpdate(http);
237
238 if (status == HTTP_CONTINUE && state == IPP_DATA && filename)
239 {
240 DEBUG_puts("cupsDoFileRequest: file write...");
241
242 /*
243 * Send the file...
244 */
245
246 rewind(file);
247
248 while ((bytes = (int)fread(buffer, 1, sizeof(buffer), file)) > 0)
249 {
250 if (httpCheck(http))
251 {
252 if ((status = httpUpdate(http)) != HTTP_CONTINUE)
253 break;
254 }
255
256 if (httpWrite2(http, buffer, bytes) < bytes)
257 break;
258 }
259 }
260
261 /*
262 * Get the server's return status...
263 */
264
265 DEBUG_puts("cupsDoFileRequest: update...");
266
267 while (status == HTTP_CONTINUE)
268 status = httpUpdate(http);
269
270 DEBUG_printf(("cupsDoFileRequest: status = %d\n", status));
271
272 if (status == HTTP_UNAUTHORIZED)
273 {
274 DEBUG_puts("cupsDoFileRequest: unauthorized...");
275
276 /*
277 * Flush any error message...
278 */
279
280 httpFlush(http);
281
282 /*
283 * See if we can do authentication...
284 */
285
286 if (cupsDoAuthentication(http, "POST", resource))
287 break;
288
289 if (httpReconnect(http))
290 {
291 status = HTTP_ERROR;
292 break;
293 }
294
295 continue;
296 }
297 else if (status == HTTP_ERROR)
298 {
299 DEBUG_printf(("cupsDoFileRequest: http->error=%d (%s)\n", http->error,
300 strerror(http->error)));
301
302 #ifdef WIN32
303 if (http->error != WSAENETDOWN && http->error != WSAENETUNREACH &&
304 http->error != WSAETIMEDOUT)
305 #else
306 if (http->error != ENETDOWN && http->error != ENETUNREACH &&
307 http->error != ETIMEDOUT)
308 #endif /* WIN32 */
309 continue;
310 else
311 break;
312 }
313 #ifdef HAVE_SSL
314 else if (status == HTTP_UPGRADE_REQUIRED)
315 {
316 /* Flush any error message... */
317 httpFlush(http);
318
319 /* Reconnect... */
320 if (httpReconnect(http))
321 {
322 status = HTTP_ERROR;
323 break;
324 }
325
326 /* Upgrade with encryption... */
327 httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
328
329 /* Try again, this time with encryption enabled... */
330 continue;
331 }
332 #endif /* HAVE_SSL */
333 else if (status == HTTP_EXPECTATION_FAILED)
334 {
335 /*
336 * Don't try using the Expect: header the next time around...
337 */
338
339 expect = (http_status_t)0;
340 }
341 else if (status != HTTP_OK)
342 {
343 DEBUG_printf(("cupsDoFileRequest: error %d...\n", status));
344
345 /*
346 * Flush any error message...
347 */
348
349 httpFlush(http);
350 break;
351 }
352 else
353 {
354 /*
355 * Read the response...
356 */
357
358 DEBUG_puts("cupsDoFileRequest: response...");
359
360 response = ippNew();
361
362 while ((state = ippRead(http, response)) != IPP_DATA)
363 if (state == IPP_ERROR)
364 break;
365
366 if (state == IPP_ERROR)
367 {
368 /*
369 * Delete the response...
370 */
371
372 DEBUG_puts("IPP read error!");
373 ippDelete(response);
374 response = NULL;
375
376 _cupsSetError(IPP_SERVICE_UNAVAILABLE, strerror(errno));
377
378 break;
379 }
380 }
381 }
382
383 /*
384 * Close the file if needed...
385 */
386
387 if (filename != NULL)
388 fclose(file);
389
390 /*
391 * Flush any remaining data...
392 */
393
394 httpFlush(http);
395
396 /*
397 * Delete the original request and return the response...
398 */
399
400 ippDelete(request);
401
402 if (response)
403 {
404 ipp_attribute_t *attr; /* status-message attribute */
405
406
407 attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
408
409 _cupsSetError(response->request.status.status_code,
410 attr ? attr->values[0].string.text :
411 ippErrorString(response->request.status.status_code));
412 }
413 else if (status != HTTP_OK)
414 {
415 switch (status)
416 {
417 case HTTP_NOT_FOUND :
418 _cupsSetError(IPP_NOT_FOUND, httpStatus(status));
419 break;
420
421 case HTTP_UNAUTHORIZED :
422 _cupsSetError(IPP_NOT_AUTHORIZED, httpStatus(status));
423 break;
424
425 case HTTP_FORBIDDEN :
426 _cupsSetError(IPP_FORBIDDEN, httpStatus(status));
427 break;
428
429 case HTTP_BAD_REQUEST :
430 _cupsSetError(IPP_BAD_REQUEST, httpStatus(status));
431 break;
432
433 case HTTP_REQUEST_TOO_LARGE :
434 _cupsSetError(IPP_REQUEST_VALUE, httpStatus(status));
435 break;
436
437 case HTTP_NOT_IMPLEMENTED :
438 _cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status));
439 break;
440
441 case HTTP_NOT_SUPPORTED :
442 _cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status));
443 break;
444
445 default :
446 DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
447 status));
448 _cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
449 break;
450 }
451 }
452
453 #ifdef HAVE_AUTHORIZATION_H
454 /*
455 * Delete any authorization reference created for this request...
456 */
457
458 if (cg->auth_ref)
459 {
460 AuthorizationFree(cg->auth_ref, kAuthorizationFlagDefaults);
461 cg->auth_ref = NULL;
462 }
463 #endif /* HAVE_AUTHORIZATION_H */
464
465 return (response);
466 }
467
468
469 /*
470 * 'cupsDoRequest()' - Do an IPP request.
471 *
472 * This function sends the IPP request to the specified server, retrying
473 * and authenticating as necessary. The request is freed with ippDelete()
474 * after receiving a valid IPP response.
475 */
476
477 ipp_t * /* O - Response data */
478 cupsDoRequest(http_t *http, /* I - HTTP connection to server */
479 ipp_t *request, /* I - IPP request */
480 const char *resource) /* I - HTTP resource for POST */
481 {
482 return (cupsDoFileRequest(http, request, resource, NULL));
483 }
484
485
486 /*
487 * '_cupsSetError()' - Set the last IPP status code and status-message.
488 */
489
490 void
491 _cupsSetError(ipp_status_t status, /* I - IPP status code */
492 const char *message) /* I - status-message value */
493 {
494 _cups_globals_t *cg; /* Global data */
495
496
497 cg = _cupsGlobals();
498 cg->last_error = status;
499
500 if (cg->last_status_message)
501 {
502 free(cg->last_status_message);
503
504 cg->last_status_message = NULL;
505 }
506
507 if (message)
508 cg->last_status_message = strdup(message);
509 }
510
511
512 /*
513 * End of "$Id: request.c 6416 2007-03-30 18:30:33Z mike $".
514 */