Coverage Report

Created: 2025-08-29 06:20

/src/libcups/cups/request.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// IPP utilities for CUPS.
3
//
4
// Copyright © 2021-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 <fcntl.h>
14
#include <sys/stat.h>
15
#if defined(_WIN32) || defined(__EMX__)
16
#  include <io.h>
17
#else
18
#  include <unistd.h>
19
#endif // _WIN32 || __EMX__
20
#ifndef O_BINARY
21
0
#  define O_BINARY 0
22
#endif // O_BINARY
23
#ifndef MSG_DONTWAIT
24
#  define MSG_DONTWAIT 0
25
#endif // !MSG_DONTWAIT
26
27
28
//
29
// 'cupsDoFileRequest()' - Do an IPP request with a file.
30
//
31
// This function sends the IPP request and attached file to the specified
32
// server, retrying and authenticating as necessary.  The request is freed with
33
// @link ippDelete@.
34
//
35
36
ipp_t *         // O - Response data
37
cupsDoFileRequest(http_t     *http, // I - Connection to server or `CUPS_HTTP_DEFAULT`
38
                  ipp_t      *request,  // I - IPP request
39
                  const char *resource, // I - HTTP resource for POST
40
      const char *filename) // I - File to send or `NULL` for none
41
0
{
42
0
  ipp_t   *response;    // IPP response data
43
0
  int   infile;     // Input file
44
45
46
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);
47
48
0
  if (filename)
49
0
  {
50
0
    if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0)
51
0
    {
52
      // Can't get file information!
53
0
      _cupsSetError(errno == ENOENT ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, NULL, false);
54
55
0
      ippDelete(request);
56
57
0
      return (NULL);
58
0
    }
59
0
  }
60
0
  else
61
0
  {
62
0
    infile = -1;
63
0
  }
64
65
0
  response = cupsDoIORequest(http, request, resource, infile, -1);
66
67
0
  if (infile >= 0)
68
0
    close(infile);
69
70
0
  return (response);
71
0
}
72
73
74
//
75
// 'cupsDoIORequest()' - Do an IPP request with file descriptors.
76
//
77
// This function sends the IPP request with the optional input file "infile" to
78
// the specified server, retrying and authenticating as necessary.  The request
79
// is freed with @link ippDelete@.
80
//
81
// If "infile" is a valid file descriptor, @code cupsDoIORequest@ copies
82
// all of the data from the file after the IPP request message.
83
//
84
// If "outfile" is a valid file descriptor, @code cupsDoIORequest@ copies
85
// all of the data after the IPP response message to the file.
86
//
87
88
ipp_t *         // O - Response data
89
cupsDoIORequest(http_t     *http, // I - Connection to server or `CUPS_HTTP_DEFAULT`
90
                ipp_t      *request,  // I - IPP request
91
                const char *resource, // I - HTTP resource for POST
92
    int        infile,  // I - File to read from or `-1` for none
93
    int        outfile) // I - File to write to or `-1` for none
94
0
{
95
0
  ipp_t   *response = NULL; // IPP response data
96
0
  size_t  length = 0;   // Content-Length value
97
0
  http_status_t status;     // Status of HTTP request
98
0
  struct stat fileinfo;   // File information
99
0
  ssize_t bytes;      // Number of bytes read/written
100
0
  char    buffer[32768];    // Output buffer
101
102
103
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);
104
105
  // Range check input...
106
0
  if (!request || !resource)
107
0
  {
108
0
    ippDelete(request);
109
110
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), false);
111
112
0
    return (NULL);
113
0
  }
114
115
  // Get the default connection as needed...
116
0
  if (!http && (http = _cupsConnect()) == NULL)
117
0
  {
118
0
    ippDelete(request);
119
120
0
    return (NULL);
121
0
  }
122
123
  // See if we have a file to send...
124
0
  if (infile >= 0)
125
0
  {
126
0
    if (fstat(infile, &fileinfo))
127
0
    {
128
      // Can't get file information!
129
0
      _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, NULL, false);
130
0
      ippDelete(request);
131
132
0
      return (NULL);
133
0
    }
134
135
#ifdef _WIN32
136
    if (fileinfo.st_mode & _S_IFDIR)
137
#else
138
0
    if (S_ISDIR(fileinfo.st_mode))
