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