Coverage Report

Created: 2025-10-13 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcups/cups/file.c
Line
Count
Source
1
//
2
// File functions for CUPS.
3
//
4
// Since stdio files max out at 256 files on many systems, we have to
5
// write similar functions without this limit.  At the same time, using
6
// our own file functions allows us to provide transparent support of
7
// different line endings, gzip'd print files, etc.
8
//
9
// Copyright © 2021-2025 by OpenPrinting.
10
// Copyright © 2007-2019 by Apple Inc.
11
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
12
//
13
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
14
// information.
15
//
16
17
#include "file-private.h"
18
#include "debug-internal.h"
19
#include <sys/stat.h>
20
#include <sys/types.h>
21
#include <zlib.h>
22
#ifndef va_copy
23
#  define va_copy(__list1, __list2) ((void)(__list1 = __list2))
24
#endif
25
26
27
//
28
// Internal structures...
29
//
30
31
struct _cups_file_s     // CUPS file structure...
32
{
33
  int   fd;     // File descriptor
34
  bool    compressed;   // Compression used?
35
  char    mode,     // Mode ('r' or 'w')
36
    buf[4096],    // Buffer
37
    *ptr,     // Pointer into buffer
38
    *end;     // End of buffer data
39
  bool    is_stdio,   // stdin/out/err?
40
    eof;      // End of file?
41
  off_t   pos,      // Position in file
42
    bufpos;     // File position for start of buffer
43
44
  z_stream  stream;     // (De)compression stream
45
  Bytef   cbuf[4096];   // (De)compression buffer
46
  uLong   crc;      // (De)compression CRC
47
48
  char    *printf_buffer;   // cupsFilePrintf buffer
49
  size_t  printf_size;    // Size of cupsFilePrintf buffer
50
};
51
52
53
//
54
// Local functions...
55
//
56
57
static bool cups_compress(cups_file_t *fp, const char *buf, size_t bytes);
58
static ssize_t  cups_fill(cups_file_t *fp);
59
static int  cups_open(const char *filename, int oflag, int mode);
60
static ssize_t  cups_read(cups_file_t *fp, char *buf, size_t bytes);
61
static bool cups_write(cups_file_t *fp, const char *buf, size_t bytes);
62
63
64
//
65
// 'cupsFileClose()' - Close a CUPS file.
66
//
67
68
bool          // O - `true` on success, `false` on error
69
cupsFileClose(cups_file_t *fp)    // I - CUPS file
70
14.8k
{
71
14.8k
  int fd;       // File descriptor
72
14.8k
  char  mode;       // Open mode
73
14.8k
  bool  status;       // Return status
74
75
76
  // Range check...
77
14.8k
  if (!fp)
78
0
    return (false);
79
80
  // Flush pending write data...
81
14.8k
  if (fp->mode == 'w')
82
10.0k
    status = cupsFileFlush(fp);
83
4.82k
  else
84
4.82k
    status = true;
85
86
14.8k
  if (fp->compressed && status)
87
2.33k
  {
88
2.33k
    if (fp->mode == 'r')
89
1.90k
    {
90
      // Free decompression data...
91
1.90k
      inflateEnd(&fp->stream);
92
1.90k
    }
93
427
    else
94
427
    {
95
      // Flush any remaining compressed data...
96
427
      unsigned char trailer[8]; // Trailer CRC and length
97
427
      bool    done;   // Done writing...
98
99
100
427
      fp->stream.avail_in = 0;
101
102
427
      for (done = false;;)
103
1.34k
      {
104
1.34k
        if (fp->stream.next_out > fp->cbuf)
105
508
  {
106
508
    status = cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf));
107
108
508
    fp->stream.next_out  = fp->cbuf;
109
508
    fp->stream.avail_out = sizeof(fp->cbuf);
110
508
  }
111
112
1.34k
        if (done || !status)
113
427
    break;
114
115
918
        done = deflate(&fp->stream, Z_FINISH) == Z_STREAM_END && fp->stream.next_out == fp->cbuf;
116
918
      }
117
118
      // Write the CRC and length...
119
427
      trailer[0] = (unsigned char)fp->crc;
120
427
      trailer[1] = (unsigned char)(fp->crc >> 8);
121
427
      trailer[2] = (unsigned char)(fp->crc >> 16);
122
427
      trailer[3] = (unsigned char)(fp->crc >> 24);
123
427
      trailer[4] = (unsigned char)fp->pos;
124
427
      trailer[5] = (unsigned char)(fp->pos >> 8);
125
427
      trailer[6] = (unsigned char)(fp->pos >> 16);
126
427
      trailer[7] = (unsigned char)(fp->pos >> 24);
127
128
427
      status = cups_write(fp, (char *)trailer, 8);
129
130
      // Free all memory used by the compression stream...
131
427
      deflateEnd(&(fp->stream));
132
427
    }
133
2.33k
  }
134
135
  // If this is one of the cupsFileStdin/out/err files, return now and don't
136
  // actually free memory or close (these last the life of the process...)
137
14.8k
  if (fp->is_stdio)
138
0
    return (status);
139
140
  // Save the file descriptor we used and free memory...
141
14.8k
  fd   = fp->fd;
142
14.8k
  mode = fp->mode;
143
144
14.8k
  free(fp->printf_buffer);
145
14.8k
  free(fp);
146
147
  // Close the file, returning the close status...
148
14.8k
  if (mode == 's')
149
0
    status = httpAddrClose(NULL, fd);
150
14.8k
  else if (close(fd) < 0)
151
0
    status = false;
152
153
14.8k
  return (status);
154
14.8k
}
155
156
157
//
158
// 'cupsFileEOF()' - Return the end-of-file status.
159
//
160
161
bool          // O - `true` on end of file, `false` otherwise
162
cupsFileEOF(cups_file_t *fp)    // I - CUPS file
163
0
{
164
0
  return (fp ? fp->eof : true);
165
0
}
166
167
168
//
169
// 'cupsFileFind()' - Find a file using the specified path.
170
//
171
// This function allows the paths in the path string to be separated by
172
// colons (POSIX standard) or semicolons (Windows standard) and stores the
173
// result in the buffer supplied.  If the file cannot be found in any of
174
// the supplied paths, `NULL` is returned.  A `NULL` path only matches the
175
// current directory.
176
//
177
178
const char *        // O - Full path to file or `NULL` if not found
179
cupsFileFind(const char *filename,  // I - File to find
180
             const char *path,    // I - Colon/semicolon-separated path
181
             bool       executable, // I - `true` = executable files, `false` = any file/dir
182
       char       *buffer,  // I - Filename buffer
183
       size_t     bufsize)  // I - Size of filename buffer
184
854
{
185
854
  char  *bufptr,      // Current position in buffer
186
854
  *bufend;      // End of buffer
187
188
189
  // Range check input...
190
854
  if (!filename || !buffer || bufsize < 2)
191
0
    return (NULL);
192
193
854
  if (!path)
194
0
  {
195
    // No path, so check current directory...
196
0
    if (!access(filename, 0))
197
0
    {
198
0
      cupsCopyString(buffer, filename, (size_t)bufsize);
199
0
      return (buffer);
200
0
    }
201
0
    else
202
0
    {
203
0
      return (NULL);
204
0
    }
205
0
  }
206
207
  // Now check each path and return the first match...
208
854
  bufend = buffer + bufsize - 1;
209
854
  bufptr = buffer;
210
211
3.84k
  while (*path)
212
2.98k
  {
213
#ifdef _WIN32
214
    if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255))))
215
#else
216
2.98k
    if (*path == ';' || *path == ':')
217
427
#endif // _WIN32
218
427
    {
219
427
      if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
220
427
        *bufptr++ = '/';
221
222
427
      cupsCopyString(bufptr, filename, (size_t)(bufend - bufptr));
223
224
#ifdef _WIN32
225
      if (!access(buffer, 0))
226
#else
227
427
      if (!access(buffer, executable ? X_OK : 0))
228
0
#endif // _WIN32
229
0
        return (buffer);
230
231
427
      bufptr = buffer;
232
427
    }
233
2.56k
    else if (bufptr < bufend)
