Coverage Report

Created: 2025-08-28 07:06

/src/cups/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, PPD files, etc.
8
 *
9
 * Copyright 2007-2018 by Apple Inc.
10
 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
11
 *
12
 * These coded instructions, statements, and computer programs are the
13
 * property of Apple Inc. and are protected by Federal copyright
14
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
15
 * which should have been included with this file.  If this file is
16
 * missing or damaged, see the license at "http://www.cups.org/".
17
 *
18
 * This file is subject to the Apple OS-Developed Software exception.
19
 */
20
21
/*
22
 * Include necessary headers...
23
 */
24
25
#include "file-private.h"
26
#include <sys/stat.h>
27
#include <sys/types.h>
28
29
#  ifdef HAVE_LIBZ
30
#    include <zlib.h>
31
#  endif /* HAVE_LIBZ */
32
33
34
/*
35
 * Internal structures...
36
 */
37
38
struct _cups_file_s     /**** CUPS file structure... ****/
39
40
{
41
  int   fd;     /* File descriptor */
42
  char    mode,     /* Mode ('r' or 'w') */
43
    compressed,   /* Compression used? */
44
    is_stdio,   /* stdin/out/err? */
45
    eof,      /* End of file? */
46
    buf[4096],    /* Buffer */
47
    *ptr,     /* Pointer into buffer */
48
    *end;     /* End of buffer data */
49
  off_t   pos,      /* Position in file */
50
    bufpos;     /* File position for start of buffer */
51
52
#ifdef HAVE_LIBZ
53
  z_stream  stream;     /* (De)compression stream */
54
  Bytef   cbuf[4096];   /* (De)compression buffer */
55
  uLong   crc;      /* (De)compression CRC */
56
#endif /* HAVE_LIBZ */
57
58
  char    *printf_buffer;   /* cupsFilePrintf buffer */
59
  size_t  printf_size;    /* Size of cupsFilePrintf buffer */
60
};
61
62
63
/*
64
 * Local functions...
65
 */
66
67
#ifdef HAVE_LIBZ
68
static ssize_t  cups_compress(cups_file_t *fp, const char *buf, size_t bytes);
69
#endif /* HAVE_LIBZ */
70
static ssize_t  cups_fill(cups_file_t *fp);
71
static int  cups_open(const char *filename, int mode);
72
static ssize_t  cups_read(cups_file_t *fp, char *buf, size_t bytes);
73
static ssize_t  cups_write(cups_file_t *fp, const char *buf, size_t bytes);
74
75
76
#ifndef _WIN32
77
/*
78
 * '_cupsFileCheck()' - Check the permissions of the given filename.
79
 */
80
81
_cups_fc_result_t     /* O - Check result */
82
_cupsFileCheck(
83
    const char          *filename,  /* I - Filename to check */
84
    _cups_fc_filetype_t filetype, /* I - Type of file checks? */
85
    int                 dorootchecks, /* I - Check for root permissions? */
86
    _cups_fc_func_t     cb,   /* I - Callback function */
87
    void                *context) /* I - Context pointer for callback */
