~ubuntu-branches/ubuntu/wily/cups/wily

« back to all changes in this revision

Viewing changes to .pc/handle-server-terminating-connection.patch/cups/request.c

  • Committer: Package Import Robot
  • Author(s): Till Kamppeter
  • Date: 2013-09-24 22:15:01 UTC
  • mfrom: (99.1.28 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130924221501-115ywce51xe7hhok
* debian/patches/cups-1.6.4-changes.patch: Merged latest fixes from upstream,
  taken from CUPS 1.6.4, as there is no public repository of CUPS any more
  and due to Apple policies the next 1.7.x release, 1.7.0 final, happens
  only with the next release of Mac OS X. Fixes:
   - Removed some duplicate page size definitions for some ISO sizes that were
     causing problems
   - The IPP backend did not add the "last-document" attribute
   - Added a SyncOnClose directive to cups-files.conf to force cupsd to
     call fsync before closing any configuration/state files it writes
     (LP: #1157972, Red Hat bug #984883).
   - Added USB quirk rule for Lexmark E238
   - Closed server connections were still not always detected
   - The libusb-based USB backend now loads its list of quirks from files
     in /usr/share/cups/usb instead of using a hardcoded table, this
     makes spotting and fixing USB problems much easier.
   - The scheduler did not properly register ICC color profiles with
     colord
* debian/patches/usb-backend-more-quirk-rules.patch,
  debian/patches/handle-server-terminating-connection.patch,
  debian/patches/colord-add-profile-fix.patch: Removed, included upstream.
* debian/patches/pidfile.patch,
  debian/patches/rootbackends-worldreadable.patch,
  debian/patches/airprint-support.patch,
  debian/patches/do-not-broadcast-with-hostnames.patch,
  debian/patches/mention-rfc2911-in-ipptoolfile-for-clarity.patch,
  debian/patches/add-ipp-backend-of-cups-1.4.patch,
  debian/patches/confdirperms.patch,
  debian/patches/show-compile-command-lines.patch,
  debian/patches/log-debug-history-nearly-unlimited.patch: Refreshed with
  quilt.
* debian/local/apparmor-profile: Silenced AppArmor noise in syslog
  (LP: #1229766).
* debian/local/cupsd-sync-files-on-close.patch: Activate CUPS daemon
  syncing files when closing, so that config files (like printers.conf)
  do not mysteriously disappear (LP: #1157972, Red Hat bug #984883).
* debian/cups-server-common.install: Install /usr/share/cups/usb/ with the
  USB backend quirk rules file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * "$Id: request.c 10996 2013-05-29 11:51:34Z msweet $"
3
 
 *
4
 
 *   IPP utilities for CUPS.
5
 
 *
6
 
 *   Copyright 2007-2013 by Apple Inc.
7
 
 *   Copyright 1997-2007 by Easy Software Products.
8
 
 *
9
 
 *   These coded instructions, statements, and computer programs are the
10
 
 *   property of Apple Inc. and are protected by Federal copyright
11
 
 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12
 
 *   which should have been included with this file.  If this file is
13
 
 *   file is missing or damaged, see the license at "http://www.cups.org/".
14
 
 *
15
 
 *   This file is subject to the Apple OS-Developed Software exception.
16
 
 *
17
 
 * Contents:
18
 
 *
19
 
 *   cupsDoFileRequest()    - Do an IPP request with a file.
20
 
 *   cupsDoIORequest()      - Do an IPP request with file descriptors.
21
 
 *   cupsDoRequest()        - Do an IPP request.
22
 
 *   cupsGetResponse()      - Get a response to an IPP request.
23
 
 *   cupsLastError()        - Return the last IPP status code.
24
 
 *   cupsLastErrorString()  - Return the last IPP status-message.
25
 
 *   _cupsNextDelay()       - Return the next retry delay value.
26
 
 *   cupsReadResponseData() - Read additional data after the IPP response.
27
 
 *   cupsSendRequest()      - Send an IPP request.
28
 
 *   cupsWriteRequestData() - Write additional data after an IPP request.
29
 
 *   _cupsConnect()         - Get the default server connection...
30
 
 *   _cupsSetError()        - Set the last IPP status code and status-message.
31
 
 *   _cupsSetHTTPError()    - Set the last error using the HTTP status.
32
 
 */
33
 
 
34
 
/*
35
 
 * Include necessary headers...
36
 
 */
37
 
 
38
 
#include "cups-private.h"
39
 
#include <fcntl.h>
40
 
#include <sys/stat.h>
41
 
#if defined(WIN32) || defined(__EMX__)
42
 
#  include <io.h>
43
 
#else
44
 
#  include <unistd.h>
45
 
#endif /* WIN32 || __EMX__ */
46
 
#ifndef O_BINARY
47
 
#  define O_BINARY 0
48
 
#endif /* O_BINARY */
49
 
#ifndef MSG_DONTWAIT
50
 
#  define MSG_DONTWAIT 0
51
 
#endif /* !MSG_DONTWAIT */
52
 
 
53
 
 
54
 
/*
55
 
 * 'cupsDoFileRequest()' - Do an IPP request with a file.
56
 
 *
57
 
 * This function sends the IPP request and attached file to the specified
58
 
 * server, retrying and authenticating as necessary.  The request is freed with
59
 
 * @link ippDelete@.
60
 
 */
61
 
 
62
 
ipp_t *                                 /* O - Response data */
63
 
cupsDoFileRequest(http_t     *http,     /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
64
 
                  ipp_t      *request,  /* I - IPP request */
65
 
                  const char *resource, /* I - HTTP resource for POST */
66
 
                  const char *filename) /* I - File to send or @code NULL@ for none */
67
 
{
68
 
  ipp_t         *response;              /* IPP response data */
69
 
  int           infile;                 /* Input file */
70
 
 
71
 
 
72
 
  DEBUG_printf(("cupsDoFileRequest(http=%p, request=%p(%s), resource=\"%s\", "
73
 
                "filename=\"%s\")", http, request,
74
 
                request ? ippOpString(request->request.op.operation_id) : "?",
75
 
                resource, filename));
76
 
 
77
 
  if (filename)
78
 
  {
79
 
    if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0)
80
 
    {
81
 
     /*
82
 
      * Can't get file information!
83
 
      */
84
 
 
85
 
      _cupsSetError(errno == ENOENT ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED,
86
 
                    NULL, 0);
87
 
 
88
 
      ippDelete(request);
89
 
 
90
 
      return (NULL);
91
 
    }
92
 
  }
93
 
  else
94
 
    infile = -1;
95
 
 
96
 
  response = cupsDoIORequest(http, request, resource, infile, -1);
97
 
 
98
 
  if (infile >= 0)
99
 
    close(infile);
100
 
 
101
 
  return (response);
102
 
}
103
 
 
104
 
 
105
 
/*
106
 
 * 'cupsDoIORequest()' - Do an IPP request with file descriptors.
107
 
 *
108
 
 * This function sends the IPP request with the optional input file "infile" to
109
 
 * the specified server, retrying and authenticating as necessary.  The request
110
 
 * is freed with @link ippDelete@.
111
 
 *
112
 
 * If "infile" is a valid file descriptor, @code cupsDoIORequest@ copies
113
 
 * all of the data from the file after the IPP request message.
114
 
 *
115
 
 * If "outfile" is a valid file descriptor, @code cupsDoIORequest@ copies
116
 
 * all of the data after the IPP response message to the file.
117
 
 *
118
 
 * @since CUPS 1.3/OS X 10.5@
119
 
 */
120
 
 
121
 
ipp_t *                                 /* O - Response data */
122
 
cupsDoIORequest(http_t     *http,       /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
123
 
                ipp_t      *request,    /* I - IPP request */
124
 
                const char *resource,   /* I - HTTP resource for POST */
125
 
                int        infile,      /* I - File to read from or -1 for none */
126
 
                int        outfile)     /* I - File to write to or -1 for none */
127
 
{
128
 
  ipp_t         *response = NULL;       /* IPP response data */
129
 
  size_t        length = 0;             /* Content-Length value */
130
 
  http_status_t status;                 /* Status of HTTP request */
131
 
  struct stat   fileinfo;               /* File information */
132
 
  int           bytes;                  /* Number of bytes read/written */
133
 
  char          buffer[32768];          /* Output buffer */
134
 
 
135
 
 
136
 
  DEBUG_printf(("cupsDoIORequest(http=%p, request=%p(%s), resource=\"%s\", "
137
 
                "infile=%d, outfile=%d)", http, request,
138
 
                request ? ippOpString(request->request.op.operation_id) : "?",
139
 
                resource, infile, outfile));
140
 
 
141
 
 /*
142
 
  * Range check input...
143
 
  */
144
 
 
145
 
  if (!request || !resource)
146
 
  {
147
 
    ippDelete(request);
148
 
 
149
 
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
150
 
 
151
 
    return (NULL);
152
 
  }
153
 
 
154
 
 /*
155
 
  * Get the default connection as needed...
156
 
  */
157
 
 
158
 
  if (!http)
159
 
    if ((http = _cupsConnect()) == NULL)
160
 
    {
161
 
      ippDelete(request);
162
 
 
163
 
      return (NULL);
164
 
    }
165
 
 
166
 
 /*
167
 
  * See if we have a file to send...
168
 
  */
169
 
 
170
 
  if (infile >= 0)
171
 
  {
172
 
    if (fstat(infile, &fileinfo))
173
 
    {
174
 
     /*
175
 
      * Can't get file information!
176
 
      */
177
 
 
178
 
      _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED,
179
 
                    NULL, 0);
180
 
 
181
 
      ippDelete(request);
182
 
 
183
 
      return (NULL);
184
 
    }
185
 
 
186
 
#ifdef WIN32
187
 
    if (fileinfo.st_mode & _S_IFDIR)
188
 
#else
189
 
    if (S_ISDIR(fileinfo.st_mode))
190
 
#endif /* WIN32 */
191
 
    {
192
 
     /*
193
 
      * Can't send a directory...
194
 
      */
195
 
 
196
 
      ippDelete(request);
197
 
 
198
 
      _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), 0);
199
 
 
200
 
      return (NULL);
201
 
    }
202
 
 
203
 
#ifndef WIN32
204
 
    if (!S_ISREG(fileinfo.st_mode))
205
 
      length = 0;                       /* Chunk when piping */
206
 
    else
207
 
#endif /* !WIN32 */
208
 
    length = ippLength(request) + fileinfo.st_size;
209
 
  }
210
 
  else
211
 
    length = ippLength(request);
212
 
 
213
 
  DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld",
214
 
                (long)ippLength(request), (long)length));