234
2.56k
      *bufptr++ = *path;
235
236
2.98k
    path ++;
237
2.98k
  }
238
239
  // Check the last path...
240
854
  if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
241
854
    *bufptr++ = '/';
242
243
854
  cupsCopyString(bufptr, filename, (size_t)(bufend - bufptr));
244
245
854
  if (!access(buffer, 0))
246
427
    return (buffer);
247
427
  else
248
427
    return (NULL);
249
854
}
250
251
252
//
253
// 'cupsFileFlush()' - Flush pending output.
254
//
255
256
bool          // O - `true` on success, `false` on error
257
cupsFileFlush(cups_file_t *fp)    // I - CUPS file
258
162k
{
259
162k
  ssize_t bytes;      // Bytes to write
260
162k
  bool    ret;      // Return value
261
262
263
  // Range check input...
264
162k
  if (!fp || fp->mode != 'w')
265
0
    return (false);
266
267
162k
  bytes = (ssize_t)(fp->ptr - fp->buf);
268
269
162k
  if (bytes > 0)
270
151k
  {
271
151k
    if (fp->compressed)
272
445
      ret = cups_compress(fp, fp->buf, (size_t)bytes);
273
151k
    else
274
151k
      ret = cups_write(fp, fp->buf, (size_t)bytes);
275
276
151k
    fp->ptr = fp->buf;
277
278
151k
    return (ret);
279
151k
  }
280
281
10.4k
  return (true);
282
162k
}
283
284
285
//
286
// 'cupsFileGetChar()' - Get a single character from a file.
287
//
288
289
int         // O - Character or `-1` on end of file
290
cupsFileGetChar(cups_file_t *fp)  // I - CUPS file
291
13.3M
{
292
  // Range check input...
293
13.3M
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
294
0
    return (-1);
295
296
13.3M
  if (fp->eof)
297
0
    return (-1);
298
299
  // If the input buffer is empty, try to read more data...
300
13.3M
  if (fp->ptr >= fp->end)
301
6.35k
  {
302
6.35k
    if (cups_fill(fp) <= 0)
303
1.63k
      return (-1);
304
6.35k
  }
305
306
  // Return the next character in the buffer...
307
13.3M
  fp->pos ++;
308
309
13.3M
  return (*(fp->ptr)++ & 255);
310
13.3M
}
311
312
313
//
314
// 'cupsFileGetConf()' - Get a line from a configuration file.
315
//
316
317
char *          // O  - Line read or `NULL` on end of file or error
318
cupsFileGetConf(cups_file_t *fp,  // I  - CUPS file
319
                char        *buf, // O  - String buffer
320
    size_t      buflen, // I  - Size of string buffer
321
                char        **value,  // O  - Pointer to value
322
    int         *linenum) // IO - Current line number
323
0
{
324
0
  char  *ptr;       // Pointer into line
325
326
327
  // Range check input...
328
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's') ||
329
0
      !buf || buflen < 2 || !value)
330
0
  {
331
0
    if (value)
332
0
      *value = NULL;
333
334
0
    return (NULL);
335
0
  }
336
337
  // Read the next non-comment line...
338
0
  *value = NULL;
339
340
0
  while (cupsFileGets(fp, buf, buflen))
341
0
  {
342
0
    (*linenum) ++;
343
344
    // Strip any comments...
345
0
    if ((ptr = strchr(buf, '#')) != NULL)
346
0
    {
347
0
      if (ptr > buf && ptr[-1] == '\\')
348
0
      {
349
        // Unquote the #...
350
0
  _cups_strcpy(ptr - 1, ptr);
351
0
      }
352
0
      else
353
0
      {
354
        // Strip the comment and any trailing whitespace...
355
0
  while (ptr > buf)
356
0
  {
357
0
    if (!_cups_isspace(ptr[-1]))
358
0
      break;
359
360
0
    ptr --;
361
0
  }
362
363
0
  *ptr = '\0';
364
0
      }
365
0
    }
366
367
    // Strip leading whitespace...
368
0
    for (ptr = buf; _cups_isspace(*ptr); ptr ++);
369
370
0
    if (ptr > buf)
371
0
      _cups_strcpy(buf, ptr);
372
373
    // See if there is anything left...
374
0
    if (buf[0])
375
0
    {
376
      // Yes, grab any value and return...
377
0
      for (ptr = buf; *ptr; ptr ++)
378
0
      {
379
0
        if (_cups_isspace(*ptr))
380
0
    break;
381
0
      }
382
383
0
      if (*ptr)
384
0
      {
385
        // Have a value, skip any other spaces...
386
0
        while (_cups_isspace(*ptr))
387
0
    *ptr++ = '\0';
388
389
0
        if (*ptr)
390
0
    *value = ptr;
391
392
        // Strip trailing whitespace and > for lines that begin with <...
393
0
        ptr += strlen(ptr) - 1;
394
395
0
        if (buf[0] == '<' && *ptr == '>')
396
0
        {
397
0
    *ptr-- = '\0';
398
0
  }
399
0
  else if (buf[0] == '<' && *ptr != '>')
400
0
        {
401
    // Syntax error...
402
0
    *value = NULL;
403
0
    return (buf);
404
0
  }
405
406
0
        while (ptr > *value && _cups_isspace(*ptr))
407
0
    *ptr-- = '\0';
408
0
      }
409
410
      // Return the line...
411
0
      return (buf);
412
0
    }
413
0
  }
414
415
0
  return (NULL);
416
0
}
417
418
419
//
420
// 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may
421
//                       contain binary data.
422
//
423
// This function differs from @link cupsFileGets@ in that the trailing CR
424
// and LF are preserved, as is any binary data on the line. The buffer is
425
// `nul`-terminated, however you should use the returned length to determine
426
// the number of bytes on the line.
427
//
428
429
size_t          // O - Number of bytes on line or 0 on end of file
430
cupsFileGetLine(cups_file_t *fp,  // I - File to read from
431
                char        *buf, // I - Buffer
432
                size_t      buflen) // I - Size of buffer
433
0
{
434
0
  int   ch;     // Character from file
435
0
  char    *ptr,     // Current position in line buffer
436
0
    *end;     // End of line buffer
437
438
439
  // Range check input...
440
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3)
441
0
    return (0);
442
443
  // Now loop until we have a valid line...
444
0
  for (ptr = buf, end = buf + buflen - 2; ptr < end ;)
445
0
  {
446
0
    if (fp->ptr >= fp->end)
447
0
    {
448
0
      if (cups_fill(fp) <= 0)
449
0
        break;
450
0
    }
451
452
0
    *ptr++ = ch = *(fp->ptr)++;
453
0
    fp->pos ++;
454
455
0
    if (ch == '\r')
456
0
    {
457
      // Check for CR LF...
458
0
      if (fp->ptr >= fp->end)
459
0
      {
460
0
  if (cups_fill(fp) <= 0)
461
0
          break;
462
0
      }
463
464
0
      if (*(fp->ptr) == '\n')
465
0
      {
466
0
        *ptr++ = *(fp->ptr)++;
467
0
  fp->pos ++;
468
0
      }
469
470
0
      break;
471
0
    }
472
0
    else if (ch == '\n')
473
0
    {
474
      // Line feed ends a line...
475
0
      break;
476
0
    }
477
0
  }
478
479
0
  *ptr = '\0';
480
481
0
  return ((size_t)(ptr - buf));
482
0
}
483
484
485
//
486
// 'cupsFileGets()' - Get a CR and/or LF-terminated line.
487
//
488
489
char *          // O - Line read or `NULL` on end of file or error
490
cupsFileGets(cups_file_t *fp,   // I - CUPS file
491
             char        *buf,    // O - String buffer
492
       size_t      buflen)  // I - Size of string buffer
493
496k
{
494
496k
  int   ch;     // Character from file
495
496k
  char    *ptr,     // Current position in line buffer
496
496k
    *end;     // End of line buffer
497
498
499
  // Range check input...
500
496k
  if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2)
501
0
    return (NULL);
502
503
  // Now loop until we have a valid line...
504
13.4M
  for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
