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