blob: 796c8ac030b3747a1f36e206815b65af57b5300f [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
82#ifdef VMS
83#define xmlRegisterDefaultInputCallbacks xmlRegisterDefInputCallbacks
84#define xmlRegisterDefaultOutputCallbacks xmlRegisterDefOutputCallbacks
85#endif
86
Daniel Veillardf012a642001-07-23 19:10:52 +000087/* #define VERBOSE_FAILURE */
Daniel Veillard1fd36d22001-07-04 22:54:28 +000088/* #define DEBUG_EXTERNAL_ENTITIES */
Owen Taylor3473f882001-02-23 17:55:21 +000089/* #define DEBUG_INPUT */
90
91#ifdef DEBUG_INPUT
92#define MINLEN 40
93#else
94#define MINLEN 4000
95#endif
96
97/*
98 * Input I/O callback sets
99 */
100typedef struct _xmlInputCallback {
101 xmlInputMatchCallback matchcallback;
102 xmlInputOpenCallback opencallback;
103 xmlInputReadCallback readcallback;
104 xmlInputCloseCallback closecallback;
105} xmlInputCallback;
106
107#define MAX_INPUT_CALLBACK 15
108
Daniel Veillard22090732001-07-16 00:06:07 +0000109static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
110static int xmlInputCallbackNr = 0;
111static int xmlInputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000112
113/*
114 * Output I/O callback sets
115 */
116typedef struct _xmlOutputCallback {
117 xmlOutputMatchCallback matchcallback;
118 xmlOutputOpenCallback opencallback;
119 xmlOutputWriteCallback writecallback;
120 xmlOutputCloseCallback closecallback;
121} xmlOutputCallback;
122
123#define MAX_OUTPUT_CALLBACK 15
124
Daniel Veillard22090732001-07-16 00:06:07 +0000125static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
126static int xmlOutputCallbackNr = 0;
127static int xmlOutputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000128
Daniel Veillardf4862f02002-09-10 11:13:43 +0000129/************************************************************************
130 * *
131 * Handling of Windows file paths *
132 * *
133 ************************************************************************/
134
135#define IS_WINDOWS_PATH(p) \
136 ((p != NULL) && \
137 (((p[0] >= 'a') && (p[0] <= 'z')) || \
138 ((p[0] >= 'A') && (p[0] <= 'Z'))) && \
139 (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\')))
140
141
142/**
143 * xmlNormalizeWindowsPath
144 * @path: a windows path like "C:/foo/bar"
145 *
146 * Normalize a Windows path to make an URL from it
147 *
148 * Returns a new URI which must be freed by the caller or NULL
149 * in case of error
150 */
151xmlChar *
152xmlNormalizeWindowsPath(const xmlChar *path)
153{
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000154 int len, i = 0, j;
Daniel Veillardf4862f02002-09-10 11:13:43 +0000155 xmlChar *ret;
156
157 if (path == NULL)
158 return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +0000159
160 len = xmlStrlen(path);
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000161 if (!IS_WINDOWS_PATH(path)) {
162 ret = xmlStrdup(path);
163 if (ret == NULL)
164 return(NULL);
165 j = 0;
166 } else {
167 ret = xmlMalloc(len + 10);
168 if (ret == NULL)
169 return(NULL);
170 ret[0] = 'f';
171 ret[1] = 'i';
172 ret[2] = 'l';
173 ret[3] = 'e';
174 ret[4] = ':';
175 ret[5] = '/';
176 ret[6] = '/';
177 ret[7] = '/';
178 j = 8;
179 }
Daniel Veillardf4862f02002-09-10 11:13:43 +0000180
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000181 while (i < len) {
Daniel Veillardf4862f02002-09-10 11:13:43 +0000182 /* TODO: UTF8 conversion + URI escaping ??? */
183 if (path[i] == '\\')
184 ret[j] = '/';
185 else
186 ret[j] = path[i];
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000187 i++;
188 j++;
Daniel Veillardf4862f02002-09-10 11:13:43 +0000189 }
190 ret[j] = 0;
Igor Zlatkovic043620e2002-10-04 13:32:07 +0000191
Daniel Veillardf4862f02002-09-10 11:13:43 +0000192 return(ret);
193}
194
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000195/**
196 * xmlCleanupInputCallbacks:
197 *
198 * clears the entire input callback table. this includes the
199 * compiled-in I/O.
200 */
201void
202xmlCleanupInputCallbacks(void)
203{
204 int i;
205
206 if (!xmlInputCallbackInitialized)
207 return;
208
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000209 for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000210 xmlInputCallbackTable[i].matchcallback = NULL;
211 xmlInputCallbackTable[i].opencallback = NULL;
212 xmlInputCallbackTable[i].readcallback = NULL;
213 xmlInputCallbackTable[i].closecallback = NULL;
214 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000215 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000216
217 xmlInputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000218 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000219}
220
221/**
222 * xmlCleanupOutputCallbacks:
223 *
224 * clears the entire output callback table. this includes the
225 * compiled-in I/O callbacks.
226 */
227void
228xmlCleanupOutputCallbacks(void)
229{
230 int i;
231
232 if (!xmlOutputCallbackInitialized)
233 return;
234
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000235 for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000236 xmlOutputCallbackTable[i].matchcallback = NULL;
237 xmlOutputCallbackTable[i].opencallback = NULL;
238 xmlOutputCallbackTable[i].writecallback = NULL;
239 xmlOutputCallbackTable[i].closecallback = NULL;
240 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000241 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000242
243 xmlOutputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000244 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000245}
246
Owen Taylor3473f882001-02-23 17:55:21 +0000247/************************************************************************
248 * *
249 * Standard I/O for file accesses *
250 * *
251 ************************************************************************/
252
253/**
254 * xmlCheckFilename
255 * @path: the path to check
256 *
257 * function checks to see if @path is a valid source
258 * (file, socket...) for XML.
259 *
260 * if stat is not available on the target machine,
261 * returns 1. if stat fails, returns 0 (if calling
262 * stat on the filename fails, it can't be right).
263 * if stat succeeds and the file is a directory,
264 * sets errno to EISDIR and returns 0. otherwise
265 * returns 1.
266 */
267
268static int
269xmlCheckFilename (const char *path)
270{
271#ifdef HAVE_STAT
272#ifdef S_ISDIR
273 struct stat stat_buffer;
274
275 if (stat(path, &stat_buffer) == -1)
276 return 0;
277
278 if (S_ISDIR(stat_buffer.st_mode)) {
279 errno = EISDIR;
280 return 0;
281 }
282
283#endif
284#endif
285 return 1;
286}
287
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000288static int
Owen Taylor3473f882001-02-23 17:55:21 +0000289xmlNop(void) {
290 return(0);
291}
292
293/**
Owen Taylor3473f882001-02-23 17:55:21 +0000294 * xmlFdRead:
295 * @context: the I/O context
296 * @buffer: where to drop data
297 * @len: number of bytes to read
298 *
299 * Read @len bytes to @buffer from the I/O channel.
300 *
301 * Returns the number of bytes written
302 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000303static int
Owen Taylor3473f882001-02-23 17:55:21 +0000304xmlFdRead (void * context, char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000305 return(read((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000306}
307
308/**
309 * xmlFdWrite:
310 * @context: the I/O context
311 * @buffer: where to get data
312 * @len: number of bytes to write
313 *
314 * Write @len bytes from @buffer to the I/O channel.
315 *
316 * Returns the number of bytes written
317 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000318static int
Owen Taylor3473f882001-02-23 17:55:21 +0000319xmlFdWrite (void * context, const char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000320 return(write((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000321}
322
323/**
324 * xmlFdClose:
325 * @context: the I/O context
326 *
327 * Close an I/O channel
Daniel Veillardf012a642001-07-23 19:10:52 +0000328 *
329 * Returns 0 in case of success and error code otherwise
Owen Taylor3473f882001-02-23 17:55:21 +0000330 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000331static int
Owen Taylor3473f882001-02-23 17:55:21 +0000332xmlFdClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000333 return ( close((int) (long) context) );
Owen Taylor3473f882001-02-23 17:55:21 +0000334}
335
336/**
337 * xmlFileMatch:
338 * @filename: the URI for matching
339 *
340 * input from FILE *
341 *
342 * Returns 1 if matches, 0 otherwise
343 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000344int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000345xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000346 return(1);
347}
348
349/**
350 * xmlFileOpen:
351 * @filename: the URI for matching
352 *
353 * input from FILE *, supports compressed input
354 * if @filename is " " then the standard input is used
355 *
356 * Returns an I/O context or NULL in case of error
357 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000358void *
Owen Taylor3473f882001-02-23 17:55:21 +0000359xmlFileOpen (const char *filename) {
360 const char *path = NULL;
361 FILE *fd;
362
363 if (!strcmp(filename, "-")) {
364 fd = stdin;
365 return((void *) fd);
366 }
367
Daniel Veillardf4862f02002-09-10 11:13:43 +0000368 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000369#if defined (_WIN32) && !defined(__CYGWIN__)
370 path = &filename[17];
371#else
Owen Taylor3473f882001-02-23 17:55:21 +0000372 path = &filename[16];
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000373#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000374 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000375#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000376 path = &filename[8];
377#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000378 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000379#endif
380 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000381 path = filename;
382
383 if (path == NULL)
384 return(NULL);
385 if (!xmlCheckFilename(path))
386 return(NULL);
387
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000388#if defined(WIN32) || defined (__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +0000389 fd = fopen(path, "rb");
390#else
391 fd = fopen(path, "r");
392#endif /* WIN32 */
393 return((void *) fd);
394}
395
396/**
397 * xmlFileOpenW:
398 * @filename: the URI for matching
399 *
400 * output to from FILE *,
401 * if @filename is "-" then the standard output is used
402 *
403 * Returns an I/O context or NULL in case of error
404 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000405static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000406xmlFileOpenW (const char *filename) {
407 const char *path = NULL;
408 FILE *fd;
409
410 if (!strcmp(filename, "-")) {
411 fd = stdout;
412 return((void *) fd);
413 }
414
Daniel Veillardf4862f02002-09-10 11:13:43 +0000415 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
416#if defined (_WIN32) && !defined(__CYGWIN__)
417 path = &filename[17];
418#else
Owen Taylor3473f882001-02-23 17:55:21 +0000419 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000420#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000421 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000422#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000423 path = &filename[8];
424#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000425 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000426#endif
427 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000428 path = filename;
429
430 if (path == NULL)
431 return(NULL);
432
433 fd = fopen(path, "w");
434 return((void *) fd);
435}
436
437/**
438 * xmlFileRead:
439 * @context: the I/O context
440 * @buffer: where to drop data
441 * @len: number of bytes to write
442 *
443 * Read @len bytes to @buffer from the I/O channel.
444 *
445 * Returns the number of bytes written
446 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000447int
Owen Taylor3473f882001-02-23 17:55:21 +0000448xmlFileRead (void * context, char * buffer, int len) {
449 return(fread(&buffer[0], 1, len, (FILE *) context));
450}
451
452/**
453 * xmlFileWrite:
454 * @context: the I/O context
455 * @buffer: where to drop data
456 * @len: number of bytes to write
457 *
458 * Write @len bytes from @buffer to the I/O channel.
459 *
460 * Returns the number of bytes written
461 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000462static int
Owen Taylor3473f882001-02-23 17:55:21 +0000463xmlFileWrite (void * context, const char * buffer, int len) {
464 return(fwrite(&buffer[0], 1, len, (FILE *) context));
465}
466
467/**
468 * xmlFileClose:
469 * @context: the I/O context
470 *
471 * Close an I/O channel
472 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000473int
Owen Taylor3473f882001-02-23 17:55:21 +0000474xmlFileClose (void * context) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000475 FILE *fil;
476
477 fil = (FILE *) context;
478 if (fil == stdin)
479 return(0);
480 if (fil == stdout)
481 return(0);
Daniel Veillardcd337f02001-11-22 18:20:37 +0000482 if (fil == stderr)
483 return(0);
Daniel Veillardf012a642001-07-23 19:10:52 +0000484 return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000485}
486
487/**
488 * xmlFileFlush:
489 * @context: the I/O context
490 *
491 * Flush an I/O channel
492 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000493static int
Owen Taylor3473f882001-02-23 17:55:21 +0000494xmlFileFlush (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000495 return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000496}
497
498#ifdef HAVE_ZLIB_H
499/************************************************************************
500 * *
501 * I/O for compressed file accesses *
502 * *
503 ************************************************************************/
504/**
505 * xmlGzfileMatch:
506 * @filename: the URI for matching
507 *
508 * input from compressed file test
509 *
510 * Returns 1 if matches, 0 otherwise
511 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000512static int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000513xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000514 return(1);
515}
516
517/**
518 * xmlGzfileOpen:
519 * @filename: the URI for matching
520 *
521 * input from compressed file open
522 * if @filename is " " then the standard input is used
523 *
524 * Returns an I/O context or NULL in case of error
525 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000526static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000527xmlGzfileOpen (const char *filename) {
528 const char *path = NULL;
529 gzFile fd;
530
531 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000532 fd = gzdopen(dup(0), "rb");
Owen Taylor3473f882001-02-23 17:55:21 +0000533 return((void *) fd);
534 }
535
Daniel Veillardf4862f02002-09-10 11:13:43 +0000536 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
537#if defined (_WIN32) && !defined(__CYGWIN__)
538 path = &filename[17];
539#else
Owen Taylor3473f882001-02-23 17:55:21 +0000540 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000541#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000542 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000543#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000544 path = &filename[8];
545#else
Owen Taylor3473f882001-02-23 17:55:21 +0000546 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000547#endif
548 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000549 path = filename;
550
551 if (path == NULL)
552 return(NULL);
553 if (!xmlCheckFilename(path))
554 return(NULL);
555
556 fd = gzopen(path, "rb");
557 return((void *) fd);
558}
559
560/**
561 * xmlGzfileOpenW:
562 * @filename: the URI for matching
563 * @compression: the compression factor (0 - 9 included)
564 *
565 * input from compressed file open
566 * if @filename is " " then the standard input is used
567 *
568 * Returns an I/O context or NULL in case of error
569 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000570static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000571xmlGzfileOpenW (const char *filename, int compression) {
572 const char *path = NULL;
573 char mode[15];
574 gzFile fd;
575
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000576 snprintf(mode, sizeof(mode), "wb%d", compression);
Owen Taylor3473f882001-02-23 17:55:21 +0000577 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000578 fd = gzdopen(dup(1), mode);
Owen Taylor3473f882001-02-23 17:55:21 +0000579 return((void *) fd);
580 }
581
Daniel Veillardf4862f02002-09-10 11:13:43 +0000582 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
583#if defined (_WIN32) && !defined(__CYGWIN__)
584 path = &filename[17];
585#else
Owen Taylor3473f882001-02-23 17:55:21 +0000586 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000587#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000588 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000589#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000590 path = &filename[8];
591#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000592 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000593#endif
594 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000595 path = filename;
596
597 if (path == NULL)
598 return(NULL);
599
600 fd = gzopen(path, mode);
601 return((void *) fd);
602}
603
604/**
605 * xmlGzfileRead:
606 * @context: the I/O context
607 * @buffer: where to drop data
608 * @len: number of bytes to write
609 *
610 * Read @len bytes to @buffer from the compressed I/O channel.
611 *
612 * Returns the number of bytes written
613 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000614static int
Owen Taylor3473f882001-02-23 17:55:21 +0000615xmlGzfileRead (void * context, char * buffer, int len) {
616 return(gzread((gzFile) context, &buffer[0], len));
617}
618
619/**
620 * xmlGzfileWrite:
621 * @context: the I/O context
622 * @buffer: where to drop data
623 * @len: number of bytes to write
624 *
625 * Write @len bytes from @buffer to the compressed I/O channel.
626 *
627 * Returns the number of bytes written
628 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000629static int
Owen Taylor3473f882001-02-23 17:55:21 +0000630xmlGzfileWrite (void * context, const char * buffer, int len) {
631 return(gzwrite((gzFile) context, (char *) &buffer[0], len));
632}
633
634/**
635 * xmlGzfileClose:
636 * @context: the I/O context
637 *
638 * Close a compressed I/O channel
639 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000640static int
Owen Taylor3473f882001-02-23 17:55:21 +0000641xmlGzfileClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000642 return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
Owen Taylor3473f882001-02-23 17:55:21 +0000643}
644#endif /* HAVE_ZLIB_H */
645
646#ifdef LIBXML_HTTP_ENABLED
647/************************************************************************
648 * *
649 * I/O for HTTP file accesses *
650 * *
651 ************************************************************************/
Daniel Veillardf012a642001-07-23 19:10:52 +0000652
653typedef struct xmlIOHTTPWriteCtxt_
654{
655 int compression;
656
657 char * uri;
658
659 void * doc_buff;
660
661} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
662
663#ifdef HAVE_ZLIB_H
664
665#define DFLT_WBITS ( -15 )
666#define DFLT_MEM_LVL ( 8 )
667#define GZ_MAGIC1 ( 0x1f )
668#define GZ_MAGIC2 ( 0x8b )
669#define LXML_ZLIB_OS_CODE ( 0x03 )
670#define INIT_HTTP_BUFF_SIZE ( 32768 )
671#define DFLT_ZLIB_RATIO ( 5 )
672
673/*
674** Data structure and functions to work with sending compressed data
675** via HTTP.
676*/
677
678typedef struct xmlZMemBuff_
679{
680 unsigned long size;
681 unsigned long crc;
682
683 unsigned char * zbuff;
684 z_stream zctrl;
685
686} xmlZMemBuff, *xmlZMemBuffPtr;
687
688/**
689 * append_reverse_ulong
690 * @buff: Compressed memory buffer
691 * @data: Unsigned long to append
692 *
693 * Append a unsigned long in reverse byte order to the end of the
694 * memory buffer.
695 */
696static void
697append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
698
699 int idx;
700
701 if ( buff == NULL )
702 return;
703
704 /*
705 ** This is plagiarized from putLong in gzio.c (zlib source) where
706 ** the number "4" is hardcoded. If zlib is ever patched to
707 ** support 64 bit file sizes, this code would need to be patched
708 ** as well.
709 */
710
711 for ( idx = 0; idx < 4; idx++ ) {
712 *buff->zctrl.next_out = ( data & 0xff );
713 data >>= 8;
714 buff->zctrl.next_out++;
715 }
716
717 return;
718}
719
720/**
721 *
722 * xmlFreeZMemBuff
723 * @buff: The memory buffer context to clear
724 *
725 * Release all the resources associated with the compressed memory buffer.
726 */
727static void
728xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
729
730 int z_err;
731
732 if ( buff == NULL )
733 return;
734
735 xmlFree( buff->zbuff );
736 z_err = deflateEnd( &buff->zctrl );
737#ifdef DEBUG_HTTP
738 if ( z_err != Z_OK )
739 xmlGenericError( xmlGenericErrorContext,
740 "xmlFreeZMemBuff: Error releasing zlib context: %d\n",
741 z_err );
742#endif
743
744 xmlFree( buff );
745 return;
746}
747
748/**
749 * xmlCreateZMemBuff
750 *@compression: Compression value to use
751 *
752 * Create a memory buffer to hold the compressed XML document. The
753 * compressed document in memory will end up being identical to what
754 * would be created if gzopen/gzwrite/gzclose were being used to
755 * write the document to disk. The code for the header/trailer data to
756 * the compression is plagiarized from the zlib source files.
757 */
758static void *
759xmlCreateZMemBuff( int compression ) {
760
761 int z_err;
762 int hdr_lgth;
763 xmlZMemBuffPtr buff = NULL;
764
765 if ( ( compression < 1 ) || ( compression > 9 ) )
766 return ( NULL );
767
768 /* Create the control and data areas */
769
770 buff = xmlMalloc( sizeof( xmlZMemBuff ) );
771 if ( buff == NULL ) {
772 xmlGenericError( xmlGenericErrorContext,
773 "xmlCreateZMemBuff: %s\n",
774 "Failure allocating buffer context." );
775 return ( NULL );
776 }
777
778 (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
779 buff->size = INIT_HTTP_BUFF_SIZE;
780 buff->zbuff = xmlMalloc( buff->size );
781 if ( buff->zbuff == NULL ) {
782 xmlFreeZMemBuff( buff );
783 xmlGenericError( xmlGenericErrorContext,
784 "xmlCreateZMemBuff: %s\n",
785 "Failure allocating data buffer." );
786 return ( NULL );
787 }
788
789 z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
790 DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
791 if ( z_err != Z_OK ) {
792 xmlFreeZMemBuff( buff );
793 buff = NULL;
794 xmlGenericError( xmlGenericErrorContext,
795 "xmlCreateZMemBuff: %s %d\n",
796 "Error initializing compression context. ZLIB error:",
797 z_err );
798 return ( NULL );
799 }
800
801 /* Set the header data. The CRC will be needed for the trailer */
Daniel Veillardf012a642001-07-23 19:10:52 +0000802 buff->crc = crc32( 0L, Z_NULL, 0 );
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000803 hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
804 "%c%c%c%c%c%c%c%c%c%c",
Daniel Veillardf012a642001-07-23 19:10:52 +0000805 GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
806 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
807 buff->zctrl.next_out = buff->zbuff + hdr_lgth;
808 buff->zctrl.avail_out = buff->size - hdr_lgth;
809
810 return ( buff );
811}
812
813/**
814 * xmlZMemBuffExtend
815 * @buff: Buffer used to compress and consolidate data.
816 * @ext_amt: Number of bytes to extend the buffer.
817 *
818 * Extend the internal buffer used to store the compressed data by the
819 * specified amount.
820 *
821 * Returns 0 on success or -1 on failure to extend the buffer. On failure
822 * the original buffer still exists at the original size.
823 */
824static int
825xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
826
827 int rc = -1;
828 size_t new_size;
829 size_t cur_used;
830
831 unsigned char * tmp_ptr = NULL;
832
833 if ( buff == NULL )
834 return ( -1 );
835
836 else if ( ext_amt == 0 )
837 return ( 0 );
838
839 cur_used = buff->zctrl.next_out - buff->zbuff;
840 new_size = buff->size + ext_amt;
841
842#ifdef DEBUG_HTTP
843 if ( cur_used > new_size )
844 xmlGenericError( xmlGenericErrorContext,
845 "xmlZMemBuffExtend: %s\n%s %d bytes.\n",
846 "Buffer overwrite detected during compressed memory",
847 "buffer extension. Overflowed by",
848 (cur_used - new_size ) );
849#endif
850
851 tmp_ptr = xmlRealloc( buff->zbuff, new_size );
852 if ( tmp_ptr != NULL ) {
853 rc = 0;
854 buff->size = new_size;
855 buff->zbuff = tmp_ptr;
856 buff->zctrl.next_out = tmp_ptr + cur_used;
857 buff->zctrl.avail_out = new_size - cur_used;
858 }
859 else {
860 xmlGenericError( xmlGenericErrorContext,
861 "xmlZMemBuffExtend: %s %lu bytes.\n",
862 "Allocation failure extending output buffer to",
863 new_size );
864 }
865
866 return ( rc );
867}
868
869/**
870 * xmlZMemBuffAppend
871 * @buff: Buffer used to compress and consolidate data
872 * @src: Uncompressed source content to append to buffer
873 * @len: Length of source data to append to buffer
874 *
875 * Compress and append data to the internal buffer. The data buffer
876 * will be expanded if needed to store the additional data.
877 *
878 * Returns the number of bytes appended to the buffer or -1 on error.
879 */
880static int
881xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
882
883 int z_err;
884 size_t min_accept;
885
886 if ( ( buff == NULL ) || ( src == NULL ) )
887 return ( -1 );
888
889 buff->zctrl.avail_in = len;
890 buff->zctrl.next_in = (unsigned char *)src;
891 while ( buff->zctrl.avail_in > 0 ) {
892 /*
893 ** Extend the buffer prior to deflate call if a reasonable amount
894 ** of output buffer space is not available.
895 */
896 min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
897 if ( buff->zctrl.avail_out <= min_accept ) {
898 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
899 return ( -1 );
900 }
901
902 z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
903 if ( z_err != Z_OK ) {
904 xmlGenericError( xmlGenericErrorContext,
905 "xmlZMemBuffAppend: %s %d %s - %d",
906 "Compression error while appending",
907 len, "bytes to buffer. ZLIB error", z_err );
908 return ( -1 );
909 }
910 }
911
912 buff->crc = crc32( buff->crc, (unsigned char *)src, len );
913
914 return ( len );
915}
916
917/**
918 * xmlZMemBuffGetContent
919 * @buff: Compressed memory content buffer
920 * @data_ref: Pointer reference to point to compressed content
921 *
922 * Flushes the compression buffers, appends gzip file trailers and
923 * returns the compressed content and length of the compressed data.
924 * NOTE: The gzip trailer code here is plagiarized from zlib source.
925 *
926 * Returns the length of the compressed data or -1 on error.
927 */
928static int
929xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
930
931 int zlgth = -1;
932 int z_err;
933
934 if ( ( buff == NULL ) || ( data_ref == NULL ) )
935 return ( -1 );
936
937 /* Need to loop until compression output buffers are flushed */
938
939 do
940 {
941 z_err = deflate( &buff->zctrl, Z_FINISH );
942 if ( z_err == Z_OK ) {
943 /* In this case Z_OK means more buffer space needed */
944
945 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
946 return ( -1 );
947 }
948 }
949 while ( z_err == Z_OK );
950
951 /* If the compression state is not Z_STREAM_END, some error occurred */
952
953 if ( z_err == Z_STREAM_END ) {
954
955 /* Need to append the gzip data trailer */
956
957 if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
958 if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
959 return ( -1 );
960 }
961
962 /*
963 ** For whatever reason, the CRC and length data are pushed out
964 ** in reverse byte order. So a memcpy can't be used here.
965 */
966
967 append_reverse_ulong( buff, buff->crc );
968 append_reverse_ulong( buff, buff->zctrl.total_in );
969
970 zlgth = buff->zctrl.next_out - buff->zbuff;
971 *data_ref = (char *)buff->zbuff;
972 }
973
974 else
975 xmlGenericError( xmlGenericErrorContext,
976 "xmlZMemBuffGetContent: %s - %d\n",
977 "Error flushing zlib buffers. Error code", z_err );
978
979 return ( zlgth );
980}
981#endif /* HAVE_ZLIB_H */
982
983/**
984 * xmlFreeHTTPWriteCtxt
985 * @ctxt: Context to cleanup
986 *
987 * Free allocated memory and reclaim system resources.
988 *
989 * No return value.
990 */
991static void
992xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
993{
994 if ( ctxt->uri != NULL )
995 free( ctxt->uri );
996
997 if ( ctxt->doc_buff != NULL ) {
998
999#ifdef HAVE_ZLIB_H
1000 if ( ctxt->compression > 0 ) {
1001 xmlFreeZMemBuff( ctxt->doc_buff );
1002 }
1003 else
1004#endif
1005 {
1006 xmlOutputBufferClose( ctxt->doc_buff );
1007 }
1008 }
1009
1010 free( ctxt );
1011 return;
1012}
1013
1014
Owen Taylor3473f882001-02-23 17:55:21 +00001015/**
1016 * xmlIOHTTPMatch:
1017 * @filename: the URI for matching
1018 *
1019 * check if the URI matches an HTTP one
1020 *
1021 * Returns 1 if matches, 0 otherwise
1022 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001023int
Owen Taylor3473f882001-02-23 17:55:21 +00001024xmlIOHTTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001025 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
Owen Taylor3473f882001-02-23 17:55:21 +00001026 return(1);
1027 return(0);
1028}
1029
1030/**
1031 * xmlIOHTTPOpen:
1032 * @filename: the URI for matching
1033 *
1034 * open an HTTP I/O channel
1035 *
1036 * Returns an I/O context or NULL in case of error
1037 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001038void *
Owen Taylor3473f882001-02-23 17:55:21 +00001039xmlIOHTTPOpen (const char *filename) {
1040 return(xmlNanoHTTPOpen(filename, NULL));
1041}
1042
1043/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001044 * xmlIOHTTPOpenW
1045 * @post_uri: The destination URI for the document
1046 * @compression: The compression desired for the document.
1047 *
1048 * Open a temporary buffer to collect the document for a subsequent HTTP POST
1049 * request. Non-static as is called from the output buffer creation routine.
1050 *
1051 * Returns an I/O context or NULL in case of error.
1052 */
1053
1054void *
Daniel Veillard572577e2002-01-18 16:23:55 +00001055xmlIOHTTPOpenW(const char *post_uri, int compression)
1056{
Daniel Veillardf012a642001-07-23 19:10:52 +00001057
Daniel Veillard572577e2002-01-18 16:23:55 +00001058 xmlIOHTTPWriteCtxtPtr ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001059
Daniel Veillard572577e2002-01-18 16:23:55 +00001060 if (post_uri == NULL)
1061 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001062
Daniel Veillard572577e2002-01-18 16:23:55 +00001063 ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1064 if (ctxt == NULL) {
1065 xmlGenericError(xmlGenericErrorContext,
1066 "xmlIOHTTPOpenW: Failed to create output HTTP context.\n");
1067 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001068 }
1069
Daniel Veillard572577e2002-01-18 16:23:55 +00001070 (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
Daniel Veillardf012a642001-07-23 19:10:52 +00001071
Daniel Veillard572577e2002-01-18 16:23:55 +00001072 ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1073 if (ctxt->uri == NULL) {
1074 xmlGenericError(xmlGenericErrorContext,
1075 "xmlIOHTTPOpenW: Failed to duplicate destination URI.\n");
1076 xmlFreeHTTPWriteCtxt(ctxt);
1077 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001078 }
1079
1080 /*
Daniel Veillard572577e2002-01-18 16:23:55 +00001081 * ** Since the document length is required for an HTTP post,
1082 * ** need to put the document into a buffer. A memory buffer
1083 * ** is being used to avoid pushing the data to disk and back.
1084 */
Daniel Veillardf012a642001-07-23 19:10:52 +00001085
1086#ifdef HAVE_ZLIB_H
Daniel Veillard572577e2002-01-18 16:23:55 +00001087 if ((compression > 0) && (compression <= 9)) {
1088
1089 ctxt->compression = compression;
1090 ctxt->doc_buff = xmlCreateZMemBuff(compression);
1091 } else
Daniel Veillardf012a642001-07-23 19:10:52 +00001092#endif
1093 {
Daniel Veillard572577e2002-01-18 16:23:55 +00001094 /* Any character conversions should have been done before this */
Daniel Veillardf012a642001-07-23 19:10:52 +00001095
Daniel Veillard572577e2002-01-18 16:23:55 +00001096 ctxt->doc_buff = xmlAllocOutputBuffer(NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001097 }
1098
Daniel Veillard572577e2002-01-18 16:23:55 +00001099 if (ctxt->doc_buff == NULL) {
1100 xmlFreeHTTPWriteCtxt(ctxt);
1101 ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001102 }
1103
Daniel Veillard572577e2002-01-18 16:23:55 +00001104 return (ctxt);
Daniel Veillardf012a642001-07-23 19:10:52 +00001105}
1106
1107/**
1108 * xmlIOHTTPDfltOpenW
1109 * @post_uri: The destination URI for this document.
1110 *
1111 * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1112 * HTTP post command. This function should generally not be used as
1113 * the open callback is short circuited in xmlOutputBufferCreateFile.
1114 *
1115 * Returns a pointer to the new IO context.
1116 */
1117static void *
1118xmlIOHTTPDfltOpenW( const char * post_uri ) {
1119 return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1120}
1121
1122/**
Owen Taylor3473f882001-02-23 17:55:21 +00001123 * xmlIOHTTPRead:
1124 * @context: the I/O context
1125 * @buffer: where to drop data
1126 * @len: number of bytes to write
1127 *
1128 * Read @len bytes to @buffer from the I/O channel.
1129 *
1130 * Returns the number of bytes written
1131 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001132int
Owen Taylor3473f882001-02-23 17:55:21 +00001133xmlIOHTTPRead(void * context, char * buffer, int len) {
1134 return(xmlNanoHTTPRead(context, &buffer[0], len));
1135}
1136
1137/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001138 * xmlIOHTTPWrite
1139 * @context: previously opened writing context
1140 * @buffer: data to output to temporary buffer
1141 * @len: bytes to output
1142 *
1143 * Collect data from memory buffer into a temporary file for later
1144 * processing.
1145 *
1146 * Returns number of bytes written.
1147 */
1148
1149static int
1150xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
1151
1152 xmlIOHTTPWriteCtxtPtr ctxt = context;
1153
1154 if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1155 return ( -1 );
1156
1157 if ( len > 0 ) {
1158
1159 /* Use gzwrite or fwrite as previously setup in the open call */
1160
1161#ifdef HAVE_ZLIB_H
1162 if ( ctxt->compression > 0 )
1163 len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1164
1165 else
1166#endif
1167 len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
1168
1169 if ( len < 0 ) {
1170 xmlGenericError( xmlGenericErrorContext,
1171 "xmlIOHTTPWrite: %s\n%s '%s'.\n",
1172 "Error appending to internal buffer.",
1173 "Error sending document to URI",
1174 ctxt->uri );
1175 }
1176 }
1177
1178 return ( len );
1179}
1180
1181
1182/**
Owen Taylor3473f882001-02-23 17:55:21 +00001183 * xmlIOHTTPClose:
1184 * @context: the I/O context
1185 *
1186 * Close an HTTP I/O channel
1187 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001188int
Owen Taylor3473f882001-02-23 17:55:21 +00001189xmlIOHTTPClose (void * context) {
1190 xmlNanoHTTPClose(context);
Daniel Veillardf012a642001-07-23 19:10:52 +00001191 return 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001192}
Daniel Veillardf012a642001-07-23 19:10:52 +00001193
1194/**
1195 * xmlIOHTTCloseWrite
1196 * @context: The I/O context
1197 * @http_mthd: The HTTP method to be used when sending the data
1198 *
1199 * Close the transmit HTTP I/O channel and actually send the data.
1200 */
1201static int
1202xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
1203
1204 int close_rc = -1;
1205 int http_rtn = 0;
1206 int content_lgth = 0;
1207 xmlIOHTTPWriteCtxtPtr ctxt = context;
1208
1209 char * http_content = NULL;
1210 char * content_encoding = NULL;
1211 char * content_type = (char *) "text/xml";
1212 void * http_ctxt = NULL;
1213
1214 if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
1215 return ( -1 );
1216
1217 /* Retrieve the content from the appropriate buffer */
1218
1219#ifdef HAVE_ZLIB_H
1220
1221 if ( ctxt->compression > 0 ) {
1222 content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
1223 content_encoding = (char *) "Content-Encoding: gzip";
1224 }
1225 else
1226#endif
1227 {
1228 /* Pull the data out of the memory output buffer */
1229
1230 xmlOutputBufferPtr dctxt = ctxt->doc_buff;
1231 http_content = (char *)dctxt->buffer->content;
1232 content_lgth = dctxt->buffer->use;
1233 }
1234
1235 if ( http_content == NULL ) {
1236 xmlGenericError( xmlGenericErrorContext,
1237 "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n",
1238 "Error retrieving content.\nUnable to",
1239 http_mthd, "data to URI", ctxt->uri );
1240 }
1241
1242 else {
1243
1244 http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
1245 &content_type, content_encoding,
1246 content_lgth );
1247
1248 if ( http_ctxt != NULL ) {
1249#ifdef DEBUG_HTTP
1250 /* If testing/debugging - dump reply with request content */
1251
1252 FILE * tst_file = NULL;
1253 char buffer[ 4096 ];
1254 char * dump_name = NULL;
1255 int avail;
1256
1257 xmlGenericError( xmlGenericErrorContext,
1258 "xmlNanoHTTPCloseWrite: HTTP %s to\n%s returned %d.\n",
1259 http_mthd, ctxt->uri,
1260 xmlNanoHTTPReturnCode( http_ctxt ) );
1261
1262 /*
1263 ** Since either content or reply may be gzipped,
1264 ** dump them to separate files instead of the
1265 ** standard error context.
1266 */
1267
1268 dump_name = tempnam( NULL, "lxml" );
1269 if ( dump_name != NULL ) {
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001270 (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001271
1272 tst_file = fopen( buffer, "w" );
1273 if ( tst_file != NULL ) {
1274 xmlGenericError( xmlGenericErrorContext,
1275 "Transmitted content saved in file: %s\n", buffer );
1276
1277 fwrite( http_content, sizeof( char ),
1278 content_lgth, tst_file );
1279 fclose( tst_file );
1280 }
1281
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001282 (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001283 tst_file = fopen( buffer, "w" );
1284 if ( tst_file != NULL ) {
1285 xmlGenericError( xmlGenericErrorContext,
1286 "Reply content saved in file: %s\n", buffer );
1287
1288
1289 while ( (avail = xmlNanoHTTPRead( http_ctxt,
1290 buffer, sizeof( buffer ) )) > 0 ) {
1291
1292 fwrite( buffer, sizeof( char ), avail, tst_file );
1293 }
1294
1295 fclose( tst_file );
1296 }
1297
1298 free( dump_name );
1299 }
1300#endif /* DEBUG_HTTP */
1301
1302 http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
1303 if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
1304 close_rc = 0;
1305 else
1306 xmlGenericError( xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001307 "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
Daniel Veillardf012a642001-07-23 19:10:52 +00001308 http_mthd, content_lgth,
1309 "bytes to URI", ctxt->uri,
1310 "failed. HTTP return code:", http_rtn );
1311
1312 xmlNanoHTTPClose( http_ctxt );
1313 xmlFree( content_type );
1314 }
1315 }
1316
1317 /* Final cleanups */
1318
1319 xmlFreeHTTPWriteCtxt( ctxt );
1320
1321 return ( close_rc );
1322}
1323
1324/**
1325 * xmlIOHTTPClosePut
1326 *
1327 * @context: The I/O context
1328 *
1329 * Close the transmit HTTP I/O channel and actually send data using a PUT
1330 * HTTP method.
1331 */
1332static int
1333xmlIOHTTPClosePut( void * ctxt ) {
1334 return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
1335}
1336
1337
1338/**
1339 * xmlIOHTTPClosePost
1340 *
1341 * @context: The I/O context
1342 *
1343 * Close the transmit HTTP I/O channel and actually send data using a POST
1344 * HTTP method.
1345 */
1346static int
1347xmlIOHTTPClosePost( void * ctxt ) {
1348 return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
1349}
1350
Owen Taylor3473f882001-02-23 17:55:21 +00001351#endif /* LIBXML_HTTP_ENABLED */
1352
1353#ifdef LIBXML_FTP_ENABLED
1354/************************************************************************
1355 * *
1356 * I/O for FTP file accesses *
1357 * *
1358 ************************************************************************/
1359/**
1360 * xmlIOFTPMatch:
1361 * @filename: the URI for matching
1362 *
1363 * check if the URI matches an FTP one
1364 *
1365 * Returns 1 if matches, 0 otherwise
1366 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001367int
Owen Taylor3473f882001-02-23 17:55:21 +00001368xmlIOFTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001369 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
Owen Taylor3473f882001-02-23 17:55:21 +00001370 return(1);
1371 return(0);
1372}
1373
1374/**
1375 * xmlIOFTPOpen:
1376 * @filename: the URI for matching
1377 *
1378 * open an FTP I/O channel
1379 *
1380 * Returns an I/O context or NULL in case of error
1381 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001382void *
Owen Taylor3473f882001-02-23 17:55:21 +00001383xmlIOFTPOpen (const char *filename) {
1384 return(xmlNanoFTPOpen(filename));
1385}
1386
1387/**
1388 * xmlIOFTPRead:
1389 * @context: the I/O context
1390 * @buffer: where to drop data
1391 * @len: number of bytes to write
1392 *
1393 * Read @len bytes to @buffer from the I/O channel.
1394 *
1395 * Returns the number of bytes written
1396 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001397int
Owen Taylor3473f882001-02-23 17:55:21 +00001398xmlIOFTPRead(void * context, char * buffer, int len) {
1399 return(xmlNanoFTPRead(context, &buffer[0], len));
1400}
1401
1402/**
1403 * xmlIOFTPClose:
1404 * @context: the I/O context
1405 *
1406 * Close an FTP I/O channel
1407 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001408int
Owen Taylor3473f882001-02-23 17:55:21 +00001409xmlIOFTPClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001410 return ( xmlNanoFTPClose(context) );
Owen Taylor3473f882001-02-23 17:55:21 +00001411}
1412#endif /* LIBXML_FTP_ENABLED */
1413
1414
1415/**
1416 * xmlRegisterInputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001417 * @matchFunc: the xmlInputMatchCallback
1418 * @openFunc: the xmlInputOpenCallback
1419 * @readFunc: the xmlInputReadCallback
1420 * @closeFunc: the xmlInputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001421 *
1422 * Register a new set of I/O callback for handling parser input.
1423 *
1424 * Returns the registered handler number or -1 in case of error
1425 */
1426int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001427xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
1428 xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
1429 xmlInputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001430 if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
1431 return(-1);
1432 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001433 xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
1434 xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
1435 xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
1436 xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001437 return(xmlInputCallbackNr++);
1438}
1439
1440/**
1441 * xmlRegisterOutputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001442 * @matchFunc: the xmlOutputMatchCallback
1443 * @openFunc: the xmlOutputOpenCallback
1444 * @writeFunc: the xmlOutputWriteCallback
1445 * @closeFunc: the xmlOutputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001446 *
1447 * Register a new set of I/O callback for handling output.
1448 *
1449 * Returns the registered handler number or -1 in case of error
1450 */
1451int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001452xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
1453 xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
1454 xmlOutputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001455 if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) {
1456 return(-1);
1457 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001458 xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
1459 xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
1460 xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
1461 xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001462 return(xmlOutputCallbackNr++);
1463}
1464
1465/**
1466 * xmlRegisterDefaultInputCallbacks:
1467 *
1468 * Registers the default compiled-in I/O handlers.
1469 */
1470void
1471#ifdef VMS
1472xmlRegisterDefInputCallbacks
1473#else
1474xmlRegisterDefaultInputCallbacks
1475#endif
1476(void) {
1477 if (xmlInputCallbackInitialized)
1478 return;
1479
1480 xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
1481 xmlFileRead, xmlFileClose);
1482#ifdef HAVE_ZLIB_H
1483 xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1484 xmlGzfileRead, xmlGzfileClose);
1485#endif /* HAVE_ZLIB_H */
1486
1487#ifdef LIBXML_HTTP_ENABLED
1488 xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
1489 xmlIOHTTPRead, xmlIOHTTPClose);
1490#endif /* LIBXML_HTTP_ENABLED */
1491
1492#ifdef LIBXML_FTP_ENABLED
1493 xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1494 xmlIOFTPRead, xmlIOFTPClose);
1495#endif /* LIBXML_FTP_ENABLED */
1496 xmlInputCallbackInitialized = 1;
1497}
1498
1499/**
1500 * xmlRegisterDefaultOutputCallbacks:
1501 *
1502 * Registers the default compiled-in I/O handlers.
1503 */
1504void
1505#ifdef VMS
1506xmlRegisterDefOutputCallbacks
1507#else
1508xmlRegisterDefaultOutputCallbacks
1509#endif
1510(void) {
1511 if (xmlOutputCallbackInitialized)
1512 return;
1513
1514 xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
1515 xmlFileWrite, xmlFileClose);
Daniel Veillardf012a642001-07-23 19:10:52 +00001516
1517#ifdef LIBXML_HTTP_ENABLED
1518 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1519 xmlIOHTTPWrite, xmlIOHTTPClosePut);
1520#endif
1521
Owen Taylor3473f882001-02-23 17:55:21 +00001522/*********************************
1523 No way a-priori to distinguish between gzipped files from
1524 uncompressed ones except opening if existing then closing
1525 and saving with same compression ratio ... a pain.
1526
1527#ifdef HAVE_ZLIB_H
1528 xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1529 xmlGzfileWrite, xmlGzfileClose);
1530#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001531
1532 Nor FTP PUT ....
1533#ifdef LIBXML_FTP_ENABLED
1534 xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1535 xmlIOFTPWrite, xmlIOFTPClose);
1536#endif
1537 **********************************/
1538 xmlOutputCallbackInitialized = 1;
1539}
1540
Daniel Veillardf012a642001-07-23 19:10:52 +00001541#ifdef LIBXML_HTTP_ENABLED
1542/**
1543 * xmlRegisterHTTPPostCallbacks
1544 *
1545 * By default, libxml submits HTTP output requests using the "PUT" method.
1546 * Calling this method changes the HTTP output method to use the "POST"
1547 * method instead.
1548 *
1549 */
1550void
1551xmlRegisterHTTPPostCallbacks( void ) {
1552
1553 /* Register defaults if not done previously */
1554
1555 if ( xmlOutputCallbackInitialized == 0 )
1556 xmlRegisterDefaultOutputCallbacks( );
1557
1558 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1559 xmlIOHTTPWrite, xmlIOHTTPClosePost);
1560 return;
1561}
1562#endif
1563
Owen Taylor3473f882001-02-23 17:55:21 +00001564/**
1565 * xmlAllocParserInputBuffer:
1566 * @enc: the charset encoding if known
1567 *
1568 * Create a buffered parser input for progressive parsing
1569 *
1570 * Returns the new parser input or NULL
1571 */
1572xmlParserInputBufferPtr
1573xmlAllocParserInputBuffer(xmlCharEncoding enc) {
1574 xmlParserInputBufferPtr ret;
1575
1576 ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
1577 if (ret == NULL) {
1578 xmlGenericError(xmlGenericErrorContext,
1579 "xmlAllocParserInputBuffer : out of memory!\n");
1580 return(NULL);
1581 }
1582 memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
1583 ret->buffer = xmlBufferCreate();
1584 if (ret->buffer == NULL) {
1585 xmlFree(ret);
1586 return(NULL);
1587 }
1588 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1589 ret->encoder = xmlGetCharEncodingHandler(enc);
1590 if (ret->encoder != NULL)
1591 ret->raw = xmlBufferCreate();
1592 else
1593 ret->raw = NULL;
1594 ret->readcallback = NULL;
1595 ret->closecallback = NULL;
1596 ret->context = NULL;
1597
1598 return(ret);
1599}
1600
1601/**
1602 * xmlAllocOutputBuffer:
1603 * @encoder: the encoding converter or NULL
1604 *
1605 * Create a buffered parser output
1606 *
1607 * Returns the new parser output or NULL
1608 */
1609xmlOutputBufferPtr
1610xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
1611 xmlOutputBufferPtr ret;
1612
1613 ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1614 if (ret == NULL) {
1615 xmlGenericError(xmlGenericErrorContext,
1616 "xmlAllocOutputBuffer : out of memory!\n");
1617 return(NULL);
1618 }
1619 memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
1620 ret->buffer = xmlBufferCreate();
1621 if (ret->buffer == NULL) {
1622 xmlFree(ret);
1623 return(NULL);
1624 }
1625 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1626 ret->encoder = encoder;
1627 if (encoder != NULL) {
1628 ret->conv = xmlBufferCreateSize(4000);
1629 /*
1630 * This call is designed to initiate the encoder state
1631 */
1632 xmlCharEncOutFunc(encoder, ret->conv, NULL);
1633 } else
1634 ret->conv = NULL;
1635 ret->writecallback = NULL;
1636 ret->closecallback = NULL;
1637 ret->context = NULL;
1638 ret->written = 0;
1639
1640 return(ret);
1641}
1642
1643/**
1644 * xmlFreeParserInputBuffer:
1645 * @in: a buffered parser input
1646 *
1647 * Free up the memory used by a buffered parser input
1648 */
1649void
1650xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
1651 if (in->raw) {
1652 xmlBufferFree(in->raw);
1653 in->raw = NULL;
1654 }
1655 if (in->encoder != NULL) {
1656 xmlCharEncCloseFunc(in->encoder);
1657 }
1658 if (in->closecallback != NULL) {
1659 in->closecallback(in->context);
1660 }
1661 if (in->buffer != NULL) {
1662 xmlBufferFree(in->buffer);
1663 in->buffer = NULL;
1664 }
1665
Owen Taylor3473f882001-02-23 17:55:21 +00001666 xmlFree(in);
1667}
1668
1669/**
1670 * xmlOutputBufferClose:
1671 * @out: a buffered output
1672 *
1673 * flushes and close the output I/O channel
1674 * and free up all the associated resources
1675 *
1676 * Returns the number of byte written or -1 in case of error.
1677 */
1678int
1679xmlOutputBufferClose(xmlOutputBufferPtr out) {
1680 int written;
Daniel Veillardf012a642001-07-23 19:10:52 +00001681 int err_rc = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001682
1683 if (out == NULL)
1684 return(-1);
1685 if (out->writecallback != NULL)
1686 xmlOutputBufferFlush(out);
1687 if (out->closecallback != NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001688 err_rc = out->closecallback(out->context);
Owen Taylor3473f882001-02-23 17:55:21 +00001689 }
1690 written = out->written;
1691 if (out->conv) {
1692 xmlBufferFree(out->conv);
1693 out->conv = NULL;
1694 }
1695 if (out->encoder != NULL) {
1696 xmlCharEncCloseFunc(out->encoder);
1697 }
1698 if (out->buffer != NULL) {
1699 xmlBufferFree(out->buffer);
1700 out->buffer = NULL;
1701 }
1702
Owen Taylor3473f882001-02-23 17:55:21 +00001703 xmlFree(out);
Daniel Veillardf012a642001-07-23 19:10:52 +00001704 return( ( err_rc == 0 ) ? written : err_rc );
Owen Taylor3473f882001-02-23 17:55:21 +00001705}
1706
1707/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001708 * xmlParserInputBufferCreateFname:
1709 * @URI: a C string containing the URI or filename
1710 * @enc: the charset encoding if known
1711 *
1712 * VMS version of xmlParserInputBufferCreateFilename()
1713 *
1714 * Returns the new parser input or NULL
1715 */
1716/**
Owen Taylor3473f882001-02-23 17:55:21 +00001717 * xmlParserInputBufferCreateFilename:
1718 * @URI: a C string containing the URI or filename
1719 * @enc: the charset encoding if known
1720 *
1721 * Create a buffered parser input for the progressive parsing of a file
1722 * If filename is "-' then we use stdin as the input.
1723 * Automatic support for ZLIB/Compress compressed document is provided
1724 * by default if found at compile-time.
1725 * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
1726 *
1727 * Returns the new parser input or NULL
1728 */
1729xmlParserInputBufferPtr
1730#ifdef VMS
1731xmlParserInputBufferCreateFname
1732#else
1733xmlParserInputBufferCreateFilename
1734#endif
1735(const char *URI, xmlCharEncoding enc) {
1736 xmlParserInputBufferPtr ret;
Daniel Veillard388236f2001-07-08 18:35:48 +00001737 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001738 void *context = NULL;
Daniel Veillard388236f2001-07-08 18:35:48 +00001739 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001740 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001741
1742 if (xmlInputCallbackInitialized == 0)
1743 xmlRegisterDefaultInputCallbacks();
1744
1745 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001746 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1747 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001748
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00001749#ifdef LIBXML_CATALOG_ENABLED
1750#endif
1751
Owen Taylor3473f882001-02-23 17:55:21 +00001752 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001753 * Try to find one of the input accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001754 * Go in reverse to give precedence to user defined handlers.
Daniel Veillard388236f2001-07-08 18:35:48 +00001755 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001756 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001757 unescaped = xmlURIUnescapeString((char *) normalized, 0, NULL);
Daniel Veillard388236f2001-07-08 18:35:48 +00001758 if (unescaped != NULL) {
1759 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1760 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1761 (xmlInputCallbackTable[i].matchcallback(unescaped) != 0)) {
1762 context = xmlInputCallbackTable[i].opencallback(unescaped);
1763 if (context != NULL)
1764 break;
1765 }
1766 }
1767 xmlFree(unescaped);
1768 }
1769
1770 /*
1771 * If this failed try with a non-escaped URI this may be a strange
1772 * filename
1773 */
1774 if (context == NULL) {
1775 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1776 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1777 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001778 context = xmlInputCallbackTable[i].opencallback(normalized);
Daniel Veillard388236f2001-07-08 18:35:48 +00001779 if (context != NULL)
1780 break;
1781 }
Owen Taylor3473f882001-02-23 17:55:21 +00001782 }
1783 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001784 xmlFree(normalized);
Owen Taylor3473f882001-02-23 17:55:21 +00001785 if (context == NULL) {
1786 return(NULL);
1787 }
1788
1789 /*
1790 * Allocate the Input buffer front-end.
1791 */
1792 ret = xmlAllocParserInputBuffer(enc);
1793 if (ret != NULL) {
1794 ret->context = context;
1795 ret->readcallback = xmlInputCallbackTable[i].readcallback;
1796 ret->closecallback = xmlInputCallbackTable[i].closecallback;
1797 }
1798 return(ret);
1799}
1800
1801/**
1802 * xmlOutputBufferCreateFilename:
1803 * @URI: a C string containing the URI or filename
1804 * @encoder: the encoding converter or NULL
1805 * @compression: the compression ration (0 none, 9 max).
1806 *
1807 * Create a buffered output for the progressive saving of a file
1808 * If filename is "-' then we use stdout as the output.
1809 * Automatic support for ZLIB/Compress compressed document is provided
1810 * by default if found at compile-time.
1811 * TODO: currently if compression is set, the library only support
1812 * writing to a local file.
1813 *
1814 * Returns the new output or NULL
1815 */
1816xmlOutputBufferPtr
1817xmlOutputBufferCreateFilename(const char *URI,
1818 xmlCharEncodingHandlerPtr encoder,
1819 int compression) {
1820 xmlOutputBufferPtr ret;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001821 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001822 void *context = NULL;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001823 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001824 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001825
Daniel Veillardf012a642001-07-23 19:10:52 +00001826 int is_http_uri = 0; /* Can't change if HTTP disabled */
1827
Owen Taylor3473f882001-02-23 17:55:21 +00001828 if (xmlOutputCallbackInitialized == 0)
1829 xmlRegisterDefaultOutputCallbacks();
1830
1831 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001832 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1833 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001834
Daniel Veillardf012a642001-07-23 19:10:52 +00001835#ifdef LIBXML_HTTP_ENABLED
1836 /* Need to prevent HTTP URI's from falling into zlib short circuit */
1837
Daniel Veillardf4862f02002-09-10 11:13:43 +00001838 is_http_uri = xmlIOHTTPMatch( normalized );
Daniel Veillardf012a642001-07-23 19:10:52 +00001839#endif
1840
Owen Taylor3473f882001-02-23 17:55:21 +00001841
1842 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001843 * Try to find one of the output accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001844 * Go in reverse to give precedence to user defined handlers.
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001845 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001846 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001847 unescaped = xmlURIUnescapeString(normalized, 0, NULL);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001848 if (unescaped != NULL) {
1849#ifdef HAVE_ZLIB_H
1850 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1851 context = xmlGzfileOpenW(unescaped, compression);
1852 if (context != NULL) {
1853 ret = xmlAllocOutputBuffer(encoder);
1854 if (ret != NULL) {
1855 ret->context = context;
1856 ret->writecallback = xmlGzfileWrite;
1857 ret->closecallback = xmlGzfileClose;
1858 }
1859 xmlFree(unescaped);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001860 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001861 return(ret);
1862 }
1863 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001864#endif
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001865 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1866 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1867 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
1868#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1869 /* Need to pass compression parameter into HTTP open calls */
1870 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1871 context = xmlIOHTTPOpenW(unescaped, compression);
1872 else
1873#endif
1874 context = xmlOutputCallbackTable[i].opencallback(unescaped);
1875 if (context != NULL)
1876 break;
1877 }
1878 }
1879 xmlFree(unescaped);
1880 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001881
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001882 /*
1883 * If this failed try with a non-escaped URI this may be a strange
1884 * filename
1885 */
1886 if (context == NULL) {
1887#ifdef HAVE_ZLIB_H
1888 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001889 context = xmlGzfileOpenW(normalized, compression);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001890 if (context != NULL) {
1891 ret = xmlAllocOutputBuffer(encoder);
1892 if (ret != NULL) {
1893 ret->context = context;
1894 ret->writecallback = xmlGzfileWrite;
1895 ret->closecallback = xmlGzfileClose;
1896 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001897 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001898 return(ret);
1899 }
1900 }
1901#endif
1902 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1903 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
Daniel Veillardf4862f02002-09-10 11:13:43 +00001904 (xmlOutputCallbackTable[i].matchcallback(normalized) != 0)) {
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001905#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1906 /* Need to pass compression parameter into HTTP open calls */
1907 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1908 context = xmlIOHTTPOpenW(URI, compression);
1909 else
1910#endif
1911 context = xmlOutputCallbackTable[i].opencallback(URI);
1912 if (context != NULL)
1913 break;
1914 }
Owen Taylor3473f882001-02-23 17:55:21 +00001915 }
1916 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001917 xmlFree(normalized);
Daniel Veillardf012a642001-07-23 19:10:52 +00001918
Owen Taylor3473f882001-02-23 17:55:21 +00001919 if (context == NULL) {
1920 return(NULL);
1921 }
1922
1923 /*
1924 * Allocate the Output buffer front-end.
1925 */
1926 ret = xmlAllocOutputBuffer(encoder);
1927 if (ret != NULL) {
1928 ret->context = context;
1929 ret->writecallback = xmlOutputCallbackTable[i].writecallback;
1930 ret->closecallback = xmlOutputCallbackTable[i].closecallback;
1931 }
1932 return(ret);
1933}
1934
1935/**
1936 * xmlParserInputBufferCreateFile:
1937 * @file: a FILE*
1938 * @enc: the charset encoding if known
1939 *
1940 * Create a buffered parser input for the progressive parsing of a FILE *
1941 * buffered C I/O
1942 *
1943 * Returns the new parser input or NULL
1944 */
1945xmlParserInputBufferPtr
1946xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
1947 xmlParserInputBufferPtr ret;
1948
1949 if (xmlInputCallbackInitialized == 0)
1950 xmlRegisterDefaultInputCallbacks();
1951
1952 if (file == NULL) return(NULL);
1953
1954 ret = xmlAllocParserInputBuffer(enc);
1955 if (ret != NULL) {
1956 ret->context = file;
1957 ret->readcallback = xmlFileRead;
1958 ret->closecallback = xmlFileFlush;
1959 }
1960
1961 return(ret);
1962}
1963
1964/**
1965 * xmlOutputBufferCreateFile:
1966 * @file: a FILE*
1967 * @encoder: the encoding converter or NULL
1968 *
1969 * Create a buffered output for the progressive saving to a FILE *
1970 * buffered C I/O
1971 *
1972 * Returns the new parser output or NULL
1973 */
1974xmlOutputBufferPtr
1975xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
1976 xmlOutputBufferPtr ret;
1977
1978 if (xmlOutputCallbackInitialized == 0)
1979 xmlRegisterDefaultOutputCallbacks();
1980
1981 if (file == NULL) return(NULL);
1982
1983 ret = xmlAllocOutputBuffer(encoder);
1984 if (ret != NULL) {
1985 ret->context = file;
1986 ret->writecallback = xmlFileWrite;
1987 ret->closecallback = xmlFileFlush;
1988 }
1989
1990 return(ret);
1991}
1992
1993/**
1994 * xmlParserInputBufferCreateFd:
1995 * @fd: a file descriptor number
1996 * @enc: the charset encoding if known
1997 *
1998 * Create a buffered parser input for the progressive parsing for the input
1999 * from a file descriptor
2000 *
2001 * Returns the new parser input or NULL
2002 */
2003xmlParserInputBufferPtr
2004xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
2005 xmlParserInputBufferPtr ret;
2006
2007 if (fd < 0) return(NULL);
2008
2009 ret = xmlAllocParserInputBuffer(enc);
2010 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002011 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002012 ret->readcallback = xmlFdRead;
2013 ret->closecallback = xmlFdClose;
2014 }
2015
2016 return(ret);
2017}
2018
2019/**
2020 * xmlParserInputBufferCreateMem:
2021 * @mem: the memory input
2022 * @size: the length of the memory block
2023 * @enc: the charset encoding if known
2024 *
2025 * Create a buffered parser input for the progressive parsing for the input
2026 * from a memory area.
2027 *
2028 * Returns the new parser input or NULL
2029 */
2030xmlParserInputBufferPtr
2031xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
2032 xmlParserInputBufferPtr ret;
2033
2034 if (size <= 0) return(NULL);
2035 if (mem == NULL) return(NULL);
2036
2037 ret = xmlAllocParserInputBuffer(enc);
2038 if (ret != NULL) {
2039 ret->context = (void *) mem;
2040 ret->readcallback = (xmlInputReadCallback) xmlNop;
2041 ret->closecallback = NULL;
2042 xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
2043 }
2044
2045 return(ret);
2046}
2047
2048/**
2049 * xmlOutputBufferCreateFd:
2050 * @fd: a file descriptor number
2051 * @encoder: the encoding converter or NULL
2052 *
2053 * Create a buffered output for the progressive saving
2054 * to a file descriptor
2055 *
2056 * Returns the new parser output or NULL
2057 */
2058xmlOutputBufferPtr
2059xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
2060 xmlOutputBufferPtr ret;
2061
2062 if (fd < 0) return(NULL);
2063
2064 ret = xmlAllocOutputBuffer(encoder);
2065 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002066 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002067 ret->writecallback = xmlFdWrite;
Daniel Veillard7db38712002-02-07 16:39:11 +00002068 ret->closecallback = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00002069 }
2070
2071 return(ret);
2072}
2073
2074/**
2075 * xmlParserInputBufferCreateIO:
2076 * @ioread: an I/O read function
2077 * @ioclose: an I/O close function
2078 * @ioctx: an I/O handler
2079 * @enc: the charset encoding if known
2080 *
2081 * Create a buffered parser input for the progressive parsing for the input
2082 * from an I/O handler
2083 *
2084 * Returns the new parser input or NULL
2085 */
2086xmlParserInputBufferPtr
2087xmlParserInputBufferCreateIO(xmlInputReadCallback ioread,
2088 xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) {
2089 xmlParserInputBufferPtr ret;
2090
2091 if (ioread == NULL) return(NULL);
2092
2093 ret = xmlAllocParserInputBuffer(enc);
2094 if (ret != NULL) {
2095 ret->context = (void *) ioctx;
2096 ret->readcallback = ioread;
2097 ret->closecallback = ioclose;
2098 }
2099
2100 return(ret);
2101}
2102
2103/**
2104 * xmlOutputBufferCreateIO:
2105 * @iowrite: an I/O write function
2106 * @ioclose: an I/O close function
2107 * @ioctx: an I/O handler
Daniel Veillard9d06d302002-01-22 18:15:52 +00002108 * @encoder: the charset encoding if known
Owen Taylor3473f882001-02-23 17:55:21 +00002109 *
2110 * Create a buffered output for the progressive saving
2111 * to an I/O handler
2112 *
2113 * Returns the new parser output or NULL
2114 */
2115xmlOutputBufferPtr
2116xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite,
2117 xmlOutputCloseCallback ioclose, void *ioctx,
2118 xmlCharEncodingHandlerPtr encoder) {
2119 xmlOutputBufferPtr ret;
2120
2121 if (iowrite == NULL) return(NULL);
2122
2123 ret = xmlAllocOutputBuffer(encoder);
2124 if (ret != NULL) {
2125 ret->context = (void *) ioctx;
2126 ret->writecallback = iowrite;
2127 ret->closecallback = ioclose;
2128 }
2129
2130 return(ret);
2131}
2132
2133/**
2134 * xmlParserInputBufferPush:
2135 * @in: a buffered parser input
2136 * @len: the size in bytes of the array.
2137 * @buf: an char array
2138 *
2139 * Push the content of the arry in the input buffer
2140 * This routine handle the I18N transcoding to internal UTF-8
2141 * This is used when operating the parser in progressive (push) mode.
2142 *
2143 * Returns the number of chars read and stored in the buffer, or -1
2144 * in case of error.
2145 */
2146int
2147xmlParserInputBufferPush(xmlParserInputBufferPtr in,
2148 int len, const char *buf) {
2149 int nbchars = 0;
2150
2151 if (len < 0) return(0);
2152 if (in->encoder != NULL) {
2153 /*
2154 * Store the data in the incoming raw buffer
2155 */
2156 if (in->raw == NULL) {
2157 in->raw = xmlBufferCreate();
2158 }
2159 xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
2160
2161 /*
2162 * convert as much as possible to the parser reading buffer.
2163 */
2164 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2165 if (nbchars < 0) {
2166 xmlGenericError(xmlGenericErrorContext,
2167 "xmlParserInputBufferPush: encoder error\n");
2168 return(-1);
2169 }
2170 } else {
2171 nbchars = len;
2172 xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
2173 }
2174#ifdef DEBUG_INPUT
2175 xmlGenericError(xmlGenericErrorContext,
2176 "I/O: pushed %d chars, buffer %d/%d\n",
2177 nbchars, in->buffer->use, in->buffer->size);
2178#endif
2179 return(nbchars);
2180}
2181
2182/**
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002183 * endOfInput:
2184 *
2185 * When reading from an Input channel indicated end of file or error
2186 * don't reread from it again.
2187 */
2188static int
2189endOfInput (void * context ATTRIBUTE_UNUSED,
2190 char * buffer ATTRIBUTE_UNUSED,
2191 int len ATTRIBUTE_UNUSED) {
2192 return(0);
2193}
2194
2195/**
Owen Taylor3473f882001-02-23 17:55:21 +00002196 * xmlParserInputBufferGrow:
2197 * @in: a buffered parser input
2198 * @len: indicative value of the amount of chars to read
2199 *
2200 * Grow up the content of the input buffer, the old data are preserved
2201 * This routine handle the I18N transcoding to internal UTF-8
2202 * This routine is used when operating the parser in normal (pull) mode
2203 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002204 * TODO: one should be able to remove one extra copy by copying directly
Owen Taylor3473f882001-02-23 17:55:21 +00002205 * onto in->buffer or in->raw
2206 *
2207 * Returns the number of chars read and stored in the buffer, or -1
2208 * in case of error.
2209 */
2210int
2211xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
2212 char *buffer = NULL;
2213 int res = 0;
2214 int nbchars = 0;
2215 int buffree;
Daniel Veillard9e412302002-06-10 15:59:44 +00002216 unsigned int needSize;
Owen Taylor3473f882001-02-23 17:55:21 +00002217
2218 if ((len <= MINLEN) && (len != 4))
2219 len = MINLEN;
2220 buffree = in->buffer->size - in->buffer->use;
2221 if (buffree <= 0) {
2222 xmlGenericError(xmlGenericErrorContext,
2223 "xmlParserInputBufferGrow : buffer full !\n");
2224 return(0);
2225 }
2226 if (len > buffree)
2227 len = buffree;
2228
Daniel Veillarde5354492002-05-16 08:43:22 +00002229 needSize = in->buffer->use + len + 1;
2230 if (needSize > in->buffer->size){
2231 if (!xmlBufferResize(in->buffer, needSize)){
2232 xmlGenericError(xmlGenericErrorContext,
2233 "xmlBufferAdd : out of memory!\n");
2234 return(0);
2235 }
Owen Taylor3473f882001-02-23 17:55:21 +00002236 }
Daniel Veillarde5354492002-05-16 08:43:22 +00002237 buffer = (char *)&in->buffer->content[in->buffer->use];
Owen Taylor3473f882001-02-23 17:55:21 +00002238
2239 /*
2240 * Call the read method for this I/O type.
2241 */
2242 if (in->readcallback != NULL) {
2243 res = in->readcallback(in->context, &buffer[0], len);
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002244 if (res <= 0)
2245 in->readcallback = endOfInput;
Owen Taylor3473f882001-02-23 17:55:21 +00002246 } else {
2247 xmlGenericError(xmlGenericErrorContext,
2248 "xmlParserInputBufferGrow : no input !\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002249 return(-1);
2250 }
2251 if (res < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00002252 return(-1);
2253 }
2254 len = res;
2255 if (in->encoder != NULL) {
2256 /*
2257 * Store the data in the incoming raw buffer
2258 */
2259 if (in->raw == NULL) {
2260 in->raw = xmlBufferCreate();
2261 }
2262 xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
2263
2264 /*
2265 * convert as much as possible to the parser reading buffer.
2266 */
2267 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2268 if (nbchars < 0) {
2269 xmlGenericError(xmlGenericErrorContext,
2270 "xmlParserInputBufferGrow: encoder error\n");
2271 return(-1);
2272 }
2273 } else {
2274 nbchars = len;
Daniel Veillarde5354492002-05-16 08:43:22 +00002275 in->buffer->use += nbchars;
2276 buffer[nbchars] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002277 }
2278#ifdef DEBUG_INPUT
2279 xmlGenericError(xmlGenericErrorContext,
2280 "I/O: read %d chars, buffer %d/%d\n",
2281 nbchars, in->buffer->use, in->buffer->size);
2282#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002283 return(nbchars);
2284}
2285
2286/**
2287 * xmlParserInputBufferRead:
2288 * @in: a buffered parser input
2289 * @len: indicative value of the amount of chars to read
2290 *
2291 * Refresh the content of the input buffer, the old data are considered
2292 * consumed
2293 * This routine handle the I18N transcoding to internal UTF-8
2294 *
2295 * Returns the number of chars read and stored in the buffer, or -1
2296 * in case of error.
2297 */
2298int
2299xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
2300 /* xmlBufferEmpty(in->buffer); */
2301 if (in->readcallback != NULL)
2302 return(xmlParserInputBufferGrow(in, len));
2303 else
2304 return(-1);
2305}
2306
2307/**
2308 * xmlOutputBufferWrite:
2309 * @out: a buffered parser output
2310 * @len: the size in bytes of the array.
2311 * @buf: an char array
2312 *
2313 * Write the content of the array in the output I/O buffer
2314 * This routine handle the I18N transcoding from internal UTF-8
2315 * The buffer is lossless, i.e. will store in case of partial
2316 * or delayed writes.
2317 *
2318 * Returns the number of chars immediately written, or -1
2319 * in case of error.
2320 */
2321int
2322xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
2323 int nbchars = 0; /* number of chars to output to I/O */
2324 int ret; /* return from function call */
2325 int written = 0; /* number of char written to I/O so far */
2326 int chunk; /* number of byte curreent processed from buf */
2327
2328 if (len < 0) return(0);
2329
2330 do {
2331 chunk = len;
2332 if (chunk > 4 * MINLEN)
2333 chunk = 4 * MINLEN;
2334
2335 /*
2336 * first handle encoding stuff.
2337 */
2338 if (out->encoder != NULL) {
2339 /*
2340 * Store the data in the incoming raw buffer
2341 */
2342 if (out->conv == NULL) {
2343 out->conv = xmlBufferCreate();
2344 }
2345 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2346
2347 if ((out->buffer->use < MINLEN) && (chunk == len))
2348 goto done;
2349
2350 /*
2351 * convert as much as possible to the parser reading buffer.
2352 */
2353 ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2354 if (ret < 0) {
2355 xmlGenericError(xmlGenericErrorContext,
2356 "xmlOutputBufferWrite: encoder error\n");
2357 return(-1);
2358 }
2359 nbchars = out->conv->use;
2360 } else {
2361 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2362 nbchars = out->buffer->use;
2363 }
2364 buf += chunk;
2365 len -= chunk;
2366
2367 if ((nbchars < MINLEN) && (len <= 0))
2368 goto done;
2369
2370 if (out->writecallback) {
2371 /*
2372 * second write the stuff to the I/O channel
2373 */
2374 if (out->encoder != NULL) {
2375 ret = out->writecallback(out->context,
2376 (const char *)out->conv->content, nbchars);
2377 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002378 xmlBufferShrink(out->conv, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002379 } else {
2380 ret = out->writecallback(out->context,
2381 (const char *)out->buffer->content, nbchars);
2382 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002383 xmlBufferShrink(out->buffer, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002384 }
2385 if (ret < 0) {
2386 xmlGenericError(xmlGenericErrorContext,
2387 "I/O: error %d writing %d bytes\n", ret, nbchars);
2388 return(ret);
2389 }
2390 out->written += ret;
2391 }
2392 written += nbchars;
2393 } while (len > 0);
2394
2395done:
2396#ifdef DEBUG_INPUT
2397 xmlGenericError(xmlGenericErrorContext,
2398 "I/O: wrote %d chars\n", written);
2399#endif
2400 return(written);
2401}
2402
2403/**
2404 * xmlOutputBufferWriteString:
2405 * @out: a buffered parser output
2406 * @str: a zero terminated C string
2407 *
2408 * Write the content of the string in the output I/O buffer
2409 * This routine handle the I18N transcoding from internal UTF-8
2410 * The buffer is lossless, i.e. will store in case of partial
2411 * or delayed writes.
2412 *
2413 * Returns the number of chars immediately written, or -1
2414 * in case of error.
2415 */
2416int
2417xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
2418 int len;
2419
2420 if (str == NULL)
2421 return(-1);
2422 len = strlen(str);
2423
2424 if (len > 0)
2425 return(xmlOutputBufferWrite(out, len, str));
2426 return(len);
2427}
2428
2429/**
2430 * xmlOutputBufferFlush:
2431 * @out: a buffered output
2432 *
2433 * flushes the output I/O channel
2434 *
2435 * Returns the number of byte written or -1 in case of error.
2436 */
2437int
2438xmlOutputBufferFlush(xmlOutputBufferPtr out) {
2439 int nbchars = 0, ret = 0;
2440
2441 /*
2442 * first handle encoding stuff.
2443 */
2444 if ((out->conv != NULL) && (out->encoder != NULL)) {
2445 /*
2446 * convert as much as possible to the parser reading buffer.
2447 */
2448 nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2449 if (nbchars < 0) {
2450 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002451 "xmlOutputBufferFlush: encoder error\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002452 return(-1);
2453 }
2454 }
2455
2456 /*
2457 * second flush the stuff to the I/O channel
2458 */
2459 if ((out->conv != NULL) && (out->encoder != NULL) &&
2460 (out->writecallback != NULL)) {
2461 ret = out->writecallback(out->context,
2462 (const char *)out->conv->content, out->conv->use);
2463 if (ret >= 0)
2464 xmlBufferShrink(out->conv, ret);
2465 } else if (out->writecallback != NULL) {
2466 ret = out->writecallback(out->context,
2467 (const char *)out->buffer->content, out->buffer->use);
2468 if (ret >= 0)
2469 xmlBufferShrink(out->buffer, ret);
2470 }
2471 if (ret < 0) {
2472 xmlGenericError(xmlGenericErrorContext,
2473 "I/O: error %d flushing %d bytes\n", ret, nbchars);
2474 return(ret);
2475 }
2476 out->written += ret;
2477
2478#ifdef DEBUG_INPUT
2479 xmlGenericError(xmlGenericErrorContext,
2480 "I/O: flushed %d chars\n", ret);
2481#endif
2482 return(ret);
2483}
2484
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002485/**
Owen Taylor3473f882001-02-23 17:55:21 +00002486 * xmlParserGetDirectory:
2487 * @filename: the path to a file
2488 *
2489 * lookup the directory for that file
2490 *
2491 * Returns a new allocated string containing the directory, or NULL.
2492 */
2493char *
2494xmlParserGetDirectory(const char *filename) {
2495 char *ret = NULL;
2496 char dir[1024];
2497 char *cur;
2498 char sep = '/';
2499
Igor Zlatkovic9181cc02002-09-29 17:51:06 +00002500#ifdef _WIN32_WCE /* easy way by now ... wince does not have dirs! */
2501 return NULL;
2502#endif
2503
Owen Taylor3473f882001-02-23 17:55:21 +00002504 if (xmlInputCallbackInitialized == 0)
2505 xmlRegisterDefaultInputCallbacks();
2506
2507 if (filename == NULL) return(NULL);
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002508#if defined(WIN32) && !defined(__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +00002509 sep = '\\';
2510#endif
2511
2512 strncpy(dir, filename, 1023);
2513 dir[1023] = 0;
2514 cur = &dir[strlen(dir)];
2515 while (cur > dir) {
2516 if (*cur == sep) break;
2517 cur --;
2518 }
2519 if (*cur == sep) {
2520 if (cur == dir) dir[1] = 0;
2521 else *cur = 0;
2522 ret = xmlMemStrdup(dir);
2523 } else {
2524 if (getcwd(dir, 1024) != NULL) {
2525 dir[1023] = 0;
2526 ret = xmlMemStrdup(dir);
2527 }
2528 }
2529 return(ret);
2530}
2531
2532/****************************************************************
2533 * *
2534 * External entities loading *
2535 * *
2536 ****************************************************************/
2537
Daniel Veillard561b7f82002-03-20 21:55:57 +00002538#ifdef LIBXML_CATALOG_ENABLED
2539static int xmlSysIDExists(const char *URL) {
Daniel Veillard6990bf32001-08-23 21:17:48 +00002540#ifdef HAVE_STAT
2541 int ret;
2542 struct stat info;
2543 const char *path;
2544
2545 if (URL == NULL)
2546 return(0);
2547
Daniel Veillardf4862f02002-09-10 11:13:43 +00002548 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2549#if defined (_WIN32) && !defined(__CYGWIN__)
2550 path = &URL[17];
2551#else
Daniel Veillard6990bf32001-08-23 21:17:48 +00002552 path = &URL[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +00002553#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002554 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002555#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard6990bf32001-08-23 21:17:48 +00002556 path = &URL[8];
2557#else
2558 path = &URL[7];
2559#endif
2560 } else
2561 path = URL;
2562 ret = stat(path, &info);
Daniel Veillard561b7f82002-03-20 21:55:57 +00002563 if (ret == 0)
2564 return(1);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002565#endif
Daniel Veillard561b7f82002-03-20 21:55:57 +00002566 return(0);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002567}
Daniel Veillard561b7f82002-03-20 21:55:57 +00002568#endif
Daniel Veillard6990bf32001-08-23 21:17:48 +00002569
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002570/**
Owen Taylor3473f882001-02-23 17:55:21 +00002571 * xmlDefaultExternalEntityLoader:
2572 * @URL: the URL for the entity to load
2573 * @ID: the System ID for the entity to load
2574 * @ctxt: the context in which the entity is called or NULL
2575 *
2576 * By default we don't load external entitites, yet.
2577 *
2578 * Returns a new allocated xmlParserInputPtr, or NULL.
2579 */
2580static
2581xmlParserInputPtr
2582xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
2583 xmlParserCtxtPtr ctxt) {
2584 xmlParserInputPtr ret = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002585 xmlChar *resource = NULL;
2586#ifdef LIBXML_CATALOG_ENABLED
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002587 xmlCatalogAllow pref;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002588#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002589
2590#ifdef DEBUG_EXTERNAL_ENTITIES
2591 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00002592 "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +00002593#endif
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002594#ifdef LIBXML_CATALOG_ENABLED
2595 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002596 * If the resource doesn't exists as a file,
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002597 * try to load it from the resource pointed in the catalogs
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002598 */
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002599 pref = xmlCatalogGetDefaults();
2600
Daniel Veillard561b7f82002-03-20 21:55:57 +00002601 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002602 /*
2603 * Do a local lookup
2604 */
2605 if ((ctxt->catalogs != NULL) &&
2606 ((pref == XML_CATA_ALLOW_ALL) ||
2607 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2608 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2609 (const xmlChar *)ID,
2610 (const xmlChar *)URL);
2611 }
2612 /*
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002613 * Try a global lookup
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002614 */
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002615 if ((resource == NULL) &&
2616 ((pref == XML_CATA_ALLOW_ALL) ||
2617 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002618 resource = xmlCatalogResolve((const xmlChar *)ID,
2619 (const xmlChar *)URL);
2620 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002621 if ((resource == NULL) && (URL != NULL))
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002622 resource = xmlStrdup((const xmlChar *) URL);
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002623
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002624 /*
2625 * TODO: do an URI lookup on the reference
2626 */
Daniel Veillard561b7f82002-03-20 21:55:57 +00002627 if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002628 xmlChar *tmp = NULL;
2629
2630 if ((ctxt->catalogs != NULL) &&
2631 ((pref == XML_CATA_ALLOW_ALL) ||
2632 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2633 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2634 }
2635 if ((tmp == NULL) &&
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002636 ((pref == XML_CATA_ALLOW_ALL) ||
2637 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002638 tmp = xmlCatalogResolveURI(resource);
2639 }
2640
2641 if (tmp != NULL) {
2642 xmlFree(resource);
2643 resource = tmp;
2644 }
2645 }
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002646 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002647#endif
2648
2649 if (resource == NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002650 resource = (xmlChar *) URL;
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002651
2652 if (resource == NULL) {
Daniel Veillardc6613042002-03-02 09:34:02 +00002653 if (ID == NULL)
2654 ID = "NULL";
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002655 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2656 (ctxt->sax->error != NULL))
2657 ctxt->sax->error(ctxt,
2658 "failed to load external entity \"%s\"\n", ID);
2659 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002660 ctxt->sax->warning(ctxt,
2661 "failed to load external entity \"%s\"\n", ID);
2662 return(NULL);
2663 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002664 ret = xmlNewInputFromFile(ctxt, (const char *)resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002665 if (ret == NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002666 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2667 (ctxt->sax->error != NULL))
2668 ctxt->sax->error(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002669 "failed to load external entity \"%s\"\n", resource);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002670 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002671 ctxt->sax->warning(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002672 "failed to load external entity \"%s\"\n", resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002673 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002674 if ((resource != NULL) && (resource != (xmlChar *) URL))
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002675 xmlFree(resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002676 return(ret);
2677}
2678
2679static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
2680 xmlDefaultExternalEntityLoader;
2681
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002682/**
Owen Taylor3473f882001-02-23 17:55:21 +00002683 * xmlSetExternalEntityLoader:
2684 * @f: the new entity resolver function
2685 *
2686 * Changes the defaultexternal entity resolver function for the application
2687 */
2688void
2689xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
2690 xmlCurrentExternalEntityLoader = f;
2691}
2692
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002693/**
Owen Taylor3473f882001-02-23 17:55:21 +00002694 * xmlGetExternalEntityLoader:
2695 *
2696 * Get the default external entity resolver function for the application
2697 *
2698 * Returns the xmlExternalEntityLoader function pointer
2699 */
2700xmlExternalEntityLoader
2701xmlGetExternalEntityLoader(void) {
2702 return(xmlCurrentExternalEntityLoader);
2703}
2704
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002705/**
Owen Taylor3473f882001-02-23 17:55:21 +00002706 * xmlLoadExternalEntity:
2707 * @URL: the URL for the entity to load
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002708 * @ID: the Public ID for the entity to load
Owen Taylor3473f882001-02-23 17:55:21 +00002709 * @ctxt: the context in which the entity is called or NULL
2710 *
2711 * Load an external entity, note that the use of this function for
2712 * unparsed entities may generate problems
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002713 * TODO: a more generic External entity API must be designed
Owen Taylor3473f882001-02-23 17:55:21 +00002714 *
2715 * Returns the xmlParserInputPtr or NULL
2716 */
2717xmlParserInputPtr
2718xmlLoadExternalEntity(const char *URL, const char *ID,
2719 xmlParserCtxtPtr ctxt) {
2720 return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
2721}
2722
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002723/************************************************************************
2724 * *
2725 * Disabling Network access *
2726 * *
2727 ************************************************************************/
2728
2729#ifdef LIBXML_CATALOG_ENABLED
2730static int
2731xmlNoNetExists(const char *URL)
2732{
2733#ifdef HAVE_STAT
2734 int ret;
2735 struct stat info;
2736 const char *path;
2737
2738 if (URL == NULL)
2739 return (0);
2740
Daniel Veillardf4862f02002-09-10 11:13:43 +00002741 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2742#if defined (_WIN32) && !defined(__CYGWIN__)
2743 path = &URL[17];
2744#else
2745 path = &URL[16];
2746#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002747 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002748#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002749 path = &URL[8];
2750#else
2751 path = &URL[7];
2752#endif
2753 } else
2754 path = URL;
2755 ret = stat(path, &info);
2756 if (ret == 0)
2757 return (1);
2758#endif
2759 return (0);
2760}
2761#endif
2762
2763/**
2764 * xmlNoNetExternalEntityLoader:
2765 * @URL: the URL for the entity to load
2766 * @ID: the System ID for the entity to load
2767 * @ctxt: the context in which the entity is called or NULL
2768 *
2769 * A specific entity loader disabling network accesses, though still
2770 * allowing local catalog accesses for resolution.
2771 *
2772 * Returns a new allocated xmlParserInputPtr, or NULL.
2773 */
2774xmlParserInputPtr
2775xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
2776 xmlParserCtxtPtr ctxt) {
2777 xmlParserInputPtr input = NULL;
2778 xmlChar *resource = NULL;
2779
2780#ifdef LIBXML_CATALOG_ENABLED
2781 xmlCatalogAllow pref;
2782
2783 /*
2784 * If the resource doesn't exists as a file,
2785 * try to load it from the resource pointed in the catalogs
2786 */
2787 pref = xmlCatalogGetDefaults();
2788
2789 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
2790 /*
2791 * Do a local lookup
2792 */
2793 if ((ctxt->catalogs != NULL) &&
2794 ((pref == XML_CATA_ALLOW_ALL) ||
2795 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2796 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2797 (const xmlChar *)ID,
2798 (const xmlChar *)URL);
2799 }
2800 /*
2801 * Try a global lookup
2802 */
2803 if ((resource == NULL) &&
2804 ((pref == XML_CATA_ALLOW_ALL) ||
2805 (pref == XML_CATA_ALLOW_GLOBAL))) {
2806 resource = xmlCatalogResolve((const xmlChar *)ID,
2807 (const xmlChar *)URL);
2808 }
2809 if ((resource == NULL) && (URL != NULL))
2810 resource = xmlStrdup((const xmlChar *) URL);
2811
2812 /*
2813 * TODO: do an URI lookup on the reference
2814 */
2815 if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
2816 xmlChar *tmp = NULL;
2817
2818 if ((ctxt->catalogs != NULL) &&
2819 ((pref == XML_CATA_ALLOW_ALL) ||
2820 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2821 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2822 }
2823 if ((tmp == NULL) &&
2824 ((pref == XML_CATA_ALLOW_ALL) ||
2825 (pref == XML_CATA_ALLOW_GLOBAL))) {
2826 tmp = xmlCatalogResolveURI(resource);
2827 }
2828
2829 if (tmp != NULL) {
2830 xmlFree(resource);
2831 resource = tmp;
2832 }
2833 }
2834 }
2835#endif
2836 if (resource == NULL)
2837 resource = (xmlChar *) URL;
2838
2839 if (resource != NULL) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002840 if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
2841 (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002842 xmlGenericError(xmlGenericErrorContext,
2843 "Attempt to load network entity %s \n", resource);
2844
2845 if (resource != (xmlChar *) URL)
2846 xmlFree(resource);
2847 return(NULL);
2848 }
2849 }
2850 input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
2851 if (resource != (xmlChar *) URL)
2852 xmlFree(resource);
2853 return(input);
2854}
2855