The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14409 ripley 1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
22776 ripley 3
 *  Copyright (C) 2001-3   The R Development Core Team.
14409 ripley 4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program; if not, write to the Free Software
17
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 */
19
 
20
 
21
/* based on libxml2-2.3.6:
22
 * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
23
 *             focuses on size, streamability, reentrancy and portability
24
 *
25
 * This is clearly not a general purpose HTTP implementation
26
 * If you look for one, check:
27
 *         http://www.w3.org/Library/
28
 *
29
 * See Copyright for the status of this software.
30
 *
31
 * Daniel.Veillard@w3.org
32
 */
33
 
34
 
35
#ifdef HAVE_CONFIG_H
36
#include <config.h>
37
#endif
38
 
15168 pd 39
#if !defined(Unix) || defined(HAVE_BSD_NETWORKING)
40
 
14409 ripley 41
#ifdef Win32
18658 ripley 42
#include <io.h>
43
#include <winsock.h>
44
#define EWOULDBLOCK             WSAEWOULDBLOCK
45
#define EINPROGRESS             WSAEINPROGRESS
46
#define EALREADY                WSAEALREADY
14409 ripley 47
#define _WINSOCKAPI_
18653 ripley 48
extern void R_ProcessEvents(void);
22790 ripley 49
extern void R_FlushConsole(void);
25220 luke 50
#define R_SelectEx(n,rfd,wrd,efd,tv,ih) select(n,rfd,wrd,efd,tv)
14409 ripley 51
#endif
52
 
53
#include <R_ext/R-ftp-http.h>
22786 ripley 54
#include <R_ext/PrtUtil.h>
14409 ripley 55
 
19292 tlumley 56
#ifdef HAVE_STRINGS_H
57
   /* may be needed to define bzero in FD_ZERO (eg AIX) */
58
  #include <strings.h>
59
#endif
60
 
14409 ripley 61
#include <stdio.h>
62
#include <string.h>
63
#include <stdlib.h>
64
 
65
#ifdef HAVE_UNISTD_H
66
#include <unistd.h>
67
#endif
68
 
69
#ifdef HAVE_BSD_NETWORKING
70
#  include <netdb.h>
71
#  include <sys/socket.h>
72
#  include <netinet/in.h>
73
#endif
74
 
75
#ifdef HAVE_FCNTL_H
76
#include <fcntl.h>
77
#endif
78
#ifdef HAVE_ERRNO_H
79
#include <errno.h>
80
#endif
81
#ifdef HAVE_SYS_TIME_H
82
#include <sys/time.h>
83
#endif
84
#ifdef HAVE_SYS_SELECT_H
85
#include <sys/select.h>
86
#endif
87
#ifdef SUPPORT_IP6
88
#include <resolv.h>
89
#endif
90
 
91
/* #define DEBUG_HTTP */
92
 
93
#define BAD_CAST (unsigned char *)
94
 
95
#define xmlFree free
96
#define xmlMalloc malloc
97
#define xmlRealloc realloc
98
#define xmlMemStrdup strdup
99
 
100
static void RxmlNanoHTTPScanProxy(const char *URL);
101
static void *RxmlNanoHTTPMethod(const char *URL, const char *method,
102
				const char *input, char **contentType,
15377 ripley 103
				const char *headers, const int cacheOK);
14409 ripley 104
 
105
#ifdef Unix
106
#include <R_ext/eventloop.h>
107
 
108
/* modified from src/unix/sys-std.c  */
109
static int
110
setSelectMask(InputHandler *handlers, fd_set *readMask)
111
{
112
    int maxfd = -1;
113
    InputHandler *tmp = handlers;
114
    FD_ZERO(readMask);
115
 
116
    while(tmp) {
117
	if(tmp->fileDescriptor > 0) {
118
	    FD_SET(tmp->fileDescriptor, readMask);
119
	    maxfd = maxfd < tmp->fileDescriptor ? tmp->fileDescriptor : maxfd;
120
	}
121
	tmp = tmp->next;
122
    }
123
 
124
    return(maxfd);
125
}
126
#endif
127
 
128
/**
129
 * A couple of portability macros
130
 */
131
#ifndef _WINSOCKAPI_
132
#define closesocket(s) close(s)
133
#define SOCKET int
134
#endif
135
 
18793 ripley 136
/* where strncasecmp is defined seems system-specific,
137
   and on Windows the cross-compiler doesn't find strings.h */
138
#if defined(HAVE_STRINGS_H) && !defined(Win32)
139
# include <strings.h>
18668 ripley 140
#endif
18793 ripley 141
#if defined(HAVE_DECL_STRNCASECMP) || !HAVE_DECL_STRNCASECMP
142
extern int strncasecmp(const char *s1, const char *s2, size_t n);
143
#endif
144
#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
14409 ripley 145
 
146
#define XML_NANO_HTTP_MAX_REDIR	10
147
 
148
#define XML_NANO_HTTP_CHUNK	4096
149
 
150
#define XML_NANO_HTTP_CLOSED	0
151
#define XML_NANO_HTTP_WRITE	1
152
#define XML_NANO_HTTP_READ	2
153
#define XML_NANO_HTTP_NONE	4
154
 
155
typedef struct RxmlNanoHTTPCtxt {
156
    char *protocol;	/* the protocol name */
157
    char *hostname;	/* the host name */
158
    int port;		/* the port */
159
    char *path;		/* the path within the URL */
160
    SOCKET fd;		/* the file descriptor for the socket */
161
    int state;		/* WRITE / READ / CLOSED */
162
    char *out;		/* buffer sent (zero terminated) */
163
    char *outptr;	/* index within the buffer sent */
164
    char *in;		/* the receiving buffer */
165
    char *content;	/* the start of the content */
166
    char *inptr;	/* the next byte to read from network */
167
    char *inrptr;	/* the next byte to give back to the client */
168
    int inlen;		/* len of the input buffer */
169
    int last;		/* return code for last operation */
170
    int returnValue;	/* the protocol return value */
22776 ripley 171
    char *statusMsg;    /* the protocol status message */
14409 ripley 172
    char *contentType;	/* the MIME type for the input */
173
    int contentLength;	/* the reported length */
174
    char *location;	/* the new URL in case of redirect */
175
    char *authHeader;	/* contents of {WWW,Proxy}-Authenticate header */
176
} RxmlNanoHTTPCtxt, *RxmlNanoHTTPCtxtPtr;
177
 
