Coverage Report

Created: 2025-06-24 07:01

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