505
13.4M
  {
506
13.4M
    if (fp->ptr >= fp->end)
507
7.44k
    {
508
7.44k
      if (cups_fill(fp) <= 0)
509
3.27k
      {
510
3.27k
        if (ptr == buf)
511
1.63k
    return (NULL);
512
1.63k
  else
513
1.63k
          break;
514
3.27k
      }
515
7.44k
    }
516
517
13.3M
    ch = *(fp->ptr)++;
518
13.3M
    fp->pos ++;
519
520
13.3M
    if (ch == '\r')
521
390k
    {
522
      // Check for CR LF...
523
390k
      if (fp->ptr >= fp->end)
524
97
      {
525
97
  if (cups_fill(fp) <= 0)
526
2
          break;
527
97
      }
528
529
390k
      if (*(fp->ptr) == '\n')
530
4.51k
      {
531
4.51k
        fp->ptr ++;
532
4.51k
  fp->pos ++;
533
4.51k
      }
534
535
390k
      break;
536
390k
    }
537
13.0M
    else if (ch == '\n')
538
73.8k
    {
539
      // Line feed ends a line...
540
73.8k
      break;
541
73.8k
    }
542
12.9M
    else
543
12.9M
    {
544
12.9M
      *ptr++ = (char)ch;
545
12.9M
    }
546
13.3M
  }
547
548
495k
  *ptr = '\0';
549
550
495k
  return (buf);
551
496k
}
552
553
554
//
555
// 'cupsFileIsCompressed()' - Return whether a file is compressed.
556
//
557
558
bool          // O - `true` if compressed, `false` if not
559
cupsFileIsCompressed(cups_file_t *fp) // I - CUPS file
560
2.06k
{
561
2.06k
  return (fp ? fp->compressed : false);
562
2.06k
}
563
564
565
//
566
// 'cupsFileLock()' - Temporarily lock access to a file.
567
//
568
569
bool          // O - `true` on success, `false` on error
570
cupsFileLock(cups_file_t *fp,   // I - CUPS file
571
             bool        block)   // I - `true` to wait for the lock, `false` to fail right away
572
990
{
573
  // Range check...
574
990
  if (!fp || fp->mode == 's')
575
0
    return (false);
576
577
  // Try the lock...
578
#ifdef _WIN32
579
  return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0) == 0);
580
#else
581
990
  return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0) == 0);
582
990
#endif // _WIN32
583
990
}
584
585
586
//
587
// 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file.
588
//
589
590
int         // O - File descriptor
591
cupsFileNumber(cups_file_t *fp)   // I - CUPS file
592
1.63k
{
593
1.63k
  if (fp)
594
1.63k
    return (fp->fd);
595
0
  else
596
0
    return (-1);
597
1.63k
}
598
599
600
//
601
// 'cupsFileOpen()' - Open a CUPS file.
602
//
603
// This function opens a file or socket for use with the CUPS file API.
604
//
605
// The "filename" argument is a filename or socket address.
606
//
607
// The "mode" parameter can be "r" to read, "w" to write, overwriting any
608
// existing file, "a" to append to an existing file or create a new file,
609
// or "s" to open a socket connection.
610
//
611
// When opening for writing ("w"), an optional number from `1` to `9` can be
612
// supplied which enables Flate compression of the file.  Compression is
613
// not supported for the "a" (append) mode.
614
//
615
// When opening for writing ("w") or append ("a"), an optional 'm###' suffix
616
// can be used to set the permissions of the opened file.
617
//
618
// When opening a socket connection, the filename is a string of the form
619
// "address:port" or "hostname:port". The socket will make an IPv4 or IPv6
620
// connection as needed, generally preferring IPv6 connections when there is
621
// a choice.
622
//
623
624
cups_file_t *       // O - CUPS file or `NULL` if the file or socket cannot be opened
625
cupsFileOpen(const char *filename,  // I - Name of file
626
             const char *mode)    // I - Open mode
627
14.8k
{
628
14.8k
  cups_file_t *fp;      // New CUPS file
629
14.8k
  int   fd;     // File descriptor
630
14.8k
  char    hostname[1024],   // Hostname
631
14.8k
    *portname;    // Port "name" (number or service)
632
14.8k
  http_addrlist_t *addrlist;    // Host address list
633
14.8k
  int   perm = 0664;    // Permissions for write/append
634
14.8k
  const char  *ptr;     // Pointer into mode string
635
636
637
  // Range check input...
638
14.8k
  if (!filename || !mode || (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || (*mode == 'a' && isdigit(mode[1] & 255)))
639
0
    return (NULL);
640
641
14.8k
  if ((ptr = strchr(mode, 'm')) != NULL && ptr[1] >= '0' && ptr[1] <= '7')
642
0
  {
643
    // Get permissions from mode string...
644
0
    perm = (int)strtol(mode + 1, NULL, 8);
645
0
  }
646
647
  // Open the file...
648
14.8k
  switch (*mode)
649
14.8k
  {
650
0
    case 'a' : // Append file
651
0
        fd = cups_open(filename, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY, perm);
652
0
        break;
653
654
4.82k
    case 'r' : // Read file
655
4.82k
  fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0);
656
4.82k
  break;
657
658
10.0k
    case 'w' : // Write file
659
10.0k
        fd    = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY, perm);
660
10.0k
  if (fd < 0 && errno == ENOENT)
661
7.06k
  {
662
7.06k
    fd = cups_open(filename, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY, perm);
663
7.06k
    if (fd < 0 && errno == EEXIST)
664
0
      fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY, perm);
665
7.06k
  }
666
667
10.0k
  if (fd >= 0)
668
#ifdef _WIN32
669
    _chsize(fd, 0);
670
#else
671
10.0k
    ftruncate(fd, 0);
672
10.0k
#endif // _WIN32
673
10.0k
        break;
674
675
0
    case 's' : // Read/write socket
676
0
        cupsCopyString(hostname, filename, sizeof(hostname));
677
0
  if ((portname = strrchr(hostname, ':')) != NULL)
678
0
    *portname++ = '\0';
679
0
  else
680
0
    return (NULL);
681
682
        // Lookup the hostname and service...
683
0
        if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
684
0
    return (NULL);
685
686
        // Connect to the server...
687
0
        if (!httpAddrConnect(addrlist, &fd, 30000, NULL))
688
0
  {
689
0
    httpAddrFreeList(addrlist);
690
0
    return (NULL);
691
0
  }
692
693
0
  httpAddrFreeList(addrlist);
694
0
  break;
695
696
0
    default : // Remove bogus compiler warning...
697
0
        return (NULL);
698
14.8k
  }
699
700
14.8k
  if (fd < 0)
701
2
    return (NULL);
702
703
  // Create the CUPS file structure...
704
14.8k
  if ((fp = cupsFileOpenFd(fd, mode)) == NULL)
705
0
  {
706
0
    if (*mode == 's')
707
0
      httpAddrClose(NULL, fd);
708
0
    else
709
0
      close(fd);
710
0
  }
711
712
  // Return it...
713
14.8k
  return (fp);
714
14.8k
}
715
716
717
//
718
// 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor.
719
//
720
// This function prepares a file descriptor for use with the CUPS file API.
721
//
722
// The "fd" argument specifies the file descriptor.
723
//
724
// The "mode" argument can be "r" to read, "w" to write, "a" to append,
725
// or "s" to treat the file descriptor as a bidirectional socket connection.
726
//
727
// When opening for writing ("w"), an optional number from `1` to `9` can be
728
// supplied which enables Flate compression of the file.  Compression is
729
// not supported for the "a" (append) mode.
730
//
731
732
cups_file_t *       // O - CUPS file or `NULL` if the file could not be opened
733
cupsFileOpenFd(int        fd,   // I - File descriptor
734
         const char *mode)  // I - Open mode