178
static int initialized = 0;
179
static char *proxy = NULL;	 /* the proxy name if any */
180
static int proxyPort;	/* the proxy port if any */
22775 ripley 181
static char *proxyUser; /* the proxy user:pwd if any */
14409 ripley 182
static unsigned int timeout = 60;/* the select() timeout in seconds */
183
 
184
/**
185
 * A portability function
186
 */
187
static int socket_errno(void)
188
{
189
#ifdef _WINSOCKAPI_
190
    return(WSAGetLastError());
191
#else
192
    return(errno);
193
#endif
194
}
195
 
196
/**
197
 * RxmlNanoHTTPInit:
198
 *
199
 * Initialize the HTTP protocol layer.
200
 * Currently it just checks for proxy informations
201
 */
202
 
22785 ripley 203
#ifdef Win32
204
# include "graphapp/graphapp.h"
205
#endif
206
 
14409 ripley 207
static void
208
RxmlNanoHTTPInit(void)
209
{
210
    const char *env;
211
#ifdef _WINSOCKAPI_
212
    WSADATA wsaData;
213
#endif
214
 
215
    if (initialized)
216
	return;
217
 
218
#ifdef _WINSOCKAPI_
219
    if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
220
	return;
221
#endif
222
 
223
    if (proxy == NULL) {
224
	proxyPort = 80;
225
	env = getenv("no_proxy");
226
	if (env != NULL)
227
	    goto done;
228
	env = getenv("http_proxy");
229
	if (env != NULL) {
230
	    RxmlNanoHTTPScanProxy(env);
22724 ripley 231
	    goto chkuser;
14409 ripley 232
	}
233
	env = getenv("HTTP_PROXY");
234
	if (env != NULL) {
235
	    RxmlNanoHTTPScanProxy(env);
22724 ripley 236
	    goto chkuser;
14409 ripley 237
	}
22724 ripley 238
    chkuser:
239
	if((env = getenv("http_proxy_user")) != NULL) {   
240
	    if (proxyUser != NULL) {xmlFree(proxyUser); proxyUser = NULL;}
22785 ripley 241
#ifdef Win32
242
	    if (strcmp(env, "ask") == 0) 
243
		env = askUserPass("Proxy Authentication");
244
#endif
22724 ripley 245
	    proxyUser = xmlMemStrdup(env);
246
	}
14409 ripley 247
    }
248
 done:
249
    initialized = 1;
250
}
251
 
252
/**
253
 * RxmlNanoHTTPClenup:
254
 *
255
 * Cleanup the HTTP protocol layer.
256
 */
257
 
258
void
259
RxmlNanoHTTPCleanup(void)
260
{
22724 ripley 261
    if (proxy != NULL) xmlFree(proxy);
262
    if (proxyUser != NULL) xmlFree(proxyUser);
14409 ripley 263
#ifdef _WINSOCKAPI_
264
    if (initialized)
265
	WSACleanup();
266
#endif
267
    initialized = 0;
268
    return;
269
}
270
 
271
/**
272
 * RxmlNanoHTTPTimeout:
273
 * @delay:  the delay in seconds
274
 *
275
 * Set the HTTP timeout, (default is 60secs).  0 means immediate
276
 * return, while -1 infinite.
277
 */
278
 
279
void
280
RxmlNanoHTTPTimeout(int delay)
281
{
282
    timeout = (unsigned int) delay;
283
}
284
 
285
/**
286
 * RxmlNanoHTTPScanURL:
287
 * @ctxt:  an HTTP context
288
 * @URL:  The URL used to initialize the context
289
 *
290
 * (Re)Initialize an HTTP context by parsing the URL and finding
291
 * the protocol host port and path it indicates.
292
 */
293
 
294
static void
295
RxmlNanoHTTPScanURL(RxmlNanoHTTPCtxtPtr ctxt, const char *URL)
296
{
297
    const char *cur = URL;
298
    char buf[4096];
299
    int indx = 0;
300
    int port = 0;
301
 
302
    if (ctxt->protocol != NULL) {
303
        xmlFree(ctxt->protocol);
304
	ctxt->protocol = NULL;
305
    }
306
    if (ctxt->hostname != NULL) {
307
        xmlFree(ctxt->hostname);
308
	ctxt->hostname = NULL;
309
    }
310
    if (ctxt->path != NULL) {
311
        xmlFree(ctxt->path);
312
	ctxt->path = NULL;
313
    }
314
    if (URL == NULL) return;
315
    buf[indx] = 0;
316
    while (*cur != 0) {
317
        if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
318
	    buf[indx] = 0;
319
	    ctxt->protocol = xmlMemStrdup(buf);
320
	    indx = 0;
321
            cur += 3;
322
	    break;
323
	}
324
	buf[indx++] = *cur++;
325
    }
326
    if (*cur == 0) return;
327
 
328
    buf[indx] = 0;
329
    while (1) {
330
        if (cur[0] == ':') {
331
	    buf[indx] = 0;
332
	    ctxt->hostname = xmlMemStrdup(buf);
333
	    indx = 0;
334
	    cur += 1;
335
	    while ((*cur >= '0') && (*cur <= '9')) {
336
	        port *= 10;
337
		port += *cur - '0';
338
		cur++;
339
	    }
340
	    if (port != 0) ctxt->port = port;
341
	    while ((cur[0] != '/') && (*cur != 0))
342
	        cur++;
343
	    break;
344
	}
345
        if ((*cur == '/') || (*cur == 0)) {
346
	    buf[indx] = 0;
347
	    ctxt->hostname = xmlMemStrdup(buf);
348
	    indx = 0;
349
	    break;
350
	}
351
	buf[indx++] = *cur++;
352
    }
