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