Coverage Report

Created: 2025-08-26 06:48

/src/libcups/cups/file.c
Line
Count
Source (jump to first uncovered line)
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
7.93k
{
71
7.93k
  int fd;       // File descriptor
72
7.93k
  char  mode;       // Open mode
73
7.93k
  bool  status;       // Return status
74
75
76
  // Range check...
77
7.93k
  if (!fp)
78
0
    return (false);
79
80
  // Flush pending write data...
81
7.93k
  if (fp->mode == 'w')
82
5.28k
    status = cupsFileFlush(fp);
83
2.64k
  else
84
2.64k
    status = true;
85
86
7.93k
  if (fp->compressed && status)
87
1.52k
  {
88
1.52k
    if (fp->mode == 'r')
89
1.52k
    {
90
      // Free decompression data...
91
1.52k
      inflateEnd(&fp->stream);
92
1.52k
    }
93
0
    else
94
0
    {
95
      // Flush any remaining compressed data...
96
0
      unsigned char trailer[8]; // Trailer CRC and length
97
0
      bool    done;   // Done writing...
98
99
100
0
      fp->stream.avail_in = 0;
101
102
0
      for (done = false;;)
103
0
      {
104
0
        if (fp->stream.next_out > fp->cbuf)
105
0
  {
106
0
    status = cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf));
107
108
0
    fp->stream.next_out  = fp->cbuf;
109
0
    fp->stream.avail_out = sizeof(fp->cbuf);
110
0
  }
111
112
0
        if (done || !status)
113
0
    break;
114
115
0
        done = deflate(&fp->stream, Z_FINISH) == Z_STREAM_END && fp->stream.next_out == fp->cbuf;
116
0
      }
117
118
      // Write the CRC and length...
119
0
      trailer[0] = (unsigned char)fp->crc;
120
0
      trailer[1] = (unsigned char)(fp->crc >> 8);
121
0
      trailer[2] = (unsigned char)(fp->crc >> 16);
122
0
      trailer[3] = (unsigned char)(fp->crc >> 24);
123
0
      trailer[4] = (unsigned char)fp->pos;
124
0
      trailer[5] = (unsigned char)(fp->pos >> 8);
125
0
      trailer[6] = (unsigned char)(fp->pos >> 16);
126
0
      trailer[7] = (unsigned char)(fp->pos >> 24);
127
128
0
      status = cups_write(fp, (char *)trailer, 8);
129
130
      // Free all memory used by the compression stream...
131
0
      deflateEnd(&(fp->stream));
132
0
    }
133
1.52k
  }
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
7.93k
  if (fp->is_stdio)
138
0
    return (status);
139
140
  // Save the file descriptor we used and free memory...
141
7.93k
  fd   = fp->fd;
142
7.93k
  mode = fp->mode;
143
144
7.93k
  free(fp->printf_buffer);
145
7.93k
  free(fp);
146
147
  // Close the file, returning the close status...
148
7.93k
  if (mode == 's')
149
0
    status = httpAddrClose(NULL, fd);
150
7.93k
  else if (close(fd) < 0)
151
0
    status = false;
152
153
7.93k
  return (status);
154
7.93k
}
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
0
{
185
0
  char  *bufptr,      // Current position in buffer
186
0
  *bufend;      // End of buffer
187
188
189
  // Range check input...
190
0
  if (!filename || !buffer || bufsize < 2)
191
0
    return (NULL);
192
193
0
  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
0
  bufend = buffer + bufsize - 1;
209
0
  bufptr = buffer;
210
211
0
  while (*path)
212
0
  {
213
#ifdef _WIN32
214
    if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255))))
215
#else
216
0
    if (*path == ';' || *path == ':')
217
0
#endif // _WIN32
218
0
    {
219
0
      if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
220
0
        *bufptr++ = '/';
221
222
0
      cupsCopyString(bufptr, filename, (size_t)(bufend - bufptr));
223
224
#ifdef _WIN32
225
      if (!access(buffer, 0))
226
#else
227
0
      if (!access(buffer, executable ? X_OK : 0))
228
0
#endif // _WIN32
229
0
        return (buffer);
230
231
0
      bufptr = buffer;
232
0
    }
233
0
    else if (bufptr < bufend)
234
0
      *bufptr++ = *path;
235
236
0
    path ++;
237
0
  }
238
239
  // Check the last path...
240
0
  if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
241
0
    *bufptr++ = '/';
242
243
0
  cupsCopyString(bufptr, filename, (size_t)(bufend - bufptr));
244
245
0
  if (!access(buffer, 0))
246
0
    return (buffer);
247
0
  else
248
0
    return (NULL);
249
0
}
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
140k
{
259
140k
  ssize_t bytes;      // Bytes to write
260
140k
  bool    ret;      // Return value
261
262
263
  // Range check input...
264
140k
  if (!fp || fp->mode != 'w')
265
0
    return (false);
266
267
140k
  bytes = (ssize_t)(fp->ptr - fp->buf);
268
269
140k
  if (bytes > 0)
270
133k
  {
271
133k
    if (fp->compressed)
272
0
      ret = cups_compress(fp, fp->buf, (size_t)bytes);
273
133k
    else
274
133k
      ret = cups_write(fp, fp->buf, (size_t)bytes);
275
276
133k
    fp->ptr = fp->buf;
277
278
133k
    return (ret);
279
133k
  }
280
281
6.58k
  return (true);
282
140k
}
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
0
{
292
  // Range check input...
293
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
294
0
    return (-1);
295
296
0
  if (fp->eof)
297
0
    return (-1);
298
299
  // If the input buffer is empty, try to read more data...
300
0
  if (fp->ptr >= fp->end)
301
0
  {
302
0
    if (cups_fill(fp) <= 0)
303
0
      return (-1);
304
0
  }
305
306
  // Return the next character in the buffer...
307
0
  fp->pos ++;
308
309
0
  return (*(fp->ptr)++ & 255);
310
0
}
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
0
{
494
0
  int   ch;     // Character from file
495
0
  char    *ptr,     // Current position in line buffer
496
0
    *end;     // End of line buffer
497
498
499
  // Range check input...
500
0
  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
0
  for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
505
0
  {
506
0
    if (fp->ptr >= fp->end)
507
0
    {
508
0
      if (cups_fill(fp) <= 0)
509
0
      {
510
0
        if (ptr == buf)
511
0
    return (NULL);
512
0
  else
513
0
          break;
514
0
      }
515
0
    }
516
517
0
    ch = *(fp->ptr)++;
518
0
    fp->pos ++;
519
520
0
    if (ch == '\r')
521
0
    {
522
      // Check for CR LF...
523
0
      if (fp->ptr >= fp->end)
524
0
      {
525
0
  if (cups_fill(fp) <= 0)
526
0
          break;
527
0
      }
528
529
0
      if (*(fp->ptr) == '\n')
530
0
      {
531
0
        fp->ptr ++;
532
0
  fp->pos ++;
533
0
      }
534
535
0
      break;
536
0
    }
537
0
    else if (ch == '\n')
538
0
    {
539
      // Line feed ends a line...
540
0
      break;
541
0
    }
542
0
    else
543
0
    {
544
0
      *ptr++ = (char)ch;
545
0
    }
546
0
  }
547
548
0
  *ptr = '\0';
549
550
0
  return (buf);
551
0
}
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
0
{
561
0
  return (fp ? fp->compressed : false);
562
0
}
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
0
{
573
  // Range check...
574
0
  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
0
  return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0) == 0);
