mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2024-12-28 07:21:26 +03:00
809faa5237
* encoding.c xmlIO.c: fixing bug #104646 about iconv based encoding conversion when the input buffer stops in the middle of a multibyte char Daniel
2869 lines
69 KiB
C
2869 lines
69 KiB
C
/*
|
|
* 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;
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Handling of Windows file paths *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#define IS_WINDOWS_PATH(p) \
|
|
((p != NULL) && \
|
|
(((p[0] >= 'a') && (p[0] <= 'z')) || \
|
|
((p[0] >= 'A') && (p[0] <= 'Z'))) && \
|
|
(p[1] == ':') && ((p[2] == '/') || (p[2] == '\\')))
|
|
|
|
|
|
/**
|
|
* xmlNormalizeWindowsPath:
|
|
* @path: a windows path like "C:/foo/bar"
|
|
*
|
|
* Normalize a Windows path to make an URL from it
|
|
*
|
|
* Returns a new URI which must be freed by the caller or NULL
|
|
* in case of error
|
|
*/
|
|
xmlChar *
|
|
xmlNormalizeWindowsPath(const xmlChar *path)
|
|
{
|
|
int len, i = 0, j;
|
|
xmlChar *ret;
|
|
|
|
if (path == NULL)
|
|
return(NULL);
|
|
|
|
len = xmlStrlen(path);
|
|
if (!IS_WINDOWS_PATH(path)) {
|
|
ret = xmlStrdup(path);
|
|
if (ret == NULL)
|
|
return(NULL);
|
|
j = 0;
|
|
} else {
|
|
ret = xmlMalloc(len + 10);
|
|
if (ret == NULL)
|
|
return(NULL);
|
|
ret[0] = 'f';
|
|
ret[1] = 'i';
|
|
ret[2] = 'l';
|
|
ret[3] = 'e';
|
|
ret[4] = ':';
|
|
ret[5] = '/';
|
|
ret[6] = '/';
|
|
ret[7] = '/';
|
|
j = 8;
|
|
}
|
|
|
|
while (i < len) {
|
|
/* TODO: UTF8 conversion + URI escaping ??? */
|
|
if (path[i] == '\\')
|
|
ret[j] = '/';
|
|
else
|
|
ret[j] = path[i];
|
|
i++;
|
|
j++;
|
|
}
|
|
ret[j] = 0;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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(__CYGWIN__)
|
|
path = &filename[17];
|
|
#else
|
|
path = &filename[16];
|
|
#endif
|
|
else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
|
|
#if defined (_WIN32) && !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 (__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(__CYGWIN__)
|
|
path = &filename[17];
|
|
#else
|
|
path = &filename[16];
|
|
#endif
|
|
else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
|
|
#if defined (_WIN32) && !defined(__CYGWIN__)
|
|
path = &filename[8];
|
|
#else
|
|
path = &filename[7];
|
|
#endif
|
|
} else
|
|
path = filename;
|
|
|
|
if (path == NULL)
|
|
return(NULL);
|
|
|
|
fd = fopen(path, "w");
|
|
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(__CYGWIN__)
|
|
path = &filename[17];
|
|
#else
|
|
path = &filename[16];
|
|
#endif
|
|
else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
|
|
#if defined (_WIN32) && !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 that try it with an unescaped
|
|
* version of @filename, if this fails fallback to @filename
|
|
*/
|
|
static void *
|
|
xmlGzfileOpen (const char *filename) {
|
|
char *unescaped;
|
|
void *retval;
|
|
unescaped = xmlURIUnescapeString(filename, 0, NULL);
|
|
if (unescaped != NULL) {
|
|
retval = xmlGzfileOpen_real(unescaped);
|
|
} else {
|
|
retval = xmlGzfileOpen_real(filename);
|
|
}
|
|
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(__CYGWIN__)
|
|
path = &filename[17];
|
|
#else
|
|
path = &filename[16];
|
|
#endif
|
|
else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
|
|
#if defined (_WIN32) && !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 ) {
|
|
|
|
int z_err;
|
|
|
|
if ( buff == NULL )
|
|
return;
|
|
|
|
xmlFree( buff->zbuff );
|
|
z_err = deflateEnd( &buff->zctrl );
|
|
#ifdef DEBUG_HTTP
|
|
if ( z_err != Z_OK )
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlFreeZMemBuff: Error releasing zlib context: %d\n",
|
|
z_err );
|
|
#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, "w" );
|
|
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, "w" );
|
|
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 = xmlBufferCreate();
|
|
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 = xmlBufferCreate();
|
|
else
|
|
ret->raw = NULL;
|
|
ret->readcallback = NULL;
|
|
ret->closecallback = NULL;
|
|
ret->context = NULL;
|
|
|
|
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->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 );
|
|
}
|
|
|
|
/**
|
|
* xmlParserInputBufferCreateFname:
|
|
* @URI: a C string containing the URI or filename
|
|
* @enc: the charset encoding if known
|
|
*
|
|
* Returns the new parser input or NULL
|
|
*/
|
|
/**
|
|
* 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;
|
|
char *normalized;
|
|
|
|
if (xmlInputCallbackInitialized == 0)
|
|
xmlRegisterDefaultInputCallbacks();
|
|
|
|
if (URI == NULL) return(NULL);
|
|
normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
|
|
if (normalized == NULL) return(NULL);
|
|
|
|
#ifdef LIBXML_CATALOG_ENABLED
|
|
#endif
|
|
|
|
/*
|
|
* 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(normalized);
|
|
if (context != NULL)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
xmlFree(normalized);
|
|
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;
|
|
}
|
|
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;
|
|
char *normalized;
|
|
|
|
int is_http_uri = 0; /* Can't change if HTTP disabled */
|
|
|
|
if (xmlOutputCallbackInitialized == 0)
|
|
xmlRegisterDefaultOutputCallbacks();
|
|
|
|
if (URI == NULL) return(NULL);
|
|
normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
|
|
if (normalized == NULL) return(NULL);
|
|
|
|
#ifdef LIBXML_HTTP_ENABLED
|
|
/* Need to prevent HTTP URI's from falling into zlib short circuit */
|
|
|
|
is_http_uri = xmlIOHTTPMatch( normalized );
|
|
#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(normalized, 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);
|
|
xmlFree(normalized);
|
|
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(normalized, compression);
|
|
if (context != NULL) {
|
|
ret = xmlAllocOutputBuffer(encoder);
|
|
if (ret != NULL) {
|
|
ret->context = context;
|
|
ret->writecallback = xmlGzfileWrite;
|
|
ret->closecallback = xmlGzfileClose;
|
|
}
|
|
xmlFree(normalized);
|
|
return(ret);
|
|
}
|
|
}
|
|
#endif
|
|
for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
|
|
if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
|
|
(xmlOutputCallbackTable[i].matchcallback(normalized) != 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;
|
|
}
|
|
}
|
|
}
|
|
xmlFree(normalized);
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
if (len > buffree)
|
|
len = buffree;
|
|
|
|
needSize = in->buffer->use + len + 1;
|
|
if (needSize > in->buffer->size){
|
|
if (!xmlBufferResize(in->buffer, needSize)){
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlBufferAdd : 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
|
|
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 *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#ifdef LIBXML_CATALOG_ENABLED
|
|
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(__CYGWIN__)
|
|
path = &URL[17];
|
|
#else
|
|
path = &URL[16];
|
|
#endif
|
|
else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
|
|
#if defined (_WIN32) && !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
|
|
|
|
/**
|
|
* 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,
|
|
"failed to load external entity \"%s\"\n", ID);
|
|
else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
|
|
ctxt->sax->warning(ctxt,
|
|
"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,
|
|
"failed to load external entity \"%s\"\n", resource);
|
|
else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
|
|
ctxt->sax->warning(ctxt,
|
|
"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) {
|
|
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(__CYGWIN__)
|
|
path = &URL[17];
|
|
#else
|
|
path = &URL[16];
|
|
#endif
|
|
else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
|
|
#if defined (_WIN32) && !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);
|
|
}
|
|
|