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