1
/*
2
 * nanoftp.c: basic FTP client support
3
 *
4
 *  Reference: RFC 959
5
 */
6
7
#ifdef TESTING
8
#define STANDALONE
9
#define HAVE_STDLIB_H
10
#define HAVE_UNISTD_H
11
#define HAVE_SYS_SOCKET_H
12
#define HAVE_NETINET_IN_H
13
#define HAVE_NETDB_H
14
#define HAVE_SYS_TIME_H
15
#else /* TESTING */
16
#define NEED_SOCKETS
17
#endif /* TESTING */
18
19
#define IN_LIBXML
20
#include "libxml.h"
21
22
#ifdef LIBXML_FTP_ENABLED
23
#include <string.h>
24
25
#ifdef HAVE_STDLIB_H
26
#include <stdlib.h>
27
#endif
28
#ifdef HAVE_UNISTD_H
29
#include <unistd.h>
30
#endif
31
#ifdef HAVE_SYS_SOCKET_H
32
#include <sys/socket.h>
33
#endif
34
#ifdef HAVE_NETINET_IN_H
35
#include <netinet/in.h>
36
#endif
37
#ifdef HAVE_ARPA_INET_H
38
#include <arpa/inet.h>
39
#endif
40
#ifdef HAVE_NETDB_H
41
#include <netdb.h>
42
#endif
43
#ifdef HAVE_FCNTL_H
44
#include <fcntl.h> 
45
#endif
46
#ifdef HAVE_ERRNO_H
47
#include <errno.h>
48
#endif
49
#ifdef HAVE_SYS_TIME_H
50
#include <sys/time.h>
51
#endif
52
#ifdef HAVE_SYS_SELECT_H
53
#include <sys/select.h>
54
#endif
55
#ifdef HAVE_SYS_SOCKET_H
56
#include <sys/socket.h>
57
#endif
58
#ifdef HAVE_SYS_TYPES_H
59
#include <sys/types.h>
60
#endif
61
#ifdef HAVE_STRINGS_H
62
#include <strings.h>
63
#endif
64
65
#include <libxml/xmlmemory.h>
66
#include <libxml/parser.h>
67
#include <libxml/xmlerror.h>
68
#include <libxml/uri.h>
69
#include <libxml/nanoftp.h>
70
#include <libxml/globals.h>
71
72
/* #define DEBUG_FTP 1  */
73
#ifdef STANDALONE
74
#ifndef DEBUG_FTP
75
#define DEBUG_FTP 1
76
#endif
77
#endif
78
79
80
#if defined(__MINGW32__) || defined(_WIN32_WCE)
81
#define _WINSOCKAPI_
82
#include <wsockcompat.h>
83
#include <winsock2.h>
84
#undef XML_SOCKLEN_T
85
#define XML_SOCKLEN_T unsigned int
86
#endif
87
88
/**
89
 * A couple portability macros
90
 */
91
#ifndef _WINSOCKAPI_
92
#if !defined(__BEOS__) || defined(__HAIKU__)
93
#define closesocket(s) close(s)
94
#endif
95
#define SOCKET int
96
#endif
97
98
#ifdef __BEOS__
99
#ifndef PF_INET
100
#define PF_INET AF_INET
101
#endif
102
#endif
103
104
#ifdef _AIX
105
#ifdef HAVE_BROKEN_SS_FAMILY
106
#define ss_family __ss_family
107
#endif
108
#endif
109
110
#ifndef XML_SOCKLEN_T
111
#define XML_SOCKLEN_T unsigned int
112
#endif
113
114
#define FTP_COMMAND_OK		200
115
#define FTP_SYNTAX_ERROR	500
116
#define FTP_GET_PASSWD		331
117
#define FTP_BUF_SIZE		1024
118
119
#define XML_NANO_MAX_URLBUF	4096
120
121
typedef struct xmlNanoFTPCtxt {
122
    char *protocol;	/* the protocol name */
123
    char *hostname;	/* the host name */
124
    int port;		/* the port */
125
    char *path;		/* the path within the URL */
126
    char *user;		/* user string */
127
    char *passwd;	/* passwd string */
128
#ifdef SUPPORT_IP6
129
    struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
130
#else
131
    struct sockaddr_in ftpAddr; /* the socket address struct */
132
#endif
133
    int passive;	/* currently we support only passive !!! */
134
    SOCKET controlFd;	/* the file descriptor for the control socket */
135
    SOCKET dataFd;	/* the file descriptor for the data socket */
136
    int state;		/* WRITE / READ / CLOSED */
137
    int returnValue;	/* the protocol return value */
138
    /* buffer for data received from the control connection */
139
    char controlBuf[FTP_BUF_SIZE + 1];
140
    int controlBufIndex;
141
    int controlBufUsed;
142
    int controlBufAnswer;
143
} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
144
145
static int initialized = 0;
146
static char *proxy = NULL;	/* the proxy name if any */
147
static int proxyPort = 0;	/* the proxy port if any */
148
static char *proxyUser = NULL;	/* user for proxy authentication */
149
static char *proxyPasswd = NULL;/* passwd for proxy authentication */
150
static int proxyType = 0;	/* uses TYPE or a@b ? */
151
152
#ifdef SUPPORT_IP6
153
static
154
int have_ipv6(void) {
155
    int s;
156
157
    s = socket (AF_INET6, SOCK_STREAM, 0);
158
    if (s != -1) {
159
	close (s);
160
	return (1);
161
    }
162
    return (0);
163
}
164
#endif
165
166
/**
167
 * xmlFTPErrMemory:
168
 * @extra:  extra informations
169
 *
170
 * Handle an out of memory condition
171
 */
172
static void
173
xmlFTPErrMemory(const char *extra)
174
{
175
    __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
176
}
177
178
/**
179
 * xmlNanoFTPInit:
180
 *
181
 * Initialize the FTP protocol layer.
182
 * Currently it just checks for proxy informations,
183
 * and get the hostname
184
 */
185
186
void
187
xmlNanoFTPInit(void) {
188
    const char *env;
189
#ifdef _WINSOCKAPI_
190
    WSADATA wsaData;    
191
#endif
192
193
    if (initialized)
194
	return;
195
196
#ifdef _WINSOCKAPI_
197
    if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
198
	return;
199
#endif
200
201
    proxyPort = 21;
202
    env = getenv("no_proxy");
203
    if (env && ((env[0] == '*' ) && (env[1] == 0)))
204
	return;
205
    env = getenv("ftp_proxy");
206
    if (env != NULL) {
207
	xmlNanoFTPScanProxy(env);
208
    } else {
209
	env = getenv("FTP_PROXY");
210
	if (env != NULL) {
211
	    xmlNanoFTPScanProxy(env);
212
	}
213
    }
214
    env = getenv("ftp_proxy_user");
215
    if (env != NULL) {
216
	proxyUser = xmlMemStrdup(env);
217
    }
218
    env = getenv("ftp_proxy_password");
219
    if (env != NULL) {
220
	proxyPasswd = xmlMemStrdup(env);
221
    }
222
    initialized = 1;
223
}
224
225
/**
226
 * xmlNanoFTPCleanup:
227
 *
228
 * Cleanup the FTP protocol layer. This cleanup proxy informations.
229
 */
230
231
void
232
xmlNanoFTPCleanup(void) {
233
    if (proxy != NULL) {
234
	xmlFree(proxy);
235
	proxy = NULL;
236
    }
237
    if (proxyUser != NULL) {
238
	xmlFree(proxyUser);
239
	proxyUser = NULL;
240
    }
241
    if (proxyPasswd != NULL) {
242
	xmlFree(proxyPasswd);
243
	proxyPasswd = NULL;
244
    }
245
#ifdef _WINSOCKAPI_
246
    if (initialized)
247
	WSACleanup();
248
#endif
249
    initialized = 0;
250
}
251
252
/**
253
 * xmlNanoFTPProxy:
254
 * @host:  the proxy host name
255
 * @port:  the proxy port
256
 * @user:  the proxy user name
257
 * @passwd:  the proxy password
258
 * @type:  the type of proxy 1 for using SITE, 2 for USER a@b
259
 *
260
 * Setup the FTP proxy informations.
261
 * This can also be done by using ftp_proxy ftp_proxy_user and
262
 * ftp_proxy_password environment variables.
263
 */