735
14.8k
{
736
14.8k
  cups_file_t *fp;      // New CUPS file
737
738
739
  // Range check input...
740
14.8k
  if (fd < 0 || !mode || (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || (*mode == 'a' && isdigit(mode[1] & 255)))
741
0
    return (NULL);
742
743
  // Allocate memory...
744
14.8k
  if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
745
0
    return (NULL);
746
747
  // Open the file...
748
14.8k
  fp->fd = fd;
749
750
14.8k
  switch (*mode)
751
14.8k
  {
752
0
    case 'a' :
753
10.0k
    case 'w' :
754
10.0k
        if (*mode == 'a')
755
0
          fp->pos = lseek(fd, 0, SEEK_END);
756
757
10.0k
  fp->mode = 'w';
758
10.0k
  fp->ptr  = fp->buf;
759
10.0k
  fp->end  = fp->buf + sizeof(fp->buf);
760
761
10.0k
  if (mode[1] >= '1' && mode[1] <= '9')
762
427
  {
763
    // Open a compressed stream, so write the standard gzip file header...
764
427
          unsigned char header[10]; // gzip file header
765
427
    time_t  curtime;  // Current time
766
767
768
427
          curtime   = time(NULL);
769
427
    header[0] = 0x1f;
770
427
    header[1] = 0x8b;
771
427
    header[2] = Z_DEFLATED;
772
427
    header[3] = 0;
773
427
    header[4] = (unsigned char)curtime;
774
427
    header[5] = (unsigned char)(curtime >> 8);
775
427
    header[6] = (unsigned char)(curtime >> 16);
776
427
    header[7] = (unsigned char)(curtime >> 24);
777
427
    header[8] = 0;
778
427
    header[9] = 0x03;
779
780
427
    if (!cups_write(fp, (char *)header, 10))
781
0
    {
782
0
            free(fp);
783
0
      return (NULL);
784
0
    }
785
786
          // Initialize the compressor...
787
427
          if (deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) < Z_OK)
788
0
          {
789
0
            free(fp);
790
0
      return (NULL);
791
0
          }
792
793
427
    fp->stream.next_out  = fp->cbuf;
794
427
    fp->stream.avail_out = sizeof(fp->cbuf);
795
427
    fp->compressed       = true;
796
427
    fp->crc              = crc32(0L, Z_NULL, 0);
797
427
  }
798
10.0k
        break;
799
800
10.0k
    case 'r' :
801
4.82k
  fp->mode = 'r';
802
4.82k
  break;
803
804
0
    case 's' :
805
0
        fp->mode = 's';
806
0
  break;
807
808
0
    default : // Remove bogus compiler warning...
809
0
        return (NULL);
810
14.8k
  }
811
812
  // Don't pass this file to child processes...
813
14.8k
#ifndef _WIN32
814
14.8k
  if (fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC))
815
0
    DEBUG_printf("cupsFileOpenFd: fcntl(F_SETFD, FD_CLOEXEC) failed - %s", strerror(errno));
816
14.8k
#endif // !_WIN32
817
818
14.8k
  return (fp);
819
14.8k
}
820
821
822
//
823
// '_cupsFilePeekAhead()' - See if the requested character is buffered up.
824
//
825
826
bool          // O - `true` if present, `false` otherwise
827
_cupsFilePeekAhead(cups_file_t *fp, // I - CUPS file
828
                   int         ch)  // I - Character
829
0
{
830
0
  return (fp && fp->ptr && memchr(fp->ptr, ch, (size_t)(fp->end - fp->ptr)));
831
0
}
832
833
834
//
835
// 'cupsFilePeekChar()' - Peek at the next character from a file.
836
//
837
838
int         // O - Character or `-1` on end of file
839
cupsFilePeekChar(cups_file_t *fp) // I - CUPS file
840
0
{
841
  // Range check input...
842
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
843
0
    return (-1);
844
845
  // If the input buffer is empty, try to read more data...
846
0
  if (fp->ptr >= fp->end)
847
0
  {
848
0
    if (cups_fill(fp) <= 0)
849
0
      return (-1);
850
0
  }
851
852
  // Return the next character in the buffer...
853
0
  return (*(fp->ptr) & 255);
854
0
}
855
856
857
//
858
// 'cupsFilePrintf()' - Write a formatted string.
859
//
860
861
bool          // O - `true` on success, `false` on error
862
cupsFilePrintf(cups_file_t *fp,   // I - CUPS file
863
               const char  *format, // I - Printf-style format string
864
         ...)     // I - Additional args as necessary
865
3.61k
{
866
3.61k
  va_list ap, ap2;    // Argument list
867
3.61k
  ssize_t bytes;      // Formatted size
868
869
870
3.61k
  if (!fp || !format || (fp->mode != 'w' && fp->mode != 's'))
871
0
    return (false);
872
873
3.61k
  if (!fp->printf_buffer)
874
3.61k
  {
875
    // Start with an 1k printf buffer...
876
3.61k
    if ((fp->printf_buffer = malloc(1024)) == NULL)
877
0
      return (false);
878
879
3.61k
    fp->printf_size = 1024;
880
3.61k
  }
881
882
3.61k
  va_start(ap, format);
883
3.61k
  va_copy(ap2, ap);
884
3.61k
  bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap2);
885
3.61k
  va_end(ap2);
886
887
3.61k
  if (bytes >= (ssize_t)fp->printf_size)
888
0
  {
889
    // Expand the printf buffer...
890
0
    char  *temp;      // Temporary buffer pointer
891
892
0
    if (bytes > 65535)
893
0
    {
894
0
      va_end(ap);
895
0
      return (-1);
896
0
    }
897
898
0
    if ((temp = realloc(fp->printf_buffer, (size_t)(bytes + 1))) == NULL)
899
0
    {
900
0
      va_end(ap);
901
0
      return (-1);
902
0
    }
903
904
0
    fp->printf_buffer = temp;
905
0
    fp->printf_size   = (size_t)(bytes + 1);
906
907
0
    bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
908
0
  }
909
910
3.61k
  va_end(ap);
911
912
3.61k
  if (fp->mode == 's')
913
0
  {
914
0
    if (!cups_write(fp, fp->printf_buffer, (size_t)bytes))
915
0
      return (false);
916
917
0
    fp->pos += bytes;
918
919
0
    return ((int)bytes);
920
0
  }
921
922
3.61k
  if ((fp->ptr + bytes) > fp->end)
923
21
  {
924
21
    if (!cupsFileFlush(fp))
925
0
      return (false);
926
21
  }
927
928
3.61k
  fp->pos += bytes;
929
930
3.61k
  if ((size_t)bytes > sizeof(fp->buf))
931
0
  {
932
0
    if (fp->compressed)
933
0
      return (cups_compress(fp, fp->printf_buffer, (size_t)bytes));
934
0
    else
935
0
      return (cups_write(fp, fp->printf_buffer, (size_t)bytes));
936
0
  }
937
3.61k
  else
938
3.61k
  {
939
3.61k
    memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
940
3.61k
    fp->ptr += bytes;
941
942
3.61k
    if (fp->is_stdio && !cupsFileFlush(fp))
943
0
      return (false);
944
3.61k
    else
945
3.61k
      return (true);
946
3.61k
  }
947
3.61k
}
948
949
950
//
951
// 'cupsFilePutChar()' - Write a character.
952
//
953
954
bool          // O - `true` on success, `false` on error
955
cupsFilePutChar(cups_file_t *fp,  // I - CUPS file
956
                int         c)    // I - Character to write
957
1.63k
{
958
  // Range check input...
959
1.63k
  if (!fp || (fp->mode != 'w' && fp->mode != 's'))
960
0
    return (false);
961
962
1.63k
  if (fp->mode == 's')
963
0
  {
964
    // Send character immediately over socket...
965
0
    char ch;        // Output character
966
967
0
    ch = (char)c;
968
969
0
    if (send(fp->fd, &ch, 1, 0) < 1)
970
0
      return (false);
971
0
  }
972
1.63k
  else
973
1.63k
  {
974
    // Buffer it up...
975
1.63k
    if (fp->ptr >= fp->end)
976
2
    {
977
2
      if (!cupsFileFlush(fp))
978
0
  return (false);
979
2
    }
980
981
1.63k
    *(fp->ptr) ++ = (char)c;
982
1.63k
  }
983
984
1.63k
  fp->pos ++;
985
986
1.63k
  return (true);
987
1.63k
}
988
989
990
//
991
// 'cupsFilePutConf()' - Write a configuration line.
992
//
993
// This function handles any comment escaping of the value.
994
//
995
996
bool          // O - `true` on success, `false` on error
997
cupsFilePutConf(cups_file_t *fp,  // I - CUPS file
998
                const char *directive,  // I - Directive
999
    const char *value)  // I - Value
