| /* |
| * xmlIO.c : implementation of the I/O interfaces used by the parser |
| * |
| * See Copyright for the status of this software. |
| * |
| * daniel@veillard.com |
| * |
| * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char |
| */ |
| |
| #define IN_LIBXML |
| #include "libxml.h" |
| |
| #include <string.h> |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #ifdef HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| #ifdef HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #ifdef HAVE_ZLIB_H |
| #include <zlib.h> |
| #endif |
| |
| /* Figure a portable way to know if a file is a directory. */ |
| #ifndef HAVE_STAT |
| # ifdef HAVE__STAT |
| /* MS C library seems to define stat and _stat. The definition |
| is identical. Still, mapping them to each other causes a warning. */ |
| # ifndef _MSC_VER |
| # define stat(x,y) _stat(x,y) |
| # endif |
| # define HAVE_STAT |
| # endif |
| #endif |
| #ifdef HAVE_STAT |
| # ifndef S_ISDIR |
| # ifdef _S_ISDIR |
| # define S_ISDIR(x) _S_ISDIR(x) |
| # else |
| # ifdef S_IFDIR |
| # ifndef S_IFMT |
| # ifdef _S_IFMT |
| # define S_IFMT _S_IFMT |
| # endif |
| # endif |
| # ifdef S_IFMT |
| # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) |
| # endif |
| # endif |
| # endif |
| # endif |
| #endif |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/parser.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/xmlIO.h> |
| #include <libxml/uri.h> |
| #include <libxml/nanohttp.h> |
| #include <libxml/nanoftp.h> |
| #include <libxml/xmlerror.h> |
| #ifdef LIBXML_CATALOG_ENABLED |
| #include <libxml/catalog.h> |
| #endif |
| #include <libxml/globals.h> |
| |
| /* #define VERBOSE_FAILURE */ |
| /* #define DEBUG_EXTERNAL_ENTITIES */ |
| /* #define DEBUG_INPUT */ |
| |
| #ifdef DEBUG_INPUT |
| #define MINLEN 40 |
| #else |
| #define MINLEN 4000 |
| #endif |
| |
| /* |
| * Input I/O callback sets |
| */ |
| typedef struct _xmlInputCallback { |
| xmlInputMatchCallback matchcallback; |
| xmlInputOpenCallback opencallback; |
| xmlInputReadCallback readcallback; |
| xmlInputCloseCallback closecallback; |
| } xmlInputCallback; |
| |
| #define MAX_INPUT_CALLBACK 15 |
| |
| static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK]; |
| static int xmlInputCallbackNr = 0; |
| static int xmlInputCallbackInitialized = 0; |
| |
| /* |
| * Output I/O callback sets |
| */ |
| typedef struct _xmlOutputCallback { |
| xmlOutputMatchCallback matchcallback; |
| xmlOutputOpenCallback opencallback; |
| xmlOutputWriteCallback writecallback; |
| xmlOutputCloseCallback closecallback; |
| } xmlOutputCallback; |
| |
| #define MAX_OUTPUT_CALLBACK 15 |
| |
| static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK]; |
| static int xmlOutputCallbackNr = 0; |
| static int xmlOutputCallbackInitialized = 0; |
| |
| |
| /** |
| * xmlNormalizeWindowsPath: |
| * @path: the input file path |
| * |
| * This function is obsolete. Please see xmlURIFromPath in uri.c for |
| * a better solution. |
| * |
| * Returns a canonicalized version of the path |
| */ |
| xmlChar * |
| xmlNormalizeWindowsPath(const xmlChar *path) |
| { |
| return xmlCanonicPath(path); |
| } |
| |
| /** |
| * xmlCleanupInputCallbacks: |
| * |
| * clears the entire input callback table. this includes the |
| * compiled-in I/O. |
| */ |
| void |
| xmlCleanupInputCallbacks(void) |
| { |
| int i; |
| |
| if (!xmlInputCallbackInitialized) |
| return; |
| |
| for (i = xmlInputCallbackNr - 1; i >= 0; i--) { |
| xmlInputCallbackTable[i].matchcallback = NULL; |
| xmlInputCallbackTable[i].opencallback = NULL; |
| xmlInputCallbackTable[i].readcallback = NULL; |
| xmlInputCallbackTable[i].closecallback = NULL; |
| } |
| xmlInputCallbackInitialized = 0; |
| |
| xmlInputCallbackNr = 0; |
| xmlInputCallbackInitialized = 0; |
| } |
| |
| /** |
| * xmlCleanupOutputCallbacks: |
| * |
| * clears the entire output callback table. this includes the |
| * compiled-in I/O callbacks. |
| */ |
| void |
| xmlCleanupOutputCallbacks(void) |
| { |
| int i; |
| |
| if (!xmlOutputCallbackInitialized) |
| return; |
| |
| for (i = xmlOutputCallbackNr - 1; i >= 0; i--) { |
| xmlOutputCallbackTable[i].matchcallback = NULL; |
| xmlOutputCallbackTable[i].opencallback = NULL; |
| xmlOutputCallbackTable[i].writecallback = NULL; |
| xmlOutputCallbackTable[i].closecallback = NULL; |
| } |
| xmlOutputCallbackInitialized = 0; |
| |
| xmlOutputCallbackNr = 0; |
| xmlOutputCallbackInitialized = 0; |
| } |
| |
| /************************************************************************ |
| * * |
| * Standard I/O for file accesses * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlCheckFilename: |
| * @path: the path to check |
| * |
| * function checks to see if @path is a valid source |
| * (file, socket...) for XML. |
| * |
| * if stat is not available on the target machine, |
| * returns 1. if stat fails, returns 0 (if calling |
| * stat on the filename fails, it can't be right). |
| * if stat succeeds and the file is a directory, |
| * returns 2. otherwise returns 1. |
| */ |
| |
| int |
| xmlCheckFilename (const char *path) |
| { |
| #ifdef HAVE_STAT |
| struct stat stat_buffer; |
| |
| if (stat(path, &stat_buffer) == -1) |
| return 0; |
| |
| #ifdef S_ISDIR |
| if (S_ISDIR(stat_buffer.st_mode)) { |
| return 2; |
| } |
| #endif |
| #endif |
| return 1; |
| } |
| |
| static int |
| xmlNop(void) { |
| return(0); |
| } |
| |
| /** |
| * xmlFdRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to read |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| static int |
| xmlFdRead (void * context, char * buffer, int len) { |
| return(read((int) (long) context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlFdWrite: |
| * @context: the I/O context |
| * @buffer: where to get data |
| * @len: number of bytes to write |
| * |
| * Write @len bytes from @buffer to the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| static int |
| xmlFdWrite (void * context, const char * buffer, int len) { |
| return(write((int) (long) context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlFdClose: |
| * @context: the I/O context |
| * |
| * Close an I/O channel |
| * |
| * Returns 0 in case of success and error code otherwise |
| */ |
| static int |
| xmlFdClose (void * context) { |
| return ( close((int) (long) context) ); |
| } |
| |
| /** |
| * xmlFileMatch: |
| * @filename: the URI for matching |
| * |
| * input from FILE * |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) { |
| return(1); |
| } |
| |
| /** |
| * xmlFileOpen_real: |
| * @filename: the URI for matching |
| * |
| * input from FILE *, supports compressed input |
| * if @filename is " " then the standard input is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| static void * |
| xmlFileOpen_real (const char *filename) { |
| const char *path = NULL; |
| FILE *fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = stdin; |
| return((void *) fd); |
| } |
| |
| if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[17]; |
| #else |
| path = &filename[16]; |
| #endif |
| else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[8]; |
| #else |
| path = &filename[7]; |
| #endif |
| } else |
| path = filename; |
| |
| if (path == NULL) |
| return(NULL); |
| if (!xmlCheckFilename(path)) |
| return(NULL); |
| |
| #if defined(WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__) |
| fd = fopen(path, "rb"); |
| #else |
| fd = fopen(path, "r"); |
| #endif /* WIN32 */ |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlFileOpen: |
| * @filename: the URI for matching |
| * |
| * Wrapper around xmlFileOpen_real that try it with an unescaped |
| * version of @filename, if this fails fallback to @filename |
| * |
| * Returns a handler or NULL in case or failure |
| */ |
| void * |
| xmlFileOpen (const char *filename) { |
| char *unescaped; |
| void *retval; |
| unescaped = xmlURIUnescapeString(filename, 0, NULL); |
| if (unescaped != NULL) { |
| retval = xmlFileOpen_real(unescaped); |
| } else { |
| retval = xmlFileOpen_real(filename); |
| } |
| xmlFree(unescaped); |
| return retval; |
| } |
| |
| /** |
| * xmlFileOpenW: |
| * @filename: the URI for matching |
| * |
| * output to from FILE *, |
| * if @filename is "-" then the standard output is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| static void * |
| xmlFileOpenW (const char *filename) { |
| const char *path = NULL; |
| FILE *fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = stdout; |
| return((void *) fd); |
| } |
| |
| if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[17]; |
| #else |
| path = &filename[16]; |
| #endif |
| else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[8]; |
| #else |
| path = &filename[7]; |
| #endif |
| } else |
| path = filename; |
| |
| if (path == NULL) |
| return(NULL); |
| |
| fd = fopen(path, "wb"); |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlFileRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlFileRead (void * context, char * buffer, int len) { |
| return(fread(&buffer[0], 1, len, (FILE *) context)); |
| } |
| |
| /** |
| * xmlFileWrite: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Write @len bytes from @buffer to the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| static int |
| xmlFileWrite (void * context, const char * buffer, int len) { |
| int items; |
| |
| items = fwrite(&buffer[0], len, 1, (FILE *) context); |
| |
| return(items * len); |
| } |
| |
| /** |
| * xmlFileClose: |
| * @context: the I/O context |
| * |
| * Close an I/O channel |
| * |
| * Returns 0 or -1 in case of error |
| */ |
| int |
| xmlFileClose (void * context) { |
| FILE *fil; |
| |
| fil = (FILE *) context; |
| if (fil == stdin) |
| return(0); |
| if (fil == stdout) |
| return(0); |
| if (fil == stderr) |
| return(0); |
| return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 ); |
| } |
| |
| /** |
| * xmlFileFlush: |
| * @context: the I/O context |
| * |
| * Flush an I/O channel |
| */ |
| static int |
| xmlFileFlush (void * context) { |
| return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 ); |
| } |
| |
| #ifdef HAVE_ZLIB_H |
| /************************************************************************ |
| * * |
| * I/O for compressed file accesses * |
| * * |
| ************************************************************************/ |
| /** |
| * xmlGzfileMatch: |
| * @filename: the URI for matching |
| * |
| * input from compressed file test |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| static int |
| xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) { |
| return(1); |
| } |
| |
| /** |
| * xmlGzfileOpen_real: |
| * @filename: the URI for matching |
| * |
| * input from compressed file open |
| * if @filename is " " then the standard input is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| static void * |
| xmlGzfileOpen_real (const char *filename) { |
| const char *path = NULL; |
| gzFile fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = gzdopen(dup(0), "rb"); |
| return((void *) fd); |
| } |
| |
| if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[17]; |
| #else |
| path = &filename[16]; |
| #endif |
| else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[8]; |
| #else |
| path = &filename[7]; |
| #endif |
| } else |
| path = filename; |
| |
| if (path == NULL) |
| return(NULL); |
| if (!xmlCheckFilename(path)) |
| return(NULL); |
| |
| fd = gzopen(path, "rb"); |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlGzfileOpen: |
| * @filename: the URI for matching |
| * |
| * Wrapper around xmlGzfileOpen if the open fais, it will |
| * try to unescape @filename |
| */ |
| static void * |
| xmlGzfileOpen (const char *filename) { |
| char *unescaped; |
| void *retval; |
| |
| retval = xmlGzfileOpen_real(filename); |
| if (retval == NULL) { |
| unescaped = xmlURIUnescapeString(filename, 0, NULL); |
| if (unescaped != NULL) { |
| retval = xmlGzfileOpen_real(unescaped); |
| } |
| xmlFree(unescaped); |
| } |
| return retval; |
| } |
| |
| /** |
| * xmlGzfileOpenW: |
| * @filename: the URI for matching |
| * @compression: the compression factor (0 - 9 included) |
| * |
| * input from compressed file open |
| * if @filename is " " then the standard input is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| static void * |
| xmlGzfileOpenW (const char *filename, int compression) { |
| const char *path = NULL; |
| char mode[15]; |
| gzFile fd; |
| |
| snprintf(mode, sizeof(mode), "wb%d", compression); |
| if (!strcmp(filename, "-")) { |
| fd = gzdopen(dup(1), mode); |
| return((void *) fd); |
| } |
| |
| if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[17]; |
| #else |
| path = &filename[16]; |
| #endif |
| else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &filename[8]; |
| #else |
| path = &filename[7]; |
| #endif |
| } else |
| path = filename; |
| |
| if (path == NULL) |
| return(NULL); |
| |
| fd = gzopen(path, mode); |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlGzfileRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the compressed I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| static int |
| xmlGzfileRead (void * context, char * buffer, int len) { |
| return(gzread((gzFile) context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlGzfileWrite: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Write @len bytes from @buffer to the compressed I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| static int |
| xmlGzfileWrite (void * context, const char * buffer, int len) { |
| return(gzwrite((gzFile) context, (char *) &buffer[0], len)); |
| } |
| |
| /** |
| * xmlGzfileClose: |
| * @context: the I/O context |
| * |
| * Close a compressed I/O channel |
| */ |
| static int |
| xmlGzfileClose (void * context) { |
| return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 ); |
| } |
| #endif /* HAVE_ZLIB_H */ |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| /************************************************************************ |
| * * |
| * I/O for HTTP file accesses * |
| * * |
| ************************************************************************/ |
| |
| typedef struct xmlIOHTTPWriteCtxt_ |
| { |
| int compression; |
| |
| char * uri; |
| |
| void * doc_buff; |
| |
| } xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr; |
| |
| #ifdef HAVE_ZLIB_H |
| |
| #define DFLT_WBITS ( -15 ) |
| #define DFLT_MEM_LVL ( 8 ) |
| #define GZ_MAGIC1 ( 0x1f ) |
| #define GZ_MAGIC2 ( 0x8b ) |
| #define LXML_ZLIB_OS_CODE ( 0x03 ) |
| #define INIT_HTTP_BUFF_SIZE ( 32768 ) |
| #define DFLT_ZLIB_RATIO ( 5 ) |
| |
| /* |
| ** Data structure and functions to work with sending compressed data |
| ** via HTTP. |
| */ |
| |
| typedef struct xmlZMemBuff_ |
| { |
| unsigned long size; |
| unsigned long crc; |
| |
| unsigned char * zbuff; |
| z_stream zctrl; |
| |
| } xmlZMemBuff, *xmlZMemBuffPtr; |
| |
| /** |
| * append_reverse_ulong |
| * @buff: Compressed memory buffer |
| * @data: Unsigned long to append |
| * |
| * Append a unsigned long in reverse byte order to the end of the |
| * memory buffer. |
| */ |
| static void |
| append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) { |
| |
| int idx; |
| |
| if ( buff == NULL ) |
| return; |
| |
| /* |
| ** This is plagiarized from putLong in gzio.c (zlib source) where |
| ** the number "4" is hardcoded. If zlib is ever patched to |
| ** support 64 bit file sizes, this code would need to be patched |
| ** as well. |
| */ |
| |
| for ( idx = 0; idx < 4; idx++ ) { |
| *buff->zctrl.next_out = ( data & 0xff ); |
| data >>= 8; |
| buff->zctrl.next_out++; |
| } |
| |
| return; |
| } |
| |
| /** |
| * |
| * xmlFreeZMemBuff |
| * @buff: The memory buffer context to clear |
| * |
| * Release all the resources associated with the compressed memory buffer. |
| */ |
| static void |
| xmlFreeZMemBuff( xmlZMemBuffPtr buff ) { |
| |
| #ifdef DEBUG_HTTP |
| int z_err; |
| #endif |
| |
| if ( buff == NULL ) |
| return; |
| |
| xmlFree( buff->zbuff ); |
| #ifdef DEBUG_HTTP |
| z_err = deflateEnd( &buff->zctrl ); |
| if ( z_err != Z_OK ) |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlFreeZMemBuff: Error releasing zlib context: %d\n", |
| z_err ); |
| #else |
| deflateEnd( &buff->zctrl ); |
| #endif |
| |
| xmlFree( buff ); |
| return; |
| } |
| |
| /** |
| * xmlCreateZMemBuff |
| *@compression: Compression value to use |
| * |
| * Create a memory buffer to hold the compressed XML document. The |
| * compressed document in memory will end up being identical to what |
| * would be created if gzopen/gzwrite/gzclose were being used to |
| * write the document to disk. The code for the header/trailer data to |
| * the compression is plagiarized from the zlib source files. |
| */ |
| static void * |
| xmlCreateZMemBuff( int compression ) { |
| |
| int z_err; |
| int hdr_lgth; |
| xmlZMemBuffPtr buff = NULL; |
| |
| if ( ( compression < 1 ) || ( compression > 9 ) ) |
| return ( NULL ); |
| |
| /* Create the control and data areas */ |
| |
| buff = xmlMalloc( sizeof( xmlZMemBuff ) ); |
| if ( buff == NULL ) { |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlCreateZMemBuff: %s\n", |
| "Failure allocating buffer context." ); |
| return ( NULL ); |
| } |
| |
| (void)memset( buff, 0, sizeof( xmlZMemBuff ) ); |
| buff->size = INIT_HTTP_BUFF_SIZE; |
| buff->zbuff = xmlMalloc( buff->size ); |
| if ( buff->zbuff == NULL ) { |
| xmlFreeZMemBuff( buff ); |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlCreateZMemBuff: %s\n", |
| "Failure allocating data buffer." ); |
| return ( NULL ); |
| } |
| |
| z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED, |
| DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY ); |
| if ( z_err != Z_OK ) { |
| xmlFreeZMemBuff( buff ); |
| buff = NULL; |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlCreateZMemBuff: %s %d\n", |
| "Error initializing compression context. ZLIB error:", |
| z_err ); |
| return ( NULL ); |
| } |
| |
| /* Set the header data. The CRC will be needed for the trailer */ |
| buff->crc = crc32( 0L, Z_NULL, 0 ); |
| hdr_lgth = snprintf( (char *)buff->zbuff, buff->size, |
| "%c%c%c%c%c%c%c%c%c%c", |
| GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED, |
| 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE ); |
| buff->zctrl.next_out = buff->zbuff + hdr_lgth; |
| buff->zctrl.avail_out = buff->size - hdr_lgth; |
| |
| return ( buff ); |
| } |
| |
| /** |
| * xmlZMemBuffExtend |
| * @buff: Buffer used to compress and consolidate data. |
| * @ext_amt: Number of bytes to extend the buffer. |
| * |
| * Extend the internal buffer used to store the compressed data by the |
| * specified amount. |
| * |
| * Returns 0 on success or -1 on failure to extend the buffer. On failure |
| * the original buffer still exists at the original size. |
| */ |
| static int |
| xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) { |
| |
| int rc = -1; |
| size_t new_size; |
| size_t cur_used; |
| |
| unsigned char * tmp_ptr = NULL; |
| |
| if ( buff == NULL ) |
| return ( -1 ); |
| |
| else if ( ext_amt == 0 ) |
| return ( 0 ); |
| |
| cur_used = buff->zctrl.next_out - buff->zbuff; |
| new_size = buff->size + ext_amt; |
| |
| #ifdef DEBUG_HTTP |
| if ( cur_used > new_size ) |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlZMemBuffExtend: %s\n%s %d bytes.\n", |
| "Buffer overwrite detected during compressed memory", |
| "buffer extension. Overflowed by", |
| (cur_used - new_size ) ); |
| #endif |
| |
| tmp_ptr = xmlRealloc( buff->zbuff, new_size ); |
| if ( tmp_ptr != NULL ) { |
| rc = 0; |
| buff->size = new_size; |
| buff->zbuff = tmp_ptr; |
| buff->zctrl.next_out = tmp_ptr + cur_used; |
| buff->zctrl.avail_out = new_size - cur_used; |
| } |
| else { |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlZMemBuffExtend: %s %lu bytes.\n", |
| "Allocation failure extending output buffer to", |
| new_size ); |
| } |
| |
| return ( rc ); |
| } |
| |
| /** |
| * xmlZMemBuffAppend |
| * @buff: Buffer used to compress and consolidate data |
| * @src: Uncompressed source content to append to buffer |
| * @len: Length of source data to append to buffer |
| * |
| * Compress and append data to the internal buffer. The data buffer |
| * will be expanded if needed to store the additional data. |
| * |
| * Returns the number of bytes appended to the buffer or -1 on error. |
| */ |
| static int |
| xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) { |
| |
| int z_err; |
| size_t min_accept; |
| |
| if ( ( buff == NULL ) || ( src == NULL ) ) |
| return ( -1 ); |
| |
| buff->zctrl.avail_in = len; |
| buff->zctrl.next_in = (unsigned char *)src; |
| while ( buff->zctrl.avail_in > 0 ) { |
| /* |
| ** Extend the buffer prior to deflate call if a reasonable amount |
| ** of output buffer space is not available. |
| */ |
| min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO; |
| if ( buff->zctrl.avail_out <= min_accept ) { |
| if ( xmlZMemBuffExtend( buff, buff->size ) == -1 ) |
| return ( -1 ); |
| } |
| |
| z_err = deflate( &buff->zctrl, Z_NO_FLUSH ); |
| if ( z_err != Z_OK ) { |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlZMemBuffAppend: %s %d %s - %d", |
| "Compression error while appending", |
| len, "bytes to buffer. ZLIB error", z_err ); |
| return ( -1 ); |
| } |
| } |
| |
| buff->crc = crc32( buff->crc, (unsigned char *)src, len ); |
| |
| return ( len ); |
| } |
| |
| /** |
| * xmlZMemBuffGetContent |
| * @buff: Compressed memory content buffer |
| * @data_ref: Pointer reference to point to compressed content |
| * |
| * Flushes the compression buffers, appends gzip file trailers and |
| * returns the compressed content and length of the compressed data. |
| * NOTE: The gzip trailer code here is plagiarized from zlib source. |
| * |
| * Returns the length of the compressed data or -1 on error. |
| */ |
| static int |
| xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) { |
| |
| int zlgth = -1; |
| int z_err; |
| |
| if ( ( buff == NULL ) || ( data_ref == NULL ) ) |
| return ( -1 ); |
| |
| /* Need to loop until compression output buffers are flushed */ |
| |
| do |
| { |
| z_err = deflate( &buff->zctrl, Z_FINISH ); |
| if ( z_err == Z_OK ) { |
| /* In this case Z_OK means more buffer space needed */ |
| |
| if ( xmlZMemBuffExtend( buff, buff->size ) == -1 ) |
| return ( -1 ); |
| } |
| } |
| while ( z_err == Z_OK ); |
| |
| /* If the compression state is not Z_STREAM_END, some error occurred */ |
| |
| if ( z_err == Z_STREAM_END ) { |
| |
| /* Need to append the gzip data trailer */ |
| |
| if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) { |
| if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 ) |
| return ( -1 ); |
| } |
| |
| /* |
| ** For whatever reason, the CRC and length data are pushed out |
| ** in reverse byte order. So a memcpy can't be used here. |
| */ |
| |
| append_reverse_ulong( buff, buff->crc ); |
| append_reverse_ulong( buff, buff->zctrl.total_in ); |
| |
| zlgth = buff->zctrl.next_out - buff->zbuff; |
| *data_ref = (char *)buff->zbuff; |
| } |
| |
| else |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlZMemBuffGetContent: %s - %d\n", |
| "Error flushing zlib buffers. Error code", z_err ); |
| |
| return ( zlgth ); |
| } |
| #endif /* HAVE_ZLIB_H */ |
| |
| /** |
| * xmlFreeHTTPWriteCtxt |
| * @ctxt: Context to cleanup |
| * |
| * Free allocated memory and reclaim system resources. |
| * |
| * No return value. |
| */ |
| static void |
| xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt ) |
| { |
| if ( ctxt->uri != NULL ) |
| xmlFree( ctxt->uri ); |
| |
| if ( ctxt->doc_buff != NULL ) { |
| |
| #ifdef HAVE_ZLIB_H |
| if ( ctxt->compression > 0 ) { |
| xmlFreeZMemBuff( ctxt->doc_buff ); |
| } |
| else |
| #endif |
| { |
| xmlOutputBufferClose( ctxt->doc_buff ); |
| } |
| } |
| |
| xmlFree( ctxt ); |
| return; |
| } |
| |
| |
| /** |
| * xmlIOHTTPMatch: |
| * @filename: the URI for matching |
| * |
| * check if the URI matches an HTTP one |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlIOHTTPMatch (const char *filename) { |
| if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7)) |
| return(1); |
| return(0); |
| } |
| |
| /** |
| * xmlIOHTTPOpen: |
| * @filename: the URI for matching |
| * |
| * open an HTTP I/O channel |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlIOHTTPOpen (const char *filename) { |
| return(xmlNanoHTTPOpen(filename, NULL)); |
| } |
| |
| /** |
| * xmlIOHTTPOpenW: |
| * @post_uri: The destination URI for the document |
| * @compression: The compression desired for the document. |
| * |
| * Open a temporary buffer to collect the document for a subsequent HTTP POST |
| * request. Non-static as is called from the output buffer creation routine. |
| * |
| * Returns an I/O context or NULL in case of error. |
| */ |
| |
| void * |
| xmlIOHTTPOpenW(const char *post_uri, int compression) |
| { |
| |
| xmlIOHTTPWriteCtxtPtr ctxt = NULL; |
| |
| if (post_uri == NULL) |
| return (NULL); |
| |
| ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt)); |
| if (ctxt == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlIOHTTPOpenW: Failed to create output HTTP context.\n"); |
| return (NULL); |
| } |
| |
| (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt)); |
| |
| ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri); |
| if (ctxt->uri == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlIOHTTPOpenW: Failed to duplicate destination URI.\n"); |
| xmlFreeHTTPWriteCtxt(ctxt); |
| return (NULL); |
| } |
| |
| /* |
| * ** Since the document length is required for an HTTP post, |
| * ** need to put the document into a buffer. A memory buffer |
| * ** is being used to avoid pushing the data to disk and back. |
| */ |
| |
| #ifdef HAVE_ZLIB_H |
| if ((compression > 0) && (compression <= 9)) { |
| |
| ctxt->compression = compression; |
| ctxt->doc_buff = xmlCreateZMemBuff(compression); |
| } else |
| #endif |
| { |
| /* Any character conversions should have been done before this */ |
| |
| ctxt->doc_buff = xmlAllocOutputBuffer(NULL); |
| } |
| |
| if (ctxt->doc_buff == NULL) { |
| xmlFreeHTTPWriteCtxt(ctxt); |
| ctxt = NULL; |
| } |
| |
| return (ctxt); |
| } |
| |
| /** |
| * xmlIOHTTPDfltOpenW |
| * @post_uri: The destination URI for this document. |
| * |
| * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent |
| * HTTP post command. This function should generally not be used as |
| * the open callback is short circuited in xmlOutputBufferCreateFile. |
| * |
| * Returns a pointer to the new IO context. |
| */ |
| static void * |
| xmlIOHTTPDfltOpenW( const char * post_uri ) { |
| return ( xmlIOHTTPOpenW( post_uri, 0 ) ); |
| } |
| |
| /** |
| * xmlIOHTTPRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlIOHTTPRead(void * context, char * buffer, int len) { |
| return(xmlNanoHTTPRead(context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlIOHTTPWrite |
| * @context: previously opened writing context |
| * @buffer: data to output to temporary buffer |
| * @len: bytes to output |
| * |
| * Collect data from memory buffer into a temporary file for later |
| * processing. |
| * |
| * Returns number of bytes written. |
| */ |
| |
| static int |
| xmlIOHTTPWrite( void * context, const char * buffer, int len ) { |
| |
| xmlIOHTTPWriteCtxtPtr ctxt = context; |
| |
| if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) ) |
| return ( -1 ); |
| |
| if ( len > 0 ) { |
| |
| /* Use gzwrite or fwrite as previously setup in the open call */ |
| |
| #ifdef HAVE_ZLIB_H |
| if ( ctxt->compression > 0 ) |
| len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len ); |
| |
| else |
| #endif |
| len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer ); |
| |
| if ( len < 0 ) { |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlIOHTTPWrite: %s\n%s '%s'.\n", |
| "Error appending to internal buffer.", |
| "Error sending document to URI", |
| ctxt->uri ); |
| } |
| } |
| |
| return ( len ); |
| } |
| |
| |
| /** |
| * xmlIOHTTPClose: |
| * @context: the I/O context |
| * |
| * Close an HTTP I/O channel |
| * |
| * Returns 0 |
| */ |
| int |
| xmlIOHTTPClose (void * context) { |
| xmlNanoHTTPClose(context); |
| return 0; |
| } |
| |
| /** |
| * xmlIOHTTCloseWrite |
| * @context: The I/O context |
| * @http_mthd: The HTTP method to be used when sending the data |
| * |
| * Close the transmit HTTP I/O channel and actually send the data. |
| */ |
| static int |
| xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) { |
| |
| int close_rc = -1; |
| int http_rtn = 0; |
| int content_lgth = 0; |
| xmlIOHTTPWriteCtxtPtr ctxt = context; |
| |
| char * http_content = NULL; |
| char * content_encoding = NULL; |
| char * content_type = (char *) "text/xml"; |
| void * http_ctxt = NULL; |
| |
| if ( ( ctxt == NULL ) || ( http_mthd == NULL ) ) |
| return ( -1 ); |
| |
| /* Retrieve the content from the appropriate buffer */ |
| |
| #ifdef HAVE_ZLIB_H |
| |
| if ( ctxt->compression > 0 ) { |
| content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content ); |
| content_encoding = (char *) "Content-Encoding: gzip"; |
| } |
| else |
| #endif |
| { |
| /* Pull the data out of the memory output buffer */ |
| |
| xmlOutputBufferPtr dctxt = ctxt->doc_buff; |
| http_content = (char *)dctxt->buffer->content; |
| content_lgth = dctxt->buffer->use; |
| } |
| |
| if ( http_content == NULL ) { |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n", |
| "Error retrieving content.\nUnable to", |
| http_mthd, "data to URI", ctxt->uri ); |
| } |
| |
| else { |
| |
| http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content, |
| &content_type, content_encoding, |
| content_lgth ); |
| |
| if ( http_ctxt != NULL ) { |
| #ifdef DEBUG_HTTP |
| /* If testing/debugging - dump reply with request content */ |
| |
| FILE * tst_file = NULL; |
| char buffer[ 4096 ]; |
| char * dump_name = NULL; |
| int avail; |
| |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlNanoHTTPCloseWrite: HTTP %s to\n%s returned %d.\n", |
| http_mthd, ctxt->uri, |
| xmlNanoHTTPReturnCode( http_ctxt ) ); |
| |
| /* |
| ** Since either content or reply may be gzipped, |
| ** dump them to separate files instead of the |
| ** standard error context. |
| */ |
| |
| dump_name = tempnam( NULL, "lxml" ); |
| if ( dump_name != NULL ) { |
| (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name ); |
| |
| tst_file = fopen( buffer, "wb" ); |
| if ( tst_file != NULL ) { |
| xmlGenericError( xmlGenericErrorContext, |
| "Transmitted content saved in file: %s\n", buffer ); |
| |
| fwrite( http_content, sizeof( char ), |
| content_lgth, tst_file ); |
| fclose( tst_file ); |
| } |
| |
| (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name ); |
| tst_file = fopen( buffer, "wb" ); |
| if ( tst_file != NULL ) { |
| xmlGenericError( xmlGenericErrorContext, |
| "Reply content saved in file: %s\n", buffer ); |
| |
| |
| while ( (avail = xmlNanoHTTPRead( http_ctxt, |
| buffer, sizeof( buffer ) )) > 0 ) { |
| |
| fwrite( buffer, sizeof( char ), avail, tst_file ); |
| } |
| |
| fclose( tst_file ); |
| } |
| |
| free( dump_name ); |
| } |
| #endif /* DEBUG_HTTP */ |
| |
| http_rtn = xmlNanoHTTPReturnCode( http_ctxt ); |
| if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) ) |
| close_rc = 0; |
| else |
| xmlGenericError( xmlGenericErrorContext, |
| "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n", |
| http_mthd, content_lgth, |
| "bytes to URI", ctxt->uri, |
| "failed. HTTP return code:", http_rtn ); |
| |
| xmlNanoHTTPClose( http_ctxt ); |
| xmlFree( content_type ); |
| } |
| } |
| |
| /* Final cleanups */ |
| |
| xmlFreeHTTPWriteCtxt( ctxt ); |
| |
| return ( close_rc ); |
| } |
| |
| /** |
| * xmlIOHTTPClosePut |
| * |
| * @context: The I/O context |
| * |
| * Close the transmit HTTP I/O channel and actually send data using a PUT |
| * HTTP method. |
| */ |
| static int |
| xmlIOHTTPClosePut( void * ctxt ) { |
| return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) ); |
| } |
| |
| |
| /** |
| * xmlIOHTTPClosePost |
| * |
| * @context: The I/O context |
| * |
| * Close the transmit HTTP I/O channel and actually send data using a POST |
| * HTTP method. |
| */ |
| static int |
| xmlIOHTTPClosePost( void * ctxt ) { |
| return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) ); |
| } |
| |
| #endif /* LIBXML_HTTP_ENABLED */ |
| |
| #ifdef LIBXML_FTP_ENABLED |
| /************************************************************************ |
| * * |
| * I/O for FTP file accesses * |
| * * |
| ************************************************************************/ |
| /** |
| * xmlIOFTPMatch: |
| * @filename: the URI for matching |
| * |
| * check if the URI matches an FTP one |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlIOFTPMatch (const char *filename) { |
| if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6)) |
| return(1); |
| return(0); |
| } |
| |
| /** |
| * xmlIOFTPOpen: |
| * @filename: the URI for matching |
| * |
| * open an FTP I/O channel |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlIOFTPOpen (const char *filename) { |
| return(xmlNanoFTPOpen(filename)); |
| } |
| |
| /** |
| * xmlIOFTPRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlIOFTPRead(void * context, char * buffer, int len) { |
| return(xmlNanoFTPRead(context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlIOFTPClose: |
| * @context: the I/O context |
| * |
| * Close an FTP I/O channel |
| * |
| * Returns 0 |
| */ |
| int |
| xmlIOFTPClose (void * context) { |
| return ( xmlNanoFTPClose(context) ); |
| } |
| #endif /* LIBXML_FTP_ENABLED */ |
| |
| |
| /** |
| * xmlRegisterInputCallbacks: |
| * @matchFunc: the xmlInputMatchCallback |
| * @openFunc: the xmlInputOpenCallback |
| * @readFunc: the xmlInputReadCallback |
| * @closeFunc: the xmlInputCloseCallback |
| * |
| * Register a new set of I/O callback for handling parser input. |
| * |
| * Returns the registered handler number or -1 in case of error |
| */ |
| int |
| xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc, |
| xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc, |
| xmlInputCloseCallback closeFunc) { |
| if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) { |
| return(-1); |
| } |
| xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc; |
| xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc; |
| xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc; |
| xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc; |
| return(xmlInputCallbackNr++); |
| } |
| |
| /** |
| * xmlRegisterOutputCallbacks: |
| * @matchFunc: the xmlOutputMatchCallback |
| * @openFunc: the xmlOutputOpenCallback |
| * @writeFunc: the xmlOutputWriteCallback |
| * @closeFunc: the xmlOutputCloseCallback |
| * |
| * Register a new set of I/O callback for handling output. |
| * |
| * Returns the registered handler number or -1 in case of error |
| */ |
| int |
| xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc, |
| xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc, |
| xmlOutputCloseCallback closeFunc) { |
| if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) { |
| return(-1); |
| } |
| xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc; |
| xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc; |
| xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc; |
| xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc; |
| return(xmlOutputCallbackNr++); |
| } |
| |
| /** |
| * xmlRegisterDefaultInputCallbacks: |
| * |
| * Registers the default compiled-in I/O handlers. |
| */ |
| void |
| xmlRegisterDefaultInputCallbacks |
| (void) { |
| if (xmlInputCallbackInitialized) |
| return; |
| |
| xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen, |
| xmlFileRead, xmlFileClose); |
| #ifdef HAVE_ZLIB_H |
| xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen, |
| xmlGzfileRead, xmlGzfileClose); |
| #endif /* HAVE_ZLIB_H */ |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen, |
| xmlIOHTTPRead, xmlIOHTTPClose); |
| #endif /* LIBXML_HTTP_ENABLED */ |
| |
| #ifdef LIBXML_FTP_ENABLED |
| xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen, |
| xmlIOFTPRead, xmlIOFTPClose); |
| #endif /* LIBXML_FTP_ENABLED */ |
| xmlInputCallbackInitialized = 1; |
| } |
| |
| /** |
| * xmlRegisterDefaultOutputCallbacks: |
| * |
| * Registers the default compiled-in I/O handlers. |
| */ |
| void |
| xmlRegisterDefaultOutputCallbacks |
| (void) { |
| if (xmlOutputCallbackInitialized) |
| return; |
| |
| xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW, |
| xmlFileWrite, xmlFileClose); |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW, |
| xmlIOHTTPWrite, xmlIOHTTPClosePut); |
| #endif |
| |
| /********************************* |
| No way a-priori to distinguish between gzipped files from |
| uncompressed ones except opening if existing then closing |
| and saving with same compression ratio ... a pain. |
| |
| #ifdef HAVE_ZLIB_H |
| xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen, |
| xmlGzfileWrite, xmlGzfileClose); |
| #endif |
| |
| Nor FTP PUT .... |
| #ifdef LIBXML_FTP_ENABLED |
| xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen, |
| xmlIOFTPWrite, xmlIOFTPClose); |
| #endif |
| **********************************/ |
| xmlOutputCallbackInitialized = 1; |
| } |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| /** |
| * xmlRegisterHTTPPostCallbacks: |
| * |
| * By default, libxml submits HTTP output requests using the "PUT" method. |
| * Calling this method changes the HTTP output method to use the "POST" |
| * method instead. |
| * |
| */ |
| void |
| xmlRegisterHTTPPostCallbacks( void ) { |
| |
| /* Register defaults if not done previously */ |
| |
| if ( xmlOutputCallbackInitialized == 0 ) |
| xmlRegisterDefaultOutputCallbacks( ); |
| |
| xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW, |
| xmlIOHTTPWrite, xmlIOHTTPClosePost); |
| return; |
| } |
| #endif |
| |
| /** |
| * xmlAllocParserInputBuffer: |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for progressive parsing |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlAllocParserInputBuffer(xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer)); |
| if (ret == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlAllocParserInputBuffer : out of memory!\n"); |
| return(NULL); |
| } |
| memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer)); |
| ret->buffer = xmlBufferCreateSize(2 * xmlDefaultBufferSize); |
| if (ret->buffer == NULL) { |
| xmlFree(ret); |
| return(NULL); |
| } |
| ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT; |
| ret->encoder = xmlGetCharEncodingHandler(enc); |
| if (ret->encoder != NULL) |
| ret->raw = xmlBufferCreateSize(2 * xmlDefaultBufferSize); |
| else |
| ret->raw = NULL; |
| ret->readcallback = NULL; |
| ret->closecallback = NULL; |
| ret->context = NULL; |
| ret->compressed = -1; |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlAllocOutputBuffer: |
| * @encoder: the encoding converter or NULL |
| * |
| * Create a buffered parser output |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer)); |
| if (ret == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlAllocOutputBuffer : out of memory!\n"); |
| return(NULL); |
| } |
| memset(ret, 0, (size_t) sizeof(xmlOutputBuffer)); |
| ret->buffer = xmlBufferCreate(); |
| if (ret->buffer == NULL) { |
| xmlFree(ret); |
| return(NULL); |
| } |
| ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT; |
| ret->encoder = encoder; |
| if (encoder != NULL) { |
| ret->conv = xmlBufferCreateSize(4000); |
| /* |
| * This call is designed to initiate the encoder state |
| */ |
| xmlCharEncOutFunc(encoder, ret->conv, NULL); |
| } else |
| ret->conv = NULL; |
| ret->writecallback = NULL; |
| ret->closecallback = NULL; |
| ret->context = NULL; |
| ret->written = 0; |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlFreeParserInputBuffer: |
| * @in: a buffered parser input |
| * |
| * Free up the memory used by a buffered parser input |
| */ |
| void |
| xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) { |
| if (in == NULL) return; |
| |
| if (in->raw) { |
| xmlBufferFree(in->raw); |
| in->raw = NULL; |
| } |
| if (in->encoder != NULL) { |
| xmlCharEncCloseFunc(in->encoder); |
| } |
| if (in->closecallback != NULL) { |
| in->closecallback(in->context); |
| } |
| if (in->buffer != NULL) { |
| xmlBufferFree(in->buffer); |
| in->buffer = NULL; |
| } |
| |
| xmlFree(in); |
| } |
| |
| /** |
| * xmlOutputBufferClose: |
| * @out: a buffered output |
| * |
| * flushes and close the output I/O channel |
| * and free up all the associated resources |
| * |
| * Returns the number of byte written or -1 in case of error. |
| */ |
| int |
| xmlOutputBufferClose(xmlOutputBufferPtr out) { |
| int written; |
| int err_rc = 0; |
| |
| if (out == NULL) |
| return(-1); |
| if (out->writecallback != NULL) |
| xmlOutputBufferFlush(out); |
| if (out->closecallback != NULL) { |
| err_rc = out->closecallback(out->context); |
| } |
| written = out->written; |
| if (out->conv) { |
| xmlBufferFree(out->conv); |
| out->conv = NULL; |
| } |
| if (out->encoder != NULL) { |
| xmlCharEncCloseFunc(out->encoder); |
| } |
| if (out->buffer != NULL) { |
| xmlBufferFree(out->buffer); |
| out->buffer = NULL; |
| } |
| |
| xmlFree(out); |
| return( ( err_rc == 0 ) ? written : err_rc ); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateFilename: |
| * @URI: a C string containing the URI or filename |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing of a file |
| * If filename is "-' then we use stdin as the input. |
| * Automatic support for ZLIB/Compress compressed document is provided |
| * by default if found at compile-time. |
| * Do an encoding check if enc == XML_CHAR_ENCODING_NONE |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| int i = 0; |
| void *context = NULL; |
| |
| if (xmlInputCallbackInitialized == 0) |
| xmlRegisterDefaultInputCallbacks(); |
| |
| if (URI == NULL) return(NULL); |
| |
| /* |
| * Try to find one of the input accept method accepting that scheme |
| * Go in reverse to give precedence to user defined handlers. |
| */ |
| if (context == NULL) { |
| for (i = xmlInputCallbackNr - 1;i >= 0;i--) { |
| if ((xmlInputCallbackTable[i].matchcallback != NULL) && |
| (xmlInputCallbackTable[i].matchcallback(URI) != 0)) { |
| context = xmlInputCallbackTable[i].opencallback(URI); |
| if (context != NULL) |
| break; |
| } |
| } |
| } |
| if (context == NULL) { |
| return(NULL); |
| } |
| |
| /* |
| * Allocate the Input buffer front-end. |
| */ |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = context; |
| ret->readcallback = xmlInputCallbackTable[i].readcallback; |
| ret->closecallback = xmlInputCallbackTable[i].closecallback; |
| #ifdef HAVE_ZLIB_H |
| if (xmlInputCallbackTable[i].opencallback == xmlGzfileOpen) { |
| if (((z_stream *)context)->avail_in > 4) { |
| char *cptr, buff4[4]; |
| cptr = (char *) ((z_stream *)context)->next_in; |
| if (gzread(context, buff4, 4) == 4) { |
| if (strncmp(buff4, cptr, 4) == 0) |
| ret->compressed = 0; |
| else |
| ret->compressed = 1; |
| gzrewind(context); |
| } |
| } |
| } |
| #endif |
| } |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateFilename: |
| * @URI: a C string containing the URI or filename |
| * @encoder: the encoding converter or NULL |
| * @compression: the compression ration (0 none, 9 max). |
| * |
| * Create a buffered output for the progressive saving of a file |
| * If filename is "-' then we use stdout as the output. |
| * Automatic support for ZLIB/Compress compressed document is provided |
| * by default if found at compile-time. |
| * TODO: currently if compression is set, the library only support |
| * writing to a local file. |
| * |
| * Returns the new output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateFilename(const char *URI, |
| xmlCharEncodingHandlerPtr encoder, |
| int compression) { |
| xmlOutputBufferPtr ret; |
| int i = 0; |
| void *context = NULL; |
| char *unescaped; |
| |
| int is_http_uri = 0; /* Can't change if HTTP disabled */ |
| |
| if (xmlOutputCallbackInitialized == 0) |
| xmlRegisterDefaultOutputCallbacks(); |
| |
| if (URI == NULL) return(NULL); |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| /* Need to prevent HTTP URI's from falling into zlib short circuit */ |
| |
| is_http_uri = xmlIOHTTPMatch( URI ); |
| #endif |
| |
| |
| /* |
| * Try to find one of the output accept method accepting that scheme |
| * Go in reverse to give precedence to user defined handlers. |
| * try with an unescaped version of the URI |
| */ |
| unescaped = xmlURIUnescapeString(URI, 0, NULL); |
| if (unescaped != NULL) { |
| #ifdef HAVE_ZLIB_H |
| if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) { |
| context = xmlGzfileOpenW(unescaped, compression); |
| if (context != NULL) { |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = context; |
| ret->writecallback = xmlGzfileWrite; |
| ret->closecallback = xmlGzfileClose; |
| } |
| xmlFree(unescaped); |
| return(ret); |
| } |
| } |
| #endif |
| for (i = xmlOutputCallbackNr - 1;i >= 0;i--) { |
| if ((xmlOutputCallbackTable[i].matchcallback != NULL) && |
| (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) { |
| #if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H) |
| /* Need to pass compression parameter into HTTP open calls */ |
| if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch) |
| context = xmlIOHTTPOpenW(unescaped, compression); |
| else |
| #endif |
| context = xmlOutputCallbackTable[i].opencallback(unescaped); |
| if (context != NULL) |
| break; |
| } |
| } |
| xmlFree(unescaped); |
| } |
| |
| /* |
| * If this failed try with a non-escaped URI this may be a strange |
| * filename |
| */ |
| if (context == NULL) { |
| #ifdef HAVE_ZLIB_H |
| if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) { |
| context = xmlGzfileOpenW(URI, compression); |
| if (context != NULL) { |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = context; |
| ret->writecallback = xmlGzfileWrite; |
| ret->closecallback = xmlGzfileClose; |
| } |
| return(ret); |
| } |
| } |
| #endif |
| for (i = xmlOutputCallbackNr - 1;i >= 0;i--) { |
| if ((xmlOutputCallbackTable[i].matchcallback != NULL) && |
| (xmlOutputCallbackTable[i].matchcallback(URI) != 0)) { |
| #if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H) |
| /* Need to pass compression parameter into HTTP open calls */ |
| if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch) |
| context = xmlIOHTTPOpenW(URI, compression); |
| else |
| #endif |
| context = xmlOutputCallbackTable[i].opencallback(URI); |
| if (context != NULL) |
| break; |
| } |
| } |
| } |
| |
| if (context == NULL) { |
| return(NULL); |
| } |
| |
| /* |
| * Allocate the Output buffer front-end. |
| */ |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = context; |
| ret->writecallback = xmlOutputCallbackTable[i].writecallback; |
| ret->closecallback = xmlOutputCallbackTable[i].closecallback; |
| } |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateFile: |
| * @file: a FILE* |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing of a FILE * |
| * buffered C I/O |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (xmlInputCallbackInitialized == 0) |
| xmlRegisterDefaultInputCallbacks(); |
| |
| if (file == NULL) return(NULL); |
| |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = file; |
| ret->readcallback = xmlFileRead; |
| ret->closecallback = xmlFileFlush; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateFile: |
| * @file: a FILE* |
| * @encoder: the encoding converter or NULL |
| * |
| * Create a buffered output for the progressive saving to a FILE * |
| * buffered C I/O |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| if (xmlOutputCallbackInitialized == 0) |
| xmlRegisterDefaultOutputCallbacks(); |
| |
| if (file == NULL) return(NULL); |
| |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = file; |
| ret->writecallback = xmlFileWrite; |
| ret->closecallback = xmlFileFlush; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateFd: |
| * @fd: a file descriptor number |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing for the input |
| * from a file descriptor |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (fd < 0) return(NULL); |
| |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = (void *) (long) fd; |
| ret->readcallback = xmlFdRead; |
| ret->closecallback = xmlFdClose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateMem: |
| * @mem: the memory input |
| * @size: the length of the memory block |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing for the input |
| * from a memory area. |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (size <= 0) return(NULL); |
| if (mem == NULL) return(NULL); |
| |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = (void *) mem; |
| ret->readcallback = (xmlInputReadCallback) xmlNop; |
| ret->closecallback = NULL; |
| xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size); |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateStatic: |
| * @mem: the memory input |
| * @size: the length of the memory block |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing for the input |
| * from an immutable memory area. This will not copy the memory area to |
| * the buffer, but the memory is expected to be available until the end of |
| * the parsing, this is useful for example when using mmap'ed file. |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateStatic(const char *mem, int size, |
| xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (size <= 0) return(NULL); |
| if (mem == NULL) return(NULL); |
| |
| ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer)); |
| if (ret == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlParserInputBufferCreateStatic : out of memory!\n"); |
| return(NULL); |
| } |
| memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer)); |
| ret->buffer = xmlBufferCreateStatic((void *)mem, (size_t) size); |
| if (ret->buffer == NULL) { |
| xmlFree(ret); |
| return(NULL); |
| } |
| ret->encoder = xmlGetCharEncodingHandler(enc); |
| if (ret->encoder != NULL) |
| ret->raw = xmlBufferCreateSize(2 * xmlDefaultBufferSize); |
| else |
| ret->raw = NULL; |
| ret->compressed = -1; |
| ret->context = (void *) mem; |
| ret->readcallback = NULL; |
| ret->closecallback = NULL; |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateFd: |
| * @fd: a file descriptor number |
| * @encoder: the encoding converter or NULL |
| * |
| * Create a buffered output for the progressive saving |
| * to a file descriptor |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| if (fd < 0) return(NULL); |
| |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = (void *) (long) fd; |
| ret->writecallback = xmlFdWrite; |
| ret->closecallback = NULL; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateIO: |
| * @ioread: an I/O read function |
| * @ioclose: an I/O close function |
| * @ioctx: an I/O handler |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing for the input |
| * from an I/O handler |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateIO(xmlInputReadCallback ioread, |
| xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (ioread == NULL) return(NULL); |
| |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = (void *) ioctx; |
| ret->readcallback = ioread; |
| ret->closecallback = ioclose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateIO: |
| * @iowrite: an I/O write function |
| * @ioclose: an I/O close function |
| * @ioctx: an I/O handler |
| * @encoder: the charset encoding if known |
| * |
| * Create a buffered output for the progressive saving |
| * to an I/O handler |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite, |
| xmlOutputCloseCallback ioclose, void *ioctx, |
| xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| if (iowrite == NULL) return(NULL); |
| |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = (void *) ioctx; |
| ret->writecallback = iowrite; |
| ret->closecallback = ioclose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferPush: |
| * @in: a buffered parser input |
| * @len: the size in bytes of the array. |
| * @buf: an char array |
| * |
| * Push the content of the arry in the input buffer |
| * This routine handle the I18N transcoding to internal UTF-8 |
| * This is used when operating the parser in progressive (push) mode. |
| * |
| * Returns the number of chars read and stored in the buffer, or -1 |
| * in case of error. |
| */ |
| int |
| xmlParserInputBufferPush(xmlParserInputBufferPtr in, |
| int len, const char *buf) { |
| int nbchars = 0; |
| |
| if (len < 0) return(0); |
| if (in->encoder != NULL) { |
| /* |
| * Store the data in the incoming raw buffer |
| */ |
| if (in->raw == NULL) { |
| in->raw = xmlBufferCreate(); |
| } |
| xmlBufferAdd(in->raw, (const xmlChar *) buf, len); |
| |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw); |
| if (nbchars < 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlParserInputBufferPush: encoder error\n"); |
| return(-1); |
| } |
| } else { |
| nbchars = len; |
| xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars); |
| } |
| #ifdef DEBUG_INPUT |
| xmlGenericError(xmlGenericErrorContext, |
| "I/O: pushed %d chars, buffer %d/%d\n", |
| nbchars, in->buffer->use, in->buffer->size); |
| #endif |
| return(nbchars); |
| } |
| |
| /** |
| * endOfInput: |
| * |
| * When reading from an Input channel indicated end of file or error |
| * don't reread from it again. |
| */ |
| static int |
| endOfInput (void * context ATTRIBUTE_UNUSED, |
| char * buffer ATTRIBUTE_UNUSED, |
| int len ATTRIBUTE_UNUSED) { |
| return(0); |
| } |
| |
| /** |
| * xmlParserInputBufferGrow: |
| * @in: a buffered parser input |
| * @len: indicative value of the amount of chars to read |
| * |
| * Grow up the content of the input buffer, the old data are preserved |
| * This routine handle the I18N transcoding to internal UTF-8 |
| * This routine is used when operating the parser in normal (pull) mode |
| * |
| * TODO: one should be able to remove one extra copy by copying directly |
| * onto in->buffer or in->raw |
| * |
| * Returns the number of chars read and stored in the buffer, or -1 |
| * in case of error. |
| */ |
| int |
| xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) { |
| char *buffer = NULL; |
| int res = 0; |
| int nbchars = 0; |
| int buffree; |
| unsigned int needSize; |
| |
| if ((len <= MINLEN) && (len != 4)) |
| len = MINLEN; |
| |
| buffree = in->buffer->size - in->buffer->use; |
| if (buffree <= 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlParserInputBufferGrow : buffer full !\n"); |
| return(0); |
| } |
| |
| needSize = in->buffer->use + len + 1; |
| if (needSize > in->buffer->size){ |
| if (!xmlBufferResize(in->buffer, needSize)){ |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlParserInputBufferGrow : out of memory!\n"); |
| return(0); |
| } |
| } |
| buffer = (char *)&in->buffer->content[in->buffer->use]; |
| |
| /* |
| * Call the read method for this I/O type. |
| */ |
| if (in->readcallback != NULL) { |
| res = in->readcallback(in->context, &buffer[0], len); |
| if (res <= 0) |
| in->readcallback = endOfInput; |
| } else { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlParserInputBufferGrow : no input !\n"); |
| return(-1); |
| } |
| if (res < 0) { |
| return(-1); |
| } |
| len = res; |
| if (in->encoder != NULL) { |
| /* |
| * Store the data in the incoming raw buffer |
| */ |
| if (in->raw == NULL) { |
| in->raw = xmlBufferCreate(); |
| } |
| xmlBufferAdd(in->raw, (const xmlChar *) buffer, len); |
| |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw); |
| if (nbchars < 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlParserInputBufferGrow: encoder error\n"); |
| return(-1); |
| } |
| } else { |
| nbchars = len; |
| in->buffer->use += nbchars; |
| buffer[nbchars] = 0; |
| } |
| #ifdef DEBUG_INPUT |
| xmlGenericError(xmlGenericErrorContext, |
| "I/O: read %d chars, buffer %d/%d\n", |
| nbchars, in->buffer->use, in->buffer->size); |
| #endif |
| return(nbchars); |
| } |
| |
| /** |
| * xmlParserInputBufferRead: |
| * @in: a buffered parser input |
| * @len: indicative value of the amount of chars to read |
| * |
| * Refresh the content of the input buffer, the old data are considered |
| * consumed |
| * This routine handle the I18N transcoding to internal UTF-8 |
| * |
| * Returns the number of chars read and stored in the buffer, or -1 |
| * in case of error. |
| */ |
| int |
| xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) { |
| /* xmlBufferEmpty(in->buffer); */ |
| if (in->readcallback != NULL) |
| return(xmlParserInputBufferGrow(in, len)); |
| else if ((in->buffer != NULL) && |
| (in->buffer->alloc == XML_BUFFER_ALLOC_IMMUTABLE)) |
| return(0); |
| else |
| return(-1); |
| } |
| |
| /** |
| * xmlOutputBufferWrite: |
| * @out: a buffered parser output |
| * @len: the size in bytes of the array. |
| * @buf: an char array |
| * |
| * Write the content of the array in the output I/O buffer |
| * This routine handle the I18N transcoding from internal UTF-8 |
| * The buffer is lossless, i.e. will store in case of partial |
| * or delayed writes. |
| * |
| * Returns the number of chars immediately written, or -1 |
| * in case of error. |
| */ |
| int |
| xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) { |
| int nbchars = 0; /* number of chars to output to I/O */ |
| int ret; /* return from function call */ |
| int written = 0; /* number of char written to I/O so far */ |
| int chunk; /* number of byte curreent processed from buf */ |
| |
| if (len < 0) return(0); |
| |
| do { |
| chunk = len; |
| if (chunk > 4 * MINLEN) |
| chunk = 4 * MINLEN; |
| |
| /* |
| * first handle encoding stuff. |
| */ |
| if (out->encoder != NULL) { |
| /* |
| * Store the data in the incoming raw buffer |
| */ |
| if (out->conv == NULL) { |
| out->conv = xmlBufferCreate(); |
| } |
| xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk); |
| |
| if ((out->buffer->use < MINLEN) && (chunk == len)) |
| goto done; |
| |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer); |
| if ((ret < 0) && (ret != -3)) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlOutputBufferWrite: encoder error\n"); |
| return(-1); |
| } |
| nbchars = out->conv->use; |
| } else { |
| xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk); |
| nbchars = out->buffer->use; |
| } |
| buf += chunk; |
| len -= chunk; |
| |
| if ((nbchars < MINLEN) && (len <= 0)) |
| goto done; |
| |
| if (out->writecallback) { |
| /* |
| * second write the stuff to the I/O channel |
| */ |
| if (out->encoder != NULL) { |
| ret = out->writecallback(out->context, |
| (const char *)out->conv->content, nbchars); |
| if (ret >= 0) |
| xmlBufferShrink(out->conv, ret); |
| } else { |
| ret = out->writecallback(out->context, |
| (const char *)out->buffer->content, nbchars); |
| if (ret >= 0) |
| xmlBufferShrink(out->buffer, ret); |
| } |
| if (ret < 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "I/O: error %d writing %d bytes\n", ret, nbchars); |
| return(ret); |
| } |
| out->written += ret; |
| } |
| written += nbchars; |
| } while (len > 0); |
| |
| done: |
| #ifdef DEBUG_INPUT |
| xmlGenericError(xmlGenericErrorContext, |
| "I/O: wrote %d chars\n", written); |
| #endif |
| return(written); |
| } |
| |
| /** |
| * xmlOutputBufferWriteString: |
| * @out: a buffered parser output |
| * @str: a zero terminated C string |
| * |
| * Write the content of the string in the output I/O buffer |
| * This routine handle the I18N transcoding from internal UTF-8 |
| * The buffer is lossless, i.e. will store in case of partial |
| * or delayed writes. |
| * |
| * Returns the number of chars immediately written, or -1 |
| * in case of error. |
| */ |
| int |
| xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) { |
| int len; |
| |
| if (str == NULL) |
| return(-1); |
| len = strlen(str); |
| |
| if (len > 0) |
| return(xmlOutputBufferWrite(out, len, str)); |
| return(len); |
| } |
| |
| /** |
| * xmlOutputBufferFlush: |
| * @out: a buffered output |
| * |
| * flushes the output I/O channel |
| * |
| * Returns the number of byte written or -1 in case of error. |
| */ |
| int |
| xmlOutputBufferFlush(xmlOutputBufferPtr out) { |
| int nbchars = 0, ret = 0; |
| |
| /* |
| * first handle encoding stuff. |
| */ |
| if ((out->conv != NULL) && (out->encoder != NULL)) { |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer); |
| if (nbchars < 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlOutputBufferFlush: encoder error\n"); |
| return(-1); |
| } |
| } |
| |
| /* |
| * second flush the stuff to the I/O channel |
| */ |
| if ((out->conv != NULL) && (out->encoder != NULL) && |
| (out->writecallback != NULL)) { |
| ret = out->writecallback(out->context, |
| (const char *)out->conv->content, out->conv->use); |
| if (ret >= 0) |
| xmlBufferShrink(out->conv, ret); |
| } else if (out->writecallback != NULL) { |
| ret = out->writecallback(out->context, |
| (const char *)out->buffer->content, out->buffer->use); |
| if (ret >= 0) |
| xmlBufferShrink(out->buffer, ret); |
| } |
| if (ret < 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "I/O: error %d flushing %d bytes\n", ret, nbchars); |
| return(ret); |
| } |
| out->written += ret; |
| |
| #ifdef DEBUG_INPUT |
| xmlGenericError(xmlGenericErrorContext, |
| "I/O: flushed %d chars\n", ret); |
| #endif |
| return(ret); |
| } |
| |
| /** |
| * xmlParserGetDirectory: |
| * @filename: the path to a file |
| * |
| * lookup the directory for that file |
| * |
| * Returns a new allocated string containing the directory, or NULL. |
| */ |
| char * |
| xmlParserGetDirectory(const char *filename) { |
| char *ret = NULL; |
| char dir[1024]; |
| char *cur; |
| char sep = '/'; |
| |
| #ifdef _WIN32_WCE /* easy way by now ... wince does not have dirs! */ |
| return NULL; |
| #endif |
| |
| if (xmlInputCallbackInitialized == 0) |
| xmlRegisterDefaultInputCallbacks(); |
| |
| if (filename == NULL) return(NULL); |
| #if defined(WIN32) && !defined(__CYGWIN__) |
| sep = '\\'; |
| #endif |
| |
| strncpy(dir, filename, 1023); |
| dir[1023] = 0; |
| cur = &dir[strlen(dir)]; |
| while (cur > dir) { |
| if (*cur == sep) break; |
| cur --; |
| } |
| if (*cur == sep) { |
| if (cur == dir) dir[1] = 0; |
| else *cur = 0; |
| ret = xmlMemStrdup(dir); |
| } else { |
| if (getcwd(dir, 1024) != NULL) { |
| dir[1023] = 0; |
| ret = xmlMemStrdup(dir); |
| } |
| } |
| return(ret); |
| } |
| |
| /**************************************************************** |
| * * |
| * External entities loading * |
| * * |
| ****************************************************************/ |
| |
| static int xmlSysIDExists(const char *URL) { |
| #ifdef HAVE_STAT |
| int ret; |
| struct stat info; |
| const char *path; |
| |
| if (URL == NULL) |
| return(0); |
| |
| if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17)) |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &URL[17]; |
| #else |
| path = &URL[16]; |
| #endif |
| else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) { |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &URL[8]; |
| #else |
| path = &URL[7]; |
| #endif |
| } else |
| path = URL; |
| ret = stat(path, &info); |
| if (ret == 0) |
| return(1); |
| #endif |
| return(0); |
| } |
| |
| /** |
| * xmlDefaultExternalEntityLoader: |
| * @URL: the URL for the entity to load |
| * @ID: the System ID for the entity to load |
| * @ctxt: the context in which the entity is called or NULL |
| * |
| * By default we don't load external entitites, yet. |
| * |
| * Returns a new allocated xmlParserInputPtr, or NULL. |
| */ |
| static |
| xmlParserInputPtr |
| xmlDefaultExternalEntityLoader(const char *URL, const char *ID, |
| xmlParserCtxtPtr ctxt) { |
| xmlParserInputPtr ret = NULL; |
| xmlChar *resource = NULL; |
| #ifdef LIBXML_CATALOG_ENABLED |
| xmlCatalogAllow pref; |
| #endif |
| |
| #ifdef DEBUG_EXTERNAL_ENTITIES |
| xmlGenericError(xmlGenericErrorContext, |
| "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL); |
| #endif |
| #ifdef LIBXML_CATALOG_ENABLED |
| /* |
| * If the resource doesn't exists as a file, |
| * try to load it from the resource pointed in the catalogs |
| */ |
| pref = xmlCatalogGetDefaults(); |
| |
| if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) { |
| /* |
| * Do a local lookup |
| */ |
| if ((ctxt->catalogs != NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_DOCUMENT))) { |
| resource = xmlCatalogLocalResolve(ctxt->catalogs, |
| (const xmlChar *)ID, |
| (const xmlChar *)URL); |
| } |
| /* |
| * Try a global lookup |
| */ |
| if ((resource == NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_GLOBAL))) { |
| resource = xmlCatalogResolve((const xmlChar *)ID, |
| (const xmlChar *)URL); |
| } |
| if ((resource == NULL) && (URL != NULL)) |
| resource = xmlStrdup((const xmlChar *) URL); |
| |
| /* |
| * TODO: do an URI lookup on the reference |
| */ |
| if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) { |
| xmlChar *tmp = NULL; |
| |
| if ((ctxt->catalogs != NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_DOCUMENT))) { |
| tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource); |
| } |
| if ((tmp == NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_GLOBAL))) { |
| tmp = xmlCatalogResolveURI(resource); |
| } |
| |
| if (tmp != NULL) { |
| xmlFree(resource); |
| resource = tmp; |
| } |
| } |
| } |
| #endif |
| |
| if (resource == NULL) |
| resource = (xmlChar *) URL; |
| |
| if (resource == NULL) { |
| if (ID == NULL) |
| ID = "NULL"; |
| if ((ctxt->validate) && (ctxt->sax != NULL) && |
| (ctxt->sax->error != NULL)) |
| ctxt->sax->error(ctxt->userData, |
| "failed to load external entity \"%s\"\n", ID); |
| else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) |
| ctxt->sax->warning(ctxt->userData, |
| "failed to load external entity \"%s\"\n", ID); |
| return(NULL); |
| } |
| ret = xmlNewInputFromFile(ctxt, (const char *)resource); |
| if (ret == NULL) { |
| if ((ctxt->validate) && (ctxt->sax != NULL) && |
| (ctxt->sax->error != NULL)) |
| ctxt->sax->error(ctxt->userData, |
| "failed to load external entity \"%s\"\n", resource); |
| else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) |
| ctxt->sax->warning(ctxt->userData, |
| "failed to load external entity \"%s\"\n", resource); |
| } |
| if ((resource != NULL) && (resource != (xmlChar *) URL)) |
| xmlFree(resource); |
| return(ret); |
| } |
| |
| static xmlExternalEntityLoader xmlCurrentExternalEntityLoader = |
| xmlDefaultExternalEntityLoader; |
| |
| /** |
| * xmlSetExternalEntityLoader: |
| * @f: the new entity resolver function |
| * |
| * Changes the defaultexternal entity resolver function for the application |
| */ |
| void |
| xmlSetExternalEntityLoader(xmlExternalEntityLoader f) { |
| xmlCurrentExternalEntityLoader = f; |
| } |
| |
| /** |
| * xmlGetExternalEntityLoader: |
| * |
| * Get the default external entity resolver function for the application |
| * |
| * Returns the xmlExternalEntityLoader function pointer |
| */ |
| xmlExternalEntityLoader |
| xmlGetExternalEntityLoader(void) { |
| return(xmlCurrentExternalEntityLoader); |
| } |
| |
| /** |
| * xmlLoadExternalEntity: |
| * @URL: the URL for the entity to load |
| * @ID: the Public ID for the entity to load |
| * @ctxt: the context in which the entity is called or NULL |
| * |
| * Load an external entity, note that the use of this function for |
| * unparsed entities may generate problems |
| * TODO: a more generic External entity API must be designed |
| * |
| * Returns the xmlParserInputPtr or NULL |
| */ |
| xmlParserInputPtr |
| xmlLoadExternalEntity(const char *URL, const char *ID, |
| xmlParserCtxtPtr ctxt) { |
| if ((URL != NULL) && (xmlSysIDExists(URL) == 0)) { |
| char *canonicFilename; |
| xmlParserInputPtr ret; |
| |
| canonicFilename = (char *) xmlCanonicPath((const xmlChar *) URL); |
| if (canonicFilename == NULL) { |
| if (xmlDefaultSAXHandler.error != NULL) { |
| xmlDefaultSAXHandler.error(NULL, "out of memory\n"); |
| } |
| return(NULL); |
| } |
| |
| ret = xmlCurrentExternalEntityLoader(canonicFilename, ID, ctxt); |
| xmlFree(canonicFilename); |
| return(ret); |
| } |
| return(xmlCurrentExternalEntityLoader(URL, ID, ctxt)); |
| } |
| |
| /************************************************************************ |
| * * |
| * Disabling Network access * |
| * * |
| ************************************************************************/ |
| |
| #ifdef LIBXML_CATALOG_ENABLED |
| static int |
| xmlNoNetExists(const char *URL) |
| { |
| #ifdef HAVE_STAT |
| int ret; |
| struct stat info; |
| const char *path; |
| |
| if (URL == NULL) |
| return (0); |
| |
| if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17)) |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &URL[17]; |
| #else |
| path = &URL[16]; |
| #endif |
| else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) { |
| #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) |
| path = &URL[8]; |
| #else |
| path = &URL[7]; |
| #endif |
| } else |
| path = URL; |
| ret = stat(path, &info); |
| if (ret == 0) |
| return (1); |
| #endif |
| return (0); |
| } |
| #endif |
| |
| /** |
| * xmlNoNetExternalEntityLoader: |
| * @URL: the URL for the entity to load |
| * @ID: the System ID for the entity to load |
| * @ctxt: the context in which the entity is called or NULL |
| * |
| * A specific entity loader disabling network accesses, though still |
| * allowing local catalog accesses for resolution. |
| * |
| * Returns a new allocated xmlParserInputPtr, or NULL. |
| */ |
| xmlParserInputPtr |
| xmlNoNetExternalEntityLoader(const char *URL, const char *ID, |
| xmlParserCtxtPtr ctxt) { |
| xmlParserInputPtr input = NULL; |
| xmlChar *resource = NULL; |
| |
| #ifdef LIBXML_CATALOG_ENABLED |
| xmlCatalogAllow pref; |
| |
| /* |
| * If the resource doesn't exists as a file, |
| * try to load it from the resource pointed in the catalogs |
| */ |
| pref = xmlCatalogGetDefaults(); |
| |
| if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) { |
| /* |
| * Do a local lookup |
| */ |
| if ((ctxt->catalogs != NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_DOCUMENT))) { |
| resource = xmlCatalogLocalResolve(ctxt->catalogs, |
| (const xmlChar *)ID, |
| (const xmlChar *)URL); |
| } |
| /* |
| * Try a global lookup |
| */ |
| if ((resource == NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_GLOBAL))) { |
| resource = xmlCatalogResolve((const xmlChar *)ID, |
| (const xmlChar *)URL); |
| } |
| if ((resource == NULL) && (URL != NULL)) |
| resource = xmlStrdup((const xmlChar *) URL); |
| |
| /* |
| * TODO: do an URI lookup on the reference |
| */ |
| if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) { |
| xmlChar *tmp = NULL; |
| |
| if ((ctxt->catalogs != NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_DOCUMENT))) { |
| tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource); |
| } |
| if ((tmp == NULL) && |
| ((pref == XML_CATA_ALLOW_ALL) || |
| (pref == XML_CATA_ALLOW_GLOBAL))) { |
| tmp = xmlCatalogResolveURI(resource); |
| } |
| |
| if (tmp != NULL) { |
| xmlFree(resource); |
| resource = tmp; |
| } |
| } |
| } |
| #endif |
| if (resource == NULL) |
| resource = (xmlChar *) URL; |
| |
| if (resource != NULL) { |
| if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) || |
| (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) { |
| xmlGenericError(xmlGenericErrorContext, |
| "Attempt to load network entity %s \n", resource); |
| |
| if (resource != (xmlChar *) URL) |
| xmlFree(resource); |
| return(NULL); |
| } |
| } |
| input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt); |
| if (resource != (xmlChar *) URL) |
| xmlFree(resource); |
| return(input); |
| } |
| |