353
    if (*cur == 0)
354
        ctxt->path = xmlMemStrdup("/");
355
    else {
356
        indx = 0;
357
        buf[indx] = 0;
358
	while (*cur != 0)
359
	    buf[indx++] = *cur++;
360
	buf[indx] = 0;
361
	ctxt->path = xmlMemStrdup(buf);
362
    }
363
}
364
 
365
/**
366
 * RxmlNanoHTTPScanProxy:
367
 * @URL:  The proxy URL used to initialize the proxy context
368
 *
369
 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
370
 * the protocol host port it indicates.
371
 * Should be like http://myproxy/ or http://myproxy:3128/
372
 * A NULL URL cleans up proxy informations.
373
 */
374
 
375
void
376
RxmlNanoHTTPScanProxy(const char *URL)
377
{
378
    const char *cur = URL;
379
    char buf[4096];
380
    int indx = 0;
381
    int port = 0;
382
 
383
    if (proxy != NULL) {
384
        xmlFree(proxy);
385
	proxy = NULL;
386
    }
20167 ripley 387
    /*if (proxyPort != 0) {
14409 ripley 388
	proxyPort = 0;
20167 ripley 389
	}*/
14409 ripley 390
    if (URL == NULL)
391
	RxmlMessage(0, "Removing HTTP proxy info");
392
    else
393
	RxmlMessage(1, "Using HTTP proxy %s", URL);
394
    if (URL == NULL) return;
395
    buf[indx] = 0;
396
    while (*cur != 0) {
397
        if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
398
	    buf[indx] = 0;
399
	    indx = 0;
400
            cur += 3;
401
	    break;
402
	}
403
	buf[indx++] = *cur++;
404
    }
405
    if (*cur == 0) return;
406
 
22775 ripley 407
    if(strchr(cur, '@')) {
408
	strcpy(buf, cur);
409
	*(strchr(buf, '@')) = '\0';
410
	if(proxyUser) xmlFree(proxyUser);
411
	proxyUser = xmlMemStrdup(buf);
412
	cur += strlen(buf) + 1;
413
    }
14409 ripley 414
    buf[indx] = 0;
415
    while (1) {
416
        if (cur[0] == ':') {
417
	    buf[indx] = 0;
418
	    proxy = xmlMemStrdup(buf);
419
	    indx = 0;
420
	    cur += 1;
421
	    while ((*cur >= '0') && (*cur <= '9')) {
422
	        port *= 10;
423
		port += *cur - '0';
424
		cur++;
425
	    }
426
	    if (port != 0) proxyPort = port;
427
	    while ((cur[0] != '/') && (*cur != 0))
428
	        cur++;
429
	    break;
430
	}
431
        if ((*cur == '/') || (*cur == 0)) {
432
	    buf[indx] = 0;
433
	    proxy = xmlMemStrdup(buf);
434
	    indx = 0;
435
	    break;
436
	}
437
	buf[indx++] = *cur++;
438
    }
439
}
440
 
441
/**
442
 * RxmlNanoHTTPNewCtxt:
443
 * @URL:  The URL used to initialize the context
444
 *
445
 * Allocate and initialize a new HTTP context.
446
 *
447
 * Returns an HTTP context or NULL in case of error.
448
 */
449
 
450
static RxmlNanoHTTPCtxtPtr
451
RxmlNanoHTTPNewCtxt(const char *URL)
452
{
453
    RxmlNanoHTTPCtxtPtr ret;
454
 
455
    ret = (RxmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(RxmlNanoHTTPCtxt));
456
    if (ret == NULL) return(NULL);
457
 
458
    memset(ret, 0, sizeof(RxmlNanoHTTPCtxt));
459
    ret->port = 80;
460
    ret->returnValue = 0;
22776 ripley 461
    ret->statusMsg = NULL;
14409 ripley 462
    ret->contentLength = -1;
463
    ret->fd = -1;
464
 
465
    RxmlNanoHTTPScanURL(ret, URL);
466
 
467
    return(ret);
468
}
469
 
470
/**
471
 * RxmlNanoHTTPFreeCtxt:
472
 * @ctxt:  an HTTP context
473
 *
474
 * Frees the context after closing the connection.
475
 */
476
 
477
static void
478
RxmlNanoHTTPFreeCtxt(RxmlNanoHTTPCtxtPtr ctxt)
479
{
480
    if (ctxt == NULL) return;
481
    if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
482
    if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
483
    if (ctxt->path != NULL) xmlFree(ctxt->path);
484
    if (ctxt->out != NULL) xmlFree(ctxt->out);
485
    if (ctxt->in != NULL) xmlFree(ctxt->in);
486
    if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
487
    if (ctxt->location != NULL) xmlFree(ctxt->location);
488
    if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
489
    ctxt->state = XML_NANO_HTTP_NONE;
490
    if (ctxt->fd >= 0) closesocket(ctxt->fd);
491
    ctxt->fd = -1;
492
    xmlFree(ctxt);
493
}
494
 
495
/**
496
 * RxmlNanoHTTPSend:
497
 * @ctxt:  an HTTP context
498
 *
499
 * Send the input needed to initiate the processing on the server side
500
 */
501
 
502
static void
503
RxmlNanoHTTPSend(RxmlNanoHTTPCtxtPtr ctxt)
504
{
505
    if (ctxt->state & XML_NANO_HTTP_WRITE) {
506
        unsigned int total_sent = 0;
507
        while (total_sent <strlen(ctxt->outptr)) {
508
            unsigned int nsent = send(ctxt->fd, ctxt->outptr+total_sent,
509
                                      strlen(ctxt->outptr)-total_sent, 0);
510
            if (nsent>0)
511
                total_sent += nsent;
512
	}
513
        ctxt->last = total_sent;
514
    }
515
}
516
 