1000
0
{
1001
0
  const char  *ptr;     // Pointer into value
1002
1003
1004
0
  if (!fp || !directive || !*directive)
1005
0
    return (false);
1006
1007
0
  if (!cupsFilePuts(fp, directive))
1008
0
    return (false);
1009
1010
0
  if (!cupsFilePutChar(fp, ' '))
1011
0
    return (false);
1012
1013
0
  if (value && *value)
1014
0
  {
1015
0
    if ((ptr = strchr(value, '#')) != NULL)
1016
0
    {
1017
      // Need to quote the first # in the info string...
1018
0
      if (!cupsFileWrite(fp, value, (size_t)(ptr - value)))
1019
0
        return (false);
1020
1021
0
      if (!cupsFilePutChar(fp, '\\'))
1022
0
        return (false);
1023
1024
0
      if (!cupsFilePuts(fp, ptr))
1025
0
        return (false);
1026
0
    }
1027
0
    else if (!cupsFilePuts(fp, value))
1028
0
    {
1029
0
      return (false);
1030
0
    }
1031
0
  }
1032
1033
0
  return (cupsFilePutChar(fp, '\n'));
1034
0
}
1035
1036
1037
//
1038
// 'cupsFilePuts()' - Write a string.
1039
//
1040
// Like the `fputs` function, no newline is appended to the string.
1041
//
1042
1043
bool          // O - `true` on success, `false` on error
1044
cupsFilePuts(cups_file_t *fp,   // I - CUPS file
1045
             const char  *s)    // I - String to write
1046
1.63k
{
1047
1.63k
  size_t  bytes;      // Bytes to write
1048
1049
1050
  // Range check input...
1051
1.63k
  if (!fp || !s || (fp->mode != 'w' && fp->mode != 's'))
1052
0
    return (false);
1053
1054
  // Write the string...
1055
1.63k
  bytes = strlen(s);
1056
1057
1.63k
  if (fp->mode == 's')
1058
0
  {
1059
0
    if (!cups_write(fp, s, bytes))
1060
0
      return (false);
1061
1062
0
    fp->pos += bytes;
1063
1064
0
    return (true);
1065
0
  }
1066
1067
1.63k
  if ((fp->ptr + bytes) > fp->end)
1068
11
  {
1069
11
    if (!cupsFileFlush(fp))
1070
0
      return (false);
1071
11
  }
1072
1073
1.63k
  fp->pos += bytes;
1074
1075
1.63k
  if (bytes > sizeof(fp->buf))
1076
0
  {
1077
0
    if (fp->compressed)
1078
0
      return (cups_compress(fp, s, bytes) > 0);
1079
0
    else
1080
0
      return (cups_write(fp, s, bytes) > 0);
1081
0
  }
1082
1.63k
  else
1083
1.63k
  {
1084
1.63k
    memcpy(fp->ptr, s, bytes);
1085
1.63k
    fp->ptr += bytes;
1086
1087
1.63k
    if (fp->is_stdio && !cupsFileFlush(fp))
1088
0
      return (false);
1089
1.63k
    else
1090
1.63k
      return (true);
1091
1.63k
  }
1092
1.63k
}
1093
1094
1095
//
1096
// 'cupsFileRead()' - Read from a file.
1097
//
1098
1099
ssize_t         // O - Number of bytes read or -1 on error
1100
cupsFileRead(cups_file_t *fp,   // I - CUPS file
1101
             char        *buf,    // O - Buffer
1102
       size_t      bytes)   // I - Number of bytes to read
1103
62.2M
{
1104
62.2M
  size_t  total;      // Total bytes read
1105
62.2M
  ssize_t count;      // Bytes read
1106
1107
1108
  // Range check input...
1109
62.2M
  if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's'))
1110
0
    return (-1);
1111
1112
62.2M
  if (bytes == 0)
1113
0
    return (0);
1114
1115
62.2M
  if (fp->eof)
1116
0
    return (-1);
1117
1118
  // Loop until all bytes are read...
1119
62.2M
  total = 0;
1120
124M
  while (bytes > 0)
1121
62.3M
  {
1122
62.3M
    if (fp->ptr >= fp->end)
1123
171k
    {
1124
171k
      if (cups_fill(fp) <= 0)
1125
3.51k
      {
1126
3.51k
        if (total > 0)
1127
2.35k
          return ((ssize_t)total);
1128
1.16k
  else
1129
1.16k
    return (-1);
1130
3.51k
      }
1131
171k
    }
1132
1133
62.3M
    count = (ssize_t)(fp->end - fp->ptr);
1134
62.3M
    if (count > (ssize_t)bytes)
1135
62.1M
      count = (ssize_t)bytes;
1136
1137
62.3M
    memcpy(buf, fp->ptr,(size_t) count);
1138
62.3M
    fp->ptr += count;
1139
62.3M
    fp->pos += count;
1140
1141
    // Update the counts for the last read...
1142
62.3M
    bytes -= (size_t)count;
1143
62.3M
    total += (size_t)count;
1144
62.3M
    buf   += count;
1145
62.3M
  }
1146
1147
  // Return the total number of bytes read...
1148
62.2M
  return ((ssize_t)total);
1149
62.2M
}
1150
1151
1152
//
1153
// 'cupsFileRewind()' - Set the current file position to the beginning of the file.
1154
//
1155
1156
off_t         // O - New file position or `-1` on error
1157
cupsFileRewind(cups_file_t *fp)   // I - CUPS file
1158
4.91k
{
1159
  // Range check input...
1160
4.91k
  if (!fp || fp->mode != 'r')
1161
0
    return (-1);
1162
1163
  // Handle special cases...
1164
4.91k
  if (fp->bufpos == 0)
1165
447
  {
1166
    // No seeking necessary...
1167
447
    fp->pos = 0;
1168
1169
447
    if (fp->ptr)
1170
447
    {
1171
447
      fp->ptr = fp->buf;
1172
447
      fp->eof = false;
1173
447
    }
1174
1175
447
    return (0);
1176
447
  }
1177
1178
  // Otherwise, seek in the file and cleanup any compression buffers...
1179
4.46k
  if (fp->compressed)
1180
324
  {
1181
324
    inflateEnd(&fp->stream);
1182
324
    fp->compressed = false;
1183
324
  }
1184
1185
4.46k
  if (lseek(fp->fd, 0, SEEK_SET))
1186
0
    return (-1);
1187
1188
4.46k
  fp->bufpos = 0;
1189
4.46k
  fp->pos    = 0;
1190
4.46k
  fp->ptr    = NULL;
1191
4.46k
  fp->end    = NULL;
1192
4.46k
  fp->eof    = false;
1193
1194
4.46k
  return (0);
1195
4.46k
}
1196
1197
1198
//
1199
// 'cupsFileSeek()' - Seek in a file.
1200
//
1201
1202
off_t         // O - New file position or `-1` on error
1203
cupsFileSeek(cups_file_t *fp,   // I - CUPS file
1204
             off_t       pos)   // I - Position in file
