Coverage Report

Created: 2026-01-25 06:28

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