582
0
#endif // _WIN32
583
0
}
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
0
{
593
0
  if (fp)
594
0
    return (fp->fd);
595
0
  else
596
0
    return (-1);
597
0
}
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
7.93k
{
628
7.93k
  cups_file_t *fp;      // New CUPS file
629
7.93k
  int   fd;     // File descriptor
630
7.93k
  char    hostname[1024],   // Hostname
631
7.93k
    *portname;    // Port "name" (number or service)
632
7.93k
  http_addrlist_t *addrlist;    // Host address list
633
7.93k
  int   perm = 0664;    // Permissions for write/append
634
7.93k
  const char  *ptr;     // Pointer into mode string
635
636
637
  // Range check input...
638
7.93k
  if (!filename || !mode || (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || (*mode == 'a' && isdigit(mode[1] & 255)))
639
0
    return (NULL);
640
641
7.93k
  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
7.93k
  switch (*mode)
649
7.93k
  {
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
2.64k
    case 'r' : // Read file
655
2.64k
  fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0);
656
2.64k
  break;
657
658
5.28k
    case 'w' : // Write file
659
5.28k
        fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY, perm);
660
5.28k
  if (fd < 0 && errno == ENOENT)
661
2.64k
  {
662
2.64k
    fd = cups_open(filename, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY, perm);
663
2.64k
    if (fd < 0 && errno == EEXIST)
664
0
      fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY, perm);
665
2.64k
  }
666
667
5.28k
  if (fd >= 0)
668
#ifdef _WIN32
669
    _chsize(fd, 0);
670
#else
671
5.28k
    ftruncate(fd, 0);
672
5.28k
#endif // _WIN32
673
5.28k
        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
7.93k
  }
699
700
7.93k
  if (fd < 0)
701
2
    return (NULL);
702
703
  // Create the CUPS file structure...
704
7.93k
  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
7.93k
  return (fp);
714
7.93k
}
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
7.93k
{
736
7.93k
  cups_file_t *fp;      // New CUPS file
737
738
739
  // Range check input...
740
7.93k
  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
7.93k
  if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
745
0
    return (NULL);
746
747
  // Open the file...
748
7.93k
  fp->fd = fd;
749
750
7.93k
  switch (*mode)
751
7.93k
  {
752
0
    case 'a' :
753
5.28k
    case 'w' :
754
5.28k
        if (*mode == 'a')
755
0
          fp->pos = lseek(fd, 0, SEEK_END);
756
757
5.28k
  fp->mode = 'w';
758
5.28k
  fp->ptr  = fp->buf;
759
5.28k
  fp->end  = fp->buf + sizeof(fp->buf);
760
761
5.28k
  if (mode[1] >= '1' && mode[1] <= '9')
762
0
  {
763
    // Open a compressed stream, so write the standard gzip file header...
764
0
          unsigned char header[10]; // gzip file header
765
0
    time_t  curtime;  // Current time
766
767
768
0
          curtime   = time(NULL);
769
0
    header[0] = 0x1f;
770
0
    header[1] = 0x8b;
771
0
    header[2] = Z_DEFLATED;
772
0
    header[3] = 0;
773
0
    header[4] = (unsigned char)curtime;
774
0
    header[5] = (unsigned char)(curtime >> 8);
775
0
    header[6] = (unsigned char)(curtime >> 16);
776
0
    header[7] = (unsigned char)(curtime >> 24);
777
0
    header[8] = 0;
778
0
    header[9] = 0x03;
779
780
0
    cups_write(fp, (char *)header, 10);
781
782
          // Initialize the compressor...
783
0
          if (deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) < Z_OK)
784
0
          {
785
0
            free(fp);
786
0
      return (NULL);
787
0
          }
788
789
0
    fp->stream.next_out  = fp->cbuf;
790
0
    fp->stream.avail_out = sizeof(fp->cbuf);
791
0
    fp->compressed       = true;
792
0
    fp->crc              = crc32(0L, Z_NULL, 0);
793
0
  }
794
5.28k
        break;
795
796
5.28k
    case 'r' :
797
2.64k
  fp->mode = 'r';
798
2.64k
  break;
799
800
0
    case 's' :
801
0
        fp->mode = 's';
802
0
  break;
803
804
0
    default : // Remove bogus compiler warning...
805
0
        return (NULL);
806
7.93k
  }
807
808
  // Don't pass this file to child processes...
809
7.93k
#ifndef _WIN32
810
7.93k
  if (fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC))
811
0
    DEBUG_printf("cupsFileOpenFd: fcntl(F_SETFD, FD_CLOEXEC) failed - %s", strerror(errno));
812
7.93k
#endif // !_WIN32
813
814
7.93k
  return (fp);