88
89
0
{
90
0
  struct stat   fileinfo; /* File information */
91
0
  char      message[1024],  /* Message string */
92
0
      temp[1024], /* Parent directory filename */
93
0
      *ptr;   /* Pointer into parent directory */
94
0
  _cups_fc_result_t result;   /* Check result */
95
96
97
 /*
98
  * Does the filename contain a relative path ("../")?
99
  */
100
101
0
  if (strstr(filename, "../"))
102
0
  {
103
   /*
104
    * Yes, fail it!
105
    */
106
107
0
    result = _CUPS_FILE_CHECK_RELATIVE_PATH;
108
0
    goto finishup;
109
0
  }
110
111
 /*
112
  * Does the program even exist and is it accessible?
113
  */
114
115
0
  if (stat(filename, &fileinfo))
116
0
  {
117
   /*
118
    * Nope...
119
    */
120
121
0
    result = _CUPS_FILE_CHECK_MISSING;
122
0
    goto finishup;
123
0
  }
124
125
 /*
126
  * Check the execute bit...
127
  */
128
129
0
  result = _CUPS_FILE_CHECK_OK;
130
131
0
  switch (filetype)
132
0
  {
133
0
    case _CUPS_FILE_CHECK_DIRECTORY :
134
0
        if (!S_ISDIR(fileinfo.st_mode))
135
0
    result = _CUPS_FILE_CHECK_WRONG_TYPE;
136
0
        break;
137
138
0
    default :
139
0
        if (!S_ISREG(fileinfo.st_mode))
140
0
    result = _CUPS_FILE_CHECK_WRONG_TYPE;
141
0
        break;
142
0
  }
143
144
0
  if (result)
145
0
    goto finishup;
146
147
 /*
148
  * Are we doing root checks?
149
  */
150
151
0
  if (!dorootchecks)
152
0
  {
153
   /*
154
    * Nope, so anything (else) goes...
155
    */
156
157
0
    goto finishup;
158
0
  }
159
160
 /*
161
  * Verify permission of the file itself:
162
  *
163
  * 1. Must be owned by root
164
  * 2. Must not be writable by group
165
  * 3. Must not be setuid
166
  * 4. Must not be writable by others
167
  */
168
169
0
  if (fileinfo.st_uid ||   /* 1. Must be owned by root */
170
0
      (fileinfo.st_mode & S_IWGRP)  ||  /* 2. Must not be writable by group */
171
0
      (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */
172
0
      (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */
173
0
  {
174
0
    result = _CUPS_FILE_CHECK_PERMISSIONS;
175
0
    goto finishup;
176
0
  }
177
178
0
  if (filetype == _CUPS_FILE_CHECK_DIRECTORY ||
179
0
      filetype == _CUPS_FILE_CHECK_FILE_ONLY)
180
0
    goto finishup;
181
182
 /*
183
  * Now check the containing directory...
184
  */
185
186
0
  strlcpy(temp, filename, sizeof(temp));
187
0
  if ((ptr = strrchr(temp, '/')) != NULL)
188
0
  {
189
0
    if (ptr == temp)
190
0
      ptr[1] = '\0';
191
0
    else
192
0
      *ptr = '\0';
193
0
  }
194
195
0
  if (stat(temp, &fileinfo))
196
0
  {
197
   /*
198
    * Doesn't exist?!?
199
    */
200
201
0
    result   = _CUPS_FILE_CHECK_MISSING;
202
0
    filetype = _CUPS_FILE_CHECK_DIRECTORY;
203
0
    filename = temp;
204
205
0
    goto finishup;
206
0
  }
207
208
0
  if (fileinfo.st_uid ||   /* 1. Must be owned by root */
209
0
      (fileinfo.st_mode & S_IWGRP) || /* 2. Must not be writable by group */
210
0
      (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */
211
0
      (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */
212
0
  {
213
0
    result   = _CUPS_FILE_CHECK_PERMISSIONS;
214
0
    filetype = _CUPS_FILE_CHECK_DIRECTORY;
215
0
    filename = temp;
216
0
  }
217
218
 /*
219
  * Common return point...
220
  */
221
222
0
  finishup:
223
224
0
  if (cb)
225
0
  {
226
0
    cups_lang_t *lang = cupsLangDefault();
227
          /* Localization information */
228
229
0
    switch (result)
230
0
    {
231
0
      case _CUPS_FILE_CHECK_OK :
232
0
    if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
233
0
      snprintf(message, sizeof(message),
234
0
         _cupsLangString(lang, _("Directory \"%s\" permissions OK "
235
0
               "(0%o/uid=%d/gid=%d).")),
236
0
         filename, fileinfo.st_mode, (int)fileinfo.st_uid,
237
0
         (int)fileinfo.st_gid);
238
0
    else
239
0
      snprintf(message, sizeof(message),
240
0
         _cupsLangString(lang, _("File \"%s\" permissions OK "
241
0
               "(0%o/uid=%d/gid=%d).")),
242
0
         filename, fileinfo.st_mode, (int)fileinfo.st_uid,
243
0
         (int)fileinfo.st_gid);
244
0
          break;
245
246
0
      case _CUPS_FILE_CHECK_MISSING :
247
0
    if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
248
0
      snprintf(message, sizeof(message),
249
0
         _cupsLangString(lang, _("Directory \"%s\" not available: "
250
0
               "%s")),
251
0
         filename, strerror(errno));
252
0
    else
253
0
      snprintf(message, sizeof(message),
254
0
         _cupsLangString(lang, _("File \"%s\" not available: %s")),
255
0
         filename, strerror(errno));
256
0
          break;
257
258
0
      case _CUPS_FILE_CHECK_PERMISSIONS :
259
0
    if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
260
0
      snprintf(message, sizeof(message),
261
0
         _cupsLangString(lang, _("Directory \"%s\" has insecure "
262
0
               "permissions "
263
0
               "(0%o/uid=%d/gid=%d).")),
264
0
         filename, fileinfo.st_mode, (int)fileinfo.st_uid,
265
0
         (int)fileinfo.st_gid);
266
0
    else
267
0
      snprintf(message, sizeof(message),
268
0
         _cupsLangString(lang, _("File \"%s\" has insecure "
269
0
                                 "permissions "
270
0
               "(0%o/uid=%d/gid=%d).")),
271
0
         filename, fileinfo.st_mode, (int)fileinfo.st_uid,
272
0
         (int)fileinfo.st_gid);
273
0
          break;
274
275
0
      case _CUPS_FILE_CHECK_WRONG_TYPE :
276
0
    if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
277
0
      snprintf(message, sizeof(message),
278
0
         _cupsLangString(lang, _("Directory \"%s\" is a file.")),
279
0
         filename);
280
0
    else
281
0
      snprintf(message, sizeof(message),
282
0
         _cupsLangString(lang, _("File \"%s\" is a directory.")),
283
0
         filename);
284
0
          break;
285
286
0
      case _CUPS_FILE_CHECK_RELATIVE_PATH :
287
0
    if (filetype == _CUPS_FILE_CHECK_DIRECTORY)
288
0
      snprintf(message, sizeof(message),
289
0
         _cupsLangString(lang, _("Directory \"%s\" contains a "
290
0
               "relative path.")), filename);
291
0
    else
292
0
      snprintf(message, sizeof(message),
293
0
         _cupsLangString(lang, _("File \"%s\" contains a relative "
294
0
               "path.")), filename);
295
0
          break;
296
0
    }
297
298
0
    (*cb)(context, result, message);
299
0
  }
300
301
0
  return (result);
302
0
}
303
304
305
/*
306
 * '_cupsFileCheckFilter()' - Report file check results as CUPS filter messages.
307
 */
308
309
void
310
_cupsFileCheckFilter(
311
    void              *context,   /* I - Context pointer (unused) */
312
    _cups_fc_result_t result,   /* I - Result code */
313
    const char        *message)   /* I - Message text */
314
0
{
315
0
  const char  *prefix;    /* Messaging prefix */
316
317
318
0
  (void)context;
319
320
0
  switch (result)
321
0
  {
322
0
    default :
323
0
    case _CUPS_FILE_CHECK_OK :
324
0
        prefix = "DEBUG2";
325
0
  break;
326
327
0
    case _CUPS_FILE_CHECK_MISSING :
328
0
    case _CUPS_FILE_CHECK_WRONG_TYPE :
329
0
        prefix = "ERROR";
330
0
  fputs("STATE: +cups-missing-filter-warning\n", stderr);
331
0
  break;
332
333
0
    case _CUPS_FILE_CHECK_PERMISSIONS :
334
0
    case _CUPS_FILE_CHECK_RELATIVE_PATH :
335
0
        prefix = "ERROR";
336
0
  fputs("STATE: +cups-insecure-filter-warning\n", stderr);
337
0
  break;
338
0
  }
339
340
0
  fprintf(stderr, "%s: %s\n", prefix, message);
341
0
}
342
#endif /* !_WIN32 */
343
344
345
/*
346
 * 'cupsFileClose()' - Close a CUPS file.
347
 *
348
 * @since CUPS 1.2/macOS 10.5@
349
 */
350
351
int         /* O - 0 on success, -1 on error */
352
cupsFileClose(cups_file_t *fp)    /* I - CUPS file */
353
0
{
354
0
  int fd;       /* File descriptor */
355
0
  char  mode;       /* Open mode */
356
0
  int status;       /* Return status */
357
358
359
0
  DEBUG_printf(("cupsFileClose(fp=%p)", (void *)fp));
360
361
 /*
362
  * Range check...
363
  */
364
365
0
  if (!fp)
366
0
    return (-1);
367
368
 /*
369
  * Flush pending write data...
370
  */
371
372
0
  if (fp->mode == 'w')
373
0
    status = cupsFileFlush(fp);
374
0
  else
375
0
    status = 0;
376
377
0
#ifdef HAVE_LIBZ
378
0
  if (fp->compressed && status >= 0)
379
0
  {
380
0
    if (fp->mode == 'r')
381
0
    {
382
     /*
383
      * Free decompression data...
384
      */
385
386
0
      inflateEnd(&fp->stream);
387
0
    }
388
0
    else
389
0
    {
390
     /*
391
      * Flush any remaining compressed data...
392
      */
393
394
0
      unsigned char trailer[8]; /* Trailer CRC and length */
395
0
      int   done;   /* Done writing... */
396
397
398
0
      fp->stream.avail_in = 0;
399
400
0
      for (done = 0;;)
401
0
      {
402
0
        if (fp->stream.next_out > fp->cbuf)
403
0
  {
404
0
    if (cups_write(fp, (char *)fp->cbuf,
405
0
                   (size_t)(fp->stream.next_out - fp->cbuf)) < 0)
406
0
      status = -1;
407
408
0
    fp->stream.next_out  = fp->cbuf;
409
0
    fp->stream.avail_out = sizeof(fp->cbuf);
410
0
  }
411
412
0
        if (done || status < 0)
413
0
    break;
414
415
0
        done = deflate(&fp->stream, Z_FINISH) == Z_STREAM_END &&
416
0
         fp->stream.next_out == fp->cbuf;
417
0
      }
418
419
     /*
420
      * Write the CRC and length...
421
      */
422
423
0
      trailer[0] = (unsigned char)fp->crc;
424
0
      trailer[1] = (unsigned char)(fp->crc >> 8);
425
0
      trailer[2] = (unsigned char)(fp->crc >> 16);
426
0
      trailer[3] = (unsigned char)(fp->crc >> 24);
427
0
      trailer[4] = (unsigned char)fp->pos;
428
0
      trailer[5] = (unsigned char)(fp->pos >> 8);
429
0
      trailer[6] = (unsigned char)(fp->pos >> 16);
430
0
      trailer[7] = (unsigned char)(fp->pos >> 24);
431
432
0
      if (cups_write(fp, (char *)trailer, 8) < 0)
433
0
        status = -1;
434
435
     /*
436
      * Free all memory used by the compression stream...
437
      */
438
439
0
      deflateEnd(&(fp->stream));
440
0
    }
441
0
  }
442
0
#endif /* HAVE_LIBZ */
443
444
 /*
445
  * If this is one of the cupsFileStdin/out/err files, return now and don't
446
  * actually free memory or close (these last the life of the process...)
447
  */
448
449
0
  if (fp->is_stdio)
450
0
    return (status);
451
452
/*
453
  * Save the file descriptor we used and free memory...
454
  */
455
456
0
  fd   = fp->fd;
457
0
  mode = fp->mode;
458
459
0
  if (fp->printf_buffer)
460
0
    free(fp->printf_buffer);
461
462
0
  free(fp);
463
464
 /*
465
  * Close the file, returning the close status...
466
  */
467
468
0
  if (mode == 's')
469
0
  {
470
0
    if (httpAddrClose(NULL, fd) < 0)
471
0
      status = -1;
472
0
  }
473
0
  else if (close(fd) < 0)
474
0
    status = -1;
475
476
0
  return (status);
477
0
}
478
479
480
/*
481
 * 'cupsFileCompression()' - Return whether a file is compressed.
482
 *
483
 * @since CUPS 1.2/macOS 10.5@
484
 */
485
486
int         /* O - @code CUPS_FILE_NONE@ or @code CUPS_FILE_GZIP@ */
487
cupsFileCompression(cups_file_t *fp)  /* I - CUPS file */
488
0
{
489
0
  return (fp ? fp->compressed : CUPS_FILE_NONE);
490
0
}
491
492
493
/*
494
 * 'cupsFileEOF()' - Return the end-of-file status.
495
 *
496
 * @since CUPS 1.2/macOS 10.5@
497
 */
498
499
int         /* O - 1 on end of file, 0 otherwise */
500
cupsFileEOF(cups_file_t *fp)    /* I - CUPS file */
501
0
{
502
0
  return (fp ? fp->eof : 1);
503
0
}
504
505
506
/*
507
 * 'cupsFileFind()' - Find a file using the specified path.
508
 *
509
 * This function allows the paths in the path string to be separated by
510
 * colons (UNIX standard) or semicolons (Windows standard) and stores the
511
 * result in the buffer supplied.  If the file cannot be found in any of
512
 * the supplied paths, @code NULL@ is returned. A @code NULL@ path only
513
 * matches the current directory.
514
 *
515
 * @since CUPS 1.2/macOS 10.5@
516
 */
517
518
const char *        /* O - Full path to file or @code NULL@ if not found */
519
cupsFileFind(const char *filename,  /* I - File to find */
520
             const char *path,    /* I - Colon/semicolon-separated path */
521
             int        executable, /* I - 1 = executable files, 0 = any file/dir */
522
       char       *buffer,  /* I - Filename buffer */
523
       int        bufsize)  /* I - Size of filename buffer */
524
0
{
525
0
  char  *bufptr,      /* Current position in buffer */
526
0
  *bufend;      /* End of buffer */
527
528
529
 /*
530
  * Range check input...
531
  */
532
533
0
  DEBUG_printf(("cupsFileFind(filename=\"%s\", path=\"%s\", executable=%d, buffer=%p, bufsize=%d)", filename, path, executable, (void *)buffer, bufsize));
534
535
0
  if (!filename || !buffer || bufsize < 2)
536
0
    return (NULL);
537
538
0
  if (!path)
539
0
  {
540
   /*
541
    * No path, so check current directory...
542
    */
543
544
0
    if (!access(filename, 0))
545
0
    {
546
0
      strlcpy(buffer, filename, (size_t)bufsize);
547
0
      return (buffer);
548
0
    }
549
0
    else
550
0
      return (NULL);
551
0
  }
552
553
 /*
554
  * Now check each path and return the first match...
555
  */
556
557
0
  bufend = buffer + bufsize - 1;
558
0
  bufptr = buffer;
559
560
0
  while (*path)
561
0
  {
562
#ifdef _WIN32
563
    if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255))))
564
#else
565
0
    if (*path == ';' || *path == ':')
566
0
#endif /* _WIN32 */
567
0
    {
568
0
      if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
569
0
        *bufptr++ = '/';
570
571
0
      strlcpy(bufptr, filename, (size_t)(bufend - bufptr));
572
573
#ifdef _WIN32
574
      if (!access(buffer, 0))
575
#else
576
0
      if (!access(buffer, executable ? X_OK : 0))
577
0
#endif /* _WIN32 */
578
0
      {
579
0
        DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer));
580
0
        return (buffer);
581
0
      }
582
583
0
      bufptr = buffer;
584
0
    }
585
0
    else if (bufptr < bufend)
586
0
      *bufptr++ = *path;
587
588
0
    path ++;
589
0
  }
590
591
 /*
592
  * Check the last path...
593
  */
594
595
0
  if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
596
0
    *bufptr++ = '/';
597
598
0
  strlcpy(bufptr, filename, (size_t)(bufend - bufptr));
599
600
0
  if (!access(buffer, 0))
601
0
  {
602
0
    DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer));
603
0
    return (buffer);
604
0
  }
605
0
  else
606
0
  {
607
0
    DEBUG_puts("1cupsFileFind: Returning NULL");
608
0
    return (NULL);
609
0
  }
610
0
}
611
612
613
/*
614
 * 'cupsFileFlush()' - Flush pending output.
615
 *
616
 * @since CUPS 1.2/macOS 10.5@
617
 */
618
619
int         /* O - 0 on success, -1 on error */
620
cupsFileFlush(cups_file_t *fp)    /* I - CUPS file */
621
0
{
622
0
  ssize_t bytes;      /* Bytes to write */
623
624
625
0
  DEBUG_printf(("cupsFileFlush(fp=%p)", (void *)fp));
626
627
 /*
628
  * Range check input...
629
  */
630
631
0
  if (!fp || fp->mode != 'w')
632
0
  {
633
0
    DEBUG_puts("1cupsFileFlush: Attempt to flush a read-only file...");
634
0
    return (-1);
635
0
  }
636
637
0
  bytes = (ssize_t)(fp->ptr - fp->buf);
638
639
0
  DEBUG_printf(("2cupsFileFlush: Flushing " CUPS_LLFMT " bytes...",
640
0
                CUPS_LLCAST bytes));
641
642
0
  if (bytes > 0)
643
0
  {
644
0
#ifdef HAVE_LIBZ
645
0
    if (fp->compressed)
646
0
      bytes = cups_compress(fp, fp->buf, (size_t)bytes);
647
0
    else
648
0
#endif /* HAVE_LIBZ */
649
0
      bytes = cups_write(fp, fp->buf, (size_t)bytes);
650
651
0
    if (bytes < 0)
652
0
      return (-1);
653
654
0
    fp->ptr = fp->buf;
655
0
  }
656
657
0
  return (0);
658
0
}
659
660
661
/*
662
 * 'cupsFileGetChar()' - Get a single character from a file.
663
 *
664
 * @since CUPS 1.2/macOS 10.5@
665
 */