264
265
void
266
xmlNanoFTPProxy(const char *host, int port, const char *user,
267
	        const char *passwd, int type) {
268
    if (proxy != NULL) {
269
	xmlFree(proxy);
270
	proxy = NULL;
271
    }
272
    if (proxyUser != NULL) {
273
	xmlFree(proxyUser);
274
	proxyUser = NULL;
275
    }
276
    if (proxyPasswd != NULL) {
277
	xmlFree(proxyPasswd);
278
	proxyPasswd = NULL;
279
    }
280
    if (host)
281
	proxy = xmlMemStrdup(host);
282
    if (user)
283
	proxyUser = xmlMemStrdup(user);
284
    if (passwd)
285
	proxyPasswd = xmlMemStrdup(passwd);
286
    proxyPort = port;
287
    proxyType = type;
288
}
289
290
/**
291
 * xmlNanoFTPScanURL:
292
 * @ctx:  an FTP context
293
 * @URL:  The URL used to initialize the context
294
 *
295
 * (Re)Initialize an FTP context by parsing the URL and finding
296
 * the protocol host port and path it indicates.
297
 */
298
299
static void
300
xmlNanoFTPScanURL(void *ctx, const char *URL) {
301
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
302
    xmlURIPtr uri;
303
304
    /*
305
     * Clear any existing data from the context
306
     */
307
    if (ctxt->protocol != NULL) { 
308
        xmlFree(ctxt->protocol);
309
	ctxt->protocol = NULL;
310
    }
311
    if (ctxt->hostname != NULL) { 
312
        xmlFree(ctxt->hostname);
313
	ctxt->hostname = NULL;
314
    }
315
    if (ctxt->path != NULL) { 
316
        xmlFree(ctxt->path);
317
	ctxt->path = NULL;
318
    }
319
    if (URL == NULL) return;
320
321
    uri = xmlParseURIRaw(URL, 1);
322
    if (uri == NULL)
323
	return;
324
325
    if ((uri->scheme == NULL) || (uri->server == NULL)) {
326
	xmlFreeURI(uri);
327
	return;
328
    }
329
    
330
    ctxt->protocol = xmlMemStrdup(uri->scheme);
331
    ctxt->hostname = xmlMemStrdup(uri->server);
332
    if (uri->path != NULL)
333
	ctxt->path = xmlMemStrdup(uri->path);
334
    else
335
	ctxt->path = xmlMemStrdup("/");
336
    if (uri->port != 0)
337
	ctxt->port = uri->port;
338
339
    if (uri->user != NULL) {
340
	char *cptr;
341
	if ((cptr=strchr(uri->user, ':')) == NULL)
342
	    ctxt->user = xmlMemStrdup(uri->user);
343
	else {
344
	    ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
345
			    (cptr - uri->user));
346
	    ctxt->passwd = xmlMemStrdup(cptr+1);
347
	}
348
    }
349
350
    xmlFreeURI(uri);
351
352
}
353
354
/**
355
 * xmlNanoFTPUpdateURL:
356
 * @ctx:  an FTP context
357
 * @URL:  The URL used to update the context
358
 *
359
 * Update an FTP context by parsing the URL and finding
360
 * new path it indicates. If there is an error in the 
361
 * protocol, hostname, port or other information, the
362
 * error is raised. It indicates a new connection has to
363
 * be established.
364
 *
365
 * Returns 0 if Ok, -1 in case of error (other host).
366
 */
367
368
int
369
xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
370
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
371
    xmlURIPtr uri;
372
373
    if (URL == NULL)
374
	return(-1);
375
    if (ctxt == NULL)
376
	return(-1);
377
    if (ctxt->protocol == NULL)
378
	return(-1);
379
    if (ctxt->hostname == NULL)
380
	return(-1);
381
382
    uri = xmlParseURIRaw(URL, 1);
383
    if (uri == NULL)
384
	return(-1);
385
386
    if ((uri->scheme == NULL) || (uri->server == NULL)) {
387
	xmlFreeURI(uri);
388
	return(-1);
389
    }
390
    if ((strcmp(ctxt->protocol, uri->scheme)) ||
391
	(strcmp(ctxt->hostname, uri->server)) ||
392
	((uri->port != 0) && (ctxt->port != uri->port))) {
393
	xmlFreeURI(uri);
394
	return(-1);
395
    }
396
397
    if (uri->port != 0)
398
	ctxt->port = uri->port;
399
400
    if (ctxt->path != NULL) {
401
	xmlFree(ctxt->path);
402
	ctxt->path = NULL;
403
    }
404
405
    if (uri->path == NULL) 
406
        ctxt->path = xmlMemStrdup("/");
407
    else
408
	ctxt->path = xmlMemStrdup(uri->path);
409
410
    xmlFreeURI(uri);
411
412
    return(0);
413
}
414
415
/**
416
 * xmlNanoFTPScanProxy:
417
 * @URL:  The proxy URL used to initialize the proxy context
418
 *
419
 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
420
 * the protocol host port it indicates.
421
 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
422
 * A NULL URL cleans up proxy informations.
423
 */
424
425
void
426
xmlNanoFTPScanProxy(const char *URL) {
427
    xmlURIPtr uri;
428
429
    if (proxy != NULL) { 
430
        xmlFree(proxy);
431
	proxy = NULL;
432
    }
433
    proxyPort = 0;
434
435
#ifdef DEBUG_FTP
436
    if (URL == NULL)
437
	xmlGenericError(xmlGenericErrorContext,
438
		"Removing FTP proxy info\n");
439
    else
440
	xmlGenericError(xmlGenericErrorContext,
441
		"Using FTP proxy %s\n", URL);
442
#endif
443
    if (URL == NULL) return;
444
445
    uri = xmlParseURIRaw(URL, 1);
446
    if ((uri == NULL) || (uri->scheme == NULL) ||
447
	(strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
448
	__xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
449
	if (uri != NULL)
450
	    xmlFreeURI(uri);
451
	return;
452
    }
453
    
454
    proxy = xmlMemStrdup(uri->server);
455
    if (uri->port != 0)
456
	proxyPort = uri->port;
457
458
    xmlFreeURI(uri);
459
}
460
461
/**
462
 * xmlNanoFTPNewCtxt:
463
 * @URL:  The URL used to initialize the context
464
 *
465
 * Allocate and initialize a new FTP context.
466
 *
467
 * Returns an FTP context or NULL in case of error.
468
 */
469
470
void*
471
xmlNanoFTPNewCtxt(const char *URL) {
472
    xmlNanoFTPCtxtPtr ret;
473
    char *unescaped;
474
475
    ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
476
    if (ret == NULL) {
477
        xmlFTPErrMemory("allocating FTP context");
478
        return(NULL);
479
    }
480
481
    memset(ret, 0, sizeof(xmlNanoFTPCtxt));
482
    ret->port = 21;
483
    ret->passive = 1;
484
    ret->returnValue = 0;
485
    ret->controlBufIndex = 0;
486
    ret->controlBufUsed = 0;
487
    ret->controlFd = -1;
488
489
    unescaped = xmlURIUnescapeString(URL, 0, NULL);
490
    if (unescaped != NULL) {
491
	xmlNanoFTPScanURL(ret, unescaped);
492
	xmlFree(unescaped);
493
    } else if (URL != NULL)
494
	xmlNanoFTPScanURL(ret, URL);
495
496
    return(ret);
497
}
498
499
/**
500
 * xmlNanoFTPFreeCtxt:
501
 * @ctx:  an FTP context
502
 *
503
 * Frees the context after closing the connection.
504
 */
505
506
void
507
xmlNanoFTPFreeCtxt(void * ctx) {
508
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
509
    if (ctxt == NULL) return;
510
    if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
511
    if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
512
    if (ctxt->path != NULL) xmlFree(ctxt->path);
513
    ctxt->passive = 1;
514
    if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
515
    ctxt->controlFd = -1;
516
    ctxt->controlBufIndex = -1;
517
    ctxt->controlBufUsed = -1;
518
    xmlFree(ctxt);
519
}
520
521
/**
522
 * xmlNanoFTPParseResponse:
523
 * @buf:  the buffer containing the response
524
 * @len:  the buffer length
525
 * 
526
 * Parsing of the server answer, we just extract the code.
527
 *
528
 * returns 0 for errors
529
 *     +XXX for last line of response
530
 *     -XXX for response to be continued
531
 */
532
static int
533
xmlNanoFTPParseResponse(char *buf, int len) {
534
    int val = 0;
535
536
    if (len < 3) return(-1);
537
    if ((*buf >= '0') && (*buf <= '9')) 
538
        val = val * 10 + (*buf - '0');
539
    else
540
        return(0);
541
    buf++;
542
    if ((*buf >= '0') && (*buf <= '9')) 
543
        val = val * 10 + (*buf - '0');
544
    else
545
        return(0);
546
    buf++;
547
    if ((*buf >= '0') && (*buf <= '9')) 
548
        val = val * 10 + (*buf - '0');
549
    else
550
        return(0);
551
    buf++;
552
    if (*buf == '-') 
553
        return(-val);
554
    return(val);
555
}
556
557
/**
558
 * xmlNanoFTPGetMore:
559
 * @ctx:  an FTP context
560
 *
561
 * Read more information from the FTP control connection
562
 * Returns the number of bytes read, < 0 indicates an error
563
 */
564
static int
565
xmlNanoFTPGetMore(void *ctx) {
566
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
567
    int len;
568
    int size;
569
570
    if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
571
572
    if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
573
#ifdef DEBUG_FTP
574
        xmlGenericError(xmlGenericErrorContext,
575
		"xmlNanoFTPGetMore : controlBufIndex = %d\n",
576
		ctxt->controlBufIndex);
577
#endif
578
	return(-1);
579
    }
580
581
    if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
582
#ifdef DEBUG_FTP
583
        xmlGenericError(xmlGenericErrorContext,
584
		"xmlNanoFTPGetMore : controlBufUsed = %d\n",
585
		ctxt->controlBufUsed);
586
#endif
587
	return(-1);
588
    }