815
7.93k
}
816
817
818
//
819
// '_cupsFilePeekAhead()' - See if the requested character is buffered up.
820
//
821
822
bool          // O - `true` if present, `false` otherwise
823
_cupsFilePeekAhead(cups_file_t *fp, // I - CUPS file
824
                   int         ch)  // I - Character
825
0
{
826
0
  return (fp && fp->ptr && memchr(fp->ptr, ch, (size_t)(fp->end - fp->ptr)));
827
0
}
828
829
830
//
831
// 'cupsFilePeekChar()' - Peek at the next character from a file.
832
//
833
834
int         // O - Character or `-1` on end of file
835
cupsFilePeekChar(cups_file_t *fp) // I - CUPS file
836
0
{
837
  // Range check input...
838
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
839
0
    return (-1);
840
841
  // If the input buffer is empty, try to read more data...
842
0
  if (fp->ptr >= fp->end)
843
0
  {
844
0
    if (cups_fill(fp) <= 0)
845
0
      return (-1);
846
0
  }
847
848
  // Return the next character in the buffer...
849
0
  return (*(fp->ptr) & 255);
850
0
}
851
852
853
//
854
// 'cupsFilePrintf()' - Write a formatted string.
855
//
856
857
bool          // O - `true` on success, `false` on error
858
cupsFilePrintf(cups_file_t *fp,   // I - CUPS file
859
               const char  *format, // I - Printf-style format string
860
         ...)     // I - Additional args as necessary
861
0
{
862
0
  va_list ap, ap2;    // Argument list
863
0
  ssize_t bytes;      // Formatted size
864
865
866
0
  if (!fp || !format || (fp->mode != 'w' && fp->mode != 's'))
867
0
    return (false);
868
869
0
  if (!fp->printf_buffer)
870
0
  {
871
    // Start with an 1k printf buffer...
872
0
    if ((fp->printf_buffer = malloc(1024)) == NULL)
873
0
      return (false);
874
875
0
    fp->printf_size = 1024;
876
0
  }
877
878
0
  va_start(ap, format);
879
0
  va_copy(ap2, ap);
880
0
  bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap2);
881
0
  va_end(ap2);
882
883
0
  if (bytes >= (ssize_t)fp->printf_size)
884
0
  {
885
    // Expand the printf buffer...
886
0
    char  *temp;      // Temporary buffer pointer
887
888
0
    if (bytes > 65535)
889
0
    {
890
0
      va_end(ap);
891
0
      return (-1);
892
0
    }
893
894
0
    if ((temp = realloc(fp->printf_buffer, (size_t)(bytes + 1))) == NULL)
895
0
    {
896
0
      va_end(ap);
897
0
      return (-1);
898
0
    }
899
900
0
    fp->printf_buffer = temp;
901
0
    fp->printf_size   = (size_t)(bytes + 1);
902
903
0
    bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
904
0
  }
905
906
0
  va_end(ap);
907
908
0
  if (fp->mode == 's')
909
0
  {
910
0
    if (!cups_write(fp, fp->printf_buffer, (size_t)bytes))
911
0
      return (false);
912
913
0
    fp->pos += bytes;
914
915
0
    return ((int)bytes);
916
0
  }
917
918
0
  if ((fp->ptr + bytes) > fp->end)
919
0
  {
920
0
    if (!cupsFileFlush(fp))
921
0
      return (false);
922
0
  }
923
924
0
  fp->pos += bytes;
925
926
0
  if ((size_t)bytes > sizeof(fp->buf))
927
0
  {
928
0
    if (fp->compressed)
929
0
      return (cups_compress(fp, fp->printf_buffer, (size_t)bytes));
930
0
    else
931
0
      return (cups_write(fp, fp->printf_buffer, (size_t)bytes));
932
0
  }
933
0
  else
934
0
  {
935
0
    memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
936
0
    fp->ptr += bytes;
937
938
0
    if (fp->is_stdio && !cupsFileFlush(fp))
939
0
      return (false);
940
0
    else
941
0
      return (true);
942
0
  }
943
0
}
944
945
946
//
947
// 'cupsFilePutChar()' - Write a character.
948
//
949
950
bool          // O - `true` on success, `false` on error
951
cupsFilePutChar(cups_file_t *fp,  // I - CUPS file
952
                int         c)    // I - Character to write
953
0
{
954
  // Range check input...
955
0
  if (!fp || (fp->mode != 'w' && fp->mode != 's'))
956
0
    return (false);
957
958
0
  if (fp->mode == 's')
959
0
  {
960
    // Send character immediately over socket...
961
0
    char ch;        // Output character
962
963
0
    ch = (char)c;
964
965
0
    if (send(fp->fd, &ch, 1, 0) < 1)
966
0
      return (false);
967
0
  }
968
0
  else
969
0
  {
970
    // Buffer it up...
971
0
    if (fp->ptr >= fp->end)
972
0
    {
973
0
      if (!cupsFileFlush(fp))
974
0
  return (false);
975
0
    }
976
977
0
    *(fp->ptr) ++ = (char)c;
978
0
  }
979
980
0
  fp->pos ++;
981
982
0
  return (true);
983
0
}
984
985
986
//
987
// 'cupsFilePutConf()' - Write a configuration line.
988
//
989
// This function handles any comment escaping of the value.
990
//
991
992
bool          // O - `true` on success, `false` on error
993
cupsFilePutConf(cups_file_t *fp,  // I - CUPS file
994
                const char *directive,  // I - Directive
995
    const char *value)  // I - Value