215
 
 
216
 
 /*
217
 
  * Clear any "Local" authentication data since it is probably stale...
218
 
  */
219
 
 
220
 
  if (http->authstring && !strncmp(http->authstring, "Local ", 6))
221
 
    httpSetAuthString(http, NULL, NULL);
222
 
 
223
 
 /*
224
 
  * Loop until we can send the request without authorization problems.
225
 
  */
226
 
 
227
 
  while (response == NULL)
228
 
  {
229
 
    DEBUG_puts("2cupsDoIORequest: setup...");
230
 
 
231
 
   /*
232
 
    * Send the request...
233
 
    */
234
 
 
235
 
    status = cupsSendRequest(http, request, resource, length);
236
 
 
237
 
    DEBUG_printf(("2cupsDoIORequest: status=%d", status));
238
 
 
239
 
    if (status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA && infile >= 0)
240
 
    {
241
 
      DEBUG_puts("2cupsDoIORequest: file write...");
242
 
 
243
 
     /*
244
 
      * Send the file with the request...
245
 
      */
246
 
 
247
 
#ifndef WIN32
248
 
      if (S_ISREG(fileinfo.st_mode))
249
 
#endif /* WIN32 */
250
 
      lseek(infile, 0, SEEK_SET);
251
 
 
252
 
      while ((bytes = (int)read(infile, buffer, sizeof(buffer))) > 0)
253
 
      {
254
 
        if ((status = cupsWriteRequestData(http, buffer, bytes))
255
 
                != HTTP_STATUS_CONTINUE)
256
 
          break;
257
 
      }
258
 
    }
259
 
 
260
 
   /*
261
 
    * Get the server's response...
262
 
    */
263
 
 
264
 
    if (status != HTTP_STATUS_ERROR)
265
 
    {
266
 
      response = cupsGetResponse(http, resource);
267
 
      status   = httpGetStatus(http);
268
 
    }
269
 
 
270
 
    DEBUG_printf(("2cupsDoIORequest: status=%d", status));
271
 
 
272
 
    if (status == HTTP_STATUS_ERROR ||
273
 
        (status >= HTTP_STATUS_BAD_REQUEST && status != HTTP_STATUS_UNAUTHORIZED &&
274
 
         status != HTTP_STATUS_UPGRADE_REQUIRED))
275
 
    {
276
 
      _cupsSetHTTPError(status);
277
 
      break;
278
 
    }
279
 
 
280
 
    if (response && outfile >= 0)
281
 
    {
282
 
     /*
283
 
      * Write trailing data to file...
284
 
      */
285
 
 
286
 
      while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0)
287
 
        if (write(outfile, buffer, bytes) < bytes)
288
 
          break;
289
 
    }
