blob: c7d76c7dc89fde0c046b93ca6a5117d192100fee [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
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
Bjorn Reese70a9da52001-04-21 16:57:29 +000015#else /* STANDALONE */
Daniel Veillardf3afa7d2001-06-09 13:52:58 +000016#define NEED_SOCKETS
Owen Taylor3473f882001-02-23 17:55:21 +000017#endif /* STANDALONE */
18
Daniel Veillard5e2dace2001-07-18 19:30:27 +000019#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000020
21#ifdef LIBXML_FTP_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +000022#include <string.h>
23
24#ifdef HAVE_STDLIB_H
25#include <stdlib.h>
26#endif
27#ifdef HAVE_UNISTD_H
28#include <unistd.h>
29#endif
30#ifdef HAVE_SYS_SOCKET_H
31#include <sys/socket.h>
32#endif
33#ifdef HAVE_NETINET_IN_H
34#include <netinet/in.h>
35#endif
36#ifdef HAVE_ARPA_INET_H
37#include <arpa/inet.h>
38#endif
39#ifdef HAVE_NETDB_H
40#include <netdb.h>
41#endif
42#ifdef HAVE_FCNTL_H
43#include <fcntl.h>
44#endif
45#ifdef HAVE_ERRNO_H
46#include <errno.h>
47#endif
48#ifdef HAVE_SYS_TIME_H
49#include <sys/time.h>
50#endif
51#ifdef HAVE_SYS_SELECT_H
52#include <sys/select.h>
53#endif
54#ifdef HAVE_STRINGS_H
55#include <strings.h>
56#endif
57
58#include <libxml/xmlmemory.h>
Daniel Veillardd0463562001-10-13 09:15:48 +000059#include <libxml/parser.h>
Owen Taylor3473f882001-02-23 17:55:21 +000060#include <libxml/xmlerror.h>
Daniel Veillardd0463562001-10-13 09:15:48 +000061#include <libxml/nanoftp.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000062#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000063
64/* #define DEBUG_FTP 1 */
65#ifdef STANDALONE
66#ifndef DEBUG_FTP
67#define DEBUG_FTP 1
68#endif
69#endif
70
71/**
72 * A couple portability macros
73 */
74#ifndef _WINSOCKAPI_
75#define closesocket(s) close(s)
76#define SOCKET int
77#endif
Daniel Veillardacf7ff02001-10-29 20:21:47 +000078#if defined(VMS) || defined(__VMS)
79#define SOCKLEN_T unsigned int
80#endif
Owen Taylor3473f882001-02-23 17:55:21 +000081
Owen Taylor3473f882001-02-23 17:55:21 +000082#define FTP_COMMAND_OK 200
83#define FTP_SYNTAX_ERROR 500
84#define FTP_GET_PASSWD 331
85#define FTP_BUF_SIZE 512
86
87typedef struct xmlNanoFTPCtxt {
88 char *protocol; /* the protocol name */
89 char *hostname; /* the host name */
90 int port; /* the port */
91 char *path; /* the path within the URL */
92 char *user; /* user string */
93 char *passwd; /* passwd string */
94 struct sockaddr_in ftpAddr; /* the socket address struct */
95 int passive; /* currently we support only passive !!! */
96 SOCKET controlFd; /* the file descriptor for the control socket */
97 SOCKET dataFd; /* the file descriptor for the data socket */
98 int state; /* WRITE / READ / CLOSED */
99 int returnValue; /* the protocol return value */
100 /* buffer for data received from the control connection */
101 char controlBuf[FTP_BUF_SIZE + 1];
102 int controlBufIndex;
103 int controlBufUsed;
104 int controlBufAnswer;
105} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
106
107static int initialized = 0;
108static char *proxy = NULL; /* the proxy name if any */
109static int proxyPort = 0; /* the proxy port if any */
110static char *proxyUser = NULL; /* user for proxy authentication */
111static char *proxyPasswd = NULL;/* passwd for proxy authentication */
112static int proxyType = 0; /* uses TYPE or a@b ? */
113
114/**
115 * xmlNanoFTPInit:
116 *
117 * Initialize the FTP protocol layer.
118 * Currently it just checks for proxy informations,
119 * and get the hostname
120 */
121
122void
123xmlNanoFTPInit(void) {
124 const char *env;
125#ifdef _WINSOCKAPI_
126 WSADATA wsaData;
127#endif
128
129 if (initialized)
130 return;
131
132#ifdef _WINSOCKAPI_
133 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
134 return;
135#endif
136
Owen Taylor3473f882001-02-23 17:55:21 +0000137 proxyPort = 21;
138 env = getenv("no_proxy");
139 if (env != NULL)
140 return;
141 env = getenv("ftp_proxy");
142 if (env != NULL) {
143 xmlNanoFTPScanProxy(env);
144 } else {
145 env = getenv("FTP_PROXY");
146 if (env != NULL) {
147 xmlNanoFTPScanProxy(env);
148 }
149 }
150 env = getenv("ftp_proxy_user");
151 if (env != NULL) {
152 proxyUser = xmlMemStrdup(env);
153 }
154 env = getenv("ftp_proxy_password");
155 if (env != NULL) {
156 proxyPasswd = xmlMemStrdup(env);
157 }
158 initialized = 1;
159}
160
161/**
Daniel Veillarde356c282001-03-10 12:32:04 +0000162 * xmlNanoFTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000163 *
164 * Cleanup the FTP protocol layer. This cleanup proxy informations.
165 */
166
167void
168xmlNanoFTPCleanup(void) {
169 if (proxy != NULL) {
170 xmlFree(proxy);
171 proxy = NULL;
172 }
173 if (proxyUser != NULL) {
174 xmlFree(proxyUser);
175 proxyUser = NULL;
176 }
177 if (proxyPasswd != NULL) {
178 xmlFree(proxyPasswd);
179 proxyPasswd = NULL;
180 }
Owen Taylor3473f882001-02-23 17:55:21 +0000181#ifdef _WINSOCKAPI_
182 if (initialized)
183 WSACleanup();
184#endif
185 initialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000186}
187
188/**
189 * xmlNanoFTPProxy:
190 * @host: the proxy host name
191 * @port: the proxy port
192 * @user: the proxy user name
193 * @passwd: the proxy password
194 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
195 *
196 * Setup the FTP proxy informations.
197 * This can also be done by using ftp_proxy ftp_proxy_user and
198 * ftp_proxy_password environment variables.
199 */
200
201void
202xmlNanoFTPProxy(const char *host, int port, const char *user,
203 const char *passwd, int type) {
204 if (proxy != NULL)
205 xmlFree(proxy);
206 if (proxyUser != NULL)
207 xmlFree(proxyUser);
208 if (proxyPasswd != NULL)
209 xmlFree(proxyPasswd);
210 if (host)
211 proxy = xmlMemStrdup(host);
212 if (user)
213 proxyUser = xmlMemStrdup(user);
214 if (passwd)
215 proxyPasswd = xmlMemStrdup(passwd);
216 proxyPort = port;
217 proxyType = type;
218}
219
220/**
221 * xmlNanoFTPScanURL:
222 * @ctx: an FTP context
223 * @URL: The URL used to initialize the context
224 *
225 * (Re)Initialize an FTP context by parsing the URL and finding
226 * the protocol host port and path it indicates.
227 */
228
229static void
230xmlNanoFTPScanURL(void *ctx, const char *URL) {
231 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
232 const char *cur = URL;
233 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000234 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000235 int port = 0;
236
237 if (ctxt->protocol != NULL) {
238 xmlFree(ctxt->protocol);
239 ctxt->protocol = NULL;
240 }
241 if (ctxt->hostname != NULL) {
242 xmlFree(ctxt->hostname);
243 ctxt->hostname = NULL;
244 }
245 if (ctxt->path != NULL) {
246 xmlFree(ctxt->path);
247 ctxt->path = NULL;
248 }
249 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000250 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000251 while (*cur != 0) {
252 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000253 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000254 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000255 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000256 cur += 3;
257 break;
258 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000259 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000260 }
261 if (*cur == 0) return;
262
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000263 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000264 while (1) {
265 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000266 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000267 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000268 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000269 cur += 1;
270 while ((*cur >= '0') && (*cur <= '9')) {
271 port *= 10;
272 port += *cur - '0';
273 cur++;
274 }
275 if (port != 0) ctxt->port = port;
276 while ((cur[0] != '/') && (*cur != 0))
277 cur++;
278 break;
279 }
280 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000281 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000282 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000283 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000284 break;
285 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000286 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000287 }
288 if (*cur == 0)
289 ctxt->path = xmlMemStrdup("/");
290 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000291 indx = 0;
292 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000293 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000294 buf[indx++] = *cur++;
295 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000296 ctxt->path = xmlMemStrdup(buf);
297 }
298}
299
300/**
301 * xmlNanoFTPUpdateURL:
302 * @ctx: an FTP context
303 * @URL: The URL used to update the context
304 *
305 * Update an FTP context by parsing the URL and finding
306 * new path it indicates. If there is an error in the
307 * protocol, hostname, port or other information, the
308 * error is raised. It indicates a new connection has to
309 * be established.
310 *
311 * Returns 0 if Ok, -1 in case of error (other host).
312 */
313
314int
315xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
316 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
317 const char *cur = URL;
318 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000319 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000320 int port = 0;
321
322 if (URL == NULL)
323 return(-1);
324 if (ctxt == NULL)
325 return(-1);
326 if (ctxt->protocol == NULL)
327 return(-1);
328 if (ctxt->hostname == NULL)
329 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000330 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000331 while (*cur != 0) {
332 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000333 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000334 if (strcmp(ctxt->protocol, buf))
335 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000336 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000337 cur += 3;
338 break;
339 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000340 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000341 }
342 if (*cur == 0)
343 return(-1);
344
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000345 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000346 while (1) {
347 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000348 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000349 if (strcmp(ctxt->hostname, buf))
350 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000351 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000352 cur += 1;
353 while ((*cur >= '0') && (*cur <= '9')) {
354 port *= 10;
355 port += *cur - '0';
356 cur++;
357 }
358 if (port != ctxt->port)
359 return(-1);
360 while ((cur[0] != '/') && (*cur != 0))
361 cur++;
362 break;
363 }
364 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000365 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000366 if (strcmp(ctxt->hostname, buf))
367 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000368 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000369 break;
370 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000371 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000372 }
373 if (ctxt->path != NULL) {
374 xmlFree(ctxt->path);
375 ctxt->path = NULL;
376 }
377
378 if (*cur == 0)
379 ctxt->path = xmlMemStrdup("/");
380 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000381 indx = 0;
382 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000383 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000384 buf[indx++] = *cur++;
385 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000386 ctxt->path = xmlMemStrdup(buf);
387 }
388 return(0);
389}
390
391/**
392 * xmlNanoFTPScanProxy:
393 * @URL: The proxy URL used to initialize the proxy context
394 *
395 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
396 * the protocol host port it indicates.
397 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
398 * A NULL URL cleans up proxy informations.
399 */
400
401void
402xmlNanoFTPScanProxy(const char *URL) {
403 const char *cur = URL;
404 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000405 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000406 int port = 0;
407
408 if (proxy != NULL) {
409 xmlFree(proxy);
410 proxy = NULL;
411 }
412 if (proxyPort != 0) {
413 proxyPort = 0;
414 }
415#ifdef DEBUG_FTP
416 if (URL == NULL)
417 xmlGenericError(xmlGenericErrorContext, "Removing FTP proxy info\n");
418 else
419 xmlGenericError(xmlGenericErrorContext, "Using FTP proxy %s\n", URL);
420#endif
421 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000422 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000423 while (*cur != 0) {
424 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000425 buf[indx] = 0;
426 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000427 cur += 3;
428 break;
429 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000430 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000431 }
432 if (*cur == 0) return;
433
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000434 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000435 while (1) {
436 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000437 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000438 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000439 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000440 cur += 1;
441 while ((*cur >= '0') && (*cur <= '9')) {
442 port *= 10;
443 port += *cur - '0';
444 cur++;
445 }
446 if (port != 0) proxyPort = port;
447 while ((cur[0] != '/') && (*cur != 0))
448 cur++;
449 break;
450 }
451 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000452 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000453 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000454 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000455 break;
456 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000457 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000458 }
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
470void*
471xmlNanoFTPNewCtxt(const char *URL) {
472 xmlNanoFTPCtxtPtr ret;
473
474 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
475 if (ret == NULL) return(NULL);
476
477 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
478 ret->port = 21;
479 ret->passive = 1;
480 ret->returnValue = 0;
481 ret->controlBufIndex = 0;
482 ret->controlBufUsed = 0;
483
484 if (URL != NULL)
485 xmlNanoFTPScanURL(ret, URL);
486
487 return(ret);
488}
489
490/**
491 * xmlNanoFTPFreeCtxt:
492 * @ctx: an FTP context
493 *
494 * Frees the context after closing the connection.
495 */
496
497void
498xmlNanoFTPFreeCtxt(void * ctx) {
499 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
500 if (ctxt == NULL) return;
501 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
502 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
503 if (ctxt->path != NULL) xmlFree(ctxt->path);
504 ctxt->passive = 1;
505 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
506 ctxt->controlFd = -1;
507 ctxt->controlBufIndex = -1;
508 ctxt->controlBufUsed = -1;
509 xmlFree(ctxt);
510}
511
512/**
513 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000514 * @buf: the buffer containing the response
515 * @len: the buffer length
516 *
517 * Parsing of the server answer, we just extract the code.
518 *
519 * returns 0 for errors
520 * +XXX for last line of response
521 * -XXX for response to be continued
522 */
523static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000524xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000525 int val = 0;
526
527 if (len < 3) return(-1);
528 if ((*buf >= '0') && (*buf <= '9'))
529 val = val * 10 + (*buf - '0');
530 else
531 return(0);
532 buf++;
533 if ((*buf >= '0') && (*buf <= '9'))
534 val = val * 10 + (*buf - '0');
535 else
536 return(0);
537 buf++;
538 if ((*buf >= '0') && (*buf <= '9'))
539 val = val * 10 + (*buf - '0');
540 else
541 return(0);
542 buf++;
543 if (*buf == '-')
544 return(-val);
545 return(val);
546}
547
548/**
549 * xmlNanoFTPGetMore:
550 * @ctx: an FTP context
551 *
552 * Read more information from the FTP control connection
553 * Returns the number of bytes read, < 0 indicates an error
554 */
555static int
556xmlNanoFTPGetMore(void *ctx) {
557 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
558 int len;
559 int size;
560
561 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
562#ifdef DEBUG_FTP
563 xmlGenericError(xmlGenericErrorContext,
564 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
565 ctxt->controlBufIndex);
566#endif
567 return(-1);
568 }
569
570 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
571#ifdef DEBUG_FTP
572 xmlGenericError(xmlGenericErrorContext,
573 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
574 ctxt->controlBufUsed);
575#endif
576 return(-1);
577 }
578 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
579#ifdef DEBUG_FTP
580 xmlGenericError(xmlGenericErrorContext,
581 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
582 ctxt->controlBufIndex, ctxt->controlBufUsed);
583#endif
584 return(-1);
585 }
586
587 /*
588 * First pack the control buffer
589 */
590 if (ctxt->controlBufIndex > 0) {
591 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
592 ctxt->controlBufUsed - ctxt->controlBufIndex);
593 ctxt->controlBufUsed -= ctxt->controlBufIndex;
594 ctxt->controlBufIndex = 0;
595 }
596 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
597 if (size == 0) {
598#ifdef DEBUG_FTP
599 xmlGenericError(xmlGenericErrorContext,
600 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
601#endif
602 return(0);
603 }
604
605 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000606 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000607 */
608 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
609 size, 0)) < 0) {
610 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
611 ctxt->controlFd = -1;
612 return(-1);
613 }
614#ifdef DEBUG_FTP
615 xmlGenericError(xmlGenericErrorContext,
616 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
617 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
618#endif
619 ctxt->controlBufUsed += len;
620 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
621
622 return(len);
623}
624
625/**
626 * xmlNanoFTPReadResponse:
627 * @ctx: an FTP context
628 *
629 * Read the response from the FTP server after a command.
630 * Returns the code number
631 */
632static int
633xmlNanoFTPReadResponse(void *ctx) {
634 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
635 char *ptr, *end;
636 int len;
637 int res = -1, cur = -1;
638
639get_more:
640 /*
641 * Assumes everything up to controlBuf[controlBufIndex] has been read
642 * and analyzed.
643 */
644 len = xmlNanoFTPGetMore(ctx);
645 if (len < 0) {
646 return(-1);
647 }
648 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
649 return(-1);
650 }
651 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
652 end = &ctxt->controlBuf[ctxt->controlBufUsed];
653
654#ifdef DEBUG_FTP
655 xmlGenericError(xmlGenericErrorContext,
656 "\n<<<\n%s\n--\n", ptr);
657#endif
658 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000659 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000660 if (cur > 0) {
661 /*
662 * Successfully scanned the control code, scratch
663 * till the end of the line, but keep the index to be
664 * able to analyze the result if needed.
665 */
666 res = cur;
667 ptr += 3;
668 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
669 while ((ptr < end) && (*ptr != '\n')) ptr++;
670 if (*ptr == '\n') ptr++;
671 if (*ptr == '\r') ptr++;
672 break;
673 }
674 while ((ptr < end) && (*ptr != '\n')) ptr++;
675 if (ptr >= end) {
676 ctxt->controlBufIndex = ctxt->controlBufUsed;
677 goto get_more;
678 }
679 if (*ptr != '\r') ptr++;
680 }
681
682 if (res < 0) goto get_more;
683 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
684#ifdef DEBUG_FTP
685 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
686 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
687#endif
688
689#ifdef DEBUG_FTP
690 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
691#endif
692 return(res / 100);
693}
694
695/**
696 * xmlNanoFTPGetResponse:
697 * @ctx: an FTP context
698 *
699 * Get the response from the FTP server after a command.
700 * Returns the code number
701 */
702
703int
704xmlNanoFTPGetResponse(void *ctx) {
705 int res;
706
707 res = xmlNanoFTPReadResponse(ctx);
708
709 return(res);
710}
711
712/**
713 * xmlNanoFTPCheckResponse:
714 * @ctx: an FTP context
715 *
716 * Check if there is a response from the FTP server after a command.
717 * Returns the code number, or 0
718 */
719
720int
721xmlNanoFTPCheckResponse(void *ctx) {
722 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
723 fd_set rfd;
724 struct timeval tv;
725
726 tv.tv_sec = 0;
727 tv.tv_usec = 0;
728 FD_ZERO(&rfd);
729 FD_SET(ctxt->controlFd, &rfd);
730 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
731 case 0:
732 return(0);
733 case -1:
734#ifdef DEBUG_FTP
735 perror("select");
736#endif
737 return(-1);
738
739 }
740
741 return(xmlNanoFTPReadResponse(ctx));
742}
743
744/**
745 * Send the user authentification
746 */
747
748static int
749xmlNanoFTPSendUser(void *ctx) {
750 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
751 char buf[200];
752 int len;
753 int res;
754
755 if (ctxt->user == NULL)
756 sprintf(buf, "USER anonymous\r\n");
757 else
Owen Taylor3473f882001-02-23 17:55:21 +0000758 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000759 buf[sizeof(buf) - 1] = 0;
760 len = strlen(buf);
761#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000762 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000763#endif
764 res = send(ctxt->controlFd, buf, len, 0);
765 if (res < 0) return(res);
766 return(0);
767}
768
769/**
770 * Send the password authentification
771 */
772
773static int
774xmlNanoFTPSendPasswd(void *ctx) {
775 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
776 char buf[200];
777 int len;
778 int res;
779
780 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000781 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000782 else
Owen Taylor3473f882001-02-23 17:55:21 +0000783 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000784 buf[sizeof(buf) - 1] = 0;
785 len = strlen(buf);
786#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000787 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000788#endif
789 res = send(ctxt->controlFd, buf, len, 0);
790 if (res < 0) return(res);
791 return(0);
792}
793
794/**
795 * xmlNanoFTPQuit:
796 * @ctx: an FTP context
797 *
798 * Send a QUIT command to the server
799 *
800 * Returns -1 in case of error, 0 otherwise
801 */
802
803
804int
805xmlNanoFTPQuit(void *ctx) {
806 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
807 char buf[200];
808 int len;
809 int res;
810
811 sprintf(buf, "QUIT\r\n");
812 len = strlen(buf);
813#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000814 xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
Owen Taylor3473f882001-02-23 17:55:21 +0000815#endif
816 res = send(ctxt->controlFd, buf, len, 0);
817 return(0);
818}
819
820/**
821 * xmlNanoFTPConnect:
822 * @ctx: an FTP context
823 *
824 * Tries to open a control connection
825 *
826 * Returns -1 in case of error, 0 otherwise
827 */
828
829int
830xmlNanoFTPConnect(void *ctx) {
831 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
832 struct hostent *hp;
833 int port;
834 int res;
835
836 if (ctxt == NULL)
837 return(-1);
838 if (ctxt->hostname == NULL)
839 return(-1);
840
841 /*
842 * do the blocking DNS query.
843 */
844 if (proxy)
845 hp = gethostbyname(proxy);
846 else
847 hp = gethostbyname(ctxt->hostname);
848 if (hp == NULL)
849 return(-1);
850
851 /*
852 * Prepare the socket
853 */
854 memset(&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
855 ctxt->ftpAddr.sin_family = AF_INET;
856 memcpy(&ctxt->ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
857 if (proxy) {
858 port = proxyPort;
859 } else {
860 port = ctxt->port;
861 }
862 if (port == 0)
863 port = 21;
864 ctxt->ftpAddr.sin_port = htons(port);
865 ctxt->controlFd = socket(AF_INET, SOCK_STREAM, 0);
866 if (ctxt->controlFd < 0)
867 return(-1);
868
869 /*
870 * Do the connect.
871 */
872 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
873 sizeof(struct sockaddr_in)) < 0) {
874 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
875 ctxt->controlFd = -1;
876 return(-1);
877 }
878
879 /*
880 * Wait for the HELLO from the server.
881 */
882 res = xmlNanoFTPGetResponse(ctxt);
883 if (res != 2) {
884 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
885 ctxt->controlFd = -1;
886 return(-1);
887 }
888
889 /*
890 * State diagram for the login operation on the FTP server
891 *
892 * Reference: RFC 959
893 *
894 * 1
895 * +---+ USER +---+------------->+---+
896 * | B |---------->| W | 2 ---->| E |
897 * +---+ +---+------ | -->+---+
898 * | | | | |
899 * 3 | | 4,5 | | |
900 * -------------- ----- | | |
901 * | | | | |
902 * | | | | |
903 * | --------- |
904 * | 1| | | |
905 * V | | | |
906 * +---+ PASS +---+ 2 | ------>+---+
907 * | |---------->| W |------------->| S |
908 * +---+ +---+ ---------->+---+
909 * | | | | |
910 * 3 | |4,5| | |
911 * -------------- -------- |
912 * | | | | |
913 * | | | | |
914 * | -----------
915 * | 1,3| | | |
916 * V | 2| | |
917 * +---+ ACCT +---+-- | ----->+---+
918 * | |---------->| W | 4,5 -------->| F |
919 * +---+ +---+------------->+---+
920 *
921 * Of course in case of using a proxy this get really nasty and is not
922 * standardized at all :-(
923 */
924 if (proxy) {
925 int len;
926 char buf[400];
927
928 if (proxyUser != NULL) {
929 /*
930 * We need proxy auth
931 */
Owen Taylor3473f882001-02-23 17:55:21 +0000932 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +0000933 buf[sizeof(buf) - 1] = 0;
934 len = strlen(buf);
935#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000936 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000937#endif
938 res = send(ctxt->controlFd, buf, len, 0);
939 if (res < 0) {
940 closesocket(ctxt->controlFd);
941 ctxt->controlFd = -1;
942 return(res);
943 }
944 res = xmlNanoFTPGetResponse(ctxt);
945 switch (res) {
946 case 2:
947 if (proxyPasswd == NULL)
948 break;
949 case 3:
950 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000951 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +0000952 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000953 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000954 buf[sizeof(buf) - 1] = 0;
955 len = strlen(buf);
956#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000957 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000958#endif
959 res = send(ctxt->controlFd, buf, len, 0);
960 if (res < 0) {
961 closesocket(ctxt->controlFd);
962 ctxt->controlFd = -1;
963 return(res);
964 }
965 res = xmlNanoFTPGetResponse(ctxt);
966 if (res > 3) {
967 closesocket(ctxt->controlFd);
968 ctxt->controlFd = -1;
969 return(-1);
970 }
971 break;
972 case 1:
973 break;
974 case 4:
975 case 5:
976 case -1:
977 default:
978 closesocket(ctxt->controlFd);
979 ctxt->controlFd = -1;
980 return(-1);
981 }
982 }
983
984 /*
985 * We assume we don't need more authentication to the proxy
986 * and that it succeeded :-\
987 */
988 switch (proxyType) {
989 case 0:
990 /* we will try in seqence */
991 case 1:
992 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +0000993 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +0000994 buf[sizeof(buf) - 1] = 0;
995 len = strlen(buf);
996#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000997 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000998#endif
999 res = send(ctxt->controlFd, buf, len, 0);
1000 if (res < 0) {
1001 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1002 ctxt->controlFd = -1;
1003 return(res);
1004 }
1005 res = xmlNanoFTPGetResponse(ctxt);
1006 if (res == 2) {
1007 /* we assume it worked :-\ 1 is error for SITE command */
1008 proxyType = 1;
1009 break;
1010 }
1011 if (proxyType == 1) {
1012 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1013 ctxt->controlFd = -1;
1014 return(-1);
1015 }
1016 case 2:
1017 /* USER user@host command */
1018 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001019 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1020 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001021 else
Owen Taylor3473f882001-02-23 17:55:21 +00001022 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1023 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001024 buf[sizeof(buf) - 1] = 0;
1025 len = strlen(buf);
1026#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001027 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001028#endif
1029 res = send(ctxt->controlFd, buf, len, 0);
1030 if (res < 0) {
1031 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1032 ctxt->controlFd = -1;
1033 return(res);
1034 }
1035 res = xmlNanoFTPGetResponse(ctxt);
1036 if ((res == 1) || (res == 2)) {
1037 /* we assume it worked :-\ */
1038 proxyType = 2;
1039 return(0);
1040 }
1041 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001042 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001043 else
Owen Taylor3473f882001-02-23 17:55:21 +00001044 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001045 buf[sizeof(buf) - 1] = 0;
1046 len = strlen(buf);
1047#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001048 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001049#endif
1050 res = send(ctxt->controlFd, buf, len, 0);
1051 if (res < 0) {
1052 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1053 ctxt->controlFd = -1;
1054 return(res);
1055 }
1056 res = xmlNanoFTPGetResponse(ctxt);
1057 if ((res == 1) || (res == 2)) {
1058 /* we assume it worked :-\ */
1059 proxyType = 2;
1060 return(0);
1061 }
1062 if (proxyType == 2) {
1063 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1064 ctxt->controlFd = -1;
1065 return(-1);
1066 }
1067 case 3:
1068 /*
1069 * If you need support for other Proxy authentication scheme
1070 * send the code or at least the sequence in use.
1071 */
1072 default:
1073 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1074 ctxt->controlFd = -1;
1075 return(-1);
1076 }
1077 }
1078 /*
1079 * Non-proxy handling.
1080 */
1081 res = xmlNanoFTPSendUser(ctxt);
1082 if (res < 0) {
1083 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1084 ctxt->controlFd = -1;
1085 return(-1);
1086 }
1087 res = xmlNanoFTPGetResponse(ctxt);
1088 switch (res) {
1089 case 2:
1090 return(0);
1091 case 3:
1092 break;
1093 case 1:
1094 case 4:
1095 case 5:
1096 case -1:
1097 default:
1098 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1099 ctxt->controlFd = -1;
1100 return(-1);
1101 }
1102 res = xmlNanoFTPSendPasswd(ctxt);
1103 if (res < 0) {
1104 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1105 ctxt->controlFd = -1;
1106 return(-1);
1107 }
1108 res = xmlNanoFTPGetResponse(ctxt);
1109 switch (res) {
1110 case 2:
1111 break;
1112 case 3:
1113 xmlGenericError(xmlGenericErrorContext,
1114 "FTP server asking for ACCNT on anonymous\n");
1115 case 1:
1116 case 4:
1117 case 5:
1118 case -1:
1119 default:
1120 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1121 ctxt->controlFd = -1;
1122 return(-1);
1123 }
1124
1125 return(0);
1126}
1127
1128/**
1129 * xmlNanoFTPConnectTo:
1130 * @server: an FTP server name
1131 * @port: the port (use 21 if 0)
1132 *
1133 * Tries to open a control connection to the given server/port
1134 *
1135 * Returns an fTP context or NULL if it failed
1136 */
1137
1138void*
1139xmlNanoFTPConnectTo(const char *server, int port) {
1140 xmlNanoFTPCtxtPtr ctxt;
1141 int res;
1142
1143 xmlNanoFTPInit();
1144 if (server == NULL)
1145 return(NULL);
1146 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1147 ctxt->hostname = xmlMemStrdup(server);
1148 if (port != 0)
1149 ctxt->port = port;
1150 res = xmlNanoFTPConnect(ctxt);
1151 if (res < 0) {
1152 xmlNanoFTPFreeCtxt(ctxt);
1153 return(NULL);
1154 }
1155 return(ctxt);
1156}
1157
1158/**
1159 * xmlNanoFTPCwd:
1160 * @ctx: an FTP context
1161 * @directory: a directory on the server
1162 *
1163 * Tries to change the remote directory
1164 *
1165 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1166 */
1167
1168int
1169xmlNanoFTPCwd(void *ctx, char *directory) {
1170 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1171 char buf[400];
1172 int len;
1173 int res;
1174
1175 /*
1176 * Expected response code for CWD:
1177 *
1178 * CWD
1179 * 250
1180 * 500, 501, 502, 421, 530, 550
1181 */
Owen Taylor3473f882001-02-23 17:55:21 +00001182 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001183 buf[sizeof(buf) - 1] = 0;
1184 len = strlen(buf);
1185#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001186 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001187#endif
1188 res = send(ctxt->controlFd, buf, len, 0);
1189 if (res < 0) return(res);
1190 res = xmlNanoFTPGetResponse(ctxt);
1191 if (res == 4) {
1192 return(-1);
1193 }
1194 if (res == 2) return(1);
1195 if (res == 5) {
1196 return(0);
1197 }
1198 return(0);
1199}
1200
1201/**
1202 * xmlNanoFTPGetConnection:
1203 * @ctx: an FTP context
1204 *
1205 * Try to open a data connection to the server. Currently only
1206 * passive mode is supported.
1207 *
1208 * Returns -1 incase of error, 0 otherwise
1209 */
1210
1211int
1212xmlNanoFTPGetConnection(void *ctx) {
1213 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1214 char buf[200], *cur;
1215 int len, i;
1216 int res;
1217 unsigned char ad[6], *adp, *portp;
1218 unsigned int temp[6];
1219 struct sockaddr_in dataAddr;
1220 SOCKLEN_T dataAddrLen;
1221
1222 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1223 if (ctxt->dataFd < 0) {
1224 xmlGenericError(xmlGenericErrorContext,
1225 "xmlNanoFTPGetConnection: failed to create socket\n");
1226 return(-1);
1227 }
1228 dataAddrLen = sizeof(dataAddr);
1229 memset(&dataAddr, 0, dataAddrLen);
1230 dataAddr.sin_family = AF_INET;
1231
1232 if (ctxt->passive) {
1233 sprintf(buf, "PASV\r\n");
1234 len = strlen(buf);
1235#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001236 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001237#endif
1238 res = send(ctxt->controlFd, buf, len, 0);
1239 if (res < 0) {
1240 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1241 return(res);
1242 }
1243 res = xmlNanoFTPReadResponse(ctx);
1244 if (res != 2) {
1245 if (res == 5) {
1246 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1247 return(-1);
1248 } else {
1249 /*
1250 * retry with an active connection
1251 */
1252 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1253 ctxt->passive = 0;
1254 }
1255 }
1256 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1257 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1258 if (sscanf(cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1259 &temp[3], &temp[4], &temp[5]) != 6) {
1260 xmlGenericError(xmlGenericErrorContext,
1261 "Invalid answer to PASV\n");
1262 if (ctxt->dataFd != -1) {
1263 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1264 }
1265 return(-1);
1266 }
1267 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1268 memcpy(&dataAddr.sin_addr, &ad[0], 4);
1269 memcpy(&dataAddr.sin_port, &ad[4], 2);
1270 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1271 xmlGenericError(xmlGenericErrorContext,
1272 "Failed to create a data connection\n");
1273 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1274 return (-1);
1275 }
1276 } else {
1277 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1278 dataAddr.sin_port = 0;
1279 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1280 xmlGenericError(xmlGenericErrorContext,
1281 "Failed to bind a port\n");
1282 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1283 return (-1);
1284 }
1285 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1286
1287 if (listen(ctxt->dataFd, 1) < 0) {
1288 xmlGenericError(xmlGenericErrorContext,
1289 "Could not listen on port %d\n",
1290 ntohs(dataAddr.sin_port));
1291 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1292 return (-1);
1293 }
1294 adp = (unsigned char *) &dataAddr.sin_addr;
1295 portp = (unsigned char *) &dataAddr.sin_port;
Owen Taylor3473f882001-02-23 17:55:21 +00001296 snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1297 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1298 portp[0] & 0xff, portp[1] & 0xff);
Owen Taylor3473f882001-02-23 17:55:21 +00001299 buf[sizeof(buf) - 1] = 0;
1300 len = strlen(buf);
1301#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001302 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001303#endif
1304
1305 res = send(ctxt->controlFd, buf, len, 0);
1306 if (res < 0) {
1307 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1308 return(res);
1309 }
1310 res = xmlNanoFTPGetResponse(ctxt);
1311 if (res != 2) {
1312 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1313 return(-1);
1314 }
1315 }
1316 return(ctxt->dataFd);
1317
1318}
1319
1320/**
1321 * xmlNanoFTPCloseConnection:
1322 * @ctx: an FTP context
1323 *
1324 * Close the data connection from the server
1325 *
1326 * Returns -1 incase of error, 0 otherwise
1327 */
1328
1329int
1330xmlNanoFTPCloseConnection(void *ctx) {
1331 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1332 int res;
1333 fd_set rfd, efd;
1334 struct timeval tv;
1335
1336 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1337 tv.tv_sec = 15;
1338 tv.tv_usec = 0;
1339 FD_ZERO(&rfd);
1340 FD_SET(ctxt->controlFd, &rfd);
1341 FD_ZERO(&efd);
1342 FD_SET(ctxt->controlFd, &efd);
1343 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1344 if (res < 0) {
1345#ifdef DEBUG_FTP
1346 perror("select");
1347#endif
1348 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1349 return(-1);
1350 }
1351 if (res == 0) {
1352#ifdef DEBUG_FTP
1353 xmlGenericError(xmlGenericErrorContext,
1354 "xmlNanoFTPCloseConnection: timeout\n");
1355#endif
1356 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1357 } else {
1358 res = xmlNanoFTPGetResponse(ctxt);
1359 if (res != 2) {
1360 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1361 return(-1);
1362 }
1363 }
1364 return(0);
1365}
1366
1367/**
1368 * xmlNanoFTPParseList:
1369 * @list: some data listing received from the server
1370 * @callback: the user callback
1371 * @userData: the user callback data
1372 *
1373 * Parse at most one entry from the listing.
1374 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001375 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001376 */
1377
1378static int
1379xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1380 const char *cur = list;
1381 char filename[151];
1382 char attrib[11];
1383 char owner[11];
1384 char group[11];
1385 char month[4];
1386 int year = 0;
1387 int minute = 0;
1388 int hour = 0;
1389 int day = 0;
1390 unsigned long size = 0;
1391 int links = 0;
1392 int i;
1393
1394 if (!strncmp(cur, "total", 5)) {
1395 cur += 5;
1396 while (*cur == ' ') cur++;
1397 while ((*cur >= '0') && (*cur <= '9'))
1398 links = (links * 10) + (*cur++ - '0');
1399 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1400 cur++;
1401 return(cur - list);
1402 } else if (*list == '+') {
1403 return(0);
1404 } else {
1405 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1406 cur++;
1407 if (*cur == 0) return(0);
1408 i = 0;
1409 while (*cur != ' ') {
1410 if (i < 10)
1411 attrib[i++] = *cur;
1412 cur++;
1413 if (*cur == 0) return(0);
1414 }
1415 attrib[10] = 0;
1416 while (*cur == ' ') cur++;
1417 if (*cur == 0) return(0);
1418 while ((*cur >= '0') && (*cur <= '9'))
1419 links = (links * 10) + (*cur++ - '0');
1420 while (*cur == ' ') cur++;
1421 if (*cur == 0) return(0);
1422 i = 0;
1423 while (*cur != ' ') {
1424 if (i < 10)
1425 owner[i++] = *cur;
1426 cur++;
1427 if (*cur == 0) return(0);
1428 }
1429 owner[i] = 0;
1430 while (*cur == ' ') cur++;
1431 if (*cur == 0) return(0);
1432 i = 0;
1433 while (*cur != ' ') {
1434 if (i < 10)
1435 group[i++] = *cur;
1436 cur++;
1437 if (*cur == 0) return(0);
1438 }
1439 group[i] = 0;
1440 while (*cur == ' ') cur++;
1441 if (*cur == 0) return(0);
1442 while ((*cur >= '0') && (*cur <= '9'))
1443 size = (size * 10) + (*cur++ - '0');
1444 while (*cur == ' ') cur++;
1445 if (*cur == 0) return(0);
1446 i = 0;
1447 while (*cur != ' ') {
1448 if (i < 3)
1449 month[i++] = *cur;
1450 cur++;
1451 if (*cur == 0) return(0);
1452 }
1453 month[i] = 0;
1454 while (*cur == ' ') cur++;
1455 if (*cur == 0) return(0);
1456 while ((*cur >= '0') && (*cur <= '9'))
1457 day = (day * 10) + (*cur++ - '0');
1458 while (*cur == ' ') cur++;
1459 if (*cur == 0) return(0);
1460 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1461 if ((cur[1] == ':') || (cur[2] == ':')) {
1462 while ((*cur >= '0') && (*cur <= '9'))
1463 hour = (hour * 10) + (*cur++ - '0');
1464 if (*cur == ':') cur++;
1465 while ((*cur >= '0') && (*cur <= '9'))
1466 minute = (minute * 10) + (*cur++ - '0');
1467 } else {
1468 while ((*cur >= '0') && (*cur <= '9'))
1469 year = (year * 10) + (*cur++ - '0');
1470 }
1471 while (*cur == ' ') cur++;
1472 if (*cur == 0) return(0);
1473 i = 0;
1474 while ((*cur != '\n') && (*cur != '\r')) {
1475 if (i < 150)
1476 filename[i++] = *cur;
1477 cur++;
1478 if (*cur == 0) return(0);
1479 }
1480 filename[i] = 0;
1481 if ((*cur != '\n') && (*cur != '\r'))
1482 return(0);
1483 while ((*cur == '\n') || (*cur == '\r'))
1484 cur++;
1485 }
1486 if (callback != NULL) {
1487 callback(userData, filename, attrib, owner, group, size, links,
1488 year, month, day, hour, minute);
1489 }
1490 return(cur - list);
1491}
1492
1493/**
1494 * xmlNanoFTPList:
1495 * @ctx: an FTP context
1496 * @callback: the user callback
1497 * @userData: the user callback data
1498 * @filename: optional files to list
1499 *
1500 * Do a listing on the server. All files info are passed back
1501 * in the callbacks.
1502 *
1503 * Returns -1 incase of error, 0 otherwise
1504 */
1505
1506int
1507xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1508 char *filename) {
1509 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1510 char buf[4096 + 1];
1511 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001512 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001513 fd_set rfd, efd;
1514 struct timeval tv;
1515
1516 if (filename == NULL) {
1517 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1518 return(-1);
1519 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1520 if (ctxt->dataFd == -1)
1521 return(-1);
1522 sprintf(buf, "LIST -L\r\n");
1523 } else {
1524 if (filename[0] != '/') {
1525 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1526 return(-1);
1527 }
1528 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1529 if (ctxt->dataFd == -1)
1530 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001531 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001532 }
1533 buf[sizeof(buf) - 1] = 0;
1534 len = strlen(buf);
1535#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001536 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001537#endif
1538 res = send(ctxt->controlFd, buf, len, 0);
1539 if (res < 0) {
1540 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1541 return(res);
1542 }
1543 res = xmlNanoFTPReadResponse(ctxt);
1544 if (res != 1) {
1545 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1546 return(-res);
1547 }
1548
1549 do {
1550 tv.tv_sec = 1;
1551 tv.tv_usec = 0;
1552 FD_ZERO(&rfd);
1553 FD_SET(ctxt->dataFd, &rfd);
1554 FD_ZERO(&efd);
1555 FD_SET(ctxt->dataFd, &efd);
1556 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1557 if (res < 0) {
1558#ifdef DEBUG_FTP
1559 perror("select");
1560#endif
1561 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1562 return(-1);
1563 }
1564 if (res == 0) {
1565 res = xmlNanoFTPCheckResponse(ctxt);
1566 if (res < 0) {
1567 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1568 ctxt->dataFd = -1;
1569 return(-1);
1570 }
1571 if (res == 2) {
1572 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1573 return(0);
1574 }
1575
1576 continue;
1577 }
1578
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001579 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001580#ifdef DEBUG_FTP
1581 perror("recv");
1582#endif
1583 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1584 ctxt->dataFd = -1;
1585 return(-1);
1586 }
1587#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001588 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001589#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001590 indx += len;
1591 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001592 base = 0;
1593 do {
1594 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1595 base += res;
1596 } while (res > 0);
1597
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001598 memmove(&buf[0], &buf[base], indx - base);
1599 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001600 } while (len != 0);
1601 xmlNanoFTPCloseConnection(ctxt);
1602 return(0);
1603}
1604
1605/**
1606 * xmlNanoFTPGetSocket:
1607 * @ctx: an FTP context
1608 * @filename: the file to retrieve (or NULL if path is in context).
1609 *
1610 * Initiate fetch of the given file from the server.
1611 *
1612 * Returns the socket for the data connection, or <0 in case of error
1613 */
1614
1615
1616int
1617xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1618 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1619 char buf[300];
1620 int res, len;
1621 if ((filename == NULL) && (ctxt->path == NULL))
1622 return(-1);
1623 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1624 if (ctxt->dataFd == -1)
1625 return(-1);
1626
1627 sprintf(buf, "TYPE I\r\n");
1628 len = strlen(buf);
1629#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001630 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001631#endif
1632 res = send(ctxt->controlFd, buf, len, 0);
1633 if (res < 0) {
1634 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1635 return(res);
1636 }
1637 res = xmlNanoFTPReadResponse(ctxt);
1638 if (res != 2) {
1639 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1640 return(-res);
1641 }
1642 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001643 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001644 else
Owen Taylor3473f882001-02-23 17:55:21 +00001645 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001646 buf[sizeof(buf) - 1] = 0;
1647 len = strlen(buf);
1648#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001649 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001650#endif
1651 res = send(ctxt->controlFd, buf, len, 0);
1652 if (res < 0) {
1653 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1654 return(res);
1655 }
1656 res = xmlNanoFTPReadResponse(ctxt);
1657 if (res != 1) {
1658 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1659 return(-res);
1660 }
1661 return(ctxt->dataFd);
1662}
1663
1664/**
1665 * xmlNanoFTPGet:
1666 * @ctx: an FTP context
1667 * @callback: the user callback
1668 * @userData: the user callback data
1669 * @filename: the file to retrieve
1670 *
1671 * Fetch the given file from the server. All data are passed back
1672 * in the callbacks. The last callback has a size of 0 block.
1673 *
1674 * Returns -1 incase of error, 0 otherwise
1675 */
1676
1677int
1678xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1679 const char *filename) {
1680 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1681 char buf[4096];
1682 int len = 0, res;
1683 fd_set rfd;
1684 struct timeval tv;
1685
1686 if ((filename == NULL) && (ctxt->path == NULL))
1687 return(-1);
1688 if (callback == NULL)
1689 return(-1);
1690 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1691 return(-1);
1692
1693 do {
1694 tv.tv_sec = 1;
1695 tv.tv_usec = 0;
1696 FD_ZERO(&rfd);
1697 FD_SET(ctxt->dataFd, &rfd);
1698 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1699 if (res < 0) {
1700#ifdef DEBUG_FTP
1701 perror("select");
1702#endif
1703 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1704 return(-1);
1705 }
1706 if (res == 0) {
1707 res = xmlNanoFTPCheckResponse(ctxt);
1708 if (res < 0) {
1709 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1710 ctxt->dataFd = -1;
1711 return(-1);
1712 }
1713 if (res == 2) {
1714 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1715 return(0);
1716 }
1717
1718 continue;
1719 }
1720 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1721 callback(userData, buf, len);
1722 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1723 return(-1);
1724 }
1725 callback(userData, buf, len);
1726 } while (len != 0);
1727
1728 return(xmlNanoFTPCloseConnection(ctxt));
1729}
1730
1731/**
1732 * xmlNanoFTPRead:
1733 * @ctx: the FTP context
1734 * @dest: a buffer
1735 * @len: the buffer length
1736 *
1737 * This function tries to read @len bytes from the existing FTP connection
1738 * and saves them in @dest. This is a blocking call.
1739 *
1740 * Returns the number of byte read. 0 is an indication of an end of connection.
1741 * -1 indicates a parameter error.
1742 */
1743int
1744xmlNanoFTPRead(void *ctx, void *dest, int len) {
1745 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1746
1747 if (ctx == NULL) return(-1);
1748 if (ctxt->dataFd < 0) return(0);
1749 if (dest == NULL) return(-1);
1750 if (len <= 0) return(0);
1751
1752 len = recv(ctxt->dataFd, dest, len, 0);
1753#ifdef DEBUG_FTP
1754 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1755#endif
1756 if (len <= 0) {
1757 xmlNanoFTPCloseConnection(ctxt);
1758 }
1759 return(len);
1760}
1761
1762/**
1763 * xmlNanoFTPOpen:
1764 * @URL: the URL to the resource
1765 *
1766 * Start to fetch the given ftp:// resource
1767 *
1768 * Returns an FTP context, or NULL
1769 */
1770
1771void*
1772xmlNanoFTPOpen(const char *URL) {
1773 xmlNanoFTPCtxtPtr ctxt;
1774 int sock;
1775
1776 xmlNanoFTPInit();
1777 if (URL == NULL) return(NULL);
1778 if (strncmp("ftp://", URL, 6)) return(NULL);
1779
1780 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
1781 if (ctxt == NULL) return(NULL);
1782 if (xmlNanoFTPConnect(ctxt) < 0) {
1783 xmlNanoFTPFreeCtxt(ctxt);
1784 return(NULL);
1785 }
1786 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1787 if (sock < 0) {
1788 xmlNanoFTPFreeCtxt(ctxt);
1789 return(NULL);
1790 }
1791 return(ctxt);
1792}
1793
1794/**
1795 * xmlNanoFTPClose:
1796 * @ctx: an FTP context
1797 *
1798 * Close the connection and both control and transport
1799 *
1800 * Returns -1 incase of error, 0 otherwise
1801 */
1802
1803int
1804xmlNanoFTPClose(void *ctx) {
1805 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1806
1807 if (ctxt == NULL)
1808 return(-1);
1809
1810 if (ctxt->dataFd >= 0) {
1811 closesocket(ctxt->dataFd);
1812 ctxt->dataFd = -1;
1813 }
1814 if (ctxt->controlFd >= 0) {
1815 xmlNanoFTPQuit(ctxt);
1816 closesocket(ctxt->controlFd);
1817 ctxt->controlFd = -1;
1818 }
1819 xmlNanoFTPFreeCtxt(ctxt);
1820 return(0);
1821}
1822
1823#ifdef STANDALONE
1824/************************************************************************
1825 * *
1826 * Basic test in Standalone mode *
1827 * *
1828 ************************************************************************/
1829void ftpList(void *userData, const char *filename, const char* attrib,
1830 const char *owner, const char *group, unsigned long size, int links,
1831 int year, const char *month, int day, int hour, int minute) {
1832 xmlGenericError(xmlGenericErrorContext,
1833 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1834}
1835void ftpData(void *userData, const char *data, int len) {
1836 if (userData == NULL) return;
1837 if (len <= 0) {
1838 fclose(userData);
1839 return;
1840 }
1841 fwrite(data, len, 1, userData);
1842}
1843
1844int main(int argc, char **argv) {
1845 void *ctxt;
1846 FILE *output;
1847 char *tstfile = NULL;
1848
1849 xmlNanoFTPInit();
1850 if (argc > 1) {
1851 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1852 if (xmlNanoFTPConnect(ctxt) < 0) {
1853 xmlGenericError(xmlGenericErrorContext,
1854 "Couldn't connect to %s\n", argv[1]);
1855 exit(1);
1856 }
1857 if (argc > 2)
1858 tstfile = argv[2];
1859 } else
1860 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1861 if (ctxt == NULL) {
1862 xmlGenericError(xmlGenericErrorContext,
1863 "Couldn't connect to localhost\n");
1864 exit(1);
1865 }
1866 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
1867 output = fopen("/tmp/tstdata", "w");
1868 if (output != NULL) {
1869 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
1870 xmlGenericError(xmlGenericErrorContext,
1871 "Failed to get file\n");
1872
1873 }
1874 xmlNanoFTPClose(ctxt);
1875 xmlMemoryDump();
1876 exit(0);
1877}
1878#endif /* STANDALONE */
1879#else /* !LIBXML_FTP_ENABLED */
1880#ifdef STANDALONE
1881#include <stdio.h>
1882int main(int argc, char **argv) {
1883 xmlGenericError(xmlGenericErrorContext,
1884 "%s : FTP support not compiled in\n", argv[0]);
1885 return(0);
1886}
1887#endif /* STANDALONE */
1888#endif /* LIBXML_FTP_ENABLED */