589
    if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
590
#ifdef DEBUG_FTP
591
        xmlGenericError(xmlGenericErrorContext,
592
		"xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
593
	       ctxt->controlBufIndex, ctxt->controlBufUsed);
594
#endif
595
	return(-1);
596
    }
597
598
    /*
599
     * First pack the control buffer
600
     */
601
    if (ctxt->controlBufIndex > 0) {
602
	memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
603
		ctxt->controlBufUsed - ctxt->controlBufIndex);
604
	ctxt->controlBufUsed -= ctxt->controlBufIndex;
605
	ctxt->controlBufIndex = 0;
606
    }
607
    size = FTP_BUF_SIZE - ctxt->controlBufUsed;
608
    if (size == 0) {
609
#ifdef DEBUG_FTP
610
        xmlGenericError(xmlGenericErrorContext,
611
		"xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
612
#endif
613
	return(0);
614
    }
615
616
    /*
617
     * Read the amount left on the control connection
618
     */
619
    if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
620
		    size, 0)) < 0) {
621
	__xmlIOErr(XML_FROM_FTP, 0, "recv failed");
622
	closesocket(ctxt->controlFd); ctxt->controlFd = -1;
623
        ctxt->controlFd = -1;
624
        return(-1);
625
    }
626
#ifdef DEBUG_FTP
627
    xmlGenericError(xmlGenericErrorContext,
628
	    "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
629
	   ctxt->controlBufUsed, ctxt->controlBufUsed + len);
630
#endif
631
    ctxt->controlBufUsed += len;
632
    ctxt->controlBuf[ctxt->controlBufUsed] = 0;
633
634
    return(len);
635
}
636
637
/**
638
 * xmlNanoFTPReadResponse:
639
 * @ctx:  an FTP context
640
 *
641
 * Read the response from the FTP server after a command.
642
 * Returns the code number
643
 */
644
static int
645
xmlNanoFTPReadResponse(void *ctx) {
646
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
647
    char *ptr, *end;
648
    int len;
649
    int res = -1, cur = -1;
650
651
    if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
652
653
get_more:
654
    /*
655
     * Assumes everything up to controlBuf[controlBufIndex] has been read
656
     * and analyzed.
657
     */
658
    len = xmlNanoFTPGetMore(ctx);
659
    if (len < 0) {
660
        return(-1);
661
    }
662
    if ((ctxt->controlBufUsed == 0) && (len == 0)) {
663
        return(-1);
664
    }
665
    ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
666
    end = &ctxt->controlBuf[ctxt->controlBufUsed];
667
668
#ifdef DEBUG_FTP
669
    xmlGenericError(xmlGenericErrorContext,
670
	    "\n<<<\n%s\n--\n", ptr);
671
#endif
672
    while (ptr < end) {
673
        cur = xmlNanoFTPParseResponse(ptr, end - ptr);
674
	if (cur > 0) {
675
	    /*
676
	     * Successfully scanned the control code, scratch
677
	     * till the end of the line, but keep the index to be
678
	     * able to analyze the result if needed.
679
	     */
680
	    res = cur;
681
	    ptr += 3;
682
	    ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
683
	    while ((ptr < end) && (*ptr != '\n')) ptr++;
684
	    if (*ptr == '\n') ptr++;
685
	    if (*ptr == '\r') ptr++;
686
	    break;
687
	}
688
	while ((ptr < end) && (*ptr != '\n')) ptr++;
689
	if (ptr >= end) {
690
	    ctxt->controlBufIndex = ctxt->controlBufUsed;
691
	    goto get_more;
692
	}
693
	if (*ptr != '\r') ptr++;
694
    }
695
696
    if (res < 0) goto get_more;
697
    ctxt->controlBufIndex = ptr - ctxt->controlBuf;
698
#ifdef DEBUG_FTP
699
    ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
700
    xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
701
#endif
702
703
#ifdef DEBUG_FTP
704
    xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
705
#endif
706
    return(res / 100);
707
}
708
709
/**
710
 * xmlNanoFTPGetResponse:
711
 * @ctx:  an FTP context
712
 *
713
 * Get the response from the FTP server after a command.
714
 * Returns the code number
715
 */
716
717
int
718
xmlNanoFTPGetResponse(void *ctx) {
719
    int res;
720
721
    res = xmlNanoFTPReadResponse(ctx);
722
723
    return(res);
724
}
725
726
/**
727
 * xmlNanoFTPCheckResponse:
728
 * @ctx:  an FTP context
729
 *
730
 * Check if there is a response from the FTP server after a command.
731
 * Returns the code number, or 0
732
 */
733
734
int
735
xmlNanoFTPCheckResponse(void *ctx) {
736
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
737
    fd_set rfd;
738
    struct timeval tv;
739
740
    if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
741
    tv.tv_sec = 0;
742
    tv.tv_usec = 0;
743
    FD_ZERO(&rfd);
744
    FD_SET(ctxt->controlFd, &rfd);
745
    switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
746
	case 0:
747
	    return(0);
748
	case -1:
749
	    __xmlIOErr(XML_FROM_FTP, 0, "select");
750
	    return(-1);
751
			
752
    }
753
754
    return(xmlNanoFTPReadResponse(ctx));
755
}
756
757
/**
758
 * Send the user authentication
759
 */
760
761
static int
762
xmlNanoFTPSendUser(void *ctx) {
763
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
764
    char buf[200];
765
    int len;
766
    int res;
767
768
    if (ctxt->user == NULL)
769
	snprintf(buf, sizeof(buf), "USER anonymous\r\n");
770
    else
771
	snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
772
    buf[sizeof(buf) - 1] = 0;
773
    len = strlen(buf);
774
#ifdef DEBUG_FTP
775
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
776
#endif
777
    res = send(ctxt->controlFd, buf, len, 0);
778
    if (res < 0) {
779
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
780
	return(res);
781
    }
782
    return(0);
783
}
784
785
/**
786
 * Send the password authentication
787
 */