1205
3.27k
{
1206
3.27k
  ssize_t bytes;      // Number bytes in buffer
1207
1208
1209
  // Range check input...
1210
3.27k
  if (!fp || pos < 0 || fp->mode != 'r')
1211
0
    return (-1);
1212
1213
  // Handle special cases...
1214
3.27k
  if (pos == 0)
1215
1.63k
    return (cupsFileRewind(fp));
1216
1217
1.63k
  if (fp->ptr)
1218
0
  {
1219
0
    bytes = (ssize_t)(fp->end - fp->buf);
1220
1221
0
    if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1222
0
    {
1223
      // No seeking necessary...
1224
0
      fp->pos = pos;
1225
0
      fp->ptr = fp->buf + (pos - fp->bufpos);
1226
0
      fp->eof = false;
1227
1228
0
      return (pos);
1229
0
    }
1230
0
  }
1231
1232
1.63k
  if (!fp->compressed && !fp->ptr)
1233
1.63k
  {
1234
    // Preload a buffer to determine whether the file is compressed...
1235
1.63k
    if (cups_fill(fp) <= 0)
1236
0
      return (-1);
1237
1.63k
  }
1238
1239
  // Seek forwards or backwards...
1240
1.63k
  fp->eof = false;
1241
1242
1.63k
  if (pos < fp->bufpos)
1243
0
  {
1244
    // Need to seek backwards...
1245
0
    if (fp->compressed)
1246
0
    {
1247
0
      inflateEnd(&fp->stream);
1248
1249
0
      lseek(fp->fd, 0, SEEK_SET);
1250
0
      fp->bufpos = 0;
1251
0
      fp->pos    = 0;
1252
0
      fp->ptr    = NULL;
1253
0
      fp->end    = NULL;
1254
1255
0
      while ((bytes = cups_fill(fp)) > 0)
1256
0
      {
1257
0
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1258
0
    break;
1259
0
      }
1260
1261
0
      if (bytes <= 0)
1262
0
        return (-1);
1263
1264
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1265
0
      fp->pos = pos;
1266
0
    }
1267
0
    else
1268
0
    {
1269
0
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1270
0
      fp->pos    = fp->bufpos;
1271
0
      fp->ptr    = NULL;
1272
0
      fp->end    = NULL;
1273
0
    }
1274
0
  }
1275
1.63k
  else
1276
1.63k
  {
1277
    // Need to seek forwards...
1278
1.63k
    if (fp->compressed)
1279
179
    {
1280
2.82k
      while ((bytes = cups_fill(fp)) > 0)
1281
2.64k
      {
1282
2.64k
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1283
0
    break;
1284
2.64k
      }
1285
1286
179
      if (bytes <= 0)
1287
179
        return (-1);
1288
1289
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1290
0
      fp->pos = pos;
1291
0
    }
1292
1.45k
    else
1293
1.45k
    {
1294
1.45k
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1295
1.45k
      fp->pos    = fp->bufpos;
1296
1.45k
      fp->ptr    = NULL;
1297
1.45k
      fp->end    = NULL;
1298
1.45k
    }
1299
1.63k
  }
1300
1301
1.45k
  return (fp->pos);
1302
1.63k
}
1303
1304
1305
//
1306
// 'cupsFileStderr()' - Return a CUPS file associated with stderr.
1307
//
1308
1309
cups_file_t *       // O - CUPS file
1310
cupsFileStderr(void)
1311
0
{
1312
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1313
1314
1315
  // Open file descriptor 2 as needed...
1316
0
  if (!cg->stdio_files[2])
1317
0
  {
1318
    // Flush any pending output on the stdio file...
1319
0
    fflush(stderr);
1320
1321
    // Open file descriptor 2...
1322
0
    if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL)
1323
0
      cg->stdio_files[2]->is_stdio = true;
1324
0
  }
1325
1326
0
  return (cg->stdio_files[2]);
1327
0
}
1328
1329
1330
//
1331
// 'cupsFileStdin()' - Return a CUPS file associated with stdin.
1332
//
1333
1334
cups_file_t *       // O - CUPS file
1335
cupsFileStdin(void)
1336
0
{
1337
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1338
1339
1340
  // Open file descriptor 0 as needed...
1341
0
  if (!cg->stdio_files[0])
1342
0
  {
1343
    // Open file descriptor 0...
1344
0
    if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL)
1345
0
      cg->stdio_files[0]->is_stdio = true;
1346
0
  }
1347
1348
0
  return (cg->stdio_files[0]);
1349
0
}
1350
1351
1352
//
1353
// 'cupsFileStdout()' - Return a CUPS file associated with stdout.
1354
//
1355
1356
cups_file_t *       // O - CUPS file
1357
cupsFileStdout(void)
1358
0
{
1359
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1360
1361
1362
  // Open file descriptor 1 as needed...
1363
0
  if (!cg->stdio_files[1])
1364
0
  {
1365
    // Flush any pending output on the stdio file...
1366
0
    fflush(stdout);
1367
1368
    // Open file descriptor 1...
1369
0
    if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL)
1370
0
      cg->stdio_files[1]->is_stdio = true;
1371
0
  }
1372
1373
0
  return (cg->stdio_files[1]);
1374
0
}
1375
1376
1377
//
1378
// 'cupsFileTell()' - Return the current file position.
1379
//
1380
1381
off_t         // O - File position
1382
cupsFileTell(cups_file_t *fp)   // I - CUPS file
1383
1.63k
{
1384
1.63k
  return (fp ? fp->pos : 0);
1385
1.63k
}
1386
1387
1388
//
1389
// 'cupsFileUnlock()' - Unlock access to a file.
1390
//
1391
1392
bool          // O - `true` on success, `false` on error
1393
cupsFileUnlock(cups_file_t *fp)   // I - CUPS file
1394
495
{
1395
  // Range check...
1396
495
  if (!fp || fp->mode == 's')
1397
0
    return (false);
1398
1399
  // Unlock...
1400
#ifdef _WIN32
1401
  return (_locking(fp->fd, _LK_UNLCK, 0) == 0);
1402
#else
1403
495
  return (lockf(fp->fd, F_ULOCK, 0) == 0);
1404
495
#endif // _WIN32
1405
495
}
1406
1407
1408
//
1409
// 'cupsFileWrite()' - Write to a file.
1410
//
1411
1412
bool          // O - `true` on success, `false` on error
1413
cupsFileWrite(cups_file_t *fp,    // I - CUPS file
1414
              const char  *buf,   // I - Buffer
1415
        size_t      bytes)  // I - Number of bytes to write
1416
9.41M
{
1417
  // Range check input...
1418
9.41M
  if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's'))
1419
0
    return (false);
1420
1421
9.41M
  if (bytes == 0)
1422
0
    return (true);
1423
1424
  // Write the buffer...
1425
9.41M
  if (fp->mode == 's')
1426
0
  {
1427
0
    if (!cups_write(fp, buf, bytes))
1428
0
      return (false);
1429
1430
0
    fp->pos += (off_t)bytes;
1431
1432
0
    return (true);
1433
0
  }
1434
1435
9.41M
  if ((fp->ptr + bytes) > fp->end)
1436
150k
  {
1437
150k
    if (!cupsFileFlush(fp))
1438
0
      return (false);
1439
150k
  }
1440
1441
9.41M
  fp->pos += (off_t)bytes;
1442
1443
9.41M
  if (bytes > sizeof(fp->buf))
1444
30.1k
  {
1445
30.1k
    if (fp->compressed)
1446
121
      return (cups_compress(fp, buf, bytes));
1447
29.9k
    else
1448
29.9k
      return (cups_write(fp, buf, bytes));
1449
30.1k
  }
1450
9.38M
  else
1451
9.38M
  {
1452
9.38M
    memcpy(fp->ptr, buf, bytes);
1453
9.38M
    fp->ptr += bytes;
1454
9.38M
    return (true);
1455
9.38M
  }
1456
9.41M
}
1457
1458
1459
//
1460
// 'cups_compress()' - Compress a buffer of data.
1461
//
1462
1463
static bool       // O - `true` on success, `false` on error
1464
cups_compress(cups_file_t *fp,    // I - CUPS file
1465
              const char  *buf,   // I - Buffer
1466
        size_t      bytes)  // I - Number bytes
1467
566
{
1468
566
  int status;       // Deflate status
1469
1470
1471
  // Update the CRC...
1472
566
  fp->crc = crc32(fp->crc, (const Bytef *)buf, (uInt)bytes);
1473
1474
  // Deflate the bytes...
1475
566
  fp->stream.next_in  = (Bytef *)buf;
1476
566
  fp->stream.avail_in = (uInt)bytes;
1477
1478
1.19k
  while (fp->stream.avail_in > 0)
1479
632
  {
1480
    // Flush the current buffer...
1481
632
    if (fp->stream.avail_out < (uInt)(sizeof(fp->cbuf) / 8))
1482
83
    {
1483
83
      if (!cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf)))
1484
0
        return (false);
1485
1486
83
      fp->stream.next_out  = fp->cbuf;
1487
83
      fp->stream.avail_out = sizeof(fp->cbuf);
1488
83
    }
