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