666
667
int         /* O - Character or -1 on end of file */
668
cupsFileGetChar(cups_file_t *fp)  /* I - CUPS file */
669
0
{
670
 /*
671
  * Range check input...
672
  */
673
674
0
  DEBUG_printf(("4cupsFileGetChar(fp=%p)", (void *)fp));
675
676
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
677
0
  {
678
0
    DEBUG_puts("5cupsFileGetChar: Bad arguments!");
679
0
    return (-1);
680
0
  }
681
682
0
  if (fp->eof)
683
0
  {
684
0
    DEBUG_puts("5cupsFileGetChar: End-of-file!");
685
0
    return (-1);
686
0
  }
687
688
 /*
689
  * If the input buffer is empty, try to read more data...
690
  */
691
692
0
  DEBUG_printf(("5cupsFileGetChar: fp->eof=%d, fp->ptr=%p, fp->end=%p", fp->eof, (void *)fp->ptr, (void *)fp->end));
693
694
0
  if (fp->ptr >= fp->end)
695
0
    if (cups_fill(fp) <= 0)
696
0
    {
697
0
      DEBUG_puts("5cupsFileGetChar: Unable to fill buffer!");
698
0
      return (-1);
699
0
    }
700
701
 /*
702
  * Return the next character in the buffer...
703
  */
704
705
0
  DEBUG_printf(("5cupsFileGetChar: Returning %d...", *(fp->ptr) & 255));
706
707
0
  fp->pos ++;
708
709
0
  DEBUG_printf(("6cupsFileGetChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
710
711
0
  return (*(fp->ptr)++ & 255);
712
0
}
713
714
715
/*
716
 * 'cupsFileGetConf()' - Get a line from a configuration file.
717
 *
718
 * @since CUPS 1.2/macOS 10.5@
719
 */
720
721
char *          /* O  - Line read or @code NULL@ on end of file or error */
722
cupsFileGetConf(cups_file_t *fp,  /* I  - CUPS file */
723
                char        *buf, /* O  - String buffer */
724
    size_t      buflen, /* I  - Size of string buffer */
725
                char        **value,  /* O  - Pointer to value */
726
    int         *linenum) /* IO - Current line number */
727
0
{
728
0
  char  *ptr;       /* Pointer into line */
729
730
731
 /*
732
  * Range check input...
733
  */
734
735
0
  DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT
736
0
                ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum));
737
738
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's') ||
739
0
      !buf || buflen < 2 || !value)
740
0
  {
741
0
    if (value)
742
0
      *value = NULL;
743
744
0
    return (NULL);
745
0
  }
746
747
 /*
748
  * Read the next non-comment line...
749
  */
750
751
0
  *value = NULL;
752
753
0
  while (cupsFileGets(fp, buf, buflen))
754
0
  {
755
0
    (*linenum) ++;
756
757
   /*
758
    * Strip any comments...
759
    */
760
761
0
    if ((ptr = strchr(buf, '#')) != NULL)
762
0
    {
763
0
      if (ptr > buf && ptr[-1] == '\\')
764
0
      {
765
        // Unquote the #...
766
0
  _cups_strcpy(ptr - 1, ptr);
767
0
      }
768
0
      else
769
0
      {
770
        // Strip the comment and any trailing whitespace...
771
0
  while (ptr > buf)
772
0
  {
773
0
    if (!_cups_isspace(ptr[-1]))
774
0
      break;
775
776
0
    ptr --;
777
0
  }
778
779
0
  *ptr = '\0';
780
0
      }
781
0
    }
782
783
   /*
784
    * Strip leading whitespace...
785
    */
786
787
0
    for (ptr = buf; _cups_isspace(*ptr); ptr ++);
788
789
0
    if (ptr > buf)
790
0
      _cups_strcpy(buf, ptr);
791
792
   /*
793
    * See if there is anything left...
794
    */
795
796
0
    if (buf[0])
797
0
    {
798
     /*
799
      * Yes, grab any value and return...
800
      */
801
802
0
      for (ptr = buf; *ptr; ptr ++)
803
0
        if (_cups_isspace(*ptr))
804
0
    break;
805
806
0
      if (*ptr)
807
0
      {
808
       /*
809
        * Have a value, skip any other spaces...
810
  */
811
812
0
        while (_cups_isspace(*ptr))
813
0
    *ptr++ = '\0';
814
815
0
        if (*ptr)
816
0
    *value = ptr;
817
818
       /*
819
        * Strip trailing whitespace and > for lines that begin with <...
820
  */
821
822
0
        ptr += strlen(ptr) - 1;
823
824
0
        if (buf[0] == '<' && *ptr == '>')
825
0
    *ptr-- = '\0';
826
0
  else if (buf[0] == '<' && *ptr != '>')
827
0
        {
828
   /*
829
    * Syntax error...
830
    */
831
832
0
    *value = NULL;
833
0
    return (buf);
834
0
  }
835
836
0
        while (ptr > *value && _cups_isspace(*ptr))
837
0
    *ptr-- = '\0';
838
0
      }
839
840
     /*
841
      * Return the line...
842
      */
843
844
0
      return (buf);
845
0
    }
846
0
  }
847
848
0
  return (NULL);
849
0
}
850
851
852
/*
853
 * 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may
854
 *                       contain binary data.
855
 *
856
 * This function differs from @link cupsFileGets@ in that the trailing CR
857
 * and LF are preserved, as is any binary data on the line. The buffer is
858
 * nul-terminated, however you should use the returned length to determine
859
 * the number of bytes on the line.
860
 *
861
 * @since CUPS 1.2/macOS 10.5@
862
 */
863
864
size_t          /* O - Number of bytes on line or 0 on end of file */
865
cupsFileGetLine(cups_file_t *fp,  /* I - File to read from */
866
                char        *buf, /* I - Buffer */
867
                size_t      buflen) /* I - Size of buffer */
