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