996
0
{
997
0
  const char  *ptr;     // Pointer into value
998
999
1000
0
  if (!fp || !directive || !*directive)
1001
0
    return (false);
1002
1003
0
  if (!cupsFilePuts(fp, directive))
1004
0
    return (false);
1005
1006
0
  if (!cupsFilePutChar(fp, ' '))
1007
0
    return (false);
1008
1009
0
  if (value && *value)
1010
0
  {
1011
0
    if ((ptr = strchr(value, '#')) != NULL)
1012
0
    {
1013
      // Need to quote the first # in the info string...
1014
0
      if (!cupsFileWrite(fp, value, (size_t)(ptr - value)))
1015
0
        return (false);
1016
1017
0
      if (!cupsFilePutChar(fp, '\\'))
1018
0
        return (false);
1019
1020
0
      if (!cupsFilePuts(fp, ptr))
1021
0
        return (false);
1022
0
    }
1023
0
    else if (!cupsFilePuts(fp, value))
1024
0
    {
1025
0
      return (false);
1026
0
    }
1027
0
  }
1028
1029
0
  return (cupsFilePutChar(fp, '\n'));
1030
0
}
1031
1032
1033
//
1034
// 'cupsFilePuts()' - Write a string.
1035
//
1036
// Like the `fputs` function, no newline is appended to the string.
1037
//
1038
1039
bool          // O - `true` on success, `false` on error
1040
cupsFilePuts(cups_file_t *fp,   // I - CUPS file
1041
             const char  *s)    // I - String to write
1042
0
{
1043
0
  size_t  bytes;      // Bytes to write
1044
1045
1046
  // Range check input...
1047
0
  if (!fp || !s || (fp->mode != 'w' && fp->mode != 's'))
1048
0
    return (false);
1049
1050
  // Write the string...
1051
0
  bytes = strlen(s);
1052
1053
0
  if (fp->mode == 's')
1054
0
  {
1055
0
    if (!cups_write(fp, s, bytes))
1056
0
      return (false);
1057
1058
0
    fp->pos += bytes;
1059
1060
0
    return (true);
1061
0
  }
1062
1063
0
  if ((fp->ptr + bytes) > fp->end)
1064
0
  {
1065
0
    if (!cupsFileFlush(fp))
1066
0
      return (false);
1067
0
  }
1068
1069
0
  fp->pos += bytes;
1070
1071
0
  if (bytes > sizeof(fp->buf))
1072
0
  {
1073
0
    if (fp->compressed)
1074
0
      return (cups_compress(fp, s, bytes) > 0);
1075
0
    else
1076
0
      return (cups_write(fp, s, bytes) > 0);
1077
0
  }
1078
0
  else
1079
0
  {
1080
0
    memcpy(fp->ptr, s, bytes);
1081
0
    fp->ptr += bytes;
1082
1083
0
    if (fp->is_stdio && !cupsFileFlush(fp))
1084
0
      return (false);
1085
0
    else
1086
0
      return (true);
1087
0
  }
1088
0
}
1089
1090
1091
//
1092
// 'cupsFileRead()' - Read from a file.
1093
//
1094
1095
ssize_t         // O - Number of bytes read or -1 on error
1096
cupsFileRead(cups_file_t *fp,   // I - CUPS file
1097
             char        *buf,    // O - Buffer
1098
       size_t      bytes)   // I - Number of bytes to read
1099
69.8M
{
1100
69.8M
  size_t  total;      // Total bytes read
1101
69.8M
  ssize_t count;      // Bytes read
1102
1103
1104
  // Range check input...
1105
69.8M
  if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's'))
1106
0
    return (-1);
1107
1108
69.8M
  if (bytes == 0)
1109
0
    return (0);
1110
1111
69.8M
  if (fp->eof)
1112
0
    return (-1);
1113
1114
  // Loop until all bytes are read...
1115
69.8M
  total = 0;
1116
139M
  while (bytes > 0)
1117
69.9M
  {
1118
69.9M
    if (fp->ptr >= fp->end)
1119
151k
    {
1120
151k
      if (cups_fill(fp) <= 0)
1121
2.05k
      {
1122
2.05k
        if (total > 0)
1123
889
          return ((ssize_t)total);
1124
1.16k
  else
1125
1.16k
    return (-1);
1126
2.05k
      }
1127
151k
    }
1128
1129
69.9M
    count = (ssize_t)(fp->end - fp->ptr);
1130
69.9M
    if (count > (ssize_t)bytes)
1131
69.8M
      count = (ssize_t)bytes;
1132
1133
69.9M
    memcpy(buf, fp->ptr,(size_t) count);
1134
69.9M
    fp->ptr += count;
1135
69.9M
    fp->pos += count;
1136
1137
    // Update the counts for the last read...
1138
69.9M
    bytes -= (size_t)count;
1139
69.9M
    total += (size_t)count;
1140
69.9M
    buf   += count;
1141
69.9M
  }
1142
1143
  // Return the total number of bytes read...
1144
69.8M
  return ((ssize_t)total);
1145
69.8M
}
1146
1147
1148
//
1149
// 'cupsFileRewind()' - Set the current file position to the beginning of the file.
1150
//
1151
1152
off_t         // O - New file position or `-1` on error
1153
cupsFileRewind(cups_file_t *fp)   // I - CUPS file
1154
0
{
1155
  // Range check input...
1156
0
  if (!fp || fp->mode != 'r')
1157
0
    return (-1);
1158
1159
  // Handle special cases...
1160
0
  if (fp->bufpos == 0)
1161
0
  {
1162
    // No seeking necessary...
1163
0
    fp->pos = 0;
1164
1165
0
    if (fp->ptr)
1166
0
    {
1167
0
      fp->ptr = fp->buf;
1168
0
      fp->eof = false;
1169
0
    }
1170
1171
0
    return (0);
1172
0
  }
1173
1174
  // Otherwise, seek in the file and cleanup any compression buffers...
1175
0
  if (fp->compressed)
1176
0
  {
1177
0
    inflateEnd(&fp->stream);
1178
0
    fp->compressed = false;
1179
0
  }
1180
1181
0
  if (lseek(fp->fd, 0, SEEK_SET))
1182
0
    return (-1);
1183
1184
0
  fp->bufpos = 0;
1185
0
  fp->pos    = 0;
1186
0
  fp->ptr    = NULL;
1187
0
  fp->end    = NULL;
1188
0
  fp->eof    = false;
1189
1190
0
  return (0);
1191
0
}
1192
1193
1194
//
1195
// 'cupsFileSeek()' - Seek in a file.
1196
//
1197
1198
off_t         // O - New file position or `-1` on error
1199
cupsFileSeek(cups_file_t *fp,   // I - CUPS file
1200
             off_t       pos)   // I - Position in file
