Coverage Report

Created: 2025-07-18 07:03

/src/cups/cups/getputfile.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Get/put file functions for CUPS.
3
 *
4
 * Copyright © 2020-2025 by OpenPrinting.
5
 * Copyright © 2007-2018 by Apple Inc.
6
 * Copyright © 1997-2006 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
22
23
/*
24
 * 'cupsGetFd()' - Get a file from the server.
25
 *
26
 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
27
 *
28
 * @since CUPS 1.1.20@
29
 */
30
31
http_status_t       /* O - HTTP status */
32
cupsGetFd(http_t     *http,   /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
33
    const char *resource,   /* I - Resource name */
34
    int        fd)    /* I - File descriptor */
35
0
{
36
0
  ssize_t bytes;      /* Number of bytes read */
37
0
  char    buffer[8192];   /* Buffer for file */
38
0
  http_status_t status;     /* HTTP status from server */
39
0
  char    if_modified_since[HTTP_MAX_VALUE];
40
          /* If-Modified-Since header */
41
0
  int   new_auth = 0;   /* Using new auth information? */
42
0
  int   digest;     /* Are we using Digest authentication? */
43
44
45
 /*
46
  * Range check input...
47
  */
48
49
0
  DEBUG_printf("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd);
50
51
0
  if (!resource || fd < 0)
52
0
  {
53
0
    if (http)
54
0
      http->error = EINVAL;
55
56
0
    return (HTTP_STATUS_ERROR);
57
0
  }
58
59
0
  if (!http)
60
0
    if ((http = _cupsConnect()) == NULL)
61
0
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
62
63
 /*
64
  * Then send GET requests to the HTTP server...
65
  */
66
67
0
  cupsCopyString(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE),
68
0
          sizeof(if_modified_since));
69
70
0
  do
71
0
  {
72
0
    if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
73
0
    {
74
0
      httpClearFields(http);
75
0
      if (httpReconnect2(http, 30000, NULL))
76
0
      {
77
0
  status = HTTP_STATUS_ERROR;
78
0
  break;
79
0
      }
80
0
    }
81
82
0
    httpClearFields(http);
83
0
    httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since);
84
85
0
    digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
86
87
0
    if (digest && !new_auth)
88
0
    {
89
     /*
90
      * Update the Digest authentication string...
91
      */
92
93
0
      _httpSetDigestAuthString(http, http->nextnonce, "GET", resource);
94
0
    }
95
96
#ifdef HAVE_GSSAPI
97
    if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth)
98
    {
99
     /*
100
      * Do not use cached Kerberos credentials since they will look like a
101
      * "replay" attack...
102
      */
103
104
      _cupsSetNegotiateAuthString(http, "GET", resource);
105
    }
106
#endif /* HAVE_GSSAPI */
107
108
0
    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
109
110
0
    if (httpGet(http, resource))
111
0
    {
112
0
      if (httpReconnect2(http, 30000, NULL))
113
0
      {
114
0
        status = HTTP_STATUS_ERROR;
115
0
  break;
116
0
      }
117
0
      else
118
0
      {
119
0
        status = HTTP_STATUS_UNAUTHORIZED;
120
0
        continue;
121
0
      }
122
0
    }
123
124
0
    new_auth = 0;
125
126
0
    while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
127
128
0
    if (status == HTTP_STATUS_UNAUTHORIZED)
129
0
    {
130
     /*
131
      * Flush any error message...
132
      */
133
134
0
      httpFlush(http);
135
136
     /*
137
      * See if we can do authentication...
138
      */
139
140
0
      new_auth = 1;
141
142
0
      if (cupsDoAuthentication(http, "GET", resource))
143
0
      {
144
0
        status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
145
0
        break;
146
0
      }
147
148
0
      if (httpReconnect2(http, 30000, NULL))
149
0
      {
150
0
        status = HTTP_STATUS_ERROR;
151
0
        break;
152
0
      }
153
154
0
      continue;
155
0
    }
156
0
    else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
157
0
    {
158
      /* Flush any error message... */
159
0
      httpFlush(http);
160
161
      /* Reconnect... */
162
0
      if (httpReconnect2(http, 30000, NULL))
163
0
      {
164
0
        status = HTTP_STATUS_ERROR;
165
0
        break;
166
0
      }
167
168
      /* Upgrade with encryption... */
169
0
      httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
170
171
      /* Try again, this time with encryption enabled... */
172
0
      continue;
173
0
    }
174
0
  }
175
0
  while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED);
176
177
 /*
178
  * See if we actually got the file or an error...
179
  */
180
181
0
  if (status == HTTP_STATUS_OK)
182
0
  {
183
   /*
184
    * Yes, copy the file...
185
    */
186
187
0
    while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
188
0
      write(fd, buffer, (size_t)bytes);
189
0
  }
190
0
  else
