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