788
789
static int
790
xmlNanoFTPSendPasswd(void *ctx) {
791
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
792
    char buf[200];
793
    int len;
794
    int res;
795
796
    if (ctxt->passwd == NULL)
797
	snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
798
    else
799
	snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
800
    buf[sizeof(buf) - 1] = 0;
801
    len = strlen(buf);
802
#ifdef DEBUG_FTP
803
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
804
#endif
805
    res = send(ctxt->controlFd, buf, len, 0);
806
    if (res < 0) {
807
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
808
	return(res);
809
    }
810
    return(0);
811
}
812
813
/**
814
 * xmlNanoFTPQuit:
815
 * @ctx:  an FTP context
816
 *
817
 * Send a QUIT command to the server
818
 *
819
 * Returns -1 in case of error, 0 otherwise
820
 */
821
822
823
int
824
xmlNanoFTPQuit(void *ctx) {
825
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
826
    char buf[200];
827
    int len, res;
828
829
    if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
830
831
    snprintf(buf, sizeof(buf), "QUIT\r\n");
832
    len = strlen(buf);
833
#ifdef DEBUG_FTP
834
    xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
835
#endif
836
    res = send(ctxt->controlFd, buf, len, 0);
837
    if (res < 0) {
838
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
839
	return(res);
840
    }
841
    return(0);
842
}
843
844
/**
845
 * xmlNanoFTPConnect:
846
 * @ctx:  an FTP context
847
 *
848
 * Tries to open a control connection
849
 *
850
 * Returns -1 in case of error, 0 otherwise
851
 */
852
853
int
854
xmlNanoFTPConnect(void *ctx) {
855
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
856
    struct hostent *hp;
857
    int port;
858
    int res;
859
    int addrlen = sizeof (struct sockaddr_in);
860
861
    if (ctxt == NULL)
862
	return(-1);
863
    if (ctxt->hostname == NULL)
864
	return(-1);
865
866
    /*
867
     * do the blocking DNS query.
868
     */
869
    if (proxy) {
870
        port = proxyPort;
871
    } else {
872
	port = ctxt->port;
873
    }
874
    if (port == 0)
875
	port = 21;
876
877
    memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
878
879
#ifdef SUPPORT_IP6
880
    if (have_ipv6 ()) {
881
	struct addrinfo hints, *tmp, *result;
882
883
	result = NULL;
884
	memset (&hints, 0, sizeof(hints));
885
	hints.ai_socktype = SOCK_STREAM;
886
887
	if (proxy) {
888
	    if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
889
		__xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
890
		return (-1);
891
	    }
892
	}
893
	else
894
	    if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
895
		__xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
896
		return (-1);
897
	    }
898
899
	for (tmp = result; tmp; tmp = tmp->ai_next)
900
	    if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
901
		break;
902
903
	if (!tmp) {
904
	    if (result)
905
		freeaddrinfo (result);
906
	    __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
907
	    return (-1);
908
	}
909
	if (tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
910
	    __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
911
	    return (-1);
912
	}
913
	if (tmp->ai_family == AF_INET6) {
914
	    memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
915
	    ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
916
	    ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
917
	}
918
	else {
919
	    memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
920
	    ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
921
	    ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
922
	}
923
	addrlen = tmp->ai_addrlen;
924
	freeaddrinfo (result);
925
    }
926
    else
927
#endif
928
    {
929
	if (proxy)
930
	    hp = gethostbyname (proxy);
931
	else
932
	    hp = gethostbyname (ctxt->hostname);
933
	if (hp == NULL) {
934
	    __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
935
	    return (-1);
936
	}
937
	if ((unsigned int) hp->h_length >
938
	    sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
939
	    __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
940
	    return (-1);
941
	}
942
943
	/*
944
	 * Prepare the socket
945
	 */
946
	((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
947
	memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
948
		hp->h_addr_list[0], hp->h_length);
949
	((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = (u_short)htons ((unsigned short)port);
950
	ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
951
	addrlen = sizeof (struct sockaddr_in);
952
    }
953
954
    if (ctxt->controlFd < 0) {
955
	__xmlIOErr(XML_FROM_FTP, 0, "socket failed");
956
        return(-1);
957
    }
958
959
    /*
960
     * Do the connect.
961
     */
962
    if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
963
	    addrlen) < 0) {
964
	__xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
965
        closesocket(ctxt->controlFd); ctxt->controlFd = -1;
966
        ctxt->controlFd = -1;
967
	return(-1);
968
    }
969
970
    /*
971
     * Wait for the HELLO from the server.
972
     */
973
    res = xmlNanoFTPGetResponse(ctxt);
974
    if (res != 2) {
975
        closesocket(ctxt->controlFd); ctxt->controlFd = -1;
976
        ctxt->controlFd = -1;
977
	return(-1);
978
    }
979
980
    /*
981
     * State diagram for the login operation on the FTP server
982
     *
983
     * Reference: RFC 959
984
     *
985
     *                       1
986
     * +---+   USER    +---+------------->+---+
987
     * | B |---------->| W | 2       ---->| E |
988
     * +---+           +---+------  |  -->+---+
989
     *                  | |       | | |
990
     *                3 | | 4,5   | | |
991
     *    --------------   -----  | | |
992
     *   |                      | | | |
993
     *   |                      | | | |
994
     *   |                 ---------  |
995
     *   |               1|     | |   |
996
     *   V                |     | |   |
997
     * +---+   PASS    +---+ 2  |  ------>+---+
998
     * |   |---------->| W |------------->| S |
999
     * +---+           +---+   ---------->+---+
1000
     *                  | |   | |     |
1001
     *                3 | |4,5| |     |
1002
     *    --------------   --------   |
1003
     *   |                    | |  |  |
1004
     *   |                    | |  |  |
1005
     *   |                 -----------
1006
     *   |             1,3|   | |  |
1007
     *   V                |  2| |  |
1008
     * +---+   ACCT    +---+--  |   ----->+---+
1009
     * |   |---------->| W | 4,5 -------->| F |
1010
     * +---+           +---+------------->+---+
1011
     *
1012
     * Of course in case of using a proxy this get really nasty and is not
1013
     * standardized at all :-(
1014
     */
1015
    if (proxy) {
1016
        int len;
1017
	char buf[400];
1018
1019
        if (proxyUser != NULL) {
1020
	    /*
1021
	     * We need proxy auth
1022
	     */
1023
	    snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
1024
            buf[sizeof(buf) - 1] = 0;
1025
            len = strlen(buf);
1026
#ifdef DEBUG_FTP
1027
	    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1028
#endif
1029
	    res = send(ctxt->controlFd, buf, len, 0);
1030
	    if (res < 0) {
1031
		__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1032
		closesocket(ctxt->controlFd);
1033
		ctxt->controlFd = -1;
1034
	        return(res);
1035
	    }
1036
	    res = xmlNanoFTPGetResponse(ctxt);
1037
	    switch (res) {
1038
		case 2:
1039
		    if (proxyPasswd == NULL)
1040
			break;
1041
		case 3:
1042
		    if (proxyPasswd != NULL)
1043
			snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
1044
		    else
1045
			snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1046
                    buf[sizeof(buf) - 1] = 0;
1047
                    len = strlen(buf);
1048
#ifdef DEBUG_FTP
1049
		    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1050
#endif
1051
		    res = send(ctxt->controlFd, buf, len, 0);
1052
		    if (res < 0) {
1053
			__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1054
			closesocket(ctxt->controlFd);
1055
			ctxt->controlFd = -1;
1056
			return(res);
1057
		    }
1058
		    res = xmlNanoFTPGetResponse(ctxt);
1059
		    if (res > 3) {
1060
			closesocket(ctxt->controlFd);
1061
			ctxt->controlFd = -1;
1062
			return(-1);
1063
		    }
1064
		    break;
1065
		case 1:
1066
		    break;
1067
		case 4:
1068
		case 5:
1069
		case -1:
1070
		default:
1071
		    closesocket(ctxt->controlFd);
1072
		    ctxt->controlFd = -1;
1073
		    return(-1);
1074
	    }
1075
	}
1076
1077
	/*
1078
	 * We assume we don't need more authentication to the proxy
1079
	 * and that it succeeded :-\
1080
	 */
1081
	switch (proxyType) {
1082
	    case 0:
1083
		/* we will try in sequence */
1084
	    case 1:
1085
		/* Using SITE command */
1086
		snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
1087
                buf[sizeof(buf) - 1] = 0;
1088
                len = strlen(buf);
1089
#ifdef DEBUG_FTP
1090
		xmlGenericError(xmlGenericErrorContext, "%s", buf);
1091
#endif
1092
		res = send(ctxt->controlFd, buf, len, 0);
1093
		if (res < 0) {
1094
		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1095
		    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1096
		    ctxt->controlFd = -1;
1097
		    return(res);
1098
		}
1099
		res = xmlNanoFTPGetResponse(ctxt);
1100
		if (res == 2) {
1101
		    /* we assume it worked :-\ 1 is error for SITE command */
1102
		    proxyType = 1;
1103
		    break;
1104
		}    
1105
		if (proxyType == 1) {
1106
		    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1107
		    ctxt->controlFd = -1;
1108
		    return(-1);
1109
		}
1110
	    case 2:
1111
		/* USER user@host command */
1112
		if (ctxt->user == NULL)
1113
		    snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1114
			           ctxt->hostname);
1115
		else
1116
		    snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1117
			           ctxt->user, ctxt->hostname);
1118
                buf[sizeof(buf) - 1] = 0;
1119
                len = strlen(buf);
1120
#ifdef DEBUG_FTP
1121
		xmlGenericError(xmlGenericErrorContext, "%s", buf);
1122
#endif
1123
		res = send(ctxt->controlFd, buf, len, 0);
1124
		if (res < 0) {
1125
		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1126
		    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1127
		    ctxt->controlFd = -1;
1128
		    return(res);
1129
		}
1130
		res = xmlNanoFTPGetResponse(ctxt);
1131
		if ((res == 1) || (res == 2)) {
1132
		    /* we assume it worked :-\ */
1133
		    proxyType = 2;
1134
		    return(0);
1135
		}    
1136
		if (ctxt->passwd == NULL)
1137
		    snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1138
		else
1139
		    snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
1140
                buf[sizeof(buf) - 1] = 0;
1141
                len = strlen(buf);
1142
#ifdef DEBUG_FTP
1143
		xmlGenericError(xmlGenericErrorContext, "%s", buf);
1144
#endif
1145
		res = send(ctxt->controlFd, buf, len, 0);
1146
		if (res < 0) {
1147
		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1148
		    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1149
		    ctxt->controlFd = -1;
1150
		    return(res);
1151
		}
1152
		res = xmlNanoFTPGetResponse(ctxt);
1153
		if ((res == 1) || (res == 2)) {
1154
		    /* we assume it worked :-\ */
1155
		    proxyType = 2;
1156
		    return(0);
1157
		}
1158
		if (proxyType == 2) {
1159
		    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1160
		    ctxt->controlFd = -1;
1161
		    return(-1);
1162
		}
1163
	    case 3:
1164
		/*
1165
		 * If you need support for other Proxy authentication scheme
1166
		 * send the code or at least the sequence in use.
1167
		 */
1168
	    default:
1169
		closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1170
		ctxt->controlFd = -1;
1171
		return(-1);
1172
	}