191
0
  {
192
0
    _cupsSetHTTPError(http, status);
193
0
    httpFlush(http);
194
0
  }
195
196
 /*
197
  * Return the request status...
198
  */
199
200
0
  DEBUG_printf("1cupsGetFd: Returning %d...", status);
201
202
0
  return (status);
203
0
}
204
205
206
/*
207
 * 'cupsGetFile()' - Get a file from the server.
208
 *
209
 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
210
 *
211
 * @since CUPS 1.1.20@
212
 */
213
214
http_status_t       /* O - HTTP status */
215
cupsGetFile(http_t     *http,   /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
216
      const char *resource, /* I - Resource name */
217
      const char *filename) /* I - Filename */
218
0
{
219
0
  int   fd;     /* File descriptor */
220
0
  http_status_t status;     /* Status */
221
222
223
 /*
224
  * Range check input...
225
  */
226
227
0
  if (!http || !resource || !filename)
228
0
  {
229
0
    if (http)
230
0
      http->error = EINVAL;
231
232
0
    return (HTTP_STATUS_ERROR);
233
0
  }
234
235
 /*
236
  * Create the file...
237
  */
238
239
0
  if ((fd = open(filename, O_WRONLY | O_EXCL | O_TRUNC)) < 0)
240
0
  {
241
   /*
242
    * Couldn't open the file!
243
    */
244
245
0
    http->error = errno;
246
247
0
    return (HTTP_STATUS_ERROR);
248
0
  }
249
250
 /*
251
  * Get the file...
252
  */
253
254
0
  status = cupsGetFd(http, resource, fd);
255
256
 /*
257
  * If the file couldn't be gotten, then remove the file...
258
  */
259
260
0
  close(fd);
261
262
0
  if (status != HTTP_STATUS_OK)
263
0
    unlink(filename);
264
265
 /*
266
  * Return the HTTP status code...
267
  */
268
269
0
  return (status);
270
0
}
271
272
273
/*
274
 * 'cupsPutFd()' - Put a file on the server.
275
 *
276
 * This function returns @code HTTP_STATUS_CREATED@ when the file is stored
277
 * successfully.
278
 *
279
 * @since CUPS 1.1.20@
280
 */
281
282
http_status_t       /* O - HTTP status */
283
cupsPutFd(http_t     *http,   /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
284
          const char *resource,   /* I - Resource name */
285
    int        fd)    /* I - File descriptor */