517
/**
518
 * RxmlNanoHTTPRecv:
519
 * @ctxt:  an HTTP context
520
 *
521
 * Read information coming from the HTTP connection.
522
 * This is a blocking call (but it blocks in select(), not read()).
523
 *
524
 * Returns the number of byte read or -1 in case of error.
525
 */
526
 
527
static int
528
RxmlNanoHTTPRecv(RxmlNanoHTTPCtxtPtr ctxt)
529
{
530
    fd_set rfd;
531
    struct timeval tv;
532
    double used = 0.0;
533
 
534
 
535
    if (ctxt->state & XML_NANO_HTTP_READ) {
536
	if (ctxt->in == NULL) {
537
	    ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
538
	    if (ctxt->in == NULL) {
539
	        ctxt->last = -1;
540
		return(-1);
541
	    }
542
	    ctxt->inlen = 65000;
543
	    ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
544
	}
545
	if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
546
	    int delta = ctxt->inrptr - ctxt->in;
547
	    int len = ctxt->inptr - ctxt->inrptr;
548
 
549
	    memmove(ctxt->in, ctxt->inrptr, len);
550
	    ctxt->inrptr -= delta;
551
	    ctxt->content -= delta;
552
	    ctxt->inptr -= delta;
553
	}
554
        if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
555
	    int d_inptr = ctxt->inptr - ctxt->in;
556
	    int d_content = ctxt->content - ctxt->in;
557
	    int d_inrptr = ctxt->inrptr - ctxt->in;
558
 
559
	    ctxt->inlen *= 2;
560
            ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
561
	    if (ctxt->in == NULL) {
562
	        ctxt->last = -1;
563
		return(-1);
564
	    }
565
            ctxt->inptr = ctxt->in + d_inptr;
566
            ctxt->content = ctxt->in + d_content;
567
            ctxt->inrptr = ctxt->in + d_inrptr;
568
	}
569
 
570
	while(1) {
571
	    int maxfd = 0, howmany;
572
#ifdef Unix
573
	    InputHandler *what;
574
 
575
	    if(R_wait_usec > 0) {
576
		R_PolledEvents();
577
		tv.tv_sec = 0;
578
		tv.tv_usec = R_wait_usec;
579
	    } else {
580
		tv.tv_sec = timeout;
581
		tv.tv_usec = 0;
582
	    }
583
#elif defined(Win32)
584
	    tv.tv_sec = 0;
585
	    tv.tv_usec = 2e5;
586
	    R_ProcessEvents();
587
#else
588
	    tv.tv_sec = timeout;
589
	    tv.tv_usec = 0;
590
#endif
591
 
592
 
593
#ifdef Unix
594
	    maxfd = setSelectMask(R_InputHandlers, &rfd);
595
#else
596
	    FD_ZERO(&rfd);
597
#endif
598
	    FD_SET(ctxt->fd, &rfd);
599
	    if(maxfd < ctxt->fd) maxfd = ctxt->fd;
600
 
25220 luke 601
	    howmany = R_SelectEx(maxfd+1, &rfd, NULL, NULL, &tv, NULL);
14409 ripley 602
 
603
	    if (howmany < 0) {
604
#ifdef DEBUG_HTTP
605
		perror("select in RxmlNanoHTTPRecv");
606
#endif
607
		return(0);
608
	    }
609
	    if (howmany == 0) {
610
		used += tv.tv_sec + 1e-6 * tv.tv_usec;
611
		if(used >= timeout) return(0);
612
		continue;
613
	    }
614
 
615
#ifdef Unix
616
	    if(!FD_ISSET(ctxt->fd, &rfd) || howmany > 1) {
617
		/* was one of the extras */
618
		what = getSelectedHandler(R_InputHandlers, &rfd);
619
		if(what != NULL) what->handler((void*) NULL);
620
		continue;
621
	    }
622
#endif
623
 
624
	    /* was the socket */
625
	    ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
626
	    if (ctxt->last > 0) {
627
		ctxt->inptr += ctxt->last;
628
		return(ctxt->last);
629
	    }
630
	    if (ctxt->last == 0) {
631
		return(0);
632
	    }
633
	    if (ctxt->last == -1) {
634
		switch (socket_errno()) {
635
		case EINPROGRESS:
636
		case EWOULDBLOCK:
637
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
638
		case EAGAIN:
639
#endif
640
		    break;
641
		default:
642
		    return(0);
643
		}
644
	    }
645
	}
646
    }
647
 
648
    return(0);
649
}
650
 
651
/**
652
 * RxmlNanoHTTPReadLine:
653
 * @ctxt:  an HTTP context
654
 *
655
 * Read one line in the HTTP server output, usually for extracting
656
 * the HTTP protocol informations from the answer header.
657
 *
658
 * Returns a newly allocated string with a copy of the line, or NULL
659
 *         which indicate the end of the input.
660
 */
661
 
662
static char *
663
RxmlNanoHTTPReadLine(RxmlNanoHTTPCtxtPtr ctxt)
664
{
665
    char buf[4096];
666
    char *bp = buf;
667
 
668
    while (bp - buf < 4095) {
669
	if (ctxt->inrptr == ctxt->inptr) {
670
	    if (RxmlNanoHTTPRecv(ctxt) == 0) {
671
		if (bp == buf)
672
		    return(NULL);
673
		else
674
		    *bp = 0;
675
		return(xmlMemStrdup(buf));
676
	    }
677
	}
678
	*bp = *ctxt->inrptr++;
679
	if (*bp == '\n') {
680
	    *bp = 0;
681
	    return(xmlMemStrdup(buf));
682
	}
683
	if (*bp != '\r')
684
	    bp++;
685
    }
686
    buf[4095] = 0;
687
    return(xmlMemStrdup(buf));
688
}
689
 
690
 
691
/**
692
 * RxmlNanoHTTPScanAnswer:
693
 * @ctxt:  an HTTP context
694
 * @line:  an HTTP header line
695
 *
696
 * Try to extract useful informations from the server answer.
697
 * We currently parse and process:
698
 *  - The HTTP revision/ return code
699
 *  - The Content-Type
700
 *  - The Location for redirrect processing.
701
 *
702
 * Returns -1 in case of failure, the file descriptor number otherwise
703
 */