290
 
 
291
 
    if (http->state != HTTP_STATE_WAITING)
292
 
    {
293
 
     /*
294
 
      * Flush any remaining data...
295
 
      */
296
 
 
297
 
      httpFlush(http);
298
 
    }
299
 
  }
300
 
 
301
 
 /*
302
 
  * Delete the original request and return the response...
303
 
  */
304
 
 
305
 
  ippDelete(request);
306
 
 
307
 
  return (response);
308
 
}
309
 
 
310
 
 
311
 
/*
312
 
 * 'cupsDoRequest()' - Do an IPP request.
313
 
 *
314
 
 * This function sends the IPP request to the specified server, retrying
315
 
 * and authenticating as necessary.  The request is freed with @link ippDelete@.
316
 
 */
317
 
 
318
 
ipp_t *                                 /* O - Response data */
319
 
cupsDoRequest(http_t     *http,         /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
320
 
              ipp_t      *request,      /* I - IPP request */
321
 
              const char *resource)     /* I - HTTP resource for POST */
322
 
{
323
 
  DEBUG_printf(("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")",
324
 
                http, request,
325
 
                request ? ippOpString(request->request.op.operation_id) : "?",
326
 
                resource));
327
 
 
328
 
  return (cupsDoIORequest(http, request, resource, -1, -1));
329
 
}
330
 
 
331
 
 
332
 
/*
333
 
 * 'cupsGetResponse()' - Get a response to an IPP request.
334
 
 *
335
 
 * Use this function to get the response for an IPP request sent using
336
 
 * @link cupsSendRequest@. For requests that return additional data, use
337
 
 * @link cupsReadResponseData@ after getting a successful response,
338
 
 * otherwise call @link httpFlush@ to complete the response processing.
339
 
 *
340
 
 * @since CUPS 1.4/OS X 10.6@
341
 
 */
342
 
 
343
 
ipp_t *                                 /* O - Response or @code NULL@ on HTTP error */
344
 
cupsGetResponse(http_t     *http,       /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
345
 
                const char *resource)   /* I - HTTP resource for POST */
346
 