868
0
{
869
0
  int   ch;     /* Character from file */
870
0
  char    *ptr,     /* Current position in line buffer */
871
0
    *end;     /* End of line buffer */
872
873
874
 /*
875
  * Range check input...
876
  */
877
878
0
  DEBUG_printf(("2cupsFileGetLine(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen));
879
880
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3)
881
0
    return (0);
882
883
 /*
884
  * Now loop until we have a valid line...
885
  */
886
887
0
  for (ptr = buf, end = buf + buflen - 2; ptr < end ;)
888
0
  {
889
0
    if (fp->ptr >= fp->end)
890
0
      if (cups_fill(fp) <= 0)
891
0
        break;
892
893
0
    *ptr++ = ch = *(fp->ptr)++;
894
0
    fp->pos ++;
895
896
0
    if (ch == '\r')
897
0
    {
898
     /*
899
      * Check for CR LF...
900
      */
901
902
0
      if (fp->ptr >= fp->end)
903
0
  if (cups_fill(fp) <= 0)
904
0
          break;
905
906
0
      if (*(fp->ptr) == '\n')
907
0
      {
908
0
        *ptr++ = *(fp->ptr)++;
909
0
  fp->pos ++;
910
0
      }
911
912
0
      break;
913
0
    }
914
0
    else if (ch == '\n')
915
0
    {
916
     /*
917
      * Line feed ends a line...
918
      */
919
920
0
      break;
921
0
    }
922
0
  }
923
924
0
  *ptr = '\0';
925
926
0
  DEBUG_printf(("4cupsFileGetLine: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
927
928
0
  return ((size_t)(ptr - buf));
929
0
}
930
931
932
/*
933
 * 'cupsFileGets()' - Get a CR and/or LF-terminated line.
934
 *
935
 * @since CUPS 1.2/macOS 10.5@
936
 */
937
938
char *          /* O - Line read or @code NULL@ on end of file or error */
939
cupsFileGets(cups_file_t *fp,   /* I - CUPS file */
940
             char        *buf,    /* O - String buffer */
941
       size_t      buflen)  /* I - Size of string buffer */
942
0
{
943
0
  int   ch;     /* Character from file */
944
0
  char    *ptr,     /* Current position in line buffer */
945
0
    *end;     /* End of line buffer */
946
947
948
 /*
949
  * Range check input...
950
  */
951
952
0
  DEBUG_printf(("2cupsFileGets(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen));
953
954
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2)
955
0
    return (NULL);
956
957
 /*
958
  * Now loop until we have a valid line...
959
  */
960
961
0
  for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
962
0
  {
963
0
    if (fp->ptr >= fp->end)
964
0
      if (cups_fill(fp) <= 0)
965
0
      {
966
0
        if (ptr == buf)
967
0
    return (NULL);
968
0
  else
969
0
          break;
970
0
      }
971
972
0
    ch = *(fp->ptr)++;
973
0
    fp->pos ++;
974
975
0
    if (ch == '\r')
976
0
    {
977
     /*
978
      * Check for CR LF...
979
      */
980
981
0
      if (fp->ptr >= fp->end)
982
0
  if (cups_fill(fp) <= 0)
983
0
          break;
984
985
0
      if (*(fp->ptr) == '\n')
986
0
      {
987
0
        fp->ptr ++;
988
0
  fp->pos ++;
989
0
      }
990
991
0
      break;
992
0
    }
993
0
    else if (ch == '\n')
994
0
    {
995
     /*
996
      * Line feed ends a line...
997
      */
998
999
0
      break;
1000
0
    }
1001
0
    else
1002
0
      *ptr++ = (char)ch;
1003
0
  }
1004
1005
0
  *ptr = '\0';
1006
1007
0
  DEBUG_printf(("4cupsFileGets: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1008
1009
0
  return (buf);
1010
0
}
1011
1012
1013
/*
1014
 * 'cupsFileLock()' - Temporarily lock access to a file.
1015
 *
1016
 * @since CUPS 1.2/macOS 10.5@
1017
 */
1018
1019
int         /* O - 0 on success, -1 on error */
1020
cupsFileLock(cups_file_t *fp,   /* I - CUPS file */
1021
             int         block)   /* I - 1 to wait for the lock, 0 to fail right away */
1022
0
{
1023
 /*
1024
  * Range check...
1025
  */
1026
1027
0
  if (!fp || fp->mode == 's')
1028
0
    return (-1);
1029
1030
 /*
1031
  * Try the lock...
1032
  */
1033
1034
#ifdef _WIN32
1035
  return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0));
1036
#else
1037
0
  return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0));
1038
0
#endif /* _WIN32 */
1039
0
}
1040
1041
1042
/*
1043
 * 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file.
1044
 *
1045
 * @since CUPS 1.2/macOS 10.5@
1046
 */
1047
1048
int         /* O - File descriptor */
1049
cupsFileNumber(cups_file_t *fp)   /* I - CUPS file */
1050
0
{
1051
0
  if (fp)
1052
0
    return (fp->fd);
1053
0
  else
1054
0
    return (-1);
1055
0
}
1056
1057
1058
/*
1059
 * 'cupsFileOpen()' - Open a CUPS file.
1060
 *
1061
 * The "mode" parameter can be "r" to read, "w" to write, overwriting any
1062
 * existing file, "a" to append to an existing file or create a new file,
1063
 * or "s" to open a socket connection.
1064
 *
1065
 * When opening for writing ("w"), an optional number from 1 to 9 can be
1066
 * supplied which enables Flate compression of the file.  Compression is
1067
 * not supported for the "a" (append) mode.
1068
 *
1069
 * When opening a socket connection, the filename is a string of the form
1070
 * "address:port" or "hostname:port". The socket will make an IPv4 or IPv6
1071
 * connection as needed, generally preferring IPv6 connections when there is
1072
 * a choice.
1073
 *
1074
 * @since CUPS 1.2/macOS 10.5@
1075
 */
1076
1077
cups_file_t *       /* O - CUPS file or @code NULL@ if the file or socket cannot be opened */
1078
cupsFileOpen(const char *filename,  /* I - Name of file */
1079
             const char *mode)    /* I - Open mode */
1080
0
{
1081
0
  cups_file_t *fp;      /* New CUPS file */
1082
0
  int   fd;     /* File descriptor */
1083
0
  char    hostname[1024],   /* Hostname */
1084
0
    *portname;    /* Port "name" (number or service) */
1085
0
  http_addrlist_t *addrlist;    /* Host address list */
1086
1087
1088
0
  DEBUG_printf(("cupsFileOpen(filename=\"%s\", mode=\"%s\")", filename,
1089
0
                mode));
1090
1091
 /*
1092
  * Range check input...
1093
  */
1094
1095
0
  if (!filename || !mode ||
1096
0
      (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
1097
0
      (*mode == 'a' && isdigit(mode[1] & 255)))
1098
0
    return (NULL);
1099
1100
 /*
1101
  * Open the file...
1102
  */
1103
1104
0
  switch (*mode)
1105
0
  {
1106
0
    case 'a' : /* Append file */
1107
0
        fd = cups_open(filename,
1108
0
           O_RDWR | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY);
1109
0
        break;
1110
1111
0
    case 'r' : /* Read file */
1112
0
  fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0);
1113
0
  break;
1114
1115
0
    case 'w' : /* Write file */
1116
0
        fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
1117
0
  if (fd < 0 && errno == ENOENT)
1118
0
  {
1119
0
    fd = cups_open(filename,
1120
0
                   O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY);
1121
0
    if (fd < 0 && errno == EEXIST)
1122
0
      fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
1123
0
  }
1124
1125
0
  if (fd >= 0)
1126
#ifdef _WIN32
1127
    _chsize(fd, 0);
1128
#else
1129
0
    ftruncate(fd, 0);
1130
0
#endif /* _WIN32 */
1131
0
        break;
1132
1133
0
    case 's' : /* Read/write socket */
1134
0
        strlcpy(hostname, filename, sizeof(hostname));
1135
0
  if ((portname = strrchr(hostname, ':')) != NULL)
1136
0
    *portname++ = '\0';
1137
0
  else
1138
0
    return (NULL);
1139
1140
       /*
1141
        * Lookup the hostname and service...
1142
  */
1143
1144
0
        if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
1145
0
    return (NULL);
1146
1147
       /*
1148
  * Connect to the server...
1149
  */
1150
1151
0
        if (!httpAddrConnect(addrlist, &fd))
1152
0
  {
1153
0
    httpAddrFreeList(addrlist);
1154
0
    return (NULL);
1155
0
  }
1156
1157
0
  httpAddrFreeList(addrlist);
1158
0
  break;
1159
1160
0
    default : /* Remove bogus compiler warning... */
1161
0
        return (NULL);
1162
0
  }
1163
1164
0
  if (fd < 0)
1165
0
    return (NULL);
1166
1167
 /*
1168
  * Create the CUPS file structure...
1169
  */
1170
1171
0
  if ((fp = cupsFileOpenFd(fd, mode)) == NULL)
1172
0
  {
1173
0
    if (*mode == 's')
1174
0
      httpAddrClose(NULL, fd);
1175
0
    else
1176
0
      close(fd);
1177
0
  }
1178
1179
 /*
1180
  * Return it...
1181
  */
1182
1183
0
  return (fp);
1184
0
}
1185
1186
/*
1187
 * 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor.
1188
 *
1189
 * The "mode" parameter can be "r" to read, "w" to write, "a" to append,
1190
 * or "s" to treat the file descriptor as a bidirectional socket connection.
1191
 *
1192
 * When opening for writing ("w"), an optional number from 1 to 9 can be
1193
 * supplied which enables Flate compression of the file.  Compression is
1194
 * not supported for the "a" (append) mode.
1195
 *
1196
 * @since CUPS 1.2/macOS 10.5@
1197
 */
1198
1199
cups_file_t *       /* O - CUPS file or @code NULL@ if the file could not be opened */
1200
cupsFileOpenFd(int        fd,   /* I - File descriptor */
1201
         const char *mode)  /* I - Open mode */
1202
0
{
1203
0
  cups_file_t *fp;      /* New CUPS file */
1204
1205
1206
0
  DEBUG_printf(("cupsFileOpenFd(fd=%d, mode=\"%s\")", fd, mode));
1207
1208
 /*
1209
  * Range check input...
1210
  */
1211
1212
0
  if (fd < 0 || !mode ||
1213
0
      (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
1214
0
      (*mode == 'a' && isdigit(mode[1] & 255)))
1215
0
    return (NULL);
1216
1217
 /*
1218
  * Allocate memory...
1219
  */
1220
1221
0
  if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
1222
0
    return (NULL);
1223
1224
 /*
1225
  * Open the file...
1226
  */
1227
1228
0
  fp->fd = fd;
1229
1230
0
  switch (*mode)
1231
0
  {
1232
0
    case 'a' :
1233
0
        fp->pos = lseek(fd, 0, SEEK_END);
1234
1235
0
    case 'w' :
1236
0
  fp->mode = 'w';
1237
0
  fp->ptr  = fp->buf;
1238
0
  fp->end  = fp->buf + sizeof(fp->buf);
1239
1240
0
#ifdef HAVE_LIBZ
1241
0
  if (mode[1] >= '1' && mode[1] <= '9')
1242
0
  {
1243
   /*
1244
    * Open a compressed stream, so write the standard gzip file
1245
    * header...
1246
    */
1247
1248
0
          unsigned char header[10]; /* gzip file header */
1249
0
    time_t  curtime;  /* Current time */
1250
1251
1252
0
          curtime   = time(NULL);
1253
0
    header[0] = 0x1f;
1254
0
    header[1] = 0x8b;
1255
0
    header[2] = Z_DEFLATED;
1256
0
    header[3] = 0;
1257
0
    header[4] = (unsigned char)curtime;
1258
0
    header[5] = (unsigned char)(curtime >> 8);
1259
0
    header[6] = (unsigned char)(curtime >> 16);
1260
0
    header[7] = (unsigned char)(curtime >> 24);
1261
0
    header[8] = 0;
1262
0
    header[9] = 0x03;
1263
1264
0
    cups_write(fp, (char *)header, 10);
1265
1266
         /*
1267
    * Initialize the compressor...
1268
    */
1269
1270
0
          deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8,
1271
0
                 Z_DEFAULT_STRATEGY);
1272
1273
0
    fp->stream.next_out  = fp->cbuf;
1274
0
    fp->stream.avail_out = sizeof(fp->cbuf);
1275
0
    fp->compressed       = 1;
1276
0
    fp->crc              = crc32(0L, Z_NULL, 0);
1277
0
  }
1278
0
#endif /* HAVE_LIBZ */
1279
0
        break;
1280
1281
0
    case 'r' :
1282
0
  fp->mode = 'r';
1283
0
  break;
1284
1285
0
    case 's' :
1286
0
        fp->mode = 's';
1287
0
  break;
1288
1289
0
    default : /* Remove bogus compiler warning... */
1290
0
        return (NULL);
1291
0
  }
1292
1293
 /*
1294
  * Don't pass this file to child processes...
1295
  */
1296
1297
0
#ifndef _WIN32
1298
0
  fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC);
1299
0
#endif /* !_WIN32 */
1300
1301
0
  return (fp);
1302
0
}
1303
1304
1305
/*
1306
 * '_cupsFilePeekAhead()' - See if the requested character is buffered up.
1307
 */
1308
1309
int         /* O - 1 if present, 0 otherwise */
1310
_cupsFilePeekAhead(cups_file_t *fp, /* I - CUPS file */
1311
                   int         ch)  /* I - Character */
1312
0
{
1313
0
  return (fp && fp->ptr && memchr(fp->ptr, ch, (size_t)(fp->end - fp->ptr)));
1314
0
}
1315
1316
1317
/*
1318
 * 'cupsFilePeekChar()' - Peek at the next character from a file.
1319
 *
1320
 * @since CUPS 1.2/macOS 10.5@
1321
 */
1322
1323
int         /* O - Character or -1 on end of file */
1324
cupsFilePeekChar(cups_file_t *fp) /* I - CUPS file */
1325
0
{
1326
 /*
1327
  * Range check input...
1328
  */
1329
1330
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
1331
0
    return (-1);
1332
1333
 /*
1334
  * If the input buffer is empty, try to read more data...
1335
  */
1336
1337
0
  if (fp->ptr >= fp->end)
1338
0
    if (cups_fill(fp) <= 0)
1339
0
      return (-1);
1340
1341
 /*
1342
  * Return the next character in the buffer...
1343
  */
1344
1345
0
  return (*(fp->ptr) & 255);
1346
0
}
1347
1348
1349
/*
1350
 * 'cupsFilePrintf()' - Write a formatted string.
1351
 *
1352
 * @since CUPS 1.2/macOS 10.5@
1353
 */
1354
1355
int         /* O - Number of bytes written or -1 on error */
1356
cupsFilePrintf(cups_file_t *fp,   /* I - CUPS file */
1357
               const char  *format, /* I - Printf-style format string */
1358
         ...)     /* I - Additional args as necessary */
1359
0
{
1360
0
  va_list ap;     /* Argument list */
1361
0
  ssize_t bytes;      /* Formatted size */
1362
1363
1364
0
  DEBUG_printf(("2cupsFilePrintf(fp=%p, format=\"%s\", ...)", (void *)fp, format));
1365
1366
0
  if (!fp || !format || (fp->mode != 'w' && fp->mode != 's'))
1367
0
    return (-1);
1368
1369
0
  if (!fp->printf_buffer)
1370
0
  {
1371
   /*
1372
    * Start with an 1k printf buffer...
1373
    */
1374
1375
0
    if ((fp->printf_buffer = malloc(1024)) == NULL)
1376
0
      return (-1);
1377
1378
0
    fp->printf_size = 1024;
1379
0
  }
1380
1381
0
  va_start(ap, format);
1382
0
  bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
1383
0
  va_end(ap);
1384
1385
0
  if (bytes >= (ssize_t)fp->printf_size)
1386
0
  {
1387
   /*
1388
    * Expand the printf buffer...
1389
    */
1390
1391
0
    char  *temp;      /* Temporary buffer pointer */
1392
1393
1394
0
    if (bytes > 65535)
1395
0
      return (-1);
1396
1397
0
    if ((temp = realloc(fp->printf_buffer, (size_t)(bytes + 1))) == NULL)
1398
0
      return (-1);
1399
1400
0
    fp->printf_buffer = temp;
1401
0
    fp->printf_size   = (size_t)(bytes + 1);
1402
1403
0
    va_start(ap, format);
1404
0
    bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
1405
0
    va_end(ap);
1406
0
  }
1407
1408
0
  if (fp->mode == 's')
1409
0
  {
1410
0
    if (cups_write(fp, fp->printf_buffer, (size_t)bytes) < 0)
1411
0
      return (-1);
1412
1413
0
    fp->pos += bytes;
1414
1415
0
    DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1416
1417
0
    return ((int)bytes);
1418
0
  }
1419
1420
0
  if ((fp->ptr + bytes) > fp->end)
1421
0
    if (cupsFileFlush(fp))
1422
0
      return (-1);
1423
1424
0
  fp->pos += bytes;
1425
1426
0
  DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1427
1428
0
  if ((size_t)bytes > sizeof(fp->buf))
1429
0
  {
1430
0
#ifdef HAVE_LIBZ
1431
0
    if (fp->compressed)
1432
0
      return ((int)cups_compress(fp, fp->printf_buffer, (size_t)bytes));
1433
0
    else
1434
0
#endif /* HAVE_LIBZ */
1435
0
      return ((int)cups_write(fp, fp->printf_buffer, (size_t)bytes));
1436
0
  }
1437
0
  else
1438
0
  {
1439
0
    memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
1440
0
    fp->ptr += bytes;
1441
1442
0
    if (fp->is_stdio && cupsFileFlush(fp))
1443
0
      return (-1);
1444
0
    else
1445
0
      return ((int)bytes);
1446
0
  }
1447
0
}
1448
1449
1450
/*
1451
 * 'cupsFilePutChar()' - Write a character.
1452
 *
1453
 * @since CUPS 1.2/macOS 10.5@
1454
 */
1455
1456
int         /* O - 0 on success, -1 on error */
1457
cupsFilePutChar(cups_file_t *fp,  /* I - CUPS file */
1458
                int         c)    /* I - Character to write */
1459
0
{
1460
 /*
1461
  * Range check input...
1462
  */
1463
1464
0
  if (!fp || (fp->mode != 'w' && fp->mode != 's'))
1465
0
    return (-1);
1466
1467
0
  if (fp->mode == 's')
1468
0
  {
1469
   /*
1470
    * Send character immediately over socket...
1471
    */
1472
1473
0
    char ch;        /* Output character */
1474
1475
1476
0
    ch = (char)c;
1477
1478
0
    if (send(fp->fd, &ch, 1, 0) < 1)
1479
0
      return (-1);
1480
0
  }
1481
0
  else
1482
0
  {
1483
   /*
1484
    * Buffer it up...
1485
    */
1486
1487
0
    if (fp->ptr >= fp->end)
1488
0
      if (cupsFileFlush(fp))
1489
0
  return (-1);
1490
1491
0
    *(fp->ptr) ++ = (char)c;
1492
0
  }
1493
1494
0
  fp->pos ++;
1495
1496
0
  DEBUG_printf(("4cupsFilePutChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1497
1498
0
  return (0);
1499
0
}
1500
1501
1502
/*
1503
 * 'cupsFilePutConf()' - Write a configuration line.
1504
 *
1505
 * This function handles any comment escaping of the value.
1506
 *
1507
 * @since CUPS 1.4/macOS 10.6@
1508
 */
1509
1510
ssize_t         /* O - Number of bytes written or -1 on error */
1511
cupsFilePutConf(cups_file_t *fp,  /* I - CUPS file */
1512
                const char *directive,  /* I - Directive */
1513
    const char *value)  /* I - Value */
1514
0
{
1515
0
  ssize_t bytes,      /* Number of bytes written */
1516
0
    temp;     /* Temporary byte count */
1517
0
  const char  *ptr;     /* Pointer into value */
1518
1519
1520
0
  if (!fp || !directive || !*directive)
1521
0
    return (-1);
1522
1523
0
  if ((bytes = cupsFilePuts(fp, directive)) < 0)
1524
0
    return (-1);
1525
1526
0
  if (cupsFilePutChar(fp, ' ') < 0)
1527
0
    return (-1);
1528
0
  bytes ++;
1529
1530
0
  if (value && *value)
1531
0
  {
1532
0
    if ((ptr = strchr(value, '#')) != NULL)
1533
0
    {
1534
     /*
1535
      * Need to quote the first # in the info string...
1536
      */
1537
1538
0
      if ((temp = cupsFileWrite(fp, value, (size_t)(ptr - value))) < 0)
1539
0
        return (-1);
1540
0
      bytes += temp;
1541
1542
0
      if (cupsFilePutChar(fp, '\\') < 0)
1543
0
        return (-1);
1544
0
      bytes ++;
1545
1546
0
      if ((temp = cupsFilePuts(fp, ptr)) < 0)
1547
0
        return (-1);
1548
0
      bytes += temp;
1549
0
    }
1550
0
    else if ((temp = cupsFilePuts(fp, value)) < 0)
1551
0
      return (-1);
1552
0
    else
1553
0
      bytes += temp;
1554
0
  }
1555
1556
0
  if (cupsFilePutChar(fp, '\n') < 0)
1557
0
    return (-1);
1558
0
  else
1559
0
    return (bytes + 1);
1560
0
}
1561
1562
1563
/*
1564
 * 'cupsFilePuts()' - Write a string.
1565
 *
1566
 * Like the @code fputs@ function, no newline is appended to the string.
1567
 *
1568
 * @since CUPS 1.2/macOS 10.5@
1569
 */
1570
1571
int         /* O - Number of bytes written or -1 on error */
1572
cupsFilePuts(cups_file_t *fp,   /* I - CUPS file */
1573
             const char  *s)    /* I - String to write */
1574
0
{
1575
0
  ssize_t bytes;      /* Bytes to write */
1576
1577
1578
 /*
1579
  * Range check input...
1580
  */
1581
1582
0
  if (!fp || !s || (fp->mode != 'w' && fp->mode != 's'))
1583
0
    return (-1);
1584
1585
 /*
1586
  * Write the string...
1587
  */
1588
1589
0
  bytes = (ssize_t)strlen(s);
1590
1591
0
  if (fp->mode == 's')
1592
0
  {
1593
0
    if (cups_write(fp, s, (size_t)bytes) < 0)
1594
0
      return (-1);
1595
1596
0
    fp->pos += bytes;
1597
1598
0
    DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1599
1600
0
    return ((int)bytes);
1601
0
  }
1602
1603
0
  if ((fp->ptr + bytes) > fp->end)
1604
0
    if (cupsFileFlush(fp))
1605
0
      return (-1);
1606
1607
0
  fp->pos += bytes;
1608
1609
0
  DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1610
1611
0
  if ((size_t)bytes > sizeof(fp->buf))
1612
0
  {
1613
0
#ifdef HAVE_LIBZ
1614
0
    if (fp->compressed)
1615
0
      return ((int)cups_compress(fp, s, (size_t)bytes));
1616
0
    else
1617
0
#endif /* HAVE_LIBZ */
1618
0
      return ((int)cups_write(fp, s, (size_t)bytes));
1619
0
  }
1620
0
  else
1621
0
  {
1622
0
    memcpy(fp->ptr, s, (size_t)bytes);
1623
0
    fp->ptr += bytes;
1624
1625
0
    if (fp->is_stdio && cupsFileFlush(fp))
1626
0
      return (-1);
1627
0
    else
1628
0
      return ((int)bytes);
1629
0
  }
1630
0
}
1631
1632
1633
/*
1634
 * 'cupsFileRead()' - Read from a file.
1635
 *
1636
 * @since CUPS 1.2/macOS 10.5@
1637
 */
1638
1639
ssize_t         /* O - Number of bytes read or -1 on error */
1640
cupsFileRead(cups_file_t *fp,   /* I - CUPS file */
1641
             char        *buf,    /* O - Buffer */
1642
       size_t      bytes)   /* I - Number of bytes to read */
1643
0
{
1644
0
  size_t  total;      /* Total bytes read */
1645
0
  ssize_t count;      /* Bytes read */
1646
1647
1648
0
  DEBUG_printf(("2cupsFileRead(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
1649
1650
 /*
1651
  * Range check input...
1652
  */
1653
1654
0
  if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's'))
1655
0
    return (-1);
1656
1657
0
  if (bytes == 0)
1658
0
    return (0);
1659
1660
0
  if (fp->eof)
1661
0
  {
1662
0
    DEBUG_puts("5cupsFileRead: End-of-file!");
1663
0
    return (-1);
1664
0
  }
1665
1666
 /*
1667
  * Loop until all bytes are read...
1668
  */
1669
1670
0
  total = 0;
1671
0
  while (bytes > 0)
1672
0
  {
1673
0
    if (fp->ptr >= fp->end)
1674
0
      if (cups_fill(fp) <= 0)
1675
0
      {
1676
0
        DEBUG_printf(("4cupsFileRead: cups_fill() returned -1, total="
1677
0
                CUPS_LLFMT, CUPS_LLCAST total));
1678
1679
0
        if (total > 0)
1680
0
          return ((ssize_t)total);
1681
0
  else
1682
0
    return (-1);
1683
0
      }
1684
1685
0
    count = (ssize_t)(fp->end - fp->ptr);
1686
0
    if (count > (ssize_t)bytes)
1687
0
      count = (ssize_t)bytes;
1688
1689
0
    memcpy(buf, fp->ptr,(size_t) count);
1690
0
    fp->ptr += count;
1691
0
    fp->pos += count;
1692
1693
0
    DEBUG_printf(("4cupsFileRead: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1694
1695
   /*
1696
    * Update the counts for the last read...
1697
    */
1698
1699
0
    bytes -= (size_t)count;
1700
0
    total += (size_t)count;
1701
0
    buf   += count;
1702
0
  }
1703
1704
 /*
1705
  * Return the total number of bytes read...
1706
  */
1707
1708
0
  DEBUG_printf(("3cupsFileRead: total=" CUPS_LLFMT, CUPS_LLCAST total));
1709
1710
0
  return ((ssize_t)total);
1711
0
}
1712
1713
1714
/*
1715
 * 'cupsFileRewind()' - Set the current file position to the beginning of the
1716
 *                      file.
1717
 *
1718
 * @since CUPS 1.2/macOS 10.5@
1719
 */
1720
1721
off_t         /* O - New file position or -1 on error */
1722
cupsFileRewind(cups_file_t *fp)   /* I - CUPS file */
1723
0
{
1724
 /*
1725
  * Range check input...
1726
  */
1727
1728
0
  DEBUG_printf(("cupsFileRewind(fp=%p)", (void *)fp));
1729
0
  DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1730
1731
0
  if (!fp || fp->mode != 'r')
1732
0
    return (-1);
1733
1734
 /*
1735
  * Handle special cases...
1736
  */
1737
1738
0
  if (fp->bufpos == 0)
1739
0
  {
1740
   /*
1741
    * No seeking necessary...
1742
    */
1743
1744
0
    fp->pos = 0;
1745
1746
0
    if (fp->ptr)
1747
0
    {
1748
0
      fp->ptr = fp->buf;
1749
0
      fp->eof = 0;
1750
0
    }
1751
1752
0
    DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1753
1754
0
    return (0);
1755
0
  }
1756
1757
 /*
1758
  * Otherwise, seek in the file and cleanup any compression buffers...
1759
  */
1760
1761
0
#ifdef HAVE_LIBZ
1762
0
  if (fp->compressed)
1763
0
  {
1764
0
    inflateEnd(&fp->stream);
1765
0
    fp->compressed = 0;
1766
0
  }
1767
0
#endif /* HAVE_LIBZ */
1768
1769
0
  if (lseek(fp->fd, 0, SEEK_SET))
1770
0
  {
1771
0
    DEBUG_printf(("1cupsFileRewind: lseek failed: %s", strerror(errno)));
1772
0
    return (-1);
1773
0
  }
1774
1775
0
  fp->bufpos = 0;
1776
0
  fp->pos    = 0;
1777
0
  fp->ptr    = NULL;
1778
0
  fp->end    = NULL;
1779
0
  fp->eof    = 0;
1780
1781
0
  DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1782
1783
0
  return (0);
1784
0
}
1785
1786
1787
/*
1788
 * 'cupsFileSeek()' - Seek in a file.
1789
 *
1790
 * @since CUPS 1.2/macOS 10.5@
1791
 */
1792
1793
off_t         /* O - New file position or -1 on error */
1794
cupsFileSeek(cups_file_t *fp,   /* I - CUPS file */
1795
             off_t       pos)   /* I - Position in file */
1796
0
{
1797
0
  ssize_t bytes;      /* Number bytes in buffer */
1798
1799
1800
0
  DEBUG_printf(("cupsFileSeek(fp=%p, pos=" CUPS_LLFMT ")", (void *)fp, CUPS_LLCAST pos));
1801
0
  DEBUG_printf(("2cupsFileSeek: fp->pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1802
0
  DEBUG_printf(("2cupsFileSeek: fp->ptr=%p, fp->end=%p", (void *)fp->ptr, (void *)fp->end));
1803
1804
 /*
1805
  * Range check input...
1806
  */
1807
1808
0
  if (!fp || pos < 0 || fp->mode != 'r')
1809
0
    return (-1);
1810
1811
 /*
1812
  * Handle special cases...
1813
  */
1814
1815
0
  if (pos == 0)
1816
0
    return (cupsFileRewind(fp));
1817
1818
0
  if (fp->ptr)
1819
0
  {
1820
0
    bytes = (ssize_t)(fp->end - fp->buf);
1821
1822
0
    DEBUG_printf(("2cupsFileSeek: bytes=" CUPS_LLFMT, CUPS_LLCAST bytes));
1823
1824
0
    if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1825
0
    {
1826
     /*
1827
      * No seeking necessary...
1828
      */
1829
1830
0
      fp->pos = pos;
1831
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1832
0
      fp->eof = 0;
1833
1834
0
      return (pos);
1835
0
    }
1836
0
  }
1837
1838
0
#ifdef HAVE_LIBZ
1839
0
  if (!fp->compressed && !fp->ptr)
1840
0
  {
1841
   /*
1842
    * Preload a buffer to determine whether the file is compressed...
1843
    */
1844
1845
0
    if (cups_fill(fp) <= 0)
1846
0
      return (-1);
1847
0
  }
1848
0
#endif /* HAVE_LIBZ */
1849
1850
 /*
1851
  * Seek forwards or backwards...
1852
  */
1853
1854
0
  fp->eof = 0;
1855
1856
0
  if (pos < fp->bufpos)
1857
0
  {
1858
   /*
1859
    * Need to seek backwards...
1860
    */
1861
1862
0
    DEBUG_puts("2cupsFileSeek: SEEK BACKWARDS");
1863
1864
0
#ifdef HAVE_LIBZ
1865
0
    if (fp->compressed)
1866
0
    {
1867
0
      inflateEnd(&fp->stream);
1868
1869
0
      lseek(fp->fd, 0, SEEK_SET);
1870
0
      fp->bufpos = 0;
1871
0
      fp->pos    = 0;
1872
0
      fp->ptr    = NULL;
1873
0
      fp->end    = NULL;
1874
1875
0
      while ((bytes = cups_fill(fp)) > 0)
1876
0
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1877
0
    break;
1878
1879
0
      if (bytes <= 0)
1880
0
        return (-1);
1881
1882
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1883
0
      fp->pos = pos;
1884
0
    }
1885
0
    else
1886
0
#endif /* HAVE_LIBZ */
1887
0
    {
1888
0
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1889
0
      fp->pos    = fp->bufpos;
1890
0
      fp->ptr    = NULL;
1891
0
      fp->end    = NULL;
1892
1893
0
      DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
1894
0
                    CUPS_LLCAST fp->pos));
1895
0
    }
1896
0
  }
1897
0
  else
1898
0
  {
1899
   /*
1900
    * Need to seek forwards...
1901
    */
1902
1903
0
    DEBUG_puts("2cupsFileSeek: SEEK FORWARDS");
1904
1905
0
#ifdef HAVE_LIBZ
1906
0
    if (fp->compressed)
1907
0
    {
1908
0
      while ((bytes = cups_fill(fp)) > 0)
1909
0
      {
1910
0
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1911
0
    break;
1912
0
      }
1913
1914
0
      if (bytes <= 0)
1915
0
        return (-1);
1916
1917
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1918
0
      fp->pos = pos;
1919
0
    }
1920
0
    else
1921
0
#endif /* HAVE_LIBZ */
1922
0
    {
1923
0
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1924
0
      fp->pos    = fp->bufpos;
1925
0
      fp->ptr    = NULL;
1926
0
      fp->end    = NULL;
1927
1928
0
      DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
1929
0
                    CUPS_LLCAST fp->pos));
1930
0
    }
1931
0
  }
1932
1933
0
  DEBUG_printf(("2cupsFileSeek: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
1934
1935
0
  return (fp->pos);
1936
0
}
1937
1938
1939
/*
1940
 * 'cupsFileStderr()' - Return a CUPS file associated with stderr.
1941
 *
1942
 * @since CUPS 1.2/macOS 10.5@
1943
 */
1944
1945
cups_file_t *       /* O - CUPS file */
1946
cupsFileStderr(void)
1947
0
{
1948
0
  _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
1949
1950
1951
 /*
1952
  * Open file descriptor 2 as needed...
1953
  */
1954
1955
0
  if (!cg->stdio_files[2])
1956
0
  {
1957
   /*
1958
    * Flush any pending output on the stdio file...
1959
    */
1960
1961
0
    fflush(stderr);
1962
1963
   /*
1964
    * Open file descriptor 2...
1965
    */
1966
1967
0
    if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL)
1968
0
      cg->stdio_files[2]->is_stdio = 1;
1969
0
  }
1970
1971
0
  return (cg->stdio_files[2]);
1972
0
}
1973
1974
1975
/*
1976
 * 'cupsFileStdin()' - Return a CUPS file associated with stdin.
1977
 *
1978
 * @since CUPS 1.2/macOS 10.5@
1979
 */
1980
1981
cups_file_t *       /* O - CUPS file */
1982
cupsFileStdin(void)
1983
0
{
1984
0
  _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
1985
1986
1987
 /*
1988
  * Open file descriptor 0 as needed...
1989
  */
1990
1991
0
  if (!cg->stdio_files[0])
1992
0
  {
1993
   /*
1994
    * Open file descriptor 0...
1995
    */
1996
1997
0
    if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL)
1998
0
      cg->stdio_files[0]->is_stdio = 1;
1999
0
  }
2000
2001
0
  return (cg->stdio_files[0]);
2002
0
}
2003
2004
2005
/*
2006
 * 'cupsFileStdout()' - Return a CUPS file associated with stdout.
2007
 *
2008
 * @since CUPS 1.2/macOS 10.5@
2009
 */
2010
2011
cups_file_t *       /* O - CUPS file */
2012
cupsFileStdout(void)
2013
0
{
2014
0
  _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
2015
2016
2017
 /*
2018
  * Open file descriptor 1 as needed...
2019
  */
2020
2021
0
  if (!cg->stdio_files[1])
2022
0
  {
2023
   /*
2024
    * Flush any pending output on the stdio file...
2025
    */
2026
2027
0
    fflush(stdout);
2028
2029
   /*
2030
    * Open file descriptor 1...
2031
    */
2032
2033
0
    if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL)
2034
0
      cg->stdio_files[1]->is_stdio = 1;
2035
0
  }
2036
2037
0
  return (cg->stdio_files[1]);
2038
0
}
2039
2040
2041
/*
2042
 * 'cupsFileTell()' - Return the current file position.
2043
 *
2044
 * @since CUPS 1.2/macOS 10.5@
2045
 */
2046
2047
off_t         /* O - File position */
2048
cupsFileTell(cups_file_t *fp)   /* I - CUPS file */
2049
0
{
2050
0
  DEBUG_printf(("2cupsFileTell(fp=%p)", (void *)fp));
2051
0
  DEBUG_printf(("3cupsFileTell: pos=" CUPS_LLFMT, CUPS_LLCAST (fp ? fp->pos : -1)));
2052
2053
0
  return (fp ? fp->pos : 0);
2054
0
}
2055
2056
2057
/*
2058
 * 'cupsFileUnlock()' - Unlock access to a file.
2059
 *
2060
 * @since CUPS 1.2/macOS 10.5@
2061
 */
2062
2063
int         /* O - 0 on success, -1 on error */
2064
cupsFileUnlock(cups_file_t *fp)   /* I - CUPS file */
2065
0
{
2066
 /*
2067
  * Range check...
2068
  */
2069
2070
0
  DEBUG_printf(("cupsFileUnlock(fp=%p)", (void *)fp));
2071
2072
0
  if (!fp || fp->mode == 's')
2073
0
    return (-1);
2074
2075
 /*
2076
  * Unlock...
2077
  */
2078
2079
#ifdef _WIN32
2080
  return (_locking(fp->fd, _LK_UNLCK, 0));
2081
#else
2082
0
  return (lockf(fp->fd, F_ULOCK, 0));
2083
0
#endif /* _WIN32 */
2084
0
}
2085
2086
2087
/*
2088
 * 'cupsFileWrite()' - Write to a file.
2089
 *
2090
 * @since CUPS 1.2/macOS 10.5@
2091
 */
2092
2093
ssize_t         /* O - Number of bytes written or -1 on error */
2094
cupsFileWrite(cups_file_t *fp,    /* I - CUPS file */
2095
              const char  *buf,   /* I - Buffer */
2096
        size_t      bytes)  /* I - Number of bytes to write */
2097
0
{
2098
 /*
2099
  * Range check input...
2100
  */
2101
2102
0
  DEBUG_printf(("2cupsFileWrite(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
2103
2104
0
  if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's'))
2105
0
    return (-1);
2106
2107
0
  if (bytes == 0)
2108
0
    return (0);
2109
2110
 /*
2111
  * Write the buffer...
2112
  */
2113
2114
0
  if (fp->mode == 's')
2115
0
  {
2116
0
    if (cups_write(fp, buf, bytes) < 0)
2117
0
      return (-1);
2118
2119
0
    fp->pos += (off_t)bytes;
2120
2121
0
    DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
2122
2123
0
    return ((ssize_t)bytes);
2124
0
  }
2125
2126
0
  if ((fp->ptr + bytes) > fp->end)
2127
0
    if (cupsFileFlush(fp))
2128
0
      return (-1);
2129
2130
0
  fp->pos += (off_t)bytes;
2131
2132
0
  DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
2133
2134
0
  if (bytes > sizeof(fp->buf))
2135
0
  {
2136
0
#ifdef HAVE_LIBZ
2137
0
    if (fp->compressed)
2138
0
      return (cups_compress(fp, buf, bytes));
2139
0
    else
2140
0
#endif /* HAVE_LIBZ */
2141
0
      return (cups_write(fp, buf, bytes));
2142
0
  }
2143
0
  else
2144
0
  {
2145
0
    memcpy(fp->ptr, buf, bytes);
2146
0
    fp->ptr += bytes;
2147
0
    return ((ssize_t)bytes);
2148
0
  }
2149
0
}
2150
2151
2152
#ifdef HAVE_LIBZ
2153
/*
2154
 * 'cups_compress()' - Compress a buffer of data.
2155
 */
2156
2157
static ssize_t        /* O - Number of bytes written or -1 */
2158
cups_compress(cups_file_t *fp,    /* I - CUPS file */
2159
              const char  *buf,   /* I - Buffer */
2160
        size_t      bytes)  /* I - Number bytes */
2161
0
{
2162
0
  DEBUG_printf(("7cups_compress(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
2163
2164
 /*
2165
  * Update the CRC...
2166
  */
2167
2168
0
  fp->crc = crc32(fp->crc, (const Bytef *)buf, (uInt)bytes);
2169
2170
 /*
2171
  * Deflate the bytes...
2172
  */
2173
2174
0
  fp->stream.next_in  = (Bytef *)buf;
2175
0
  fp->stream.avail_in = (uInt)bytes;
2176
2177
0
  while (fp->stream.avail_in > 0)
2178
0
  {
2179
   /*
2180
    * Flush the current buffer...
2181
    */
2182
2183
0
    DEBUG_printf(("9cups_compress: avail_in=%d, avail_out=%d",
2184
0
                  fp->stream.avail_in, fp->stream.avail_out));
2185
2186
0
    if (fp->stream.avail_out < (uInt)(sizeof(fp->cbuf) / 8))
2187
0
    {
2188
0
      if (cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf)) < 0)
2189
0
        return (-1);
2190
2191
0
      fp->stream.next_out  = fp->cbuf;
2192
0
      fp->stream.avail_out = sizeof(fp->cbuf);
2193
0
    }
2194
2195
0
    deflate(&(fp->stream), Z_NO_FLUSH);
2196
0
  }
2197
2198
0
  return ((ssize_t)bytes);
2199
0
}
2200
#endif /* HAVE_LIBZ */
2201
2202
2203
/*
2204
 * 'cups_fill()' - Fill the input buffer.
2205
 */
2206
2207
static ssize_t        /* O - Number of bytes or -1 */
2208
cups_fill(cups_file_t *fp)    /* I - CUPS file */
2209
0
{
2210
0
  ssize_t   bytes;    /* Number of bytes read */
2211
0
#ifdef HAVE_LIBZ
2212
0
  int     status;   /* Decompression status */
2213
0
  const unsigned char *ptr,   /* Pointer into buffer */
2214
0
      *end;   /* End of buffer */
2215
0
#endif /* HAVE_LIBZ */
2216
2217
2218
0
  DEBUG_printf(("7cups_fill(fp=%p)", (void *)fp));
2219
0
  DEBUG_printf(("9cups_fill: fp->ptr=%p, fp->end=%p, fp->buf=%p, fp->bufpos=" CUPS_LLFMT ", fp->eof=%d", (void *)fp->ptr, (void *)fp->end, (void *)fp->buf, CUPS_LLCAST fp->bufpos, fp->eof));
2220
2221
0
  if (fp->ptr && fp->end)
2222
0
    fp->bufpos += fp->end - fp->buf;
2223
2224
0
#ifdef HAVE_LIBZ
2225
0
  DEBUG_printf(("9cups_fill: fp->compressed=%d", fp->compressed));
2226
2227
0
  while (!fp->ptr || fp->compressed)
2228
0
  {
2229
   /*
2230
    * Check to see if we have read any data yet; if not, see if we have a
2231
    * compressed file...
2232
    */
2233
2234
0
    if (!fp->ptr)
2235
0
    {
2236
     /*
2237
      * Reset the file position in case we are seeking...
2238
      */
2239
2240
0
      fp->compressed = 0;
2241
2242
     /*
2243
      * Read the first bytes in the file to determine if we have a gzip'd
2244
      * file...
2245
      */
2246
2247
0
      if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0)
2248
0
      {
2249
       /*
2250
  * Can't read from file!
2251
  */
2252
2253
0
        DEBUG_printf(("9cups_fill: cups_read() returned " CUPS_LLFMT,
2254
0
                CUPS_LLCAST bytes));
2255
2256
0
        fp->eof = 1;
2257
2258
0
  return (-1);
2259
0
      }
2260
2261
0
      if (bytes < 10 || fp->buf[0] != 0x1f ||
2262
0
          (fp->buf[1] & 255) != 0x8b ||
2263
0
          fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
2264
0
      {
2265
       /*
2266
  * Not a gzip'd file!
2267
  */
2268
2269
0
  fp->ptr = fp->buf;
2270
0
  fp->end = fp->buf + bytes;
2271
2272
0
        DEBUG_printf(("9cups_fill: Returning " CUPS_LLFMT,
2273
0
                CUPS_LLCAST bytes));
2274
2275
0
  return (bytes);
2276
0
      }
2277
2278
     /*
2279
      * Parse header junk: extra data, original name, and comment...
2280
      */
2281
2282
0
      ptr = (unsigned char *)fp->buf + 10;
2283
0
      end = (unsigned char *)fp->buf + bytes;
2284
2285
0
      if (fp->buf[3] & 0x04)
2286
0
      {
2287
       /*
2288
  * Skip extra data...
2289
  */
2290
2291
0
  if ((ptr + 2) > end)
2292
0
  {
2293
   /*
2294
    * Can't read from file!
2295
    */
2296
2297
0
    DEBUG_puts("9cups_fill: Extra gzip header data missing, returning -1.");
2298
2299
0
          fp->eof = 1;
2300
0
    errno   = EIO;
2301
2302
0
    return (-1);
2303
0
  }
2304
2305
0
  bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
2306
0
  ptr   += 2 + bytes;
2307
2308
0
  if (ptr > end)
2309
0
  {
2310
   /*
2311
    * Can't read from file!
2312
    */
2313
2314
0
    DEBUG_puts("9cups_fill: Extra gzip header data does not fit in initial buffer, returning -1.");
2315
2316
0
          fp->eof = 1;
2317
0
    errno   = EIO;
2318
2319
0
    return (-1);
2320
0
  }
2321
0
      }
2322
2323
0
      if (fp->buf[3] & 0x08)
2324
0
      {
2325
       /*
2326
  * Skip original name data...
2327
  */
2328
2329
0
  while (ptr < end && *ptr)
2330
0
          ptr ++;
2331
2332
0
  if (ptr < end)
2333
0
          ptr ++;
2334
0
  else
2335
0
  {
2336
   /*
2337
    * Can't read from file!
2338
    */
2339
2340
0
    DEBUG_puts("9cups_fill: Original filename in gzip header data does not fit in initial buffer, returning -1.");
2341
2342
0
          fp->eof = 1;
2343
0
    errno   = EIO;
2344
2345
0
    return (-1);
2346
0
  }
2347
0
      }
2348
2349
0
      if (fp->buf[3] & 0x10)
2350
0
      {
2351
       /*
2352
  * Skip comment data...
2353
  */
2354
2355
0
  while (ptr < end && *ptr)
2356
0
          ptr ++;
2357
2358
0
  if (ptr < end)
2359
0
          ptr ++;
2360
0
  else
2361
0
  {
2362
   /*
2363
    * Can't read from file!
2364
    */
2365
2366
0
    DEBUG_puts("9cups_fill: Comment in gzip header data does not fit in initial buffer, returning -1.");
2367
2368
0
          fp->eof = 1;
2369
0
    errno   = EIO;
2370
2371
0
    return (-1);
2372
0
  }
2373
0
      }
2374
2375
0
      if (fp->buf[3] & 0x02)
2376
0
      {
2377
       /*
2378
  * Skip header CRC data...
2379
  */
2380
2381
0
  ptr += 2;
2382
2383
0
  if (ptr > end)
2384
0
  {
2385
   /*
2386
    * Can't read from file!
2387
    */
2388
2389
0
    DEBUG_puts("9cups_fill: Header CRC in gzip header data does not fit in initial buffer, returning -1.");
2390
2391
0
          fp->eof = 1;
2392
0
    errno   = EIO;
2393
2394
0
    return (-1);
2395
0
  }
2396
0
      }
2397
2398
     /*
2399
      * Copy the flate-compressed data to the compression buffer...
2400
      */
2401
2402
0
      if ((bytes = end - ptr) > 0)
2403
0
        memcpy(fp->cbuf, ptr, (size_t)bytes);
2404
2405
     /*
2406
      * Setup the decompressor data...
2407
      */
2408
2409
0
      fp->stream.zalloc    = (alloc_func)0;
2410
0
      fp->stream.zfree     = (free_func)0;
2411
0
      fp->stream.opaque    = (voidpf)0;
2412
0
      fp->stream.next_in   = (Bytef *)fp->cbuf;
2413
0
      fp->stream.next_out  = NULL;
2414
0
      fp->stream.avail_in  = (uInt)bytes;
2415
0
      fp->stream.avail_out = 0;
2416
0
      fp->crc              = crc32(0L, Z_NULL, 0);
2417
2418
0
      if ((status = inflateInit2(&(fp->stream), -15)) != Z_OK)
2419
0
      {
2420
0
  DEBUG_printf(("9cups_fill: inflateInit2 returned %d, returning -1.", status));
2421
2422
0
        fp->eof = 1;
2423
0
        errno   = EIO;
2424
2425
0
  return (-1);
2426
0
      }
2427
2428
0
      fp->compressed = 1;
2429
0
    }
2430
2431
0
    if (fp->compressed)
2432
0
    {
2433
     /*
2434
      * If we have reached end-of-file, return immediately...
2435
      */
2436
2437
0
      if (fp->eof)
2438
0
      {
2439
0
        DEBUG_puts("9cups_fill: EOF, returning 0.");
2440
2441
0
  return (0);
2442
0
      }
2443
2444
     /*
2445
      * Fill the decompression buffer as needed...
2446
      */
2447
2448
0
      if (fp->stream.avail_in == 0)
2449
0
      {
2450
0
  if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
2451
0
  {
2452
0
    DEBUG_printf(("9cups_fill: cups_read error, returning %d.", (int)bytes));
2453
2454
0
    fp->eof = 1;
2455
2456
0
          return (bytes);
2457
0
  }
2458
2459
0
  fp->stream.next_in  = fp->cbuf;
2460
0
  fp->stream.avail_in = (uInt)bytes;
2461
0
      }
2462
2463
     /*
2464
      * Decompress data from the buffer...
2465
      */
2466
2467
0
      fp->stream.next_out  = (Bytef *)fp->buf;
2468
0
      fp->stream.avail_out = sizeof(fp->buf);
2469
2470
0
      status = inflate(&(fp->stream), Z_NO_FLUSH);
2471
2472
0
      if (fp->stream.next_out > (Bytef *)fp->buf)
2473
0
        fp->crc = crc32(fp->crc, (Bytef *)fp->buf,
2474
0
                  (uInt)(fp->stream.next_out - (Bytef *)fp->buf));
2475
2476
0
      if (status == Z_STREAM_END)
2477
0
      {
2478
       /*
2479
  * Read the CRC and length...
2480
  */
2481
2482
0
  unsigned char trailer[8]; /* Trailer bytes */
2483
0
  uLong   tcrc;   /* Trailer CRC */
2484
0
  ssize_t   tbytes = 0; /* Number of bytes */
2485
2486
0
  if (fp->stream.avail_in > 0)
2487
0
  {
2488
0
    if (fp->stream.avail_in > sizeof(trailer))
2489
0
      tbytes = (ssize_t)sizeof(trailer);
2490
0
    else
2491
0
      tbytes = (ssize_t)fp->stream.avail_in;
2492
2493
0
    memcpy(trailer, fp->stream.next_in, (size_t)tbytes);
2494
0
    fp->stream.next_in  += tbytes;
2495
0
    fp->stream.avail_in -= (size_t)tbytes;
2496
0
  }
2497
2498
0
        if (tbytes < (ssize_t)sizeof(trailer))
2499
0
  {
2500
0
    if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes))
2501
0
    {
2502
     /*
2503
      * Can't get it, so mark end-of-file...
2504
      */
2505
2506
0
      DEBUG_puts("9cups_fill: Unable to read gzip CRC trailer, returning -1.");
2507
2508
0
      fp->eof = 1;
2509
0
      errno   = EIO;
2510
2511
0
      return (-1);
2512
0
    }
2513
0
  }
2514
2515
0
  tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) |
2516
0
    (uLong)trailer[1]) << 8) | (uLong)trailer[0];
2517
2518
0
  if (tcrc != fp->crc)
2519
0
  {
2520
   /*
2521
    * Bad CRC, mark end-of-file...
2522
    */
2523
2524
0
    DEBUG_printf(("9cups_fill: tcrc=%08x != fp->crc=%08x, returning -1.", (unsigned int)tcrc, (unsigned int)fp->crc));
2525
2526
0
    fp->eof = 1;
2527
0
    errno   = EIO;
2528
2529
0
    return (-1);
2530
0
  }
2531
2532
       /*
2533
  * Otherwise, reset the compressed flag so that we re-read the
2534
  * file header...
2535
  */
2536
2537
0
        inflateEnd(&fp->stream);
2538
2539
0
  fp->compressed = 0;
2540
0
      }