1173
    }
1174
    /*
1175
     * Non-proxy handling.
1176
     */
1177
    res = xmlNanoFTPSendUser(ctxt);
1178
    if (res < 0) {
1179
        closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1180
        ctxt->controlFd = -1;
1181
	return(-1);
1182
    }
1183
    res = xmlNanoFTPGetResponse(ctxt);
1184
    switch (res) {
1185
	case 2:
1186
	    return(0);
1187
	case 3:
1188
	    break;
1189
	case 1:
1190
	case 4:
1191
	case 5:
1192
        case -1:
1193
	default:
1194
	    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1195
	    ctxt->controlFd = -1;
1196
	    return(-1);
1197
    }
1198
    res = xmlNanoFTPSendPasswd(ctxt);
1199
    if (res < 0) {
1200
        closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1201
        ctxt->controlFd = -1;
1202
	return(-1);
1203
    }
1204
    res = xmlNanoFTPGetResponse(ctxt);
1205
    switch (res) {
1206
	case 2:
1207
	    break;
1208
	case 3:
1209
	    __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
1210
		       "FTP server asking for ACCNT on anonymous\n");
1211
	case 1:
1212
	case 4:
1213
	case 5:
1214
        case -1:
1215
	default:
1216
	    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1217
	    ctxt->controlFd = -1;
1218
	    return(-1);
1219
    }
1220
1221
    return(0);
1222
}
1223
1224
/**
1225
 * xmlNanoFTPConnectTo:
1226
 * @server:  an FTP server name
1227
 * @port:  the port (use 21 if 0)
1228
 *
1229
 * Tries to open a control connection to the given server/port
1230
 *
1231
 * Returns an fTP context or NULL if it failed
1232
 */
1233
1234
void*
1235
xmlNanoFTPConnectTo(const char *server, int port) {
1236
    xmlNanoFTPCtxtPtr ctxt;
1237
    int res;
1238
1239
    xmlNanoFTPInit();
1240
    if (server == NULL) 
1241
	return(NULL);
1242
    if (port <= 0)
1243
	return(NULL);
1244
    ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1245
    ctxt->hostname = xmlMemStrdup(server);
1246
    if (port != 0)
1247
	ctxt->port = port;
1248
    res = xmlNanoFTPConnect(ctxt);
1249
    if (res < 0) {
1250
	xmlNanoFTPFreeCtxt(ctxt);
1251
	return(NULL);
1252
    }
1253
    return(ctxt);
1254
}
1255
1256
/**
1257
 * xmlNanoFTPCwd:
1258
 * @ctx:  an FTP context
1259
 * @directory:  a directory on the server
1260
 *
1261
 * Tries to change the remote directory
1262
 *
1263
 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1264
 */
1265
1266
int
1267
xmlNanoFTPCwd(void *ctx, const char *directory) {
1268
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1269
    char buf[400];
1270
    int len;
1271
    int res;
1272
1273
    if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1274
    if (directory == NULL) return 0;
1275
1276
    /*
1277
     * Expected response code for CWD:
1278
     *
1279
     * CWD
1280
     *     250
1281
     *     500, 501, 502, 421, 530, 550
1282
     */
1283
    snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
1284
    buf[sizeof(buf) - 1] = 0;
1285
    len = strlen(buf);
1286
#ifdef DEBUG_FTP
1287
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1288
#endif
1289
    res = send(ctxt->controlFd, buf, len, 0);
1290
    if (res < 0) {
1291
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1292
	return(res);
1293
    }
1294
    res = xmlNanoFTPGetResponse(ctxt);
1295
    if (res == 4) {
1296
	return(-1);
1297
    }
1298
    if (res == 2) return(1);
1299
    if (res == 5) {
1300
	return(0);
1301
    }
1302
    return(0);
1303
}
1304
1305
/**
1306
 * xmlNanoFTPDele:
1307
 * @ctx:  an FTP context
1308
 * @file:  a file or directory on the server
1309
 *
1310
 * Tries to delete an item (file or directory) from server
1311
 *
1312
 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1313
 */
1314
1315
int
1316
xmlNanoFTPDele(void *ctx, const char *file) {
1317
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1318
    char buf[400];
1319
    int len;
1320
    int res;
1321
1322
    if ((ctxt == NULL) || (ctxt->controlFd < 0) || (file == NULL)) return(-1);
1323
    if (file == NULL) return (0);
1324
1325
    /*
1326
     * Expected response code for DELE:
1327
     *
1328
     * DELE
1329
     *       250
1330
     *       450, 550
1331
     *       500, 501, 502, 421, 530
1332
     */
1333
	 
1334
    snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1335
    buf[sizeof(buf) - 1] = 0;
1336
    len = strlen(buf);
1337
#ifdef DEBUG_FTP
1338
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1339
#endif
1340
    res = send(ctxt->controlFd, buf, len, 0);
1341
    if (res < 0) {
1342
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1343
	return(res);
1344
    }
1345
    res = xmlNanoFTPGetResponse(ctxt);
1346
    if (res == 4) {
1347
	return(-1);
1348
    }
1349
    if (res == 2) return(1);
1350
    if (res == 5) {
1351
	return(0);
1352
    }
1353
    return(0);
1354
}
1355
/**
1356
 * xmlNanoFTPGetConnection:
1357
 * @ctx:  an FTP context
1358
 *
1359
 * Try to open a data connection to the server. Currently only
1360
 * passive mode is supported.
1361
 *
1362
 * Returns -1 incase of error, 0 otherwise
1363
 */