139
0
#endif // _WIN32
140
0
    {
141
      // Can't send a directory...
142
0
      _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), false);
143
0
      ippDelete(request);
144
145
0
      return (NULL);
146
0
    }
147
148
0
#ifndef _WIN32
149
0
    if (!S_ISREG(fileinfo.st_mode))
150
0
      length = 0;     // Chunk when piping
151
0
    else
152
0
#endif // !_WIN32
153
0
    length = ippGetLength(request) + (size_t)fileinfo.st_size;
154
0
  }
155
0
  else
156
0
  {
157
0
    length = ippGetLength(request);
158
0
  }
159
160
0
  DEBUG_printf("2cupsDoIORequest: Request length=%ld, total length=%ld", (long)ippGetLength(request), (long)length);
161
162
  // Clear any "Local" authentication data since it is probably stale...
163
0
  if (http->authstring && !strncmp(http->authstring, "Local ", 6))
164
0
    httpSetAuthString(http, NULL, NULL);
165
166
  // Loop until we can send the request without authorization problems.
167
0
  while (response == NULL)
168
0
  {
169
0
    DEBUG_puts("2cupsDoIORequest: setup...");
170
171
    // Send the request...
172
0
    status = cupsSendRequest(http, request, resource, length);
173
174
0
    DEBUG_printf("2cupsDoIORequest: status=%d", status);
175
176
0
    if (status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA && infile >= 0)
177
0
    {
178
0
      DEBUG_puts("2cupsDoIORequest: file write...");
179
180
      // Send the file with the request...
181
0
#ifndef _WIN32
182
0
      if (S_ISREG(fileinfo.st_mode))
183
0
#endif // _WIN32
184
0
      lseek(infile, 0, SEEK_SET);
185
186
0
      while ((bytes = read(infile, buffer, sizeof(buffer))) > 0)
187
0
      {
188
0
        if ((status = cupsWriteRequestData(http, buffer, (size_t)bytes))
189
0
                != HTTP_STATUS_CONTINUE)
190
0
    break;
191
0
      }
192
0
    }
193
194
    // Get the server's response...
195
0
    if (status <= HTTP_STATUS_CONTINUE || status == HTTP_STATUS_OK)
196
0
    {
197
0
      response = cupsGetResponse(http, resource);
198
0
      status   = httpGetStatus(http);
199
0
    }
200
201
0
    DEBUG_printf("2cupsDoIORequest: status=%d", status);
202
203
0
    if (status == HTTP_STATUS_ERROR || (status >= HTTP_STATUS_BAD_REQUEST && status != HTTP_STATUS_UNAUTHORIZED && status != HTTP_STATUS_UPGRADE_REQUIRED))
204
0
    {
205
0
      _cupsSetHTTPError(http, status);
206
0
      break;
207
0
    }
208
209
0
    if (response && outfile >= 0)
210
0
    {
211
      // Write trailing data to file...
212
0
      while ((bytes = httpRead(http, buffer, sizeof(buffer))) > 0)
213
0
      {
214
0
  if (write(outfile, buffer, (size_t)bytes) < bytes)
215
0
    break;
216
0
      }
217
0
    }
218
219
0
    if (http->state != HTTP_STATE_WAITING)
220
0
    {
221
      // Flush any remaining data...
222
0
      httpFlush(http);
223
0
    }
224
0
  }
225
226
  // Delete the original request and return the response...
227
0
  ippDelete(request);
228
229
0
  return (response);
230
0
}
231
232
233
//
234
// 'cupsDoRequest()' - Do an IPP request.
235
//
236
// This function sends the IPP request to the specified server, retrying
237
// and authenticating as necessary.  The request is freed with @link ippDelete@.
238
//
239
240
ipp_t *         // O - Response data
241
cupsDoRequest(http_t     *http,   // I - Connection to server or `CUPS_HTTP_DEFAULT`
242
              ipp_t      *request,  // I - IPP request
243
              const char *resource) // I - HTTP resource for POST