1201
0
{
1202
0
  ssize_t bytes;      // Number bytes in buffer
1203
1204
1205
  // Range check input...
1206
0
  if (!fp || pos < 0 || fp->mode != 'r')
1207
0
    return (-1);
1208
1209
  // Handle special cases...
1210
0
  if (pos == 0)
1211
0
    return (cupsFileRewind(fp));
1212
1213
0
  if (fp->ptr)
1214
0
  {
1215
0
    bytes = (ssize_t)(fp->end - fp->buf);
1216
1217
0
    if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1218
0
    {
1219
      // No seeking necessary...
1220
0
      fp->pos = pos;
1221
0
      fp->ptr = fp->buf + (pos - fp->bufpos);
1222
0
      fp->eof = false;
1223
1224
0
      return (pos);
1225
0
    }
1226
0
  }
1227
1228
0
  if (!fp->compressed && !fp->ptr)
1229
0
  {
1230
    // Preload a buffer to determine whether the file is compressed...
1231
0
    if (cups_fill(fp) <= 0)
1232
0
      return (-1);
1233
0
  }
1234
1235
  // Seek forwards or backwards...
1236
0
  fp->eof = false;
1237
1238
0
  if (pos < fp->bufpos)
1239
0
  {
1240
    // Need to seek backwards...
1241
0
    if (fp->compressed)
1242
0
    {
1243
0
      inflateEnd(&fp->stream);
1244
1245
0
      lseek(fp->fd, 0, SEEK_SET);
1246
0
      fp->bufpos = 0;
1247
0
      fp->pos    = 0;
1248
0
      fp->ptr    = NULL;
1249
0
      fp->end    = NULL;
1250
1251
0
      while ((bytes = cups_fill(fp)) > 0)
1252
0
      {
1253
0
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1254
0
    break;
1255
0
      }
1256
1257
0
      if (bytes <= 0)
1258
0
        return (-1);
1259
1260
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1261
0
      fp->pos = pos;
1262
0
    }
1263
0
    else
1264
0
    {
1265
0
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1266
0
      fp->pos    = fp->bufpos;
1267
0
      fp->ptr    = NULL;
1268
0
      fp->end    = NULL;
1269
0
    }
1270
0
  }
1271
0
  else
1272
0
  {
1273
    // Need to seek forwards...
1274
0
    if (fp->compressed)
1275
0
    {
1276
0
      while ((bytes = cups_fill(fp)) > 0)
1277
0
      {
1278
0
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1279
0
    break;
1280
0
      }
1281
1282
0
      if (bytes <= 0)
1283
0
        return (-1);
1284
1285
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1286
0
      fp->pos = pos;
1287
0
    }
1288
0
    else
1289
0
    {
1290
0
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1291
0
      fp->pos    = fp->bufpos;
1292
0
      fp->ptr    = NULL;
1293
0
      fp->end    = NULL;
1294
0
    }
1295
0
  }
1296
1297
0
  return (fp->pos);
1298
0
}
1299
1300
1301
//
1302
// 'cupsFileStderr()' - Return a CUPS file associated with stderr.
1303
//
1304
1305
cups_file_t *       // O - CUPS file
1306
cupsFileStderr(void)
1307
0
{
1308
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1309
1310
1311
  // Open file descriptor 2 as needed...
1312
0
  if (!cg->stdio_files[2])
1313
0
  {
1314
    // Flush any pending output on the stdio file...
1315
0
    fflush(stderr);
1316
1317
    // Open file descriptor 2...
1318
0
    if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL)
1319
0
      cg->stdio_files[2]->is_stdio = true;
1320
0
  }
1321
1322
0
  return (cg->stdio_files[2]);
1323
0
}
1324
1325
1326
//
1327
// 'cupsFileStdin()' - Return a CUPS file associated with stdin.
1328
//
1329
1330
cups_file_t *       // O - CUPS file
1331
cupsFileStdin(void)
1332
0
{
1333
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1334
1335
1336
  // Open file descriptor 0 as needed...
1337
0
  if (!cg->stdio_files[0])
1338
0
  {
1339
    // Open file descriptor 0...
1340
0
    if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL)
1341
0
      cg->stdio_files[0]->is_stdio = true;
1342
0
  }
1343
1344
0
  return (cg->stdio_files[0]);
1345
0
}
1346
1347
1348
//
1349
// 'cupsFileStdout()' - Return a CUPS file associated with stdout.
1350
//
1351
1352
cups_file_t *       // O - CUPS file
1353
cupsFileStdout(void)
1354
0
{
1355
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1356
1357
1358
  // Open file descriptor 1 as needed...
1359
0
  if (!cg->stdio_files[1])
1360
0
  {
1361
    // Flush any pending output on the stdio file...
1362
0
    fflush(stdout);
1363
1364
    // Open file descriptor 1...
1365
0
    if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL)
1366
0
      cg->stdio_files[1]->is_stdio = true;
1367
0
  }
1368
1369
0
  return (cg->stdio_files[1]);
1370
0
}
1371
1372
1373
//
1374
// 'cupsFileTell()' - Return the current file position.
1375
//
1376
1377
off_t         // O - File position
1378
cupsFileTell(cups_file_t *fp)   // I - CUPS file
1379
0
{
1380
0
  return (fp ? fp->pos : 0);
1381
0
}
1382
1383
1384
//
1385
// 'cupsFileUnlock()' - Unlock access to a file.
1386
//
1387
1388
bool          // O - `true` on success, `false` on error
1389
cupsFileUnlock(cups_file_t *fp)   // I - CUPS file
1390
0
{
1391
  // Range check...
1392
0
  if (!fp || fp->mode == 's')
1393
0
    return (false);
1394
1395
  // Unlock...
1396
#ifdef _WIN32
1397
  return (_locking(fp->fd, _LK_UNLCK, 0) == 0);
1398
#else
1399
0
  return (lockf(fp->fd, F_ULOCK, 0) == 0);
1400
0
#endif // _WIN32
1401
0
}
1402
1403
1404
//
1405
// 'cupsFileWrite()' - Write to a file.
1406
//
1407
1408
bool          // O - `true` on success, `false` on error
1409
cupsFileWrite(cups_file_t *fp,    // I - CUPS file
1410
              const char  *buf,   // I - Buffer
1411
        size_t      bytes)  // I - Number of bytes to write