{
347
 
  http_status_t status;                 /* HTTP status */
348
 
  ipp_state_t   state;                  /* IPP read state */
349
 
  ipp_t         *response = NULL;       /* IPP response */
350
 
 
351
 
 
352
 
  DEBUG_printf(("cupsGetResponse(http=%p, resource=\"%s\")", http, resource));
353
 
 
354
 
 /*
355
 
  * Connect to the default server as needed...
356
 
  */
357
 
 
358
 
  if (!http)
359
 
    http = _cupsConnect();
360
 
 
361
 
  if (!http || (http->state != HTTP_STATE_POST_RECV &&
362
 
                http->state != HTTP_STATE_POST_SEND))
363
 
    return (NULL);
364
 
 
365
 
 /*
366
 
  * Check for an unfinished chunked request...
367
 
  */
368
 
 
369
 
  if (http->data_encoding == HTTP_ENCODING_CHUNKED)
370
 
  {
371
 
   /*
372
 
    * Send a 0-length chunk to finish off the request...
373
 
    */
374
 
 
375
 
    DEBUG_puts("2cupsGetResponse: Finishing chunked POST...");
376
 
 
377
 
    if (httpWrite2(http, "", 0) < 0)
378
 
      return (NULL);
379
 
  }
380
 
 
381
 
 /*
382
 
  * Wait for a response from the server...
383
 
  */
384
 
 
385
 
  DEBUG_printf(("2cupsGetResponse: Update loop, http->status=%d...",
386
 
                http->status));
387
 
 
388
 
  do
389
 
  {
390
 
    status = httpUpdate(http);
391
 
  }
392
 
  while (status == HTTP_STATUS_CONTINUE);
393
 
 
394
 
  DEBUG_printf(("2cupsGetResponse: status=%d", status));
395
 
 
396
 
  if (status == HTTP_STATUS_OK)
397
 
  {
398
 
   /*
399
 
    * Get the IPP response...
400
 
    */
401
 
 
402
 
    response = ippNew();
403
 
 
404
 
    while ((state = ippRead(http, response)) != IPP_STATE_DATA)
405
 
      if (state == IPP_STATE_ERROR)
406
 
        break;
407
 
 
408
 
    if (state == IPP_STATE_ERROR)
409
 
    {
410
 
     /*
411
 
      * Flush remaining data and delete the response...
412
 
      */
413
 
 
414
 
      DEBUG_puts("1cupsGetResponse: IPP read error!");
415
 
 
416
 
      httpFlush(http);
417
 
 
418
 
      ippDelete(response);
419
 
      response = NULL;
420
 
 
421
 
      http->status = status = HTTP_STATUS_ERROR;
422
 
      http->error  = EINVAL;
423
 
    }
424
 
  }
425
 
  else if (status != HTTP_STATUS_ERROR)
426
 
  {
427
 
   /*
428
 
    * Flush any error message...
429
 
    */
430
 
 
431
 
    httpFlush(http);
432
 
 
433
 
   /*
434
 
    * Then handle encryption and authentication...
435
 
    */
436
 
 
437
 
    if (status == HTTP_STATUS_UNAUTHORIZED)
438
 
    {
439
 
     /*
440
 
      * See if we can do authentication...
441
 
      */
442
 
 
443
 
      DEBUG_puts("2cupsGetResponse: Need authorization...");
444
 
 
445
 
      if (!cupsDoAuthentication(http, "POST", resource))
446
 
        httpReconnect2(http, 30000, NULL);
447
 
      else
448
 
        http->status = status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
449
 
    }
450
 
 
451
 
#ifdef HAVE_SSL
452
 
    else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
453
 
    {
454
 
     /*
455
 
      * Force a reconnect with encryption...
456
 
      */
457
 
 
458
 
      DEBUG_puts("2cupsGetResponse: Need encryption...");
459
 
 
460
 
      if (!httpReconnect2(http, 30000, NULL))
461
 
        httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
462
 
    }
463
 
#endif /* HAVE_SSL */
464
 
  }
465
 
 
466
 
  if (response)
467
 
  {
468
 
    ipp_attribute_t     *attr;          /* status-message attribute */
469
 
 
470
 
 
471
 
    attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
472
 
 
473
 
    DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"",
474
 
                  ippErrorString(response->request.status.status_code),
475
 
                  attr ? attr->values[0].string.text : ""));
476
 
 
477
 
    _cupsSetError(response->request.status.status_code,
478
 
                  attr ? attr->values[0].string.text :
479
 
                      ippErrorString(response->request.status.status_code), 0);
480
 
  }
481
 
 
482
 
  return (response);
483
 
}
484
 
 
485
 
 
486
 
/*
487
 
 * 'cupsLastError()' - Return the last IPP status code received on the current
488
 
 *                     thread.
489
 
 */
490
 
 
491
 
ipp_status_t                            /* O - IPP status code from last request */
492
 
cupsLastError(void)
493
 
{
494
 
  return (_cupsGlobals()->last_error);
495
 
}
496
 
 
497
 
 
498
 
/*
499
 
 * 'cupsLastErrorString()' - Return the last IPP status-message received on the
500
 
 *                           current thread.
501
 
 *
502
 
 * @since CUPS 1.2/OS X 10.5@
503
 
 */
504
 
 
505
 
const char *                            /* O - status-message text from last request */
506
 
cupsLastErrorString(void)
507
 
{
508
 
  return (_cupsGlobals()->last_status_message);
509
 
}
510
 
 
511
 
 
512
 
/*
513
 
 * '_cupsNextDelay()' - Return the next retry delay value.
514
 
 *
515
 
 * This function currently returns the Fibonacci sequence 1 1 2 3 5 8.
516
 
 *
517
 
 * Pass 0 for the current delay value to initialize the sequence.
518
 
 */
519
 
 
520
 
int                                     /* O  - Next delay value */
521
 
_cupsNextDelay(int current,             /* I  - Current delay value or 0 */
522
 
               int *previous)           /* IO - Previous delay value */
523
 
{
524
 
  int   next;                           /* Next delay value */
525
 
 
526
 
 
527
 
  if (current > 0)
528
 
  {
529
 
    next      = (current + *previous) % 12;
530
 
    *previous = next < current ? 0 : current;
531
 
  }
532
 
  else
533
 
  {
534
 
    next      = 1;
535
 
    *previous = 0;
536
 
  }
537
 
 
538
 
  return (next);
539
 
}
540
 
 
541
 
 
542
 
/*
543
 
 * 'cupsReadResponseData()' - Read additional data after the IPP response.
544
 
 *
545
 
 * This function is used after @link cupsGetResponse@ to read the PPD or document
546
 
 * files from @code CUPS_GET_PPD@ and @code CUPS_GET_DOCUMENT@ requests,
547
 
 * respectively.
548
 
 *
549
 
 * @since CUPS 1.4/OS X 10.6@
550
 
 */
