blob: 7224a5ad1484049146e4b484e73907900427b237 [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;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000264 /* allow user@ and user:pass@ forms */
265 {
266 const char *p = strchr(cur, '@');
267 if(p) {
268 while(1) {
269 if(cur[0] == ':' || cur[0] == '@') break;
270 buf[indx++] = *cur++;
271 }
272 buf[indx] = 0;
273 ctxt->user = xmlMemStrdup(buf);
274 indx = 0;
275 if(cur[0] == ':') {
276 cur++;
277 while(1) {
278 if(cur[0] == '@') break;
279 buf[indx++] = *cur++;
280 }
281 buf[indx] = 0;
282 ctxt->passwd = xmlMemStrdup(buf);
283 indx = 0;
284 }
285 cur = p+1;
286 }
287 }
288
Owen Taylor3473f882001-02-23 17:55:21 +0000289 while (1) {
290 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000291 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000292 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000293 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000294 cur += 1;
295 while ((*cur >= '0') && (*cur <= '9')) {
296 port *= 10;
297 port += *cur - '0';
298 cur++;
299 }
300 if (port != 0) ctxt->port = port;
301 while ((cur[0] != '/') && (*cur != 0))
302 cur++;
303 break;
304 }
305 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000306 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000307 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000308 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000309 break;
310 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000311 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000312 }
313 if (*cur == 0)
314 ctxt->path = xmlMemStrdup("/");
315 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000316 indx = 0;
317 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000318 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000319 buf[indx++] = *cur++;
320 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000321 ctxt->path = xmlMemStrdup(buf);
322 }
323}
324
325/**
326 * xmlNanoFTPUpdateURL:
327 * @ctx: an FTP context
328 * @URL: The URL used to update the context
329 *
330 * Update an FTP context by parsing the URL and finding
331 * new path it indicates. If there is an error in the
332 * protocol, hostname, port or other information, the
333 * error is raised. It indicates a new connection has to
334 * be established.
335 *
336 * Returns 0 if Ok, -1 in case of error (other host).
337 */
338
339int
340xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
341 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
342 const char *cur = URL;
343 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000344 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000345 int port = 0;
346
347 if (URL == NULL)
348 return(-1);
349 if (ctxt == NULL)
350 return(-1);
351 if (ctxt->protocol == NULL)
352 return(-1);
353 if (ctxt->hostname == NULL)
354 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000355 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000356 while (*cur != 0) {
357 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000358 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000359 if (strcmp(ctxt->protocol, buf))
360 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000361 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000362 cur += 3;
363 break;
364 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000365 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000366 }
367 if (*cur == 0)
368 return(-1);
369
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000370 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000371 while (1) {
372 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000373 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000374 if (strcmp(ctxt->hostname, buf))
375 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000376 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000377 cur += 1;
378 while ((*cur >= '0') && (*cur <= '9')) {
379 port *= 10;
380 port += *cur - '0';
381 cur++;
382 }
383 if (port != ctxt->port)
384 return(-1);
385 while ((cur[0] != '/') && (*cur != 0))
386 cur++;
387 break;
388 }
389 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000390 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000391 if (strcmp(ctxt->hostname, buf))
392 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000393 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000394 break;
395 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000396 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000397 }
398 if (ctxt->path != NULL) {
399 xmlFree(ctxt->path);
400 ctxt->path = NULL;
401 }
402
403 if (*cur == 0)
404 ctxt->path = xmlMemStrdup("/");
405 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000406 indx = 0;
407 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000408 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000409 buf[indx++] = *cur++;
410 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000411 ctxt->path = xmlMemStrdup(buf);
412 }
413 return(0);
414}
415
416/**
417 * xmlNanoFTPScanProxy:
418 * @URL: The proxy URL used to initialize the proxy context
419 *
420 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
421 * the protocol host port it indicates.
422 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
423 * A NULL URL cleans up proxy informations.
424 */
425
426void
427xmlNanoFTPScanProxy(const char *URL) {
428 const char *cur = URL;
429 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000430 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000431 int port = 0;
432
433 if (proxy != NULL) {
434 xmlFree(proxy);
435 proxy = NULL;
436 }
437 if (proxyPort != 0) {
438 proxyPort = 0;
439 }
440#ifdef DEBUG_FTP
441 if (URL == NULL)
442 xmlGenericError(xmlGenericErrorContext, "Removing FTP proxy info\n");
443 else
444 xmlGenericError(xmlGenericErrorContext, "Using FTP proxy %s\n", URL);
445#endif
446 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000447 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000448 while (*cur != 0) {
449 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000450 buf[indx] = 0;
451 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000452 cur += 3;
453 break;
454 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000455 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000456 }
457 if (*cur == 0) return;
458
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000459 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000460 while (1) {
461 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000462 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000463 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000464 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000465 cur += 1;
466 while ((*cur >= '0') && (*cur <= '9')) {
467 port *= 10;
468 port += *cur - '0';
469 cur++;
470 }
471 if (port != 0) proxyPort = port;
472 while ((cur[0] != '/') && (*cur != 0))
473 cur++;
474 break;
475 }
476 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000477 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000478 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000479 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000480 break;
481 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000482 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000483 }
484}
485
486/**
487 * xmlNanoFTPNewCtxt:
488 * @URL: The URL used to initialize the context
489 *
490 * Allocate and initialize a new FTP context.
491 *
492 * Returns an FTP context or NULL in case of error.
493 */
494
495void*
496xmlNanoFTPNewCtxt(const char *URL) {
497 xmlNanoFTPCtxtPtr ret;
498
499 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
500 if (ret == NULL) return(NULL);
501
502 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
503 ret->port = 21;
504 ret->passive = 1;
505 ret->returnValue = 0;
506 ret->controlBufIndex = 0;
507 ret->controlBufUsed = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000508 ret->controlFd = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000509
510 if (URL != NULL)
511 xmlNanoFTPScanURL(ret, URL);
512
513 return(ret);
514}
515
516/**
517 * xmlNanoFTPFreeCtxt:
518 * @ctx: an FTP context
519 *
520 * Frees the context after closing the connection.
521 */
522
523void
524xmlNanoFTPFreeCtxt(void * ctx) {
525 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
526 if (ctxt == NULL) return;
527 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
528 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
529 if (ctxt->path != NULL) xmlFree(ctxt->path);
530 ctxt->passive = 1;
531 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
532 ctxt->controlFd = -1;
533 ctxt->controlBufIndex = -1;
534 ctxt->controlBufUsed = -1;
535 xmlFree(ctxt);
536}
537
538/**
539 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000540 * @buf: the buffer containing the response
541 * @len: the buffer length
542 *
543 * Parsing of the server answer, we just extract the code.
544 *
545 * returns 0 for errors
546 * +XXX for last line of response
547 * -XXX for response to be continued
548 */
549static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000550xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000551 int val = 0;
552
553 if (len < 3) return(-1);
554 if ((*buf >= '0') && (*buf <= '9'))
555 val = val * 10 + (*buf - '0');
556 else
557 return(0);
558 buf++;
559 if ((*buf >= '0') && (*buf <= '9'))
560 val = val * 10 + (*buf - '0');
561 else
562 return(0);
563 buf++;
564 if ((*buf >= '0') && (*buf <= '9'))
565 val = val * 10 + (*buf - '0');
566 else
567 return(0);
568 buf++;
569 if (*buf == '-')
570 return(-val);
571 return(val);
572}
573
574/**
575 * xmlNanoFTPGetMore:
576 * @ctx: an FTP context
577 *
578 * Read more information from the FTP control connection
579 * Returns the number of bytes read, < 0 indicates an error
580 */
581static int
582xmlNanoFTPGetMore(void *ctx) {
583 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
584 int len;
585 int size;
586
587 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
588#ifdef DEBUG_FTP
589 xmlGenericError(xmlGenericErrorContext,
590 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
591 ctxt->controlBufIndex);
592#endif
593 return(-1);
594 }
595
596 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
597#ifdef DEBUG_FTP
598 xmlGenericError(xmlGenericErrorContext,
599 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
600 ctxt->controlBufUsed);
601#endif
602 return(-1);
603 }
604 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
605#ifdef DEBUG_FTP
606 xmlGenericError(xmlGenericErrorContext,
607 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
608 ctxt->controlBufIndex, ctxt->controlBufUsed);
609#endif
610 return(-1);
611 }
612
613 /*
614 * First pack the control buffer
615 */
616 if (ctxt->controlBufIndex > 0) {
617 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
618 ctxt->controlBufUsed - ctxt->controlBufIndex);
619 ctxt->controlBufUsed -= ctxt->controlBufIndex;
620 ctxt->controlBufIndex = 0;
621 }
622 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
623 if (size == 0) {
624#ifdef DEBUG_FTP
625 xmlGenericError(xmlGenericErrorContext,
626 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
627#endif
628 return(0);
629 }
630
631 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000632 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000633 */
634 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
635 size, 0)) < 0) {
636 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
637 ctxt->controlFd = -1;
638 return(-1);
639 }
640#ifdef DEBUG_FTP
641 xmlGenericError(xmlGenericErrorContext,
642 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
643 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
644#endif
645 ctxt->controlBufUsed += len;
646 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
647
648 return(len);
649}
650
651/**
652 * xmlNanoFTPReadResponse:
653 * @ctx: an FTP context
654 *
655 * Read the response from the FTP server after a command.
656 * Returns the code number
657 */
658static int
659xmlNanoFTPReadResponse(void *ctx) {
660 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
661 char *ptr, *end;
662 int len;
663 int res = -1, cur = -1;
664
665get_more:
666 /*
667 * Assumes everything up to controlBuf[controlBufIndex] has been read
668 * and analyzed.
669 */
670 len = xmlNanoFTPGetMore(ctx);
671 if (len < 0) {
672 return(-1);
673 }
674 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
675 return(-1);
676 }
677 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
678 end = &ctxt->controlBuf[ctxt->controlBufUsed];
679
680#ifdef DEBUG_FTP
681 xmlGenericError(xmlGenericErrorContext,
682 "\n<<<\n%s\n--\n", ptr);
683#endif
684 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000685 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000686 if (cur > 0) {
687 /*
688 * Successfully scanned the control code, scratch
689 * till the end of the line, but keep the index to be
690 * able to analyze the result if needed.
691 */
692 res = cur;
693 ptr += 3;
694 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
695 while ((ptr < end) && (*ptr != '\n')) ptr++;
696 if (*ptr == '\n') ptr++;
697 if (*ptr == '\r') ptr++;
698 break;
699 }
700 while ((ptr < end) && (*ptr != '\n')) ptr++;
701 if (ptr >= end) {
702 ctxt->controlBufIndex = ctxt->controlBufUsed;
703 goto get_more;
704 }
705 if (*ptr != '\r') ptr++;
706 }
707
708 if (res < 0) goto get_more;
709 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
710#ifdef DEBUG_FTP
711 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
712 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
713#endif
714
715#ifdef DEBUG_FTP
716 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
717#endif
718 return(res / 100);
719}
720
721/**
722 * xmlNanoFTPGetResponse:
723 * @ctx: an FTP context
724 *
725 * Get the response from the FTP server after a command.
726 * Returns the code number
727 */
728
729int
730xmlNanoFTPGetResponse(void *ctx) {
731 int res;
732
733 res = xmlNanoFTPReadResponse(ctx);
734
735 return(res);
736}
737
738/**
739 * xmlNanoFTPCheckResponse:
740 * @ctx: an FTP context
741 *
742 * Check if there is a response from the FTP server after a command.
743 * Returns the code number, or 0
744 */
745
746int
747xmlNanoFTPCheckResponse(void *ctx) {
748 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
749 fd_set rfd;
750 struct timeval tv;
751
752 tv.tv_sec = 0;
753 tv.tv_usec = 0;
754 FD_ZERO(&rfd);
755 FD_SET(ctxt->controlFd, &rfd);
756 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
757 case 0:
758 return(0);
759 case -1:
760#ifdef DEBUG_FTP
761 perror("select");
762#endif
763 return(-1);
764
765 }
766
767 return(xmlNanoFTPReadResponse(ctx));
768}
769
770/**
771 * Send the user authentification
772 */
773
774static int
775xmlNanoFTPSendUser(void *ctx) {
776 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
777 char buf[200];
778 int len;
779 int res;
780
781 if (ctxt->user == NULL)
782 sprintf(buf, "USER anonymous\r\n");
783 else
Owen Taylor3473f882001-02-23 17:55:21 +0000784 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000785 buf[sizeof(buf) - 1] = 0;
786 len = strlen(buf);
787#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000788 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000789#endif
790 res = send(ctxt->controlFd, buf, len, 0);
791 if (res < 0) return(res);
792 return(0);
793}
794
795/**
796 * Send the password authentification
797 */
798
799static int
800xmlNanoFTPSendPasswd(void *ctx) {
801 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
802 char buf[200];
803 int len;
804 int res;
805
806 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000807 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000808 else
Owen Taylor3473f882001-02-23 17:55:21 +0000809 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000810 buf[sizeof(buf) - 1] = 0;
811 len = strlen(buf);
812#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000813 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000814#endif
815 res = send(ctxt->controlFd, buf, len, 0);
816 if (res < 0) return(res);
817 return(0);
818}
819
820/**
821 * xmlNanoFTPQuit:
822 * @ctx: an FTP context
823 *
824 * Send a QUIT command to the server
825 *
826 * Returns -1 in case of error, 0 otherwise
827 */
828
829
830int
831xmlNanoFTPQuit(void *ctx) {
832 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
833 char buf[200];
834 int len;
835 int res;
836
837 sprintf(buf, "QUIT\r\n");
838 len = strlen(buf);
839#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000840 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 +0000841#endif
842 res = send(ctxt->controlFd, buf, len, 0);
843 return(0);
844}
845
846/**
847 * xmlNanoFTPConnect:
848 * @ctx: an FTP context
849 *
850 * Tries to open a control connection
851 *
852 * Returns -1 in case of error, 0 otherwise
853 */
854
855int
856xmlNanoFTPConnect(void *ctx) {
857 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
858 struct hostent *hp;
859 int port;
860 int res;
861
862 if (ctxt == NULL)
863 return(-1);
864 if (ctxt->hostname == NULL)
865 return(-1);
866
867 /*
868 * do the blocking DNS query.
869 */
870 if (proxy)
871 hp = gethostbyname(proxy);
872 else
873 hp = gethostbyname(ctxt->hostname);
874 if (hp == NULL)
875 return(-1);
876
877 /*
878 * Prepare the socket
879 */
880 memset(&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
881 ctxt->ftpAddr.sin_family = AF_INET;
882 memcpy(&ctxt->ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
883 if (proxy) {
884 port = proxyPort;
885 } else {
886 port = ctxt->port;
887 }
888 if (port == 0)
889 port = 21;
890 ctxt->ftpAddr.sin_port = htons(port);
891 ctxt->controlFd = socket(AF_INET, SOCK_STREAM, 0);
892 if (ctxt->controlFd < 0)
893 return(-1);
894
895 /*
896 * Do the connect.
897 */
898 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
899 sizeof(struct sockaddr_in)) < 0) {
900 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
901 ctxt->controlFd = -1;
902 return(-1);
903 }
904
905 /*
906 * Wait for the HELLO from the server.
907 */
908 res = xmlNanoFTPGetResponse(ctxt);
909 if (res != 2) {
910 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
911 ctxt->controlFd = -1;
912 return(-1);
913 }
914
915 /*
916 * State diagram for the login operation on the FTP server
917 *
918 * Reference: RFC 959
919 *
920 * 1
921 * +---+ USER +---+------------->+---+
922 * | B |---------->| W | 2 ---->| E |
923 * +---+ +---+------ | -->+---+
924 * | | | | |
925 * 3 | | 4,5 | | |
926 * -------------- ----- | | |
927 * | | | | |
928 * | | | | |
929 * | --------- |
930 * | 1| | | |
931 * V | | | |
932 * +---+ PASS +---+ 2 | ------>+---+
933 * | |---------->| W |------------->| S |
934 * +---+ +---+ ---------->+---+
935 * | | | | |
936 * 3 | |4,5| | |
937 * -------------- -------- |
938 * | | | | |
939 * | | | | |
940 * | -----------
941 * | 1,3| | | |
942 * V | 2| | |
943 * +---+ ACCT +---+-- | ----->+---+
944 * | |---------->| W | 4,5 -------->| F |
945 * +---+ +---+------------->+---+
946 *
947 * Of course in case of using a proxy this get really nasty and is not
948 * standardized at all :-(
949 */
950 if (proxy) {
951 int len;
952 char buf[400];
953
954 if (proxyUser != NULL) {
955 /*
956 * We need proxy auth
957 */
Owen Taylor3473f882001-02-23 17:55:21 +0000958 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +0000959 buf[sizeof(buf) - 1] = 0;
960 len = strlen(buf);
961#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000962 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000963#endif
964 res = send(ctxt->controlFd, buf, len, 0);
965 if (res < 0) {
966 closesocket(ctxt->controlFd);
967 ctxt->controlFd = -1;
968 return(res);
969 }
970 res = xmlNanoFTPGetResponse(ctxt);
971 switch (res) {
972 case 2:
973 if (proxyPasswd == NULL)
974 break;
975 case 3:
976 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000977 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +0000978 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000979 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000980 buf[sizeof(buf) - 1] = 0;
981 len = strlen(buf);
982#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000983 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000984#endif
985 res = send(ctxt->controlFd, buf, len, 0);
986 if (res < 0) {
987 closesocket(ctxt->controlFd);
988 ctxt->controlFd = -1;
989 return(res);
990 }
991 res = xmlNanoFTPGetResponse(ctxt);
992 if (res > 3) {
993 closesocket(ctxt->controlFd);
994 ctxt->controlFd = -1;
995 return(-1);
996 }
997 break;
998 case 1:
999 break;
1000 case 4:
1001 case 5:
1002 case -1:
1003 default:
1004 closesocket(ctxt->controlFd);
1005 ctxt->controlFd = -1;
1006 return(-1);
1007 }
1008 }
1009
1010 /*
1011 * We assume we don't need more authentication to the proxy
1012 * and that it succeeded :-\
1013 */
1014 switch (proxyType) {
1015 case 0:
1016 /* we will try in seqence */
1017 case 1:
1018 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001019 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001020 buf[sizeof(buf) - 1] = 0;
1021 len = strlen(buf);
1022#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001023 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001024#endif
1025 res = send(ctxt->controlFd, buf, len, 0);
1026 if (res < 0) {
1027 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1028 ctxt->controlFd = -1;
1029 return(res);
1030 }
1031 res = xmlNanoFTPGetResponse(ctxt);
1032 if (res == 2) {
1033 /* we assume it worked :-\ 1 is error for SITE command */
1034 proxyType = 1;
1035 break;
1036 }
1037 if (proxyType == 1) {
1038 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1039 ctxt->controlFd = -1;
1040 return(-1);
1041 }
1042 case 2:
1043 /* USER user@host command */
1044 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001045 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1046 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001047 else
Owen Taylor3473f882001-02-23 17:55:21 +00001048 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1049 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001050 buf[sizeof(buf) - 1] = 0;
1051 len = strlen(buf);
1052#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001053 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001054#endif
1055 res = send(ctxt->controlFd, buf, len, 0);
1056 if (res < 0) {
1057 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1058 ctxt->controlFd = -1;
1059 return(res);
1060 }
1061 res = xmlNanoFTPGetResponse(ctxt);
1062 if ((res == 1) || (res == 2)) {
1063 /* we assume it worked :-\ */
1064 proxyType = 2;
1065 return(0);
1066 }
1067 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001068 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001069 else
Owen Taylor3473f882001-02-23 17:55:21 +00001070 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001071 buf[sizeof(buf) - 1] = 0;
1072 len = strlen(buf);
1073#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001074 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001075#endif
1076 res = send(ctxt->controlFd, buf, len, 0);
1077 if (res < 0) {
1078 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1079 ctxt->controlFd = -1;
1080 return(res);
1081 }
1082 res = xmlNanoFTPGetResponse(ctxt);
1083 if ((res == 1) || (res == 2)) {
1084 /* we assume it worked :-\ */
1085 proxyType = 2;
1086 return(0);
1087 }
1088 if (proxyType == 2) {
1089 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1090 ctxt->controlFd = -1;
1091 return(-1);
1092 }
1093 case 3:
1094 /*
1095 * If you need support for other Proxy authentication scheme
1096 * send the code or at least the sequence in use.
1097 */
1098 default:
1099 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1100 ctxt->controlFd = -1;
1101 return(-1);
1102 }
1103 }
1104 /*
1105 * Non-proxy handling.
1106 */
1107 res = xmlNanoFTPSendUser(ctxt);
1108 if (res < 0) {
1109 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1110 ctxt->controlFd = -1;
1111 return(-1);
1112 }
1113 res = xmlNanoFTPGetResponse(ctxt);
1114 switch (res) {
1115 case 2:
1116 return(0);
1117 case 3:
1118 break;
1119 case 1:
1120 case 4:
1121 case 5:
1122 case -1:
1123 default:
1124 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1125 ctxt->controlFd = -1;
1126 return(-1);
1127 }
1128 res = xmlNanoFTPSendPasswd(ctxt);
1129 if (res < 0) {
1130 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1131 ctxt->controlFd = -1;
1132 return(-1);
1133 }
1134 res = xmlNanoFTPGetResponse(ctxt);
1135 switch (res) {
1136 case 2:
1137 break;
1138 case 3:
1139 xmlGenericError(xmlGenericErrorContext,
1140 "FTP server asking for ACCNT on anonymous\n");
1141 case 1:
1142 case 4:
1143 case 5:
1144 case -1:
1145 default:
1146 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1147 ctxt->controlFd = -1;
1148 return(-1);
1149 }
1150
1151 return(0);
1152}
1153
1154/**
1155 * xmlNanoFTPConnectTo:
1156 * @server: an FTP server name
1157 * @port: the port (use 21 if 0)
1158 *
1159 * Tries to open a control connection to the given server/port
1160 *
1161 * Returns an fTP context or NULL if it failed
1162 */
1163
1164void*
1165xmlNanoFTPConnectTo(const char *server, int port) {
1166 xmlNanoFTPCtxtPtr ctxt;
1167 int res;
1168
1169 xmlNanoFTPInit();
1170 if (server == NULL)
1171 return(NULL);
1172 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1173 ctxt->hostname = xmlMemStrdup(server);
1174 if (port != 0)
1175 ctxt->port = port;
1176 res = xmlNanoFTPConnect(ctxt);
1177 if (res < 0) {
1178 xmlNanoFTPFreeCtxt(ctxt);
1179 return(NULL);
1180 }
1181 return(ctxt);
1182}
1183
1184/**
1185 * xmlNanoFTPCwd:
1186 * @ctx: an FTP context
1187 * @directory: a directory on the server
1188 *
1189 * Tries to change the remote directory
1190 *
1191 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1192 */
1193
1194int
1195xmlNanoFTPCwd(void *ctx, char *directory) {
1196 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1197 char buf[400];
1198 int len;
1199 int res;
1200
1201 /*
1202 * Expected response code for CWD:
1203 *
1204 * CWD
1205 * 250
1206 * 500, 501, 502, 421, 530, 550
1207 */
Owen Taylor3473f882001-02-23 17:55:21 +00001208 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001209 buf[sizeof(buf) - 1] = 0;
1210 len = strlen(buf);
1211#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001212 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001213#endif
1214 res = send(ctxt->controlFd, buf, len, 0);
1215 if (res < 0) return(res);
1216 res = xmlNanoFTPGetResponse(ctxt);
1217 if (res == 4) {
1218 return(-1);
1219 }
1220 if (res == 2) return(1);
1221 if (res == 5) {
1222 return(0);
1223 }
1224 return(0);
1225}
1226
1227/**
1228 * xmlNanoFTPGetConnection:
1229 * @ctx: an FTP context
1230 *
1231 * Try to open a data connection to the server. Currently only
1232 * passive mode is supported.
1233 *
1234 * Returns -1 incase of error, 0 otherwise
1235 */
1236
1237int
1238xmlNanoFTPGetConnection(void *ctx) {
1239 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1240 char buf[200], *cur;
1241 int len, i;
1242 int res;
1243 unsigned char ad[6], *adp, *portp;
1244 unsigned int temp[6];
1245 struct sockaddr_in dataAddr;
1246 SOCKLEN_T dataAddrLen;
1247
1248 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1249 if (ctxt->dataFd < 0) {
1250 xmlGenericError(xmlGenericErrorContext,
1251 "xmlNanoFTPGetConnection: failed to create socket\n");
1252 return(-1);
1253 }
1254 dataAddrLen = sizeof(dataAddr);
1255 memset(&dataAddr, 0, dataAddrLen);
1256 dataAddr.sin_family = AF_INET;
1257
1258 if (ctxt->passive) {
1259 sprintf(buf, "PASV\r\n");
1260 len = strlen(buf);
1261#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001262 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001263#endif
1264 res = send(ctxt->controlFd, buf, len, 0);
1265 if (res < 0) {
1266 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1267 return(res);
1268 }
1269 res = xmlNanoFTPReadResponse(ctx);
1270 if (res != 2) {
1271 if (res == 5) {
1272 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1273 return(-1);
1274 } else {
1275 /*
1276 * retry with an active connection
1277 */
1278 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1279 ctxt->passive = 0;
1280 }
1281 }
1282 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1283 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1284 if (sscanf(cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1285 &temp[3], &temp[4], &temp[5]) != 6) {
1286 xmlGenericError(xmlGenericErrorContext,
1287 "Invalid answer to PASV\n");
1288 if (ctxt->dataFd != -1) {
1289 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1290 }
1291 return(-1);
1292 }
1293 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1294 memcpy(&dataAddr.sin_addr, &ad[0], 4);
1295 memcpy(&dataAddr.sin_port, &ad[4], 2);
1296 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1297 xmlGenericError(xmlGenericErrorContext,
1298 "Failed to create a data connection\n");
1299 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1300 return (-1);
1301 }
1302 } else {
1303 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1304 dataAddr.sin_port = 0;
1305 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1306 xmlGenericError(xmlGenericErrorContext,
1307 "Failed to bind a port\n");
1308 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1309 return (-1);
1310 }
1311 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1312
1313 if (listen(ctxt->dataFd, 1) < 0) {
1314 xmlGenericError(xmlGenericErrorContext,
1315 "Could not listen on port %d\n",
1316 ntohs(dataAddr.sin_port));
1317 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1318 return (-1);
1319 }
1320 adp = (unsigned char *) &dataAddr.sin_addr;
1321 portp = (unsigned char *) &dataAddr.sin_port;
Owen Taylor3473f882001-02-23 17:55:21 +00001322 snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1323 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1324 portp[0] & 0xff, portp[1] & 0xff);
Owen Taylor3473f882001-02-23 17:55:21 +00001325 buf[sizeof(buf) - 1] = 0;
1326 len = strlen(buf);
1327#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001328 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001329#endif
1330
1331 res = send(ctxt->controlFd, buf, len, 0);
1332 if (res < 0) {
1333 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1334 return(res);
1335 }
1336 res = xmlNanoFTPGetResponse(ctxt);
1337 if (res != 2) {
1338 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1339 return(-1);
1340 }
1341 }
1342 return(ctxt->dataFd);
1343
1344}
1345
1346/**
1347 * xmlNanoFTPCloseConnection:
1348 * @ctx: an FTP context
1349 *
1350 * Close the data connection from the server
1351 *
1352 * Returns -1 incase of error, 0 otherwise
1353 */
1354
1355int
1356xmlNanoFTPCloseConnection(void *ctx) {
1357 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1358 int res;
1359 fd_set rfd, efd;
1360 struct timeval tv;
1361
1362 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1363 tv.tv_sec = 15;
1364 tv.tv_usec = 0;
1365 FD_ZERO(&rfd);
1366 FD_SET(ctxt->controlFd, &rfd);
1367 FD_ZERO(&efd);
1368 FD_SET(ctxt->controlFd, &efd);
1369 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1370 if (res < 0) {
1371#ifdef DEBUG_FTP
1372 perror("select");
1373#endif
1374 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1375 return(-1);
1376 }
1377 if (res == 0) {
1378#ifdef DEBUG_FTP
1379 xmlGenericError(xmlGenericErrorContext,
1380 "xmlNanoFTPCloseConnection: timeout\n");
1381#endif
1382 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1383 } else {
1384 res = xmlNanoFTPGetResponse(ctxt);
1385 if (res != 2) {
1386 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1387 return(-1);
1388 }
1389 }
1390 return(0);
1391}
1392
1393/**
1394 * xmlNanoFTPParseList:
1395 * @list: some data listing received from the server
1396 * @callback: the user callback
1397 * @userData: the user callback data
1398 *
1399 * Parse at most one entry from the listing.
1400 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001401 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001402 */
1403
1404static int
1405xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1406 const char *cur = list;
1407 char filename[151];
1408 char attrib[11];
1409 char owner[11];
1410 char group[11];
1411 char month[4];
1412 int year = 0;
1413 int minute = 0;
1414 int hour = 0;
1415 int day = 0;
1416 unsigned long size = 0;
1417 int links = 0;
1418 int i;
1419
1420 if (!strncmp(cur, "total", 5)) {
1421 cur += 5;
1422 while (*cur == ' ') cur++;
1423 while ((*cur >= '0') && (*cur <= '9'))
1424 links = (links * 10) + (*cur++ - '0');
1425 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1426 cur++;
1427 return(cur - list);
1428 } else if (*list == '+') {
1429 return(0);
1430 } else {
1431 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1432 cur++;
1433 if (*cur == 0) return(0);
1434 i = 0;
1435 while (*cur != ' ') {
1436 if (i < 10)
1437 attrib[i++] = *cur;
1438 cur++;
1439 if (*cur == 0) return(0);
1440 }
1441 attrib[10] = 0;
1442 while (*cur == ' ') cur++;
1443 if (*cur == 0) return(0);
1444 while ((*cur >= '0') && (*cur <= '9'))
1445 links = (links * 10) + (*cur++ - '0');
1446 while (*cur == ' ') cur++;
1447 if (*cur == 0) return(0);
1448 i = 0;
1449 while (*cur != ' ') {
1450 if (i < 10)
1451 owner[i++] = *cur;
1452 cur++;
1453 if (*cur == 0) return(0);
1454 }
1455 owner[i] = 0;
1456 while (*cur == ' ') cur++;
1457 if (*cur == 0) return(0);
1458 i = 0;
1459 while (*cur != ' ') {
1460 if (i < 10)
1461 group[i++] = *cur;
1462 cur++;
1463 if (*cur == 0) return(0);
1464 }
1465 group[i] = 0;
1466 while (*cur == ' ') cur++;
1467 if (*cur == 0) return(0);
1468 while ((*cur >= '0') && (*cur <= '9'))
1469 size = (size * 10) + (*cur++ - '0');
1470 while (*cur == ' ') cur++;
1471 if (*cur == 0) return(0);
1472 i = 0;
1473 while (*cur != ' ') {
1474 if (i < 3)
1475 month[i++] = *cur;
1476 cur++;
1477 if (*cur == 0) return(0);
1478 }
1479 month[i] = 0;
1480 while (*cur == ' ') cur++;
1481 if (*cur == 0) return(0);
1482 while ((*cur >= '0') && (*cur <= '9'))
1483 day = (day * 10) + (*cur++ - '0');
1484 while (*cur == ' ') cur++;
1485 if (*cur == 0) return(0);
1486 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1487 if ((cur[1] == ':') || (cur[2] == ':')) {
1488 while ((*cur >= '0') && (*cur <= '9'))
1489 hour = (hour * 10) + (*cur++ - '0');
1490 if (*cur == ':') cur++;
1491 while ((*cur >= '0') && (*cur <= '9'))
1492 minute = (minute * 10) + (*cur++ - '0');
1493 } else {
1494 while ((*cur >= '0') && (*cur <= '9'))
1495 year = (year * 10) + (*cur++ - '0');
1496 }
1497 while (*cur == ' ') cur++;
1498 if (*cur == 0) return(0);
1499 i = 0;
1500 while ((*cur != '\n') && (*cur != '\r')) {
1501 if (i < 150)
1502 filename[i++] = *cur;
1503 cur++;
1504 if (*cur == 0) return(0);
1505 }
1506 filename[i] = 0;
1507 if ((*cur != '\n') && (*cur != '\r'))
1508 return(0);
1509 while ((*cur == '\n') || (*cur == '\r'))
1510 cur++;
1511 }
1512 if (callback != NULL) {
1513 callback(userData, filename, attrib, owner, group, size, links,
1514 year, month, day, hour, minute);
1515 }
1516 return(cur - list);
1517}
1518
1519/**
1520 * xmlNanoFTPList:
1521 * @ctx: an FTP context
1522 * @callback: the user callback
1523 * @userData: the user callback data
1524 * @filename: optional files to list
1525 *
1526 * Do a listing on the server. All files info are passed back
1527 * in the callbacks.
1528 *
1529 * Returns -1 incase of error, 0 otherwise
1530 */
1531
1532int
1533xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1534 char *filename) {
1535 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1536 char buf[4096 + 1];
1537 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001538 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001539 fd_set rfd, efd;
1540 struct timeval tv;
1541
1542 if (filename == NULL) {
1543 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1544 return(-1);
1545 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1546 if (ctxt->dataFd == -1)
1547 return(-1);
1548 sprintf(buf, "LIST -L\r\n");
1549 } else {
1550 if (filename[0] != '/') {
1551 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1552 return(-1);
1553 }
1554 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1555 if (ctxt->dataFd == -1)
1556 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001557 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001558 }
1559 buf[sizeof(buf) - 1] = 0;
1560 len = strlen(buf);
1561#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001562 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001563#endif
1564 res = send(ctxt->controlFd, buf, len, 0);
1565 if (res < 0) {
1566 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1567 return(res);
1568 }
1569 res = xmlNanoFTPReadResponse(ctxt);
1570 if (res != 1) {
1571 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1572 return(-res);
1573 }
1574
1575 do {
1576 tv.tv_sec = 1;
1577 tv.tv_usec = 0;
1578 FD_ZERO(&rfd);
1579 FD_SET(ctxt->dataFd, &rfd);
1580 FD_ZERO(&efd);
1581 FD_SET(ctxt->dataFd, &efd);
1582 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1583 if (res < 0) {
1584#ifdef DEBUG_FTP
1585 perror("select");
1586#endif
1587 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1588 return(-1);
1589 }
1590 if (res == 0) {
1591 res = xmlNanoFTPCheckResponse(ctxt);
1592 if (res < 0) {
1593 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1594 ctxt->dataFd = -1;
1595 return(-1);
1596 }
1597 if (res == 2) {
1598 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1599 return(0);
1600 }
1601
1602 continue;
1603 }
1604
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001605 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001606#ifdef DEBUG_FTP
1607 perror("recv");
1608#endif
1609 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1610 ctxt->dataFd = -1;
1611 return(-1);
1612 }
1613#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001614 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001615#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001616 indx += len;
1617 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001618 base = 0;
1619 do {
1620 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1621 base += res;
1622 } while (res > 0);
1623
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001624 memmove(&buf[0], &buf[base], indx - base);
1625 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001626 } while (len != 0);
1627 xmlNanoFTPCloseConnection(ctxt);
1628 return(0);
1629}
1630
1631/**
1632 * xmlNanoFTPGetSocket:
1633 * @ctx: an FTP context
1634 * @filename: the file to retrieve (or NULL if path is in context).
1635 *
1636 * Initiate fetch of the given file from the server.
1637 *
1638 * Returns the socket for the data connection, or <0 in case of error
1639 */
1640
1641
1642int
1643xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1644 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1645 char buf[300];
1646 int res, len;
1647 if ((filename == NULL) && (ctxt->path == NULL))
1648 return(-1);
1649 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1650 if (ctxt->dataFd == -1)
1651 return(-1);
1652
1653 sprintf(buf, "TYPE I\r\n");
1654 len = strlen(buf);
1655#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001656 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001657#endif
1658 res = send(ctxt->controlFd, buf, len, 0);
1659 if (res < 0) {
1660 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1661 return(res);
1662 }
1663 res = xmlNanoFTPReadResponse(ctxt);
1664 if (res != 2) {
1665 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1666 return(-res);
1667 }
1668 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001669 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001670 else
Owen Taylor3473f882001-02-23 17:55:21 +00001671 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001672 buf[sizeof(buf) - 1] = 0;
1673 len = strlen(buf);
1674#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001675 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001676#endif
1677 res = send(ctxt->controlFd, buf, len, 0);
1678 if (res < 0) {
1679 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1680 return(res);
1681 }
1682 res = xmlNanoFTPReadResponse(ctxt);
1683 if (res != 1) {
1684 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1685 return(-res);
1686 }
1687 return(ctxt->dataFd);
1688}
1689
1690/**
1691 * xmlNanoFTPGet:
1692 * @ctx: an FTP context
1693 * @callback: the user callback
1694 * @userData: the user callback data
1695 * @filename: the file to retrieve
1696 *
1697 * Fetch the given file from the server. All data are passed back
1698 * in the callbacks. The last callback has a size of 0 block.
1699 *
1700 * Returns -1 incase of error, 0 otherwise
1701 */
1702
1703int
1704xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1705 const char *filename) {
1706 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1707 char buf[4096];
1708 int len = 0, res;
1709 fd_set rfd;
1710 struct timeval tv;
1711
1712 if ((filename == NULL) && (ctxt->path == NULL))
1713 return(-1);
1714 if (callback == NULL)
1715 return(-1);
1716 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1717 return(-1);
1718
1719 do {
1720 tv.tv_sec = 1;
1721 tv.tv_usec = 0;
1722 FD_ZERO(&rfd);
1723 FD_SET(ctxt->dataFd, &rfd);
1724 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1725 if (res < 0) {
1726#ifdef DEBUG_FTP
1727 perror("select");
1728#endif
1729 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1730 return(-1);
1731 }
1732 if (res == 0) {
1733 res = xmlNanoFTPCheckResponse(ctxt);
1734 if (res < 0) {
1735 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1736 ctxt->dataFd = -1;
1737 return(-1);
1738 }
1739 if (res == 2) {
1740 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1741 return(0);
1742 }
1743
1744 continue;
1745 }
1746 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1747 callback(userData, buf, len);
1748 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1749 return(-1);
1750 }
1751 callback(userData, buf, len);
1752 } while (len != 0);
1753
1754 return(xmlNanoFTPCloseConnection(ctxt));
1755}
1756
1757/**
1758 * xmlNanoFTPRead:
1759 * @ctx: the FTP context
1760 * @dest: a buffer
1761 * @len: the buffer length
1762 *
1763 * This function tries to read @len bytes from the existing FTP connection
1764 * and saves them in @dest. This is a blocking call.
1765 *
1766 * Returns the number of byte read. 0 is an indication of an end of connection.
1767 * -1 indicates a parameter error.
1768 */
1769int
1770xmlNanoFTPRead(void *ctx, void *dest, int len) {
1771 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1772
1773 if (ctx == NULL) return(-1);
1774 if (ctxt->dataFd < 0) return(0);
1775 if (dest == NULL) return(-1);
1776 if (len <= 0) return(0);
1777
1778 len = recv(ctxt->dataFd, dest, len, 0);
1779#ifdef DEBUG_FTP
1780 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1781#endif
1782 if (len <= 0) {
1783 xmlNanoFTPCloseConnection(ctxt);
1784 }
1785 return(len);
1786}
1787
1788/**
1789 * xmlNanoFTPOpen:
1790 * @URL: the URL to the resource
1791 *
1792 * Start to fetch the given ftp:// resource
1793 *
1794 * Returns an FTP context, or NULL
1795 */
1796
1797void*
1798xmlNanoFTPOpen(const char *URL) {
1799 xmlNanoFTPCtxtPtr ctxt;
1800 int sock;
1801
1802 xmlNanoFTPInit();
1803 if (URL == NULL) return(NULL);
1804 if (strncmp("ftp://", URL, 6)) return(NULL);
1805
1806 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
1807 if (ctxt == NULL) return(NULL);
1808 if (xmlNanoFTPConnect(ctxt) < 0) {
1809 xmlNanoFTPFreeCtxt(ctxt);
1810 return(NULL);
1811 }
1812 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1813 if (sock < 0) {
1814 xmlNanoFTPFreeCtxt(ctxt);
1815 return(NULL);
1816 }
1817 return(ctxt);
1818}
1819
1820/**
1821 * xmlNanoFTPClose:
1822 * @ctx: an FTP context
1823 *
1824 * Close the connection and both control and transport
1825 *
1826 * Returns -1 incase of error, 0 otherwise
1827 */
1828
1829int
1830xmlNanoFTPClose(void *ctx) {
1831 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1832
1833 if (ctxt == NULL)
1834 return(-1);
1835
1836 if (ctxt->dataFd >= 0) {
1837 closesocket(ctxt->dataFd);
1838 ctxt->dataFd = -1;
1839 }
1840 if (ctxt->controlFd >= 0) {
1841 xmlNanoFTPQuit(ctxt);
1842 closesocket(ctxt->controlFd);
1843 ctxt->controlFd = -1;
1844 }
1845 xmlNanoFTPFreeCtxt(ctxt);
1846 return(0);
1847}
1848
1849#ifdef STANDALONE
1850/************************************************************************
1851 * *
1852 * Basic test in Standalone mode *
1853 * *
1854 ************************************************************************/
1855void ftpList(void *userData, const char *filename, const char* attrib,
1856 const char *owner, const char *group, unsigned long size, int links,
1857 int year, const char *month, int day, int hour, int minute) {
1858 xmlGenericError(xmlGenericErrorContext,
1859 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1860}
1861void ftpData(void *userData, const char *data, int len) {
1862 if (userData == NULL) return;
1863 if (len <= 0) {
1864 fclose(userData);
1865 return;
1866 }
1867 fwrite(data, len, 1, userData);
1868}
1869
1870int main(int argc, char **argv) {
1871 void *ctxt;
1872 FILE *output;
1873 char *tstfile = NULL;
1874
1875 xmlNanoFTPInit();
1876 if (argc > 1) {
1877 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1878 if (xmlNanoFTPConnect(ctxt) < 0) {
1879 xmlGenericError(xmlGenericErrorContext,
1880 "Couldn't connect to %s\n", argv[1]);
1881 exit(1);
1882 }
1883 if (argc > 2)
1884 tstfile = argv[2];
1885 } else
1886 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1887 if (ctxt == NULL) {
1888 xmlGenericError(xmlGenericErrorContext,
1889 "Couldn't connect to localhost\n");
1890 exit(1);
1891 }
1892 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
1893 output = fopen("/tmp/tstdata", "w");
1894 if (output != NULL) {
1895 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
1896 xmlGenericError(xmlGenericErrorContext,
1897 "Failed to get file\n");
1898
1899 }
1900 xmlNanoFTPClose(ctxt);
1901 xmlMemoryDump();
1902 exit(0);
1903}
1904#endif /* STANDALONE */
1905#else /* !LIBXML_FTP_ENABLED */
1906#ifdef STANDALONE
1907#include <stdio.h>
1908int main(int argc, char **argv) {
1909 xmlGenericError(xmlGenericErrorContext,
1910 "%s : FTP support not compiled in\n", argv[0]);
1911 return(0);
1912}
1913#endif /* STANDALONE */
1914#endif /* LIBXML_FTP_ENABLED */