244
0
{
245
0
  DEBUG_printf("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource);
246
247
0
  return (cupsDoIORequest(http, request, resource, -1, -1));
248
0
}
249
250
251
//
252
// 'cupsGetResponse()' - Get a response to an IPP request.
253
//
254
// Use this function to get the response for an IPP request sent using
255
// @link cupsSendRequest@. For requests that return additional data, use
256
// @link cupsReadResponseData@ after getting a successful response,
257
// otherwise call @link httpFlush@ to complete the response processing.
258
//
259
260
ipp_t *         // O - Response or `NULL` on HTTP error
261
cupsGetResponse(http_t     *http, // I - Connection to server or `CUPS_HTTP_DEFAULT`
262
                const char *resource) // I - HTTP resource for POST
263
0
{
264
0
  http_status_t status;     // HTTP status
265
0
  ipp_state_t state;      // IPP read state
266
0
  ipp_t   *response = NULL; // IPP response
267
268
269
0
  DEBUG_printf("cupsGetResponse(http=%p, resource=\"%s\")", (void *)http, resource);
270
0
  DEBUG_printf("1cupsGetResponse: http->state=%d", http ? http->state : HTTP_STATE_ERROR);
271
272
  // Connect to the default server as needed...
273
0
  if (!http)
274
0
  {
275
0
    _cups_globals_t *cg = _cupsGlobals();
276
          // Pointer to library globals
277
278
0
    if ((http = cg->http) == NULL)
279
0
    {
280
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection."), true);
281
0
      DEBUG_puts("1cupsGetResponse: No active connection - returning NULL.");
282
0
      return (NULL);
283
0
    }
284
0
  }
285
286
0
  if (http->state != HTTP_STATE_POST_RECV && http->state != HTTP_STATE_POST_SEND)
287
0
  {
288
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No request sent."), true);
289
0
    DEBUG_puts("1cupsGetResponse: Not in POST state - returning NULL.");
290
0
    return (NULL);
291
0
  }
292
293
  // Check for an unfinished chunked request...
294
0
  if (http->state == HTTP_STATE_POST_RECV && http->data_encoding == HTTP_ENCODING_CHUNKED)
295
0
  {
296
    // Send a 0-length chunk to finish off the request...
297
0
    DEBUG_puts("2cupsGetResponse: Finishing chunked POST...");
298
299
0
    if (httpWrite(http, "", 0) < 0)
300
0
    {
301
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to finish request."), true);
302
0
      return (NULL);
303
0
    }
304
0
  }
305
306
  // Wait for a response from the server...
307
0
  DEBUG_printf("2cupsGetResponse: Update loop, http->status=%d...", http->status);
308
309
0
  do
310
0
  {
311
0
    status = httpUpdate(http);
312
0
  }
313
0
  while (status == HTTP_STATUS_CONTINUE);
314
315
0
  DEBUG_printf("2cupsGetResponse: status=%d", status);
316
317
0
  if (status == HTTP_STATUS_OK)
318
0
  {
319
    // Get the IPP response...
320
0
    response = ippNew();
321
322
0
    while ((state = ippRead(http, response)) != IPP_STATE_DATA)
323
0
    {
324
0
      if (state == IPP_STATE_ERROR)
325
0
  break;
326
0
    }
327
328
0
    if (state == IPP_STATE_ERROR)
329
0
    {
330
      // Flush remaining data and delete the response...
331
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to read response."), true);
332
0
      DEBUG_puts("1cupsGetResponse: IPP read error!");
333
334
0
      httpFlush(http);
335
336
0
      ippDelete(response);
337
0
      response = NULL;
338
339
0
      http->status = HTTP_STATUS_ERROR;
340
0
      http->error  = EINVAL;
341
0
    }
342
0
  }
343
0
  else if (status != HTTP_STATUS_ERROR)
344
0
  {
345
    // Flush any error message...
346
0
    httpFlush(http);
347
348
0
    _cupsSetHTTPError(http, status);
349
350
    // Then handle encryption and authentication...
351
0
    if (status == HTTP_STATUS_UNAUTHORIZED)
352
0
    {
353
      // See if we can do authentication...
354
0
      DEBUG_puts("2cupsGetResponse: Need authorization...");
355
356
0
      if (cupsDoAuthentication(http, "POST", resource))
357
0
      {
358
0
        if (!httpConnectAgain(http, 30000, NULL))
359
0
          http->status = HTTP_STATUS_ERROR;
360
0
      }
361
0
      else
362
0
      {
363
0
        http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
364
0
      }
365
0
    }
366
0
    else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
367
0
    {
368
      // Force a reconnect with encryption...
369
0
      DEBUG_puts("2cupsGetResponse: Need encryption...");
370
371
0
      if (httpConnectAgain(http, 30000, NULL))
372
0
        httpSetEncryption(http, HTTP_ENCRYPTION_REQUIRED);
373
0
    }
374
0
  }
375
376
0
  if (response)
377
0
  {
378
0
    ipp_attribute_t *attr;    // status-message attribute
379
380
0
    attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
381
382
0
    DEBUG_printf("1cupsGetResponse: status-code=%s, status-message=\"%s\"", ippErrorString(response->request.status.status_code), attr ? attr->values[0].string.text : "");
383
384
0
    _cupsSetError(response->request.status.status_code, attr ? attr->values[0].string.text : ippErrorString(response->request.status.status_code), false);
385
0
  }
386
387
0
  return (response);
388
0
}
389
390
391
//
392
// 'cupsGetError()' - Return the last IPP status code received on the current
393
//                     thread.
394
//
395
396
ipp_status_t        // O - IPP status code from last request
397
cupsGetError(void)
398
0
{
399
0
  return (_cupsGlobals()->last_error);
400
0
}
401
402
403
//
404
// 'cupsGetErrorString()' - Return the last IPP status-message received on the
405
//                           current thread.
406
//
407
408
const char *        // O - "status-message" text from last request
409
cupsGetErrorString(void)
410
0
{
411
0
  return (_cupsGlobals()->last_status_message);
412
0
}
413
414
415
//
416
// 'cupsReadResponseData()' - Read additional data after the IPP response.
417
//
418
// This function is used after @link cupsGetResponse@ to read any trailing
419
// document data after an IPP response.
420
//
421
422
ssize_t         // O - Bytes read, 0 on EOF, -1 on error
423
cupsReadResponseData(
424
    http_t *http,     // I - Connection to server or `CUPS_HTTP_DEFAULT`
425
    char   *buffer,     // I - Buffer to use
426
    size_t length)      // I - Number of bytes to read
427
0
{
428
  // Get the default connection as needed...
429
0
  DEBUG_printf("cupsReadResponseData(http=%p, buffer=%p, length=" CUPS_LLFMT ")", (void *)http, (void *)buffer, CUPS_LLCAST length);
430
431
0
  if (!http)
432
0
  {
433
0
    _cups_globals_t *cg = _cupsGlobals();
434
          // Pointer to library globals
435
436
0
    if ((http = cg->http) == NULL)
437
0
    {
438
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), true);
439
0
      return (-1);
440
0
    }
441
0
  }