1412
9.39M
{
1413
  // Range check input...
1414
9.39M
  if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's'))
1415
0
    return (false);
1416
1417
9.39M
  if (bytes == 0)
1418
0
    return (true);
1419
1420
  // Write the buffer...
1421
9.39M
  if (fp->mode == 's')
1422
0
  {
1423
0
    if (!cups_write(fp, buf, bytes))
1424
0
      return (false);
1425
1426
0
    fp->pos += (off_t)bytes;
1427
1428
0
    return (true);
1429
0
  }
1430
1431
9.39M
  if ((fp->ptr + bytes) > fp->end)
1432
134k
  {
1433
134k
    if (!cupsFileFlush(fp))
1434
0
      return (false);
1435
134k
  }
1436
1437
9.39M
  fp->pos += (off_t)bytes;
1438
1439
9.39M
  if (bytes > sizeof(fp->buf))
1440
25.0k
  {
1441
25.0k
    if (fp->compressed)
1442
0
      return (cups_compress(fp, buf, bytes));
1443
25.0k
    else
1444
25.0k
      return (cups_write(fp, buf, bytes));
1445
25.0k
  }
1446
9.36M
  else
1447
9.36M
  {
1448
9.36M
    memcpy(fp->ptr, buf, bytes);
1449
9.36M
    fp->ptr += bytes;
1450
9.36M
    return (true);
1451
9.36M
  }
1452
9.39M
}
1453
1454
1455
//
1456
// 'cups_compress()' - Compress a buffer of data.
1457
//
1458
1459
static bool       // O - `true` on success, `false` on error
1460
cups_compress(cups_file_t *fp,    // I - CUPS file
1461
              const char  *buf,   // I - Buffer
1462
        size_t      bytes)  // I - Number bytes
1463
0
{
1464
0
  int status;       // Deflate status
1465
1466
1467
  // Update the CRC...
1468
0
  fp->crc = crc32(fp->crc, (const Bytef *)buf, (uInt)bytes);
1469
1470
  // Deflate the bytes...
1471
0
  fp->stream.next_in  = (Bytef *)buf;
1472
0
  fp->stream.avail_in = (uInt)bytes;
1473
1474
0
  while (fp->stream.avail_in > 0)
1475
0
  {
1476
    // Flush the current buffer...
1477
0
    if (fp->stream.avail_out < (uInt)(sizeof(fp->cbuf) / 8))
1478
0
    {
1479
0
      if (!cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf)))
1480
0
        return (false);
1481
1482
0
      fp->stream.next_out  = fp->cbuf;
1483
0
      fp->stream.avail_out = sizeof(fp->cbuf);
1484
0
    }
1485
1486
0
    if ((status = deflate(&(fp->stream), Z_NO_FLUSH)) < Z_OK && status != Z_BUF_ERROR)
1487
0
      return (false);
1488
0
  }
1489
1490
0
  return (true);