704
 
705
static void
706
RxmlNanoHTTPScanAnswer(RxmlNanoHTTPCtxtPtr ctxt, const char *line)
707
{
708
    const char *cur = line;
709
 
710
    if (line == NULL) return;
711
 
712
    if (!strncmp(line, "HTTP/", 5)) {
713
        int version = 0;
714
	int ret = 0;
715
 
716
	cur += 5;
717
	while ((*cur >= '0') && (*cur <= '9')) {
718
	    version *= 10;
719
	    version += *cur - '0';
720
	    cur++;
721
	}
722
	if (*cur == '.') {
723
	    cur++;
724
	    if ((*cur >= '0') && (*cur <= '9')) {
725
		version *= 10;
726
		version += *cur - '0';
727
		cur++;
728
	    }
729
	    while ((*cur >= '0') && (*cur <= '9'))
730
		cur++;
731
	} else
732
	    version *= 10;
733
	if ((*cur != ' ') && (*cur != '\t')) return;
734
	while ((*cur == ' ') || (*cur == '\t')) cur++;
735
	if ((*cur < '0') || (*cur > '9')) return;
736
	while ((*cur >= '0') && (*cur <= '9')) {
737
	    ret *= 10;
738
	    ret += *cur - '0';
739
	    cur++;
740
	}
741
	if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
742
	ctxt->returnValue = ret;
22776 ripley 743
	if ((*cur == ' ') || (*cur == '\t')) cur++;
744
	if(ctxt->statusMsg) xmlFree(ctxt->statusMsg);
745
	ctxt->statusMsg = xmlMemStrdup(cur);
14409 ripley 746
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
747
        cur += 13;
748
	while ((*cur == ' ') || (*cur == '\t')) cur++;
749
	if (ctxt->contentType != NULL)
750
	    xmlFree(ctxt->contentType);
751
	ctxt->contentType = xmlMemStrdup(cur);
752
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
753
        cur += 12;
754
	if (ctxt->contentType != NULL) return;
755
	while ((*cur == ' ') || (*cur == '\t')) cur++;
756
	ctxt->contentType = xmlMemStrdup(cur);
757
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Length:", 15)) {
758
        cur += 15;
759
	while ((*cur == ' ') || (*cur == '\t')) cur++;
760
	ctxt->contentLength = atoi(cur);
761
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
762
        cur += 9;
763
	while ((*cur == ' ') || (*cur == '\t')) cur++;
764
	if (ctxt->location != NULL)
765
	    xmlFree(ctxt->location);
766
	ctxt->location = xmlMemStrdup(cur);
767
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
768
        cur += 17;
769
	while ((*cur == ' ') || (*cur == '\t')) cur++;
770
	if (ctxt->authHeader != NULL)
771
	    xmlFree(ctxt->authHeader);
772
	ctxt->authHeader = xmlMemStrdup(cur);
773
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
774
        cur += 19;
775
	while ((*cur == ' ') || (*cur == '\t')) cur++;
776
	if (ctxt->authHeader != NULL)
777
	    xmlFree(ctxt->authHeader);
778
	ctxt->authHeader = xmlMemStrdup(cur);
779
    }
780
}
781
 
782
/**
783
 * RxmlNanoHTTPConnectAttempt:
784
 * @addr:  a socket address structure
785
 *
786
 * Attempt a connection to the given IP:port endpoint. It forces
787
 * non-blocking semantic on the socket, and allow 60 seconds for
788
 * the host to answer.
789
 *
790
 * Returns -1 in case of failure, the file descriptor number otherwise
791
 */
792
 
793
static int
794
RxmlNanoHTTPConnectAttempt(struct sockaddr *addr)
795
{
796
    SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
797
    fd_set wfd, rfd;
798
    struct timeval tv;
18658 ripley 799
    int status = 0;
14409 ripley 800
    double used = 0.0;
801
 
802
    if (s==-1) {
803
#ifdef DEBUG_HTTP
804
	perror("socket");
805
#endif
806
	return(-1);
807
    }
808
 
809
#ifdef _WINSOCKAPI_
810
    {
811
	u_long one = 1;
812
 
813
	status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
814
    }
815
#else /* _WINSOCKAPI_ */
816
#if defined(VMS)
817
    {
818
	int enable = 1;
819
	status = ioctl(s, FIONBIO, &enable);
820
    }
821
#else /* VMS */
18658 ripley 822
#ifdef HAVE_FCNTL
14409 ripley 823
    if ((status = fcntl(s, F_GETFL, 0)) != -1) {
824
#ifdef O_NONBLOCK
825
	status |= O_NONBLOCK;
826
#else /* O_NONBLOCK */
827
#ifdef F_NDELAY
828
	status |= F_NDELAY;
829
#endif /* F_NDELAY */
830
#endif /* !O_NONBLOCK */
831
	status = fcntl(s, F_SETFL, status);
832
    }
18658 ripley 833
#endif
14409 ripley 834
    if (status < 0) {
835
#ifdef DEBUG_HTTP
836
	perror("nonblocking");
837
#endif
838
	closesocket(s);
839
	return(-1);
840
    }
841
#endif /* !VMS */
842
#endif /* !_WINSOCKAPI_ */
843
 
844
 
845
    if ((connect(s, addr, sizeof(*addr))==-1)) {
846
 
847
	switch (socket_errno()) {
848
	case EINPROGRESS:
849
	case EWOULDBLOCK:
850
	    break;
851
	default:
852
	    perror("connect");
853
	    closesocket(s);
854
	    return(-1);
855
	}
856
    }
857
 
858
    while(1) {
859
	int maxfd = 0;
860
#ifdef Unix
861
	InputHandler *what;
862
 
863
	if(R_wait_usec > 0) {
864
	    R_PolledEvents();
865
	    tv.tv_sec = 0;
866
	    tv.tv_usec = R_wait_usec;
867
	} else {
868
	    tv.tv_sec = timeout;
869
	    tv.tv_usec = 0;
870
	}
871
#elif defined(Win32)
872
	tv.tv_sec = 0;
873
	tv.tv_usec = 2e5;
874
	R_ProcessEvents();
875
#else
876
	tv.tv_sec = timeout;
877
	tv.tv_usec = 0;
878
#endif
879
 
880
 
881
#ifdef Unix
882
	maxfd = setSelectMask(R_InputHandlers, &rfd);
883
#else
884
	FD_ZERO(&rfd);
885
#endif
886
	FD_ZERO(&wfd);
887
	FD_SET(s, &wfd);
888
	if(maxfd < s) maxfd = s;
889
 
25220 luke 890
	switch(R_SelectEx(maxfd+1, &rfd, &wfd, NULL, &tv, NULL))
14409 ripley 891
	{
892
	case 0:
893
	    /* Time out */
894
	    used += tv.tv_sec + 1e-6 * tv.tv_usec;
895
	    if(used < timeout) continue;
896
	    closesocket(s);
897
	    return(-1);
898
	case -1:
899
	    /* Ermm.. ?? */
900
#ifdef DEBUG_HTTP
901
	    perror("select");
902
#endif
903
	    closesocket(s);
904
	    return(-1);
905
	}
906
 
907
	if ( FD_ISSET(s, &wfd) ) {
908
	    SOCKLEN_T len;
909
	    len = sizeof(status);
910
	    if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0){
911
		/* Solaris error code */
912
		return (-1);
913
	    }
914
	    if ( status ) {
915
		closesocket(s);
916
		errno = status;
917
		return (-1);
918
	    } else return(s);
919
#ifdef Unix
920
	} else { /* some other handler needed */
921
	    what = getSelectedHandler(R_InputHandlers, &rfd);
922
	    if(what != NULL) what->handler((void*) NULL);
923
	    continue;
924
#endif
925
	}
926
    }