551
 
 
552
 
ssize_t                                 /* O - Bytes read, 0 on EOF, -1 on error */
553
 
cupsReadResponseData(
554
 
    http_t *http,                       /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
555
 
    char   *buffer,                     /* I - Buffer to use */
556
 
    size_t length)                      /* I - Number of bytes to read */
557
 
{
558
 
 /*
559
 
  * Get the default connection as needed...
560
 
  */
561
 
 
562
 
  DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, "
563
 
                "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
564
 
 
565
 
  if (!http)
566
 
  {
567
 
    _cups_globals_t *cg = _cupsGlobals();
568
 
                                        /* Pointer to library globals */
569
 
 
570
 
    if ((http = cg->http) == NULL)
571
 
    {
572
 
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1);
573
 
      return (-1);
574
 
    }
575
 
  }
576
 
 
577
 
 /*
578
 
  * Then read from the HTTP connection...
579
 
  */
580
 
 
581
 
  return (httpRead2(http, buffer, length));
582
 
}
583
 
 
584
 
 
585
 
/*
586
 
 * 'cupsSendRequest()' - Send an IPP request.
587
 
 *
588
 
 * Use @link cupsWriteRequestData@ to write any additional data (document, PPD
589
 
 * file, etc.) for the request, @link cupsGetResponse@ to get the IPP response,
590
 
 * and @link cupsReadResponseData@ to read any additional data following the
591
 
 * response. Only one request can be sent/queued at a time per @code http_t@
592
 
 * connection.
593
 
 *
594
 
 * Returns the initial HTTP status code, which will be @code HTTP_STATUS_CONTINUE@
595
 
 * on a successful send of the request.
596
 
 *
597
 
 * Note: Unlike @link cupsDoFileRequest@, @link cupsDoIORequest@, and
598
 
 * @link cupsDoRequest@, the request is NOT freed with @link ippDelete@.
599
 
 *
600
 
 * @since CUPS 1.4/OS X 10.6@
601
 
 */
602
 
 
603
 
http_status_t                           /* O - Initial HTTP status */
604
 
cupsSendRequest(http_t     *http,       /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
605
 
                ipp_t      *request,    /* I - IPP request */
606
 
                const char *resource,   /* I - Resource path */
607
 
                size_t     length)      /* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */
608
 
{
609
 
  http_status_t         status;         /* Status of HTTP request */
610
 
  int                   got_status;     /* Did we get the status? */
611
 
  ipp_state_t           state;          /* State of IPP processing */
612
 
  http_status_t         expect;         /* Expect: header to use */
613
 
 
614
 
 
615
 
  DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", "
616
 
                "length=" CUPS_LLFMT ")", http, request,
617
 
                request ? ippOpString(request->request.op.operation_id) : "?",
618
 
                resource, CUPS_LLCAST length));
619
 
 
620
 
 /*
621
 
  * Range check input...
622
 
  */
623
 
 
624
 
  if (!request || !resource)
625
 
  {
626
 
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
627
 
 
628
 
    return (HTTP_STATUS_ERROR);
629
 
  }
630
 
 
631
 
 /*
632
 
  * Get the default connection as needed...
633
 
  */
634
 
 
635
 
  if (!http)
636
 
    if ((http = _cupsConnect()) == NULL)
637
 
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
638
 
 
639
 
 /*
640
 
  * If the prior request was not flushed out, do so now...
641
 
  */
642
 
 
643
 
  if (http->state == HTTP_STATE_GET_SEND ||
644
 
      http->state == HTTP_STATE_POST_SEND)
645
 
  {
646
 
    DEBUG_puts("2cupsSendRequest: Flush prior response.");
647
 
    httpFlush(http);
648
 
  }
649
 
  else if (http->state != HTTP_STATE_WAITING)
650
 
  {
651
 
    DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), "
652
 
                  "reconnecting.", http->state));
653
 
    if (httpReconnect2(http, 30000, NULL))
654
 
      return (HTTP_STATUS_ERROR);
655
 
  }
656
 
 
657
 
#ifdef HAVE_SSL
658
 
 /*
659
 
  * See if we have an auth-info attribute and are communicating over
660
 
  * a non-local link.  If so, encrypt the link so that we can pass
661
 
  * the authentication information securely...
662
 
  */
663
 
 
664
 
  if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
665
 
      !httpAddrLocalhost(http->hostaddr) && !http->tls &&
666
 
      httpEncryption(http, HTTP_ENCRYPTION_REQUIRED))
667
 
  {
668
 
    DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
669
 
    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
670
 
  }
671
 
#endif /* HAVE_SSL */
672
 
 
673
 
 /*
674
 
  * Reconnect if the last response had a "Connection: close"...
675
 
  */
676
 
 
677
 
  if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close"))
678
 
  {
679
 
    DEBUG_puts("2cupsSendRequest: Connection: close");
680
 
    httpClearFields(http);
681
 
    if (httpReconnect2(http, 30000, NULL))
682
 
    {
683
 
      DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
684
 
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
685
 
    }
686
 
  }
687
 
 
688
 
 /*
689
 
  * Loop until we can send the request without authorization problems.
690
 
  */
691
 
 
692
 
  expect = HTTP_STATUS_CONTINUE;
693
 
 
694
 
  for (;;)