2541
0
      else if (status < Z_OK)
2542
0
      {
2543
0
  DEBUG_printf(("9cups_fill: inflate returned %d, returning -1.", status));
2544
2545
0
        fp->eof = 1;
2546
0
        errno   = EIO;
2547
2548
0
  return (-1);
2549
0
      }
2550
2551
0
      bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out;
2552
2553
     /*
2554
      * Return the decompressed data...
2555
      */
2556
2557
0
      fp->ptr = fp->buf;
2558
0
      fp->end = fp->buf + bytes;
2559
2560
0
      if (bytes)
2561
0
      {
2562
0
        DEBUG_printf(("9cups_fill: Returning %d.", (int)bytes));
2563
0
  return (bytes);
2564
0
      }
2565
0
    }
2566
0
  }
2567
0
#endif /* HAVE_LIBZ */
2568
2569
 /*
2570
  * Read a buffer's full of data...
2571
  */
2572
2573
0
  if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0)
2574
0
  {
2575
   /*
2576
    * Can't read from file!
2577
    */
2578
2579
0
    fp->eof = 1;
2580
0
    fp->ptr = fp->buf;
2581
0
    fp->end = fp->buf;
2582
0
  }
2583
0
  else
2584
0
  {
2585
   /*
2586
    * Return the bytes we read...
2587
    */
2588
2589
0
    fp->eof = 0;
2590
0
    fp->ptr = fp->buf;
2591
0
    fp->end = fp->buf + bytes;
2592
0
  }