927
    /* not reached */
928
    return(-1);
929
}
930
 
931
/**
932
 * RxmlNanoHTTPConnectHost:
933
 * @host:  the host name
934
 * @port:  the port number
935
 *
936
 * Attempt a connection to the given host:port endpoint. It tries
937
 * the multiple IP provided by the DNS if available.
938
 *
939
 * Returns -1 in case of failure, the file descriptor number otherwise
940
 */
941
 
942
static int
943
RxmlNanoHTTPConnectHost(const char *host, int port)
944
{
945
    struct hostent *h;
946
    struct sockaddr *addr;
947
    struct in_addr ia;
948
    struct sockaddr_in sockin;
949
#ifdef SUPPORT_IP6
950
    struct in6_addr ia6;
951
    struct sockaddr_in6 sockin6;
952
#endif
953
    int i;
954
    int s;
955
 
956
#if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
957
    if (!(_res.options & RES_INIT))
958
	res_init();
959
    _res.options |= RES_USE_INET6;
960
#endif
961
    h=gethostbyname(host);
962
    if (h==NULL)
963
    {
964
	RxmlMessage(2, "unable to resolve '%s'.", host);
965
	return(-1);
966
    }
967
 
968
    for(i=0; h->h_addr_list[i]; i++)
969
    {
970
	if (h->h_addrtype == AF_INET) {
971
	    /* A records (IPv4) */
972
	    memcpy(&ia, h->h_addr_list[i], h->h_length);
973
	    sockin.sin_family = h->h_addrtype;
974
	    sockin.sin_addr   = ia;
975
	    sockin.sin_port   = htons(port);
976
	    addr = (struct sockaddr *)&sockin;
977
#ifdef SUPPORT_IP6
978
	} else if (h->h_addrtype == AF_INET6) {
979
	    /* AAAA records (IPv6) */
980
	    memcpy(&ia6, h->h_addr_list[i], h->h_length);
981
	    sockin6.sin_family = h->h_addrtype;
982
	    sockin6.sin_addr   = ia6;
983
	    sockin6.sin_port   = htons(port);
984
	    addr = (struct sockaddr *)&sockin6;
985
#endif
986
	} else
987
	    break; /* for */
988
 
989
	s = RxmlNanoHTTPConnectAttempt(addr);
22775 ripley 990
	if (s != -1) {
23246 ripley 991
	    RxmlMessage(1, "connected to '%s' on port %d.", host, port);
14409 ripley 992
	    return(s);
22775 ripley 993
	}
14409 ripley 994
    }
995
 
22775 ripley 996
    RxmlMessage(2, "unable to connect to '%s' on port %d.", host, port);
14409 ripley 997
    return(-1);
998
}
999
 
1000
 
1001
/**
1002
 * RxmlNanoHTTPOpen:
1003
 * @URL:  The URL to load
1004
 * @contentType:  if available the Content-Type information will be
1005
 *                returned at that location
1006
 *
1007
 * This function try to open a connection to the indicated resource
1008
 * via HTTP GET.
1009
 *
1010
 * Returns NULL in case of failure, otherwise a request handler.
1011
 *     The contentType, if provided must be freed by the caller
1012
 */
1013
 
1014
void*
15377 ripley 1015
RxmlNanoHTTPOpen(const char *URL, char **contentType, int cacheOK)
14409 ripley 1016
{
1017
    if (contentType != NULL) *contentType = NULL;
15377 ripley 1018
    return RxmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, cacheOK);
14409 ripley 1019
}
1020
 
1021
/**
1022
 * RxmlNanoHTTPRead:
1023
 * @ctx:  the HTTP context
1024
 * @dest:  a buffer
1025
 * @len:  the buffer length
1026
 *
1027
 * This function tries to read @len bytes from the existing HTTP connection
1028
 * and saves them in @dest. This is a blocking call.
1029
 *
1030
 * Returns the number of byte read. 0 is an indication of an end of connection.
1031
 *         -1 indicates a parameter error.
1032
 */
