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