blob: 0add8237d767dc88a493336d06c9cde43ca4c7eb [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * xmlIO.c : implementation of the I/O interfaces used by the parser
3 *
4 * See Copyright for the status of this software.
5 *
Daniel Veillard344cee72001-08-20 00:08:40 +00006 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00007 *
8 * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char
9 */
10
Daniel Veillard34ce8be2002-03-18 19:37:11 +000011#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000012#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000013
Owen Taylor3473f882001-02-23 17:55:21 +000014#include <string.h>
Daniel Veillard92727042002-09-17 17:59:20 +000015#ifdef HAVE_ERRNO_H
Owen Taylor3473f882001-02-23 17:55:21 +000016#include <errno.h>
Daniel Veillard92727042002-09-17 17:59:20 +000017#endif
18
Owen Taylor3473f882001-02-23 17:55:21 +000019
20#ifdef HAVE_SYS_TYPES_H
21#include <sys/types.h>
22#endif
23#ifdef HAVE_SYS_STAT_H
24#include <sys/stat.h>
25#endif
26#ifdef HAVE_FCNTL_H
27#include <fcntl.h>
28#endif
29#ifdef HAVE_UNISTD_H
30#include <unistd.h>
31#endif
32#ifdef HAVE_STDLIB_H
33#include <stdlib.h>
34#endif
35#ifdef HAVE_ZLIB_H
36#include <zlib.h>
37#endif
38
39/* Figure a portable way to know if a file is a directory. */
40#ifndef HAVE_STAT
41# ifdef HAVE__STAT
Daniel Veillard50f34372001-08-03 12:06:36 +000042 /* MS C library seems to define stat and _stat. The definition
43 is identical. Still, mapping them to each other causes a warning. */
44# ifndef _MSC_VER
45# define stat(x,y) _stat(x,y)
46# endif
Owen Taylor3473f882001-02-23 17:55:21 +000047# define HAVE_STAT
48# endif
49#endif
50#ifdef HAVE_STAT
51# ifndef S_ISDIR
52# ifdef _S_ISDIR
53# define S_ISDIR(x) _S_ISDIR(x)
54# else
55# ifdef S_IFDIR
56# ifndef S_IFMT
57# ifdef _S_IFMT
58# define S_IFMT _S_IFMT
59# endif
60# endif
61# ifdef S_IFMT
62# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
63# endif
64# endif
65# endif
66# endif
67#endif
68
69#include <libxml/xmlmemory.h>
70#include <libxml/parser.h>
71#include <libxml/parserInternals.h>
72#include <libxml/xmlIO.h>
Daniel Veillard388236f2001-07-08 18:35:48 +000073#include <libxml/uri.h>
Owen Taylor3473f882001-02-23 17:55:21 +000074#include <libxml/nanohttp.h>
75#include <libxml/nanoftp.h>
76#include <libxml/xmlerror.h>
Daniel Veillard7d6fd212001-05-10 15:34:11 +000077#ifdef LIBXML_CATALOG_ENABLED
78#include <libxml/catalog.h>
79#endif
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000080#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000081
Daniel Veillardf012a642001-07-23 19:10:52 +000082/* #define VERBOSE_FAILURE */
Daniel Veillard1fd36d22001-07-04 22:54:28 +000083/* #define DEBUG_EXTERNAL_ENTITIES */
Owen Taylor3473f882001-02-23 17:55:21 +000084/* #define DEBUG_INPUT */
85
86#ifdef DEBUG_INPUT
87#define MINLEN 40
88#else
89#define MINLEN 4000
90#endif
91
92/*
93 * Input I/O callback sets
94 */
95typedef struct _xmlInputCallback {
96 xmlInputMatchCallback matchcallback;
97 xmlInputOpenCallback opencallback;
98 xmlInputReadCallback readcallback;
99 xmlInputCloseCallback closecallback;
100} xmlInputCallback;
101
102#define MAX_INPUT_CALLBACK 15
103
Daniel Veillard22090732001-07-16 00:06:07 +0000104static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
105static int xmlInputCallbackNr = 0;
106static int xmlInputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000107
108/*
109 * Output I/O callback sets
110 */
111typedef struct _xmlOutputCallback {
112 xmlOutputMatchCallback matchcallback;
113 xmlOutputOpenCallback opencallback;
114 xmlOutputWriteCallback writecallback;
115 xmlOutputCloseCallback closecallback;
116} xmlOutputCallback;
117
118#define MAX_OUTPUT_CALLBACK 15
119
Daniel Veillard22090732001-07-16 00:06:07 +0000120static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
121static int xmlOutputCallbackNr = 0;
122static int xmlOutputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000123
Daniel Veillardf4862f02002-09-10 11:13:43 +0000124/************************************************************************
125 * *
126 * Handling of Windows file paths *
127 * *
128 ************************************************************************/
129
130#define IS_WINDOWS_PATH(p) \
131 ((p != NULL) && \
132 (((p[0] >= 'a') && (p[0] <= 'z')) || \
133 ((p[0] >= 'A') && (p[0] <= 'Z'))) && \
134 (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\')))
135
136
137/**
Daniel Veillard01c13b52002-12-10 15:19:08 +0000138 * xmlNormalizeWindowsPath:
Daniel Veillardf4862f02002-09-10 11:13:43 +0000139 * @path: a windows path like "C:/foo/bar"
140 *
141 * Normalize a Windows path to make an URL from it
142 *
143 * Returns a new URI which must be freed by the caller or NULL
144 * in case of error
145 */
146xmlChar *
147xmlNormalizeWindowsPath(const xmlChar *path)
148{
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000149 int len, i = 0, j;
Daniel Veillardf4862f02002-09-10 11:13:43 +0000150 xmlChar *ret;
151
152 if (path == NULL)
153 return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +0000154
155 len = xmlStrlen(path);
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000156 if (!IS_WINDOWS_PATH(path)) {
157 ret = xmlStrdup(path);
158 if (ret == NULL)
159 return(NULL);
160 j = 0;
161 } else {
162 ret = xmlMalloc(len + 10);
163 if (ret == NULL)
164 return(NULL);
165 ret[0] = 'f';
166 ret[1] = 'i';
167 ret[2] = 'l';
168 ret[3] = 'e';
169 ret[4] = ':';
170 ret[5] = '/';
171 ret[6] = '/';
172 ret[7] = '/';
173 j = 8;
174 }
Daniel Veillardf4862f02002-09-10 11:13:43 +0000175
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000176 while (i < len) {
Daniel Veillardf4862f02002-09-10 11:13:43 +0000177 /* TODO: UTF8 conversion + URI escaping ??? */
178 if (path[i] == '\\')
179 ret[j] = '/';
180 else
181 ret[j] = path[i];
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000182 i++;
183 j++;
Daniel Veillardf4862f02002-09-10 11:13:43 +0000184 }
185 ret[j] = 0;
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000186
Daniel Veillardf4862f02002-09-10 11:13:43 +0000187 return(ret);
188}
189
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000190/**
191 * xmlCleanupInputCallbacks:
192 *
193 * clears the entire input callback table. this includes the
194 * compiled-in I/O.
195 */
196void
197xmlCleanupInputCallbacks(void)
198{
199 int i;
200
201 if (!xmlInputCallbackInitialized)
202 return;
203
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000204 for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000205 xmlInputCallbackTable[i].matchcallback = NULL;
206 xmlInputCallbackTable[i].opencallback = NULL;
207 xmlInputCallbackTable[i].readcallback = NULL;
208 xmlInputCallbackTable[i].closecallback = NULL;
209 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000210 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000211
212 xmlInputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000213 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000214}
215
216/**
217 * xmlCleanupOutputCallbacks:
218 *
219 * clears the entire output callback table. this includes the
220 * compiled-in I/O callbacks.
221 */
222void
223xmlCleanupOutputCallbacks(void)
224{
225 int i;
226
227 if (!xmlOutputCallbackInitialized)
228 return;
229
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000230 for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000231 xmlOutputCallbackTable[i].matchcallback = NULL;
232 xmlOutputCallbackTable[i].opencallback = NULL;
233 xmlOutputCallbackTable[i].writecallback = NULL;
234 xmlOutputCallbackTable[i].closecallback = NULL;
235 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000236 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000237
238 xmlOutputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000239 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000240}
241
Owen Taylor3473f882001-02-23 17:55:21 +0000242/************************************************************************
243 * *
244 * Standard I/O for file accesses *
245 * *
246 ************************************************************************/
247
248/**
249 * xmlCheckFilename
250 * @path: the path to check
251 *
252 * function checks to see if @path is a valid source
253 * (file, socket...) for XML.
254 *
255 * if stat is not available on the target machine,
256 * returns 1. if stat fails, returns 0 (if calling
257 * stat on the filename fails, it can't be right).
258 * if stat succeeds and the file is a directory,
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000259 * returns 2. otherwise returns 1.
Owen Taylor3473f882001-02-23 17:55:21 +0000260 */
261
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000262int
Owen Taylor3473f882001-02-23 17:55:21 +0000263xmlCheckFilename (const char *path)
264{
265#ifdef HAVE_STAT
Owen Taylor3473f882001-02-23 17:55:21 +0000266 struct stat stat_buffer;
267
268 if (stat(path, &stat_buffer) == -1)
269 return 0;
270
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000271#ifdef S_ISDIR
Owen Taylor3473f882001-02-23 17:55:21 +0000272 if (S_ISDIR(stat_buffer.st_mode)) {
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000273 return 2;
Owen Taylor3473f882001-02-23 17:55:21 +0000274 }
Owen Taylor3473f882001-02-23 17:55:21 +0000275#endif
276#endif
277 return 1;
278}
279
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000280static int
Owen Taylor3473f882001-02-23 17:55:21 +0000281xmlNop(void) {
282 return(0);
283}
284
285/**
Owen Taylor3473f882001-02-23 17:55:21 +0000286 * xmlFdRead:
287 * @context: the I/O context
288 * @buffer: where to drop data
289 * @len: number of bytes to read
290 *
291 * Read @len bytes to @buffer from the I/O channel.
292 *
293 * Returns the number of bytes written
294 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000295static int
Owen Taylor3473f882001-02-23 17:55:21 +0000296xmlFdRead (void * context, char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000297 return(read((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000298}
299
300/**
301 * xmlFdWrite:
302 * @context: the I/O context
303 * @buffer: where to get data
304 * @len: number of bytes to write
305 *
306 * Write @len bytes from @buffer to the I/O channel.
307 *
308 * Returns the number of bytes written
309 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000310static int
Owen Taylor3473f882001-02-23 17:55:21 +0000311xmlFdWrite (void * context, const char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000312 return(write((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000313}
314
315/**
316 * xmlFdClose:
317 * @context: the I/O context
318 *
319 * Close an I/O channel
Daniel Veillardf012a642001-07-23 19:10:52 +0000320 *
321 * Returns 0 in case of success and error code otherwise
Owen Taylor3473f882001-02-23 17:55:21 +0000322 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000323static int
Owen Taylor3473f882001-02-23 17:55:21 +0000324xmlFdClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000325 return ( close((int) (long) context) );
Owen Taylor3473f882001-02-23 17:55:21 +0000326}
327
328/**
329 * xmlFileMatch:
330 * @filename: the URI for matching
331 *
332 * input from FILE *
333 *
334 * Returns 1 if matches, 0 otherwise
335 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000336int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000337xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000338 return(1);
339}
340
341/**
342 * xmlFileOpen:
343 * @filename: the URI for matching
344 *
345 * input from FILE *, supports compressed input
346 * if @filename is " " then the standard input is used
347 *
348 * Returns an I/O context or NULL in case of error
349 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000350void *
Owen Taylor3473f882001-02-23 17:55:21 +0000351xmlFileOpen (const char *filename) {
352 const char *path = NULL;
353 FILE *fd;
354
355 if (!strcmp(filename, "-")) {
356 fd = stdin;
357 return((void *) fd);
358 }
359
Daniel Veillardf4862f02002-09-10 11:13:43 +0000360 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000361#if defined (_WIN32) && !defined(__CYGWIN__)
362 path = &filename[17];
363#else
Owen Taylor3473f882001-02-23 17:55:21 +0000364 path = &filename[16];
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000365#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000366 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000367#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000368 path = &filename[8];
369#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000370 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000371#endif
372 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000373 path = filename;
374
375 if (path == NULL)
376 return(NULL);
377 if (!xmlCheckFilename(path))
378 return(NULL);
379
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000380#if defined(WIN32) || defined (__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +0000381 fd = fopen(path, "rb");
382#else
383 fd = fopen(path, "r");
384#endif /* WIN32 */
385 return((void *) fd);
386}
387
388/**
389 * xmlFileOpenW:
390 * @filename: the URI for matching
391 *
392 * output to from FILE *,
393 * if @filename is "-" then the standard output is used
394 *
395 * Returns an I/O context or NULL in case of error
396 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000397static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000398xmlFileOpenW (const char *filename) {
399 const char *path = NULL;
400 FILE *fd;
401
402 if (!strcmp(filename, "-")) {
403 fd = stdout;
404 return((void *) fd);
405 }
406
Daniel Veillardf4862f02002-09-10 11:13:43 +0000407 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
408#if defined (_WIN32) && !defined(__CYGWIN__)
409 path = &filename[17];
410#else
Owen Taylor3473f882001-02-23 17:55:21 +0000411 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000412#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000413 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000414#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000415 path = &filename[8];
416#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000417 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000418#endif
419 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000420 path = filename;
421
422 if (path == NULL)
423 return(NULL);
424
425 fd = fopen(path, "w");
426 return((void *) fd);
427}
428
429/**
430 * xmlFileRead:
431 * @context: the I/O context
432 * @buffer: where to drop data
433 * @len: number of bytes to write
434 *
435 * Read @len bytes to @buffer from the I/O channel.
436 *
437 * Returns the number of bytes written
438 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000439int
Owen Taylor3473f882001-02-23 17:55:21 +0000440xmlFileRead (void * context, char * buffer, int len) {
441 return(fread(&buffer[0], 1, len, (FILE *) context));
442}
443
444/**
445 * xmlFileWrite:
446 * @context: the I/O context
447 * @buffer: where to drop data
448 * @len: number of bytes to write
449 *
450 * Write @len bytes from @buffer to the I/O channel.
451 *
452 * Returns the number of bytes written
453 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000454static int
Owen Taylor3473f882001-02-23 17:55:21 +0000455xmlFileWrite (void * context, const char * buffer, int len) {
456 return(fwrite(&buffer[0], 1, len, (FILE *) context));
457}
458
459/**
460 * xmlFileClose:
461 * @context: the I/O context
462 *
463 * Close an I/O channel
464 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000465int
Owen Taylor3473f882001-02-23 17:55:21 +0000466xmlFileClose (void * context) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000467 FILE *fil;
468
469 fil = (FILE *) context;
470 if (fil == stdin)
471 return(0);
472 if (fil == stdout)
473 return(0);
Daniel Veillardcd337f02001-11-22 18:20:37 +0000474 if (fil == stderr)
475 return(0);
Daniel Veillardf012a642001-07-23 19:10:52 +0000476 return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000477}
478
479/**
480 * xmlFileFlush:
481 * @context: the I/O context
482 *
483 * Flush an I/O channel
484 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000485static int
Owen Taylor3473f882001-02-23 17:55:21 +0000486xmlFileFlush (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000487 return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000488}
489
490#ifdef HAVE_ZLIB_H
491/************************************************************************
492 * *
493 * I/O for compressed file accesses *
494 * *
495 ************************************************************************/
496/**
497 * xmlGzfileMatch:
498 * @filename: the URI for matching
499 *
500 * input from compressed file test
501 *
502 * Returns 1 if matches, 0 otherwise
503 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000504static int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000505xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000506 return(1);
507}
508
509/**
510 * xmlGzfileOpen:
511 * @filename: the URI for matching
512 *
513 * input from compressed file open
514 * if @filename is " " then the standard input is used
515 *
516 * Returns an I/O context or NULL in case of error
517 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000518static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000519xmlGzfileOpen (const char *filename) {
520 const char *path = NULL;
521 gzFile fd;
522
523 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000524 fd = gzdopen(dup(0), "rb");
Owen Taylor3473f882001-02-23 17:55:21 +0000525 return((void *) fd);
526 }
527
Daniel Veillardf4862f02002-09-10 11:13:43 +0000528 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
529#if defined (_WIN32) && !defined(__CYGWIN__)
530 path = &filename[17];
531#else
Owen Taylor3473f882001-02-23 17:55:21 +0000532 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000533#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000534 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000535#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000536 path = &filename[8];
537#else
Owen Taylor3473f882001-02-23 17:55:21 +0000538 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000539#endif
540 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000541 path = filename;
542
543 if (path == NULL)
544 return(NULL);
545 if (!xmlCheckFilename(path))
546 return(NULL);
547
548 fd = gzopen(path, "rb");
549 return((void *) fd);
550}
551
552/**
553 * xmlGzfileOpenW:
554 * @filename: the URI for matching
555 * @compression: the compression factor (0 - 9 included)
556 *
557 * input from compressed file open
558 * if @filename is " " then the standard input is used
559 *
560 * Returns an I/O context or NULL in case of error
561 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000562static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000563xmlGzfileOpenW (const char *filename, int compression) {
564 const char *path = NULL;
565 char mode[15];
566 gzFile fd;
567
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000568 snprintf(mode, sizeof(mode), "wb%d", compression);
Owen Taylor3473f882001-02-23 17:55:21 +0000569 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000570 fd = gzdopen(dup(1), mode);
Owen Taylor3473f882001-02-23 17:55:21 +0000571 return((void *) fd);
572 }
573
Daniel Veillardf4862f02002-09-10 11:13:43 +0000574 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
575#if defined (_WIN32) && !defined(__CYGWIN__)
576 path = &filename[17];
577#else
Owen Taylor3473f882001-02-23 17:55:21 +0000578 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000579#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000580 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000581#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000582 path = &filename[8];
583#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000584 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000585#endif
586 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000587 path = filename;
588
589 if (path == NULL)
590 return(NULL);
591
592 fd = gzopen(path, mode);
593 return((void *) fd);
594}
595
596/**
597 * xmlGzfileRead:
598 * @context: the I/O context
599 * @buffer: where to drop data
600 * @len: number of bytes to write
601 *
602 * Read @len bytes to @buffer from the compressed I/O channel.
603 *
604 * Returns the number of bytes written
605 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000606static int
Owen Taylor3473f882001-02-23 17:55:21 +0000607xmlGzfileRead (void * context, char * buffer, int len) {
608 return(gzread((gzFile) context, &buffer[0], len));
609}
610
611/**
612 * xmlGzfileWrite:
613 * @context: the I/O context
614 * @buffer: where to drop data
615 * @len: number of bytes to write
616 *
617 * Write @len bytes from @buffer to the compressed I/O channel.
618 *
619 * Returns the number of bytes written
620 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000621static int
Owen Taylor3473f882001-02-23 17:55:21 +0000622xmlGzfileWrite (void * context, const char * buffer, int len) {
623 return(gzwrite((gzFile) context, (char *) &buffer[0], len));
624}
625
626/**
627 * xmlGzfileClose:
628 * @context: the I/O context
629 *
630 * Close a compressed I/O channel
631 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000632static int
Owen Taylor3473f882001-02-23 17:55:21 +0000633xmlGzfileClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000634 return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
Owen Taylor3473f882001-02-23 17:55:21 +0000635}
636#endif /* HAVE_ZLIB_H */
637
638#ifdef LIBXML_HTTP_ENABLED
639/************************************************************************
640 * *
641 * I/O for HTTP file accesses *
642 * *
643 ************************************************************************/
Daniel Veillardf012a642001-07-23 19:10:52 +0000644
645typedef struct xmlIOHTTPWriteCtxt_
646{
647 int compression;
648
649 char * uri;
650
651 void * doc_buff;
652
653} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
654
655#ifdef HAVE_ZLIB_H
656
657#define DFLT_WBITS ( -15 )
658#define DFLT_MEM_LVL ( 8 )
659#define GZ_MAGIC1 ( 0x1f )
660#define GZ_MAGIC2 ( 0x8b )
661#define LXML_ZLIB_OS_CODE ( 0x03 )
662#define INIT_HTTP_BUFF_SIZE ( 32768 )
663#define DFLT_ZLIB_RATIO ( 5 )
664
665/*
666** Data structure and functions to work with sending compressed data
667** via HTTP.
668*/
669
670typedef struct xmlZMemBuff_
671{
672 unsigned long size;
673 unsigned long crc;
674
675 unsigned char * zbuff;
676 z_stream zctrl;
677
678} xmlZMemBuff, *xmlZMemBuffPtr;
679
680/**
681 * append_reverse_ulong
682 * @buff: Compressed memory buffer
683 * @data: Unsigned long to append
684 *
685 * Append a unsigned long in reverse byte order to the end of the
686 * memory buffer.
687 */
688static void
689append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
690
691 int idx;
692
693 if ( buff == NULL )
694 return;
695
696 /*
697 ** This is plagiarized from putLong in gzio.c (zlib source) where
698 ** the number "4" is hardcoded. If zlib is ever patched to
699 ** support 64 bit file sizes, this code would need to be patched
700 ** as well.
701 */
702
703 for ( idx = 0; idx < 4; idx++ ) {
704 *buff->zctrl.next_out = ( data & 0xff );
705 data >>= 8;
706 buff->zctrl.next_out++;
707 }
708
709 return;
710}
711
712/**
713 *
714 * xmlFreeZMemBuff
715 * @buff: The memory buffer context to clear
716 *
717 * Release all the resources associated with the compressed memory buffer.
718 */
719static void
720xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
721
722 int z_err;
723
724 if ( buff == NULL )
725 return;
726
727 xmlFree( buff->zbuff );
728 z_err = deflateEnd( &buff->zctrl );
729#ifdef DEBUG_HTTP
730 if ( z_err != Z_OK )
731 xmlGenericError( xmlGenericErrorContext,
732 "xmlFreeZMemBuff: Error releasing zlib context: %d\n",
733 z_err );
734#endif
735
736 xmlFree( buff );
737 return;
738}
739
740/**
741 * xmlCreateZMemBuff
742 *@compression: Compression value to use
743 *
744 * Create a memory buffer to hold the compressed XML document. The
745 * compressed document in memory will end up being identical to what
746 * would be created if gzopen/gzwrite/gzclose were being used to
747 * write the document to disk. The code for the header/trailer data to
748 * the compression is plagiarized from the zlib source files.
749 */
750static void *
751xmlCreateZMemBuff( int compression ) {
752
753 int z_err;
754 int hdr_lgth;
755 xmlZMemBuffPtr buff = NULL;
756
757 if ( ( compression < 1 ) || ( compression > 9 ) )
758 return ( NULL );
759
760 /* Create the control and data areas */
761
762 buff = xmlMalloc( sizeof( xmlZMemBuff ) );
763 if ( buff == NULL ) {
764 xmlGenericError( xmlGenericErrorContext,
765 "xmlCreateZMemBuff: %s\n",
766 "Failure allocating buffer context." );
767 return ( NULL );
768 }
769
770 (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
771 buff->size = INIT_HTTP_BUFF_SIZE;
772 buff->zbuff = xmlMalloc( buff->size );
773 if ( buff->zbuff == NULL ) {
774 xmlFreeZMemBuff( buff );
775 xmlGenericError( xmlGenericErrorContext,
776 "xmlCreateZMemBuff: %s\n",
777 "Failure allocating data buffer." );
778 return ( NULL );
779 }
780
781 z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
782 DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
783 if ( z_err != Z_OK ) {
784 xmlFreeZMemBuff( buff );
785 buff = NULL;
786 xmlGenericError( xmlGenericErrorContext,
787 "xmlCreateZMemBuff: %s %d\n",
788 "Error initializing compression context. ZLIB error:",
789 z_err );
790 return ( NULL );
791 }
792
793 /* Set the header data. The CRC will be needed for the trailer */
Daniel Veillardf012a642001-07-23 19:10:52 +0000794 buff->crc = crc32( 0L, Z_NULL, 0 );
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000795 hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
796 "%c%c%c%c%c%c%c%c%c%c",
Daniel Veillardf012a642001-07-23 19:10:52 +0000797 GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
798 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
799 buff->zctrl.next_out = buff->zbuff + hdr_lgth;
800 buff->zctrl.avail_out = buff->size - hdr_lgth;
801
802 return ( buff );
803}
804
805/**
806 * xmlZMemBuffExtend
807 * @buff: Buffer used to compress and consolidate data.
808 * @ext_amt: Number of bytes to extend the buffer.
809 *
810 * Extend the internal buffer used to store the compressed data by the
811 * specified amount.
812 *
813 * Returns 0 on success or -1 on failure to extend the buffer. On failure
814 * the original buffer still exists at the original size.
815 */
816static int
817xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
818
819 int rc = -1;
820 size_t new_size;
821 size_t cur_used;
822
823 unsigned char * tmp_ptr = NULL;
824
825 if ( buff == NULL )
826 return ( -1 );
827
828 else if ( ext_amt == 0 )
829 return ( 0 );
830
831 cur_used = buff->zctrl.next_out - buff->zbuff;
832 new_size = buff->size + ext_amt;
833
834#ifdef DEBUG_HTTP
835 if ( cur_used > new_size )
836 xmlGenericError( xmlGenericErrorContext,
837 "xmlZMemBuffExtend: %s\n%s %d bytes.\n",
838 "Buffer overwrite detected during compressed memory",
839 "buffer extension. Overflowed by",
840 (cur_used - new_size ) );
841#endif
842
843 tmp_ptr = xmlRealloc( buff->zbuff, new_size );
844 if ( tmp_ptr != NULL ) {
845 rc = 0;
846 buff->size = new_size;
847 buff->zbuff = tmp_ptr;
848 buff->zctrl.next_out = tmp_ptr + cur_used;
849 buff->zctrl.avail_out = new_size - cur_used;
850 }
851 else {
852 xmlGenericError( xmlGenericErrorContext,
853 "xmlZMemBuffExtend: %s %lu bytes.\n",
854 "Allocation failure extending output buffer to",
855 new_size );
856 }
857
858 return ( rc );
859}
860
861/**
862 * xmlZMemBuffAppend
863 * @buff: Buffer used to compress and consolidate data
864 * @src: Uncompressed source content to append to buffer
865 * @len: Length of source data to append to buffer
866 *
867 * Compress and append data to the internal buffer. The data buffer
868 * will be expanded if needed to store the additional data.
869 *
870 * Returns the number of bytes appended to the buffer or -1 on error.
871 */
872static int
873xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
874
875 int z_err;
876 size_t min_accept;
877
878 if ( ( buff == NULL ) || ( src == NULL ) )
879 return ( -1 );
880
881 buff->zctrl.avail_in = len;
882 buff->zctrl.next_in = (unsigned char *)src;
883 while ( buff->zctrl.avail_in > 0 ) {
884 /*
885 ** Extend the buffer prior to deflate call if a reasonable amount
886 ** of output buffer space is not available.
887 */
888 min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
889 if ( buff->zctrl.avail_out <= min_accept ) {
890 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
891 return ( -1 );
892 }
893
894 z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
895 if ( z_err != Z_OK ) {
896 xmlGenericError( xmlGenericErrorContext,
897 "xmlZMemBuffAppend: %s %d %s - %d",
898 "Compression error while appending",
899 len, "bytes to buffer. ZLIB error", z_err );
900 return ( -1 );
901 }
902 }
903
904 buff->crc = crc32( buff->crc, (unsigned char *)src, len );
905
906 return ( len );
907}
908
909/**
910 * xmlZMemBuffGetContent
911 * @buff: Compressed memory content buffer
912 * @data_ref: Pointer reference to point to compressed content
913 *
914 * Flushes the compression buffers, appends gzip file trailers and
915 * returns the compressed content and length of the compressed data.
916 * NOTE: The gzip trailer code here is plagiarized from zlib source.
917 *
918 * Returns the length of the compressed data or -1 on error.
919 */
920static int
921xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
922
923 int zlgth = -1;
924 int z_err;
925
926 if ( ( buff == NULL ) || ( data_ref == NULL ) )
927 return ( -1 );
928
929 /* Need to loop until compression output buffers are flushed */
930
931 do
932 {
933 z_err = deflate( &buff->zctrl, Z_FINISH );
934 if ( z_err == Z_OK ) {
935 /* In this case Z_OK means more buffer space needed */
936
937 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
938 return ( -1 );
939 }
940 }
941 while ( z_err == Z_OK );
942
943 /* If the compression state is not Z_STREAM_END, some error occurred */
944
945 if ( z_err == Z_STREAM_END ) {
946
947 /* Need to append the gzip data trailer */
948
949 if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
950 if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
951 return ( -1 );
952 }
953
954 /*
955 ** For whatever reason, the CRC and length data are pushed out
956 ** in reverse byte order. So a memcpy can't be used here.
957 */
958
959 append_reverse_ulong( buff, buff->crc );
960 append_reverse_ulong( buff, buff->zctrl.total_in );
961
962 zlgth = buff->zctrl.next_out - buff->zbuff;
963 *data_ref = (char *)buff->zbuff;
964 }
965
966 else
967 xmlGenericError( xmlGenericErrorContext,
968 "xmlZMemBuffGetContent: %s - %d\n",
969 "Error flushing zlib buffers. Error code", z_err );
970
971 return ( zlgth );
972}
973#endif /* HAVE_ZLIB_H */
974
975/**
976 * xmlFreeHTTPWriteCtxt
977 * @ctxt: Context to cleanup
978 *
979 * Free allocated memory and reclaim system resources.
980 *
981 * No return value.
982 */
983static void
984xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
985{
986 if ( ctxt->uri != NULL )
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000987 xmlFree( ctxt->uri );
Daniel Veillardf012a642001-07-23 19:10:52 +0000988
989 if ( ctxt->doc_buff != NULL ) {
990
991#ifdef HAVE_ZLIB_H
992 if ( ctxt->compression > 0 ) {
993 xmlFreeZMemBuff( ctxt->doc_buff );
994 }
995 else
996#endif
997 {
998 xmlOutputBufferClose( ctxt->doc_buff );
999 }
1000 }
1001
Daniel Veillard819d5cb2002-10-14 11:15:18 +00001002 xmlFree( ctxt );
Daniel Veillardf012a642001-07-23 19:10:52 +00001003 return;
1004}
1005
1006
Owen Taylor3473f882001-02-23 17:55:21 +00001007/**
1008 * xmlIOHTTPMatch:
1009 * @filename: the URI for matching
1010 *
1011 * check if the URI matches an HTTP one
1012 *
1013 * Returns 1 if matches, 0 otherwise
1014 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001015int
Owen Taylor3473f882001-02-23 17:55:21 +00001016xmlIOHTTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001017 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
Owen Taylor3473f882001-02-23 17:55:21 +00001018 return(1);
1019 return(0);
1020}
1021
1022/**
1023 * xmlIOHTTPOpen:
1024 * @filename: the URI for matching
1025 *
1026 * open an HTTP I/O channel
1027 *
1028 * Returns an I/O context or NULL in case of error
1029 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001030void *
Owen Taylor3473f882001-02-23 17:55:21 +00001031xmlIOHTTPOpen (const char *filename) {
1032 return(xmlNanoHTTPOpen(filename, NULL));
1033}
1034
1035/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001036 * xmlIOHTTPOpenW
1037 * @post_uri: The destination URI for the document
1038 * @compression: The compression desired for the document.
1039 *
1040 * Open a temporary buffer to collect the document for a subsequent HTTP POST
1041 * request. Non-static as is called from the output buffer creation routine.
1042 *
1043 * Returns an I/O context or NULL in case of error.
1044 */
1045
1046void *
Daniel Veillard572577e2002-01-18 16:23:55 +00001047xmlIOHTTPOpenW(const char *post_uri, int compression)
1048{
Daniel Veillardf012a642001-07-23 19:10:52 +00001049
Daniel Veillard572577e2002-01-18 16:23:55 +00001050 xmlIOHTTPWriteCtxtPtr ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001051
Daniel Veillard572577e2002-01-18 16:23:55 +00001052 if (post_uri == NULL)
1053 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001054
Daniel Veillard572577e2002-01-18 16:23:55 +00001055 ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1056 if (ctxt == NULL) {
1057 xmlGenericError(xmlGenericErrorContext,
1058 "xmlIOHTTPOpenW: Failed to create output HTTP context.\n");
1059 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001060 }
1061
Daniel Veillard572577e2002-01-18 16:23:55 +00001062 (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
Daniel Veillardf012a642001-07-23 19:10:52 +00001063
Daniel Veillard572577e2002-01-18 16:23:55 +00001064 ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1065 if (ctxt->uri == NULL) {
1066 xmlGenericError(xmlGenericErrorContext,
1067 "xmlIOHTTPOpenW: Failed to duplicate destination URI.\n");
1068 xmlFreeHTTPWriteCtxt(ctxt);
1069 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001070 }
1071
1072 /*
Daniel Veillard572577e2002-01-18 16:23:55 +00001073 * ** Since the document length is required for an HTTP post,
1074 * ** need to put the document into a buffer. A memory buffer
1075 * ** is being used to avoid pushing the data to disk and back.
1076 */
Daniel Veillardf012a642001-07-23 19:10:52 +00001077
1078#ifdef HAVE_ZLIB_H
Daniel Veillard572577e2002-01-18 16:23:55 +00001079 if ((compression > 0) && (compression <= 9)) {
1080
1081 ctxt->compression = compression;
1082 ctxt->doc_buff = xmlCreateZMemBuff(compression);
1083 } else
Daniel Veillardf012a642001-07-23 19:10:52 +00001084#endif
1085 {
Daniel Veillard572577e2002-01-18 16:23:55 +00001086 /* Any character conversions should have been done before this */
Daniel Veillardf012a642001-07-23 19:10:52 +00001087
Daniel Veillard572577e2002-01-18 16:23:55 +00001088 ctxt->doc_buff = xmlAllocOutputBuffer(NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001089 }
1090
Daniel Veillard572577e2002-01-18 16:23:55 +00001091 if (ctxt->doc_buff == NULL) {
1092 xmlFreeHTTPWriteCtxt(ctxt);
1093 ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001094 }
1095
Daniel Veillard572577e2002-01-18 16:23:55 +00001096 return (ctxt);
Daniel Veillardf012a642001-07-23 19:10:52 +00001097}
1098
1099/**
1100 * xmlIOHTTPDfltOpenW
1101 * @post_uri: The destination URI for this document.
1102 *
1103 * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1104 * HTTP post command. This function should generally not be used as
1105 * the open callback is short circuited in xmlOutputBufferCreateFile.
1106 *
1107 * Returns a pointer to the new IO context.
1108 */
1109static void *
1110xmlIOHTTPDfltOpenW( const char * post_uri ) {
1111 return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1112}
1113
1114/**
Owen Taylor3473f882001-02-23 17:55:21 +00001115 * xmlIOHTTPRead:
1116 * @context: the I/O context
1117 * @buffer: where to drop data
1118 * @len: number of bytes to write
1119 *
1120 * Read @len bytes to @buffer from the I/O channel.
1121 *
1122 * Returns the number of bytes written
1123 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001124int
Owen Taylor3473f882001-02-23 17:55:21 +00001125xmlIOHTTPRead(void * context, char * buffer, int len) {
1126 return(xmlNanoHTTPRead(context, &buffer[0], len));
1127}
1128
1129/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001130 * xmlIOHTTPWrite
1131 * @context: previously opened writing context
1132 * @buffer: data to output to temporary buffer
1133 * @len: bytes to output
1134 *
1135 * Collect data from memory buffer into a temporary file for later
1136 * processing.
1137 *
1138 * Returns number of bytes written.
1139 */
1140
1141static int
1142xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
1143
1144 xmlIOHTTPWriteCtxtPtr ctxt = context;
1145
1146 if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1147 return ( -1 );
1148
1149 if ( len > 0 ) {
1150
1151 /* Use gzwrite or fwrite as previously setup in the open call */
1152
1153#ifdef HAVE_ZLIB_H
1154 if ( ctxt->compression > 0 )
1155 len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1156
1157 else
1158#endif
1159 len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
1160
1161 if ( len < 0 ) {
1162 xmlGenericError( xmlGenericErrorContext,
1163 "xmlIOHTTPWrite: %s\n%s '%s'.\n",
1164 "Error appending to internal buffer.",
1165 "Error sending document to URI",
1166 ctxt->uri );
1167 }
1168 }
1169
1170 return ( len );
1171}
1172
1173
1174/**
Owen Taylor3473f882001-02-23 17:55:21 +00001175 * xmlIOHTTPClose:
1176 * @context: the I/O context
1177 *
1178 * Close an HTTP I/O channel
1179 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001180int
Owen Taylor3473f882001-02-23 17:55:21 +00001181xmlIOHTTPClose (void * context) {
1182 xmlNanoHTTPClose(context);
Daniel Veillardf012a642001-07-23 19:10:52 +00001183 return 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001184}
Daniel Veillardf012a642001-07-23 19:10:52 +00001185
1186/**
1187 * xmlIOHTTCloseWrite
1188 * @context: The I/O context
1189 * @http_mthd: The HTTP method to be used when sending the data
1190 *
1191 * Close the transmit HTTP I/O channel and actually send the data.
1192 */
1193static int
1194xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
1195
1196 int close_rc = -1;
1197 int http_rtn = 0;
1198 int content_lgth = 0;
1199 xmlIOHTTPWriteCtxtPtr ctxt = context;
1200
1201 char * http_content = NULL;
1202 char * content_encoding = NULL;
1203 char * content_type = (char *) "text/xml";
1204 void * http_ctxt = NULL;
1205
1206 if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
1207 return ( -1 );
1208
1209 /* Retrieve the content from the appropriate buffer */
1210
1211#ifdef HAVE_ZLIB_H
1212
1213 if ( ctxt->compression > 0 ) {
1214 content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
1215 content_encoding = (char *) "Content-Encoding: gzip";
1216 }
1217 else
1218#endif
1219 {
1220 /* Pull the data out of the memory output buffer */
1221
1222 xmlOutputBufferPtr dctxt = ctxt->doc_buff;
1223 http_content = (char *)dctxt->buffer->content;
1224 content_lgth = dctxt->buffer->use;
1225 }
1226
1227 if ( http_content == NULL ) {
1228 xmlGenericError( xmlGenericErrorContext,
1229 "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n",
1230 "Error retrieving content.\nUnable to",
1231 http_mthd, "data to URI", ctxt->uri );
1232 }
1233
1234 else {
1235
1236 http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
1237 &content_type, content_encoding,
1238 content_lgth );
1239
1240 if ( http_ctxt != NULL ) {
1241#ifdef DEBUG_HTTP
1242 /* If testing/debugging - dump reply with request content */
1243
1244 FILE * tst_file = NULL;
1245 char buffer[ 4096 ];
1246 char * dump_name = NULL;
1247 int avail;
1248
1249 xmlGenericError( xmlGenericErrorContext,
1250 "xmlNanoHTTPCloseWrite: HTTP %s to\n%s returned %d.\n",
1251 http_mthd, ctxt->uri,
1252 xmlNanoHTTPReturnCode( http_ctxt ) );
1253
1254 /*
1255 ** Since either content or reply may be gzipped,
1256 ** dump them to separate files instead of the
1257 ** standard error context.
1258 */
1259
1260 dump_name = tempnam( NULL, "lxml" );
1261 if ( dump_name != NULL ) {
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001262 (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001263
1264 tst_file = fopen( buffer, "w" );
1265 if ( tst_file != NULL ) {
1266 xmlGenericError( xmlGenericErrorContext,
1267 "Transmitted content saved in file: %s\n", buffer );
1268
1269 fwrite( http_content, sizeof( char ),
1270 content_lgth, tst_file );
1271 fclose( tst_file );
1272 }
1273
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001274 (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001275 tst_file = fopen( buffer, "w" );
1276 if ( tst_file != NULL ) {
1277 xmlGenericError( xmlGenericErrorContext,
1278 "Reply content saved in file: %s\n", buffer );
1279
1280
1281 while ( (avail = xmlNanoHTTPRead( http_ctxt,
1282 buffer, sizeof( buffer ) )) > 0 ) {
1283
1284 fwrite( buffer, sizeof( char ), avail, tst_file );
1285 }
1286
1287 fclose( tst_file );
1288 }
1289
1290 free( dump_name );
1291 }
1292#endif /* DEBUG_HTTP */
1293
1294 http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
1295 if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
1296 close_rc = 0;
1297 else
1298 xmlGenericError( xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001299 "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
Daniel Veillardf012a642001-07-23 19:10:52 +00001300 http_mthd, content_lgth,
1301 "bytes to URI", ctxt->uri,
1302 "failed. HTTP return code:", http_rtn );
1303
1304 xmlNanoHTTPClose( http_ctxt );
1305 xmlFree( content_type );
1306 }
1307 }
1308
1309 /* Final cleanups */
1310
1311 xmlFreeHTTPWriteCtxt( ctxt );
1312
1313 return ( close_rc );
1314}
1315
1316/**
1317 * xmlIOHTTPClosePut
1318 *
1319 * @context: The I/O context
1320 *
1321 * Close the transmit HTTP I/O channel and actually send data using a PUT
1322 * HTTP method.
1323 */
1324static int
1325xmlIOHTTPClosePut( void * ctxt ) {
1326 return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
1327}
1328
1329
1330/**
1331 * xmlIOHTTPClosePost
1332 *
1333 * @context: The I/O context
1334 *
1335 * Close the transmit HTTP I/O channel and actually send data using a POST
1336 * HTTP method.
1337 */
1338static int
1339xmlIOHTTPClosePost( void * ctxt ) {
1340 return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
1341}
1342
Owen Taylor3473f882001-02-23 17:55:21 +00001343#endif /* LIBXML_HTTP_ENABLED */
1344
1345#ifdef LIBXML_FTP_ENABLED
1346/************************************************************************
1347 * *
1348 * I/O for FTP file accesses *
1349 * *
1350 ************************************************************************/
1351/**
1352 * xmlIOFTPMatch:
1353 * @filename: the URI for matching
1354 *
1355 * check if the URI matches an FTP one
1356 *
1357 * Returns 1 if matches, 0 otherwise
1358 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001359int
Owen Taylor3473f882001-02-23 17:55:21 +00001360xmlIOFTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001361 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
Owen Taylor3473f882001-02-23 17:55:21 +00001362 return(1);
1363 return(0);
1364}
1365
1366/**
1367 * xmlIOFTPOpen:
1368 * @filename: the URI for matching
1369 *
1370 * open an FTP I/O channel
1371 *
1372 * Returns an I/O context or NULL in case of error
1373 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001374void *
Owen Taylor3473f882001-02-23 17:55:21 +00001375xmlIOFTPOpen (const char *filename) {
1376 return(xmlNanoFTPOpen(filename));
1377}
1378
1379/**
1380 * xmlIOFTPRead:
1381 * @context: the I/O context
1382 * @buffer: where to drop data
1383 * @len: number of bytes to write
1384 *
1385 * Read @len bytes to @buffer from the I/O channel.
1386 *
1387 * Returns the number of bytes written
1388 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001389int
Owen Taylor3473f882001-02-23 17:55:21 +00001390xmlIOFTPRead(void * context, char * buffer, int len) {
1391 return(xmlNanoFTPRead(context, &buffer[0], len));
1392}
1393
1394/**
1395 * xmlIOFTPClose:
1396 * @context: the I/O context
1397 *
1398 * Close an FTP I/O channel
1399 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001400int
Owen Taylor3473f882001-02-23 17:55:21 +00001401xmlIOFTPClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001402 return ( xmlNanoFTPClose(context) );
Owen Taylor3473f882001-02-23 17:55:21 +00001403}
1404#endif /* LIBXML_FTP_ENABLED */
1405
1406
1407/**
1408 * xmlRegisterInputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001409 * @matchFunc: the xmlInputMatchCallback
1410 * @openFunc: the xmlInputOpenCallback
1411 * @readFunc: the xmlInputReadCallback
1412 * @closeFunc: the xmlInputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001413 *
1414 * Register a new set of I/O callback for handling parser input.
1415 *
1416 * Returns the registered handler number or -1 in case of error
1417 */
1418int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001419xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
1420 xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
1421 xmlInputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001422 if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
1423 return(-1);
1424 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001425 xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
1426 xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
1427 xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
1428 xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001429 return(xmlInputCallbackNr++);
1430}
1431
1432/**
1433 * xmlRegisterOutputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001434 * @matchFunc: the xmlOutputMatchCallback
1435 * @openFunc: the xmlOutputOpenCallback
1436 * @writeFunc: the xmlOutputWriteCallback
1437 * @closeFunc: the xmlOutputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001438 *
1439 * Register a new set of I/O callback for handling output.
1440 *
1441 * Returns the registered handler number or -1 in case of error
1442 */
1443int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001444xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
1445 xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
1446 xmlOutputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001447 if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) {
1448 return(-1);
1449 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001450 xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
1451 xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
1452 xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
1453 xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001454 return(xmlOutputCallbackNr++);
1455}
1456
1457/**
1458 * xmlRegisterDefaultInputCallbacks:
1459 *
1460 * Registers the default compiled-in I/O handlers.
1461 */
1462void
Owen Taylor3473f882001-02-23 17:55:21 +00001463xmlRegisterDefaultInputCallbacks
Owen Taylor3473f882001-02-23 17:55:21 +00001464(void) {
1465 if (xmlInputCallbackInitialized)
1466 return;
1467
1468 xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
1469 xmlFileRead, xmlFileClose);
1470#ifdef HAVE_ZLIB_H
1471 xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1472 xmlGzfileRead, xmlGzfileClose);
1473#endif /* HAVE_ZLIB_H */
1474
1475#ifdef LIBXML_HTTP_ENABLED
1476 xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
1477 xmlIOHTTPRead, xmlIOHTTPClose);
1478#endif /* LIBXML_HTTP_ENABLED */
1479
1480#ifdef LIBXML_FTP_ENABLED
1481 xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1482 xmlIOFTPRead, xmlIOFTPClose);
1483#endif /* LIBXML_FTP_ENABLED */
1484 xmlInputCallbackInitialized = 1;
1485}
1486
1487/**
1488 * xmlRegisterDefaultOutputCallbacks:
1489 *
1490 * Registers the default compiled-in I/O handlers.
1491 */
1492void
Owen Taylor3473f882001-02-23 17:55:21 +00001493xmlRegisterDefaultOutputCallbacks
Owen Taylor3473f882001-02-23 17:55:21 +00001494(void) {
1495 if (xmlOutputCallbackInitialized)
1496 return;
1497
1498 xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
1499 xmlFileWrite, xmlFileClose);
Daniel Veillardf012a642001-07-23 19:10:52 +00001500
1501#ifdef LIBXML_HTTP_ENABLED
1502 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1503 xmlIOHTTPWrite, xmlIOHTTPClosePut);
1504#endif
1505
Owen Taylor3473f882001-02-23 17:55:21 +00001506/*********************************
1507 No way a-priori to distinguish between gzipped files from
1508 uncompressed ones except opening if existing then closing
1509 and saving with same compression ratio ... a pain.
1510
1511#ifdef HAVE_ZLIB_H
1512 xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1513 xmlGzfileWrite, xmlGzfileClose);
1514#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001515
1516 Nor FTP PUT ....
1517#ifdef LIBXML_FTP_ENABLED
1518 xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1519 xmlIOFTPWrite, xmlIOFTPClose);
1520#endif
1521 **********************************/
1522 xmlOutputCallbackInitialized = 1;
1523}
1524
Daniel Veillardf012a642001-07-23 19:10:52 +00001525#ifdef LIBXML_HTTP_ENABLED
1526/**
1527 * xmlRegisterHTTPPostCallbacks
1528 *
1529 * By default, libxml submits HTTP output requests using the "PUT" method.
1530 * Calling this method changes the HTTP output method to use the "POST"
1531 * method instead.
1532 *
1533 */
1534void
1535xmlRegisterHTTPPostCallbacks( void ) {
1536
1537 /* Register defaults if not done previously */
1538
1539 if ( xmlOutputCallbackInitialized == 0 )
1540 xmlRegisterDefaultOutputCallbacks( );
1541
1542 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1543 xmlIOHTTPWrite, xmlIOHTTPClosePost);
1544 return;
1545}
1546#endif
1547
Owen Taylor3473f882001-02-23 17:55:21 +00001548/**
1549 * xmlAllocParserInputBuffer:
1550 * @enc: the charset encoding if known
1551 *
1552 * Create a buffered parser input for progressive parsing
1553 *
1554 * Returns the new parser input or NULL
1555 */
1556xmlParserInputBufferPtr
1557xmlAllocParserInputBuffer(xmlCharEncoding enc) {
1558 xmlParserInputBufferPtr ret;
1559
1560 ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
1561 if (ret == NULL) {
1562 xmlGenericError(xmlGenericErrorContext,
1563 "xmlAllocParserInputBuffer : out of memory!\n");
1564 return(NULL);
1565 }
1566 memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
1567 ret->buffer = xmlBufferCreate();
1568 if (ret->buffer == NULL) {
1569 xmlFree(ret);
1570 return(NULL);
1571 }
1572 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1573 ret->encoder = xmlGetCharEncodingHandler(enc);
1574 if (ret->encoder != NULL)
1575 ret->raw = xmlBufferCreate();
1576 else
1577 ret->raw = NULL;
1578 ret->readcallback = NULL;
1579 ret->closecallback = NULL;
1580 ret->context = NULL;
1581
1582 return(ret);
1583}
1584
1585/**
1586 * xmlAllocOutputBuffer:
1587 * @encoder: the encoding converter or NULL
1588 *
1589 * Create a buffered parser output
1590 *
1591 * Returns the new parser output or NULL
1592 */
1593xmlOutputBufferPtr
1594xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
1595 xmlOutputBufferPtr ret;
1596
1597 ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1598 if (ret == NULL) {
1599 xmlGenericError(xmlGenericErrorContext,
1600 "xmlAllocOutputBuffer : out of memory!\n");
1601 return(NULL);
1602 }
1603 memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
1604 ret->buffer = xmlBufferCreate();
1605 if (ret->buffer == NULL) {
1606 xmlFree(ret);
1607 return(NULL);
1608 }
1609 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1610 ret->encoder = encoder;
1611 if (encoder != NULL) {
1612 ret->conv = xmlBufferCreateSize(4000);
1613 /*
1614 * This call is designed to initiate the encoder state
1615 */
1616 xmlCharEncOutFunc(encoder, ret->conv, NULL);
1617 } else
1618 ret->conv = NULL;
1619 ret->writecallback = NULL;
1620 ret->closecallback = NULL;
1621 ret->context = NULL;
1622 ret->written = 0;
1623
1624 return(ret);
1625}
1626
1627/**
1628 * xmlFreeParserInputBuffer:
1629 * @in: a buffered parser input
1630 *
1631 * Free up the memory used by a buffered parser input
1632 */
1633void
1634xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
1635 if (in->raw) {
1636 xmlBufferFree(in->raw);
1637 in->raw = NULL;
1638 }
1639 if (in->encoder != NULL) {
1640 xmlCharEncCloseFunc(in->encoder);
1641 }
1642 if (in->closecallback != NULL) {
1643 in->closecallback(in->context);
1644 }
1645 if (in->buffer != NULL) {
1646 xmlBufferFree(in->buffer);
1647 in->buffer = NULL;
1648 }
1649
Owen Taylor3473f882001-02-23 17:55:21 +00001650 xmlFree(in);
1651}
1652
1653/**
1654 * xmlOutputBufferClose:
1655 * @out: a buffered output
1656 *
1657 * flushes and close the output I/O channel
1658 * and free up all the associated resources
1659 *
1660 * Returns the number of byte written or -1 in case of error.
1661 */
1662int
1663xmlOutputBufferClose(xmlOutputBufferPtr out) {
1664 int written;
Daniel Veillardf012a642001-07-23 19:10:52 +00001665 int err_rc = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001666
1667 if (out == NULL)
1668 return(-1);
1669 if (out->writecallback != NULL)
1670 xmlOutputBufferFlush(out);
1671 if (out->closecallback != NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001672 err_rc = out->closecallback(out->context);
Owen Taylor3473f882001-02-23 17:55:21 +00001673 }
1674 written = out->written;
1675 if (out->conv) {
1676 xmlBufferFree(out->conv);
1677 out->conv = NULL;
1678 }
1679 if (out->encoder != NULL) {
1680 xmlCharEncCloseFunc(out->encoder);
1681 }
1682 if (out->buffer != NULL) {
1683 xmlBufferFree(out->buffer);
1684 out->buffer = NULL;
1685 }
1686
Owen Taylor3473f882001-02-23 17:55:21 +00001687 xmlFree(out);
Daniel Veillardf012a642001-07-23 19:10:52 +00001688 return( ( err_rc == 0 ) ? written : err_rc );
Owen Taylor3473f882001-02-23 17:55:21 +00001689}
1690
1691/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001692 * xmlParserInputBufferCreateFname:
1693 * @URI: a C string containing the URI or filename
1694 * @enc: the charset encoding if known
1695 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001696 * Returns the new parser input or NULL
1697 */
1698/**
Owen Taylor3473f882001-02-23 17:55:21 +00001699 * xmlParserInputBufferCreateFilename:
1700 * @URI: a C string containing the URI or filename
1701 * @enc: the charset encoding if known
1702 *
1703 * Create a buffered parser input for the progressive parsing of a file
1704 * If filename is "-' then we use stdin as the input.
1705 * Automatic support for ZLIB/Compress compressed document is provided
1706 * by default if found at compile-time.
1707 * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
1708 *
1709 * Returns the new parser input or NULL
1710 */
1711xmlParserInputBufferPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001712xmlParserInputBufferCreateFilename
Owen Taylor3473f882001-02-23 17:55:21 +00001713(const char *URI, xmlCharEncoding enc) {
1714 xmlParserInputBufferPtr ret;
Daniel Veillard388236f2001-07-08 18:35:48 +00001715 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001716 void *context = NULL;
Daniel Veillard388236f2001-07-08 18:35:48 +00001717 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001718 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001719
1720 if (xmlInputCallbackInitialized == 0)
1721 xmlRegisterDefaultInputCallbacks();
1722
1723 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001724 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1725 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001726
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00001727#ifdef LIBXML_CATALOG_ENABLED
1728#endif
1729
Owen Taylor3473f882001-02-23 17:55:21 +00001730 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001731 * Try to find one of the input accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001732 * Go in reverse to give precedence to user defined handlers.
Daniel Veillard388236f2001-07-08 18:35:48 +00001733 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001734 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001735 unescaped = xmlURIUnescapeString((char *) normalized, 0, NULL);
Daniel Veillard388236f2001-07-08 18:35:48 +00001736 if (unescaped != NULL) {
1737 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1738 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1739 (xmlInputCallbackTable[i].matchcallback(unescaped) != 0)) {
1740 context = xmlInputCallbackTable[i].opencallback(unescaped);
1741 if (context != NULL)
1742 break;
1743 }
1744 }
1745 xmlFree(unescaped);
1746 }
1747
1748 /*
1749 * If this failed try with a non-escaped URI this may be a strange
1750 * filename
1751 */
1752 if (context == NULL) {
1753 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1754 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1755 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001756 context = xmlInputCallbackTable[i].opencallback(normalized);
Daniel Veillard388236f2001-07-08 18:35:48 +00001757 if (context != NULL)
1758 break;
1759 }
Owen Taylor3473f882001-02-23 17:55:21 +00001760 }
1761 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001762 xmlFree(normalized);
Owen Taylor3473f882001-02-23 17:55:21 +00001763 if (context == NULL) {
1764 return(NULL);
1765 }
1766
1767 /*
1768 * Allocate the Input buffer front-end.
1769 */
1770 ret = xmlAllocParserInputBuffer(enc);
1771 if (ret != NULL) {
1772 ret->context = context;
1773 ret->readcallback = xmlInputCallbackTable[i].readcallback;
1774 ret->closecallback = xmlInputCallbackTable[i].closecallback;
1775 }
1776 return(ret);
1777}
1778
1779/**
1780 * xmlOutputBufferCreateFilename:
1781 * @URI: a C string containing the URI or filename
1782 * @encoder: the encoding converter or NULL
1783 * @compression: the compression ration (0 none, 9 max).
1784 *
1785 * Create a buffered output for the progressive saving of a file
1786 * If filename is "-' then we use stdout as the output.
1787 * Automatic support for ZLIB/Compress compressed document is provided
1788 * by default if found at compile-time.
1789 * TODO: currently if compression is set, the library only support
1790 * writing to a local file.
1791 *
1792 * Returns the new output or NULL
1793 */
1794xmlOutputBufferPtr
1795xmlOutputBufferCreateFilename(const char *URI,
1796 xmlCharEncodingHandlerPtr encoder,
1797 int compression) {
1798 xmlOutputBufferPtr ret;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001799 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001800 void *context = NULL;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001801 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001802 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001803
Daniel Veillardf012a642001-07-23 19:10:52 +00001804 int is_http_uri = 0; /* Can't change if HTTP disabled */
1805
Owen Taylor3473f882001-02-23 17:55:21 +00001806 if (xmlOutputCallbackInitialized == 0)
1807 xmlRegisterDefaultOutputCallbacks();
1808
1809 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001810 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1811 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001812
Daniel Veillardf012a642001-07-23 19:10:52 +00001813#ifdef LIBXML_HTTP_ENABLED
1814 /* Need to prevent HTTP URI's from falling into zlib short circuit */
1815
Daniel Veillardf4862f02002-09-10 11:13:43 +00001816 is_http_uri = xmlIOHTTPMatch( normalized );
Daniel Veillardf012a642001-07-23 19:10:52 +00001817#endif
1818
Owen Taylor3473f882001-02-23 17:55:21 +00001819
1820 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001821 * Try to find one of the output accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001822 * Go in reverse to give precedence to user defined handlers.
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001823 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001824 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001825 unescaped = xmlURIUnescapeString(normalized, 0, NULL);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001826 if (unescaped != NULL) {
1827#ifdef HAVE_ZLIB_H
1828 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1829 context = xmlGzfileOpenW(unescaped, compression);
1830 if (context != NULL) {
1831 ret = xmlAllocOutputBuffer(encoder);
1832 if (ret != NULL) {
1833 ret->context = context;
1834 ret->writecallback = xmlGzfileWrite;
1835 ret->closecallback = xmlGzfileClose;
1836 }
1837 xmlFree(unescaped);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001838 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001839 return(ret);
1840 }
1841 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001842#endif
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001843 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1844 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1845 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
1846#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1847 /* Need to pass compression parameter into HTTP open calls */
1848 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1849 context = xmlIOHTTPOpenW(unescaped, compression);
1850 else
1851#endif
1852 context = xmlOutputCallbackTable[i].opencallback(unescaped);
1853 if (context != NULL)
1854 break;
1855 }
1856 }
1857 xmlFree(unescaped);
1858 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001859
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001860 /*
1861 * If this failed try with a non-escaped URI this may be a strange
1862 * filename
1863 */
1864 if (context == NULL) {
1865#ifdef HAVE_ZLIB_H
1866 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001867 context = xmlGzfileOpenW(normalized, compression);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001868 if (context != NULL) {
1869 ret = xmlAllocOutputBuffer(encoder);
1870 if (ret != NULL) {
1871 ret->context = context;
1872 ret->writecallback = xmlGzfileWrite;
1873 ret->closecallback = xmlGzfileClose;
1874 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001875 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001876 return(ret);
1877 }
1878 }
1879#endif
1880 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1881 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
Daniel Veillardf4862f02002-09-10 11:13:43 +00001882 (xmlOutputCallbackTable[i].matchcallback(normalized) != 0)) {
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001883#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1884 /* Need to pass compression parameter into HTTP open calls */
1885 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1886 context = xmlIOHTTPOpenW(URI, compression);
1887 else
1888#endif
1889 context = xmlOutputCallbackTable[i].opencallback(URI);
1890 if (context != NULL)
1891 break;
1892 }
Owen Taylor3473f882001-02-23 17:55:21 +00001893 }
1894 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001895 xmlFree(normalized);
Daniel Veillardf012a642001-07-23 19:10:52 +00001896
Owen Taylor3473f882001-02-23 17:55:21 +00001897 if (context == NULL) {
1898 return(NULL);
1899 }
1900
1901 /*
1902 * Allocate the Output buffer front-end.
1903 */
1904 ret = xmlAllocOutputBuffer(encoder);
1905 if (ret != NULL) {
1906 ret->context = context;
1907 ret->writecallback = xmlOutputCallbackTable[i].writecallback;
1908 ret->closecallback = xmlOutputCallbackTable[i].closecallback;
1909 }
1910 return(ret);
1911}
1912
1913/**
1914 * xmlParserInputBufferCreateFile:
1915 * @file: a FILE*
1916 * @enc: the charset encoding if known
1917 *
1918 * Create a buffered parser input for the progressive parsing of a FILE *
1919 * buffered C I/O
1920 *
1921 * Returns the new parser input or NULL
1922 */
1923xmlParserInputBufferPtr
1924xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
1925 xmlParserInputBufferPtr ret;
1926
1927 if (xmlInputCallbackInitialized == 0)
1928 xmlRegisterDefaultInputCallbacks();
1929
1930 if (file == NULL) return(NULL);
1931
1932 ret = xmlAllocParserInputBuffer(enc);
1933 if (ret != NULL) {
1934 ret->context = file;
1935 ret->readcallback = xmlFileRead;
1936 ret->closecallback = xmlFileFlush;
1937 }
1938
1939 return(ret);
1940}
1941
1942/**
1943 * xmlOutputBufferCreateFile:
1944 * @file: a FILE*
1945 * @encoder: the encoding converter or NULL
1946 *
1947 * Create a buffered output for the progressive saving to a FILE *
1948 * buffered C I/O
1949 *
1950 * Returns the new parser output or NULL
1951 */
1952xmlOutputBufferPtr
1953xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
1954 xmlOutputBufferPtr ret;
1955
1956 if (xmlOutputCallbackInitialized == 0)
1957 xmlRegisterDefaultOutputCallbacks();
1958
1959 if (file == NULL) return(NULL);
1960
1961 ret = xmlAllocOutputBuffer(encoder);
1962 if (ret != NULL) {
1963 ret->context = file;
1964 ret->writecallback = xmlFileWrite;
1965 ret->closecallback = xmlFileFlush;
1966 }
1967
1968 return(ret);
1969}
1970
1971/**
1972 * xmlParserInputBufferCreateFd:
1973 * @fd: a file descriptor number
1974 * @enc: the charset encoding if known
1975 *
1976 * Create a buffered parser input for the progressive parsing for the input
1977 * from a file descriptor
1978 *
1979 * Returns the new parser input or NULL
1980 */
1981xmlParserInputBufferPtr
1982xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
1983 xmlParserInputBufferPtr ret;
1984
1985 if (fd < 0) return(NULL);
1986
1987 ret = xmlAllocParserInputBuffer(enc);
1988 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00001989 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00001990 ret->readcallback = xmlFdRead;
1991 ret->closecallback = xmlFdClose;
1992 }
1993
1994 return(ret);
1995}
1996
1997/**
1998 * xmlParserInputBufferCreateMem:
1999 * @mem: the memory input
2000 * @size: the length of the memory block
2001 * @enc: the charset encoding if known
2002 *
2003 * Create a buffered parser input for the progressive parsing for the input
2004 * from a memory area.
2005 *
2006 * Returns the new parser input or NULL
2007 */
2008xmlParserInputBufferPtr
2009xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
2010 xmlParserInputBufferPtr ret;
2011
2012 if (size <= 0) return(NULL);
2013 if (mem == NULL) return(NULL);
2014
2015 ret = xmlAllocParserInputBuffer(enc);
2016 if (ret != NULL) {
2017 ret->context = (void *) mem;
2018 ret->readcallback = (xmlInputReadCallback) xmlNop;
2019 ret->closecallback = NULL;
2020 xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
2021 }
2022
2023 return(ret);
2024}
2025
2026/**
2027 * xmlOutputBufferCreateFd:
2028 * @fd: a file descriptor number
2029 * @encoder: the encoding converter or NULL
2030 *
2031 * Create a buffered output for the progressive saving
2032 * to a file descriptor
2033 *
2034 * Returns the new parser output or NULL
2035 */
2036xmlOutputBufferPtr
2037xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
2038 xmlOutputBufferPtr ret;
2039
2040 if (fd < 0) return(NULL);
2041
2042 ret = xmlAllocOutputBuffer(encoder);
2043 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002044 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002045 ret->writecallback = xmlFdWrite;
Daniel Veillard7db38712002-02-07 16:39:11 +00002046 ret->closecallback = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00002047 }
2048
2049 return(ret);
2050}
2051
2052/**
2053 * xmlParserInputBufferCreateIO:
2054 * @ioread: an I/O read function
2055 * @ioclose: an I/O close function
2056 * @ioctx: an I/O handler
2057 * @enc: the charset encoding if known
2058 *
2059 * Create a buffered parser input for the progressive parsing for the input
2060 * from an I/O handler
2061 *
2062 * Returns the new parser input or NULL
2063 */
2064xmlParserInputBufferPtr
2065xmlParserInputBufferCreateIO(xmlInputReadCallback ioread,
2066 xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) {
2067 xmlParserInputBufferPtr ret;
2068
2069 if (ioread == NULL) return(NULL);
2070
2071 ret = xmlAllocParserInputBuffer(enc);
2072 if (ret != NULL) {
2073 ret->context = (void *) ioctx;
2074 ret->readcallback = ioread;
2075 ret->closecallback = ioclose;
2076 }
2077
2078 return(ret);
2079}
2080
2081/**
2082 * xmlOutputBufferCreateIO:
2083 * @iowrite: an I/O write function
2084 * @ioclose: an I/O close function
2085 * @ioctx: an I/O handler
Daniel Veillard9d06d302002-01-22 18:15:52 +00002086 * @encoder: the charset encoding if known
Owen Taylor3473f882001-02-23 17:55:21 +00002087 *
2088 * Create a buffered output for the progressive saving
2089 * to an I/O handler
2090 *
2091 * Returns the new parser output or NULL
2092 */
2093xmlOutputBufferPtr
2094xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite,
2095 xmlOutputCloseCallback ioclose, void *ioctx,
2096 xmlCharEncodingHandlerPtr encoder) {
2097 xmlOutputBufferPtr ret;
2098
2099 if (iowrite == NULL) return(NULL);
2100
2101 ret = xmlAllocOutputBuffer(encoder);
2102 if (ret != NULL) {
2103 ret->context = (void *) ioctx;
2104 ret->writecallback = iowrite;
2105 ret->closecallback = ioclose;
2106 }
2107
2108 return(ret);
2109}
2110
2111/**
2112 * xmlParserInputBufferPush:
2113 * @in: a buffered parser input
2114 * @len: the size in bytes of the array.
2115 * @buf: an char array
2116 *
2117 * Push the content of the arry in the input buffer
2118 * This routine handle the I18N transcoding to internal UTF-8
2119 * This is used when operating the parser in progressive (push) mode.
2120 *
2121 * Returns the number of chars read and stored in the buffer, or -1
2122 * in case of error.
2123 */
2124int
2125xmlParserInputBufferPush(xmlParserInputBufferPtr in,
2126 int len, const char *buf) {
2127 int nbchars = 0;
2128
2129 if (len < 0) return(0);
2130 if (in->encoder != NULL) {
2131 /*
2132 * Store the data in the incoming raw buffer
2133 */
2134 if (in->raw == NULL) {
2135 in->raw = xmlBufferCreate();
2136 }
2137 xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
2138
2139 /*
2140 * convert as much as possible to the parser reading buffer.
2141 */
2142 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2143 if (nbchars < 0) {
2144 xmlGenericError(xmlGenericErrorContext,
2145 "xmlParserInputBufferPush: encoder error\n");
2146 return(-1);
2147 }
2148 } else {
2149 nbchars = len;
2150 xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
2151 }
2152#ifdef DEBUG_INPUT
2153 xmlGenericError(xmlGenericErrorContext,
2154 "I/O: pushed %d chars, buffer %d/%d\n",
2155 nbchars, in->buffer->use, in->buffer->size);
2156#endif
2157 return(nbchars);
2158}
2159
2160/**
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002161 * endOfInput:
2162 *
2163 * When reading from an Input channel indicated end of file or error
2164 * don't reread from it again.
2165 */
2166static int
2167endOfInput (void * context ATTRIBUTE_UNUSED,
2168 char * buffer ATTRIBUTE_UNUSED,
2169 int len ATTRIBUTE_UNUSED) {
2170 return(0);
2171}
2172
2173/**
Owen Taylor3473f882001-02-23 17:55:21 +00002174 * xmlParserInputBufferGrow:
2175 * @in: a buffered parser input
2176 * @len: indicative value of the amount of chars to read
2177 *
2178 * Grow up the content of the input buffer, the old data are preserved
2179 * This routine handle the I18N transcoding to internal UTF-8
2180 * This routine is used when operating the parser in normal (pull) mode
2181 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002182 * TODO: one should be able to remove one extra copy by copying directly
Owen Taylor3473f882001-02-23 17:55:21 +00002183 * onto in->buffer or in->raw
2184 *
2185 * Returns the number of chars read and stored in the buffer, or -1
2186 * in case of error.
2187 */
2188int
2189xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
2190 char *buffer = NULL;
2191 int res = 0;
2192 int nbchars = 0;
2193 int buffree;
Daniel Veillard9e412302002-06-10 15:59:44 +00002194 unsigned int needSize;
Owen Taylor3473f882001-02-23 17:55:21 +00002195
2196 if ((len <= MINLEN) && (len != 4))
2197 len = MINLEN;
2198 buffree = in->buffer->size - in->buffer->use;
2199 if (buffree <= 0) {
2200 xmlGenericError(xmlGenericErrorContext,
2201 "xmlParserInputBufferGrow : buffer full !\n");
2202 return(0);
2203 }
2204 if (len > buffree)
2205 len = buffree;
2206
Daniel Veillarde5354492002-05-16 08:43:22 +00002207 needSize = in->buffer->use + len + 1;
2208 if (needSize > in->buffer->size){
2209 if (!xmlBufferResize(in->buffer, needSize)){
2210 xmlGenericError(xmlGenericErrorContext,
2211 "xmlBufferAdd : out of memory!\n");
2212 return(0);
2213 }
Owen Taylor3473f882001-02-23 17:55:21 +00002214 }
Daniel Veillarde5354492002-05-16 08:43:22 +00002215 buffer = (char *)&in->buffer->content[in->buffer->use];
Owen Taylor3473f882001-02-23 17:55:21 +00002216
2217 /*
2218 * Call the read method for this I/O type.
2219 */
2220 if (in->readcallback != NULL) {
2221 res = in->readcallback(in->context, &buffer[0], len);
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002222 if (res <= 0)
2223 in->readcallback = endOfInput;
Owen Taylor3473f882001-02-23 17:55:21 +00002224 } else {
2225 xmlGenericError(xmlGenericErrorContext,
2226 "xmlParserInputBufferGrow : no input !\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002227 return(-1);
2228 }
2229 if (res < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00002230 return(-1);
2231 }
2232 len = res;
2233 if (in->encoder != NULL) {
2234 /*
2235 * Store the data in the incoming raw buffer
2236 */
2237 if (in->raw == NULL) {
2238 in->raw = xmlBufferCreate();
2239 }
2240 xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
2241
2242 /*
2243 * convert as much as possible to the parser reading buffer.
2244 */
2245 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2246 if (nbchars < 0) {
2247 xmlGenericError(xmlGenericErrorContext,
2248 "xmlParserInputBufferGrow: encoder error\n");
2249 return(-1);
2250 }
2251 } else {
2252 nbchars = len;
Daniel Veillarde5354492002-05-16 08:43:22 +00002253 in->buffer->use += nbchars;
2254 buffer[nbchars] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002255 }
2256#ifdef DEBUG_INPUT
2257 xmlGenericError(xmlGenericErrorContext,
2258 "I/O: read %d chars, buffer %d/%d\n",
2259 nbchars, in->buffer->use, in->buffer->size);
2260#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002261 return(nbchars);
2262}
2263
2264/**
2265 * xmlParserInputBufferRead:
2266 * @in: a buffered parser input
2267 * @len: indicative value of the amount of chars to read
2268 *
2269 * Refresh the content of the input buffer, the old data are considered
2270 * consumed
2271 * This routine handle the I18N transcoding to internal UTF-8
2272 *
2273 * Returns the number of chars read and stored in the buffer, or -1
2274 * in case of error.
2275 */
2276int
2277xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
2278 /* xmlBufferEmpty(in->buffer); */
2279 if (in->readcallback != NULL)
2280 return(xmlParserInputBufferGrow(in, len));
2281 else
2282 return(-1);
2283}
2284
2285/**
2286 * xmlOutputBufferWrite:
2287 * @out: a buffered parser output
2288 * @len: the size in bytes of the array.
2289 * @buf: an char array
2290 *
2291 * Write the content of the array in the output I/O buffer
2292 * This routine handle the I18N transcoding from internal UTF-8
2293 * The buffer is lossless, i.e. will store in case of partial
2294 * or delayed writes.
2295 *
2296 * Returns the number of chars immediately written, or -1
2297 * in case of error.
2298 */
2299int
2300xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
2301 int nbchars = 0; /* number of chars to output to I/O */
2302 int ret; /* return from function call */
2303 int written = 0; /* number of char written to I/O so far */
2304 int chunk; /* number of byte curreent processed from buf */
2305
2306 if (len < 0) return(0);
2307
2308 do {
2309 chunk = len;
2310 if (chunk > 4 * MINLEN)
2311 chunk = 4 * MINLEN;
2312
2313 /*
2314 * first handle encoding stuff.
2315 */
2316 if (out->encoder != NULL) {
2317 /*
2318 * Store the data in the incoming raw buffer
2319 */
2320 if (out->conv == NULL) {
2321 out->conv = xmlBufferCreate();
2322 }
2323 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2324
2325 if ((out->buffer->use < MINLEN) && (chunk == len))
2326 goto done;
2327
2328 /*
2329 * convert as much as possible to the parser reading buffer.
2330 */
2331 ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2332 if (ret < 0) {
2333 xmlGenericError(xmlGenericErrorContext,
2334 "xmlOutputBufferWrite: encoder error\n");
2335 return(-1);
2336 }
2337 nbchars = out->conv->use;
2338 } else {
2339 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2340 nbchars = out->buffer->use;
2341 }
2342 buf += chunk;
2343 len -= chunk;
2344
2345 if ((nbchars < MINLEN) && (len <= 0))
2346 goto done;
2347
2348 if (out->writecallback) {
2349 /*
2350 * second write the stuff to the I/O channel
2351 */
2352 if (out->encoder != NULL) {
2353 ret = out->writecallback(out->context,
2354 (const char *)out->conv->content, nbchars);
2355 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002356 xmlBufferShrink(out->conv, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002357 } else {
2358 ret = out->writecallback(out->context,
2359 (const char *)out->buffer->content, nbchars);
2360 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002361 xmlBufferShrink(out->buffer, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002362 }
2363 if (ret < 0) {
2364 xmlGenericError(xmlGenericErrorContext,
2365 "I/O: error %d writing %d bytes\n", ret, nbchars);
2366 return(ret);
2367 }
2368 out->written += ret;
2369 }
2370 written += nbchars;
2371 } while (len > 0);
2372
2373done:
2374#ifdef DEBUG_INPUT
2375 xmlGenericError(xmlGenericErrorContext,
2376 "I/O: wrote %d chars\n", written);
2377#endif
2378 return(written);
2379}
2380
2381/**
2382 * xmlOutputBufferWriteString:
2383 * @out: a buffered parser output
2384 * @str: a zero terminated C string
2385 *
2386 * Write the content of the string in the output I/O buffer
2387 * This routine handle the I18N transcoding from internal UTF-8
2388 * The buffer is lossless, i.e. will store in case of partial
2389 * or delayed writes.
2390 *
2391 * Returns the number of chars immediately written, or -1
2392 * in case of error.
2393 */
2394int
2395xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
2396 int len;
2397
2398 if (str == NULL)
2399 return(-1);
2400 len = strlen(str);
2401
2402 if (len > 0)
2403 return(xmlOutputBufferWrite(out, len, str));
2404 return(len);
2405}
2406
2407/**
2408 * xmlOutputBufferFlush:
2409 * @out: a buffered output
2410 *
2411 * flushes the output I/O channel
2412 *
2413 * Returns the number of byte written or -1 in case of error.
2414 */
2415int
2416xmlOutputBufferFlush(xmlOutputBufferPtr out) {
2417 int nbchars = 0, ret = 0;
2418
2419 /*
2420 * first handle encoding stuff.
2421 */
2422 if ((out->conv != NULL) && (out->encoder != NULL)) {
2423 /*
2424 * convert as much as possible to the parser reading buffer.
2425 */
2426 nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2427 if (nbchars < 0) {
2428 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002429 "xmlOutputBufferFlush: encoder error\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002430 return(-1);
2431 }
2432 }
2433
2434 /*
2435 * second flush the stuff to the I/O channel
2436 */
2437 if ((out->conv != NULL) && (out->encoder != NULL) &&
2438 (out->writecallback != NULL)) {
2439 ret = out->writecallback(out->context,
2440 (const char *)out->conv->content, out->conv->use);
2441 if (ret >= 0)
2442 xmlBufferShrink(out->conv, ret);
2443 } else if (out->writecallback != NULL) {
2444 ret = out->writecallback(out->context,
2445 (const char *)out->buffer->content, out->buffer->use);
2446 if (ret >= 0)
2447 xmlBufferShrink(out->buffer, ret);
2448 }
2449 if (ret < 0) {
2450 xmlGenericError(xmlGenericErrorContext,
2451 "I/O: error %d flushing %d bytes\n", ret, nbchars);
2452 return(ret);
2453 }
2454 out->written += ret;
2455
2456#ifdef DEBUG_INPUT
2457 xmlGenericError(xmlGenericErrorContext,
2458 "I/O: flushed %d chars\n", ret);
2459#endif
2460 return(ret);
2461}
2462
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002463/**
Owen Taylor3473f882001-02-23 17:55:21 +00002464 * xmlParserGetDirectory:
2465 * @filename: the path to a file
2466 *
2467 * lookup the directory for that file
2468 *
2469 * Returns a new allocated string containing the directory, or NULL.
2470 */
2471char *
2472xmlParserGetDirectory(const char *filename) {
2473 char *ret = NULL;
2474 char dir[1024];
2475 char *cur;
2476 char sep = '/';
2477
Igor Zlatkovic9181cc02002-09-29 17:51:06 +00002478#ifdef _WIN32_WCE /* easy way by now ... wince does not have dirs! */
2479 return NULL;
2480#endif
2481
Owen Taylor3473f882001-02-23 17:55:21 +00002482 if (xmlInputCallbackInitialized == 0)
2483 xmlRegisterDefaultInputCallbacks();
2484
2485 if (filename == NULL) return(NULL);
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002486#if defined(WIN32) && !defined(__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +00002487 sep = '\\';
2488#endif
2489
2490 strncpy(dir, filename, 1023);
2491 dir[1023] = 0;
2492 cur = &dir[strlen(dir)];
2493 while (cur > dir) {
2494 if (*cur == sep) break;
2495 cur --;
2496 }
2497 if (*cur == sep) {
2498 if (cur == dir) dir[1] = 0;
2499 else *cur = 0;
2500 ret = xmlMemStrdup(dir);
2501 } else {
2502 if (getcwd(dir, 1024) != NULL) {
2503 dir[1023] = 0;
2504 ret = xmlMemStrdup(dir);
2505 }
2506 }
2507 return(ret);
2508}
2509
2510/****************************************************************
2511 * *
2512 * External entities loading *
2513 * *
2514 ****************************************************************/
2515
Daniel Veillard561b7f82002-03-20 21:55:57 +00002516#ifdef LIBXML_CATALOG_ENABLED
2517static int xmlSysIDExists(const char *URL) {
Daniel Veillard6990bf32001-08-23 21:17:48 +00002518#ifdef HAVE_STAT
2519 int ret;
2520 struct stat info;
2521 const char *path;
2522
2523 if (URL == NULL)
2524 return(0);
2525
Daniel Veillardf4862f02002-09-10 11:13:43 +00002526 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2527#if defined (_WIN32) && !defined(__CYGWIN__)
2528 path = &URL[17];
2529#else
Daniel Veillard6990bf32001-08-23 21:17:48 +00002530 path = &URL[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +00002531#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002532 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002533#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard6990bf32001-08-23 21:17:48 +00002534 path = &URL[8];
2535#else
2536 path = &URL[7];
2537#endif
2538 } else
2539 path = URL;
2540 ret = stat(path, &info);
Daniel Veillard561b7f82002-03-20 21:55:57 +00002541 if (ret == 0)
2542 return(1);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002543#endif
Daniel Veillard561b7f82002-03-20 21:55:57 +00002544 return(0);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002545}
Daniel Veillard561b7f82002-03-20 21:55:57 +00002546#endif
Daniel Veillard6990bf32001-08-23 21:17:48 +00002547
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002548/**
Owen Taylor3473f882001-02-23 17:55:21 +00002549 * xmlDefaultExternalEntityLoader:
2550 * @URL: the URL for the entity to load
2551 * @ID: the System ID for the entity to load
2552 * @ctxt: the context in which the entity is called or NULL
2553 *
2554 * By default we don't load external entitites, yet.
2555 *
2556 * Returns a new allocated xmlParserInputPtr, or NULL.
2557 */
2558static
2559xmlParserInputPtr
2560xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
2561 xmlParserCtxtPtr ctxt) {
2562 xmlParserInputPtr ret = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002563 xmlChar *resource = NULL;
2564#ifdef LIBXML_CATALOG_ENABLED
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002565 xmlCatalogAllow pref;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002566#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002567
2568#ifdef DEBUG_EXTERNAL_ENTITIES
2569 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00002570 "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +00002571#endif
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002572#ifdef LIBXML_CATALOG_ENABLED
2573 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002574 * If the resource doesn't exists as a file,
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002575 * try to load it from the resource pointed in the catalogs
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002576 */
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002577 pref = xmlCatalogGetDefaults();
2578
Daniel Veillard561b7f82002-03-20 21:55:57 +00002579 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002580 /*
2581 * Do a local lookup
2582 */
2583 if ((ctxt->catalogs != NULL) &&
2584 ((pref == XML_CATA_ALLOW_ALL) ||
2585 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2586 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2587 (const xmlChar *)ID,
2588 (const xmlChar *)URL);
2589 }
2590 /*
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002591 * Try a global lookup
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002592 */
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002593 if ((resource == NULL) &&
2594 ((pref == XML_CATA_ALLOW_ALL) ||
2595 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002596 resource = xmlCatalogResolve((const xmlChar *)ID,
2597 (const xmlChar *)URL);
2598 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002599 if ((resource == NULL) && (URL != NULL))
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002600 resource = xmlStrdup((const xmlChar *) URL);
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002601
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002602 /*
2603 * TODO: do an URI lookup on the reference
2604 */
Daniel Veillard561b7f82002-03-20 21:55:57 +00002605 if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002606 xmlChar *tmp = NULL;
2607
2608 if ((ctxt->catalogs != NULL) &&
2609 ((pref == XML_CATA_ALLOW_ALL) ||
2610 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2611 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2612 }
2613 if ((tmp == NULL) &&
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002614 ((pref == XML_CATA_ALLOW_ALL) ||
2615 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002616 tmp = xmlCatalogResolveURI(resource);
2617 }
2618
2619 if (tmp != NULL) {
2620 xmlFree(resource);
2621 resource = tmp;
2622 }
2623 }
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002624 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002625#endif
2626
2627 if (resource == NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002628 resource = (xmlChar *) URL;
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002629
2630 if (resource == NULL) {
Daniel Veillardc6613042002-03-02 09:34:02 +00002631 if (ID == NULL)
2632 ID = "NULL";
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002633 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2634 (ctxt->sax->error != NULL))
2635 ctxt->sax->error(ctxt,
2636 "failed to load external entity \"%s\"\n", ID);
2637 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002638 ctxt->sax->warning(ctxt,
2639 "failed to load external entity \"%s\"\n", ID);
2640 return(NULL);
2641 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002642 ret = xmlNewInputFromFile(ctxt, (const char *)resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002643 if (ret == NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002644 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2645 (ctxt->sax->error != NULL))
2646 ctxt->sax->error(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002647 "failed to load external entity \"%s\"\n", resource);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002648 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002649 ctxt->sax->warning(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002650 "failed to load external entity \"%s\"\n", resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002651 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002652 if ((resource != NULL) && (resource != (xmlChar *) URL))
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002653 xmlFree(resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002654 return(ret);
2655}
2656
2657static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
2658 xmlDefaultExternalEntityLoader;
2659
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002660/**
Owen Taylor3473f882001-02-23 17:55:21 +00002661 * xmlSetExternalEntityLoader:
2662 * @f: the new entity resolver function
2663 *
2664 * Changes the defaultexternal entity resolver function for the application
2665 */
2666void
2667xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
2668 xmlCurrentExternalEntityLoader = f;
2669}
2670
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002671/**
Owen Taylor3473f882001-02-23 17:55:21 +00002672 * xmlGetExternalEntityLoader:
2673 *
2674 * Get the default external entity resolver function for the application
2675 *
2676 * Returns the xmlExternalEntityLoader function pointer
2677 */
2678xmlExternalEntityLoader
2679xmlGetExternalEntityLoader(void) {
2680 return(xmlCurrentExternalEntityLoader);
2681}
2682
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002683/**
Owen Taylor3473f882001-02-23 17:55:21 +00002684 * xmlLoadExternalEntity:
2685 * @URL: the URL for the entity to load
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002686 * @ID: the Public ID for the entity to load
Owen Taylor3473f882001-02-23 17:55:21 +00002687 * @ctxt: the context in which the entity is called or NULL
2688 *
2689 * Load an external entity, note that the use of this function for
2690 * unparsed entities may generate problems
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002691 * TODO: a more generic External entity API must be designed
Owen Taylor3473f882001-02-23 17:55:21 +00002692 *
2693 * Returns the xmlParserInputPtr or NULL
2694 */
2695xmlParserInputPtr
2696xmlLoadExternalEntity(const char *URL, const char *ID,
2697 xmlParserCtxtPtr ctxt) {
2698 return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
2699}
2700
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002701/************************************************************************
2702 * *
2703 * Disabling Network access *
2704 * *
2705 ************************************************************************/
2706
2707#ifdef LIBXML_CATALOG_ENABLED
2708static int
2709xmlNoNetExists(const char *URL)
2710{
2711#ifdef HAVE_STAT
2712 int ret;
2713 struct stat info;
2714 const char *path;
2715
2716 if (URL == NULL)
2717 return (0);
2718
Daniel Veillardf4862f02002-09-10 11:13:43 +00002719 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2720#if defined (_WIN32) && !defined(__CYGWIN__)
2721 path = &URL[17];
2722#else
2723 path = &URL[16];
2724#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002725 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002726#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002727 path = &URL[8];
2728#else
2729 path = &URL[7];
2730#endif
2731 } else
2732 path = URL;
2733 ret = stat(path, &info);
2734 if (ret == 0)
2735 return (1);
2736#endif
2737 return (0);
2738}
2739#endif
2740
2741/**
2742 * xmlNoNetExternalEntityLoader:
2743 * @URL: the URL for the entity to load
2744 * @ID: the System ID for the entity to load
2745 * @ctxt: the context in which the entity is called or NULL
2746 *
2747 * A specific entity loader disabling network accesses, though still
2748 * allowing local catalog accesses for resolution.
2749 *
2750 * Returns a new allocated xmlParserInputPtr, or NULL.
2751 */
2752xmlParserInputPtr
2753xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
2754 xmlParserCtxtPtr ctxt) {
2755 xmlParserInputPtr input = NULL;
2756 xmlChar *resource = NULL;
2757
2758#ifdef LIBXML_CATALOG_ENABLED
2759 xmlCatalogAllow pref;
2760
2761 /*
2762 * If the resource doesn't exists as a file,
2763 * try to load it from the resource pointed in the catalogs
2764 */
2765 pref = xmlCatalogGetDefaults();
2766
2767 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
2768 /*
2769 * Do a local lookup
2770 */
2771 if ((ctxt->catalogs != NULL) &&
2772 ((pref == XML_CATA_ALLOW_ALL) ||
2773 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2774 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2775 (const xmlChar *)ID,
2776 (const xmlChar *)URL);
2777 }
2778 /*
2779 * Try a global lookup
2780 */
2781 if ((resource == NULL) &&
2782 ((pref == XML_CATA_ALLOW_ALL) ||
2783 (pref == XML_CATA_ALLOW_GLOBAL))) {
2784 resource = xmlCatalogResolve((const xmlChar *)ID,
2785 (const xmlChar *)URL);
2786 }
2787 if ((resource == NULL) && (URL != NULL))
2788 resource = xmlStrdup((const xmlChar *) URL);
2789
2790 /*
2791 * TODO: do an URI lookup on the reference
2792 */
2793 if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
2794 xmlChar *tmp = NULL;
2795
2796 if ((ctxt->catalogs != NULL) &&
2797 ((pref == XML_CATA_ALLOW_ALL) ||
2798 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2799 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2800 }
2801 if ((tmp == NULL) &&
2802 ((pref == XML_CATA_ALLOW_ALL) ||
2803 (pref == XML_CATA_ALLOW_GLOBAL))) {
2804 tmp = xmlCatalogResolveURI(resource);
2805 }
2806
2807 if (tmp != NULL) {
2808 xmlFree(resource);
2809 resource = tmp;
2810 }
2811 }
2812 }
2813#endif
2814 if (resource == NULL)
2815 resource = (xmlChar *) URL;
2816
2817 if (resource != NULL) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002818 if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
2819 (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002820 xmlGenericError(xmlGenericErrorContext,
2821 "Attempt to load network entity %s \n", resource);
2822
2823 if (resource != (xmlChar *) URL)
2824 xmlFree(resource);
2825 return(NULL);
2826 }
2827 }
2828 input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
2829 if (resource != (xmlChar *) URL)
2830 xmlFree(resource);
2831 return(input);
2832}
2833