1364
1365
int
1366
xmlNanoFTPGetConnection(void *ctx) {
1367
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1368
    char buf[200], *cur;
1369
    int len, i;
1370
    int res;
1371
    unsigned char ad[6], *adp, *portp;
1372
    unsigned int temp[6];
1373
#ifdef SUPPORT_IP6
1374
    struct sockaddr_storage dataAddr;
1375
#else
1376
    struct sockaddr_in dataAddr;
1377
#endif
1378
    XML_SOCKLEN_T dataAddrLen;
1379
1380
    if (ctxt == NULL) return(-1);
1381
1382
    memset (&dataAddr, 0, sizeof(dataAddr));
1383
#ifdef SUPPORT_IP6
1384
    if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1385
	ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1386
	((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1387
	dataAddrLen = sizeof(struct sockaddr_in6);
1388
    } else
1389
#endif
1390
    {
1391
	ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1392
	((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1393
	dataAddrLen = sizeof (struct sockaddr_in);
1394
    }
1395
1396
    if (ctxt->dataFd < 0) {
1397
	__xmlIOErr(XML_FROM_FTP, 0, "socket failed");
1398
	return (-1);
1399
    }
1400
1401
    if (ctxt->passive) {
1402
#ifdef SUPPORT_IP6
1403
	if ((ctxt->ftpAddr).ss_family == AF_INET6)
1404
	    snprintf (buf, sizeof(buf), "EPSV\r\n");
1405
	else
1406
#endif
1407
	    snprintf (buf, sizeof(buf), "PASV\r\n");
1408
        len = strlen (buf);
1409
#ifdef DEBUG_FTP
1410
	xmlGenericError(xmlGenericErrorContext, "%s", buf);
1411
#endif
1412
	res = send(ctxt->controlFd, buf, len, 0);
1413
	if (res < 0) {
1414
	    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1415
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1416
	    return(res);
1417
	}
1418
        res = xmlNanoFTPReadResponse(ctx);
1419
	if (res != 2) {
1420
	    if (res == 5) {
1421
	        closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1422
		return(-1);
1423
	    } else {
1424
		/*
1425
		 * retry with an active connection
1426
		 */
1427
	        closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1428
	        ctxt->passive = 0;
1429
	    }
1430
	}
1431
	cur = &ctxt->controlBuf[ctxt->controlBufAnswer]; 
1432
	while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1433
#ifdef SUPPORT_IP6
1434
	if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1435
	    if (sscanf (cur, "%u", &temp[0]) != 1) {
1436
		__xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
1437
			"Invalid answer to EPSV\n");
1438
		if (ctxt->dataFd != -1) {
1439
		    closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1440
		}
1441
		return (-1);
1442
	    }
1443
	    memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1444
	    ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
1445
	}
1446
	else
1447
#endif
1448
	{
1449
	    if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1450
		&temp[3], &temp[4], &temp[5]) != 6) {
1451
		__xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
1452
			"Invalid answer to PASV\n");
1453
		if (ctxt->dataFd != -1) {
1454
		    closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1455
		}
1456
		return (-1);
1457
	    }
1458
	    for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1459
	    memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1460
	    memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1461
	}
1462
1463
	if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1464
	    __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
1465
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1466
	    return (-1);
1467
	}
1468
    } else {
1469
        getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1470
#ifdef SUPPORT_IP6
1471
	if ((ctxt->ftpAddr).ss_family == AF_INET6)
1472
	    ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1473
	else
1474
#endif
1475
	    ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1476
1477
	if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1478
	    __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
1479
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1480
	    return (-1);
1481
	}
1482
        getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1483
1484
	if (listen(ctxt->dataFd, 1) < 0) {
1485
	    __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
1486
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1487
	    return (-1);
1488
	}
1489
#ifdef SUPPORT_IP6
1490
	if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1491
	    char buf6[INET6_ADDRSTRLEN];
1492
	    inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1493
		    buf6, INET6_ADDRSTRLEN);
1494
	    adp = (unsigned char *) buf6;
1495
	    portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1496
	    snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1497
        } else
1498
#endif
1499
	{
1500
	    adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1501
	    portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1502
	    snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1503
	    adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1504
	    portp[0] & 0xff, portp[1] & 0xff);
1505
	}
1506
1507
        buf[sizeof(buf) - 1] = 0;
1508
        len = strlen(buf);
1509
#ifdef DEBUG_FTP
1510
	xmlGenericError(xmlGenericErrorContext, "%s", buf);
1511
#endif
1512
1513
	res = send(ctxt->controlFd, buf, len, 0);
1514
	if (res < 0) {
1515
	    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1516
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1517
	    return(res);
1518
	}
1519
        res = xmlNanoFTPGetResponse(ctxt);
1520
	if (res != 2) {
1521
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1522
	    return(-1);
1523
        }
1524
    }
1525
    return(ctxt->dataFd);
1526
    
1527
}
1528
1529
/**
1530
 * xmlNanoFTPCloseConnection:
1531
 * @ctx:  an FTP context
1532
 *
1533
 * Close the data connection from the server
1534
 *
1535
 * Returns -1 incase of error, 0 otherwise
1536
 */
1537
1538
int
1539
xmlNanoFTPCloseConnection(void *ctx) {
1540
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1541
    int res;
1542
    fd_set rfd, efd;
1543
    struct timeval tv;
1544
1545
    if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1546
1547
    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1548
    tv.tv_sec = 15;
1549
    tv.tv_usec = 0;
1550
    FD_ZERO(&rfd);
1551
    FD_SET(ctxt->controlFd, &rfd);
1552
    FD_ZERO(&efd);
1553
    FD_SET(ctxt->controlFd, &efd);
1554
    res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1555
    if (res < 0) {
1556
#ifdef DEBUG_FTP
1557
	perror("select");
1558
#endif
1559
	closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1560
	return(-1);
1561
    }
1562
    if (res == 0) {
1563
#ifdef DEBUG_FTP
1564
	xmlGenericError(xmlGenericErrorContext,
1565
		"xmlNanoFTPCloseConnection: timeout\n");
1566
#endif
1567
	closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1568
    } else {
1569
	res = xmlNanoFTPGetResponse(ctxt);
1570
	if (res != 2) {
1571
	    closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1572
	    return(-1);
1573
	}
1574
    }
1575
    return(0);
1576
}
1577
1578
/**
1579
 * xmlNanoFTPParseList:
1580
 * @list:  some data listing received from the server
1581
 * @callback:  the user callback
1582
 * @userData:  the user callback data
1583
 *
1584
 * Parse at most one entry from the listing. 
1585
 *
1586
 * Returns -1 incase of error, the length of data parsed otherwise
1587
 */
1588
1589
static int
1590
xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1591
    const char *cur = list;
1592
    char filename[151];
1593
    char attrib[11];
1594
    char owner[11];
1595
    char group[11];
1596
    char month[4];
1597
    int year = 0;
1598
    int minute = 0;
1599
    int hour = 0;
1600
    int day = 0;
1601
    unsigned long size = 0;
1602
    int links = 0;
1603
    int i;
