Coverage Report

Created: 2025-10-08 06:06

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