695
 
  {
696
 
    DEBUG_puts("2cupsSendRequest: Setup...");
697
 
 
698
 
   /*
699
 
    * Setup the HTTP variables needed...
700
 
    */
701
 
 
702
 
    httpClearFields(http);
703
 
    httpSetExpect(http, expect);
704
 
    httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
705
 
    httpSetLength(http, length);
706
 
 
707
 
#ifdef HAVE_GSSAPI
708
 
    if (http->authstring && !strncmp(http->authstring, "Negotiate", 9))
709
 
    {
710
 
     /*
711
 
      * Do not use cached Kerberos credentials since they will look like a
712
 
      * "replay" attack...
713
 
      */
714
 
 
715
 
      _cupsSetNegotiateAuthString(http, "POST", resource);
716
 
    }
717
 
#endif /* HAVE_GSSAPI */
718
 
 
719
 
    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
720
 
 
721
 
    DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring));
722
 
 
723
 
   /*
724
 
    * Try the request...
725
 
    */
726
 
 
727
 
    DEBUG_puts("2cupsSendRequest: Sending HTTP POST...");
728
 
 
729
 
    if (httpPost(http, resource))
730
 
    {
731
 
      DEBUG_puts("2cupsSendRequest: POST failed, reconnecting.");
732
 
      if (httpReconnect2(http, 30000, NULL))
733
 
      {
734
 
        DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
735
 
        return (HTTP_STATUS_SERVICE_UNAVAILABLE);
736
 
      }
737
 
      else
738
 
        continue;
739
 
    }
740
 
 
741
 
   /*
742
 
    * Send the IPP data...
743
 
    */
744
 
 
745
 
    DEBUG_puts("2cupsSendRequest: Writing IPP request...");
746
 
 
747
 
    request->state = IPP_STATE_IDLE;
748
 
    status         = HTTP_STATUS_CONTINUE;
749
 
    got_status     = 0;
750
 
 
751
 
    while ((state = ippWrite(http, request)) != IPP_STATE_DATA)
752
 
      if (state == IPP_STATE_ERROR)
753
 
        break;
754
 
      else if (httpCheck(http))
755
 
      {
756
 
        got_status = 1;
757
 
 
758
 
        _httpUpdate(http, &status);
759
 
        if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
760
 
          break;
761
 
      }
762
 
 
763
 
    if (state == IPP_STATE_ERROR)
764
 
    {
765
 
      DEBUG_puts("1cupsSendRequest: Unable to send IPP request.");
766
 
 
767
 
      http->status = HTTP_STATUS_ERROR;
768
 
      http->state  = HTTP_STATE_WAITING;
769
 
 
770
 
      return (HTTP_STATUS_ERROR);
771
 
    }
772
 
 
773
 
   /*
774
 
    * Wait up to 1 second to get the 100-continue response as needed...
775
 
    */
776
 
 
777
 
    if (!got_status)
778
 
    {
779
 
      if (expect == HTTP_STATUS_CONTINUE)
780
 
      {
781
 
        DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
782
 
 
783
 
        if (httpWait(http, 1000))
784
 
          _httpUpdate(http, &status);
785
 
      }
786
 
      else if (httpCheck(http))
787
 
        _httpUpdate(http, &status);
788
 
    }
789
 
 
790
 
    DEBUG_printf(("2cupsSendRequest: status=%d", status));
791
 
 
792
 
   /*
793
 
    * Process the current HTTP status...
794
 
    */
795
 
 
796
 
    if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
797
 
    {
798
 
      int temp_status;                  /* Temporary status */
799
 
 
800
 
      _cupsSetHTTPError(status);
801
 
 
802
 
      do
803
 
      {
804
 
        temp_status = httpUpdate(http);
805
 
      }
806
 
      while (temp_status != HTTP_STATUS_ERROR &&
807
 
             http->state == HTTP_STATE_POST_RECV);
808
 
 
809
 
      httpFlush(http);
810
 
    }
811
 
 
812
 
    switch (status)
813
 
    {
814
 
      case HTTP_STATUS_CONTINUE :
815
 
      case HTTP_STATUS_OK :
816
 
      case HTTP_STATUS_ERROR :
817
 
          DEBUG_printf(("1cupsSendRequest: Returning %d.", status));
818
 
          return (status);
819
 
 
820
 
      case HTTP_STATUS_UNAUTHORIZED :
821
 
          if (cupsDoAuthentication(http, "POST", resource))
822
 
          {
823
 
            DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED.");
824
 
            return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED);
825
 
          }
826
 
 
827
 
          DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED.");
828
 
 
829
 
          if (httpReconnect2(http, 30000, NULL))
830
 
          {
831
 
            DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
832
 
            return (HTTP_STATUS_SERVICE_UNAVAILABLE);
833
 
          }
834
 
          break;
835
 
 
836
 
#ifdef HAVE_SSL
837
 
      case HTTP_STATUS_UPGRADE_REQUIRED :
838
 
         /*
839
 
          * Flush any error message, reconnect, and then upgrade with
840
 
          * encryption...
841
 
          */
842
 
 
843
 
          DEBUG_puts("2cupsSendRequest: Reconnecting after "
844
 
                     "HTTP_STATUS_UPGRADE_REQUIRED.");
845
 
 
846
 
          if (httpReconnect2(http, 30000, NULL))
847
 
          {
848
 
            DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
849
 
            return (HTTP_STATUS_SERVICE_UNAVAILABLE);
850
 
          }
851
 
 
852
 
          DEBUG_puts("2cupsSendRequest: Upgrading to TLS.");
853
 
          if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED))
854
 
          {
855
 
            DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
856
 
            return (HTTP_STATUS_SERVICE_UNAVAILABLE);
857
 
          }
858
 
          break;
859
 