286
0
{
287
0
  ssize_t bytes;      /* Number of bytes read */
288
0
  int   retries;    /* Number of retries */
289
0
  char    buffer[8192];   /* Buffer for file */
290
0
  http_status_t status;     /* HTTP status from server */
291
0
  int   new_auth = 0;   /* Using new auth information? */
292
0
  int   digest;     /* Are we using Digest authentication? */
293
294
295
 /*
296
  * Range check input...
297
  */
298
299
0
  DEBUG_printf("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd);
300
301
0
  if (!resource || fd < 0)
302
0
  {
303
0
    if (http)
304
0
      http->error = EINVAL;
305
306
0
    return (HTTP_STATUS_ERROR);
307
0
  }
308
309
0
  if (!http)
310
0
    if ((http = _cupsConnect()) == NULL)
311
0
      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
312
313
 /*
314
  * Then send PUT requests to the HTTP server...
315
  */
316
317
0
  retries = 0;
318
319
0
  do
320
0
  {
321
0
    if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
322
0
    {
323
0
      httpClearFields(http);
324
0
      if (httpReconnect2(http, 30000, NULL))
325
0
      {
326
0
  status = HTTP_STATUS_ERROR;
327
0
  break;
328
0
      }
329
0
    }
330
331
0
    DEBUG_printf("2cupsPutFd: starting attempt, authstring=\"%s\"...", http->authstring);
332
333
0
    httpClearFields(http);
334
0
    httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked");
335
0
    httpSetExpect(http, HTTP_STATUS_CONTINUE);
336
337
0
    digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
338
339
0
    if (digest && !new_auth)
340
0
    {
341
     /*
342
      * Update the Digest authentication string...
343
      */
344
345
0
      _httpSetDigestAuthString(http, http->nextnonce, "PUT", resource);
346
0
    }
347
348
#ifdef HAVE_GSSAPI
349
    if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth)
350
    {
351
     /*
352
      * Do not use cached Kerberos credentials since they will look like a
353
      * "replay" attack...
354
      */
355
356
      _cupsSetNegotiateAuthString(http, "PUT", resource);
357
    }
358
#endif /* HAVE_GSSAPI */
359
360
0
    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
361
362
0
    if (httpPut(http, resource))
363
0
    {
364
0
      if (httpReconnect2(http, 30000, NULL))
365
0
      {
366
0
        status = HTTP_STATUS_ERROR;
367
0
  break;
368
0
      }
369
0
      else
370
0
      {
371
0
        status = HTTP_STATUS_UNAUTHORIZED;
372
0
        continue;
373
0
      }
374
0
    }
375
376
   /*
377
    * Wait up to 1 second for a 100-continue response...
378
    */
379
380
0
    if (httpWait(http, 1000))
381
0
      status = httpUpdate(http);
382
0
    else
383
0
      status = HTTP_STATUS_CONTINUE;
384
385
0
    if (status == HTTP_STATUS_CONTINUE)
386
0
    {
387
     /*
388
      * Copy the file...
389
      */
390
391
0
      lseek(fd, 0, SEEK_SET);
392
393
0
      while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
394
0
  if (httpCheck(http))
395
0
  {
396
0
          if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE)
397
0
            break;
398
0
  }
399
0
  else
400
0
          httpWrite2(http, buffer, (size_t)bytes);
401
0
    }
402
403
0
    if (status == HTTP_STATUS_CONTINUE)
404
0
    {
405
0
      httpWrite2(http, buffer, 0);
406
407
0
      while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
408
0
    }
409
410
0
    if (status == HTTP_STATUS_ERROR && !retries)
411
0
    {
412
0
      DEBUG_printf("2cupsPutFd: retry on status %d", status);
413
414
0
      retries ++;
415
0
      status = HTTP_STATUS_NONE;
416
417
      /* Flush any error message... */
418
0
      httpFlush(http);
419
420
      /* Reconnect... */
421
0
      if (httpReconnect2(http, 30000, NULL))
422
0
      {
423
0
        status = HTTP_STATUS_ERROR;
424
0
        break;
425
0
      }
426
427
      /* Try again... */
428
0
      continue;
429
0
    }
430
431
0
    DEBUG_printf("2cupsPutFd: status=%d", status);
432
433
0
    new_auth = 0;
434
435
0
    if (status == HTTP_STATUS_UNAUTHORIZED)
436
0
    {
437
     /*
438
      * Flush any error message...
439
      */
440
441
0
      httpFlush(http);
442
443
     /*
444
      * See if we can do authentication...
445
      */
446
447
0
      new_auth = 1;
448
449
0
      if (cupsDoAuthentication(http, "PUT", resource))
450
0
      {
451
0
        status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
452
0
        break;
453
0
      }
454
455
0
      if (httpReconnect2(http, 30000, NULL))
456
0
      {
457
0
        status = HTTP_STATUS_ERROR;
458
0
        break;
459
0
      }
460
461
0
      continue;
462
0
    }
463
0
    else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
464
0
    {
465
      /* Flush any error message... */
466
0
      httpFlush(http);
467
468
      /* Reconnect... */
469
0
      if (httpReconnect2(http, 30000, NULL))
470
0
      {
471
0
        status = HTTP_STATUS_ERROR;
472
0
        break;
473
0
      }
474
475
      /* Upgrade with encryption... */
476
0
      httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
477
478
      /* Try again, this time with encryption enabled... */
479
0
      continue;
480
0
    }
481
0
  }
482
0
  while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED || status == HTTP_STATUS_NONE);
483
484
 /*
485
  * See if we actually put the file or an error...
486
  */
487
488
0
  if (status != HTTP_STATUS_CREATED)
489
0
  {
490
0
    _cupsSetHTTPError(http, status);
491
0
    httpFlush(http);
492
0
  }
493
494
0
  DEBUG_printf("1cupsPutFd: Returning %d...", status);
495
496
0
  return (status);
497
0
}
498
499
500
/*
501
 * 'cupsPutFile()' - Put a file on the server.
502
 *
503
 * This function returns @code HTTP_CREATED@ when the file is stored
504
 * successfully.
505
 *
506
 * @since CUPS 1.1.20@
507
 */
508
509
http_status_t       /* O - HTTP status */
510
cupsPutFile(http_t     *http,   /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
511
            const char *resource, /* I - Resource name */
512
      const char *filename) /* I - Filename */
513
0
{
514
0
  int   fd;     /* File descriptor */
515
0
  http_status_t status;     /* Status */
516
517
518
 /*
519
  * Range check input...
520
  */
521
522
0
  if (!http || !resource || !filename)
523
0
  {
524
0
    if (http)
525
0
      http->error = EINVAL;
526
527
0
    return (HTTP_STATUS_ERROR);
528
0
  }
529
530
 /*
531
  * Open the local file...
532
  */
533
534
0
  if ((fd = open(filename, O_RDONLY)) < 0)
535
0
  {
536
   /*
537
    * Couldn't open the file!
538
    */
539
540
0
    http->error = errno;
541
542
0
    return (HTTP_STATUS_ERROR);
543
0
  }
544
545
 /*
546
  * Put the file...
547
  */
548
549
0
  status = cupsPutFd(http, resource, fd);
550
551
0
  close(fd);
552
553
0
  return (status);
554
0
}