442
443
  // Then read from the HTTP connection...
444
0
  return (httpRead(http, buffer, length));
445
0
}
446
447
448
//
449
// 'cupsSendRequest()' - Send an IPP request.
450
//
451
// Use @link cupsWriteRequestData@ to write any additional data (document, etc.)
452
// for the request, @link cupsGetResponse@ to get the IPP response, and
453
// @link cupsReadResponseData@ to read any additional data following the
454
// response. Only one request can be sent/queued at a time per `http_t`
455
// connection.
456
//
457
// Returns the initial HTTP status code, which will be `HTTP_STATUS_CONTINUE`
458
// on a successful send of the request.
459
//
460
// Note: Unlike @link cupsDoFileRequest@, @link cupsDoIORequest@, and
461
// @link cupsDoRequest@, the request is NOT freed with @link ippDelete@.
462
//
463
464
http_status_t       // O - Initial HTTP status
465
cupsSendRequest(http_t     *http, // I - Connection to server or `CUPS_HTTP_DEFAULT`
466
                ipp_t      *request,  // I - IPP request
467
                const char *resource, // I - Resource path
468
    size_t     length)  // I - Length of data to follow or `CUPS_LENGTH_VARIABLE`
469
0
{
470
0
  http_status_t   status;   // Status of HTTP request
471
0
  bool      got_status; // Did we get the status?
472
0
  ipp_state_t   state;    // State of IPP processing
473
0
  http_status_t   expect;   // Expect: header to use
474
0
  char      date[256];  // Date: header value
475
0
  int     digest;   // Are we using Digest authentication?
476
477
478
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);
479
480
  // Range check input...
481
0
  if (!request || !resource)
482
0
  {
483
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), false);
484
485
0
    return (HTTP_STATUS_ERROR);
