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