1489
1490
632
    if ((status = deflate(&(fp->stream), Z_NO_FLUSH)) < Z_OK && status != Z_BUF_ERROR)
1491
0
      return (false);
1492
632
  }
1493
1494
566
  return (true);
1495
566
}
1496
1497
1498
//
1499
// 'cups_fill()' - Fill the input buffer.
1500
//
1501
1502
static ssize_t        // O - Number of bytes or -1
1503
cups_fill(cups_file_t *fp)    // I - CUPS file
1504
189k
{
1505
189k
  ssize_t   bytes;    // Number of bytes read
1506
189k
  int     status;   // Decompression status
1507
189k
  const unsigned char *ptr,   // Pointer into buffer
1508
189k
      *end;   // End of buffer
1509
1510
1511
189k
  if (fp->ptr && fp->end)
1512
180k
    fp->bufpos += fp->end - fp->buf;
1513
1514
189k
  while (!fp->ptr || fp->compressed)
1515
182k
  {
1516
    // Check to see if we have read any data yet; if not, see if we have a compressed file...
1517
182k
    if (!fp->ptr)
1518
9.28k
    {
1519
      // Reset the file position in case we are seeking...
1520
9.28k
      fp->compressed = false;
1521
1522
      // Read the first bytes in the file to determine if we have a gzip'd file...
1523
9.28k
      if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0)
1524
0
      {
1525
        // Can't read from file!
1526
0
        fp->eof = true;
1527
1528
0
  return (-1);
1529
0
      }
1530
1531
9.28k
      if (bytes < 10 || fp->buf[0] != 0x1f || (fp->buf[1] & 255) != 0x8b || fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
1532
6.52k
      {
1533
        // Not a gzip'd file!
1534
6.52k
  fp->ptr = fp->buf;
1535
6.52k
  fp->end = fp->buf + bytes;
1536
1537
6.52k
  return (bytes);
1538
6.52k
      }
1539
1540
      // Parse header junk: extra data, original name, and comment...
1541
2.76k
      ptr = (unsigned char *)fp->buf + 10;
1542
2.76k
      end = (unsigned char *)fp->buf + bytes;
1543
1544
2.76k
      if (fp->buf[3] & 0x04)
1545
107
      {
1546
        // Skip extra data...
1547
107
  if ((ptr + 2) > end)
1548
5
  {
1549
    // Can't read from file!
1550
5
          fp->eof = true;
1551
5
    errno   = EIO;
1552
1553
5
    return (-1);
1554
5
  }
1555
1556
102
  bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
1557
102
  ptr   += 2 + bytes;
1558
1559
102
  if (ptr > end)
1560
34
  {
1561
    // Can't read from file!
1562
34
          fp->eof = true;
1563
34
    errno   = EIO;
1564
1565
34
    return (-1);
1566
34
  }
1567
102
      }
1568
1569
2.72k
      if (fp->buf[3] & 0x08)
1570
51
      {
1571
        // Skip original name data...
1572
4.37k
  while (ptr < end && *ptr)
1573
4.32k
          ptr ++;
1574
1575
51
  if (ptr < end)
1576
31
  {
1577
31
          ptr ++;
1578
31
  }
1579
20
  else
1580
20
  {
1581
    // Can't read from file!
1582
20
          fp->eof = true;
1583
20
    errno   = EIO;
1584
1585
20
    return (-1);
1586
20
  }
1587
51
      }
1588
1589
2.70k
      if (fp->buf[3] & 0x10)
1590
26
      {
1591
        // Skip comment data...
1592
1.01k
  while (ptr < end && *ptr)
1593
986
          ptr ++;
1594
1595
26
  if (ptr < end)
1596
8
  {
1597
8
          ptr ++;
1598
8
  }
1599
18
  else
1600
18
  {
1601
    // Can't read from file!
1602
18
          fp->eof = true;
1603
18
    errno   = EIO;
1604
1605
18
    return (-1);
1606
18
  }
1607
26
      }
1608
1609
2.69k
      if (fp->buf[3] & 0x02)
1610
70
      {
1611
        // Skip header CRC data...
1612
70
  ptr += 2;
1613
1614
70
  if (ptr > end)
1615
3
  {
1616
    // Can't read from file!
1617
3
          fp->eof = true;
1618
3
    errno   = EIO;
1619
1620
3
    return (-1);
1621
3
  }
1622
70
      }
1623
1624
      // Copy the flate-compressed data to the compression buffer...
1625
2.68k
      if ((bytes = end - ptr) > 0)
1626
2.68k
        memcpy(fp->cbuf, ptr, (size_t)bytes);
1627
1628
      // Setup the decompressor data...
1629
2.68k
      fp->stream.zalloc    = (alloc_func)0;
1630
2.68k
      fp->stream.zfree     = (free_func)0;
1631
2.68k
      fp->stream.opaque    = (voidpf)0;
1632
2.68k
      fp->stream.next_in   = (Bytef *)fp->cbuf;
1633
2.68k
      fp->stream.next_out  = NULL;
1634
2.68k
      fp->stream.avail_in  = (uInt)bytes;
1635
2.68k
      fp->stream.avail_out = 0;
1636
2.68k
      fp->crc              = crc32(0L, Z_NULL, 0);
1637
1638
2.68k
      if (inflateInit2(&(fp->stream), -15) != Z_OK)
1639
0
      {
1640
0
        fp->eof = true;
1641
0
        errno   = EIO;
1642
1643
0
  return (-1);
1644
0
      }
1645
1646
2.68k
      fp->compressed = true;
1647
2.68k
    }
1648
1649
175k
    if (fp->compressed)
1650
175k
    {
1651
      // If we have reached end-of-file, return immediately...
1652
175k
      if (fp->eof)
1653
149
  return (0);
1654
1655
      // Fill the decompression buffer as needed...
1656
175k
      if (fp->stream.avail_in == 0)
1657
3.40k
      {
1658
3.40k
  if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
1659
1.58k
  {
1660
1.58k
    fp->eof = true;
1661
1662
1.58k
          return (bytes);
1663
1.58k
  }
1664
1665
1.82k
  fp->stream.next_in  = fp->cbuf;
1666
1.82k
  fp->stream.avail_in = (uInt)bytes;
1667
1.82k
      }
1668
1669
      // Decompress data from the buffer...
1670
174k
      fp->stream.next_out  = (Bytef *)fp->buf;
1671
174k
      fp->stream.avail_out = sizeof(fp->buf);
1672
1673
174k
      status = inflate(&(fp->stream), Z_NO_FLUSH);
1674
1675
174k
      if (fp->stream.next_out > (Bytef *)fp->buf)
1676
173k
        fp->crc = crc32(fp->crc, (Bytef *)fp->buf, (uInt)(fp->stream.next_out - (Bytef *)fp->buf));
1677
1678
174k
      if (status == Z_STREAM_END)
1679
460
      {
1680
        // Read the CRC and length...
1681
460
  unsigned char trailer[8]; // Trailer bytes
1682
460
  uLong   tcrc;   // Trailer CRC
1683
460
  ssize_t   tbytes = 0; // Number of bytes
1684
1685
460
  if (fp->stream.avail_in > 0)
1686
457
  {
1687
    // Get the first N trailer bytes from the inflate stream...
1688
457
    if (fp->stream.avail_in > sizeof(trailer))
1689
112
      tbytes = (ssize_t)sizeof(trailer);
1690
345
    else
1691
345
      tbytes = (ssize_t)fp->stream.avail_in;
1692
1693
457
    memcpy(trailer, fp->stream.next_in, (size_t)tbytes);
1694
457
    fp->stream.next_in  += tbytes;
1695
457
    fp->stream.avail_in -= (size_t)tbytes;
1696
457
  }
1697
1698
        // Reset the compressed flag so that we re-read the file header...
1699
460
        inflateEnd(&fp->stream);
1700
1701
460
  fp->compressed = false;
1702
1703
        // Get any remaining trailer bytes...
1704
460
        if (tbytes < (ssize_t)sizeof(trailer))
1705
16
  {
1706
16
    if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes))
1707
11
    {
1708
      // Can't get it, so mark end-of-file...
1709
11
      fp->eof = true;
1710
11
      errno   = EIO;
1711
1712
11
      return (-1);
1713
11
    }
1714
16
  }
1715
1716
        // Calculate and compare the CRC...
1717
449
  tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) | (uLong)trailer[1]) << 8) | (uLong)trailer[0];