2593
2594
0
  DEBUG_printf(("9cups_fill: Not gzip, returning %d.", (int)bytes));
2595
2596
0
  return (bytes);
2597
0
}
2598
2599
2600
/*
2601
 * 'cups_open()' - Safely open a file for writing.
2602
 *
2603
 * We don't allow appending to directories or files that are hard-linked or
2604
 * symlinked.
2605
 */
2606
2607
static int        /* O - File descriptor or -1 otherwise */
2608
cups_open(const char *filename,   /* I - Filename */
2609
    int        mode)    /* I - Open mode */
2610
0
{
2611
0
  int   fd;     /* File descriptor */
2612
0
  struct stat fileinfo;   /* File information */
2613
0
#ifndef _WIN32
2614
0
  struct stat linkinfo;   /* Link information */
2615
0
#endif /* !_WIN32 */
2616
2617
2618
 /*
2619
  * Open the file...
2620
  */
2621
2622
0
  if ((fd = open(filename, mode, 0666)) < 0)
2623
0
    return (-1);
2624
2625
 /*
2626
  * Then verify that the file descriptor doesn't point to a directory or hard-
2627
  * linked file.
2628
  */
2629
2630
0
  if (fstat(fd, &fileinfo))
2631
0
  {
2632
0
    close(fd);
2633
0
    return (-1);
2634
0
  }
2635
2636
0
  if (fileinfo.st_nlink != 1)
2637
0
  {
2638
0
    close(fd);
2639
0
    errno = EPERM;
2640
0
    return (-1);
2641
0
  }
2642
2643
#ifdef _WIN32
2644
  if (fileinfo.st_mode & _S_IFDIR)
2645
#else
2646
0
  if (S_ISDIR(fileinfo.st_mode))
2647
0
#endif /* _WIN32 */
2648
0
  {
2649
0
    close(fd);
2650
0
    errno = EISDIR;
2651
0
    return (-1);
2652
0
  }
2653
2654
0
#ifndef _WIN32
2655
 /*
2656
  * Then use lstat to determine whether the filename is a symlink...
2657
  */
2658
2659
0
  if (lstat(filename, &linkinfo))
2660
0
  {
2661
0
    close(fd);
2662
0
    return (-1);
2663
0
  }
2664
2665
0
  if (S_ISLNK(linkinfo.st_mode) ||
2666
0
      fileinfo.st_dev != linkinfo.st_dev ||
2667
0
      fileinfo.st_ino != linkinfo.st_ino ||
2668
#ifdef HAVE_ST_GEN
2669
      fileinfo.st_gen != linkinfo.st_gen ||
2670
#endif /* HAVE_ST_GEN */
2671
0
      fileinfo.st_nlink != linkinfo.st_nlink ||
2672
0
      fileinfo.st_mode != linkinfo.st_mode)
2673
0
  {
2674
   /*
2675
    * Yes, don't allow!
2676
    */
2677
2678
0
    close(fd);
2679
0
    errno = EPERM;
2680
0
    return (-1);
2681
0
  }
2682
0
#endif /* !_WIN32 */
2683
2684
0
  return (fd);
2685
0
}
2686
2687
2688
/*
2689
 * 'cups_read()' - Read from a file descriptor.
2690
 */
