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