486
0
  }
487
488
  // Get the default connection as needed...
489
0
  if (!http && (http = _cupsConnect()) == NULL)
490
0
    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
491
492
  // If the prior request was not flushed out, do so now...
493
0
  if (http->state == HTTP_STATE_GET_SEND || http->state == HTTP_STATE_POST_SEND)
494
0
  {
495
0
    DEBUG_puts("2cupsSendRequest: Flush prior response.");
496
0
    httpFlush(http);
497
0
  }
498
0
  else if (http->state != HTTP_STATE_WAITING)
499
0
  {
500
0
    DEBUG_printf("1cupsSendRequest: Unknown HTTP state (%d), reconnecting.", http->state);
501
0
    if (!httpConnectAgain(http, 30000, NULL))
502
0
      return (HTTP_STATUS_ERROR);
503
0
  }
504
505
  // See if we have an auth-info attribute and are communicating over
506
  // a non-local link.  If so, encrypt the link so that we can pass
507
  // the authentication information securely...
508
0
  if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) && !httpAddrIsLocalhost(http->hostaddr) && !http->tls && !httpSetEncryption(http, HTTP_ENCRYPTION_REQUIRED))
509
0
  {
510
0
    DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
511
0
    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
512
0
  }
513
514
  // Reconnect if the last response had a "Connection: close"...
515
0
  if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
516
0
  {
517
0
    DEBUG_puts("2cupsSendRequest: Connection: close");
518
0
    httpClearFields(http);
519
0
    if (!httpConnectAgain(http, 30000, NULL))
520
0
    {
521
0
      DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
522
0
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
523
0
    }
524
0
  }
525
526
  // Loop until we can send the request without authorization problems.
527
0
  expect = HTTP_STATUS_CONTINUE;
528
529
0
  for (;;)