2691
2692
static ssize_t        /* O - Number of bytes read or -1 */
2693
cups_read(cups_file_t *fp,    /* I - CUPS file */
2694
          char        *buf,   /* I - Buffer */
2695
    size_t      bytes)    /* I - Number bytes */
2696
0
{
2697
0
  ssize_t total;      /* Total bytes read */
2698
2699
2700
0
  DEBUG_printf(("7cups_read(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
2701
2702
 /*
2703
  * Loop until we read at least 0 bytes...
2704
  */
2705
2706
0
  for (;;)
2707
0
  {
2708
#ifdef _WIN32
2709
    if (fp->mode == 's')
2710
      total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
2711
    else
2712
      total = (ssize_t)read(fp->fd, buf, (unsigned)bytes);
2713
#else
2714
0
    if (fp->mode == 's')
2715
0
      total = recv(fp->fd, buf, bytes, 0);
2716
0
    else
2717
0
      total = read(fp->fd, buf, bytes);
2718
0
#endif /* _WIN32 */
2719
2720
0
    DEBUG_printf(("9cups_read: total=" CUPS_LLFMT, CUPS_LLCAST total));
2721
2722
0
    if (total >= 0)
2723
0
      break;
2724
2725
   /*
2726
    * Reads can be interrupted by signals and unavailable resources...
2727
    */
2728
2729
0
    if (errno == EAGAIN || errno == EINTR)
2730
0
      continue;
2731
0
    else
2732
0
      return (-1);
2733
0
  }
2734
2735
 /*
2736
  * Return the total number of bytes read...
2737
  */
2738
2739
0
  return (total);
2740
0
}
2741
2742
2743
/*
2744
 * 'cups_write()' - Write to a file descriptor.
2745
 */
2746
2747
static ssize_t        /* O - Number of bytes written or -1 */
2748
cups_write(cups_file_t *fp,   /* I - CUPS file */
2749
           const char  *buf,    /* I - Buffer */
2750
     size_t      bytes)   /* I - Number bytes */
2751
0
{
2752
0
  size_t  total;      /* Total bytes written */
2753
0
  ssize_t count;      /* Count this time */
2754
2755
2756
0
  DEBUG_printf(("7cups_write(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
2757
2758
 /*
2759
  * Loop until all bytes are written...
2760
  */
2761
2762
0
  total = 0;
2763
0
  while (bytes > 0)
2764
0
  {
2765
#ifdef _WIN32
2766
    if (fp->mode == 's')
2767
      count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
2768
    else
2769
      count = (ssize_t)write(fp->fd, buf, (unsigned)bytes);
2770
#else
2771
0
    if (fp->mode == 's')
2772
0
      count = send(fp->fd, buf, bytes, 0);
2773
0
    else
2774
0
      count = write(fp->fd, buf, bytes);
2775
0
#endif /* _WIN32 */
2776
2777
0
    DEBUG_printf(("9cups_write: count=" CUPS_LLFMT, CUPS_LLCAST count));
2778
2779
0
    if (count < 0)
2780
0
    {
2781
     /*
2782
      * Writes can be interrupted by signals and unavailable resources...
2783
      */
2784
2785
0
      if (errno == EAGAIN || errno == EINTR)
2786
0
        continue;
2787
0
      else
2788
0
        return (-1);
2789
0
    }
2790
2791
   /*
2792
    * Update the counts for the last write call...
2793
    */
2794
2795
0
    bytes -= (size_t)count;
2796
0
    total += (size_t)count;
2797
0
    buf   += count;
2798
0
  }
2799
2800
 /*
2801
  * Return the total number of bytes written...
2802
  */
2803
2804
0
  return ((ssize_t)total);
2805
0
}