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