X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=cups%2Ffile.c;h=2711e56800fef4f7031a27eaa3bee78f3bd1e302;hb=83e080011117b6125721e132de22edb18266e5eb;hp=20aae7247947725318354f2b8a5546a34dee936a;hpb=b423cd4cb9fa8cf030cc046a59c28196f3edf6b4;p=thirdparty%2Fcups.git diff --git a/cups/file.c b/cups/file.c index 20aae7247..2711e5680 100644 --- a/cups/file.c +++ b/cups/file.c @@ -1,132 +1,358 @@ /* - * "$Id: file.c 5186 2006-02-26 18:56:05Z mike $" + * "$Id: file.c 7672 2008-06-18 22:03:02Z mike $" * - * File functions for the Common UNIX Printing System (CUPS). + * File functions for CUPS. * * Since stdio files max out at 256 files on many systems, we have to * write similar functions without this limit. At the same time, using * our own file functions allows us to provide transparent support of * gzip'd print files, PPD files, etc. * - * Copyright 1997-2006 by Easy Software Products, all rights reserved. + * Copyright 2007-2011 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the - * property of Easy Software Products and are protected by Federal - * copyright law. Distribution and use rights are outlined in the file - * "LICENSE.txt" which should have been included with this file. If this - * file is missing or damaged please contact Easy Software Products - * at: - * - * Attn: CUPS Licensing Information - * Easy Software Products - * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636 USA - * - * Voice: (301) 373-9600 - * EMail: cups-info@cups.org - * WWW: http://www.cups.org + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". * * Contents: * - * cupsFileClose() - Close a CUPS file. - * cupsFileCompression() - Return whether a file is compressed. - * cupsFileEOF() - Return the end-of-file status. - * cupsFileFind() - Find a file using the specified path. - * cupsFileFlush() - Flush pending output. - * cupsFileGetChar() - Get a single character from a file. - * cupsFileGetConf() - Get a line from a configuration file... - * cupsFileGets() - Get a CR and/or LF-terminated line. - * cupsFileLock() - Temporarily lock access to a file. - * cupsFileNumber() - Return the file descriptor associated with a CUPS file. - * cupsFileOpen() - Open a CUPS file. - * cupsFileOpenFd() - Open a CUPS file using a file descriptor. - * cupsFilePeekChar() - Peek at the next character from a file. - * cupsFilePrintf() - Write a formatted string. - * cupsFilePutChar() - Write a character. - * cupsFilePuts() - Write a string. - * cupsFileRead() - Read from a file. - * cupsFileRewind() - Rewind a file. - * cupsFileSeek() - Seek in a file. - * cupsFileTell() - Return the current file position. - * cupsFileUnlock() - Unlock access to a file. - * cupsFileWrite() - Write to a file. - * cups_compress() - Compress a buffer of data... - * cups_fill() - Fill the input buffer... - * cups_read() - Read from a file descriptor. - * cups_write() - Write to a file descriptor. + * _cupsFileCheck() - Check the permissions of the given filename. + * _cupsFileCheckFilter() - Report file check results as CUPS filter messages. + * cupsFileClose() - Close a CUPS file. + * cupsFileCompression() - Return whether a file is compressed. + * cupsFileEOF() - Return the end-of-file status. + * cupsFileFind() - Find a file using the specified path. + * cupsFileFlush() - Flush pending output. + * cupsFileGetChar() - Get a single character from a file. + * cupsFileGetConf() - Get a line from a configuration file. + * cupsFileGetLine() - Get a CR and/or LF-terminated line that may + * contain binary data. + * cupsFileGets() - Get a CR and/or LF-terminated line. + * cupsFileLock() - Temporarily lock access to a file. + * cupsFileNumber() - Return the file descriptor associated with a CUPS + * file. + * cupsFileOpen() - Open a CUPS file. + * cupsFileOpenFd() - Open a CUPS file using a file descriptor. + * cupsFilePeekChar() - Peek at the next character from a file. + * cupsFilePrintf() - Write a formatted string. + * cupsFilePutChar() - Write a character. + * cupsFilePutConf() - Write a configuration line. + * cupsFilePuts() - Write a string. + * cupsFileRead() - Read from a file. + * cupsFileRewind() - Set the current file position to the beginning of + * the file. + * cupsFileSeek() - Seek in a file. + * cupsFileStderr() - Return a CUPS file associated with stderr. + * cupsFileStdin() - Return a CUPS file associated with stdin. + * cupsFileStdout() - Return a CUPS file associated with stdout. + * cupsFileTell() - Return the current file position. + * cupsFileUnlock() - Unlock access to a file. + * cupsFileWrite() - Write to a file. + * cups_compress() - Compress a buffer of data. + * cups_fill() - Fill the input buffer. + * cups_open() - Safely open a file for writing. + * cups_read() - Read from a file descriptor. + * cups_write() - Write to a file descriptor. */ /* * Include necessary headers... */ -#include -#include -#include -#include "http-private.h" -#include "string.h" -#include -#include +#include "file-private.h" +#include #include -#include - -#include "file.h" -#ifdef HAVE_LIBZ -# include -#endif /* HAVE_LIBZ */ -#ifdef WIN32 -# include -# include -#endif /* WIN32 */ /* - * Some operating systems support large files via open flag O_LARGEFILE... + * Local functions... */ -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif /* !O_LARGEFILE */ +#ifdef HAVE_LIBZ +static ssize_t cups_compress(cups_file_t *fp, const char *buf, size_t bytes); +#endif /* HAVE_LIBZ */ +static ssize_t cups_fill(cups_file_t *fp); +static int cups_open(const char *filename, int mode); +static ssize_t cups_read(cups_file_t *fp, char *buf, size_t bytes); +static ssize_t cups_write(cups_file_t *fp, const char *buf, size_t bytes); +#ifndef WIN32 /* - * Types and structures... + * '_cupsFileCheck()' - Check the permissions of the given filename. */ -struct _cups_file_s /**** CUPS file structure... ****/ +_cups_fc_result_t /* O - Check result */ +_cupsFileCheck( + const char *filename, /* I - Filename to check */ + _cups_fc_filetype_t filetype, /* I - Type of file checks? */ + int dorootchecks, /* I - Check for root permissions? */ + _cups_fc_func_t cb, /* I - Callback function */ + void *context) /* I - Context pointer for callback */ { - int fd; /* File descriptor */ - char mode, /* Mode ('r' or 'w') */ - compressed, /* Compression used? */ - eof, /* End of file? */ - buf[4096], /* Buffer */ - *ptr, /* Pointer into buffer */ - *end; /* End of buffer data */ - off_t pos; /* File position for start of buffer */ + struct stat fileinfo; /* File information */ + char message[1024], /* Message string */ + temp[1024], /* Parent directory filename */ + *ptr; /* Pointer into parent directory */ + _cups_fc_result_t result; /* Check result */ -#ifdef HAVE_LIBZ - z_stream stream; /* (De)compression stream */ - Bytef cbuf[4096]; /* (De)compression buffer */ - uLong crc; /* (De)compression CRC */ -#endif /* HAVE_LIBZ */ -}; + + /* + * Does the filename contain a relative path ("../")? + */ + + if (strstr(filename, "../")) + { + /* + * Yes, fail it! + */ + + result = _CUPS_FILE_CHECK_RELATIVE_PATH; + goto finishup; + } + + /* + * Does the program even exist and is it accessible? + */ + + if (stat(filename, &fileinfo)) + { + /* + * Nope... + */ + + result = _CUPS_FILE_CHECK_MISSING; + goto finishup; + } + + /* + * Check the execute bit... + */ + + result = _CUPS_FILE_CHECK_OK; + + switch (filetype) + { + case _CUPS_FILE_CHECK_DIRECTORY : + if (!S_ISDIR(fileinfo.st_mode)) + result = _CUPS_FILE_CHECK_WRONG_TYPE; + break; + + default : + if (!S_ISREG(fileinfo.st_mode)) + result = _CUPS_FILE_CHECK_WRONG_TYPE; + break; + } + + if (result) + goto finishup; + + /* + * Are we doing root checks? + */ + + if (!dorootchecks) + { + /* + * Nope, so anything (else) goes... + */ + + goto finishup; + } + + /* + * Verify permission of the file itself: + * + * 1. Must be owned by root + * 2. Must not be writable by group + * 3. Must not be setuid + * 4. Must not be writable by others + */ + + if (fileinfo.st_uid || /* 1. Must be owned by root */ + (fileinfo.st_mode & S_IWGRP) || /* 2. Must not be writable by group */ + (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */ + (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */ + { + result = _CUPS_FILE_CHECK_PERMISSIONS; + goto finishup; + } + + if (filetype == _CUPS_FILE_CHECK_DIRECTORY || + filetype == _CUPS_FILE_CHECK_FILE_ONLY) + goto finishup; + + /* + * Now check the containing directory... + */ + + strlcpy(temp, filename, sizeof(temp)); + if ((ptr = strrchr(temp, '/')) != NULL) + { + if (ptr == temp) + ptr[1] = '\0'; + else + *ptr = '\0'; + } + + if (stat(temp, &fileinfo)) + { + /* + * Doesn't exist?!? + */ + + result = _CUPS_FILE_CHECK_MISSING; + filetype = _CUPS_FILE_CHECK_DIRECTORY; + filename = temp; + + goto finishup; + } + + if (fileinfo.st_uid || /* 1. Must be owned by root */ + (fileinfo.st_mode & S_IWGRP) || /* 2. Must not be writable by group */ + (fileinfo.st_mode & S_ISUID) || /* 3. Must not be setuid */ + (fileinfo.st_mode & S_IWOTH)) /* 4. Must not be writable by others */ + { + result = _CUPS_FILE_CHECK_PERMISSIONS; + filetype = _CUPS_FILE_CHECK_DIRECTORY; + filename = temp; + } + + /* + * Common return point... + */ + + finishup: + + if (cb) + { + cups_lang_t *lang = cupsLangDefault(); + /* Localization information */ + + switch (result) + { + case _CUPS_FILE_CHECK_OK : + if (filetype == _CUPS_FILE_CHECK_DIRECTORY) + snprintf(message, sizeof(message), + _cupsLangString(lang, _("Directory \"%s\" permissions OK " + "(0%o/uid=%d/gid=%d).")), + filename, fileinfo.st_mode, (int)fileinfo.st_uid, + (int)fileinfo.st_gid); + else + snprintf(message, sizeof(message), + _cupsLangString(lang, _("File \"%s\" permissions OK " + "(0%o/uid=%d/gid=%d).")), + filename, fileinfo.st_mode, (int)fileinfo.st_uid, + (int)fileinfo.st_gid); + break; + + case _CUPS_FILE_CHECK_MISSING : + if (filetype == _CUPS_FILE_CHECK_DIRECTORY) + snprintf(message, sizeof(message), + _cupsLangString(lang, _("Directory \"%s\" not available: " + "%s")), + filename, strerror(errno)); + else + snprintf(message, sizeof(message), + _cupsLangString(lang, _("File \"%s\" not available: %s")), + filename, strerror(errno)); + break; + + case _CUPS_FILE_CHECK_PERMISSIONS : + if (filetype == _CUPS_FILE_CHECK_DIRECTORY) + snprintf(message, sizeof(message), + _cupsLangString(lang, _("Directory \"%s\" has insecure " + "permissions " + "(0%o/uid=%d/gid=%d).")), + filename, fileinfo.st_mode, (int)fileinfo.st_uid, + (int)fileinfo.st_gid); + else + snprintf(message, sizeof(message), + _cupsLangString(lang, _("File \"%s\" has insecure " + "permissions " + "(0%o/uid=%d/gid=%d).")), + filename, fileinfo.st_mode, (int)fileinfo.st_uid, + (int)fileinfo.st_gid); + break; + + case _CUPS_FILE_CHECK_WRONG_TYPE : + if (filetype == _CUPS_FILE_CHECK_DIRECTORY) + snprintf(message, sizeof(message), + _cupsLangString(lang, _("Directory \"%s\" is a file.")), + filename); + else + snprintf(message, sizeof(message), + _cupsLangString(lang, _("File \"%s\" is a directory.")), + filename); + break; + + case _CUPS_FILE_CHECK_RELATIVE_PATH : + if (filetype == _CUPS_FILE_CHECK_DIRECTORY) + snprintf(message, sizeof(message), + _cupsLangString(lang, _("Directory \"%s\" contains a " + "relative path.")), filename); + else + snprintf(message, sizeof(message), + _cupsLangString(lang, _("File \"%s\" contains a relative " + "path.")), filename); + break; + } + + (*cb)(context, result, message); + } + + return (result); +} /* - * Local functions... + * '_cupsFileCheckFilter()' - Report file check results as CUPS filter messages. */ -#ifdef HAVE_LIBZ -static ssize_t cups_compress(cups_file_t *fp, const char *buf, size_t bytes); -#endif /* HAVE_LIBZ */ -static ssize_t cups_fill(cups_file_t *fp); -static ssize_t cups_read(cups_file_t *fp, char *buf, size_t bytes); -static ssize_t cups_write(cups_file_t *fp, const char *buf, size_t bytes); +void +_cupsFileCheckFilter( + void *context, /* I - Context pointer (unused) */ + _cups_fc_result_t result, /* I - Result code */ + const char *message) /* I - Message text */ +{ + const char *prefix; /* Messaging prefix */ + + + (void)context; + + switch (result) + { + default : + case _CUPS_FILE_CHECK_OK : + prefix = "DEBUG2"; + break; + + case _CUPS_FILE_CHECK_MISSING : + case _CUPS_FILE_CHECK_WRONG_TYPE : + prefix = "ERROR"; + fputs("STATE: +cups-missing-filter-warning\n", stderr); + break; + + case _CUPS_FILE_CHECK_PERMISSIONS : + case _CUPS_FILE_CHECK_RELATIVE_PATH : + prefix = "ERROR"; + fputs("STATE: +cups-insecure-filter-warning\n", stderr); + break; + } + + fprintf(stderr, "%s: %s\n", prefix, message); +} +#endif /* !WIN32 */ /* * 'cupsFileClose()' - Close a CUPS file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ int /* O - 0 on success, -1 on error */ @@ -135,9 +361,10 @@ cupsFileClose(cups_file_t *fp) /* I - CUPS file */ int fd; /* File descriptor */ char mode; /* Open mode */ int status; /* Return status */ + int is_stdio; /* Is a stdio file? */ - DEBUG_printf(("cupsFileClose(fp=%p)\n", fp)); + DEBUG_printf(("cupsFileClose(fp=%p)", fp)); /* * Range check... @@ -226,8 +453,12 @@ cupsFileClose(cups_file_t *fp) /* I - CUPS file */ * Save the file descriptor we used and free memory... */ - fd = fp->fd; - mode = fp->mode; + fd = fp->fd; + mode = fp->mode; + is_stdio = fp->is_stdio; + + if (fp->printf_buffer) + free(fp->printf_buffer); free(fp); @@ -240,7 +471,7 @@ cupsFileClose(cups_file_t *fp) /* I - CUPS file */ if (closesocket(fd) < 0) status = -1; } - else + else if (!is_stdio) { if (close(fd) < 0) status = -1; @@ -252,23 +483,27 @@ cupsFileClose(cups_file_t *fp) /* I - CUPS file */ /* * 'cupsFileCompression()' - Return whether a file is compressed. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -int /* O - CUPS_FILE_NONE or CUPS_FILE_GZIP */ +int /* O - @code CUPS_FILE_NONE@ or @code CUPS_FILE_GZIP@ */ cupsFileCompression(cups_file_t *fp) /* I - CUPS file */ { - return (fp->compressed); + return (fp ? fp->compressed : CUPS_FILE_NONE); } /* * 'cupsFileEOF()' - Return the end-of-file status. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -int /* O - 1 on EOF, 0 otherwise */ +int /* O - 1 on end of file, 0 otherwise */ cupsFileEOF(cups_file_t *fp) /* I - CUPS file */ { - return (fp->eof); + return (fp ? fp->eof : 1); } @@ -278,11 +513,13 @@ cupsFileEOF(cups_file_t *fp) /* I - CUPS file */ * This function allows the paths in the path string to be separated by * colons (UNIX standard) or semicolons (Windows standard) and stores the * result in the buffer supplied. If the file cannot be found in any of - * the supplied paths, NULL is returned. A NULL path only matches the - * current directory. + * the supplied paths, @code NULL@ is returned. A @code NULL@ path only + * matches the current directory. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -const char * /* O - Full path to file or NULL */ +const char * /* O - Full path to file or @code NULL@ if not found */ cupsFileFind(const char *filename, /* I - File to find */ const char *path, /* I - Colon/semicolon-separated path */ int executable, /* I - 1 = executable files, 0 = any file/dir */ @@ -297,6 +534,10 @@ cupsFileFind(const char *filename, /* I - File to find */ * Range check input... */ + DEBUG_printf(("cupsFileFind(filename=\"%s\", path=\"%s\", executable=%d, " + "buffer=%p, bufsize=%d)", filename, path, executable, buffer, + bufsize)); + if (!filename || !buffer || bufsize < 2) return (NULL); @@ -324,7 +565,11 @@ cupsFileFind(const char *filename, /* I - File to find */ while (*path) { +#ifdef WIN32 + if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255)))) +#else if (*path == ';' || *path == ':') +#endif /* WIN32 */ { if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend) *bufptr++ = '/'; @@ -336,7 +581,10 @@ cupsFileFind(const char *filename, /* I - File to find */ #else if (!access(buffer, executable ? X_OK : 0)) #endif /* WIN32 */ + { + DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer)); return (buffer); + } bufptr = buffer; } @@ -356,23 +604,31 @@ cupsFileFind(const char *filename, /* I - File to find */ strlcpy(bufptr, filename, bufend - bufptr); if (!access(buffer, 0)) + { + DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer)); return (buffer); + } else + { + DEBUG_puts("1cupsFileFind: Returning NULL"); return (NULL); + } } /* * 'cupsFileFlush()' - Flush pending output. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ int /* O - 0 on success, -1 on error */ cupsFileFlush(cups_file_t *fp) /* I - CUPS file */ { - size_t bytes; /* Bytes to write */ + ssize_t bytes; /* Bytes to write */ - DEBUG_printf(("cupsFileFlush(fp=%p)\n", fp)); + DEBUG_printf(("cupsFileFlush(fp=%p)", fp)); /* * Range check input... @@ -380,13 +636,14 @@ cupsFileFlush(cups_file_t *fp) /* I - CUPS file */ if (!fp || fp->mode != 'w') { - DEBUG_puts(" Attempt to flush a read-only file..."); + DEBUG_puts("1cupsFileFlush: Attempt to flush a read-only file..."); return (-1); } - bytes = fp->ptr - fp->buf; + bytes = (ssize_t)(fp->ptr - fp->buf); - DEBUG_printf((" Flushing %ld bytes...\n", (long)bytes)); + DEBUG_printf(("2cupsFileFlush: Flushing " CUPS_LLFMT " bytes...", + CUPS_LLCAST bytes)); if (bytes > 0) { @@ -402,16 +659,18 @@ cupsFileFlush(cups_file_t *fp) /* I - CUPS file */ fp->ptr = fp->buf; } - + return (0); } /* * 'cupsFileGetChar()' - Get a single character from a file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -int /* O - Character or -1 on EOF */ +int /* O - Character or -1 on end of file */ cupsFileGetChar(cups_file_t *fp) /* I - CUPS file */ { /* @@ -419,7 +678,10 @@ cupsFileGetChar(cups_file_t *fp) /* I - CUPS file */ */ if (!fp || (fp->mode != 'r' && fp->mode != 's')) + { + DEBUG_puts("5cupsFileGetChar: Bad arguments!"); return (-1); + } /* * If the input buffer is empty, try to read more data... @@ -427,21 +689,32 @@ cupsFileGetChar(cups_file_t *fp) /* I - CUPS file */ if (fp->ptr >= fp->end) if (cups_fill(fp) < 0) + { + DEBUG_puts("5cupsFileGetChar: Unable to fill buffer!"); return (-1); + } /* * Return the next character in the buffer... */ + DEBUG_printf(("5cupsFileGetChar: Returning %d...", *(fp->ptr) & 255)); + + fp->pos ++; + + DEBUG_printf(("6cupsFileGetChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + return (*(fp->ptr)++ & 255); } /* - * 'cupsFileGetConf()' - Get a line from a configuration file... + * 'cupsFileGetConf()' - Get a line from a configuration file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -char * /* O - Line read or NULL on eof/error */ +char * /* O - Line read or @code NULL@ on end of file or error */ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ char *buf, /* O - String buffer */ size_t buflen, /* I - Size of string buffer */ @@ -455,6 +728,10 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ * Range check input... */ + DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT + ", value=%p, linenum=%p)", fp, buf, CUPS_LLCAST buflen, + value, linenum)); + if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2 || !value) { @@ -469,7 +746,7 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ */ *value = NULL; - + while (cupsFileGets(fp, buf, buflen)) { (*linenum) ++; @@ -480,22 +757,31 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ if ((ptr = strchr(buf, '#')) != NULL) { - while (ptr > buf) + if (ptr > buf && ptr[-1] == '\\') { - if (!isspace(ptr[-1] & 255)) - break; - - ptr --; + // Unquote the #... + _cups_strcpy(ptr - 1, ptr); } + else + { + // Strip the comment and any trailing whitespace... + while (ptr > buf) + { + if (!_cups_isspace(ptr[-1])) + break; - *ptr = '\0'; + ptr --; + } + + *ptr = '\0'; + } } /* * Strip leading whitespace... */ - for (ptr = buf; isspace(*ptr & 255); ptr ++); + for (ptr = buf; _cups_isspace(*ptr); ptr ++); if (ptr > buf) _cups_strcpy(buf, ptr); @@ -511,7 +797,7 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ */ for (ptr = buf; *ptr; ptr ++) - if (isspace(*ptr & 255)) + if (_cups_isspace(*ptr)) break; if (*ptr) @@ -520,7 +806,7 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ * Have a value, skip any other spaces... */ - while (isspace(*ptr & 255)) + while (_cups_isspace(*ptr)) *ptr++ = '\0'; if (*ptr) @@ -544,7 +830,7 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ return (buf); } - while (ptr > *value && isspace(*ptr & 255)) + while (ptr > *value && _cups_isspace(*ptr)) *ptr-- = '\0'; } @@ -560,11 +846,94 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ } +/* + * 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may + * contain binary data. + * + * This function differs from @link cupsFileGets@ in that the trailing CR + * and LF are preserved, as is any binary data on the line. The buffer is + * nul-terminated, however you should use the returned length to determine + * the number of bytes on the line. + * + * @since CUPS 1.2/Mac OS X 10.5@ + */ + +size_t /* O - Number of bytes on line or 0 on end of file */ +cupsFileGetLine(cups_file_t *fp, /* I - File to read from */ + char *buf, /* I - Buffer */ + size_t buflen) /* I - Size of buffer */ +{ + int ch; /* Character from file */ + char *ptr, /* Current position in line buffer */ + *end; /* End of line buffer */ + + + /* + * Range check input... + */ + + DEBUG_printf(("2cupsFileGetLine(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", + fp, buf, CUPS_LLCAST buflen)); + + if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3) + return (0); + + /* + * Now loop until we have a valid line... + */ + + for (ptr = buf, end = buf + buflen - 2; ptr < end ;) + { + if (fp->ptr >= fp->end) + if (cups_fill(fp) <= 0) + break; + + *ptr++ = ch = *(fp->ptr)++; + fp->pos ++; + + if (ch == '\r') + { + /* + * Check for CR LF... + */ + + if (fp->ptr >= fp->end) + if (cups_fill(fp) <= 0) + break; + + if (*(fp->ptr) == '\n') + { + *ptr++ = *(fp->ptr)++; + fp->pos ++; + } + + break; + } + else if (ch == '\n') + { + /* + * Line feed ends a line... + */ + + break; + } + } + + *ptr = '\0'; + + DEBUG_printf(("4cupsFileGetLine: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + + return (ptr - buf); +} + + /* * 'cupsFileGets()' - Get a CR and/or LF-terminated line. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -char * /* O - Line read or NULL on eof/error */ +char * /* O - Line read or @code NULL@ on end of file or error */ cupsFileGets(cups_file_t *fp, /* I - CUPS file */ char *buf, /* O - String buffer */ size_t buflen) /* I - Size of string buffer */ @@ -578,6 +947,9 @@ cupsFileGets(cups_file_t *fp, /* I - CUPS file */ * Range check input... */ + DEBUG_printf(("2cupsFileGets(fp=%p, buf=%p, buflen=" CUPS_LLFMT ")", fp, buf, + CUPS_LLCAST buflen)); + if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2) return (NULL); @@ -597,6 +969,7 @@ cupsFileGets(cups_file_t *fp, /* I - CUPS file */ } ch = *(fp->ptr)++; + fp->pos ++; if (ch == '\r') { @@ -609,7 +982,10 @@ cupsFileGets(cups_file_t *fp, /* I - CUPS file */ break; if (*(fp->ptr) == '\n') - fp->ptr ++; + { + fp->ptr ++; + fp->pos ++; + } break; } @@ -627,16 +1003,20 @@ cupsFileGets(cups_file_t *fp, /* I - CUPS file */ *ptr = '\0'; + DEBUG_printf(("4cupsFileGets: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + return (buf); } /* * 'cupsFileLock()' - Temporarily lock access to a file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ int /* O - 0 on success, -1 on error */ -cupsFileLock(cups_file_t *fp, /* I - File to lock */ +cupsFileLock(cups_file_t *fp, /* I - CUPS file */ int block) /* I - 1 to wait for the lock, 0 to fail right away */ { /* @@ -651,7 +1031,7 @@ cupsFileLock(cups_file_t *fp, /* I - File to lock */ */ #ifdef WIN32 - return (locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0)); + return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0)); #else return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0)); #endif /* WIN32 */ @@ -660,20 +1040,40 @@ cupsFileLock(cups_file_t *fp, /* I - File to lock */ /* * 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ int /* O - File descriptor */ cupsFileNumber(cups_file_t *fp) /* I - CUPS file */ { - return (fp->fd); + if (fp) + return (fp->fd); + else + return (-1); } /* * 'cupsFileOpen()' - Open a CUPS file. + * + * The "mode" parameter can be "r" to read, "w" to write, overwriting any + * existing file, "a" to append to an existing file or create a new file, + * or "s" to open a socket connection. + * + * When opening for writing ("w"), an optional number from 1 to 9 can be + * supplied which enables Flate compression of the file. Compression is + * not supported for the "a" (append) mode. + * + * When opening a socket connection, the filename is a string of the form + * "address:port" or "hostname:port". The socket will make an IPv4 or IPv6 + * connection as needed, generally preferring IPv6 connections when there is + * a choice. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -cups_file_t * /* O - CUPS file or NULL */ +cups_file_t * /* O - CUPS file or @code NULL@ if the file or socket cannot be opened */ cupsFileOpen(const char *filename, /* I - Name of file */ const char *mode) /* I - Open mode */ { @@ -684,7 +1084,7 @@ cupsFileOpen(const char *filename, /* I - Name of file */ http_addrlist_t *addrlist; /* Host address list */ - DEBUG_printf(("cupsFileOpen(filename=\"%s\", mode=\"%s\")\n", filename, + DEBUG_printf(("cupsFileOpen(filename=\"%s\", mode=\"%s\")", filename, mode)); /* @@ -692,7 +1092,8 @@ cupsFileOpen(const char *filename, /* I - Name of file */ */ if (!filename || !mode || - (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's')) + (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || + (*mode == 'a' && isdigit(mode[1] & 255))) return (NULL); /* @@ -702,15 +1103,30 @@ cupsFileOpen(const char *filename, /* I - Name of file */ switch (*mode) { case 'a' : /* Append file */ - fd = open(filename, O_RDWR | O_CREAT | O_APPEND | O_LARGEFILE, 0666); + fd = cups_open(filename, + O_RDWR | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY); break; case 'r' : /* Read file */ - fd = open(filename, O_RDONLY | O_LARGEFILE, 0); + fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0); break; case 'w' : /* Write file */ - fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_LARGEFILE, 0666); + fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY); + if (fd < 0 && errno == ENOENT) + { + fd = cups_open(filename, + O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY); + if (fd < 0 && errno == EEXIST) + fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY); + } + + if (fd >= 0) +#ifdef WIN32 + _chsize(fd, 0); +#else + ftruncate(fd, 0); +#endif /* WIN32 */ break; case 's' : /* Read/write socket */ @@ -768,23 +1184,33 @@ cupsFileOpen(const char *filename, /* I - Name of file */ /* * 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor. + * + * The "mode" parameter can be "r" to read, "w" to write, "a" to append, + * or "s" to treat the file descriptor as a bidirectional socket connection. + * + * When opening for writing ("w"), an optional number from 1 to 9 can be + * supplied which enables Flate compression of the file. Compression is + * not supported for the "a" (append) mode. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -cups_file_t * /* O - CUPS file or NULL */ +cups_file_t * /* O - CUPS file or @code NULL@ if the file could not be opened */ cupsFileOpenFd(int fd, /* I - File descriptor */ const char *mode) /* I - Open mode */ { cups_file_t *fp; /* New CUPS file */ - DEBUG_printf(("cupsFileOpenFd(fd=%d, mode=\"%s\")\n", fd, mode)); + DEBUG_printf(("cupsFileOpenFd(fd=%d, mode=\"%s\")", fd, mode)); /* * Range check input... */ if (fd < 0 || !mode || - (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's')) + (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || + (*mode == 'a' && isdigit(mode[1] & 255))) return (NULL); /* @@ -802,8 +1228,10 @@ cupsFileOpenFd(int fd, /* I - File descriptor */ switch (*mode) { - case 'w' : case 'a' : + fp->pos = lseek(fd, 0, SEEK_END); + + case 'w' : fp->mode = 'w'; fp->ptr = fp->buf; fp->end = fp->buf + sizeof(fp->buf); @@ -875,9 +1303,11 @@ cupsFileOpenFd(int fd, /* I - File descriptor */ /* * 'cupsFilePeekChar()' - Peek at the next character from a file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -int /* O - Character or -1 on EOF */ +int /* O - Character or -1 on end of file */ cupsFilePeekChar(cups_file_t *fp) /* I - CUPS file */ { /* @@ -905,32 +1335,74 @@ cupsFilePeekChar(cups_file_t *fp) /* I - CUPS file */ /* * 'cupsFilePrintf()' - Write a formatted string. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -int /* O - Number of bytes written or -1 */ +int /* O - Number of bytes written or -1 on error */ cupsFilePrintf(cups_file_t *fp, /* I - CUPS file */ const char *format, /* I - Printf-style format string */ ...) /* I - Additional args as necessary */ { va_list ap; /* Argument list */ - size_t bytes; /* Formatted size */ - char buf[8192]; /* Formatted text */ + ssize_t bytes; /* Formatted size */ - DEBUG_printf(("cupsFilePrintf(fp=%p, format=\"%s\", ...)\n", fp, format)); + DEBUG_printf(("2cupsFilePrintf(fp=%p, format=\"%s\", ...)", fp, format)); if (!fp || !format || (fp->mode != 'w' && fp->mode != 's')) return (-1); - va_start(ap, format); - bytes = vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); + if (!fp->printf_buffer) + { + /* + * Start with an 1k printf buffer... + */ - if (bytes >= sizeof(buf)) - return (-1); + if ((fp->printf_buffer = malloc(1024)) == NULL) + return (-1); + + fp->printf_size = 1024; + } + + va_start(ap, format); + bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap); + va_end(ap); + + if (bytes >= (ssize_t)fp->printf_size) + { + /* + * Expand the printf buffer... + */ + + char *temp; /* Temporary buffer pointer */ + + + if (bytes > 65535) + return (-1); + + if ((temp = realloc(fp->printf_buffer, bytes + 1)) == NULL) + return (-1); + + fp->printf_buffer = temp; + fp->printf_size = bytes + 1; + + va_start(ap, format); + bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap); + va_end(ap); + } if (fp->mode == 's') - return (cups_write(fp, buf, bytes)); + { + if (cups_write(fp, fp->printf_buffer, bytes) < 0) + return (-1); + + fp->pos += bytes; + + DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + + return (bytes); + } if ((fp->ptr + bytes) > fp->end) if (cupsFileFlush(fp)) @@ -938,18 +1410,20 @@ cupsFilePrintf(cups_file_t *fp, /* I - CUPS file */ fp->pos += bytes; + DEBUG_printf(("4cupsFilePrintf: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + if (bytes > sizeof(fp->buf)) { #ifdef HAVE_LIBZ if (fp->compressed) - return (cups_compress(fp, buf, bytes)); + return (cups_compress(fp, fp->printf_buffer, bytes)); else #endif /* HAVE_LIBZ */ - return (cups_write(fp, buf, bytes)); + return (cups_write(fp, fp->printf_buffer, bytes)); } else { - memcpy(fp->ptr, buf, bytes); + memcpy(fp->ptr, fp->printf_buffer, bytes); fp->ptr += bytes; return (bytes); } @@ -958,6 +1432,8 @@ cupsFilePrintf(cups_file_t *fp, /* I - CUPS file */ /* * 'cupsFilePutChar()' - Write a character. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ int /* O - 0 on success, -1 on error */ @@ -1000,19 +1476,86 @@ cupsFilePutChar(cups_file_t *fp, /* I - CUPS file */ fp->pos ++; + DEBUG_printf(("4cupsFilePutChar: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + return (0); } +/* + * 'cupsFilePutConf()' - Write a configuration line. + * + * This function handles any comment escaping of the value. + * + * @since CUPS 1.4/Mac OS X 10.6@ + */ + +ssize_t /* O - Number of bytes written or -1 on error */ +cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */ + const char *directive, /* I - Directive */ + const char *value) /* I - Value */ +{ + ssize_t bytes, /* Number of bytes written */ + temp; /* Temporary byte count */ + const char *ptr; /* Pointer into value */ + + + if (!fp || !directive || !*directive) + return (-1); + + if ((bytes = cupsFilePuts(fp, directive)) < 0) + return (-1); + + if (cupsFilePutChar(fp, ' ') < 0) + return (-1); + bytes ++; + + if (value && *value) + { + if ((ptr = strchr(value, '#')) != NULL) + { + /* + * Need to quote the first # in the info string... + */ + + if ((temp = cupsFileWrite(fp, value, ptr - value)) < 0) + return (-1); + bytes += temp; + + if (cupsFilePutChar(fp, '\\') < 0) + return (-1); + bytes ++; + + if ((temp = cupsFilePuts(fp, ptr)) < 0) + return (-1); + bytes += temp; + } + else if ((temp = cupsFilePuts(fp, value)) < 0) + return (-1); + else + bytes += temp; + } + + if (cupsFilePutChar(fp, '\n') < 0) + return (-1); + else + return (bytes + 1); +} + + /* * 'cupsFilePuts()' - Write a string. + * + * Like the @code fputs@ function, no newline is appended to the string. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -int /* O - Number of bytes written or -1 */ +int /* O - Number of bytes written or -1 on error */ cupsFilePuts(cups_file_t *fp, /* I - CUPS file */ const char *s) /* I - String to write */ { - size_t bytes; /* Bytes to write */ + ssize_t bytes; /* Bytes to write */ /* @@ -1026,7 +1569,7 @@ cupsFilePuts(cups_file_t *fp, /* I - CUPS file */ * Write the string... */ - bytes = strlen(s); + bytes = (int)strlen(s); if (fp->mode == 's') { @@ -1035,6 +1578,8 @@ cupsFilePuts(cups_file_t *fp, /* I - CUPS file */ fp->pos += bytes; + DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + return (bytes); } @@ -1044,6 +1589,8 @@ cupsFilePuts(cups_file_t *fp, /* I - CUPS file */ fp->pos += bytes; + DEBUG_printf(("4cupsFilePuts: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + if (bytes > sizeof(fp->buf)) { #ifdef HAVE_LIBZ @@ -1064,25 +1611,27 @@ cupsFilePuts(cups_file_t *fp, /* I - CUPS file */ /* * 'cupsFileRead()' - Read from a file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -ssize_t /* O - Number of bytes read or -1 */ +ssize_t /* O - Number of bytes read or -1 on error */ cupsFileRead(cups_file_t *fp, /* I - CUPS file */ char *buf, /* O - Buffer */ size_t bytes) /* I - Number of bytes to read */ { - size_t total, /* Total bytes read */ - count; /* Bytes read */ + size_t total; /* Total bytes read */ + ssize_t count; /* Bytes read */ - DEBUG_printf(("cupsFileRead(fp=%p, buf=%p, bytes=%ld)\n", fp, buf, - (long)bytes)); + DEBUG_printf(("2cupsFileRead(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf, + CUPS_LLCAST bytes)); /* * Range check input... */ - if (!fp || !buf || bytes < 0 || (fp->mode != 'r' && fp->mode != 's')) + if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's')) return (-1); if (bytes == 0) @@ -1098,20 +1647,24 @@ cupsFileRead(cups_file_t *fp, /* I - CUPS file */ if (fp->ptr >= fp->end) if (cups_fill(fp) <= 0) { - DEBUG_printf((" cups_fill() returned -1, total=%d\n", total)); + DEBUG_printf(("4cupsFileRead: cups_fill() returned -1, total=" + CUPS_LLFMT, CUPS_LLCAST total)); if (total > 0) - return (total); + return ((ssize_t)total); else return (-1); } - count = fp->end - fp->ptr; - if (count > bytes) - count = bytes; + count = (ssize_t)(fp->end - fp->ptr); + if (count > (ssize_t)bytes) + count = (ssize_t)bytes; memcpy(buf, fp->ptr, count); fp->ptr += count; + fp->pos += count; + + DEBUG_printf(("4cupsFileRead: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); /* * Update the counts for the last read... @@ -1126,37 +1679,102 @@ cupsFileRead(cups_file_t *fp, /* I - CUPS file */ * Return the total number of bytes read... */ - DEBUG_printf((" total=%d\n", total)); + DEBUG_printf(("3cupsFileRead: total=" CUPS_LLFMT, CUPS_LLCAST total)); - return (total); + return ((ssize_t)total); } /* - * 'cupsFileRewind()' - Rewind a file. + * 'cupsFileRewind()' - Set the current file position to the beginning of the + * file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -off_t /* O - New file position or -1 */ +off_t /* O - New file position or -1 on error */ cupsFileRewind(cups_file_t *fp) /* I - CUPS file */ { - return (cupsFileSeek(fp, 0L)); + /* + * Range check input... + */ + + DEBUG_printf(("cupsFileRewind(fp=%p)", fp)); + DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + + if (!fp || fp->mode != 'r') + return (-1); + + /* + * Handle special cases... + */ + + if (fp->bufpos == 0) + { + /* + * No seeking necessary... + */ + + fp->pos = 0; + + if (fp->ptr) + { + fp->ptr = fp->buf; + fp->eof = 0; + } + + DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + + return (0); + } + + /* + * Otherwise, seek in the file and cleanup any compression buffers... + */ + +#ifdef HAVE_LIBZ + if (fp->compressed) + { + inflateEnd(&fp->stream); + fp->compressed = 0; + } +#endif /* HAVE_LIBZ */ + + if (lseek(fp->fd, 0, SEEK_SET)) + { + DEBUG_printf(("1cupsFileRewind: lseek failed: %s", strerror(errno))); + return (-1); + } + + fp->bufpos = 0; + fp->pos = 0; + fp->ptr = NULL; + fp->end = NULL; + fp->eof = 0; + + DEBUG_printf(("2cupsFileRewind: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + + return (0); } /* * 'cupsFileSeek()' - Seek in a file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -off_t /* O - New file position or -1 */ +off_t /* O - New file position or -1 on error */ cupsFileSeek(cups_file_t *fp, /* I - CUPS file */ off_t pos) /* I - Position in file */ { - size_t bytes; /* Number bytes in buffer */ + ssize_t bytes; /* Number bytes in buffer */ - DEBUG_printf(("cupsFileSeek(fp=%p, pos=" CUPS_LLFMT ")\n", fp, pos)); - DEBUG_printf((" fp->pos=" CUPS_LLFMT "\n", fp->pos)); - DEBUG_printf((" fp->ptr=%p, fp->end=%p\n", fp->ptr, fp->end)); + DEBUG_printf(("cupsFileSeek(fp=%p, pos=" CUPS_LLFMT ")", fp, + CUPS_LLCAST pos)); + DEBUG_printf(("2cupsFileSeek: fp->pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + DEBUG_printf(("2cupsFileSeek: fp->ptr=%p, fp->end=%p", fp->ptr, fp->end)); /* * Range check input... @@ -1165,123 +1783,268 @@ cupsFileSeek(cups_file_t *fp, /* I - CUPS file */ if (!fp || pos < 0 || fp->mode != 'r') return (-1); - if (fp->pos == pos) + /* + * Handle special cases... + */ + + if (pos == 0) + return (cupsFileRewind(fp)); + + if (fp->ptr) { - /* - * No seeking necessary... - */ + bytes = (ssize_t)(fp->end - fp->buf); - if (fp->ptr) + DEBUG_printf(("2cupsFileSeek: bytes=" CUPS_LLFMT, CUPS_LLCAST bytes)); + + if (pos >= fp->bufpos && pos < (fp->bufpos + bytes)) { - fp->ptr = fp->buf; + /* + * No seeking necessary... + */ + + fp->pos = pos; + fp->ptr = fp->buf + pos - fp->bufpos; fp->eof = 0; + + return (pos); } + } - return (pos); +#ifdef HAVE_LIBZ + if (!fp->compressed && !fp->ptr) + { + /* + * Preload a buffer to determine whether the file is compressed... + */ + + if (cups_fill(fp) < 0) + return (-1); } +#endif /* HAVE_LIBZ */ /* - * Figure out the number of bytes in the current buffer, and then - * see if we are outside of it... + * Seek forwards or backwards... */ - bytes = fp->end - fp->buf; fp->eof = 0; - if (pos < fp->pos) + if (pos < fp->bufpos) { /* * Need to seek backwards... */ + DEBUG_puts("2cupsFileSeek: SEEK BACKWARDS"); + #ifdef HAVE_LIBZ if (fp->compressed) { inflateEnd(&fp->stream); lseek(fp->fd, 0, SEEK_SET); - fp->pos = 0; - fp->ptr = NULL; - fp->end = NULL; + fp->bufpos = 0; + fp->pos = 0; + fp->ptr = NULL; + fp->end = NULL; while ((bytes = cups_fill(fp)) > 0) - if (pos >= fp->pos && pos < (fp->pos + bytes)) + if (pos >= fp->bufpos && pos < (fp->bufpos + bytes)) break; if (bytes <= 0) return (-1); + + fp->ptr = fp->buf + pos - fp->bufpos; + fp->pos = pos; } else #endif /* HAVE_LIBZ */ { - fp->pos = lseek(fp->fd, pos, SEEK_SET); - DEBUG_printf((" lseek() returned %ld...\n", (long)fp->pos)); - fp->ptr = NULL; - fp->end = NULL; + fp->bufpos = lseek(fp->fd, pos, SEEK_SET); + fp->pos = fp->bufpos; + fp->ptr = NULL; + fp->end = NULL; + + DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT, + CUPS_LLCAST fp->pos)); } } - else if (pos >= (fp->pos + bytes)) + else { /* * Need to seek forwards... */ + DEBUG_puts("2cupsFileSeek: SEEK FORWARDS"); + #ifdef HAVE_LIBZ - if (fp->compressed || !fp->ptr) + if (fp->compressed) { while ((bytes = cups_fill(fp)) > 0) - if (pos >= fp->pos && pos < (fp->pos + bytes)) + { + if (pos >= fp->bufpos && pos < (fp->bufpos + bytes)) break; + } if (bytes <= 0) return (-1); + + fp->ptr = fp->buf + pos - fp->bufpos; + fp->pos = pos; } else #endif /* HAVE_LIBZ */ { - fp->pos = lseek(fp->fd, pos, SEEK_SET); - DEBUG_printf((" lseek() returned " CUPS_LLFMT "...\n", fp->pos)); - fp->ptr = NULL; - fp->end = NULL; + fp->bufpos = lseek(fp->fd, pos, SEEK_SET); + fp->pos = fp->bufpos; + fp->ptr = NULL; + fp->end = NULL; + + DEBUG_printf(("2cupsFileSeek: lseek() returned " CUPS_LLFMT, + CUPS_LLCAST fp->pos)); } } - else + + DEBUG_printf(("2cupsFileSeek: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + + return (fp->pos); +} + + +/* + * 'cupsFileStderr()' - Return a CUPS file associated with stderr. + * + * @since CUPS 1.2/Mac OS X 10.5@ + */ + +cups_file_t * /* O - CUPS file */ +cupsFileStderr(void) +{ + _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */ + + + /* + * Open file descriptor 2 as needed... + */ + + if (!cg->stdio_files[2]) { /* - * Just reposition the current pointer, since we have the right - * range... + * Flush any pending output on the stdio file... + */ + + fflush(stderr); + + /* + * Open file descriptor 2... */ - fp->ptr = fp->buf + pos - fp->pos; - DEBUG_puts((" seek inside buffer...")); + if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL) + cg->stdio_files[2]->is_stdio = 1; } - return (fp->pos); + return (cg->stdio_files[2]); +} + + +/* + * 'cupsFileStdin()' - Return a CUPS file associated with stdin. + * + * @since CUPS 1.2/Mac OS X 10.5@ + */ + +cups_file_t * /* O - CUPS file */ +cupsFileStdin(void) +{ + _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */ + + + /* + * Open file descriptor 0 as needed... + */ + + if (!cg->stdio_files[0]) + { + /* + * Open file descriptor 0... + */ + + if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL) + cg->stdio_files[0]->is_stdio = 1; + } + + return (cg->stdio_files[0]); +} + + +/* + * 'cupsFileStdout()' - Return a CUPS file associated with stdout. + * + * @since CUPS 1.2/Mac OS X 10.5@ + */ + +cups_file_t * /* O - CUPS file */ +cupsFileStdout(void) +{ + _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals... */ + + + /* + * Open file descriptor 1 as needed... + */ + + if (!cg->stdio_files[1]) + { + /* + * Flush any pending output on the stdio file... + */ + + fflush(stdout); + + /* + * Open file descriptor 1... + */ + + if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL) + cg->stdio_files[1]->is_stdio = 1; + } + + return (cg->stdio_files[1]); } /* * 'cupsFileTell()' - Return the current file position. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ off_t /* O - File position */ cupsFileTell(cups_file_t *fp) /* I - CUPS file */ { - return (fp->pos); + DEBUG_printf(("2cupsFileTell(fp=%p)", fp)); + DEBUG_printf(("3cupsFileTell: pos=" CUPS_LLFMT, + CUPS_LLCAST (fp ? fp->pos : -1))); + + return (fp ? fp->pos : 0); } /* * 'cupsFileUnlock()' - Unlock access to a file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ int /* O - 0 on success, -1 on error */ -cupsFileUnlock(cups_file_t *fp) /* I - File to lock */ +cupsFileUnlock(cups_file_t *fp) /* I - CUPS file */ { /* * Range check... */ + DEBUG_printf(("cupsFileUnlock(fp=%p)", fp)); + if (!fp || fp->mode == 's') return (-1); @@ -1290,7 +2053,7 @@ cupsFileUnlock(cups_file_t *fp) /* I - File to lock */ */ #ifdef WIN32 - return (locking(fp->fd, _LK_UNLCK, 0)); + return (_locking(fp->fd, _LK_UNLCK, 0)); #else return (lockf(fp->fd, F_ULOCK, 0)); #endif /* WIN32 */ @@ -1299,9 +2062,11 @@ cupsFileUnlock(cups_file_t *fp) /* I - File to lock */ /* * 'cupsFileWrite()' - Write to a file. + * + * @since CUPS 1.2/Mac OS X 10.5@ */ -ssize_t /* O - Number of bytes written */ +ssize_t /* O - Number of bytes written or -1 on error */ cupsFileWrite(cups_file_t *fp, /* I - CUPS file */ const char *buf, /* I - Buffer */ size_t bytes) /* I - Number of bytes to write */ @@ -1310,7 +2075,10 @@ cupsFileWrite(cups_file_t *fp, /* I - CUPS file */ * Range check input... */ - if (!fp || !buf || bytes < 0 || (fp->mode != 'w' && fp->mode != 's')) + DEBUG_printf(("2cupsFileWrite(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", + fp, buf, CUPS_LLCAST bytes)); + + if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's')) return (-1); if (bytes == 0) @@ -1325,16 +2093,20 @@ cupsFileWrite(cups_file_t *fp, /* I - CUPS file */ if (cups_write(fp, buf, bytes) < 0) return (-1); - fp->pos += bytes; + fp->pos += (off_t)bytes; - return (bytes); + DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); + + return ((ssize_t)bytes); } if ((fp->ptr + bytes) > fp->end) if (cupsFileFlush(fp)) return (-1); - fp->pos += bytes; + fp->pos += (off_t)bytes; + + DEBUG_printf(("4cupsFileWrite: pos=" CUPS_LLFMT, CUPS_LLCAST fp->pos)); if (bytes > sizeof(fp->buf)) { @@ -1349,14 +2121,14 @@ cupsFileWrite(cups_file_t *fp, /* I - CUPS file */ { memcpy(fp->ptr, buf, bytes); fp->ptr += bytes; - return (bytes); + return ((ssize_t)bytes); } } #ifdef HAVE_LIBZ /* - * 'cups_compress()' - Compress a buffer of data... + * 'cups_compress()' - Compress a buffer of data. */ static ssize_t /* O - Number of bytes written or -1 */ @@ -1364,8 +2136,8 @@ cups_compress(cups_file_t *fp, /* I - CUPS file */ const char *buf, /* I - Buffer */ size_t bytes) /* I - Number bytes */ { - DEBUG_printf(("cups_compress(fp=%p, buf=%p, bytes=%ld)\n", fp, buf, - (long)bytes)); + DEBUG_printf(("7cups_compress(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf, + CUPS_LLCAST bytes)); /* * Update the CRC... @@ -1386,8 +2158,8 @@ cups_compress(cups_file_t *fp, /* I - CUPS file */ * Flush the current buffer... */ - DEBUG_printf((" avail_in=%d, avail_out=%d\n", fp->stream.avail_in, - fp->stream.avail_out)); + DEBUG_printf(("9cups_compress: avail_in=%d, avail_out=%d", + fp->stream.avail_in, fp->stream.avail_out)); if (fp->stream.avail_out < (int)(sizeof(fp->cbuf) / 8)) { @@ -1407,7 +2179,7 @@ cups_compress(cups_file_t *fp, /* I - CUPS file */ /* - * 'cups_fill()' - Fill the input buffer... + * 'cups_fill()' - Fill the input buffer. */ static ssize_t /* O - Number of bytes or -1 */ @@ -1415,25 +2187,22 @@ cups_fill(cups_file_t *fp) /* I - CUPS file */ { ssize_t bytes; /* Number of bytes read */ #ifdef HAVE_LIBZ + int status; /* Decompression status */ const unsigned char *ptr, /* Pointer into buffer */ *end; /* End of buffer */ #endif /* HAVE_LIBZ */ - DEBUG_printf(("cups_fill(fp=%p)\n", fp)); - DEBUG_printf((" fp->ptr=%p, fp->end=%p, fp->buf=%p, " - "fp->pos=" CUPS_LLFMT ", fp->eof=%d\n", - fp->ptr, fp->end, fp->buf, fp->pos, fp->eof)); - - /* - * Update the "pos" element as needed... - */ + DEBUG_printf(("7cups_fill(fp=%p)", fp)); + DEBUG_printf(("9cups_fill: fp->ptr=%p, fp->end=%p, fp->buf=%p, " + "fp->bufpos=" CUPS_LLFMT ", fp->eof=%d", + fp->ptr, fp->end, fp->buf, CUPS_LLCAST fp->bufpos, fp->eof)); if (fp->ptr && fp->end) - fp->pos += fp->end - fp->buf; + fp->bufpos += fp->end - fp->buf; #ifdef HAVE_LIBZ - DEBUG_printf((" fp->compressed=%d\n", fp->compressed)); + DEBUG_printf(("9cups_fill: fp->compressed=%d", fp->compressed)); while (!fp->ptr || fp->compressed) { @@ -1461,7 +2230,7 @@ cups_fill(cups_file_t *fp) /* I - CUPS file */ * Can't read from file! */ - DEBUG_printf((" cups_read() returned " CUPS_LLFMT "!\n", + DEBUG_printf(("9cups_fill: cups_read() returned " CUPS_LLFMT, CUPS_LLCAST bytes)); return (-1); @@ -1478,7 +2247,8 @@ cups_fill(cups_file_t *fp) /* I - CUPS file */ fp->ptr = fp->buf; fp->end = fp->buf + bytes; - DEBUG_printf((" returning " CUPS_LLFMT "!\n", CUPS_LLCAST bytes)); + DEBUG_printf(("9cups_fill: Returning " CUPS_LLFMT, + CUPS_LLCAST bytes)); return (bytes); } @@ -1633,7 +2403,13 @@ cups_fill(cups_file_t *fp) /* I - CUPS file */ fp->stream.next_out = (Bytef *)fp->buf; fp->stream.avail_out = sizeof(fp->buf); - if (inflate(&(fp->stream), Z_NO_FLUSH) == Z_STREAM_END) + status = inflate(&(fp->stream), Z_NO_FLUSH); + + if (fp->stream.next_out > (Bytef *)fp->buf) + fp->crc = crc32(fp->crc, (Bytef *)fp->buf, + fp->stream.next_out - (Bytef *)fp->buf); + + if (status == Z_STREAM_END) { /* * Read the CRC and length... @@ -1662,6 +2438,9 @@ cups_fill(cups_file_t *fp) /* I - CUPS file */ * Bad CRC, mark end-of-file... */ + DEBUG_printf(("9cups_fill: tcrc=%08x, fp->crc=%08x", + (unsigned int)tcrc, (unsigned int)fp->crc)); + fp->eof = 1; return (-1); @@ -1720,6 +2499,94 @@ cups_fill(cups_file_t *fp) /* I - CUPS file */ } +/* + * 'cups_open()' - Safely open a file for writing. + * + * We don't allow appending to directories or files that are hard-linked or + * symlinked. + */ + +static int /* O - File descriptor or -1 otherwise */ +cups_open(const char *filename, /* I - Filename */ + int mode) /* I - Open mode */ +{ + int fd; /* File descriptor */ + struct stat fileinfo; /* File information */ +#ifndef WIN32 + struct stat linkinfo; /* Link information */ +#endif /* !WIN32 */ + + + /* + * Open the file... + */ + + if ((fd = open(filename, mode, 0666)) < 0) + return (-1); + + /* + * Then verify that the file descriptor doesn't point to a directory or hard- + * linked file. + */ + + if (fstat(fd, &fileinfo)) + { + close(fd); + return (-1); + } + + if (fileinfo.st_nlink != 1) + { + close(fd); + errno = EPERM; + return (-1); + } + +#ifdef WIN32 + if (fileinfo.st_mode & _S_IFDIR) +#else + if (S_ISDIR(fileinfo.st_mode)) +#endif /* WIN32 */ + { + close(fd); + errno = EISDIR; + return (-1); + } + +#ifndef WIN32 + /* + * Then use lstat to determine whether the filename is a symlink... + */ + + if (lstat(filename, &linkinfo)) + { + close(fd); + return (-1); + } + + if (S_ISLNK(linkinfo.st_mode) || + fileinfo.st_dev != linkinfo.st_dev || + fileinfo.st_ino != linkinfo.st_ino || +#ifdef HAVE_ST_GEN + fileinfo.st_gen != linkinfo.st_gen || +#endif /* HAVE_ST_GEN */ + fileinfo.st_nlink != linkinfo.st_nlink || + fileinfo.st_mode != linkinfo.st_mode) + { + /* + * Yes, don't allow! + */ + + close(fd); + errno = EPERM; + return (-1); + } +#endif /* !WIN32 */ + + return (fd); +} + + /* * 'cups_read()' - Read from a file descriptor. */ @@ -1732,16 +2599,28 @@ cups_read(cups_file_t *fp, /* I - CUPS file */ ssize_t total; /* Total bytes read */ + DEBUG_printf(("7cups_read(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf, + CUPS_LLCAST bytes)); + /* * Loop until we read at least 0 bytes... */ for (;;) { +#ifdef WIN32 + if (fp->mode == 's') + total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0); + else + total = (ssize_t)read(fp->fd, buf, (unsigned)bytes); +#else if (fp->mode == 's') total = recv(fp->fd, buf, bytes, 0); else total = read(fp->fd, buf, bytes); +#endif /* WIN32 */ + + DEBUG_printf(("9cups_read: total=" CUPS_LLFMT, CUPS_LLCAST total)); if (total >= 0) break; @@ -1773,12 +2652,12 @@ cups_write(cups_file_t *fp, /* I - CUPS file */ const char *buf, /* I - Buffer */ size_t bytes) /* I - Number bytes */ { - size_t total, /* Total bytes written */ - count; /* Count this time */ + size_t total; /* Total bytes written */ + ssize_t count; /* Count this time */ - DEBUG_printf(("cups_write(fp=%p, buf=%p, bytes=%ld)\n", fp, buf, - (long)bytes)); + DEBUG_printf(("7cups_write(fp=%p, buf=%p, bytes=" CUPS_LLFMT ")", fp, buf, + CUPS_LLCAST bytes)); /* * Loop until all bytes are written... @@ -1787,10 +2666,19 @@ cups_write(cups_file_t *fp, /* I - CUPS file */ total = 0; while (bytes > 0) { +#ifdef WIN32 + if (fp->mode == 's') + count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0); + else + count = (ssize_t)write(fp->fd, buf, (unsigned)bytes); +#else if (fp->mode == 's') count = send(fp->fd, buf, bytes, 0); else count = write(fp->fd, buf, bytes); +#endif /* WIN32 */ + + DEBUG_printf(("9cups_write: count=" CUPS_LLFMT, CUPS_LLCAST count)); if (count < 0) { @@ -1804,8 +2692,6 @@ cups_write(cups_file_t *fp, /* I - CUPS file */ return (-1); } - DEBUG_printf((" count=%ld\n", (long)count)); - /* * Update the counts for the last write call... */ @@ -1819,10 +2705,10 @@ cups_write(cups_file_t *fp, /* I - CUPS file */ * Return the total number of bytes written... */ - return (total); + return ((ssize_t)total); } /* - * End of "$Id: file.c 5186 2006-02-26 18:56:05Z mike $". + * End of "$Id: file.c 7672 2008-06-18 22:03:02Z mike $". */