blob: 0b8927a2fec9609ee04f05f2bc08af33a496e064 [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>
15#include <errno.h>
16
17#ifdef HAVE_SYS_TYPES_H
18#include <sys/types.h>
19#endif
20#ifdef HAVE_SYS_STAT_H
21#include <sys/stat.h>
22#endif
23#ifdef HAVE_FCNTL_H
24#include <fcntl.h>
25#endif
26#ifdef HAVE_UNISTD_H
27#include <unistd.h>
28#endif
29#ifdef HAVE_STDLIB_H
30#include <stdlib.h>
31#endif
32#ifdef HAVE_ZLIB_H
33#include <zlib.h>
34#endif
35
36/* Figure a portable way to know if a file is a directory. */
37#ifndef HAVE_STAT
38# ifdef HAVE__STAT
Daniel Veillard50f34372001-08-03 12:06:36 +000039 /* MS C library seems to define stat and _stat. The definition
40 is identical. Still, mapping them to each other causes a warning. */
41# ifndef _MSC_VER
42# define stat(x,y) _stat(x,y)
43# endif
Owen Taylor3473f882001-02-23 17:55:21 +000044# define HAVE_STAT
45# endif
46#endif
47#ifdef HAVE_STAT
48# ifndef S_ISDIR
49# ifdef _S_ISDIR
50# define S_ISDIR(x) _S_ISDIR(x)
51# else
52# ifdef S_IFDIR
53# ifndef S_IFMT
54# ifdef _S_IFMT
55# define S_IFMT _S_IFMT
56# endif
57# endif
58# ifdef S_IFMT
59# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
60# endif
61# endif
62# endif
63# endif
64#endif
65
66#include <libxml/xmlmemory.h>
67#include <libxml/parser.h>
68#include <libxml/parserInternals.h>
69#include <libxml/xmlIO.h>
Daniel Veillard388236f2001-07-08 18:35:48 +000070#include <libxml/uri.h>
Owen Taylor3473f882001-02-23 17:55:21 +000071#include <libxml/nanohttp.h>
72#include <libxml/nanoftp.h>
73#include <libxml/xmlerror.h>
Daniel Veillard7d6fd212001-05-10 15:34:11 +000074#ifdef LIBXML_CATALOG_ENABLED
75#include <libxml/catalog.h>
76#endif
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000077#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000078
79#ifdef VMS
80#define xmlRegisterDefaultInputCallbacks xmlRegisterDefInputCallbacks
81#define xmlRegisterDefaultOutputCallbacks xmlRegisterDefOutputCallbacks
82#endif
83
Daniel Veillardf012a642001-07-23 19:10:52 +000084/* #define VERBOSE_FAILURE */
Daniel Veillard1fd36d22001-07-04 22:54:28 +000085/* #define DEBUG_EXTERNAL_ENTITIES */
Owen Taylor3473f882001-02-23 17:55:21 +000086/* #define DEBUG_INPUT */
87
88#ifdef DEBUG_INPUT
89#define MINLEN 40
90#else
91#define MINLEN 4000
92#endif
93
94/*
95 * Input I/O callback sets
96 */
97typedef struct _xmlInputCallback {
98 xmlInputMatchCallback matchcallback;
99 xmlInputOpenCallback opencallback;
100 xmlInputReadCallback readcallback;
101 xmlInputCloseCallback closecallback;
102} xmlInputCallback;
103
104#define MAX_INPUT_CALLBACK 15
105
Daniel Veillard22090732001-07-16 00:06:07 +0000106static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
107static int xmlInputCallbackNr = 0;
108static int xmlInputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000109
110/*
111 * Output I/O callback sets
112 */
113typedef struct _xmlOutputCallback {
114 xmlOutputMatchCallback matchcallback;
115 xmlOutputOpenCallback opencallback;
116 xmlOutputWriteCallback writecallback;
117 xmlOutputCloseCallback closecallback;
118} xmlOutputCallback;
119
120#define MAX_OUTPUT_CALLBACK 15
121
Daniel Veillard22090732001-07-16 00:06:07 +0000122static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
123static int xmlOutputCallbackNr = 0;
124static int xmlOutputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000125
Daniel Veillardf4862f02002-09-10 11:13:43 +0000126/************************************************************************
127 * *
128 * Handling of Windows file paths *
129 * *
130 ************************************************************************/
131
132#define IS_WINDOWS_PATH(p) \
133 ((p != NULL) && \
134 (((p[0] >= 'a') && (p[0] <= 'z')) || \
135 ((p[0] >= 'A') && (p[0] <= 'Z'))) && \
136 (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\')))
137
138
139/**
140 * xmlNormalizeWindowsPath
141 * @path: a windows path like "C:/foo/bar"
142 *
143 * Normalize a Windows path to make an URL from it
144 *
145 * Returns a new URI which must be freed by the caller or NULL
146 * in case of error
147 */
148xmlChar *
149xmlNormalizeWindowsPath(const xmlChar *path)
150{
151 int len, i, j;
152 xmlChar *ret;
153
154 if (path == NULL)
155 return(NULL);
156 if (!IS_WINDOWS_PATH(path))
157 return(xmlStrdup(path));
158
159 len = xmlStrlen(path);
Daniel Veillard607b35c2002-09-10 12:16:19 +0000160 ret = xmlMalloc(len + 10);
Daniel Veillardf4862f02002-09-10 11:13:43 +0000161 if (ret == NULL)
162 return(NULL);
163
164 ret[0] = 'f';
165 ret[1] = 'i';
166 ret[2] = 'l';
167 ret[3] = 'e';
168 ret[4] = ':';
169 ret[5] = '/';
170 ret[6] = '/';
171 ret[7] = '/';
172 for (i = 0,j = 8;i < len;i++,j++) {
173 /* TODO: UTF8 conversion + URI escaping ??? */
174 if (path[i] == '\\')
175 ret[j] = '/';
176 else
177 ret[j] = path[i];
178 }
179 ret[j] = 0;
180 return(ret);
181}
182
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000183/**
184 * xmlCleanupInputCallbacks:
185 *
186 * clears the entire input callback table. this includes the
187 * compiled-in I/O.
188 */
189void
190xmlCleanupInputCallbacks(void)
191{
192 int i;
193
194 if (!xmlInputCallbackInitialized)
195 return;
196
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000197 for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000198 xmlInputCallbackTable[i].matchcallback = NULL;
199 xmlInputCallbackTable[i].opencallback = NULL;
200 xmlInputCallbackTable[i].readcallback = NULL;
201 xmlInputCallbackTable[i].closecallback = NULL;
202 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000203 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000204
205 xmlInputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000206 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000207}
208
209/**
210 * xmlCleanupOutputCallbacks:
211 *
212 * clears the entire output callback table. this includes the
213 * compiled-in I/O callbacks.
214 */
215void
216xmlCleanupOutputCallbacks(void)
217{
218 int i;
219
220 if (!xmlOutputCallbackInitialized)
221 return;
222
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000223 for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000224 xmlOutputCallbackTable[i].matchcallback = NULL;
225 xmlOutputCallbackTable[i].opencallback = NULL;
226 xmlOutputCallbackTable[i].writecallback = NULL;
227 xmlOutputCallbackTable[i].closecallback = NULL;
228 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000229 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000230
231 xmlOutputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000232 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000233}
234
Owen Taylor3473f882001-02-23 17:55:21 +0000235/************************************************************************
236 * *
237 * Standard I/O for file accesses *
238 * *
239 ************************************************************************/
240
241/**
242 * xmlCheckFilename
243 * @path: the path to check
244 *
245 * function checks to see if @path is a valid source
246 * (file, socket...) for XML.
247 *
248 * if stat is not available on the target machine,
249 * returns 1. if stat fails, returns 0 (if calling
250 * stat on the filename fails, it can't be right).
251 * if stat succeeds and the file is a directory,
252 * sets errno to EISDIR and returns 0. otherwise
253 * returns 1.
254 */
255
256static int
257xmlCheckFilename (const char *path)
258{
259#ifdef HAVE_STAT
260#ifdef S_ISDIR
261 struct stat stat_buffer;
262
263 if (stat(path, &stat_buffer) == -1)
264 return 0;
265
266 if (S_ISDIR(stat_buffer.st_mode)) {
267 errno = EISDIR;
268 return 0;
269 }
270
271#endif
272#endif
273 return 1;
274}
275
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000276static int
Owen Taylor3473f882001-02-23 17:55:21 +0000277xmlNop(void) {
278 return(0);
279}
280
281/**
Owen Taylor3473f882001-02-23 17:55:21 +0000282 * xmlFdRead:
283 * @context: the I/O context
284 * @buffer: where to drop data
285 * @len: number of bytes to read
286 *
287 * Read @len bytes to @buffer from the I/O channel.
288 *
289 * Returns the number of bytes written
290 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000291static int
Owen Taylor3473f882001-02-23 17:55:21 +0000292xmlFdRead (void * context, char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000293 return(read((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000294}
295
296/**
297 * xmlFdWrite:
298 * @context: the I/O context
299 * @buffer: where to get data
300 * @len: number of bytes to write
301 *
302 * Write @len bytes from @buffer to the I/O channel.
303 *
304 * Returns the number of bytes written
305 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000306static int
Owen Taylor3473f882001-02-23 17:55:21 +0000307xmlFdWrite (void * context, const char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000308 return(write((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000309}
310
311/**
312 * xmlFdClose:
313 * @context: the I/O context
314 *
315 * Close an I/O channel
Daniel Veillardf012a642001-07-23 19:10:52 +0000316 *
317 * Returns 0 in case of success and error code otherwise
Owen Taylor3473f882001-02-23 17:55:21 +0000318 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000319static int
Owen Taylor3473f882001-02-23 17:55:21 +0000320xmlFdClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000321 return ( close((int) (long) context) );
Owen Taylor3473f882001-02-23 17:55:21 +0000322}
323
324/**
325 * xmlFileMatch:
326 * @filename: the URI for matching
327 *
328 * input from FILE *
329 *
330 * Returns 1 if matches, 0 otherwise
331 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000332int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000333xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000334 return(1);
335}
336
337/**
338 * xmlFileOpen:
339 * @filename: the URI for matching
340 *
341 * input from FILE *, supports compressed input
342 * if @filename is " " then the standard input is used
343 *
344 * Returns an I/O context or NULL in case of error
345 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000346void *
Owen Taylor3473f882001-02-23 17:55:21 +0000347xmlFileOpen (const char *filename) {
348 const char *path = NULL;
349 FILE *fd;
350
351 if (!strcmp(filename, "-")) {
352 fd = stdin;
353 return((void *) fd);
354 }
355
Daniel Veillardf4862f02002-09-10 11:13:43 +0000356 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000357#if defined (_WIN32) && !defined(__CYGWIN__)
358 path = &filename[17];
359#else
Owen Taylor3473f882001-02-23 17:55:21 +0000360 path = &filename[16];
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000361#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000362 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000363#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000364 path = &filename[8];
365#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000366 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000367#endif
368 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000369 path = filename;
370
371 if (path == NULL)
372 return(NULL);
373 if (!xmlCheckFilename(path))
374 return(NULL);
375
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000376#if defined(WIN32) || defined (__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +0000377 fd = fopen(path, "rb");
378#else
379 fd = fopen(path, "r");
380#endif /* WIN32 */
381 return((void *) fd);
382}
383
384/**
385 * xmlFileOpenW:
386 * @filename: the URI for matching
387 *
388 * output to from FILE *,
389 * if @filename is "-" then the standard output is used
390 *
391 * Returns an I/O context or NULL in case of error
392 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000393static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000394xmlFileOpenW (const char *filename) {
395 const char *path = NULL;
396 FILE *fd;
397
398 if (!strcmp(filename, "-")) {
399 fd = stdout;
400 return((void *) fd);
401 }
402
Daniel Veillardf4862f02002-09-10 11:13:43 +0000403 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
404#if defined (_WIN32) && !defined(__CYGWIN__)
405 path = &filename[17];
406#else
Owen Taylor3473f882001-02-23 17:55:21 +0000407 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000408#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000409 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000410#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000411 path = &filename[8];
412#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000413 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000414#endif
415 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000416 path = filename;
417
418 if (path == NULL)
419 return(NULL);
420
421 fd = fopen(path, "w");
422 return((void *) fd);
423}
424
425/**
426 * xmlFileRead:
427 * @context: the I/O context
428 * @buffer: where to drop data
429 * @len: number of bytes to write
430 *
431 * Read @len bytes to @buffer from the I/O channel.
432 *
433 * Returns the number of bytes written
434 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000435int
Owen Taylor3473f882001-02-23 17:55:21 +0000436xmlFileRead (void * context, char * buffer, int len) {
437 return(fread(&buffer[0], 1, len, (FILE *) context));
438}
439
440/**
441 * xmlFileWrite:
442 * @context: the I/O context
443 * @buffer: where to drop data
444 * @len: number of bytes to write
445 *
446 * Write @len bytes from @buffer to the I/O channel.
447 *
448 * Returns the number of bytes written
449 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000450static int
Owen Taylor3473f882001-02-23 17:55:21 +0000451xmlFileWrite (void * context, const char * buffer, int len) {
452 return(fwrite(&buffer[0], 1, len, (FILE *) context));
453}
454
455/**
456 * xmlFileClose:
457 * @context: the I/O context
458 *
459 * Close an I/O channel
460 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000461int
Owen Taylor3473f882001-02-23 17:55:21 +0000462xmlFileClose (void * context) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000463 FILE *fil;
464
465 fil = (FILE *) context;
466 if (fil == stdin)
467 return(0);
468 if (fil == stdout)
469 return(0);
Daniel Veillardcd337f02001-11-22 18:20:37 +0000470 if (fil == stderr)
471 return(0);
Daniel Veillardf012a642001-07-23 19:10:52 +0000472 return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000473}
474
475/**
476 * xmlFileFlush:
477 * @context: the I/O context
478 *
479 * Flush an I/O channel
480 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000481static int
Owen Taylor3473f882001-02-23 17:55:21 +0000482xmlFileFlush (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000483 return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000484}
485
486#ifdef HAVE_ZLIB_H
487/************************************************************************
488 * *
489 * I/O for compressed file accesses *
490 * *
491 ************************************************************************/
492/**
493 * xmlGzfileMatch:
494 * @filename: the URI for matching
495 *
496 * input from compressed file test
497 *
498 * Returns 1 if matches, 0 otherwise
499 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000500static int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000501xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000502 return(1);
503}
504
505/**
506 * xmlGzfileOpen:
507 * @filename: the URI for matching
508 *
509 * input from compressed file open
510 * if @filename is " " then the standard input is used
511 *
512 * Returns an I/O context or NULL in case of error
513 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000514static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000515xmlGzfileOpen (const char *filename) {
516 const char *path = NULL;
517 gzFile fd;
518
519 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000520 fd = gzdopen(dup(0), "rb");
Owen Taylor3473f882001-02-23 17:55:21 +0000521 return((void *) fd);
522 }
523
Daniel Veillardf4862f02002-09-10 11:13:43 +0000524 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
525#if defined (_WIN32) && !defined(__CYGWIN__)
526 path = &filename[17];
527#else
Owen Taylor3473f882001-02-23 17:55:21 +0000528 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000529#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000530 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000531#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000532 path = &filename[8];
533#else
Owen Taylor3473f882001-02-23 17:55:21 +0000534 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000535#endif
536 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000537 path = filename;
538
539 if (path == NULL)
540 return(NULL);
541 if (!xmlCheckFilename(path))
542 return(NULL);
543
544 fd = gzopen(path, "rb");
545 return((void *) fd);
546}
547
548/**
549 * xmlGzfileOpenW:
550 * @filename: the URI for matching
551 * @compression: the compression factor (0 - 9 included)
552 *
553 * input from compressed file open
554 * if @filename is " " then the standard input is used
555 *
556 * Returns an I/O context or NULL in case of error
557 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000558static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000559xmlGzfileOpenW (const char *filename, int compression) {
560 const char *path = NULL;
561 char mode[15];
562 gzFile fd;
563
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000564 snprintf(mode, sizeof(mode), "wb%d", compression);
Owen Taylor3473f882001-02-23 17:55:21 +0000565 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000566 fd = gzdopen(dup(1), mode);
Owen Taylor3473f882001-02-23 17:55:21 +0000567 return((void *) fd);
568 }
569
Daniel Veillardf4862f02002-09-10 11:13:43 +0000570 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
571#if defined (_WIN32) && !defined(__CYGWIN__)
572 path = &filename[17];
573#else
Owen Taylor3473f882001-02-23 17:55:21 +0000574 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000575#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000576 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000577#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000578 path = &filename[8];
579#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000580 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000581#endif
582 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000583 path = filename;
584
585 if (path == NULL)
586 return(NULL);
587
588 fd = gzopen(path, mode);
589 return((void *) fd);
590}
591
592/**
593 * xmlGzfileRead:
594 * @context: the I/O context
595 * @buffer: where to drop data
596 * @len: number of bytes to write
597 *
598 * Read @len bytes to @buffer from the compressed I/O channel.
599 *
600 * Returns the number of bytes written
601 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000602static int
Owen Taylor3473f882001-02-23 17:55:21 +0000603xmlGzfileRead (void * context, char * buffer, int len) {
604 return(gzread((gzFile) context, &buffer[0], len));
605}
606
607/**
608 * xmlGzfileWrite:
609 * @context: the I/O context
610 * @buffer: where to drop data
611 * @len: number of bytes to write
612 *
613 * Write @len bytes from @buffer to the compressed I/O channel.
614 *
615 * Returns the number of bytes written
616 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000617static int
Owen Taylor3473f882001-02-23 17:55:21 +0000618xmlGzfileWrite (void * context, const char * buffer, int len) {
619 return(gzwrite((gzFile) context, (char *) &buffer[0], len));
620}
621
622/**
623 * xmlGzfileClose:
624 * @context: the I/O context
625 *
626 * Close a compressed I/O channel
627 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000628static int
Owen Taylor3473f882001-02-23 17:55:21 +0000629xmlGzfileClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000630 return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
Owen Taylor3473f882001-02-23 17:55:21 +0000631}
632#endif /* HAVE_ZLIB_H */
633
634#ifdef LIBXML_HTTP_ENABLED
635/************************************************************************
636 * *
637 * I/O for HTTP file accesses *
638 * *
639 ************************************************************************/
Daniel Veillardf012a642001-07-23 19:10:52 +0000640
641typedef struct xmlIOHTTPWriteCtxt_
642{
643 int compression;
644
645 char * uri;
646
647 void * doc_buff;
648
649} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
650
651#ifdef HAVE_ZLIB_H
652
653#define DFLT_WBITS ( -15 )
654#define DFLT_MEM_LVL ( 8 )
655#define GZ_MAGIC1 ( 0x1f )
656#define GZ_MAGIC2 ( 0x8b )
657#define LXML_ZLIB_OS_CODE ( 0x03 )
658#define INIT_HTTP_BUFF_SIZE ( 32768 )
659#define DFLT_ZLIB_RATIO ( 5 )
660
661/*
662** Data structure and functions to work with sending compressed data
663** via HTTP.
664*/
665
666typedef struct xmlZMemBuff_
667{
668 unsigned long size;
669 unsigned long crc;
670
671 unsigned char * zbuff;
672 z_stream zctrl;
673
674} xmlZMemBuff, *xmlZMemBuffPtr;
675
676/**
677 * append_reverse_ulong
678 * @buff: Compressed memory buffer
679 * @data: Unsigned long to append
680 *
681 * Append a unsigned long in reverse byte order to the end of the
682 * memory buffer.
683 */
684static void
685append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
686
687 int idx;
688
689 if ( buff == NULL )
690 return;
691
692 /*
693 ** This is plagiarized from putLong in gzio.c (zlib source) where
694 ** the number "4" is hardcoded. If zlib is ever patched to
695 ** support 64 bit file sizes, this code would need to be patched
696 ** as well.
697 */
698
699 for ( idx = 0; idx < 4; idx++ ) {
700 *buff->zctrl.next_out = ( data & 0xff );
701 data >>= 8;
702 buff->zctrl.next_out++;
703 }
704
705 return;
706}
707
708/**
709 *
710 * xmlFreeZMemBuff
711 * @buff: The memory buffer context to clear
712 *
713 * Release all the resources associated with the compressed memory buffer.
714 */
715static void
716xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
717
718 int z_err;
719
720 if ( buff == NULL )
721 return;
722
723 xmlFree( buff->zbuff );
724 z_err = deflateEnd( &buff->zctrl );
725#ifdef DEBUG_HTTP
726 if ( z_err != Z_OK )
727 xmlGenericError( xmlGenericErrorContext,
728 "xmlFreeZMemBuff: Error releasing zlib context: %d\n",
729 z_err );
730#endif
731
732 xmlFree( buff );
733 return;
734}
735
736/**
737 * xmlCreateZMemBuff
738 *@compression: Compression value to use
739 *
740 * Create a memory buffer to hold the compressed XML document. The
741 * compressed document in memory will end up being identical to what
742 * would be created if gzopen/gzwrite/gzclose were being used to
743 * write the document to disk. The code for the header/trailer data to
744 * the compression is plagiarized from the zlib source files.
745 */
746static void *
747xmlCreateZMemBuff( int compression ) {
748
749 int z_err;
750 int hdr_lgth;
751 xmlZMemBuffPtr buff = NULL;
752
753 if ( ( compression < 1 ) || ( compression > 9 ) )
754 return ( NULL );
755
756 /* Create the control and data areas */
757
758 buff = xmlMalloc( sizeof( xmlZMemBuff ) );
759 if ( buff == NULL ) {
760 xmlGenericError( xmlGenericErrorContext,
761 "xmlCreateZMemBuff: %s\n",
762 "Failure allocating buffer context." );
763 return ( NULL );
764 }
765
766 (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
767 buff->size = INIT_HTTP_BUFF_SIZE;
768 buff->zbuff = xmlMalloc( buff->size );
769 if ( buff->zbuff == NULL ) {
770 xmlFreeZMemBuff( buff );
771 xmlGenericError( xmlGenericErrorContext,
772 "xmlCreateZMemBuff: %s\n",
773 "Failure allocating data buffer." );
774 return ( NULL );
775 }
776
777 z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
778 DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
779 if ( z_err != Z_OK ) {
780 xmlFreeZMemBuff( buff );
781 buff = NULL;
782 xmlGenericError( xmlGenericErrorContext,
783 "xmlCreateZMemBuff: %s %d\n",
784 "Error initializing compression context. ZLIB error:",
785 z_err );
786 return ( NULL );
787 }
788
789 /* Set the header data. The CRC will be needed for the trailer */
Daniel Veillardf012a642001-07-23 19:10:52 +0000790 buff->crc = crc32( 0L, Z_NULL, 0 );
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000791 hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
792 "%c%c%c%c%c%c%c%c%c%c",
Daniel Veillardf012a642001-07-23 19:10:52 +0000793 GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
794 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
795 buff->zctrl.next_out = buff->zbuff + hdr_lgth;
796 buff->zctrl.avail_out = buff->size - hdr_lgth;
797
798 return ( buff );
799}
800
801/**
802 * xmlZMemBuffExtend
803 * @buff: Buffer used to compress and consolidate data.
804 * @ext_amt: Number of bytes to extend the buffer.
805 *
806 * Extend the internal buffer used to store the compressed data by the
807 * specified amount.
808 *
809 * Returns 0 on success or -1 on failure to extend the buffer. On failure
810 * the original buffer still exists at the original size.
811 */
812static int
813xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
814
815 int rc = -1;
816 size_t new_size;
817 size_t cur_used;
818
819 unsigned char * tmp_ptr = NULL;
820
821 if ( buff == NULL )
822 return ( -1 );
823
824 else if ( ext_amt == 0 )
825 return ( 0 );
826
827 cur_used = buff->zctrl.next_out - buff->zbuff;
828 new_size = buff->size + ext_amt;
829
830#ifdef DEBUG_HTTP
831 if ( cur_used > new_size )
832 xmlGenericError( xmlGenericErrorContext,
833 "xmlZMemBuffExtend: %s\n%s %d bytes.\n",
834 "Buffer overwrite detected during compressed memory",
835 "buffer extension. Overflowed by",
836 (cur_used - new_size ) );
837#endif
838
839 tmp_ptr = xmlRealloc( buff->zbuff, new_size );
840 if ( tmp_ptr != NULL ) {
841 rc = 0;
842 buff->size = new_size;
843 buff->zbuff = tmp_ptr;
844 buff->zctrl.next_out = tmp_ptr + cur_used;
845 buff->zctrl.avail_out = new_size - cur_used;
846 }
847 else {
848 xmlGenericError( xmlGenericErrorContext,
849 "xmlZMemBuffExtend: %s %lu bytes.\n",
850 "Allocation failure extending output buffer to",
851 new_size );
852 }
853
854 return ( rc );
855}
856
857/**
858 * xmlZMemBuffAppend
859 * @buff: Buffer used to compress and consolidate data
860 * @src: Uncompressed source content to append to buffer
861 * @len: Length of source data to append to buffer
862 *
863 * Compress and append data to the internal buffer. The data buffer
864 * will be expanded if needed to store the additional data.
865 *
866 * Returns the number of bytes appended to the buffer or -1 on error.
867 */
868static int
869xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
870
871 int z_err;
872 size_t min_accept;
873
874 if ( ( buff == NULL ) || ( src == NULL ) )
875 return ( -1 );
876
877 buff->zctrl.avail_in = len;
878 buff->zctrl.next_in = (unsigned char *)src;
879 while ( buff->zctrl.avail_in > 0 ) {
880 /*
881 ** Extend the buffer prior to deflate call if a reasonable amount
882 ** of output buffer space is not available.
883 */
884 min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
885 if ( buff->zctrl.avail_out <= min_accept ) {
886 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
887 return ( -1 );
888 }
889
890 z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
891 if ( z_err != Z_OK ) {
892 xmlGenericError( xmlGenericErrorContext,
893 "xmlZMemBuffAppend: %s %d %s - %d",
894 "Compression error while appending",
895 len, "bytes to buffer. ZLIB error", z_err );
896 return ( -1 );
897 }
898 }
899
900 buff->crc = crc32( buff->crc, (unsigned char *)src, len );
901
902 return ( len );
903}
904
905/**
906 * xmlZMemBuffGetContent
907 * @buff: Compressed memory content buffer
908 * @data_ref: Pointer reference to point to compressed content
909 *
910 * Flushes the compression buffers, appends gzip file trailers and
911 * returns the compressed content and length of the compressed data.
912 * NOTE: The gzip trailer code here is plagiarized from zlib source.
913 *
914 * Returns the length of the compressed data or -1 on error.
915 */
916static int
917xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
918
919 int zlgth = -1;
920 int z_err;
921
922 if ( ( buff == NULL ) || ( data_ref == NULL ) )
923 return ( -1 );
924
925 /* Need to loop until compression output buffers are flushed */
926
927 do
928 {
929 z_err = deflate( &buff->zctrl, Z_FINISH );
930 if ( z_err == Z_OK ) {
931 /* In this case Z_OK means more buffer space needed */
932
933 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
934 return ( -1 );
935 }
936 }
937 while ( z_err == Z_OK );
938
939 /* If the compression state is not Z_STREAM_END, some error occurred */
940
941 if ( z_err == Z_STREAM_END ) {
942
943 /* Need to append the gzip data trailer */
944
945 if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
946 if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
947 return ( -1 );
948 }
949
950 /*
951 ** For whatever reason, the CRC and length data are pushed out
952 ** in reverse byte order. So a memcpy can't be used here.
953 */
954
955 append_reverse_ulong( buff, buff->crc );
956 append_reverse_ulong( buff, buff->zctrl.total_in );
957
958 zlgth = buff->zctrl.next_out - buff->zbuff;
959 *data_ref = (char *)buff->zbuff;
960 }
961
962 else
963 xmlGenericError( xmlGenericErrorContext,
964 "xmlZMemBuffGetContent: %s - %d\n",
965 "Error flushing zlib buffers. Error code", z_err );
966
967 return ( zlgth );
968}
969#endif /* HAVE_ZLIB_H */
970
971/**
972 * xmlFreeHTTPWriteCtxt
973 * @ctxt: Context to cleanup
974 *
975 * Free allocated memory and reclaim system resources.
976 *
977 * No return value.
978 */
979static void
980xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
981{
982 if ( ctxt->uri != NULL )
983 free( ctxt->uri );
984
985 if ( ctxt->doc_buff != NULL ) {
986
987#ifdef HAVE_ZLIB_H
988 if ( ctxt->compression > 0 ) {
989 xmlFreeZMemBuff( ctxt->doc_buff );
990 }
991 else
992#endif
993 {
994 xmlOutputBufferClose( ctxt->doc_buff );
995 }
996 }
997
998 free( ctxt );
999 return;
1000}
1001
1002
Owen Taylor3473f882001-02-23 17:55:21 +00001003/**
1004 * xmlIOHTTPMatch:
1005 * @filename: the URI for matching
1006 *
1007 * check if the URI matches an HTTP one
1008 *
1009 * Returns 1 if matches, 0 otherwise
1010 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001011int
Owen Taylor3473f882001-02-23 17:55:21 +00001012xmlIOHTTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001013 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
Owen Taylor3473f882001-02-23 17:55:21 +00001014 return(1);
1015 return(0);
1016}
1017
1018/**
1019 * xmlIOHTTPOpen:
1020 * @filename: the URI for matching
1021 *
1022 * open an HTTP I/O channel
1023 *
1024 * Returns an I/O context or NULL in case of error
1025 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001026void *
Owen Taylor3473f882001-02-23 17:55:21 +00001027xmlIOHTTPOpen (const char *filename) {
1028 return(xmlNanoHTTPOpen(filename, NULL));
1029}
1030
1031/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001032 * xmlIOHTTPOpenW
1033 * @post_uri: The destination URI for the document
1034 * @compression: The compression desired for the document.
1035 *
1036 * Open a temporary buffer to collect the document for a subsequent HTTP POST
1037 * request. Non-static as is called from the output buffer creation routine.
1038 *
1039 * Returns an I/O context or NULL in case of error.
1040 */
1041
1042void *
Daniel Veillard572577e2002-01-18 16:23:55 +00001043xmlIOHTTPOpenW(const char *post_uri, int compression)
1044{
Daniel Veillardf012a642001-07-23 19:10:52 +00001045
Daniel Veillard572577e2002-01-18 16:23:55 +00001046 xmlIOHTTPWriteCtxtPtr ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001047
Daniel Veillard572577e2002-01-18 16:23:55 +00001048 if (post_uri == NULL)
1049 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001050
Daniel Veillard572577e2002-01-18 16:23:55 +00001051 ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1052 if (ctxt == NULL) {
1053 xmlGenericError(xmlGenericErrorContext,
1054 "xmlIOHTTPOpenW: Failed to create output HTTP context.\n");
1055 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001056 }
1057
Daniel Veillard572577e2002-01-18 16:23:55 +00001058 (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
Daniel Veillardf012a642001-07-23 19:10:52 +00001059
Daniel Veillard572577e2002-01-18 16:23:55 +00001060 ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1061 if (ctxt->uri == NULL) {
1062 xmlGenericError(xmlGenericErrorContext,
1063 "xmlIOHTTPOpenW: Failed to duplicate destination URI.\n");
1064 xmlFreeHTTPWriteCtxt(ctxt);
1065 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001066 }
1067
1068 /*
Daniel Veillard572577e2002-01-18 16:23:55 +00001069 * ** Since the document length is required for an HTTP post,
1070 * ** need to put the document into a buffer. A memory buffer
1071 * ** is being used to avoid pushing the data to disk and back.
1072 */
Daniel Veillardf012a642001-07-23 19:10:52 +00001073
1074#ifdef HAVE_ZLIB_H
Daniel Veillard572577e2002-01-18 16:23:55 +00001075 if ((compression > 0) && (compression <= 9)) {
1076
1077 ctxt->compression = compression;
1078 ctxt->doc_buff = xmlCreateZMemBuff(compression);
1079 } else
Daniel Veillardf012a642001-07-23 19:10:52 +00001080#endif
1081 {
Daniel Veillard572577e2002-01-18 16:23:55 +00001082 /* Any character conversions should have been done before this */
Daniel Veillardf012a642001-07-23 19:10:52 +00001083
Daniel Veillard572577e2002-01-18 16:23:55 +00001084 ctxt->doc_buff = xmlAllocOutputBuffer(NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001085 }
1086
Daniel Veillard572577e2002-01-18 16:23:55 +00001087 if (ctxt->doc_buff == NULL) {
1088 xmlFreeHTTPWriteCtxt(ctxt);
1089 ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001090 }
1091
Daniel Veillard572577e2002-01-18 16:23:55 +00001092 return (ctxt);
Daniel Veillardf012a642001-07-23 19:10:52 +00001093}
1094
1095/**
1096 * xmlIOHTTPDfltOpenW
1097 * @post_uri: The destination URI for this document.
1098 *
1099 * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1100 * HTTP post command. This function should generally not be used as
1101 * the open callback is short circuited in xmlOutputBufferCreateFile.
1102 *
1103 * Returns a pointer to the new IO context.
1104 */
1105static void *
1106xmlIOHTTPDfltOpenW( const char * post_uri ) {
1107 return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1108}
1109
1110/**
Owen Taylor3473f882001-02-23 17:55:21 +00001111 * xmlIOHTTPRead:
1112 * @context: the I/O context
1113 * @buffer: where to drop data
1114 * @len: number of bytes to write
1115 *
1116 * Read @len bytes to @buffer from the I/O channel.
1117 *
1118 * Returns the number of bytes written
1119 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001120int
Owen Taylor3473f882001-02-23 17:55:21 +00001121xmlIOHTTPRead(void * context, char * buffer, int len) {
1122 return(xmlNanoHTTPRead(context, &buffer[0], len));
1123}
1124
1125/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001126 * xmlIOHTTPWrite
1127 * @context: previously opened writing context
1128 * @buffer: data to output to temporary buffer
1129 * @len: bytes to output
1130 *
1131 * Collect data from memory buffer into a temporary file for later
1132 * processing.
1133 *
1134 * Returns number of bytes written.
1135 */
1136
1137static int
1138xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
1139
1140 xmlIOHTTPWriteCtxtPtr ctxt = context;
1141
1142 if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1143 return ( -1 );
1144
1145 if ( len > 0 ) {
1146
1147 /* Use gzwrite or fwrite as previously setup in the open call */
1148
1149#ifdef HAVE_ZLIB_H
1150 if ( ctxt->compression > 0 )
1151 len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1152
1153 else
1154#endif
1155 len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
1156
1157 if ( len < 0 ) {
1158 xmlGenericError( xmlGenericErrorContext,
1159 "xmlIOHTTPWrite: %s\n%s '%s'.\n",
1160 "Error appending to internal buffer.",
1161 "Error sending document to URI",
1162 ctxt->uri );
1163 }
1164 }
1165
1166 return ( len );
1167}
1168
1169
1170/**
Owen Taylor3473f882001-02-23 17:55:21 +00001171 * xmlIOHTTPClose:
1172 * @context: the I/O context
1173 *
1174 * Close an HTTP I/O channel
1175 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001176int
Owen Taylor3473f882001-02-23 17:55:21 +00001177xmlIOHTTPClose (void * context) {
1178 xmlNanoHTTPClose(context);
Daniel Veillardf012a642001-07-23 19:10:52 +00001179 return 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001180}
Daniel Veillardf012a642001-07-23 19:10:52 +00001181
1182/**
1183 * xmlIOHTTCloseWrite
1184 * @context: The I/O context
1185 * @http_mthd: The HTTP method to be used when sending the data
1186 *
1187 * Close the transmit HTTP I/O channel and actually send the data.
1188 */
1189static int
1190xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
1191
1192 int close_rc = -1;
1193 int http_rtn = 0;
1194 int content_lgth = 0;
1195 xmlIOHTTPWriteCtxtPtr ctxt = context;
1196
1197 char * http_content = NULL;
1198 char * content_encoding = NULL;
1199 char * content_type = (char *) "text/xml";
1200 void * http_ctxt = NULL;
1201
1202 if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
1203 return ( -1 );
1204
1205 /* Retrieve the content from the appropriate buffer */
1206
1207#ifdef HAVE_ZLIB_H
1208
1209 if ( ctxt->compression > 0 ) {
1210 content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
1211 content_encoding = (char *) "Content-Encoding: gzip";
1212 }
1213 else
1214#endif
1215 {
1216 /* Pull the data out of the memory output buffer */
1217
1218 xmlOutputBufferPtr dctxt = ctxt->doc_buff;
1219 http_content = (char *)dctxt->buffer->content;
1220 content_lgth = dctxt->buffer->use;
1221 }
1222
1223 if ( http_content == NULL ) {
1224 xmlGenericError( xmlGenericErrorContext,
1225 "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n",
1226 "Error retrieving content.\nUnable to",
1227 http_mthd, "data to URI", ctxt->uri );
1228 }
1229
1230 else {
1231
1232 http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
1233 &content_type, content_encoding,
1234 content_lgth );
1235
1236 if ( http_ctxt != NULL ) {
1237#ifdef DEBUG_HTTP
1238 /* If testing/debugging - dump reply with request content */
1239
1240 FILE * tst_file = NULL;
1241 char buffer[ 4096 ];
1242 char * dump_name = NULL;
1243 int avail;
1244
1245 xmlGenericError( xmlGenericErrorContext,
1246 "xmlNanoHTTPCloseWrite: HTTP %s to\n%s returned %d.\n",
1247 http_mthd, ctxt->uri,
1248 xmlNanoHTTPReturnCode( http_ctxt ) );
1249
1250 /*
1251 ** Since either content or reply may be gzipped,
1252 ** dump them to separate files instead of the
1253 ** standard error context.
1254 */
1255
1256 dump_name = tempnam( NULL, "lxml" );
1257 if ( dump_name != NULL ) {
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001258 (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001259
1260 tst_file = fopen( buffer, "w" );
1261 if ( tst_file != NULL ) {
1262 xmlGenericError( xmlGenericErrorContext,
1263 "Transmitted content saved in file: %s\n", buffer );
1264
1265 fwrite( http_content, sizeof( char ),
1266 content_lgth, tst_file );
1267 fclose( tst_file );
1268 }
1269
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001270 (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001271 tst_file = fopen( buffer, "w" );
1272 if ( tst_file != NULL ) {
1273 xmlGenericError( xmlGenericErrorContext,
1274 "Reply content saved in file: %s\n", buffer );
1275
1276
1277 while ( (avail = xmlNanoHTTPRead( http_ctxt,
1278 buffer, sizeof( buffer ) )) > 0 ) {
1279
1280 fwrite( buffer, sizeof( char ), avail, tst_file );
1281 }
1282
1283 fclose( tst_file );
1284 }
1285
1286 free( dump_name );
1287 }
1288#endif /* DEBUG_HTTP */
1289
1290 http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
1291 if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
1292 close_rc = 0;
1293 else
1294 xmlGenericError( xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001295 "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
Daniel Veillardf012a642001-07-23 19:10:52 +00001296 http_mthd, content_lgth,
1297 "bytes to URI", ctxt->uri,
1298 "failed. HTTP return code:", http_rtn );
1299
1300 xmlNanoHTTPClose( http_ctxt );
1301 xmlFree( content_type );
1302 }
1303 }
1304
1305 /* Final cleanups */
1306
1307 xmlFreeHTTPWriteCtxt( ctxt );
1308
1309 return ( close_rc );
1310}
1311
1312/**
1313 * xmlIOHTTPClosePut
1314 *
1315 * @context: The I/O context
1316 *
1317 * Close the transmit HTTP I/O channel and actually send data using a PUT
1318 * HTTP method.
1319 */
1320static int
1321xmlIOHTTPClosePut( void * ctxt ) {
1322 return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
1323}
1324
1325
1326/**
1327 * xmlIOHTTPClosePost
1328 *
1329 * @context: The I/O context
1330 *
1331 * Close the transmit HTTP I/O channel and actually send data using a POST
1332 * HTTP method.
1333 */
1334static int
1335xmlIOHTTPClosePost( void * ctxt ) {
1336 return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
1337}
1338
Owen Taylor3473f882001-02-23 17:55:21 +00001339#endif /* LIBXML_HTTP_ENABLED */
1340
1341#ifdef LIBXML_FTP_ENABLED
1342/************************************************************************
1343 * *
1344 * I/O for FTP file accesses *
1345 * *
1346 ************************************************************************/
1347/**
1348 * xmlIOFTPMatch:
1349 * @filename: the URI for matching
1350 *
1351 * check if the URI matches an FTP one
1352 *
1353 * Returns 1 if matches, 0 otherwise
1354 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001355int
Owen Taylor3473f882001-02-23 17:55:21 +00001356xmlIOFTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001357 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
Owen Taylor3473f882001-02-23 17:55:21 +00001358 return(1);
1359 return(0);
1360}
1361
1362/**
1363 * xmlIOFTPOpen:
1364 * @filename: the URI for matching
1365 *
1366 * open an FTP I/O channel
1367 *
1368 * Returns an I/O context or NULL in case of error
1369 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001370void *
Owen Taylor3473f882001-02-23 17:55:21 +00001371xmlIOFTPOpen (const char *filename) {
1372 return(xmlNanoFTPOpen(filename));
1373}
1374
1375/**
1376 * xmlIOFTPRead:
1377 * @context: the I/O context
1378 * @buffer: where to drop data
1379 * @len: number of bytes to write
1380 *
1381 * Read @len bytes to @buffer from the I/O channel.
1382 *
1383 * Returns the number of bytes written
1384 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001385int
Owen Taylor3473f882001-02-23 17:55:21 +00001386xmlIOFTPRead(void * context, char * buffer, int len) {
1387 return(xmlNanoFTPRead(context, &buffer[0], len));
1388}
1389
1390/**
1391 * xmlIOFTPClose:
1392 * @context: the I/O context
1393 *
1394 * Close an FTP I/O channel
1395 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001396int
Owen Taylor3473f882001-02-23 17:55:21 +00001397xmlIOFTPClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001398 return ( xmlNanoFTPClose(context) );
Owen Taylor3473f882001-02-23 17:55:21 +00001399}
1400#endif /* LIBXML_FTP_ENABLED */
1401
1402
1403/**
1404 * xmlRegisterInputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001405 * @matchFunc: the xmlInputMatchCallback
1406 * @openFunc: the xmlInputOpenCallback
1407 * @readFunc: the xmlInputReadCallback
1408 * @closeFunc: the xmlInputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001409 *
1410 * Register a new set of I/O callback for handling parser input.
1411 *
1412 * Returns the registered handler number or -1 in case of error
1413 */
1414int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001415xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
1416 xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
1417 xmlInputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001418 if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
1419 return(-1);
1420 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001421 xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
1422 xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
1423 xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
1424 xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001425 return(xmlInputCallbackNr++);
1426}
1427
1428/**
1429 * xmlRegisterOutputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001430 * @matchFunc: the xmlOutputMatchCallback
1431 * @openFunc: the xmlOutputOpenCallback
1432 * @writeFunc: the xmlOutputWriteCallback
1433 * @closeFunc: the xmlOutputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001434 *
1435 * Register a new set of I/O callback for handling output.
1436 *
1437 * Returns the registered handler number or -1 in case of error
1438 */
1439int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001440xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
1441 xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
1442 xmlOutputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001443 if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) {
1444 return(-1);
1445 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001446 xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
1447 xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
1448 xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
1449 xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001450 return(xmlOutputCallbackNr++);
1451}
1452
1453/**
1454 * xmlRegisterDefaultInputCallbacks:
1455 *
1456 * Registers the default compiled-in I/O handlers.
1457 */
1458void
1459#ifdef VMS
1460xmlRegisterDefInputCallbacks
1461#else
1462xmlRegisterDefaultInputCallbacks
1463#endif
1464(void) {
1465 if (xmlInputCallbackInitialized)
1466 return;
1467
1468 xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
1469 xmlFileRead, xmlFileClose);
1470#ifdef HAVE_ZLIB_H
1471 xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1472 xmlGzfileRead, xmlGzfileClose);
1473#endif /* HAVE_ZLIB_H */
1474
1475#ifdef LIBXML_HTTP_ENABLED
1476 xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
1477 xmlIOHTTPRead, xmlIOHTTPClose);
1478#endif /* LIBXML_HTTP_ENABLED */
1479
1480#ifdef LIBXML_FTP_ENABLED
1481 xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1482 xmlIOFTPRead, xmlIOFTPClose);
1483#endif /* LIBXML_FTP_ENABLED */
1484 xmlInputCallbackInitialized = 1;
1485}
1486
1487/**
1488 * xmlRegisterDefaultOutputCallbacks:
1489 *
1490 * Registers the default compiled-in I/O handlers.
1491 */
1492void
1493#ifdef VMS
1494xmlRegisterDefOutputCallbacks
1495#else
1496xmlRegisterDefaultOutputCallbacks
1497#endif
1498(void) {
1499 if (xmlOutputCallbackInitialized)
1500 return;
1501
1502 xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
1503 xmlFileWrite, xmlFileClose);
Daniel Veillardf012a642001-07-23 19:10:52 +00001504
1505#ifdef LIBXML_HTTP_ENABLED
1506 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1507 xmlIOHTTPWrite, xmlIOHTTPClosePut);
1508#endif
1509
Owen Taylor3473f882001-02-23 17:55:21 +00001510/*********************************
1511 No way a-priori to distinguish between gzipped files from
1512 uncompressed ones except opening if existing then closing
1513 and saving with same compression ratio ... a pain.
1514
1515#ifdef HAVE_ZLIB_H
1516 xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1517 xmlGzfileWrite, xmlGzfileClose);
1518#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001519
1520 Nor FTP PUT ....
1521#ifdef LIBXML_FTP_ENABLED
1522 xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1523 xmlIOFTPWrite, xmlIOFTPClose);
1524#endif
1525 **********************************/
1526 xmlOutputCallbackInitialized = 1;
1527}
1528
Daniel Veillardf012a642001-07-23 19:10:52 +00001529#ifdef LIBXML_HTTP_ENABLED
1530/**
1531 * xmlRegisterHTTPPostCallbacks
1532 *
1533 * By default, libxml submits HTTP output requests using the "PUT" method.
1534 * Calling this method changes the HTTP output method to use the "POST"
1535 * method instead.
1536 *
1537 */
1538void
1539xmlRegisterHTTPPostCallbacks( void ) {
1540
1541 /* Register defaults if not done previously */
1542
1543 if ( xmlOutputCallbackInitialized == 0 )
1544 xmlRegisterDefaultOutputCallbacks( );
1545
1546 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1547 xmlIOHTTPWrite, xmlIOHTTPClosePost);
1548 return;
1549}
1550#endif
1551
Owen Taylor3473f882001-02-23 17:55:21 +00001552/**
1553 * xmlAllocParserInputBuffer:
1554 * @enc: the charset encoding if known
1555 *
1556 * Create a buffered parser input for progressive parsing
1557 *
1558 * Returns the new parser input or NULL
1559 */
1560xmlParserInputBufferPtr
1561xmlAllocParserInputBuffer(xmlCharEncoding enc) {
1562 xmlParserInputBufferPtr ret;
1563
1564 ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
1565 if (ret == NULL) {
1566 xmlGenericError(xmlGenericErrorContext,
1567 "xmlAllocParserInputBuffer : out of memory!\n");
1568 return(NULL);
1569 }
1570 memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
1571 ret->buffer = xmlBufferCreate();
1572 if (ret->buffer == NULL) {
1573 xmlFree(ret);
1574 return(NULL);
1575 }
1576 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1577 ret->encoder = xmlGetCharEncodingHandler(enc);
1578 if (ret->encoder != NULL)
1579 ret->raw = xmlBufferCreate();
1580 else
1581 ret->raw = NULL;
1582 ret->readcallback = NULL;
1583 ret->closecallback = NULL;
1584 ret->context = NULL;
1585
1586 return(ret);
1587}
1588
1589/**
1590 * xmlAllocOutputBuffer:
1591 * @encoder: the encoding converter or NULL
1592 *
1593 * Create a buffered parser output
1594 *
1595 * Returns the new parser output or NULL
1596 */
1597xmlOutputBufferPtr
1598xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
1599 xmlOutputBufferPtr ret;
1600
1601 ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1602 if (ret == NULL) {
1603 xmlGenericError(xmlGenericErrorContext,
1604 "xmlAllocOutputBuffer : out of memory!\n");
1605 return(NULL);
1606 }
1607 memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
1608 ret->buffer = xmlBufferCreate();
1609 if (ret->buffer == NULL) {
1610 xmlFree(ret);
1611 return(NULL);
1612 }
1613 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1614 ret->encoder = encoder;
1615 if (encoder != NULL) {
1616 ret->conv = xmlBufferCreateSize(4000);
1617 /*
1618 * This call is designed to initiate the encoder state
1619 */
1620 xmlCharEncOutFunc(encoder, ret->conv, NULL);
1621 } else
1622 ret->conv = NULL;
1623 ret->writecallback = NULL;
1624 ret->closecallback = NULL;
1625 ret->context = NULL;
1626 ret->written = 0;
1627
1628 return(ret);
1629}
1630
1631/**
1632 * xmlFreeParserInputBuffer:
1633 * @in: a buffered parser input
1634 *
1635 * Free up the memory used by a buffered parser input
1636 */
1637void
1638xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
1639 if (in->raw) {
1640 xmlBufferFree(in->raw);
1641 in->raw = NULL;
1642 }
1643 if (in->encoder != NULL) {
1644 xmlCharEncCloseFunc(in->encoder);
1645 }
1646 if (in->closecallback != NULL) {
1647 in->closecallback(in->context);
1648 }
1649 if (in->buffer != NULL) {
1650 xmlBufferFree(in->buffer);
1651 in->buffer = NULL;
1652 }
1653
Owen Taylor3473f882001-02-23 17:55:21 +00001654 xmlFree(in);
1655}
1656
1657/**
1658 * xmlOutputBufferClose:
1659 * @out: a buffered output
1660 *
1661 * flushes and close the output I/O channel
1662 * and free up all the associated resources
1663 *
1664 * Returns the number of byte written or -1 in case of error.
1665 */
1666int
1667xmlOutputBufferClose(xmlOutputBufferPtr out) {
1668 int written;
Daniel Veillardf012a642001-07-23 19:10:52 +00001669 int err_rc = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001670
1671 if (out == NULL)
1672 return(-1);
1673 if (out->writecallback != NULL)
1674 xmlOutputBufferFlush(out);
1675 if (out->closecallback != NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001676 err_rc = out->closecallback(out->context);
Owen Taylor3473f882001-02-23 17:55:21 +00001677 }
1678 written = out->written;
1679 if (out->conv) {
1680 xmlBufferFree(out->conv);
1681 out->conv = NULL;
1682 }
1683 if (out->encoder != NULL) {
1684 xmlCharEncCloseFunc(out->encoder);
1685 }
1686 if (out->buffer != NULL) {
1687 xmlBufferFree(out->buffer);
1688 out->buffer = NULL;
1689 }
1690
Owen Taylor3473f882001-02-23 17:55:21 +00001691 xmlFree(out);
Daniel Veillardf012a642001-07-23 19:10:52 +00001692 return( ( err_rc == 0 ) ? written : err_rc );
Owen Taylor3473f882001-02-23 17:55:21 +00001693}
1694
1695/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001696 * xmlParserInputBufferCreateFname:
1697 * @URI: a C string containing the URI or filename
1698 * @enc: the charset encoding if known
1699 *
1700 * VMS version of xmlParserInputBufferCreateFilename()
1701 *
1702 * Returns the new parser input or NULL
1703 */
1704/**
Owen Taylor3473f882001-02-23 17:55:21 +00001705 * xmlParserInputBufferCreateFilename:
1706 * @URI: a C string containing the URI or filename
1707 * @enc: the charset encoding if known
1708 *
1709 * Create a buffered parser input for the progressive parsing of a file
1710 * If filename is "-' then we use stdin as the input.
1711 * Automatic support for ZLIB/Compress compressed document is provided
1712 * by default if found at compile-time.
1713 * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
1714 *
1715 * Returns the new parser input or NULL
1716 */
1717xmlParserInputBufferPtr
1718#ifdef VMS
1719xmlParserInputBufferCreateFname
1720#else
1721xmlParserInputBufferCreateFilename
1722#endif
1723(const char *URI, xmlCharEncoding enc) {
1724 xmlParserInputBufferPtr ret;
Daniel Veillard388236f2001-07-08 18:35:48 +00001725 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001726 void *context = NULL;
Daniel Veillard388236f2001-07-08 18:35:48 +00001727 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001728 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001729
1730 if (xmlInputCallbackInitialized == 0)
1731 xmlRegisterDefaultInputCallbacks();
1732
1733 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001734 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1735 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001736
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00001737#ifdef LIBXML_CATALOG_ENABLED
1738#endif
1739
Owen Taylor3473f882001-02-23 17:55:21 +00001740 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001741 * Try to find one of the input accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001742 * Go in reverse to give precedence to user defined handlers.
Daniel Veillard388236f2001-07-08 18:35:48 +00001743 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001744 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001745 unescaped = xmlURIUnescapeString((char *) normalized, 0, NULL);
Daniel Veillard388236f2001-07-08 18:35:48 +00001746 if (unescaped != NULL) {
1747 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1748 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1749 (xmlInputCallbackTable[i].matchcallback(unescaped) != 0)) {
1750 context = xmlInputCallbackTable[i].opencallback(unescaped);
1751 if (context != NULL)
1752 break;
1753 }
1754 }
1755 xmlFree(unescaped);
1756 }
1757
1758 /*
1759 * If this failed try with a non-escaped URI this may be a strange
1760 * filename
1761 */
1762 if (context == NULL) {
1763 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1764 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1765 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001766 context = xmlInputCallbackTable[i].opencallback(normalized);
Daniel Veillard388236f2001-07-08 18:35:48 +00001767 if (context != NULL)
1768 break;
1769 }
Owen Taylor3473f882001-02-23 17:55:21 +00001770 }
1771 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001772 xmlFree(normalized);
Owen Taylor3473f882001-02-23 17:55:21 +00001773 if (context == NULL) {
1774 return(NULL);
1775 }
1776
1777 /*
1778 * Allocate the Input buffer front-end.
1779 */
1780 ret = xmlAllocParserInputBuffer(enc);
1781 if (ret != NULL) {
1782 ret->context = context;
1783 ret->readcallback = xmlInputCallbackTable[i].readcallback;
1784 ret->closecallback = xmlInputCallbackTable[i].closecallback;
1785 }
1786 return(ret);
1787}
1788
1789/**
1790 * xmlOutputBufferCreateFilename:
1791 * @URI: a C string containing the URI or filename
1792 * @encoder: the encoding converter or NULL
1793 * @compression: the compression ration (0 none, 9 max).
1794 *
1795 * Create a buffered output for the progressive saving of a file
1796 * If filename is "-' then we use stdout as the output.
1797 * Automatic support for ZLIB/Compress compressed document is provided
1798 * by default if found at compile-time.
1799 * TODO: currently if compression is set, the library only support
1800 * writing to a local file.
1801 *
1802 * Returns the new output or NULL
1803 */
1804xmlOutputBufferPtr
1805xmlOutputBufferCreateFilename(const char *URI,
1806 xmlCharEncodingHandlerPtr encoder,
1807 int compression) {
1808 xmlOutputBufferPtr ret;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001809 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001810 void *context = NULL;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001811 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001812 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001813
Daniel Veillardf012a642001-07-23 19:10:52 +00001814 int is_http_uri = 0; /* Can't change if HTTP disabled */
1815
Owen Taylor3473f882001-02-23 17:55:21 +00001816 if (xmlOutputCallbackInitialized == 0)
1817 xmlRegisterDefaultOutputCallbacks();
1818
1819 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001820 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1821 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001822
Daniel Veillardf012a642001-07-23 19:10:52 +00001823#ifdef LIBXML_HTTP_ENABLED
1824 /* Need to prevent HTTP URI's from falling into zlib short circuit */
1825
Daniel Veillardf4862f02002-09-10 11:13:43 +00001826 is_http_uri = xmlIOHTTPMatch( normalized );
Daniel Veillardf012a642001-07-23 19:10:52 +00001827#endif
1828
Owen Taylor3473f882001-02-23 17:55:21 +00001829
1830 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001831 * Try to find one of the output accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001832 * Go in reverse to give precedence to user defined handlers.
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001833 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001834 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001835 unescaped = xmlURIUnescapeString(normalized, 0, NULL);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001836 if (unescaped != NULL) {
1837#ifdef HAVE_ZLIB_H
1838 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1839 context = xmlGzfileOpenW(unescaped, compression);
1840 if (context != NULL) {
1841 ret = xmlAllocOutputBuffer(encoder);
1842 if (ret != NULL) {
1843 ret->context = context;
1844 ret->writecallback = xmlGzfileWrite;
1845 ret->closecallback = xmlGzfileClose;
1846 }
1847 xmlFree(unescaped);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001848 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001849 return(ret);
1850 }
1851 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001852#endif
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001853 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1854 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1855 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
1856#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1857 /* Need to pass compression parameter into HTTP open calls */
1858 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1859 context = xmlIOHTTPOpenW(unescaped, compression);
1860 else
1861#endif
1862 context = xmlOutputCallbackTable[i].opencallback(unescaped);
1863 if (context != NULL)
1864 break;
1865 }
1866 }
1867 xmlFree(unescaped);
1868 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001869
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001870 /*
1871 * If this failed try with a non-escaped URI this may be a strange
1872 * filename
1873 */
1874 if (context == NULL) {
1875#ifdef HAVE_ZLIB_H
1876 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001877 context = xmlGzfileOpenW(normalized, compression);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001878 if (context != NULL) {
1879 ret = xmlAllocOutputBuffer(encoder);
1880 if (ret != NULL) {
1881 ret->context = context;
1882 ret->writecallback = xmlGzfileWrite;
1883 ret->closecallback = xmlGzfileClose;
1884 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001885 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001886 return(ret);
1887 }
1888 }
1889#endif
1890 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1891 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
Daniel Veillardf4862f02002-09-10 11:13:43 +00001892 (xmlOutputCallbackTable[i].matchcallback(normalized) != 0)) {
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001893#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1894 /* Need to pass compression parameter into HTTP open calls */
1895 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1896 context = xmlIOHTTPOpenW(URI, compression);
1897 else
1898#endif
1899 context = xmlOutputCallbackTable[i].opencallback(URI);
1900 if (context != NULL)
1901 break;
1902 }
Owen Taylor3473f882001-02-23 17:55:21 +00001903 }
1904 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001905 xmlFree(normalized);
Daniel Veillardf012a642001-07-23 19:10:52 +00001906
Owen Taylor3473f882001-02-23 17:55:21 +00001907 if (context == NULL) {
1908 return(NULL);
1909 }
1910
1911 /*
1912 * Allocate the Output buffer front-end.
1913 */
1914 ret = xmlAllocOutputBuffer(encoder);
1915 if (ret != NULL) {
1916 ret->context = context;
1917 ret->writecallback = xmlOutputCallbackTable[i].writecallback;
1918 ret->closecallback = xmlOutputCallbackTable[i].closecallback;
1919 }
1920 return(ret);
1921}
1922
1923/**
1924 * xmlParserInputBufferCreateFile:
1925 * @file: a FILE*
1926 * @enc: the charset encoding if known
1927 *
1928 * Create a buffered parser input for the progressive parsing of a FILE *
1929 * buffered C I/O
1930 *
1931 * Returns the new parser input or NULL
1932 */
1933xmlParserInputBufferPtr
1934xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
1935 xmlParserInputBufferPtr ret;
1936
1937 if (xmlInputCallbackInitialized == 0)
1938 xmlRegisterDefaultInputCallbacks();
1939
1940 if (file == NULL) return(NULL);
1941
1942 ret = xmlAllocParserInputBuffer(enc);
1943 if (ret != NULL) {
1944 ret->context = file;
1945 ret->readcallback = xmlFileRead;
1946 ret->closecallback = xmlFileFlush;
1947 }
1948
1949 return(ret);
1950}
1951
1952/**
1953 * xmlOutputBufferCreateFile:
1954 * @file: a FILE*
1955 * @encoder: the encoding converter or NULL
1956 *
1957 * Create a buffered output for the progressive saving to a FILE *
1958 * buffered C I/O
1959 *
1960 * Returns the new parser output or NULL
1961 */
1962xmlOutputBufferPtr
1963xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
1964 xmlOutputBufferPtr ret;
1965
1966 if (xmlOutputCallbackInitialized == 0)
1967 xmlRegisterDefaultOutputCallbacks();
1968
1969 if (file == NULL) return(NULL);
1970
1971 ret = xmlAllocOutputBuffer(encoder);
1972 if (ret != NULL) {
1973 ret->context = file;
1974 ret->writecallback = xmlFileWrite;
1975 ret->closecallback = xmlFileFlush;
1976 }
1977
1978 return(ret);
1979}
1980
1981/**
1982 * xmlParserInputBufferCreateFd:
1983 * @fd: a file descriptor number
1984 * @enc: the charset encoding if known
1985 *
1986 * Create a buffered parser input for the progressive parsing for the input
1987 * from a file descriptor
1988 *
1989 * Returns the new parser input or NULL
1990 */
1991xmlParserInputBufferPtr
1992xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
1993 xmlParserInputBufferPtr ret;
1994
1995 if (fd < 0) return(NULL);
1996
1997 ret = xmlAllocParserInputBuffer(enc);
1998 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00001999 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002000 ret->readcallback = xmlFdRead;
2001 ret->closecallback = xmlFdClose;
2002 }
2003
2004 return(ret);
2005}
2006
2007/**
2008 * xmlParserInputBufferCreateMem:
2009 * @mem: the memory input
2010 * @size: the length of the memory block
2011 * @enc: the charset encoding if known
2012 *
2013 * Create a buffered parser input for the progressive parsing for the input
2014 * from a memory area.
2015 *
2016 * Returns the new parser input or NULL
2017 */
2018xmlParserInputBufferPtr
2019xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
2020 xmlParserInputBufferPtr ret;
2021
2022 if (size <= 0) return(NULL);
2023 if (mem == NULL) return(NULL);
2024
2025 ret = xmlAllocParserInputBuffer(enc);
2026 if (ret != NULL) {
2027 ret->context = (void *) mem;
2028 ret->readcallback = (xmlInputReadCallback) xmlNop;
2029 ret->closecallback = NULL;
2030 xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
2031 }
2032
2033 return(ret);
2034}
2035
2036/**
2037 * xmlOutputBufferCreateFd:
2038 * @fd: a file descriptor number
2039 * @encoder: the encoding converter or NULL
2040 *
2041 * Create a buffered output for the progressive saving
2042 * to a file descriptor
2043 *
2044 * Returns the new parser output or NULL
2045 */
2046xmlOutputBufferPtr
2047xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
2048 xmlOutputBufferPtr ret;
2049
2050 if (fd < 0) return(NULL);
2051
2052 ret = xmlAllocOutputBuffer(encoder);
2053 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002054 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002055 ret->writecallback = xmlFdWrite;
Daniel Veillard7db38712002-02-07 16:39:11 +00002056 ret->closecallback = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00002057 }
2058
2059 return(ret);
2060}
2061
2062/**
2063 * xmlParserInputBufferCreateIO:
2064 * @ioread: an I/O read function
2065 * @ioclose: an I/O close function
2066 * @ioctx: an I/O handler
2067 * @enc: the charset encoding if known
2068 *
2069 * Create a buffered parser input for the progressive parsing for the input
2070 * from an I/O handler
2071 *
2072 * Returns the new parser input or NULL
2073 */
2074xmlParserInputBufferPtr
2075xmlParserInputBufferCreateIO(xmlInputReadCallback ioread,
2076 xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) {
2077 xmlParserInputBufferPtr ret;
2078
2079 if (ioread == NULL) return(NULL);
2080
2081 ret = xmlAllocParserInputBuffer(enc);
2082 if (ret != NULL) {
2083 ret->context = (void *) ioctx;
2084 ret->readcallback = ioread;
2085 ret->closecallback = ioclose;
2086 }
2087
2088 return(ret);
2089}
2090
2091/**
2092 * xmlOutputBufferCreateIO:
2093 * @iowrite: an I/O write function
2094 * @ioclose: an I/O close function
2095 * @ioctx: an I/O handler
Daniel Veillard9d06d302002-01-22 18:15:52 +00002096 * @encoder: the charset encoding if known
Owen Taylor3473f882001-02-23 17:55:21 +00002097 *
2098 * Create a buffered output for the progressive saving
2099 * to an I/O handler
2100 *
2101 * Returns the new parser output or NULL
2102 */
2103xmlOutputBufferPtr
2104xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite,
2105 xmlOutputCloseCallback ioclose, void *ioctx,
2106 xmlCharEncodingHandlerPtr encoder) {
2107 xmlOutputBufferPtr ret;
2108
2109 if (iowrite == NULL) return(NULL);
2110
2111 ret = xmlAllocOutputBuffer(encoder);
2112 if (ret != NULL) {
2113 ret->context = (void *) ioctx;
2114 ret->writecallback = iowrite;
2115 ret->closecallback = ioclose;
2116 }
2117
2118 return(ret);
2119}
2120
2121/**
2122 * xmlParserInputBufferPush:
2123 * @in: a buffered parser input
2124 * @len: the size in bytes of the array.
2125 * @buf: an char array
2126 *
2127 * Push the content of the arry in the input buffer
2128 * This routine handle the I18N transcoding to internal UTF-8
2129 * This is used when operating the parser in progressive (push) mode.
2130 *
2131 * Returns the number of chars read and stored in the buffer, or -1
2132 * in case of error.
2133 */
2134int
2135xmlParserInputBufferPush(xmlParserInputBufferPtr in,
2136 int len, const char *buf) {
2137 int nbchars = 0;
2138
2139 if (len < 0) return(0);
2140 if (in->encoder != NULL) {
2141 /*
2142 * Store the data in the incoming raw buffer
2143 */
2144 if (in->raw == NULL) {
2145 in->raw = xmlBufferCreate();
2146 }
2147 xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
2148
2149 /*
2150 * convert as much as possible to the parser reading buffer.
2151 */
2152 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2153 if (nbchars < 0) {
2154 xmlGenericError(xmlGenericErrorContext,
2155 "xmlParserInputBufferPush: encoder error\n");
2156 return(-1);
2157 }
2158 } else {
2159 nbchars = len;
2160 xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
2161 }
2162#ifdef DEBUG_INPUT
2163 xmlGenericError(xmlGenericErrorContext,
2164 "I/O: pushed %d chars, buffer %d/%d\n",
2165 nbchars, in->buffer->use, in->buffer->size);
2166#endif
2167 return(nbchars);
2168}
2169
2170/**
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002171 * endOfInput:
2172 *
2173 * When reading from an Input channel indicated end of file or error
2174 * don't reread from it again.
2175 */
2176static int
2177endOfInput (void * context ATTRIBUTE_UNUSED,
2178 char * buffer ATTRIBUTE_UNUSED,
2179 int len ATTRIBUTE_UNUSED) {
2180 return(0);
2181}
2182
2183/**
Owen Taylor3473f882001-02-23 17:55:21 +00002184 * xmlParserInputBufferGrow:
2185 * @in: a buffered parser input
2186 * @len: indicative value of the amount of chars to read
2187 *
2188 * Grow up the content of the input buffer, the old data are preserved
2189 * This routine handle the I18N transcoding to internal UTF-8
2190 * This routine is used when operating the parser in normal (pull) mode
2191 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002192 * TODO: one should be able to remove one extra copy by copying directly
Owen Taylor3473f882001-02-23 17:55:21 +00002193 * onto in->buffer or in->raw
2194 *
2195 * Returns the number of chars read and stored in the buffer, or -1
2196 * in case of error.
2197 */
2198int
2199xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
2200 char *buffer = NULL;
2201 int res = 0;
2202 int nbchars = 0;
2203 int buffree;
Daniel Veillard9e412302002-06-10 15:59:44 +00002204 unsigned int needSize;
Owen Taylor3473f882001-02-23 17:55:21 +00002205
2206 if ((len <= MINLEN) && (len != 4))
2207 len = MINLEN;
2208 buffree = in->buffer->size - in->buffer->use;
2209 if (buffree <= 0) {
2210 xmlGenericError(xmlGenericErrorContext,
2211 "xmlParserInputBufferGrow : buffer full !\n");
2212 return(0);
2213 }
2214 if (len > buffree)
2215 len = buffree;
2216
Daniel Veillarde5354492002-05-16 08:43:22 +00002217 needSize = in->buffer->use + len + 1;
2218 if (needSize > in->buffer->size){
2219 if (!xmlBufferResize(in->buffer, needSize)){
2220 xmlGenericError(xmlGenericErrorContext,
2221 "xmlBufferAdd : out of memory!\n");
2222 return(0);
2223 }
Owen Taylor3473f882001-02-23 17:55:21 +00002224 }
Daniel Veillarde5354492002-05-16 08:43:22 +00002225 buffer = (char *)&in->buffer->content[in->buffer->use];
Owen Taylor3473f882001-02-23 17:55:21 +00002226
2227 /*
2228 * Call the read method for this I/O type.
2229 */
2230 if (in->readcallback != NULL) {
2231 res = in->readcallback(in->context, &buffer[0], len);
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002232 if (res <= 0)
2233 in->readcallback = endOfInput;
Owen Taylor3473f882001-02-23 17:55:21 +00002234 } else {
2235 xmlGenericError(xmlGenericErrorContext,
2236 "xmlParserInputBufferGrow : no input !\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002237 return(-1);
2238 }
2239 if (res < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00002240 return(-1);
2241 }
2242 len = res;
2243 if (in->encoder != NULL) {
2244 /*
2245 * Store the data in the incoming raw buffer
2246 */
2247 if (in->raw == NULL) {
2248 in->raw = xmlBufferCreate();
2249 }
2250 xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
2251
2252 /*
2253 * convert as much as possible to the parser reading buffer.
2254 */
2255 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2256 if (nbchars < 0) {
2257 xmlGenericError(xmlGenericErrorContext,
2258 "xmlParserInputBufferGrow: encoder error\n");
2259 return(-1);
2260 }
2261 } else {
2262 nbchars = len;
Daniel Veillarde5354492002-05-16 08:43:22 +00002263 in->buffer->use += nbchars;
2264 buffer[nbchars] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002265 }
2266#ifdef DEBUG_INPUT
2267 xmlGenericError(xmlGenericErrorContext,
2268 "I/O: read %d chars, buffer %d/%d\n",
2269 nbchars, in->buffer->use, in->buffer->size);
2270#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002271 return(nbchars);
2272}
2273
2274/**
2275 * xmlParserInputBufferRead:
2276 * @in: a buffered parser input
2277 * @len: indicative value of the amount of chars to read
2278 *
2279 * Refresh the content of the input buffer, the old data are considered
2280 * consumed
2281 * This routine handle the I18N transcoding to internal UTF-8
2282 *
2283 * Returns the number of chars read and stored in the buffer, or -1
2284 * in case of error.
2285 */
2286int
2287xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
2288 /* xmlBufferEmpty(in->buffer); */
2289 if (in->readcallback != NULL)
2290 return(xmlParserInputBufferGrow(in, len));
2291 else
2292 return(-1);
2293}
2294
2295/**
2296 * xmlOutputBufferWrite:
2297 * @out: a buffered parser output
2298 * @len: the size in bytes of the array.
2299 * @buf: an char array
2300 *
2301 * Write the content of the array in the output I/O buffer
2302 * This routine handle the I18N transcoding from internal UTF-8
2303 * The buffer is lossless, i.e. will store in case of partial
2304 * or delayed writes.
2305 *
2306 * Returns the number of chars immediately written, or -1
2307 * in case of error.
2308 */
2309int
2310xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
2311 int nbchars = 0; /* number of chars to output to I/O */
2312 int ret; /* return from function call */
2313 int written = 0; /* number of char written to I/O so far */
2314 int chunk; /* number of byte curreent processed from buf */
2315
2316 if (len < 0) return(0);
2317
2318 do {
2319 chunk = len;
2320 if (chunk > 4 * MINLEN)
2321 chunk = 4 * MINLEN;
2322
2323 /*
2324 * first handle encoding stuff.
2325 */
2326 if (out->encoder != NULL) {
2327 /*
2328 * Store the data in the incoming raw buffer
2329 */
2330 if (out->conv == NULL) {
2331 out->conv = xmlBufferCreate();
2332 }
2333 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2334
2335 if ((out->buffer->use < MINLEN) && (chunk == len))
2336 goto done;
2337
2338 /*
2339 * convert as much as possible to the parser reading buffer.
2340 */
2341 ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2342 if (ret < 0) {
2343 xmlGenericError(xmlGenericErrorContext,
2344 "xmlOutputBufferWrite: encoder error\n");
2345 return(-1);
2346 }
2347 nbchars = out->conv->use;
2348 } else {
2349 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2350 nbchars = out->buffer->use;
2351 }
2352 buf += chunk;
2353 len -= chunk;
2354
2355 if ((nbchars < MINLEN) && (len <= 0))
2356 goto done;
2357
2358 if (out->writecallback) {
2359 /*
2360 * second write the stuff to the I/O channel
2361 */
2362 if (out->encoder != NULL) {
2363 ret = out->writecallback(out->context,
2364 (const char *)out->conv->content, nbchars);
2365 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002366 xmlBufferShrink(out->conv, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002367 } else {
2368 ret = out->writecallback(out->context,
2369 (const char *)out->buffer->content, nbchars);
2370 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002371 xmlBufferShrink(out->buffer, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002372 }
2373 if (ret < 0) {
2374 xmlGenericError(xmlGenericErrorContext,
2375 "I/O: error %d writing %d bytes\n", ret, nbchars);
2376 return(ret);
2377 }
2378 out->written += ret;
2379 }
2380 written += nbchars;
2381 } while (len > 0);
2382
2383done:
2384#ifdef DEBUG_INPUT
2385 xmlGenericError(xmlGenericErrorContext,
2386 "I/O: wrote %d chars\n", written);
2387#endif
2388 return(written);
2389}
2390
2391/**
2392 * xmlOutputBufferWriteString:
2393 * @out: a buffered parser output
2394 * @str: a zero terminated C string
2395 *
2396 * Write the content of the string in the output I/O buffer
2397 * This routine handle the I18N transcoding from internal UTF-8
2398 * The buffer is lossless, i.e. will store in case of partial
2399 * or delayed writes.
2400 *
2401 * Returns the number of chars immediately written, or -1
2402 * in case of error.
2403 */
2404int
2405xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
2406 int len;
2407
2408 if (str == NULL)
2409 return(-1);
2410 len = strlen(str);
2411
2412 if (len > 0)
2413 return(xmlOutputBufferWrite(out, len, str));
2414 return(len);
2415}
2416
2417/**
2418 * xmlOutputBufferFlush:
2419 * @out: a buffered output
2420 *
2421 * flushes the output I/O channel
2422 *
2423 * Returns the number of byte written or -1 in case of error.
2424 */
2425int
2426xmlOutputBufferFlush(xmlOutputBufferPtr out) {
2427 int nbchars = 0, ret = 0;
2428
2429 /*
2430 * first handle encoding stuff.
2431 */
2432 if ((out->conv != NULL) && (out->encoder != NULL)) {
2433 /*
2434 * convert as much as possible to the parser reading buffer.
2435 */
2436 nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2437 if (nbchars < 0) {
2438 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002439 "xmlOutputBufferFlush: encoder error\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002440 return(-1);
2441 }
2442 }
2443
2444 /*
2445 * second flush the stuff to the I/O channel
2446 */
2447 if ((out->conv != NULL) && (out->encoder != NULL) &&
2448 (out->writecallback != NULL)) {
2449 ret = out->writecallback(out->context,
2450 (const char *)out->conv->content, out->conv->use);
2451 if (ret >= 0)
2452 xmlBufferShrink(out->conv, ret);
2453 } else if (out->writecallback != NULL) {
2454 ret = out->writecallback(out->context,
2455 (const char *)out->buffer->content, out->buffer->use);
2456 if (ret >= 0)
2457 xmlBufferShrink(out->buffer, ret);
2458 }
2459 if (ret < 0) {
2460 xmlGenericError(xmlGenericErrorContext,
2461 "I/O: error %d flushing %d bytes\n", ret, nbchars);
2462 return(ret);
2463 }
2464 out->written += ret;
2465
2466#ifdef DEBUG_INPUT
2467 xmlGenericError(xmlGenericErrorContext,
2468 "I/O: flushed %d chars\n", ret);
2469#endif
2470 return(ret);
2471}
2472
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002473/**
Owen Taylor3473f882001-02-23 17:55:21 +00002474 * xmlParserGetDirectory:
2475 * @filename: the path to a file
2476 *
2477 * lookup the directory for that file
2478 *
2479 * Returns a new allocated string containing the directory, or NULL.
2480 */
2481char *
2482xmlParserGetDirectory(const char *filename) {
2483 char *ret = NULL;
2484 char dir[1024];
2485 char *cur;
2486 char sep = '/';
2487
2488 if (xmlInputCallbackInitialized == 0)
2489 xmlRegisterDefaultInputCallbacks();
2490
2491 if (filename == NULL) return(NULL);
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002492#if defined(WIN32) && !defined(__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +00002493 sep = '\\';
2494#endif
2495
2496 strncpy(dir, filename, 1023);
2497 dir[1023] = 0;
2498 cur = &dir[strlen(dir)];
2499 while (cur > dir) {
2500 if (*cur == sep) break;
2501 cur --;
2502 }
2503 if (*cur == sep) {
2504 if (cur == dir) dir[1] = 0;
2505 else *cur = 0;
2506 ret = xmlMemStrdup(dir);
2507 } else {
2508 if (getcwd(dir, 1024) != NULL) {
2509 dir[1023] = 0;
2510 ret = xmlMemStrdup(dir);
2511 }
2512 }
2513 return(ret);
2514}
2515
2516/****************************************************************
2517 * *
2518 * External entities loading *
2519 * *
2520 ****************************************************************/
2521
Daniel Veillard561b7f82002-03-20 21:55:57 +00002522#ifdef LIBXML_CATALOG_ENABLED
2523static int xmlSysIDExists(const char *URL) {
Daniel Veillard6990bf32001-08-23 21:17:48 +00002524#ifdef HAVE_STAT
2525 int ret;
2526 struct stat info;
2527 const char *path;
2528
2529 if (URL == NULL)
2530 return(0);
2531
Daniel Veillardf4862f02002-09-10 11:13:43 +00002532 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2533#if defined (_WIN32) && !defined(__CYGWIN__)
2534 path = &URL[17];
2535#else
Daniel Veillard6990bf32001-08-23 21:17:48 +00002536 path = &URL[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +00002537#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002538 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002539#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard6990bf32001-08-23 21:17:48 +00002540 path = &URL[8];
2541#else
2542 path = &URL[7];
2543#endif
2544 } else
2545 path = URL;
2546 ret = stat(path, &info);
Daniel Veillard561b7f82002-03-20 21:55:57 +00002547 if (ret == 0)
2548 return(1);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002549#endif
Daniel Veillard561b7f82002-03-20 21:55:57 +00002550 return(0);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002551}
Daniel Veillard561b7f82002-03-20 21:55:57 +00002552#endif
Daniel Veillard6990bf32001-08-23 21:17:48 +00002553
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002554/**
Owen Taylor3473f882001-02-23 17:55:21 +00002555 * xmlDefaultExternalEntityLoader:
2556 * @URL: the URL for the entity to load
2557 * @ID: the System ID for the entity to load
2558 * @ctxt: the context in which the entity is called or NULL
2559 *
2560 * By default we don't load external entitites, yet.
2561 *
2562 * Returns a new allocated xmlParserInputPtr, or NULL.
2563 */
2564static
2565xmlParserInputPtr
2566xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
2567 xmlParserCtxtPtr ctxt) {
2568 xmlParserInputPtr ret = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002569 xmlChar *resource = NULL;
2570#ifdef LIBXML_CATALOG_ENABLED
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002571 xmlCatalogAllow pref;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002572#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002573
2574#ifdef DEBUG_EXTERNAL_ENTITIES
2575 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00002576 "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +00002577#endif
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002578#ifdef LIBXML_CATALOG_ENABLED
2579 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002580 * If the resource doesn't exists as a file,
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002581 * try to load it from the resource pointed in the catalogs
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002582 */
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002583 pref = xmlCatalogGetDefaults();
2584
Daniel Veillard561b7f82002-03-20 21:55:57 +00002585 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002586 /*
2587 * Do a local lookup
2588 */
2589 if ((ctxt->catalogs != NULL) &&
2590 ((pref == XML_CATA_ALLOW_ALL) ||
2591 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2592 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2593 (const xmlChar *)ID,
2594 (const xmlChar *)URL);
2595 }
2596 /*
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002597 * Try a global lookup
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002598 */
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002599 if ((resource == NULL) &&
2600 ((pref == XML_CATA_ALLOW_ALL) ||
2601 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002602 resource = xmlCatalogResolve((const xmlChar *)ID,
2603 (const xmlChar *)URL);
2604 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002605 if ((resource == NULL) && (URL != NULL))
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002606 resource = xmlStrdup((const xmlChar *) URL);
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002607
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002608 /*
2609 * TODO: do an URI lookup on the reference
2610 */
Daniel Veillard561b7f82002-03-20 21:55:57 +00002611 if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002612 xmlChar *tmp = NULL;
2613
2614 if ((ctxt->catalogs != NULL) &&
2615 ((pref == XML_CATA_ALLOW_ALL) ||
2616 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2617 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2618 }
2619 if ((tmp == NULL) &&
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002620 ((pref == XML_CATA_ALLOW_ALL) ||
2621 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002622 tmp = xmlCatalogResolveURI(resource);
2623 }
2624
2625 if (tmp != NULL) {
2626 xmlFree(resource);
2627 resource = tmp;
2628 }
2629 }
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002630 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002631#endif
2632
2633 if (resource == NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002634 resource = (xmlChar *) URL;
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002635
2636 if (resource == NULL) {
Daniel Veillardc6613042002-03-02 09:34:02 +00002637 if (ID == NULL)
2638 ID = "NULL";
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002639 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2640 (ctxt->sax->error != NULL))
2641 ctxt->sax->error(ctxt,
2642 "failed to load external entity \"%s\"\n", ID);
2643 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002644 ctxt->sax->warning(ctxt,
2645 "failed to load external entity \"%s\"\n", ID);
2646 return(NULL);
2647 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002648 ret = xmlNewInputFromFile(ctxt, (const char *)resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002649 if (ret == NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002650 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2651 (ctxt->sax->error != NULL))
2652 ctxt->sax->error(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002653 "failed to load external entity \"%s\"\n", resource);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002654 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002655 ctxt->sax->warning(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002656 "failed to load external entity \"%s\"\n", resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002657 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002658 if ((resource != NULL) && (resource != (xmlChar *) URL))
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002659 xmlFree(resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002660 return(ret);
2661}
2662
2663static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
2664 xmlDefaultExternalEntityLoader;
2665
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002666/**
Owen Taylor3473f882001-02-23 17:55:21 +00002667 * xmlSetExternalEntityLoader:
2668 * @f: the new entity resolver function
2669 *
2670 * Changes the defaultexternal entity resolver function for the application
2671 */
2672void
2673xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
2674 xmlCurrentExternalEntityLoader = f;
2675}
2676
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002677/**
Owen Taylor3473f882001-02-23 17:55:21 +00002678 * xmlGetExternalEntityLoader:
2679 *
2680 * Get the default external entity resolver function for the application
2681 *
2682 * Returns the xmlExternalEntityLoader function pointer
2683 */
2684xmlExternalEntityLoader
2685xmlGetExternalEntityLoader(void) {
2686 return(xmlCurrentExternalEntityLoader);
2687}
2688
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002689/**
Owen Taylor3473f882001-02-23 17:55:21 +00002690 * xmlLoadExternalEntity:
2691 * @URL: the URL for the entity to load
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002692 * @ID: the Public ID for the entity to load
Owen Taylor3473f882001-02-23 17:55:21 +00002693 * @ctxt: the context in which the entity is called or NULL
2694 *
2695 * Load an external entity, note that the use of this function for
2696 * unparsed entities may generate problems
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002697 * TODO: a more generic External entity API must be designed
Owen Taylor3473f882001-02-23 17:55:21 +00002698 *
2699 * Returns the xmlParserInputPtr or NULL
2700 */
2701xmlParserInputPtr
2702xmlLoadExternalEntity(const char *URL, const char *ID,
2703 xmlParserCtxtPtr ctxt) {
2704 return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
2705}
2706
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002707/************************************************************************
2708 * *
2709 * Disabling Network access *
2710 * *
2711 ************************************************************************/
2712
2713#ifdef LIBXML_CATALOG_ENABLED
2714static int
2715xmlNoNetExists(const char *URL)
2716{
2717#ifdef HAVE_STAT
2718 int ret;
2719 struct stat info;
2720 const char *path;
2721
2722 if (URL == NULL)
2723 return (0);
2724
Daniel Veillardf4862f02002-09-10 11:13:43 +00002725 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2726#if defined (_WIN32) && !defined(__CYGWIN__)
2727 path = &URL[17];
2728#else
2729 path = &URL[16];
2730#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002731 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002732#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002733 path = &URL[8];
2734#else
2735 path = &URL[7];
2736#endif
2737 } else
2738 path = URL;
2739 ret = stat(path, &info);
2740 if (ret == 0)
2741 return (1);
2742#endif
2743 return (0);
2744}
2745#endif
2746
2747/**
2748 * xmlNoNetExternalEntityLoader:
2749 * @URL: the URL for the entity to load
2750 * @ID: the System ID for the entity to load
2751 * @ctxt: the context in which the entity is called or NULL
2752 *
2753 * A specific entity loader disabling network accesses, though still
2754 * allowing local catalog accesses for resolution.
2755 *
2756 * Returns a new allocated xmlParserInputPtr, or NULL.
2757 */
2758xmlParserInputPtr
2759xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
2760 xmlParserCtxtPtr ctxt) {
2761 xmlParserInputPtr input = NULL;
2762 xmlChar *resource = NULL;
2763
2764#ifdef LIBXML_CATALOG_ENABLED
2765 xmlCatalogAllow pref;
2766
2767 /*
2768 * If the resource doesn't exists as a file,
2769 * try to load it from the resource pointed in the catalogs
2770 */
2771 pref = xmlCatalogGetDefaults();
2772
2773 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
2774 /*
2775 * Do a local lookup
2776 */
2777 if ((ctxt->catalogs != NULL) &&
2778 ((pref == XML_CATA_ALLOW_ALL) ||
2779 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2780 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2781 (const xmlChar *)ID,
2782 (const xmlChar *)URL);
2783 }
2784 /*
2785 * Try a global lookup
2786 */
2787 if ((resource == NULL) &&
2788 ((pref == XML_CATA_ALLOW_ALL) ||
2789 (pref == XML_CATA_ALLOW_GLOBAL))) {
2790 resource = xmlCatalogResolve((const xmlChar *)ID,
2791 (const xmlChar *)URL);
2792 }
2793 if ((resource == NULL) && (URL != NULL))
2794 resource = xmlStrdup((const xmlChar *) URL);
2795
2796 /*
2797 * TODO: do an URI lookup on the reference
2798 */
2799 if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
2800 xmlChar *tmp = NULL;
2801
2802 if ((ctxt->catalogs != NULL) &&
2803 ((pref == XML_CATA_ALLOW_ALL) ||
2804 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2805 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2806 }
2807 if ((tmp == NULL) &&
2808 ((pref == XML_CATA_ALLOW_ALL) ||
2809 (pref == XML_CATA_ALLOW_GLOBAL))) {
2810 tmp = xmlCatalogResolveURI(resource);
2811 }
2812
2813 if (tmp != NULL) {
2814 xmlFree(resource);
2815 resource = tmp;
2816 }
2817 }
2818 }
2819#endif
2820 if (resource == NULL)
2821 resource = (xmlChar *) URL;
2822
2823 if (resource != NULL) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002824 if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
2825 (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002826 xmlGenericError(xmlGenericErrorContext,
2827 "Attempt to load network entity %s \n", resource);
2828
2829 if (resource != (xmlChar *) URL)
2830 xmlFree(resource);
2831 return(NULL);
2832 }
2833 }
2834 input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
2835 if (resource != (xmlChar *) URL)
2836 xmlFree(resource);
2837 return(input);
2838}
2839