530
0
  {
531
0
    DEBUG_puts("2cupsSendRequest: Setup...");
532
533
    // Setup the HTTP variables needed...
534
0
    httpClearFields(http);
535
0
    httpSetExpect(http, expect);
536
0
    httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
537
0
    httpSetField(http, HTTP_FIELD_DATE, httpGetDateString(time(NULL), date, sizeof(date)));
538
0
    httpSetLength(http, length);
539
540
0
    digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
541
542
0
    if (digest)
543
0
    {
544
      // Update the Digest authentication string...
545
0
      _httpSetDigestAuthString(http, http->nextnonce, "POST", resource);
546
0
    }
547
548
0
    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
549
550
0
    DEBUG_printf("2cupsSendRequest: authstring=\"%s\"", http->authstring);
551
552
    // Try the request...
553
0
    DEBUG_puts("2cupsSendRequest: Sending HTTP POST...");
554
555
0
    if (!httpWriteRequest(http, "POST", resource))
556
0
    {
557
0
      DEBUG_puts("2cupsSendRequest: POST failed, reconnecting.");
558
0
      if (!httpConnectAgain(http, 30000, NULL))
559
0
      {
560
0
        DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
561
0
        return (HTTP_STATUS_SERVICE_UNAVAILABLE);
562
0
      }
563
0
      else
564
0
      {
565
0
        continue;
566
0
      }
567
0
    }
568
569
    // Send the IPP data...
570
0
    DEBUG_puts("2cupsSendRequest: Writing IPP request...");
571
572
0
    request->state = IPP_STATE_IDLE;
573
0
    status         = HTTP_STATUS_CONTINUE;
574
0
    got_status     = false;
575
576
0
    while ((state = ippWrite(http, request)) != IPP_STATE_DATA)
577
0
    {
578
0
      if (httpWait(http, 0))
579
0
      {
580
0
        got_status = true;
581
582
0
        _httpUpdate(http, &status);
583
0
  if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
584
0
    break;
585
0
      }
586
0
      else if (state == IPP_STATE_ERROR)
587
0
      {
588
0
  break;
589
0
      }
590
0
    }
591
592
0
    if (state == IPP_STATE_ERROR)
593
0
    {
594
      // We weren't able to send the IPP request. But did we already get a HTTP
595
      // error status?
596
0
      if (!got_status || status < HTTP_STATUS_MULTIPLE_CHOICES)
597
0
      {
598
        // No, something else went wrong.
599
0
  DEBUG_puts("1cupsSendRequest: Unable to send IPP request.");
600
601
0
  http->status = HTTP_STATUS_ERROR;
602
0
  http->state  = HTTP_STATE_WAITING;
603
604
0
  return (HTTP_STATUS_ERROR);
605
0
      }
606
0
    }
607
608
    // Wait up to 1 second to get the 100-continue response as needed...
609
0
    if (!got_status || (digest && status == HTTP_STATUS_CONTINUE))
610
0
    {
611
0
      if (expect == HTTP_STATUS_CONTINUE || digest)
612
0
      {
613
0
  DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
614
615
0
  if (httpWait(http, 1000))
616
0
    _httpUpdate(http, &status);
617
0
      }
618
0
      else if (httpWait(http, 0))
619
0
      {
620
0
  _httpUpdate(http, &status);
621
0
      }
622
0
    }
623
624
0
    DEBUG_printf("2cupsSendRequest: status=%d", status);
625
626
    // Process the current HTTP status...
627
0
    if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
628
0
    {
629
0
      int temp_status;      // Temporary status
630
631
0
      _cupsSetHTTPError(http, status);
632
633
0
      do
634
0
      {
635
0
  temp_status = httpUpdate(http);
636
0
      }
637
0
      while (temp_status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV);
638
639
0
      httpFlush(http);
640
0
    }
641
642
0
    switch (status)
643
0
    {
644
0
      case HTTP_STATUS_CONTINUE :
645
0
      case HTTP_STATUS_OK :
646
0
      case HTTP_STATUS_ERROR :
647
0
          DEBUG_printf("1cupsSendRequest: Returning %d.", status);
648
0
          return (status);
649
650
0
      case HTTP_STATUS_UNAUTHORIZED :
651
0
          if (!cupsDoAuthentication(http, "POST", resource))
652
0
    {
653
0
            DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED.");
654
0
      return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED);
655
0
    }
656
657
0
          DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED.");
658
659
0
    if (!httpConnectAgain(http, 30000, NULL))
660
0
    {
661
0
      DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
662
0
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
663
0
    }
664
0
    break;
665
666
0
      case HTTP_STATUS_UPGRADE_REQUIRED :
667
    // Flush any error message, reconnect, and then upgrade with
668
    // encryption...
669
0
          DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UPGRADE_REQUIRED.");
670
671
0
    if (!httpConnectAgain(http, 30000, NULL))
672
0
    {
673
0
      DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
674
0
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
675
0
    }
676
677
0
    DEBUG_puts("2cupsSendRequest: Upgrading to TLS.");
678
0
    if (!httpSetEncryption(http, HTTP_ENCRYPTION_REQUIRED))
679
0
    {
680
0
      DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
681
0
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
682
0
    }
683
0
    break;
684
685
0
      case HTTP_STATUS_EXPECTATION_FAILED :
686
    // Don't try using the Expect: header the next time around...
687
0
    expect = HTTP_STATUS_NONE;
688
689
0
          DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_EXPECTATION_FAILED.");
690
691
0
    if (!httpConnectAgain(http, 30000, NULL))
692
0
    {
693
0
      DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
694
0
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
695
0
    }
696
0
    break;
697
698
0
      default :
699
          // Some other error...
700
0
    return (status);
701
0
    }
702
0
  }
703
0
}
704
705
706
//
707
// 'cupsWriteRequestData()' - Write additional data after an IPP request.
708
//
709
// This function writes a buffer of additional data after an IPP request and is
710
// used after calling the @link cupsSendRequest@ and/or @link cupsStartDocument@
711
// functions, typically to send all or part of a document or file.  Each call
712
// appends the given buffer of data to the request, allowing an application to
713
// stream content to the receiving IPP server.
714
//
715
// Call the @link cupsGetResponse@ or @link cupsFinishDestDocument@ functions
716
// to complete the current request and get the corresponding response.
717
//
718
719
http_status_t       // O - `HTTP_STATUS_CONTINUE` if OK or HTTP status on error
720
cupsWriteRequestData(
721
    http_t     *http,     // I - Connection to server or `CUPS_HTTP_DEFAULT`
722
    const char *buffer,     // I - Bytes to write
723
    size_t     length)      // I - Number of bytes to write