1604
1605
    if (!strncmp(cur, "total", 5)) {
1606
        cur += 5;
1607
	while (*cur == ' ') cur++;
1608
	while ((*cur >= '0') && (*cur <= '9'))
1609
	    links = (links * 10) + (*cur++ - '0');
1610
	while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
1611
	    cur++;
1612
	return(cur - list);
1613
    } else if (*list == '+') {
1614
	return(0);
1615
    } else {
1616
	while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
1617
	    cur++;
1618
	if (*cur == 0) return(0);
1619
	i = 0;
1620
	while (*cur != ' ') {
1621
	    if (i < 10) 
1622
		attrib[i++] = *cur;
1623
	    cur++;
1624
	    if (*cur == 0) return(0);
1625
	}
1626
	attrib[10] = 0;
1627
	while (*cur == ' ') cur++;
1628
	if (*cur == 0) return(0);
1629
	while ((*cur >= '0') && (*cur <= '9'))
1630
	    links = (links * 10) + (*cur++ - '0');
1631
	while (*cur == ' ') cur++;
1632
	if (*cur == 0) return(0);
1633
	i = 0;
1634
	while (*cur != ' ') {
1635
	    if (i < 10) 
1636
		owner[i++] = *cur;
1637
	    cur++;
1638
	    if (*cur == 0) return(0);
1639
	}
1640
	owner[i] = 0;
1641
	while (*cur == ' ') cur++;
1642
	if (*cur == 0) return(0);
1643
	i = 0;
1644
	while (*cur != ' ') {
1645
	    if (i < 10) 
1646
		group[i++] = *cur;
1647
	    cur++;
1648
	    if (*cur == 0) return(0);
1649
	}
1650
	group[i] = 0;
1651
	while (*cur == ' ') cur++;
1652
	if (*cur == 0) return(0);
1653
	while ((*cur >= '0') && (*cur <= '9'))
1654
	    size = (size * 10) + (*cur++ - '0');
1655
	while (*cur == ' ') cur++;
1656
	if (*cur == 0) return(0);
1657
	i = 0;
1658
	while (*cur != ' ') {
1659
	    if (i < 3)
1660
		month[i++] = *cur;
1661
	    cur++;
1662
	    if (*cur == 0) return(0);
1663
	}
1664
	month[i] = 0;
1665
	while (*cur == ' ') cur++;
1666
	if (*cur == 0) return(0);
1667
        while ((*cur >= '0') && (*cur <= '9'))
1668
	    day = (day * 10) + (*cur++ - '0');
1669
	while (*cur == ' ') cur++;
1670
	if (*cur == 0) return(0);
1671
	if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1672
	if ((cur[1] == ':') || (cur[2] == ':')) {
1673
	    while ((*cur >= '0') && (*cur <= '9'))
1674
		hour = (hour * 10) + (*cur++ - '0');
1675
	    if (*cur == ':') cur++;
1676
	    while ((*cur >= '0') && (*cur <= '9'))
1677
		minute = (minute * 10) + (*cur++ - '0');
1678
	} else {
1679
	    while ((*cur >= '0') && (*cur <= '9'))
1680
		year = (year * 10) + (*cur++ - '0');
1681
	}
1682
	while (*cur == ' ') cur++;
1683
	if (*cur == 0) return(0);
1684
	i = 0;
1685
	while ((*cur != '\n')  && (*cur != '\r')) {
1686
	    if (i < 150)
1687
		filename[i++] = *cur;
1688
	    cur++;
1689
	    if (*cur == 0) return(0);
1690
	}
1691
	filename[i] = 0;
1692
	if ((*cur != '\n') && (*cur != '\r'))
1693
	    return(0);
1694
	while ((*cur == '\n')  || (*cur == '\r'))
1695
	    cur++;
1696
    }
1697
    if (callback != NULL) {
1698
        callback(userData, filename, attrib, owner, group, size, links,
1699
		 year, month, day, hour, minute);
1700
    }
1701
    return(cur - list);
1702
}
1703
1704
/**
1705
 * xmlNanoFTPList:
1706
 * @ctx:  an FTP context
1707
 * @callback:  the user callback
1708
 * @userData:  the user callback data
1709
 * @filename:  optional files to list
1710
 *
1711
 * Do a listing on the server. All files info are passed back
1712
 * in the callbacks.
1713
 *
1714
 * Returns -1 incase of error, 0 otherwise
1715
 */
1716
1717
int
1718
xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1719
	       const char *filename) {
1720
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1721
    char buf[4096 + 1];
1722
    int len, res;
1723
    int indx = 0, base;
1724
    fd_set rfd, efd;
1725
    struct timeval tv;
1726
1727
    if (ctxt == NULL) return (-1);
1728
    if (filename == NULL) {
1729
        if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1730
	    return(-1);
1731
	ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1732
	if (ctxt->dataFd == -1)
1733
	    return(-1);
1734
	snprintf(buf, sizeof(buf), "LIST -L\r\n");
1735
    } else {
1736
	if (filename[0] != '/') {
1737
	    if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1738
		return(-1);
1739
	}
1740
	ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1741
	if (ctxt->dataFd == -1)
1742
	    return(-1);
1743
	snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
1744
    }
1745
    buf[sizeof(buf) - 1] = 0;
1746
    len = strlen(buf);
1747
#ifdef DEBUG_FTP
1748
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1749
#endif
1750
    res = send(ctxt->controlFd, buf, len, 0);
1751
    if (res < 0) {
1752
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1753
	closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1754
	return(res);
1755
    }
1756
    res = xmlNanoFTPReadResponse(ctxt);
1757
    if (res != 1) {
1758
	closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1759
	return(-res);
1760
    }
1761
1762
    do {
1763
	tv.tv_sec = 1;
1764
	tv.tv_usec = 0;
1765
	FD_ZERO(&rfd);
1766
	FD_SET(ctxt->dataFd, &rfd);
1767
	FD_ZERO(&efd);
1768
	FD_SET(ctxt->dataFd, &efd);
1769
	res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1770
	if (res < 0) {
1771
#ifdef DEBUG_FTP
1772
	    perror("select");
1773
#endif
1774
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1775
	    return(-1);
1776
	}
1777
	if (res == 0) {
1778
	    res = xmlNanoFTPCheckResponse(ctxt);
1779
	    if (res < 0) {
1780
		closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1781
		ctxt->dataFd = -1;
1782
		return(-1);
1783
	    }
1784
	    if (res == 2) {
1785
		closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1786
		return(0);
1787
	    }
1788
1789
	    continue;
1790
	}
1791
1792
	if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
1793
	    __xmlIOErr(XML_FROM_FTP, 0, "recv");
1794
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1795
	    ctxt->dataFd = -1;
1796
	    return(-1);
1797
	}
1798
#ifdef DEBUG_FTP
1799
        write(1, &buf[indx], len);
1800
#endif
1801
	indx += len;
1802
	buf[indx] = 0;
1803
	base = 0;
1804
	do {
1805
	    res = xmlNanoFTPParseList(&buf[base], callback, userData);
1806
	    base += res;
1807
	} while (res > 0);
1808
1809
	memmove(&buf[0], &buf[base], indx - base);
1810
	indx -= base;
1811
    } while (len != 0);
1812
    xmlNanoFTPCloseConnection(ctxt);
1813
    return(0);
1814
}
1815
1816
/**
1817
 * xmlNanoFTPGetSocket:
1818
 * @ctx:  an FTP context
1819
 * @filename:  the file to retrieve (or NULL if path is in context).
1820
 *
1821
 * Initiate fetch of the given file from the server.
1822
 *
1823
 * Returns the socket for the data connection, or <0 in case of error
1824
 */
1825
1826
1827
int
1828
xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1829
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1830
    char buf[300];
1831
    int res, len;
1832
    if (ctx == NULL)
1833
	return(-1);
1834
    if ((filename == NULL) && (ctxt->path == NULL))
1835
	return(-1);
1836
    ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1837
    if (ctxt->dataFd == -1)
1838
	return(-1);
1839
1840
    snprintf(buf, sizeof(buf), "TYPE I\r\n");
1841
    len = strlen(buf);
1842
#ifdef DEBUG_FTP
1843
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1844
#endif
1845
    res = send(ctxt->controlFd, buf, len, 0);
1846
    if (res < 0) {
1847
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1848
	closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1849
	return(res);
1850
    }
1851
    res = xmlNanoFTPReadResponse(ctxt);
1852
    if (res != 2) {
1853
	closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1854
	return(-res);
1855
    }
1856
    if (filename == NULL)
1857
	snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
1858
    else
1859
	snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
1860
    buf[sizeof(buf) - 1] = 0;
1861
    len = strlen(buf);