1033
int
1034
RxmlNanoHTTPRead(void *ctx, void *dest, int len)
1035
{
1036
    RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx;
1037
 
1038
    if (ctx == NULL) return(-1);
1039
    if (dest == NULL) return(-1);
1040
    if (len <= 0) return(0);
1041
 
1042
    while (ctxt->inptr - ctxt->inrptr < len) {
1043
        if (RxmlNanoHTTPRecv(ctxt) == 0) break;
1044
    }
1045
    if (ctxt->inptr - ctxt->inrptr < len)
1046
        len = ctxt->inptr - ctxt->inrptr;
1047
    memcpy(dest, ctxt->inrptr, len);
1048
    ctxt->inrptr += len;
1049
    return(len);
1050
}
1051
 
22775 ripley 1052
static void base64_encode(char *proxyUser, char *out)
22724 ripley 1053
{
1054
    /* Conversion table.  */
1055
    static char tbl[64] = {
1056
	'A','B','C','D','E','F','G','H',
1057
	'I','J','K','L','M','N','O','P',
1058
	'Q','R','S','T','U','V','W','X',
1059
	'Y','Z','a','b','c','d','e','f',
1060
	'g','h','i','j','k','l','m','n',
1061
	'o','p','q','r','s','t','u','v',
1062
	'w','x','y','z','0','1','2','3',
1063
	'4','5','6','7','8','9','+','/'
1064
    };
22775 ripley 1065
    char *s = proxyUser;
22724 ripley 1066
    int i, length;
1067
    unsigned char *p = (unsigned char *)out;
1068
 
1069
    length = strlen(s);
1070
    for (i = 0; i < length; i += 3) {
1071
	*p++ = tbl[s[i] >> 2];
1072
	*p++ = tbl[((s[i] & 3) << 4) + (s[i+1] >> 4)];
1073
	*p++ = tbl[((s[i+1] & 0xf) << 2) + (s[i+2] >> 6)];
1074
	*p++ = tbl[s[i+2] & 0x3f];
1075
    }
1076
    /* Pad the result if necessary...  */
1077
    if (i == length + 1) *(p - 1) = '=';
1078
    else if (i == length + 2) *(p - 1) = *(p - 2) = '=';
1079
    /* ...and zero-terminate it.  */
1080
    *p = '\0';
1081
}
1082
 
14409 ripley 1083
/**
1084
 * RxmlNanoHTTPClose:
1085
 * @ctx:  the HTTP context
1086
 *
1087
 * This function closes an HTTP context, it ends up the connection and
1088
 * free all data related to it.
1089
 */
1090
void
1091
RxmlNanoHTTPClose(void *ctx)
1092
{
1093
    RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx;
1094
 
1095
    if (ctx == NULL) return;
1096
 
1097
    RxmlNanoHTTPFreeCtxt(ctxt);
1098
}
1099
 
1100
/**
1101
 * RxmlNanoHTTPMethod:
1102
 * @URL:  The URL to load
1103
 * @method:  the HTTP method to use
1104
 * @input:  the input string if any
1105
 * @contentType:  the Content-Type information IN and OUT
1106
 * @headers:  the extra headers
1107
 *
1108
 * This function try to open a connection to the indicated resource
1109
 * via HTTP using the given @method, adding the given extra headers
1110
 * and the input buffer for the request content.
1111
 *
1112
 * Returns NULL in case of failure, otherwise a request handler.
1113
 *     The contentType, if provided must be freed by the caller
1114
 */
1115
 
1116
void*
1117
RxmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
15377 ripley 1118
                  char **contentType, const char *headers, const int cacheOK)
14409 ripley 1119
{
1120
    RxmlNanoHTTPCtxtPtr ctxt;
1121
    char *bp, *p;
1122
    int blen, ilen, ret;
1123
    int head;
22786 ripley 1124
    int nbRedirects = 0;
1125
#ifdef Win32
1126
    int nAuthenticate = 0;
1127
#endif
14409 ripley 1128
    char *redirURL = NULL;
22767 ripley 1129
    char buf[1000];
14409 ripley 1130
 
1131
    if (URL == NULL) return(NULL);
1132
    if (method == NULL) method = "GET";
1133
    RxmlNanoHTTPInit();
1134
 
1135
 retry:
1136
    if (redirURL == NULL)
1137
	ctxt = RxmlNanoHTTPNewCtxt(URL);
1138
    else {
1139
	ctxt = RxmlNanoHTTPNewCtxt(redirURL);
1140
	xmlFree(redirURL);
1141
	redirURL = NULL;
1142
    }
1143
 
1144
    if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
1145
        RxmlNanoHTTPFreeCtxt(ctxt);
1146
	if (redirURL != NULL) xmlFree(redirURL);
1147
        return(NULL);
1148
    }
1149
    if (ctxt->hostname == NULL) {
1150
        RxmlNanoHTTPFreeCtxt(ctxt);
1151
        return(NULL);
1152
    }
1153
    if (proxy) {
1154
	blen = strlen(ctxt->hostname) * 2 + 16;
1155
	ret = RxmlNanoHTTPConnectHost(proxy, proxyPort);
1156
    }
1157
    else {
1158
	blen = strlen(ctxt->hostname);
1159
	ret = RxmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1160
    }
1161
    if (ret < 0) {
1162
        RxmlNanoHTTPFreeCtxt(ctxt);
1163
        return(NULL);
1164
    }
1165
    ctxt->fd = ret;
1166
 
1167
    if (input != NULL) {
1168
	ilen = strlen(input);
1169
	blen += ilen + 32;
1170
    }
1171
    else
1172
	ilen = 0;
15377 ripley 1173
    if (!cacheOK)
1174
	blen += 20;
14409 ripley 1175
    if (headers != NULL)
1176
	blen += strlen(headers);
1177
    if (contentType && *contentType)
1178
	blen += strlen(*contentType) + 16;
22775 ripley 1179
    if (proxy && proxyUser) {
1180
	base64_encode(proxyUser,  buf);
22767 ripley 1181
	blen +=strlen(buf) + 50;
1182
    }
14409 ripley 1183
    blen += strlen(method) + strlen(ctxt->path) + 23;
