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