724
0
{
725
0
  int wused;        // Previous bytes in buffer
726
727
728
  // Get the default connection as needed...
729
0
  DEBUG_printf("cupsWriteRequestData(http=%p, buffer=%p, length=" CUPS_LLFMT ")", (void *)http, (void *)buffer, CUPS_LLCAST length);
730
731
0
  if (!http)
732
0
  {
733
0
    _cups_globals_t *cg = _cupsGlobals();
734
          // Pointer to library globals
735
736
0
    if ((http = cg->http) == NULL)
737
0
    {
738
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), true);
739
0
      DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
740
0
      return (HTTP_STATUS_ERROR);
741
0
    }
742
0
  }
743
744
  // Then write to the HTTP connection...
745
0
  wused = http->wused;
746
747
0
  if (httpWrite(http, buffer, length) < 0)
748
0
  {
749
0
    DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
750
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), false);
751
0
    return (HTTP_STATUS_ERROR);
752
0
  }
753
754
  // Finally, check if we have any pending data from the server...
755
0
  if (length >= HTTP_MAX_BUFFER || http->wused < wused || (wused > 0 && (size_t)http->wused == length))
756
0
  {
757
    // We've written something to the server, so check for response data...
758
0
    if (_httpWait(http, 0, 1))
759
0
    {
760
0
      http_status_t status;   // Status from _httpUpdate
761
762
0
      _httpUpdate(http, &status);
763
0
      if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
764
0
      {
765
0
        _cupsSetHTTPError(http, status);
766
767
0
  do
768
0
  {
769
0
    status = httpUpdate(http);
770
0
  }
771
0
  while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV);
772
773
0
        httpFlush(http);
774
0
      }
775
776
0
      DEBUG_printf("1cupsWriteRequestData: Returning %d.\n", status);
777
0
      return (status);
778
0
    }
779
0
  }
780
781
0
  DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE.");
782
0
  return (HTTP_STATUS_CONTINUE);
783
0
}
784
785
786
//
787
// '_cupsConnect()' - Get the default server connection...
788
//
789
790
http_t *        // O - HTTP connection
791
_cupsConnect(void)
792
0
{
793
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals
794
795
796
  // See if we are connected to the same server...
797
0
  if (cg->http)
798
0
  {
799
    // Compare the connection hostname, port, and encryption settings to
800
    // the cached defaults; these were initialized the first time we
801
    // connected...
802
0
    if (strcmp(cg->http->hostname, cg->server) ||
803
0
#ifdef AF_LOCAL
804
0
        (httpAddrGetFamily(cg->http->hostaddr) != AF_LOCAL && cg->ipp_port != httpAddrGetPort(cg->http->hostaddr)) ||
805
#else
806
        cg->ipp_port != httpAddrGetPort(cg->http->hostaddr) ||
807
#endif // AF_LOCAL
808
0
        (cg->http->encryption != cg->encryption &&
809
0
   cg->http->encryption == HTTP_ENCRYPTION_NEVER))
810
0
    {
811
      // Need to close the current connection because something has changed...
812
0
      httpClose(cg->http);
813
0
      cg->http = NULL;
814
0
    }
815
0
    else
816
0
    {
817
      // Same server, see if the connection is still established...
818
0
      char  ch;     // Connection check byte
819
0
      ssize_t n;      // Number of bytes
820
821
#ifdef _WIN32
822
      if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK)) == 0 ||
823
          (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK))
824
#else
825
0
      if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK | MSG_DONTWAIT)) == 0 ||
826
0
          (n < 0 && errno != EWOULDBLOCK))
827
0
#endif // _WIN32
828
0
      {
829
        // Nope, close the connection...
830
0
  httpClose(cg->http);
831
0
  cg->http = NULL;
832
0
      }
833
0
    }
834
0
  }
835
836
  // (Re)connect as needed...
837
0
  if (!cg->http)
838
0
  {
839
0
    if ((cg->http = httpConnect(cupsGetServer(), ippGetPort(), NULL, AF_UNSPEC, cupsGetEncryption(), 1, 30000, NULL)) == NULL)
840
0
    {
841
0
      if (errno)
842
0
        _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, false);
843
0
      else
844
0
        _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, _("Unable to connect to host."), true);
845
0
    }
846
0
  }
847
848
  // Return the cached connection...
849
0
  return (cg->http);