1862
#ifdef DEBUG_FTP
1863
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1864
#endif
1865
    res = send(ctxt->controlFd, buf, len, 0);
1866
    if (res < 0) {
1867
	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1868
	closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1869
	return(res);
1870
    }
1871
    res = xmlNanoFTPReadResponse(ctxt);
1872
    if (res != 1) {
1873
	closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1874
	return(-res);
1875
    }
1876
    return(ctxt->dataFd);
1877
}
1878
1879
/**
1880
 * xmlNanoFTPGet:
1881
 * @ctx:  an FTP context
1882
 * @callback:  the user callback
1883
 * @userData:  the user callback data
1884
 * @filename:  the file to retrieve
1885
 *
1886
 * Fetch the given file from the server. All data are passed back
1887
 * in the callbacks. The last callback has a size of 0 block.
1888
 *
1889
 * Returns -1 incase of error, 0 otherwise
1890
 */
1891
1892
int
1893
xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1894
	      const char *filename) {
1895
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1896
    char buf[4096];
1897
    int len = 0, res;
1898
    fd_set rfd;
1899
    struct timeval tv;
1900
1901
    if (ctxt == NULL) return(-1);
1902
    if ((filename == NULL) && (ctxt->path == NULL))
1903
	return(-1);
1904
    if (callback == NULL)
1905
	return(-1);
1906
    if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1907
	return(-1);
1908
1909
    do {
1910
	tv.tv_sec = 1;
1911
	tv.tv_usec = 0;
1912
	FD_ZERO(&rfd);
1913
	FD_SET(ctxt->dataFd, &rfd);
1914
	res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1915
	if (res < 0) {
1916
#ifdef DEBUG_FTP
1917
	    perror("select");
1918
#endif
1919
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1920
	    return(-1);
1921
	}
1922
	if (res == 0) {
1923
	    res = xmlNanoFTPCheckResponse(ctxt);
1924
	    if (res < 0) {
1925
		closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1926
		ctxt->dataFd = -1;
1927
		return(-1);
1928
	    }
1929
	    if (res == 2) {
1930
		closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1931
		return(0);
1932
	    }
1933
1934
	    continue;
1935
	}
1936
	if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1937
	    __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1938
	    callback(userData, buf, len);
1939
	    closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1940
	    return(-1);
1941
	}
1942
	callback(userData, buf, len);
1943
    } while (len != 0);
1944
1945
    return(xmlNanoFTPCloseConnection(ctxt));
1946
}
1947
1948
/**
1949
 * xmlNanoFTPRead:
1950
 * @ctx:  the FTP context
1951
 * @dest:  a buffer
1952
 * @len:  the buffer length
1953
 *
1954
 * This function tries to read @len bytes from the existing FTP connection
1955
 * and saves them in @dest. This is a blocking call.
1956
 *
1957
 * Returns the number of byte read. 0 is an indication of an end of connection.
1958
 *         -1 indicates a parameter error.
1959
 */
1960
int
1961
xmlNanoFTPRead(void *ctx, void *dest, int len) {
1962
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1963
1964
    if (ctx == NULL) return(-1);
1965
    if (ctxt->dataFd < 0) return(0);
1966
    if (dest == NULL) return(-1);
1967
    if (len <= 0) return(0);
1968
1969
    len = recv(ctxt->dataFd, dest, len, 0);
1970
    if (len <= 0) {
1971
	if (len < 0)
1972
	    __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1973
	xmlNanoFTPCloseConnection(ctxt);
1974
    }
1975
#ifdef DEBUG_FTP
1976
    xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1977
#endif
1978
    return(len);
1979
}
1980
1981
/**
1982
 * xmlNanoFTPOpen:
1983
 * @URL: the URL to the resource
1984
 *
1985
 * Start to fetch the given ftp:// resource
1986
 *
1987
 * Returns an FTP context, or NULL 
1988
 */
1989
1990
void*
1991
xmlNanoFTPOpen(const char *URL) {
1992
    xmlNanoFTPCtxtPtr ctxt;
1993
    int sock;
1994
1995
    xmlNanoFTPInit();
1996
    if (URL == NULL) return(NULL);
1997
    if (strncmp("ftp://", URL, 6)) return(NULL);
1998
1999
    ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2000
    if (ctxt == NULL) return(NULL);
2001
    if (xmlNanoFTPConnect(ctxt) < 0) {
2002
	xmlNanoFTPFreeCtxt(ctxt);
2003
	return(NULL);
2004
    }
2005
    sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2006
    if (sock < 0) {
2007
	xmlNanoFTPFreeCtxt(ctxt);
2008
	return(NULL);
2009
    }
2010
    return(ctxt);
2011
}
2012
2013
/**
2014
 * xmlNanoFTPClose:
2015
 * @ctx: an FTP context
2016
 *
2017
 * Close the connection and both control and transport
2018
 *
2019
 * Returns -1 incase of error, 0 otherwise
2020
 */
2021
2022
int
2023
xmlNanoFTPClose(void *ctx) {
2024
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2025
2026
    if (ctxt == NULL)
2027
	return(-1);
2028
2029
    if (ctxt->dataFd >= 0) {
2030
	closesocket(ctxt->dataFd);
2031
	ctxt->dataFd = -1;
2032
    }
2033
    if (ctxt->controlFd >= 0) {
2034
	xmlNanoFTPQuit(ctxt);
2035
	closesocket(ctxt->controlFd);
2036
	ctxt->controlFd = -1;
2037
    }
2038
    xmlNanoFTPFreeCtxt(ctxt);
2039
    return(0);
2040
}
2041
2042
#ifdef STANDALONE
2043
/************************************************************************
2044
 * 									*
2045
 * 			Basic test in Standalone mode			*
2046
 * 									*
2047
 ************************************************************************/
2048
static
2049
void ftpList(void *userData, const char *filename, const char* attrib,
2050
	     const char *owner, const char *group, unsigned long size, int links,
2051
	     int year, const char *month, int day, int hour, int minute) {
2052
    xmlGenericError(xmlGenericErrorContext,
2053
	    "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2054
}
2055
static
2056
void ftpData(void *userData, const char *data, int len) {
2057
    if (userData == NULL) return;
2058
    if (len <= 0) {
2059
	fclose((FILE*)userData);
2060
	return;
2061
    }	
2062
    fwrite(data, len, 1, (FILE*)userData);
2063
}
2064
2065
int main(int argc, char **argv) {
2066
    void *ctxt;
2067
    FILE *output;
2068
    char *tstfile = NULL;
2069
2070
    xmlNanoFTPInit();
2071
    if (argc > 1) {
2072
	ctxt = xmlNanoFTPNewCtxt(argv[1]);
2073
	if (xmlNanoFTPConnect(ctxt) < 0) {
2074
	    xmlGenericError(xmlGenericErrorContext,
2075
		    "Couldn't connect to %s\n", argv[1]);
2076
	    exit(1);
2077
	}
2078
	if (argc > 2)
2079
	    tstfile = argv[2];
2080
    } else
2081
	ctxt = xmlNanoFTPConnectTo("localhost", 0);
2082
    if (ctxt == NULL) {
2083
        xmlGenericError(xmlGenericErrorContext,
2084
		"Couldn't connect to localhost\n");
2085
        exit(1);
2086
    }
2087
    xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2088
    output = fopen("/tmp/tstdata", "w");
2089
    if (output != NULL) {
2090
	if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2091
	    xmlGenericError(xmlGenericErrorContext,
2092
		    "Failed to get file\n");
2093
	
2094
    }
2095
    xmlNanoFTPClose(ctxt);
2096
    xmlMemoryDump();
2097
    exit(0);
2098
}
2099
#endif /* STANDALONE */
2100
#else /* !LIBXML_FTP_ENABLED */
2101
#ifdef STANDALONE
2102
#include <stdio.h>
2103
int main(int argc, char **argv) {
2104
    xmlGenericError(xmlGenericErrorContext,
2105
	    "%s : FTP support not compiled in\n", argv[0]);
2106
    return(0);
2107
}
2108
#endif /* STANDALONE */
2109
#endif /* LIBXML_FTP_ENABLED */
2110
#define bottom_nanoftp
2111
#include "elfgcchack.h"