1718
1719
449
  if (tcrc != fp->crc)
1720
165
  {
1721
    // Bad CRC, mark end-of-file...
1722
165
    fp->eof = true;
1723
165
    errno   = EIO;
1724
1725
165
    return (-1);
1726
165
  }
1727
449
      }
1728
173k
      else if (status < Z_OK)
1729
101
      {
1730
101
        fp->eof = true;
1731
101
        errno   = EIO;
1732
1733
101
  return (-1);
1734
101
      }
1735
1736
173k
      bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out;
1737
1738
      // Return the decompressed data...
1739
173k
      fp->ptr = fp->buf;
1740
173k
      fp->end = fp->buf + bytes;
1741
1742
173k
      if (bytes)
1743
173k
  return (bytes);
1744
173k
    }
1745
175k
  }
1746
1747
  // Read a buffer's full of data...
1748
7.51k
  if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0)
1749
6.51k
  {
1750
    // Can't read from file!
1751
6.51k
    fp->eof = true;
1752
6.51k
    fp->ptr = fp->buf;
1753
6.51k
    fp->end = fp->buf;
1754
6.51k
  }
1755
998
  else
1756
998
  {
1757
    // Return the bytes we read...
1758
998
    fp->eof = false;
1759
998
    fp->ptr = fp->buf;
1760
998
    fp->end = fp->buf + bytes;
1761
998
  }
1762
1763
7.51k
  return (bytes);
1764
189k
}
1765
1766
1767
//
1768
// 'cups_open()' - Safely open a file for writing.
1769
//
1770
// We don't allow appending to directories or files that are hard-linked or
1771
// symlinked.
1772
//
1773
1774
static int        // O - File descriptor or -1 otherwise
1775
cups_open(const char *filename,   // I - Filename
1776
          int        oflag,   // I - Open flags
1777
    int        mode)    // I - Open permissions
1778
17.1k
{
1779
17.1k
  int   fd;     // File descriptor
1780
17.1k
  struct stat fileinfo;   // File information
1781
17.1k
#ifndef _WIN32
1782
17.1k
  struct stat linkinfo;   // Link information
1783
17.1k
#endif // !_WIN32
1784
1785
1786
  // Open the file...
1787
17.1k
  if ((fd = open(filename, oflag, mode)) < 0)
1788
7.06k
    return (-1);
1789
1790
  // Then verify that the file descriptor doesn't point to a directory or hard-linked file.
1791
10.0k
  if (fstat(fd, &fileinfo))
1792
0
  {
1793
0
    int temp = errno;
1794
0
    close(fd);
1795
0
    errno = temp;
1796
0
    return (-1);
1797
0
  }
1798
1799
10.0k
  if (fileinfo.st_nlink != 1)
1800
0
  {
1801
0
    close(fd);
1802
0
    errno = EPERM;
1803
0
    return (-1);
1804
0
  }
1805
1806
#ifdef _WIN32
1807
  if (fileinfo.st_mode & _S_IFDIR)
1808
#else
1809
10.0k
  if (S_ISDIR(fileinfo.st_mode))
1810
0
#endif // _WIN32
1811
0
  {
1812
0
    close(fd);
1813
0
    errno = EISDIR;
1814
0
    return (-1);
1815
0
  }
1816
1817
10.0k
#ifndef _WIN32
1818
  // Then use lstat to determine whether the filename is a symlink...
1819
10.0k
  if (lstat(filename, &linkinfo))
1820
0
  {
1821
0
    int temp = errno;
1822
0
    close(fd);
1823
0
    errno = temp;
1824
0
    return (-1);
1825
0
  }
1826
1827
10.0k
  if (S_ISLNK(linkinfo.st_mode) ||
1828
10.0k
      fileinfo.st_dev != linkinfo.st_dev ||
1829
10.0k
      fileinfo.st_ino != linkinfo.st_ino ||
1830
#ifdef HAVE_ST_GEN
1831
      fileinfo.st_gen != linkinfo.st_gen ||
1832
#endif // HAVE_ST_GEN
1833
10.0k
      fileinfo.st_nlink != linkinfo.st_nlink ||
1834
10.0k
      fileinfo.st_mode != linkinfo.st_mode)
1835
0
  {
1836
    // Yes, don't allow!
1837
0
    close(fd);
1838
0
    errno = EPERM;
1839
0
    return (-1);
1840
0
  }
1841
10.0k
#endif // !_WIN32
1842
1843
10.0k
  return (fd);
1844
10.0k
}
1845
1846
1847
//
1848
// 'cups_read()' - Read from a file descriptor.
1849
//
1850
1851
static ssize_t        // O - Number of bytes read or -1
1852
cups_read(cups_file_t *fp,    // I - CUPS file
1853
          char        *buf,   // I - Buffer
1854
    size_t      bytes)    // I - Number bytes
1855
20.2k
{
1856
20.2k
  ssize_t total;      // Total bytes read
1857
1858
1859
  // Loop until we read at least 0 bytes...
1860
20.2k
  for (;;)
1861
20.2k
  {
1862
#ifdef _WIN32
1863
    if (fp->mode == 's')
1864
      total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
1865
    else
1866
      total = (ssize_t)read(fp->fd, buf, (unsigned)bytes);
1867
#else
1868
20.2k
    if (fp->mode == 's')
1869
0
      total = recv(fp->fd, buf, bytes, 0);
1870
20.2k
    else
1871
20.2k
      total = read(fp->fd, buf, bytes);
1872
20.2k
#endif // _WIN32
1873
1874
20.2k
    if (total >= 0)
1875
20.2k
      break;
1876
1877
    // Reads can be interrupted by signals and unavailable resources...
1878
0
    if (errno == EAGAIN || errno == EINTR)
1879
0
      continue;
1880
0
    else
1881
0
      return (-1);
1882
0
  }
1883
1884
  // Return the total number of bytes read...
1885
20.2k
  return (total);
1886
20.2k
}
1887
1888
1889
//
1890
// 'cups_write()' - Write to a file descriptor.
1891
//
1892
1893
static bool       // O - `true` on success, `false` on error
1894
cups_write(cups_file_t *fp,   // I - CUPS file
1895
           const char  *buf,    // I - Buffer
1896
     size_t      bytes)   // I - Number bytes
1897
182k
{
1898
182k
  ssize_t count;      // Count this time
1899
1900
1901
  // Loop until all bytes are written...
1902
365k
  while (bytes > 0)
1903
182k
  {
1904
#ifdef _WIN32
1905
    if (fp->mode == 's')
1906
      count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
1907
    else
1908
      count = (ssize_t)write(fp->fd, buf, (unsigned)bytes);
1909
#else
1910
182k
    if (fp->mode == 's')
1911
0
      count = send(fp->fd, buf, bytes, 0);
1912
182k
    else
1913
182k
      count = write(fp->fd, buf, bytes);
1914
182k
#endif // _WIN32
1915
1916
182k
    if (count < 0)
1917
0
    {
1918
      // Writes can be interrupted by signals and unavailable resources...
1919
0
      if (errno == EAGAIN || errno == EINTR)
1920
0
        continue;
1921
0
      else
1922
0
        return (false);
1923
0
    }
1924
1925
    // Update the counts for the last write call...
1926
182k
    bytes -= (size_t)count;
1927
182k
    buf   += count;
1928
182k
  }
1929
1930
  // Return the total number of bytes written...
1931
182k
  return (true);
1932
182k
}