1491
0
}
1492
1493
1494
//
1495
// 'cups_fill()' - Fill the input buffer.
1496
//
1497
1498
static ssize_t        // O - Number of bytes or -1
1499
cups_fill(cups_file_t *fp)    // I - CUPS file
1500
151k
{
1501
151k
  ssize_t   bytes;    // Number of bytes read
1502
151k
  int     status;   // Decompression status
1503
151k
  const unsigned char *ptr,   // Pointer into buffer
1504
151k
      *end;   // End of buffer
1505
1506
1507
151k
  if (fp->ptr && fp->end)
1508
148k
    fp->bufpos += fp->end - fp->buf;
1509
1510
151k
  while (!fp->ptr || fp->compressed)
1511
150k
  {
1512
    // Check to see if we have read any data yet; if not, see if we have a compressed file...
1513
150k
    if (!fp->ptr)
1514
2.64k
    {
1515
      // Reset the file position in case we are seeking...
1516
2.64k
      fp->compressed = false;
1517
1518
      // Read the first bytes in the file to determine if we have a gzip'd file...
1519
2.64k
      if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0)
1520
0
      {
1521
        // Can't read from file!
1522
0
        fp->eof = true;
1523
1524
0
  return (-1);
1525
0
      }
1526
1527
2.64k
      if (bytes < 10 || fp->buf[0] != 0x1f || (fp->buf[1] & 255) != 0x8b || fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
1528
981
      {
1529
        // Not a gzip'd file!
1530
981
  fp->ptr = fp->buf;
1531
981
  fp->end = fp->buf + bytes;
1532
1533
981
  return (bytes);
1534
981
      }
1535
1536
      // Parse header junk: extra data, original name, and comment...
1537
1.66k
      ptr = (unsigned char *)fp->buf + 10;
1538
1.66k
      end = (unsigned char *)fp->buf + bytes;
1539
1540
1.66k
      if (fp->buf[3] & 0x04)
1541
18
      {
1542
        // Skip extra data...
1543
18
  if ((ptr + 2) > end)
1544
4
  {
1545
    // Can't read from file!
1546
4
          fp->eof = true;
1547
4
    errno   = EIO;
1548
1549
4
    return (-1);
1550
4
  }
1551
1552
14
  bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
1553
14
  ptr   += 2 + bytes;
1554
1555
14
  if (ptr > end)
1556
8
  {
1557
    // Can't read from file!
1558
8
          fp->eof = true;
1559
8
    errno   = EIO;
1560
1561
8
    return (-1);
1562
8
  }
1563
14
      }
1564
1565
1.65k
      if (fp->buf[3] & 0x08)
1566
26
      {
1567
        // Skip original name data...
1568
7.59k
  while (ptr < end && *ptr)
1569
7.56k
          ptr ++;
1570
1571
26
  if (ptr < end)
1572
9
  {
1573
9
          ptr ++;
1574
9
  }
1575
17
  else
1576
17
  {
1577
    // Can't read from file!
1578
17
          fp->eof = true;
1579
17
    errno   = EIO;
1580
1581
17
    return (-1);
1582
17
  }
1583
26
      }
1584
1585
1.63k
      if (fp->buf[3] & 0x10)
1586
25
      {
1587
        // Skip comment data...
1588
244
  while (ptr < end && *ptr)
1589
219
          ptr ++;
1590
1591
25
  if (ptr < end)
1592
5
  {
1593
5
          ptr ++;
1594
5
  }
1595
20
  else
1596
20
  {
1597
    // Can't read from file!
1598
20
          fp->eof = true;
1599
20
    errno   = EIO;
1600
1601
20
    return (-1);
1602
20
  }
1603
25
      }
1604
1605
1.61k
      if (fp->buf[3] & 0x02)
1606
9
      {
1607
        // Skip header CRC data...
1608
9
  ptr += 2;
1609
1610
9
  if (ptr > end)
1611
6
  {
1612
    // Can't read from file!
1613
6
          fp->eof = true;
1614
6
    errno   = EIO;
1615
1616
6
    return (-1);
1617
6
  }
1618
9
      }
1619
1620
      // Copy the flate-compressed data to the compression buffer...
1621
1.60k
      if ((bytes = end - ptr) > 0)
1622
1.60k
        memcpy(fp->cbuf, ptr, (size_t)bytes);
1623
1624
      // Setup the decompressor data...
1625
1.60k
      fp->stream.zalloc    = (alloc_func)0;
1626
1.60k
      fp->stream.zfree     = (free_func)0;
1627
1.60k
      fp->stream.opaque    = (voidpf)0;
1628
1.60k
      fp->stream.next_in   = (Bytef *)fp->cbuf;
1629
1.60k
      fp->stream.next_out  = NULL;
1630
1.60k
      fp->stream.avail_in  = (uInt)bytes;
1631
1.60k
      fp->stream.avail_out = 0;
1632
1.60k
      fp->crc              = crc32(0L, Z_NULL, 0);
1633
1634
1.60k
      if (inflateInit2(&(fp->stream), -15) != Z_OK)
1635
0
      {
1636
0
        fp->eof = true;
1637
0
        errno   = EIO;
1638
1639
0
  return (-1);
1640
0
      }
1641
1642
1.60k
      fp->compressed = true;
1643
1.60k
    }
1644
1645
149k
    if (fp->compressed)
1646
149k
    {
1647
      // If we have reached end-of-file, return immediately...
1648
149k
      if (fp->eof)
1649
0
  return (0);
1650
1651
      // Fill the decompression buffer as needed...
1652
149k
      if (fp->stream.avail_in == 0)
1653
2.59k
      {
1654
2.59k
  if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
1655
1.15k
  {
1656
1.15k
    fp->eof = true;
1657
1658
1.15k
          return (bytes);
1659
1.15k
  }
1660
1661
1.43k
  fp->stream.next_in  = fp->cbuf;
1662
1.43k
  fp->stream.avail_in = (uInt)bytes;
1663
1.43k
      }
1664
1665
      // Decompress data from the buffer...
1666
148k
      fp->stream.next_out  = (Bytef *)fp->buf;
1667
148k
      fp->stream.avail_out = sizeof(fp->buf);
1668
1669
148k
      status = inflate(&(fp->stream), Z_NO_FLUSH);
1670
1671
148k
      if (fp->stream.next_out > (Bytef *)fp->buf)
1672
148k
        fp->crc = crc32(fp->crc, (Bytef *)fp->buf, (uInt)(fp->stream.next_out - (Bytef *)fp->buf));
1673
1674
148k
      if (status == Z_STREAM_END)
1675
80
      {
1676
        // Read the CRC and length...
1677
80
  unsigned char trailer[8]; // Trailer bytes
1678
80
  uLong   tcrc;   // Trailer CRC
1679
80
  ssize_t   tbytes = 0; // Number of bytes
1680
1681
80
  if (fp->stream.avail_in > 0)
1682
77
  {
1683
    // Get the first N trailer bytes from the inflate stream...
1684
77
    if (fp->stream.avail_in > sizeof(trailer))
1685
19
      tbytes = (ssize_t)sizeof(trailer);
1686
58
    else
1687
58
      tbytes = (ssize_t)fp->stream.avail_in;
1688
1689
77
    memcpy(trailer, fp->stream.next_in, (size_t)tbytes);
1690
77
    fp->stream.next_in  += tbytes;
1691
77
    fp->stream.avail_in -= (size_t)tbytes;
1692
77
  }
1693
1694
        // Reset the compressed flag so that we re-read the file header...
1695
80
        inflateEnd(&fp->stream);
1696
1697
80
  fp->compressed = false;
1698
1699
        // Get any remaining trailer bytes...
1700
80
        if (tbytes < (ssize_t)sizeof(trailer))
1701
9
  {
1702
9
    if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes))
1703
8
    {
1704
      // Can't get it, so mark end-of-file...
1705
8
      fp->eof = true;
1706
8
      errno   = EIO;
1707
1708
8
      return (-1);
1709
8
    }
1710
9
  }
1711
1712
        // Calculate and compare the CRC...
1713
72
  tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) | (uLong)trailer[1]) << 8) | (uLong)trailer[0];
1714
1715
72
  if (tcrc != fp->crc)
1716
71
  {
1717
    // Bad CRC, mark end-of-file...
1718
71
    fp->eof = true;
1719
71
    errno   = EIO;
1720
1721
71
    return (-1);
1722
71
  }
1723
72
      }
1724
148k
      else if (status < Z_OK)
1725
25
      {
1726
25
        fp->eof = true;
1727
25
        errno   = EIO;
1728
1729
25
  return (-1);
1730
25
      }
1731
1732
148k
      bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out;
1733
1734
      // Return the decompressed data...
1735
148k
      fp->ptr = fp->buf;
1736
148k
      fp->end = fp->buf + bytes;
1737
1738
148k
      if (bytes)
1739
148k
  return (bytes);
1740
148k
    }
1741
149k
  }
1742
1743
  // Read a buffer's full of data...
1744
893
  if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0)
1745
732
  {
1746
    // Can't read from file!
1747
732
    fp->eof = true;
1748
732
    fp->ptr = fp->buf;
1749
732
    fp->end = fp->buf;
1750
732
  }
1751
161
  else