850
0
}
851
852
853
//
854
// '_cupsSetError()' - Set the last IPP status code and status-message.
855
//
856
857
void
858
_cupsSetError(ipp_status_t status,  // I - IPP status code
859
              const char   *message,  // I - status-message value
860
        bool         localize)  // I - Localize the message?
861
490
{
862
490
  _cups_globals_t *cg;    // Global data
863
864
865
490
  if (!message && errno)
866
0
  {
867
0
    message  = strerror(errno);
868
0
    localize = 0;
869
0
  }
870
871
490
  cg             = _cupsGlobals();
872
490
  cg->last_error = status;
873
874
490
  if (cg->last_status_message)
875
489
  {
876
489
    _cupsStrFree(cg->last_status_message);
877
878
489
    cg->last_status_message = NULL;
879
489
  }
880
881
490
  if (message)
882
490
  {
883
490
    if (localize)
884
490
    {
885
      // Get the message catalog...
886
490
      if (!cg->lang_default)
887
0
  cg->lang_default = cupsLangDefault();
888
889
490
      cg->last_status_message = _cupsStrAlloc(cupsLangGetString(cg->lang_default, message));
890
490
    }
891
0
    else
892
0
    {
893
0
      cg->last_status_message = _cupsStrAlloc(message);
894
0
    }
895
490
  }
896
897
490
  DEBUG_printf("4_cupsSetError: last_error=%s, last_status_message=\"%s\"", ippErrorString(cg->last_error), cg->last_status_message);
898
490
}
899
900
901
//
902
// '_cupsSetHTTPError()' - Set the last error using the HTTP status.
903
//
904
905
void
906
_cupsSetHTTPError(http_t        *http,  // I - HTTP connection
907
                  http_status_t status) // I - HTTP status code
908
0
{
909
0
  switch (status)
910
0
  {
911
0
    case HTTP_STATUS_NOT_MODIFIED :
912
0
        _cupsSetError(IPP_STATUS_OK_EVENTS_COMPLETE, httpStatusString(status), false);
913
0
        break;
914
915
0
    case HTTP_STATUS_NOT_FOUND :
916
0
  _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, httpStatusString(status), false);
917
0
  break;
918
919
0
    case HTTP_STATUS_UNAUTHORIZED :
920
0
  _cupsSetError(IPP_STATUS_ERROR_NOT_AUTHENTICATED, httpStatusString(status), false);
921
0
  break;
922
923
0
    case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
924
0
  _cupsSetError(IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED, httpStatusString(status), false);
925
0
  break;
926
927
0
    case HTTP_STATUS_FORBIDDEN :
928
0
  _cupsSetError(IPP_STATUS_ERROR_FORBIDDEN, httpStatusString(status), false);
929
0
  break;
930
931
0
    case HTTP_STATUS_BAD_REQUEST :
932
0
  _cupsSetError(IPP_STATUS_ERROR_BAD_REQUEST, httpStatusString(status), false);
933
0
  break;
934
935
0
    case HTTP_STATUS_CONTENT_TOO_LARGE :
936
0
  _cupsSetError(IPP_STATUS_ERROR_REQUEST_VALUE, httpStatusString(status), false);
937
0
  break;
938
939
0
    case HTTP_STATUS_NOT_IMPLEMENTED :
940
0
  _cupsSetError(IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, httpStatusString(status), false);
941
0
  break;
942
943
0
    case HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED :
944
0
  _cupsSetError(IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, httpStatusString(status), false);
945
0
  break;
946
947
0
    case HTTP_STATUS_UPGRADE_REQUIRED :
948
0
  _cupsSetError(IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED, httpStatusString(status), false);
949
0
        break;
950
951
0
    case HTTP_STATUS_CUPS_PKI_ERROR :
952
0
  _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, httpStatusString(status), false);
953
0
        break;
954
955
0
    case HTTP_STATUS_ERROR :
956
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), false);
957
0
        break;
958
959
0
    default :
960
0
        if ((int)status >= 300)
961
0
        {
962
0
    DEBUG_printf("4_cupsSetHTTPError: HTTP error %d mapped to IPP_STATUS_ERROR_SERVICE_UNAVAILABLE!", status);
963
0
    _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, httpStatusString(status), false);
964
0
  }
965
0
  break;
966
0
  }
967
0
}