]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/file.c
Update ipp documentation to reflect the behavior of configuring WiFi on IPP USB printers.
[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 *
96fee303 9 * Copyright © 2007-2019 by Apple Inc.
5a855d85 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
96fee303
MS
678 if (fp->eof)
679 {
680 DEBUG_puts("5cupsFileGetChar: End-of-file!");
681 return (-1);
682 }
683
ef416fc2 684 /*
685 * If the input buffer is empty, try to read more data...
686 */
687
807315e6 688 DEBUG_printf(("5cupsFileGetChar: fp->eof=%d, fp->ptr=%p, fp->end=%p", fp->eof, (void *)fp->ptr, (void *)fp->end));
cbf9404a 689
ef416fc2 690 if (fp->ptr >= fp->end)
cbf9404a 691 if (cups_fill(fp) <= 0)
b86bc4cf 692 {
f14324a7 693 DEBUG_puts("5cupsFileGetChar: Unable to fill buffer!");
ef416fc2 694 return (-1);
b86bc4cf 695 }
ef416fc2 696
697 /*
698 * Return the next character in the buffer...
699 */
700
f14324a7 701 DEBUG_printf(("5cupsFileGetChar: Returning %d...", *(fp->ptr) & 255));
b86bc4cf 702
634763e8
MS
703 fp->pos ++;
704
f14324a7 705 DEBUG_printf(("6cupsFileGetChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 706
ef416fc2 707 return (*(fp->ptr)++ & 255);
708}
709
710
711/*
22c9029b 712 * 'cupsFileGetConf()' - Get a line from a configuration file.
5a738aea 713 *
8072030b 714 * @since CUPS 1.2/macOS 10.5@
ef416fc2 715 */
716
5a738aea 717char * /* O - Line read or @code NULL@ on end of file or error */
ef416fc2 718cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */
719 char *buf, /* O - String buffer */
720 size_t buflen, /* I - Size of string buffer */
721 char **value, /* O - Pointer to value */
722 int *linenum) /* IO - Current line number */
723{
724 char *ptr; /* Pointer into line */
725
726
727 /*
728 * Range check input...
729 */
730
e07d4801 731 DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT
807315e6 732 ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum));
634763e8 733
ef416fc2 734 if (!fp || (fp->mode != 'r' && fp->mode != 's') ||
735 !buf || buflen < 2 || !value)
736 {
737 if (value)
738 *value = NULL;
739
740 return (NULL);
741 }
742
743 /*
744 * Read the next non-comment line...
745 */
746
747 *value = NULL;
f7deaa1a 748
ef416fc2 749 while (cupsFileGets(fp, buf, buflen))
750 {
751 (*linenum) ++;
752
753 /*
754 * Strip any comments...
755 */
756
757 if ((ptr = strchr(buf, '#')) != NULL)
758 {
f7deaa1a 759 if (ptr > buf && ptr[-1] == '\\')
ef416fc2 760 {
f7deaa1a 761 // Unquote the #...
762 _cups_strcpy(ptr - 1, ptr);
ef416fc2 763 }
f7deaa1a 764 else
765 {
766 // Strip the comment and any trailing whitespace...
767 while (ptr > buf)
768 {
7cf5915e 769 if (!_cups_isspace(ptr[-1]))
f7deaa1a 770 break;
771
772 ptr --;
773 }
ef416fc2 774
f7deaa1a 775 *ptr = '\0';
776 }
ef416fc2 777 }
778
779 /*
780 * Strip leading whitespace...
781 */
782
7cf5915e 783 for (ptr = buf; _cups_isspace(*ptr); ptr ++);
ef416fc2 784
785 if (ptr > buf)
786 _cups_strcpy(buf, ptr);
787
788 /*
789 * See if there is anything left...
790 */
791
792 if (buf[0])
793 {
794 /*
795 * Yes, grab any value and return...
796 */
797
798 for (ptr = buf; *ptr; ptr ++)
7cf5915e 799 if (_cups_isspace(*ptr))
ef416fc2 800 break;
801
802 if (*ptr)
803 {
804 /*
805 * Have a value, skip any other spaces...
806 */
807
7cf5915e 808 while (_cups_isspace(*ptr))
ef416fc2 809 *ptr++ = '\0';
810
811 if (*ptr)
812 *value = ptr;
813
814 /*
815 * Strip trailing whitespace and > for lines that begin with <...
816 */
817
818 ptr += strlen(ptr) - 1;
819
820 if (buf[0] == '<' && *ptr == '>')
821 *ptr-- = '\0';
822 else if (buf[0] == '<' && *ptr != '>')
823 {
824 /*
825 * Syntax error...
826 */
827
828 *value = NULL;
829 return (buf);
830 }
831
7cf5915e 832 while (ptr > *value && _cups_isspace(*ptr))
ef416fc2 833 *ptr-- = '\0';
834 }
835
836 /*
837 * Return the line...
838 */
839
840 return (buf);
841 }
842 }
843
844 return (NULL);
845}
846
847
80ca4592 848/*
849 * 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may
850 * contain binary data.
851 *
5a738aea
MS
852 * This function differs from @link cupsFileGets@ in that the trailing CR
853 * and LF are preserved, as is any binary data on the line. The buffer is
854 * nul-terminated, however you should use the returned length to determine
80ca4592 855 * the number of bytes on the line.
5a738aea 856 *
8072030b 857 * @since CUPS 1.2/macOS 10.5@
80ca4592 858 */
859
5a738aea 860size_t /* O - Number of bytes on line or 0 on end of file */
80ca4592 861cupsFileGetLine(cups_file_t *fp, /* I - File to read from */
862 char *buf, /* I - Buffer */
863 size_t buflen) /* I - Size of buffer */
864{
865 int ch; /* Character from file */
866 char *ptr, /* Current position in line buffer */
867 *end; /* End of line buffer */
868
869
870 /*
871 * Range check input...
872 */
873
807315e6 874 DEBUG_printf(("2cupsFileGetLine(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen));
634763e8 875
80ca4592 876 if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3)
877 return (0);
878
879 /*
880 * Now loop until we have a valid line...
881 */
882
883 for (ptr = buf, end = buf + buflen - 2; ptr < end ;)
884 {
885 if (fp->ptr >= fp->end)
886 if (cups_fill(fp) <= 0)
887 break;
888
889 *ptr++ = ch = *(fp->ptr)++;
634763e8 890 fp->pos ++;
80ca4592 891
892 if (ch == '\r')
893 {
894 /*
895 * Check for CR LF...
896 */
897
898 if (fp->ptr >= fp->end)
899 if (cups_fill(fp) <= 0)
900 break;
901
902 if (*(fp->ptr) == '\n')
634763e8 903 {
80ca4592 904 *ptr++ = *(fp->ptr)++;
634763e8
MS
905 fp->pos ++;
906 }
80ca4592 907
908 break;
909 }
910 else if (ch == '\n')
911 {
912 /*
913 * Line feed ends a line...
914 */
915
916 break;
917 }
918 }
919
920 *ptr = '\0';
921
e07d4801 922 DEBUG_printf(("4cupsFileGetLine: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 923
7e86f2f6 924 return ((size_t)(ptr - buf));
80ca4592 925}
926
927
ef416fc2 928/*
929 * 'cupsFileGets()' - Get a CR and/or LF-terminated line.
5a738aea 930 *
8072030b 931 * @since CUPS 1.2/macOS 10.5@
ef416fc2 932 */
933
5a738aea 934char * /* O - Line read or @code NULL@ on end of file or error */
ef416fc2 935cupsFileGets(cups_file_t *fp, /* I - CUPS file */
936 char *buf, /* O - String buffer */
937 size_t buflen) /* I - Size of string buffer */
938{
939 int ch; /* Character from file */
940 char *ptr, /* Current position in line buffer */
941 *end; /* End of line buffer */
942
943
944 /*
945 * Range check input...
946 */
947
807315e6 948 DEBUG_printf(("2cupsFileGets(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST buflen));
634763e8 949
ef416fc2 950 if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2)
951 return (NULL);
952
953 /*
954 * Now loop until we have a valid line...
955 */
956
957 for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
958 {
959 if (fp->ptr >= fp->end)
960 if (cups_fill(fp) <= 0)
961 {
962 if (ptr == buf)
963 return (NULL);
964 else
965 break;
966 }
967
968 ch = *(fp->ptr)++;
634763e8 969 fp->pos ++;
ef416fc2 970
971 if (ch == '\r')
972 {
973 /*
974 * Check for CR LF...
975 */
976
977 if (fp->ptr >= fp->end)
978 if (cups_fill(fp) <= 0)
979 break;
980
981 if (*(fp->ptr) == '\n')
634763e8
MS
982 {
983 fp->ptr ++;
984 fp->pos ++;
985 }
ef416fc2 986
987 break;
988 }
989 else if (ch == '\n')
990 {
991 /*
992 * Line feed ends a line...
993 */
994
995 break;
996 }
997 else
7e86f2f6 998 *ptr++ = (char)ch;
ef416fc2 999 }
1000
1001 *ptr = '\0';
1002
e07d4801 1003 DEBUG_printf(("4cupsFileGets: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1004
ef416fc2 1005 return (buf);
1006}
1007
1008
1009/*
1010 * 'cupsFileLock()' - Temporarily lock access to a file.
5a738aea 1011 *
8072030b 1012 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1013 */
1014
1015int /* O - 0 on success, -1 on error */
5a738aea 1016cupsFileLock(cups_file_t *fp, /* I - CUPS file */
ef416fc2 1017 int block) /* I - 1 to wait for the lock, 0 to fail right away */
1018{
1019 /*
1020 * Range check...
1021 */
1022
1023 if (!fp || fp->mode == 's')
1024 return (-1);
1025
1026 /*
1027 * Try the lock...
1028 */
1029
19dc16f7 1030#ifdef _WIN32
536bc2c6 1031 return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0));
ef416fc2 1032#else
1033 return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0));
19dc16f7 1034#endif /* _WIN32 */
ef416fc2 1035}
1036
1037
1038/*
1039 * 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file.
5a738aea 1040 *
8072030b 1041 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1042 */
1043
1044int /* O - File descriptor */
1045cupsFileNumber(cups_file_t *fp) /* I - CUPS file */
1046{
5a738aea
MS
1047 if (fp)
1048 return (fp->fd);
1049 else
1050 return (-1);
ef416fc2 1051}
1052
1053
1054/*
1055 * 'cupsFileOpen()' - Open a CUPS file.
5a738aea
MS
1056 *
1057 * The "mode" parameter can be "r" to read, "w" to write, overwriting any
1058 * existing file, "a" to append to an existing file or create a new file,
1059 * or "s" to open a socket connection.
1060 *
634763e8
MS
1061 * When opening for writing ("w"), an optional number from 1 to 9 can be
1062 * supplied which enables Flate compression of the file. Compression is
1063 * not supported for the "a" (append) mode.
5a738aea
MS
1064 *
1065 * When opening a socket connection, the filename is a string of the form
1066 * "address:port" or "hostname:port". The socket will make an IPv4 or IPv6
1067 * connection as needed, generally preferring IPv6 connections when there is
1068 * a choice.
1069 *
8072030b 1070 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1071 */
1072
5a738aea 1073cups_file_t * /* O - CUPS file or @code NULL@ if the file or socket cannot be opened */
ef416fc2 1074cupsFileOpen(const char *filename, /* I - Name of file */
1075 const char *mode) /* I - Open mode */
1076{
1077 cups_file_t *fp; /* New CUPS file */
1078 int fd; /* File descriptor */
1079 char hostname[1024], /* Hostname */
1080 *portname; /* Port "name" (number or service) */
1081 http_addrlist_t *addrlist; /* Host address list */
1082
1083
e07d4801 1084 DEBUG_printf(("cupsFileOpen(filename=\"%s\", mode=\"%s\")", filename,
b423cd4c 1085 mode));
1086
ef416fc2 1087 /*
1088 * Range check input...
1089 */
1090
1091 if (!filename || !mode ||
634763e8
MS
1092 (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
1093 (*mode == 'a' && isdigit(mode[1] & 255)))
ef416fc2 1094 return (NULL);
1095
1096 /*
1097 * Open the file...
1098 */
1099
1100 switch (*mode)
1101 {
1102 case 'a' : /* Append file */
c7017ecc
MS
1103 fd = cups_open(filename,
1104 O_RDWR | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY);
ef416fc2 1105 break;
1106
1107 case 'r' : /* Read file */
b86bc4cf 1108 fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0);
ef416fc2 1109 break;
1110
1111 case 'w' : /* Write file */
c7017ecc
MS
1112 fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
1113 if (fd < 0 && errno == ENOENT)
1114 {
1115 fd = cups_open(filename,
1116 O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY);
1117 if (fd < 0 && errno == EEXIST)
1118 fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY);
1119 }
1120
1121 if (fd >= 0)
19dc16f7 1122#ifdef _WIN32
c7017ecc
MS
1123 _chsize(fd, 0);
1124#else
1125 ftruncate(fd, 0);
19dc16f7 1126#endif /* _WIN32 */
ef416fc2 1127 break;
1128
1129 case 's' : /* Read/write socket */
1130 strlcpy(hostname, filename, sizeof(hostname));
1131 if ((portname = strrchr(hostname, ':')) != NULL)
1132 *portname++ = '\0';
1133 else
1134 return (NULL);
1135
1136 /*
1137 * Lookup the hostname and service...
1138 */
1139
1140 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
1141 return (NULL);
1142
1143 /*
1144 * Connect to the server...
1145 */
1146
1147 if (!httpAddrConnect(addrlist, &fd))
1148 {
1149 httpAddrFreeList(addrlist);
1150 return (NULL);
1151 }
1152
1153 httpAddrFreeList(addrlist);
1154 break;
1155
1156 default : /* Remove bogus compiler warning... */
1157 return (NULL);
1158 }
1159
1160 if (fd < 0)
1161 return (NULL);
1162
1163 /*
1164 * Create the CUPS file structure...
1165 */
1166
1167 if ((fp = cupsFileOpenFd(fd, mode)) == NULL)
1168 {
1169 if (*mode == 's')
87e98392 1170 httpAddrClose(NULL, fd);
ef416fc2 1171 else
1172 close(fd);
1173 }
1174
1175 /*
1176 * Return it...
1177 */
1178
1179 return (fp);
1180}
1181
1182/*
1183 * 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor.
5a738aea 1184 *
634763e8
MS
1185 * The "mode" parameter can be "r" to read, "w" to write, "a" to append,
1186 * or "s" to treat the file descriptor as a bidirectional socket connection.
5a738aea 1187 *
634763e8
MS
1188 * When opening for writing ("w"), an optional number from 1 to 9 can be
1189 * supplied which enables Flate compression of the file. Compression is
1190 * not supported for the "a" (append) mode.
5a738aea 1191 *
8072030b 1192 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1193 */
1194
5a738aea 1195cups_file_t * /* O - CUPS file or @code NULL@ if the file could not be opened */
ef416fc2 1196cupsFileOpenFd(int fd, /* I - File descriptor */
1197 const char *mode) /* I - Open mode */
1198{
1199 cups_file_t *fp; /* New CUPS file */
1200
1201
e07d4801 1202 DEBUG_printf(("cupsFileOpenFd(fd=%d, mode=\"%s\")", fd, mode));
b423cd4c 1203
ef416fc2 1204 /*
1205 * Range check input...
1206 */
1207
1208 if (fd < 0 || !mode ||
634763e8
MS
1209 (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') ||
1210 (*mode == 'a' && isdigit(mode[1] & 255)))
ef416fc2 1211 return (NULL);
1212
1213 /*
1214 * Allocate memory...
1215 */
1216
1217 if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
1218 return (NULL);
1219
1220 /*
1221 * Open the file...
1222 */
1223
1224 fp->fd = fd;
1225
1226 switch (*mode)
1227 {
ef416fc2 1228 case 'a' :
634763e8
MS
1229 fp->pos = lseek(fd, 0, SEEK_END);
1230
1231 case 'w' :
ef416fc2 1232 fp->mode = 'w';
1233 fp->ptr = fp->buf;
1234 fp->end = fp->buf + sizeof(fp->buf);
1235
1236#ifdef HAVE_LIBZ
1237 if (mode[1] >= '1' && mode[1] <= '9')
1238 {
1239 /*
1240 * Open a compressed stream, so write the standard gzip file
1241 * header...
1242 */
1243
1244 unsigned char header[10]; /* gzip file header */
1245 time_t curtime; /* Current time */
1246
1247
1248 curtime = time(NULL);
1249 header[0] = 0x1f;
1250 header[1] = 0x8b;
1251 header[2] = Z_DEFLATED;
1252 header[3] = 0;
7e86f2f6
MS
1253 header[4] = (unsigned char)curtime;
1254 header[5] = (unsigned char)(curtime >> 8);
1255 header[6] = (unsigned char)(curtime >> 16);
1256 header[7] = (unsigned char)(curtime >> 24);
ef416fc2 1257 header[8] = 0;
1258 header[9] = 0x03;
1259
1260 cups_write(fp, (char *)header, 10);
1261
1262 /*
1263 * Initialize the compressor...
1264 */
1265
1266 deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8,
1267 Z_DEFAULT_STRATEGY);
1268
1269 fp->stream.next_out = fp->cbuf;
1270 fp->stream.avail_out = sizeof(fp->cbuf);
1271 fp->compressed = 1;
1272 fp->crc = crc32(0L, Z_NULL, 0);
1273 }
1274#endif /* HAVE_LIBZ */
1275 break;
1276
1277 case 'r' :
1278 fp->mode = 'r';
1279 break;
1280
1281 case 's' :
1282 fp->mode = 's';
1283 break;
1284
1285 default : /* Remove bogus compiler warning... */
1286 return (NULL);
1287 }
1288
1289 /*
1290 * Don't pass this file to child processes...
1291 */
1292
19dc16f7 1293#ifndef _WIN32
ef416fc2 1294 fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC);
19dc16f7 1295#endif /* !_WIN32 */
ef416fc2 1296
1297 return (fp);
1298}
1299
1300
5a855d85
MS
1301/*
1302 * '_cupsFilePeekAhead()' - See if the requested character is buffered up.
1303 */
1304
1305int /* O - 1 if present, 0 otherwise */
1306_cupsFilePeekAhead(cups_file_t *fp, /* I - CUPS file */
1307 int ch) /* I - Character */
1308{
1309 return (fp && fp->ptr && memchr(fp->ptr, ch, (size_t)(fp->end - fp->ptr)));
1310}
1311
1312
ef416fc2 1313/*
1314 * 'cupsFilePeekChar()' - Peek at the next character from a file.
5a738aea 1315 *
8072030b 1316 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1317 */
1318
5a738aea 1319int /* O - Character or -1 on end of file */
ef416fc2 1320cupsFilePeekChar(cups_file_t *fp) /* I - CUPS file */
1321{
1322 /*
1323 * Range check input...
1324 */
1325
1326 if (!fp || (fp->mode != 'r' && fp->mode != 's'))
1327 return (-1);
1328
1329 /*
1330 * If the input buffer is empty, try to read more data...
1331 */
1332
1333 if (fp->ptr >= fp->end)
cbf9404a 1334 if (cups_fill(fp) <= 0)
ef416fc2 1335 return (-1);
1336
1337 /*
1338 * Return the next character in the buffer...
1339 */
1340
1341 return (*(fp->ptr) & 255);
1342}
1343
1344
1345/*
1346 * 'cupsFilePrintf()' - Write a formatted string.
5a738aea 1347 *
8072030b 1348 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1349 */
1350
5a738aea 1351int /* O - Number of bytes written or -1 on error */
ef416fc2 1352cupsFilePrintf(cups_file_t *fp, /* I - CUPS file */
1353 const char *format, /* I - Printf-style format string */
1354 ...) /* I - Additional args as necessary */
1355{
1356 va_list ap; /* Argument list */
2abf387c 1357 ssize_t bytes; /* Formatted size */
ecdc0628 1358
ef416fc2 1359
807315e6 1360 DEBUG_printf(("2cupsFilePrintf(fp=%p, format=\"%s\", ...)", (void *)fp, format));
ef416fc2 1361
1362 if (!fp || !format || (fp->mode != 'w' && fp->mode != 's'))
1363 return (-1);
1364
75bd9771
MS
1365 if (!fp->printf_buffer)
1366 {
1367 /*
1368 * Start with an 1k printf buffer...
1369 */
1370
1371 if ((fp->printf_buffer = malloc(1024)) == NULL)
1372 return (-1);
1373
1374 fp->printf_size = 1024;
1375 }
1376
ef416fc2 1377 va_start(ap, format);
75bd9771 1378 bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
ef416fc2 1379 va_end(ap);
1380
536bc2c6 1381 if (bytes >= (ssize_t)fp->printf_size)
75bd9771
MS
1382 {
1383 /*
1384 * Expand the printf buffer...
1385 */
1386
1387 char *temp; /* Temporary buffer pointer */
1388
1389
1390 if (bytes > 65535)
1391 return (-1);
1392
7e86f2f6 1393 if ((temp = realloc(fp->printf_buffer, (size_t)(bytes + 1))) == NULL)
75bd9771
MS
1394 return (-1);
1395
1396 fp->printf_buffer = temp;
7e86f2f6 1397 fp->printf_size = (size_t)(bytes + 1);
75bd9771
MS
1398
1399 va_start(ap, format);
1400 bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
1401 va_end(ap);
1402 }
ecdc0628 1403
ef416fc2 1404 if (fp->mode == 's')
634763e8 1405 {
7e86f2f6 1406 if (cups_write(fp, fp->printf_buffer, (size_t)bytes) < 0)
634763e8
MS
1407 return (-1);
1408
1409 fp->pos += bytes;
1410
e07d4801 1411 DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1412
7e86f2f6 1413 return ((int)bytes);
634763e8 1414 }
ef416fc2 1415
1416 if ((fp->ptr + bytes) > fp->end)
1417 if (cupsFileFlush(fp))
1418 return (-1);
1419
1420 fp->pos += bytes;
1421
e07d4801 1422 DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1423
7e86f2f6 1424 if ((size_t)bytes > sizeof(fp->buf))
ef416fc2 1425 {
1426#ifdef HAVE_LIBZ
1427 if (fp->compressed)
7e86f2f6 1428 return ((int)cups_compress(fp, fp->printf_buffer, (size_t)bytes));
ef416fc2 1429 else
1430#endif /* HAVE_LIBZ */
7e86f2f6 1431 return ((int)cups_write(fp, fp->printf_buffer, (size_t)bytes));
ef416fc2 1432 }
1433 else
1434 {
07623986 1435 memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
ef416fc2 1436 fp->ptr += bytes;
35fc2243
MS
1437
1438 if (fp->is_stdio && cupsFileFlush(fp))
1439 return (-1);
1440 else
1441 return ((int)bytes);
ef416fc2 1442 }
1443}
1444
1445
1446/*
1447 * 'cupsFilePutChar()' - Write a character.
5a738aea 1448 *
8072030b 1449 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1450 */
1451
1452int /* O - 0 on success, -1 on error */
1453cupsFilePutChar(cups_file_t *fp, /* I - CUPS file */
1454 int c) /* I - Character to write */
1455{
1456 /*
1457 * Range check input...
1458 */
1459
1460 if (!fp || (fp->mode != 'w' && fp->mode != 's'))
1461 return (-1);
1462
1463 if (fp->mode == 's')
1464 {
1465 /*
1466 * Send character immediately over socket...
1467 */
1468
1469 char ch; /* Output character */
1470
1471
7e86f2f6 1472 ch = (char)c;
ef416fc2 1473
1474 if (send(fp->fd, &ch, 1, 0) < 1)
1475 return (-1);
1476 }
1477 else
1478 {
1479 /*
1480 * Buffer it up...
1481 */
1482
1483 if (fp->ptr >= fp->end)
1484 if (cupsFileFlush(fp))
1485 return (-1);
1486
7e86f2f6 1487 *(fp->ptr) ++ = (char)c;
ef416fc2 1488 }
1489
1490 fp->pos ++;
1491
e07d4801 1492 DEBUG_printf(("4cupsFilePutChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1493
ef416fc2 1494 return (0);
1495}
1496
1497
58dc1933
MS
1498/*
1499 * 'cupsFilePutConf()' - Write a configuration line.
1500 *
1501 * This function handles any comment escaping of the value.
1502 *
8072030b 1503 * @since CUPS 1.4/macOS 10.6@
58dc1933
MS
1504 */
1505
1506ssize_t /* O - Number of bytes written or -1 on error */
1507cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */
1508 const char *directive, /* I - Directive */
1509 const char *value) /* I - Value */
1510{
1511 ssize_t bytes, /* Number of bytes written */
1512 temp; /* Temporary byte count */
1513 const char *ptr; /* Pointer into value */
1514
1515
1516 if (!fp || !directive || !*directive)
1517 return (-1);
1518
1519 if ((bytes = cupsFilePuts(fp, directive)) < 0)
1520 return (-1);
1521
1522 if (cupsFilePutChar(fp, ' ') < 0)
1523 return (-1);
1524 bytes ++;
1525
1526 if (value && *value)
1527 {
1528 if ((ptr = strchr(value, '#')) != NULL)
1529 {
1530 /*
1531 * Need to quote the first # in the info string...
1532 */
1533
7e86f2f6 1534 if ((temp = cupsFileWrite(fp, value, (size_t)(ptr - value))) < 0)
58dc1933
MS
1535 return (-1);
1536 bytes += temp;
1537
1538 if (cupsFilePutChar(fp, '\\') < 0)
1539 return (-1);
1540 bytes ++;
1541
1542 if ((temp = cupsFilePuts(fp, ptr)) < 0)
1543 return (-1);
1544 bytes += temp;
1545 }
1546 else if ((temp = cupsFilePuts(fp, value)) < 0)
1547 return (-1);
1548 else
1549 bytes += temp;
1550 }
1551
1552 if (cupsFilePutChar(fp, '\n') < 0)
1553 return (-1);
1554 else
1555 return (bytes + 1);
1556}
1557
1558
ef416fc2 1559/*
1560 * 'cupsFilePuts()' - Write a string.
5a738aea
MS
1561 *
1562 * Like the @code fputs@ function, no newline is appended to the string.
1563 *
8072030b 1564 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1565 */
1566
5a738aea 1567int /* O - Number of bytes written or -1 on error */
ef416fc2 1568cupsFilePuts(cups_file_t *fp, /* I - CUPS file */
1569 const char *s) /* I - String to write */
1570{
2abf387c 1571 ssize_t bytes; /* Bytes to write */
ef416fc2 1572
1573
1574 /*
1575 * Range check input...
1576 */
1577
1578 if (!fp || !s || (fp->mode != 'w' && fp->mode != 's'))
1579 return (-1);
1580
1581 /*
1582 * Write the string...
1583 */
1584
7e86f2f6 1585 bytes = (ssize_t)strlen(s);
ef416fc2 1586
1587 if (fp->mode == 's')
1588 {
7e86f2f6 1589 if (cups_write(fp, s, (size_t)bytes) < 0)
ef416fc2 1590 return (-1);
1591
1592 fp->pos += bytes;
1593
e07d4801 1594 DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1595
7e86f2f6 1596 return ((int)bytes);
ef416fc2 1597 }
1598
1599 if ((fp->ptr + bytes) > fp->end)
1600 if (cupsFileFlush(fp))
1601 return (-1);
1602
1603 fp->pos += bytes;
1604
e07d4801 1605 DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1606
7e86f2f6 1607 if ((size_t)bytes > sizeof(fp->buf))
ef416fc2 1608 {
1609#ifdef HAVE_LIBZ
1610 if (fp->compressed)
7e86f2f6 1611 return ((int)cups_compress(fp, s, (size_t)bytes));
ef416fc2 1612 else
1613#endif /* HAVE_LIBZ */
7e86f2f6 1614 return ((int)cups_write(fp, s, (size_t)bytes));
ef416fc2 1615 }
1616 else
1617 {
07623986 1618 memcpy(fp->ptr, s, (size_t)bytes);
ef416fc2 1619 fp->ptr += bytes;
35fc2243
MS
1620
1621 if (fp->is_stdio && cupsFileFlush(fp))
1622 return (-1);
1623 else
1624 return ((int)bytes);
ef416fc2 1625 }
1626}
1627
1628
1629/*
1630 * 'cupsFileRead()' - Read from a file.
5a738aea 1631 *
8072030b 1632 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1633 */
1634
5a738aea 1635ssize_t /* O - Number of bytes read or -1 on error */
ef416fc2 1636cupsFileRead(cups_file_t *fp, /* I - CUPS file */
1637 char *buf, /* O - Buffer */
1638 size_t bytes) /* I - Number of bytes to read */
1639{
2abf387c 1640 size_t total; /* Total bytes read */
1641 ssize_t count; /* Bytes read */
ef416fc2 1642
1643
807315e6 1644 DEBUG_printf(("2cupsFileRead(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
ef416fc2 1645
1646 /*
1647 * Range check input...
1648 */
1649
82f97232 1650 if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's'))
ef416fc2 1651 return (-1);
1652
1653 if (bytes == 0)
1654 return (0);
1655
96fee303
MS
1656 if (fp->eof)
1657 {
1658 DEBUG_puts("5cupsFileRead: End-of-file!");
1659 return (-1);
1660 }
1661
ef416fc2 1662 /*
1663 * Loop until all bytes are read...
1664 */
1665
1666 total = 0;
1667 while (bytes > 0)
1668 {
1669 if (fp->ptr >= fp->end)
1670 if (cups_fill(fp) <= 0)
1671 {
e07d4801
MS
1672 DEBUG_printf(("4cupsFileRead: cups_fill() returned -1, total="
1673 CUPS_LLFMT, CUPS_LLCAST total));
ef416fc2 1674
1675 if (total > 0)
b86bc4cf 1676 return ((ssize_t)total);
ef416fc2 1677 else
1678 return (-1);
1679 }
1680
b86bc4cf 1681 count = (ssize_t)(fp->end - fp->ptr);
1682 if (count > (ssize_t)bytes)
1683 count = (ssize_t)bytes;
ef416fc2 1684
07623986 1685 memcpy(buf, fp->ptr,(size_t) count);
ef416fc2 1686 fp->ptr += count;
634763e8
MS
1687 fp->pos += count;
1688
e07d4801 1689 DEBUG_printf(("4cupsFileRead: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
ef416fc2 1690
1691 /*
1692 * Update the counts for the last read...
1693 */
1694
7e86f2f6
MS
1695 bytes -= (size_t)count;
1696 total += (size_t)count;
ef416fc2 1697 buf += count;
1698 }
1699
1700 /*
1701 * Return the total number of bytes read...
1702 */
1703
e07d4801 1704 DEBUG_printf(("3cupsFileRead: total=" CUPS_LLFMT, CUPS_LLCAST total));
ef416fc2 1705
b86bc4cf 1706 return ((ssize_t)total);
ef416fc2 1707}
1708
1709
1710/*
5a738aea
MS
1711 * 'cupsFileRewind()' - Set the current file position to the beginning of the
1712 * file.
1713 *
8072030b 1714 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1715 */
1716
5a738aea 1717off_t /* O - New file position or -1 on error */
ef416fc2 1718cupsFileRewind(cups_file_t *fp) /* I - CUPS file */
1719{
80ca4592 1720 /*
1721 * Range check input...
1722 */
1723
807315e6 1724 DEBUG_printf(("cupsFileRewind(fp=%p)", (void *)fp));
e07d4801 1725 DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1726
80ca4592 1727 if (!fp || fp->mode != 'r')
1728 return (-1);
1729
1730 /*
1731 * Handle special cases...
1732 */
1733
634763e8 1734 if (fp->bufpos == 0)
80ca4592 1735 {
1736 /*
1737 * No seeking necessary...
1738 */
1739
634763e8
MS
1740 fp->pos = 0;
1741
80ca4592 1742 if (fp->ptr)
1743 {
1744 fp->ptr = fp->buf;
1745 fp->eof = 0;
1746 }
1747
e07d4801 1748 DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 1749
80ca4592 1750 return (0);
1751 }
1752
1753 /*
1754 * Otherwise, seek in the file and cleanup any compression buffers...
1755 */
1756
1757#ifdef HAVE_LIBZ
1758 if (fp->compressed)
1759 {
1760 inflateEnd(&fp->stream);
1761 fp->compressed = 0;
1762 }
1763#endif /* HAVE_LIBZ */
1764
c9fc04c6
MS
1765 if (lseek(fp->fd, 0, SEEK_SET))
1766 {
e07d4801 1767 DEBUG_printf(("1cupsFileRewind: lseek failed: %s", strerror(errno)));
c9fc04c6
MS
1768 return (-1);
1769 }
80ca4592 1770
634763e8
MS
1771 fp->bufpos = 0;
1772 fp->pos = 0;
1773 fp->ptr = NULL;
1774 fp->end = NULL;
1775 fp->eof = 0;
1776
e07d4801 1777 DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
80ca4592 1778
1779 return (0);
ef416fc2 1780}
1781
1782
1783/*
1784 * 'cupsFileSeek()' - Seek in a file.
5a738aea 1785 *
8072030b 1786 * @since CUPS 1.2/macOS 10.5@
ef416fc2 1787 */
1788
5a738aea 1789off_t /* O - New file position or -1 on error */
ef416fc2 1790cupsFileSeek(cups_file_t *fp, /* I - CUPS file */
1791 off_t pos) /* I - Position in file */
1792{
2abf387c 1793 ssize_t bytes; /* Number bytes in buffer */
ef416fc2 1794
1795
807315e6 1796 DEBUG_printf(("cupsFileSeek(fp=%p, pos=" CUPS_LLFMT ")", (void *)fp, CUPS_LLCAST pos));
e07d4801 1797 DEBUG_printf(("2cupsFileSeek: fp->pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
807315e6 1798 DEBUG_printf(("2cupsFileSeek: fp->ptr=%p, fp->end=%p", (void *)fp->ptr, (void *)fp->end));
ef416fc2 1799
1800 /*
1801 * Range check input...
1802 */
1803
1804 if (!fp || pos < 0 || fp->mode != 'r')
1805 return (-1);
1806
80ca4592 1807 /*
1808 * Handle special cases...
1809 */
1810
1811 if (pos == 0)
1812 return (cupsFileRewind(fp));
1813
634763e8 1814 if (fp->ptr)
b423cd4c 1815 {
634763e8 1816 bytes = (ssize_t)(fp->end - fp->buf);
b423cd4c 1817
e07d4801 1818 DEBUG_printf(("2cupsFileSeek: bytes=" CUPS_LLFMT, CUPS_LLCAST bytes));
c168a833 1819
634763e8 1820 if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
b423cd4c 1821 {
634763e8
MS
1822 /*
1823 * No seeking necessary...
1824 */
1825
1826 fp->pos = pos;
1827 fp->ptr = fp->buf + pos - fp->bufpos;
b423cd4c 1828 fp->eof = 0;
b423cd4c 1829
634763e8
MS
1830 return (pos);
1831 }
b423cd4c 1832 }
1833
80ca4592 1834#ifdef HAVE_LIBZ
1835 if (!fp->compressed && !fp->ptr)
1836 {
1837 /*
1838 * Preload a buffer to determine whether the file is compressed...
1839 */
1840
cbf9404a 1841 if (cups_fill(fp) <= 0)
80ca4592 1842 return (-1);
1843 }
1844#endif /* HAVE_LIBZ */
1845
ef416fc2 1846 /*
634763e8 1847 * Seek forwards or backwards...
ef416fc2 1848 */
1849
ef416fc2 1850 fp->eof = 0;
1851
634763e8 1852 if (pos < fp->bufpos)
ef416fc2 1853 {
1854 /*
1855 * Need to seek backwards...
1856 */
1857
e07d4801 1858 DEBUG_puts("2cupsFileSeek: SEEK BACKWARDS");
80ca4592 1859
ef416fc2 1860#ifdef HAVE_LIBZ
1861 if (fp->compressed)
1862 {
1863 inflateEnd(&fp->stream);
1864
1865 lseek(fp->fd, 0, SEEK_SET);
634763e8
MS
1866 fp->bufpos = 0;
1867 fp->pos = 0;
1868 fp->ptr = NULL;
1869 fp->end = NULL;
ef416fc2 1870
1871 while ((bytes = cups_fill(fp)) > 0)
634763e8 1872 if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
ef416fc2 1873 break;
1874
1875 if (bytes <= 0)
1876 return (-1);
80ca4592 1877
634763e8
MS
1878 fp->ptr = fp->buf + pos - fp->bufpos;
1879 fp->pos = pos;
ef416fc2 1880 }
1881 else
1882#endif /* HAVE_LIBZ */
1883 {
634763e8
MS
1884 fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1885 fp->pos = fp->bufpos;
1886 fp->ptr = NULL;
1887 fp->end = NULL;
80ca4592 1888
e07d4801 1889 DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
634763e8 1890 CUPS_LLCAST fp->pos));
ef416fc2 1891 }
1892 }
634763e8 1893 else
ef416fc2 1894 {
1895 /*
1896 * Need to seek forwards...
1897 */
1898
e07d4801 1899 DEBUG_puts("2cupsFileSeek: SEEK FORWARDS");
80ca4592 1900
ef416fc2 1901#ifdef HAVE_LIBZ
80ca4592 1902 if (fp->compressed)
ef416fc2 1903 {
1904 while ((bytes = cups_fill(fp)) > 0)
80ca4592 1905 {
634763e8 1906 if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
ef416fc2 1907 break;
80ca4592 1908 }
ef416fc2 1909
1910 if (bytes <= 0)
1911 return (-1);
80ca4592 1912
634763e8
MS
1913 fp->ptr = fp->buf + pos - fp->bufpos;
1914 fp->pos = pos;
ef416fc2 1915 }
1916 else
1917#endif /* HAVE_LIBZ */
1918 {
634763e8
MS
1919 fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1920 fp->pos = fp->bufpos;
1921 fp->ptr = NULL;
1922 fp->end = NULL;
80ca4592 1923
e07d4801 1924 DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT,
634763e8 1925 CUPS_LLCAST fp->pos));
ef416fc2 1926 }
1927 }
ef416fc2 1928
e07d4801 1929 DEBUG_printf(("2cupsFileSeek: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
ef416fc2 1930
1931 return (fp->pos);
1932}
1933
1934
80ca4592 1935/*
1936 * 'cupsFileStderr()' - Return a CUPS file associated with stderr.
5a738aea 1937 *
8072030b 1938 * @since CUPS 1.2/macOS 10.5@
80ca4592 1939 */
1940
5a738aea 1941cups_file_t * /* O - CUPS file */
80ca4592 1942cupsFileStderr(void)
1943{
1944 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
1945
1946
1947 /*
1948 * Open file descriptor 2 as needed...
1949 */
1950
1951 if (!cg->stdio_files[2])
1952 {
1953 /*
1954 * Flush any pending output on the stdio file...
1955 */
1956
1957 fflush(stderr);
1958
1959 /*
1960 * Open file descriptor 2...
1961 */
1962
1963 if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL)
1964 cg->stdio_files[2]->is_stdio = 1;
1965 }
1966
1967 return (cg->stdio_files[2]);
1968}
1969
1970
1971/*
1972 * 'cupsFileStdin()' - Return a CUPS file associated with stdin.
5a738aea 1973 *
8072030b 1974 * @since CUPS 1.2/macOS 10.5@
80ca4592 1975 */
1976
5a738aea 1977cups_file_t * /* O - CUPS file */
80ca4592 1978cupsFileStdin(void)
1979{
1980 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
1981
1982
1983 /*
1984 * Open file descriptor 0 as needed...
1985 */
1986
1987 if (!cg->stdio_files[0])
1988 {
1989 /*
1990 * Open file descriptor 0...
1991 */
1992
1993 if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL)
1994 cg->stdio_files[0]->is_stdio = 1;
1995 }
1996
1997 return (cg->stdio_files[0]);
1998}
1999
2000
2001/*
2002 * 'cupsFileStdout()' - Return a CUPS file associated with stdout.
5a738aea 2003 *
8072030b 2004 * @since CUPS 1.2/macOS 10.5@
80ca4592 2005 */
2006
5a738aea 2007cups_file_t * /* O - CUPS file */
80ca4592 2008cupsFileStdout(void)
2009{
2010 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */
2011
2012
2013 /*
2014 * Open file descriptor 1 as needed...
2015 */
2016
2017 if (!cg->stdio_files[1])
2018 {
2019 /*
2020 * Flush any pending output on the stdio file...
2021 */
2022
2023 fflush(stdout);
2024
2025 /*
2026 * Open file descriptor 1...
2027 */
2028
2029 if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL)
2030 cg->stdio_files[1]->is_stdio = 1;
2031 }
2032
2033 return (cg->stdio_files[1]);
2034}
2035
2036
ef416fc2 2037/*
2038 * 'cupsFileTell()' - Return the current file position.
5a738aea 2039 *
8072030b 2040 * @since CUPS 1.2/macOS 10.5@
ef416fc2 2041 */
2042
2043off_t /* O - File position */
2044cupsFileTell(cups_file_t *fp) /* I - CUPS file */
2045{
807315e6
MS
2046 DEBUG_printf(("2cupsFileTell(fp=%p)", (void *)fp));
2047 DEBUG_printf(("3cupsFileTell: pos=" CUPS_LLFMT, CUPS_LLCAST (fp ? fp->pos : -1)));
634763e8 2048
80ca4592 2049 return (fp ? fp->pos : 0);
ef416fc2 2050}
2051
2052
2053/*
2054 * 'cupsFileUnlock()' - Unlock access to a file.
5a738aea 2055 *
8072030b 2056 * @since CUPS 1.2/macOS 10.5@
ef416fc2 2057 */
2058
2059int /* O - 0 on success, -1 on error */
5a738aea 2060cupsFileUnlock(cups_file_t *fp) /* I - CUPS file */
ef416fc2 2061{
2062 /*
2063 * Range check...
2064 */
2065
807315e6 2066 DEBUG_printf(("cupsFileUnlock(fp=%p)", (void *)fp));
634763e8 2067
ef416fc2 2068 if (!fp || fp->mode == 's')
2069 return (-1);
2070
2071 /*
2072 * Unlock...
2073 */
2074
19dc16f7 2075#ifdef _WIN32
536bc2c6 2076 return (_locking(fp->fd, _LK_UNLCK, 0));
ef416fc2 2077#else
2078 return (lockf(fp->fd, F_ULOCK, 0));
19dc16f7 2079#endif /* _WIN32 */
ef416fc2 2080}
2081
2082
2083/*
2084 * 'cupsFileWrite()' - Write to a file.
5a738aea 2085 *
8072030b 2086 * @since CUPS 1.2/macOS 10.5@
ef416fc2 2087 */
2088
5a738aea 2089ssize_t /* O - Number of bytes written or -1 on error */
ef416fc2 2090cupsFileWrite(cups_file_t *fp, /* I - CUPS file */
2091 const char *buf, /* I - Buffer */
2092 size_t bytes) /* I - Number of bytes to write */
2093{
2094 /*
2095 * Range check input...
2096 */
2097
807315e6 2098 DEBUG_printf(("2cupsFileWrite(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
634763e8 2099
82f97232 2100 if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's'))
ef416fc2 2101 return (-1);
2102
2103 if (bytes == 0)
2104 return (0);
2105
2106 /*
2107 * Write the buffer...
2108 */
2109
2110 if (fp->mode == 's')
2111 {
2112 if (cups_write(fp, buf, bytes) < 0)
2113 return (-1);
2114
b86bc4cf 2115 fp->pos += (off_t)bytes;
ef416fc2 2116
e07d4801 2117 DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 2118
b86bc4cf 2119 return ((ssize_t)bytes);
ef416fc2 2120 }
2121
2122 if ((fp->ptr + bytes) > fp->end)
2123 if (cupsFileFlush(fp))
2124 return (-1);
2125
b86bc4cf 2126 fp->pos += (off_t)bytes;
ef416fc2 2127
e07d4801 2128 DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos));
634763e8 2129
ef416fc2 2130 if (bytes > sizeof(fp->buf))
2131 {
2132#ifdef HAVE_LIBZ
2133 if (fp->compressed)
2134 return (cups_compress(fp, buf, bytes));
2135 else
2136#endif /* HAVE_LIBZ */
2137 return (cups_write(fp, buf, bytes));
2138 }
2139 else
2140 {
2141 memcpy(fp->ptr, buf, bytes);
2142 fp->ptr += bytes;
b86bc4cf 2143 return ((ssize_t)bytes);
ef416fc2 2144 }
2145}
2146
2147
2148#ifdef HAVE_LIBZ
2149/*
22c9029b 2150 * 'cups_compress()' - Compress a buffer of data.
ef416fc2 2151 */
2152
2153static ssize_t /* O - Number of bytes written or -1 */
2154cups_compress(cups_file_t *fp, /* I - CUPS file */
2155 const char *buf, /* I - Buffer */
2156 size_t bytes) /* I - Number bytes */
2157{
807315e6 2158 DEBUG_printf(("7cups_compress(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
ecdc0628 2159
ef416fc2 2160 /*
2161 * Update the CRC...
2162 */
2163
7e86f2f6 2164 fp->crc = crc32(fp->crc, (const Bytef *)buf, (uInt)bytes);
ef416fc2 2165
2166 /*
2167 * Deflate the bytes...
2168 */
2169
2170 fp->stream.next_in = (Bytef *)buf;
7e86f2f6 2171 fp->stream.avail_in = (uInt)bytes;
ef416fc2 2172
2173 while (fp->stream.avail_in > 0)
2174 {
2175 /*
2176 * Flush the current buffer...
2177 */
2178
e07d4801 2179 DEBUG_printf(("9cups_compress: avail_in=%d, avail_out=%d",
634763e8 2180 fp->stream.avail_in, fp->stream.avail_out));
ecdc0628 2181
7e86f2f6 2182 if (fp->stream.avail_out < (uInt)(sizeof(fp->cbuf) / 8))
ef416fc2 2183 {
7e86f2f6 2184 if (cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf)) < 0)
ef416fc2 2185 return (-1);
ecdc0628 2186
2187 fp->stream.next_out = fp->cbuf;
2188 fp->stream.avail_out = sizeof(fp->cbuf);
ef416fc2 2189 }
2190
2191 deflate(&(fp->stream), Z_NO_FLUSH);
2192 }
2193
7e86f2f6 2194 return ((ssize_t)bytes);
ef416fc2 2195}
2196#endif /* HAVE_LIBZ */
2197
2198
2199/*
22c9029b 2200 * 'cups_fill()' - Fill the input buffer.
ef416fc2 2201 */
2202
2203static ssize_t /* O - Number of bytes or -1 */
2204cups_fill(cups_file_t *fp) /* I - CUPS file */
2205{
2206 ssize_t bytes; /* Number of bytes read */
2207#ifdef HAVE_LIBZ
c277e2f8 2208 int status; /* Decompression status */
ef416fc2 2209 const unsigned char *ptr, /* Pointer into buffer */
2210 *end; /* End of buffer */
2211#endif /* HAVE_LIBZ */
2212
2213
807315e6
MS
2214 DEBUG_printf(("7cups_fill(fp=%p)", (void *)fp));
2215 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 2216
2217 if (fp->ptr && fp->end)
c9fc04c6 2218 fp->bufpos += fp->end - fp->buf;
ef416fc2 2219
2220#ifdef HAVE_LIBZ
e07d4801 2221 DEBUG_printf(("9cups_fill: fp->compressed=%d", fp->compressed));
b423cd4c 2222
ef416fc2 2223 while (!fp->ptr || fp->compressed)
2224 {
2225 /*
2226 * Check to see if we have read any data yet; if not, see if we have a
2227 * compressed file...
2228 */
2229
2230 if (!fp->ptr)
2231 {
2232 /*
2233 * Reset the file position in case we are seeking...
2234 */
2235
2236 fp->compressed = 0;
ef416fc2 2237
2238 /*
2239 * Read the first bytes in the file to determine if we have a gzip'd
2240 * file...
2241 */
2242
fa73b229 2243 if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0)
ef416fc2 2244 {
2245 /*
2246 * Can't read from file!
2247 */
2248
e07d4801 2249 DEBUG_printf(("9cups_fill: cups_read() returned " CUPS_LLFMT,
b423cd4c 2250 CUPS_LLCAST bytes));
2251
cbf9404a
MS
2252 fp->eof = 1;
2253
ef416fc2 2254 return (-1);
2255 }
2256
fa73b229 2257 if (bytes < 10 || fp->buf[0] != 0x1f ||
e00b005a 2258 (fp->buf[1] & 255) != 0x8b ||
fa73b229 2259 fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
ef416fc2 2260 {
2261 /*
2262 * Not a gzip'd file!
2263 */
2264
ef416fc2 2265 fp->ptr = fp->buf;
2266 fp->end = fp->buf + bytes;
2267
e07d4801 2268 DEBUG_printf(("9cups_fill: Returning " CUPS_LLFMT,
c9fc04c6 2269 CUPS_LLCAST bytes));
b423cd4c 2270
ef416fc2 2271 return (bytes);
2272 }
2273
2274 /*
2275 * Parse header junk: extra data, original name, and comment...
2276 */
2277
fa73b229 2278 ptr = (unsigned char *)fp->buf + 10;
2279 end = (unsigned char *)fp->buf + bytes;
ef416fc2 2280
fa73b229 2281 if (fp->buf[3] & 0x04)
ef416fc2 2282 {
2283 /*
2284 * Skip extra data...
2285 */
2286
2287 if ((ptr + 2) > end)
2288 {
2289 /*
2290 * Can't read from file!
2291 */
2292
cbf9404a
MS
2293 DEBUG_puts("9cups_fill: Extra gzip header data missing, returning -1.");
2294
2295 fp->eof = 1;
2296 errno = EIO;
2297
ef416fc2 2298 return (-1);
2299 }
2300
2301 bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
2302 ptr += 2 + bytes;
2303
2304 if (ptr > end)
2305 {
2306 /*
2307 * Can't read from file!
2308 */
2309
cbf9404a
MS
2310 DEBUG_puts("9cups_fill: Extra gzip header data does not fit in initial buffer, returning -1.");
2311
2312 fp->eof = 1;
2313 errno = EIO;
2314
ef416fc2 2315 return (-1);
2316 }
2317 }
2318
fa73b229 2319 if (fp->buf[3] & 0x08)
ef416fc2 2320 {
2321 /*
2322 * Skip original name data...
2323 */
2324
2325 while (ptr < end && *ptr)
2326 ptr ++;
2327
2328 if (ptr < end)
2329 ptr ++;
2330 else
2331 {
2332 /*
2333 * Can't read from file!
2334 */
2335
cbf9404a
MS
2336 DEBUG_puts("9cups_fill: Original filename in gzip header data does not fit in initial buffer, returning -1.");
2337
2338 fp->eof = 1;
2339 errno = EIO;
2340
ef416fc2 2341 return (-1);
2342 }
2343 }
2344
fa73b229 2345 if (fp->buf[3] & 0x10)
ef416fc2 2346 {
2347 /*
2348 * Skip comment data...
2349 */
2350
2351 while (ptr < end && *ptr)
2352 ptr ++;
2353
2354 if (ptr < end)
2355 ptr ++;
2356 else
2357 {
2358 /*
2359 * Can't read from file!
2360 */
2361
cbf9404a
MS
2362 DEBUG_puts("9cups_fill: Comment in gzip header data does not fit in initial buffer, returning -1.");
2363
2364 fp->eof = 1;
2365 errno = EIO;
2366
ef416fc2 2367 return (-1);
2368 }
2369 }
2370
fa73b229 2371 if (fp->buf[3] & 0x02)
ef416fc2 2372 {
2373 /*
2374 * Skip header CRC data...
2375 */
2376
2377 ptr += 2;
2378
2379 if (ptr > end)
2380 {
2381 /*
2382 * Can't read from file!
2383 */
2384
cbf9404a
MS
2385 DEBUG_puts("9cups_fill: Header CRC in gzip header data does not fit in initial buffer, returning -1.");
2386
2387 fp->eof = 1;
2388 errno = EIO;
2389
ef416fc2 2390 return (-1);
2391 }
2392 }
2393
fa73b229 2394 /*
2395 * Copy the flate-compressed data to the compression buffer...
2396 */
2397
2398 if ((bytes = end - ptr) > 0)
07623986 2399 memcpy(fp->cbuf, ptr, (size_t)bytes);
fa73b229 2400
ef416fc2 2401 /*
2402 * Setup the decompressor data...
2403 */
2404
2405 fp->stream.zalloc = (alloc_func)0;
2406 fp->stream.zfree = (free_func)0;
2407 fp->stream.opaque = (voidpf)0;
fa73b229 2408 fp->stream.next_in = (Bytef *)fp->cbuf;
ef416fc2 2409 fp->stream.next_out = NULL;
7e86f2f6 2410 fp->stream.avail_in = (uInt)bytes;
ef416fc2 2411 fp->stream.avail_out = 0;
2412 fp->crc = crc32(0L, Z_NULL, 0);
2413
cbf9404a
MS
2414 if ((status = inflateInit2(&(fp->stream), -15)) != Z_OK)
2415 {
2416 DEBUG_printf(("9cups_fill: inflateInit2 returned %d, returning -1.", status));
2417
2418 fp->eof = 1;
2419 errno = EIO;
2420
ef416fc2 2421 return (-1);
cbf9404a 2422 }
ef416fc2 2423
2424 fp->compressed = 1;
2425 }
2426
2427 if (fp->compressed)
2428 {
2429 /*
2430 * If we have reached end-of-file, return immediately...
2431 */
2432
2433 if (fp->eof)
cbf9404a
MS
2434 {
2435 DEBUG_puts("9cups_fill: EOF, returning 0.");
2436
2437 return (0);
2438 }
ef416fc2 2439
2440 /*
2441 * Fill the decompression buffer as needed...
2442 */
2443
2444 if (fp->stream.avail_in == 0)
2445 {
2446 if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
cbf9404a
MS
2447 {
2448 DEBUG_printf(("9cups_fill: cups_read error, returning %d.", (int)bytes));
2449
2450 fp->eof = 1;
2451
2452 return (bytes);
2453 }
ef416fc2 2454
2455 fp->stream.next_in = fp->cbuf;
7e86f2f6 2456 fp->stream.avail_in = (uInt)bytes;
ef416fc2 2457 }
2458
2459 /*
2460 * Decompress data from the buffer...
2461 */
2462
2463 fp->stream.next_out = (Bytef *)fp->buf;
2464 fp->stream.avail_out = sizeof(fp->buf);
2465
c277e2f8
MS
2466 status = inflate(&(fp->stream), Z_NO_FLUSH);
2467
2468 if (fp->stream.next_out > (Bytef *)fp->buf)
2469 fp->crc = crc32(fp->crc, (Bytef *)fp->buf,
7e86f2f6 2470 (uInt)(fp->stream.next_out - (Bytef *)fp->buf));
c277e2f8
MS
2471
2472 if (status == Z_STREAM_END)
ef416fc2 2473 {
2474 /*
2475 * Read the CRC and length...
2476 */
2477
2478 unsigned char trailer[8]; /* Trailer bytes */
2479 uLong tcrc; /* Trailer CRC */
cbf9404a 2480 ssize_t tbytes = 0; /* Number of bytes */
ef416fc2 2481
cbf9404a 2482 if (fp->stream.avail_in > 0)
ef416fc2 2483 {
cbf9404a
MS
2484 if (fp->stream.avail_in > sizeof(trailer))
2485 tbytes = (ssize_t)sizeof(trailer);
2486 else
2487 tbytes = (ssize_t)fp->stream.avail_in;
ef416fc2 2488
b2250eaa 2489 memcpy(trailer, fp->stream.next_in, (size_t)tbytes);
cbf9404a
MS
2490 fp->stream.next_in += tbytes;
2491 fp->stream.avail_in -= (size_t)tbytes;
ef416fc2 2492 }
2493
cbf9404a
MS
2494 if (tbytes < (ssize_t)sizeof(trailer))
2495 {
2496 if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes))
fa73b229 2497 {
2498 /*
cbf9404a 2499 * Can't get it, so mark end-of-file...
fa73b229 2500 */
2501
cbf9404a 2502 DEBUG_puts("9cups_fill: Unable to read gzip CRC trailer, returning -1.");
c277e2f8 2503
fa73b229 2504 fp->eof = 1;
cbf9404a 2505 errno = EIO;
fa73b229 2506
2507 return (-1);
2508 }
cbf9404a
MS
2509 }
2510
2511 tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) |
2512 (uLong)trailer[1]) << 8) | (uLong)trailer[0];
ef416fc2 2513
cbf9404a
MS
2514 if (tcrc != fp->crc)
2515 {
ef416fc2 2516 /*
cbf9404a 2517 * Bad CRC, mark end-of-file...
ef416fc2 2518 */
ef416fc2 2519
cbf9404a
MS
2520 DEBUG_printf(("9cups_fill: tcrc=%08x != fp->crc=%08x, returning -1.", (unsigned int)tcrc, (unsigned int)fp->crc));
2521
2522 fp->eof = 1;
2523 errno = EIO;
2524
2525 return (-1);
ef416fc2 2526 }
cbf9404a
MS
2527
2528 /*
2529 * Otherwise, reset the compressed flag so that we re-read the
2530 * file header...
2531 */
2532
b908d72c
MS
2533 inflateEnd(&fp->stream);
2534
cbf9404a
MS
2535 fp->compressed = 0;
2536 }
2537 else if (status < Z_OK)
2538 {
2539 DEBUG_printf(("9cups_fill: inflate returned %d, returning -1.", status));
2540
2541 fp->eof = 1;
2542 errno = EIO;
2543
2544 return (-1);
ef416fc2 2545 }
2546
7e86f2f6 2547 bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out;
ef416fc2 2548
2549 /*
2550 * Return the decompressed data...
2551 */
2552
2553 fp->ptr = fp->buf;
2554 fp->end = fp->buf + bytes;
2555
fa73b229 2556 if (bytes)
cbf9404a
MS
2557 {
2558 DEBUG_printf(("9cups_fill: Returning %d.", (int)bytes));
fa73b229 2559 return (bytes);
cbf9404a 2560 }
ef416fc2 2561 }
2562 }
2563#endif /* HAVE_LIBZ */
2564
2565 /*
2566 * Read a buffer's full of data...
2567 */
2568
2569 if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0)
2570 {
2571 /*
2572 * Can't read from file!
2573 */
2574
2575 fp->eof = 1;
2576 fp->ptr = fp->buf;
2577 fp->end = fp->buf;
ef416fc2 2578 }
cbf9404a
MS
2579 else
2580 {
2581 /*
2582 * Return the bytes we read...
2583 */
ef416fc2 2584
cbf9404a
MS
2585 fp->eof = 0;
2586 fp->ptr = fp->buf;
2587 fp->end = fp->buf + bytes;
2588 }
ef416fc2 2589
cbf9404a 2590 DEBUG_printf(("9cups_fill: Not gzip, returning %d.", (int)bytes));
ef416fc2 2591
2592 return (bytes);
2593}
2594
2595
c7017ecc
MS
2596/*
2597 * 'cups_open()' - Safely open a file for writing.
2598 *
2599 * We don't allow appending to directories or files that are hard-linked or
2600 * symlinked.
2601 */
2602
2603static int /* O - File descriptor or -1 otherwise */
2604cups_open(const char *filename, /* I - Filename */
2605 int mode) /* I - Open mode */
2606{
2607 int fd; /* File descriptor */
2608 struct stat fileinfo; /* File information */
19dc16f7 2609#ifndef _WIN32
c7017ecc 2610 struct stat linkinfo; /* Link information */
19dc16f7 2611#endif /* !_WIN32 */
c7017ecc
MS
2612
2613
2614 /*
2615 * Open the file...
2616 */
2617
2618 if ((fd = open(filename, mode, 0666)) < 0)
2619 return (-1);
2620
2621 /*
2622 * Then verify that the file descriptor doesn't point to a directory or hard-
2623 * linked file.
2624 */
2625
2626 if (fstat(fd, &fileinfo))
2627 {
2628 close(fd);
2629 return (-1);
2630 }
2631
2632 if (fileinfo.st_nlink != 1)
2633 {
2634 close(fd);
2635 errno = EPERM;
2636 return (-1);
2637 }
2638
19dc16f7 2639#ifdef _WIN32
c7017ecc
MS
2640 if (fileinfo.st_mode & _S_IFDIR)
2641#else
2642 if (S_ISDIR(fileinfo.st_mode))
19dc16f7 2643#endif /* _WIN32 */
c7017ecc
MS
2644 {
2645 close(fd);
2646 errno = EISDIR;
2647 return (-1);
2648 }
2649
19dc16f7 2650#ifndef _WIN32
c7017ecc
MS
2651 /*
2652 * Then use lstat to determine whether the filename is a symlink...
2653 */
2654
2655 if (lstat(filename, &linkinfo))
2656 {
2657 close(fd);
2658 return (-1);
2659 }
2660
2661 if (S_ISLNK(linkinfo.st_mode) ||
2662 fileinfo.st_dev != linkinfo.st_dev ||
2663 fileinfo.st_ino != linkinfo.st_ino ||
2664#ifdef HAVE_ST_GEN
2665 fileinfo.st_gen != linkinfo.st_gen ||
2666#endif /* HAVE_ST_GEN */
2667 fileinfo.st_nlink != linkinfo.st_nlink ||
2668 fileinfo.st_mode != linkinfo.st_mode)
2669 {
2670 /*
2671 * Yes, don't allow!
2672 */
2673
2674 close(fd);
2675 errno = EPERM;
2676 return (-1);
2677 }
19dc16f7 2678#endif /* !_WIN32 */
c7017ecc
MS
2679
2680 return (fd);
2681}
2682
2683
ef416fc2 2684/*
2685 * 'cups_read()' - Read from a file descriptor.
2686 */
2687
2688static ssize_t /* O - Number of bytes read or -1 */
2689cups_read(cups_file_t *fp, /* I - CUPS file */
2690 char *buf, /* I - Buffer */
2691 size_t bytes) /* I - Number bytes */
2692{
2693 ssize_t total; /* Total bytes read */
2694
2695
807315e6 2696 DEBUG_printf(("7cups_read(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
634763e8 2697
ef416fc2 2698 /*
2699 * Loop until we read at least 0 bytes...
2700 */
2701
2702 for (;;)
2703 {
19dc16f7 2704#ifdef _WIN32
b86bc4cf 2705 if (fp->mode == 's')
2706 total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
2707 else
2708 total = (ssize_t)read(fp->fd, buf, (unsigned)bytes);
2709#else
ef416fc2 2710 if (fp->mode == 's')
2711 total = recv(fp->fd, buf, bytes, 0);
2712 else
2713 total = read(fp->fd, buf, bytes);
19dc16f7 2714#endif /* _WIN32 */
ef416fc2 2715
e07d4801 2716 DEBUG_printf(("9cups_read: total=" CUPS_LLFMT, CUPS_LLCAST total));
634763e8 2717
ef416fc2 2718 if (total >= 0)
2719 break;
2720
2721 /*
2722 * Reads can be interrupted by signals and unavailable resources...
2723 */
2724
2725 if (errno == EAGAIN || errno == EINTR)
2726 continue;
2727 else
2728 return (-1);
2729 }
2730
2731 /*
2732 * Return the total number of bytes read...
2733 */
2734
2735 return (total);
2736}
2737
2738
2739/*
2740 * 'cups_write()' - Write to a file descriptor.
2741 */
2742
2743static ssize_t /* O - Number of bytes written or -1 */
2744cups_write(cups_file_t *fp, /* I - CUPS file */
2745 const char *buf, /* I - Buffer */
2746 size_t bytes) /* I - Number bytes */
2747{
2abf387c 2748 size_t total; /* Total bytes written */
2749 ssize_t count; /* Count this time */
ef416fc2 2750
2751
807315e6 2752 DEBUG_printf(("7cups_write(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)fp, (void *)buf, CUPS_LLCAST bytes));
ecdc0628 2753
ef416fc2 2754 /*
2755 * Loop until all bytes are written...
2756 */
2757
2758 total = 0;
2759 while (bytes > 0)
2760 {
19dc16f7 2761#ifdef _WIN32
b86bc4cf 2762 if (fp->mode == 's')
2763 count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
2764 else
2765 count = (ssize_t)write(fp->fd, buf, (unsigned)bytes);
2766#else
ef416fc2 2767 if (fp->mode == 's')
2768 count = send(fp->fd, buf, bytes, 0);
2769 else
2770 count = write(fp->fd, buf, bytes);
19dc16f7 2771#endif /* _WIN32 */
ef416fc2 2772
e07d4801 2773 DEBUG_printf(("9cups_write: count=" CUPS_LLFMT, CUPS_LLCAST count));
634763e8 2774
ef416fc2 2775 if (count < 0)
2776 {
2777 /*
2778 * Writes can be interrupted by signals and unavailable resources...
2779 */
2780
2781 if (errno == EAGAIN || errno == EINTR)
2782 continue;
2783 else
2784 return (-1);
2785 }
2786
2787 /*
2788 * Update the counts for the last write call...
2789 */
2790
7e86f2f6
MS
2791 bytes -= (size_t)count;
2792 total += (size_t)count;
ef416fc2 2793 buf += count;
2794 }
2795
2796 /*
2797 * Return the total number of bytes written...
2798 */
2799
b86bc4cf 2800 return ((ssize_t)total);
ef416fc2 2801}