1184
    bp = xmlMalloc(blen);
1185
    if (proxy) {
1186
	if (ctxt->port != 80) {
1187
	    sprintf(bp, "%s http://%s:%d%s", method, ctxt->hostname,
1188
		    ctxt->port, ctxt->path);
1189
	}
1190
	else
1191
	    sprintf(bp, "%s http://%s%s", method, ctxt->hostname, ctxt->path);
1192
    }
1193
    else
1194
	sprintf(bp, "%s %s", method, ctxt->path);
1195
    p = bp + strlen(bp);
1196
    sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
1197
    p += strlen(p);
15377 ripley 1198
    if(!cacheOK) {
1199
	sprintf(p, "Pragma: no-cache\r\n");
1200
	p += strlen(p);
1201
    }
22775 ripley 1202
    if(proxy && proxyUser) {
22767 ripley 1203
	sprintf(p, "Proxy-Authorization: Basic %s\r\n", buf);
22724 ripley 1204
	p += strlen(p);
1205
    }
14409 ripley 1206
    if (contentType != NULL && *contentType) {
1207
	sprintf(p, "Content-Type: %s\r\n", *contentType);
1208
	p += strlen(p);
1209
    }
1210
    if (headers != NULL) {
1211
	strcpy(p, headers);
1212
	p += strlen(p);
1213
    }
1214
    if (input != NULL)
1215
	sprintf(p, "Content-Length: %d\r\n\r\n%s", ilen, input);
1216
    else
1217
	strcpy(p, "\r\n");
1218
    RxmlMessage(0, "-> %s%s", proxy? "(Proxy) " : "", bp);
1219
    if ((blen -= strlen(bp)+1) < 0)
1220
	RxmlMessage(0, "ERROR: overflowed buffer by %d bytes\n", -blen);
1221
    ctxt->outptr = ctxt->out = bp;
1222
    ctxt->state = XML_NANO_HTTP_WRITE;
1223
    RxmlNanoHTTPSend(ctxt);
1224
    ctxt->state = XML_NANO_HTTP_READ;
1225
    head = 1;
1226
 
1227
    while ((p = RxmlNanoHTTPReadLine(ctxt)) != NULL) {
1228
        if (head && (*p == 0)) {
1229
	    head = 0;
1230
	    ctxt->content = ctxt->inrptr;
1231
	    xmlFree(p);
1232
	    break;
1233
	}
1234
	RxmlNanoHTTPScanAnswer(ctxt, p);
1235
 
1236
	RxmlMessage(0, "<- %s\n", p);
1237
        xmlFree(p);
1238
    }
1239
 
22785 ripley 1240
#ifdef Win32
1241
    /* Prompt for username/password again if status was proxy
1242
       authentication failure */
1243
    if(proxy && !nAuthenticate && ctxt->returnValue == 407) {
1244
	char *env;
23246 ripley 1245
	REprintf("%s\n%s\n", "Proxy authentication failed:",
22790 ripley 1246
		"\tplease re-enter the credentials or hit Cancel");
1247
	R_FlushConsole(); R_ProcessEvents();
22785 ripley 1248
	env = askUserPass("Proxy Authentication");
1249
	if(strlen(env)) {
1250
	    if (proxyUser != NULL) {xmlFree(proxyUser); proxyUser = NULL;}
1251
	    proxyUser = xmlMemStrdup(env);
1252
	    nAuthenticate++;
1253
	    goto retry;
1254
	}
1255
    }
1256
#endif
1257
 
14409 ripley 1258
    if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1259
        (ctxt->returnValue < 400)) {
1260
	RxmlMessage(1, "Redirect to: %s", ctxt->location);
1261
	while (RxmlNanoHTTPRecv(ctxt)) ;
1262
        if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1263
	    nbRedirects++;
1264
	    redirURL = xmlMemStrdup(ctxt->location);
1265
	    RxmlNanoHTTPFreeCtxt(ctxt);
1266
	    goto retry;
1267
	}
1268
	RxmlNanoHTTPFreeCtxt(ctxt);
1269
	RxmlMessage(2, "Too many redirects, aborting ...");
1270
	return(NULL);
1271
 
1272
    }
1273
 
1274
    if (contentType != NULL) {
1275
	if (ctxt->contentType != NULL)
1276
	    *contentType = xmlMemStrdup(ctxt->contentType);
1277
	else
1278
	    *contentType = NULL;
1279
    }
1280
 
1281
    if (ctxt->contentType != NULL)
1282
	RxmlMessage(1, "Code %d, content-type '%s'",
1283
		    ctxt->returnValue, ctxt->contentType);
1284
    else
1285
	RxmlMessage(1, "Code %d, no content-type",
1286
		    ctxt->returnValue);
1287
 
1288
    return((void *) ctxt);
1289
}
1290
 
1291
 
1292
 
1293
/**
1294
 * RxmlNanoHTTPReturnCode:
1295
 * @ctx:  the HTTP context
1296
 *
1297
 * Returns the HTTP return code for the request.
1298
 */
1299
int
1300
RxmlNanoHTTPReturnCode(void *ctx)
1301
{
1302
    RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx;
1303
 
1304
    if (ctxt == NULL) return(-1);
1305
 
1306
    return(ctxt->returnValue);
1307
}
1308
 
22776 ripley 1309
char *
1310
RxmlNanoHTTPStatusMsg(void *ctx)
1311
{
1312
    RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx;
1313
    return(ctxt->statusMsg);
1314
}
1315
 
14409 ripley 1316
int
1317
RxmlNanoHTTPContentLength(void *ctx)
1318
{
1319
    RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx;
1320
 
1321
    if (ctxt == NULL) return(-1);
1322
 
1323
    return(ctxt->contentLength);
1324
}
1325
 
1326
char *
1327
RxmlNanoHTTPContentType(void *ctx)
1328
{
1329
    RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx;
1330
    return(ctxt->contentType);
1331
}
1332
 
1333
#endif /* !Unix or BSD_NETWORKING */