1752
161
  {
1753
    // Return the bytes we read...
1754
161
    fp->eof = false;
1755
161
    fp->ptr = fp->buf;
1756
161
    fp->end = fp->buf + bytes;
1757
161
  }
1758
1759
893
  return (bytes);
1760
151k
}
1761
1762
1763
//
1764
// 'cups_open()' - Safely open a file for writing.
1765
//
1766
// We don't allow appending to directories or files that are hard-linked or
1767
// symlinked.
1768
//
1769
1770
static int        // O - File descriptor or -1 otherwise
1771
cups_open(const char *filename,   // I - Filename
1772
          int        oflag,   // I - Open flags
1773
    int        mode)    // I - Open permissions
1774
7.93k
{
1775
7.93k
  int   fd;     // File descriptor
1776
7.93k
  struct stat fileinfo;   // File information
1777
7.93k
#ifndef _WIN32
1778
7.93k
  struct stat linkinfo;   // Link information
1779
7.93k
#endif // !_WIN32
1780
1781
1782
  // Open the file...
1783
7.93k
  if ((fd = open(filename, oflag, mode)) < 0)
1784
2.64k
    return (-1);
1785
1786
  // Then verify that the file descriptor doesn't point to a directory or hard-linked file.
1787
5.28k
  if (fstat(fd, &fileinfo))
1788
0
  {
1789
0
    close(fd);
1790
0
    return (-1);
1791
0
  }
1792
1793
5.28k
  if (fileinfo.st_nlink != 1)
1794
0
  {
1795
0
    close(fd);
1796
0
    errno = EPERM;
1797
0
    return (-1);
1798
0
  }
1799
1800
#ifdef _WIN32
1801
  if (fileinfo.st_mode & _S_IFDIR)
1802
#else
1803
5.28k
  if (S_ISDIR(fileinfo.st_mode))
1804
0
#endif // _WIN32
1805
0
  {
1806
0
    close(fd);
1807
0
    errno = EISDIR;
1808
0
    return (-1);
1809
0
  }
1810
1811
5.28k
#ifndef _WIN32
1812
  // Then use lstat to determine whether the filename is a symlink...
1813
5.28k
  if (lstat(filename, &linkinfo))
1814
0
  {
1815
0
    close(fd);
1816
0
    return (-1);
1817
0
  }
1818
1819
5.28k
  if (S_ISLNK(linkinfo.st_mode) ||
1820
5.28k
      fileinfo.st_dev != linkinfo.st_dev ||
1821
5.28k
      fileinfo.st_ino != linkinfo.st_ino ||
1822
#ifdef HAVE_ST_GEN
1823
      fileinfo.st_gen != linkinfo.st_gen ||
1824
#endif // HAVE_ST_GEN
1825
5.28k
      fileinfo.st_nlink != linkinfo.st_nlink ||
1826
5.28k
      fileinfo.st_mode != linkinfo.st_mode)
1827
0
  {
1828
    // Yes, don't allow!
1829
0
    close(fd);
1830
0
    errno = EPERM;
1831
0
    return (-1);
1832
0
  }
1833
5.28k
#endif // !_WIN32
1834
1835
5.28k
  return (fd);
1836
5.28k
}
1837
1838
1839
//
1840
// 'cups_read()' - Read from a file descriptor.
1841
//
1842
1843
static ssize_t        // O - Number of bytes read or -1
1844
cups_read(cups_file_t *fp,    // I - CUPS file
1845
          char        *buf,   // I - Buffer
1846
    size_t      bytes)    // I - Number bytes
1847
6.13k
{
1848
6.13k
  ssize_t total;      // Total bytes read
1849
1850
1851
  // Loop until we read at least 0 bytes...
1852
6.13k
  for (;;)
1853
6.13k
  {
1854
#ifdef _WIN32
1855
    if (fp->mode == 's')
1856
      total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
1857
    else
1858
      total = (ssize_t)read(fp->fd, buf, (unsigned)bytes);
1859
#else
1860
6.13k
    if (fp->mode == 's')
1861
0
      total = recv(fp->fd, buf, bytes, 0);
1862
6.13k
    else
1863
6.13k
      total = read(fp->fd, buf, bytes);
1864
6.13k
#endif // _WIN32
1865
1866
6.13k
    if (total >= 0)
1867
6.13k
      break;
1868
1869
    // Reads can be interrupted by signals and unavailable resources...
1870
0
    if (errno == EAGAIN || errno == EINTR)
1871
0
      continue;
1872
0
    else
1873
0
      return (-1);
1874
0
  }
1875
1876
  // Return the total number of bytes read...
1877
6.13k
  return (total);
1878
6.13k
}
1879
1880
1881
//
1882
// 'cups_write()' - Write to a file descriptor.
1883
//
1884
1885
static bool       // O - `true` on success, `false` on error
1886
cups_write(cups_file_t *fp,   // I - CUPS file
1887
           const char  *buf,    // I - Buffer
1888
     size_t      bytes)   // I - Number bytes
1889
158k
{
1890
158k
  ssize_t count;      // Count this time
1891
1892
1893
  // Loop until all bytes are written...
1894
317k
  while (bytes > 0)
1895
158k
  {
1896
#ifdef _WIN32
1897
    if (fp->mode == 's')
1898
      count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
1899
    else
1900
      count = (ssize_t)write(fp->fd, buf, (unsigned)bytes);
1901
#else
1902
158k
    if (fp->mode == 's')
1903
0
      count = send(fp->fd, buf, bytes, 0);
1904
158k
    else
1905
158k
      count = write(fp->fd, buf, bytes);
1906
158k
#endif // _WIN32
1907
1908
158k
    if (count < 0)
1909
0
    {
1910
      // Writes can be interrupted by signals and unavailable resources...
1911
0
      if (errno == EAGAIN || errno == EINTR)
1912
0
        continue;
1913
0
      else
1914
0
        return (false);
1915
0
    }
1916
1917
    // Update the counts for the last write call...
1918
158k
    bytes -= (size_t)count;
1919
158k
    buf   += count;
1920
158k
  }
1921
1922
  // Return the total number of bytes written...
1923
158k
  return (true);
1924
158k
}