#endif /* HAVE_SSL */
860
 
 
861
 
      case HTTP_STATUS_EXPECTATION_FAILED :
862
 
         /*
863
 
          * Don't try using the Expect: header the next time around...
864
 
          */
865
 
 
866
 
          expect = (http_status_t)0;
867
 
 
868
 
          DEBUG_puts("2cupsSendRequest: Reconnecting after "
869
 
                     "HTTP_EXPECTATION_FAILED.");
870
 
 
871
 
          if (httpReconnect2(http, 30000, NULL))
872
 
          {
873
 
            DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
874
 
            return (HTTP_STATUS_SERVICE_UNAVAILABLE);
875
 
          }
876
 
          break;
877
 
 
878
 
      default :
879
 
         /*
880
 
          * Some other error...
881
 
          */
882
 
 
883
 
          return (status);
884
 
    }
885
 
  }
886
 
}
887
 
 
888
 
 
889
 
/*
890
 
 * 'cupsWriteRequestData()' - Write additional data after an IPP request.
891
 
 *
892
 
 * This function is used after @link cupsSendRequest@ to provide a PPD and
893
 
 * after @link cupsStartDocument@ to provide a document file.
894
 
 *
895
 
 * @since CUPS 1.4/OS X 10.6@
896
 
 */
897
 
 
898
 
http_status_t                           /* O - @code HTTP_STATUS_CONTINUE@ if OK or HTTP status on error */
899
 
cupsWriteRequestData(
900
 
    http_t     *http,                   /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
901
 
    const char *buffer,                 /* I - Bytes to write */
902
 
    size_t     length)                  /* I - Number of bytes to write */
903
 
{
904
 
  int   wused;                          /* Previous bytes in buffer */
905
 
 
906
 
 
907
 
 /*
908
 
  * Get the default connection as needed...
909
 
  */
910
 
 
911
 
  DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, "
912
 
                "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
913
 
 
914
 
  if (!http)
915
 
  {
916
 
    _cups_globals_t *cg = _cupsGlobals();
917
 
                                        /* Pointer to library globals */
918
 
 
919
 
    if ((http = cg->http) == NULL)
920
 
    {
921
 
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1);
922
 
      DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
923
 
      return (HTTP_STATUS_ERROR);
924
 
    }
925
 
  }
926
 
 
927
 
 /*
928
 
  * Then write to the HTTP connection...
929
 
  */
930
 
 
931
 
  wused = http->wused;
932
 
 
933
 
  if (httpWrite2(http, buffer, length) < 0)
934
 
  {
935
 
    DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
936
 
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0);
937
 
    return (HTTP_STATUS_ERROR);
938
 
  }
939
 
 
940
 
 /*
941
 
  * Finally, check if we have any pending data from the server...
942
 
  */
943
 
 
944
 
  if (length >= HTTP_MAX_BUFFER ||
945
 
      http->wused < wused ||
946
 
      (wused > 0 && http->wused == length))
947
 
  {
948
 
   /*
949
 
    * We've written something to the server, so check for response data...
950
 
    */
951
 
 
952
 
    if (_httpWait(http, 0, 1))
953
 
    {
954
 
      http_status_t     status;         /* Status from _httpUpdate */
955
 
 
956
 
      _httpUpdate(http, &status);
957
 
      if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
958
 
      {
959
 
        _cupsSetHTTPError(status);
960
 
 
961
 
        do
962
 
        {
963
 
          status = httpUpdate(http);
964
 
        }
965
 
        while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV);
966
 
 
967
 
        httpFlush(http);
968
 
      }
969
 
 
970
 
      DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status));
971
 
      return (status);
972
 
    }
973
 
  }
974
 
 
975
 
  DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE.");
976
 
  return (HTTP_STATUS_CONTINUE);
977
 
}
978
 
 
979
 
 
980
 
/*
981
 
 * '_cupsConnect()' - Get the default server connection...
982
 
 */
983
 
 
984
 
http_t *                                /* O - HTTP connection */
985
 
_cupsConnect(void)
986
 
{
987
 
  _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
988
 
 
989
 
 
990
 
 /*
991
 
  * See if we are connected to the same server...
992
 
  */
993
 
 
994
 
  if (cg->http)
995
 
  {
996
 
   /*
997
 
    * Compare the connection hostname, port, and encryption settings to
998
 
    * the cached defaults; these were initialized the first time we
999
 
    * connected...
1000
 
    */
1001
 
 
1002
 
    if (strcmp(cg->http->hostname, cg->server) ||
1003
 
        cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
1004
 
        (cg->http->encryption != cg->encryption &&
1005
 
         cg->http->encryption == HTTP_ENCRYPTION_NEVER))
1006
 
    {
1007
 
     /*
1008
 
      * Need to close the current connection because something has changed...
1009
 
      */
1010
 
 
1011
 
      httpClose(cg->http);
1012
 
      cg->http = NULL;
1013
 
    }
1014
 
    else
1015
 
    {
1016
 
     /*
1017
 
      * Same server, see if the connection is still established...
1018
 
      */
1019
 
 
1020
 
      char ch;                          /* Connection check byte */
1021
 
 
1022
 
#ifdef WIN32
1023
 
      if (recv(cg->http->fd, &ch, 1, MSG_PEEK) < 0 &&
1024
 
          WSAGetLastError() != WSAEWOULDBLOCK)
1025
 
#else
1026
 
      if (recv(cg->http->fd, &ch, 1, MSG_PEEK | MSG_DONTWAIT) < 0 &&
1027
 
          errno != EWOULDBLOCK)
1028
 
#endif /* WIN32 */
1029
 
      {
1030
 
       /*
1031
 
        * Nope, close the connection...
1032
 
        */
1033
 
 
1034
 
        httpClose(cg->http);
1035
 
        cg->http = NULL;
1036
 
      }
1037
 
    }
1038
 
  }
1039
 
 
1040
 
 /*
1041
 
  * (Re)connect as needed...
1042
 
  */
1043
 
 
1044
 
  if (!cg->http)
1045
 
  {
1046
 
    if ((cg->http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
1047
 
                                 cupsEncryption(), 1, 30000, NULL)) == NULL)
1048
 
    {
1049
 
      if (errno)
1050
 
        _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, 0);
1051
 
      else
1052
 
        _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE,
1053
 
                      _("Unable to connect to host."), 1);
