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