1054
 
    }
1055
 
  }
1056
 
 
1057
 
 /*
1058
 
  * Return the cached connection...
1059
 
  */
1060
 
 
1061
 
  return (cg->http);
1062
 
}
1063
 
 
1064
 
 
1065
 
/*
1066
 
 * '_cupsSetError()' - Set the last IPP status code and status-message.
1067
 
 */
1068
 
 
1069
 
void
1070
 
_cupsSetError(ipp_status_t status,      /* I - IPP status code */
1071
 
              const char   *message,    /* I - status-message value */
1072
 
              int          localize)    /* I - Localize the message? */
1073
 
{
1074
 
  _cups_globals_t       *cg;            /* Global data */
1075
 
 
1076
 
 
1077
 
  if (!message && errno)
1078
 
  {
1079
 
    message  = strerror(errno);
1080
 
    localize = 0;
1081
 
  }
1082
 
 
1083
 
  cg             = _cupsGlobals();
1084
 
  cg->last_error = status;
1085
 
 
1086
 
  if (cg->last_status_message)
1087
 
  {
1088
 
    _cupsStrFree(cg->last_status_message);
1089
 
 
1090
 
    cg->last_status_message = NULL;
1091
 
  }
1092
 
 
1093
 
  if (message)
1094
 
  {
1095
 
    if (localize)
1096
 
    {
1097
 
     /*
1098
 
      * Get the message catalog...
1099
 
      */
1100
 
 
1101
 
      if (!cg->lang_default)
1102
 
        cg->lang_default = cupsLangDefault();
1103
 
 
1104
 
      cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default,
1105
 
                                                              message));
1106
 
    }
1107
 
    else
1108
 
      cg->last_status_message = _cupsStrAlloc(message);
1109
 
  }
1110
 
 
1111
 
  DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"",
1112
 
                ippErrorString(cg->last_error), cg->last_status_message));
1113
 
}
1114
 
 
1115
 
 
1116
 
/*
1117
 
 * '_cupsSetHTTPError()' - Set the last error using the HTTP status.
1118
 
 */
1119
 
 
1120
 
void
1121
 
_cupsSetHTTPError(http_status_t status) /* I - HTTP status code */
1122
 
{
1123
 
  switch (status)
1124
 
  {
1125
 
    case HTTP_STATUS_NOT_FOUND :
1126
 
        _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, httpStatus(status), 0);
1127
 
        break;
1128
 
 
1129
 
    case HTTP_STATUS_UNAUTHORIZED :
1130
 
        _cupsSetError(IPP_STATUS_ERROR_NOT_AUTHENTICATED, httpStatus(status), 0);
1131
 
        break;
1132
 
 
1133
 
    case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
1134
 
        _cupsSetError(IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED, httpStatus(status), 0);
1135
 
        break;
1136
 
 
1137
 
    case HTTP_STATUS_FORBIDDEN :
1138
 
        _cupsSetError(IPP_STATUS_ERROR_FORBIDDEN, httpStatus(status), 0);
1139
 
        break;
1140
 
 
1141
 
    case HTTP_STATUS_BAD_REQUEST :
1142
 
        _cupsSetError(IPP_STATUS_ERROR_BAD_REQUEST, httpStatus(status), 0);
1143
 
        break;
1144
 
 
1145
 
    case HTTP_STATUS_REQUEST_TOO_LARGE :
1146
 
        _cupsSetError(IPP_STATUS_ERROR_REQUEST_VALUE, httpStatus(status), 0);
1147
 
        break;
1148
 
 
1149
 
    case HTTP_STATUS_NOT_IMPLEMENTED :
1150
 
        _cupsSetError(IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, httpStatus(status), 0);
1151
 
        break;
1152
 
 
1153
 
    case HTTP_STATUS_NOT_SUPPORTED :
1154
 
        _cupsSetError(IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, httpStatus(status), 0);
1155
 
        break;
1156
 
 
1157
 
    case HTTP_STATUS_UPGRADE_REQUIRED :
1158
 
        _cupsSetError(IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED, httpStatus(status), 0);
1159
 
        break;
1160
 
 
1161
 
    case HTTP_STATUS_CUPS_PKI_ERROR :
1162
 
        _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, httpStatus(status), 0);
1163
 
        break;
1164
 
 
1165
 
    case HTTP_STATUS_ERROR :
1166
 
        _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
1167
 
        break;
1168
 
 
1169
 
    default :
1170
 
        DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to "
1171
 
                      "IPP_STATUS_ERROR_SERVICE_UNAVAILABLE!", status));
1172
 
        _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, httpStatus(status), 0);
1173
 
        break;
1174
 
  }
1175
 
}
1176
 
 
1177
 
 
1178
 
/*
1179
 
 * End of "$Id: request.c 10996 2013-05-29 11:51:34Z msweet $".
1180
 
 */