blob: 03d68884fc470593bcf5a051bf836808a09a9f82 [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
Daniel Veillardcbaf3992001-12-31 16:16:02 +000015#else /* TESTING */
Daniel Veillardf3afa7d2001-06-09 13:52:58 +000016#define NEED_SOCKETS
Daniel Veillardcbaf3992001-12-31 16:16:02 +000017#endif /* TESTING */
Owen Taylor3473f882001-02-23 17:55:21 +000018
Daniel Veillard34ce8be2002-03-18 19:37:11 +000019#define IN_LIBXML
Daniel Veillard5e2dace2001-07-18 19:30:27 +000020#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000021
22#ifdef LIBXML_FTP_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +000023#include <string.h>
24
25#ifdef HAVE_STDLIB_H
26#include <stdlib.h>
27#endif
28#ifdef HAVE_UNISTD_H
29#include <unistd.h>
30#endif
31#ifdef HAVE_SYS_SOCKET_H
32#include <sys/socket.h>
33#endif
34#ifdef HAVE_NETINET_IN_H
35#include <netinet/in.h>
36#endif
37#ifdef HAVE_ARPA_INET_H
38#include <arpa/inet.h>
39#endif
40#ifdef HAVE_NETDB_H
41#include <netdb.h>
42#endif
43#ifdef HAVE_FCNTL_H
44#include <fcntl.h>
45#endif
46#ifdef HAVE_ERRNO_H
47#include <errno.h>
48#endif
49#ifdef HAVE_SYS_TIME_H
50#include <sys/time.h>
51#endif
52#ifdef HAVE_SYS_SELECT_H
53#include <sys/select.h>
54#endif
55#ifdef HAVE_STRINGS_H
56#include <strings.h>
57#endif
58
59#include <libxml/xmlmemory.h>
Daniel Veillardd0463562001-10-13 09:15:48 +000060#include <libxml/parser.h>
Owen Taylor3473f882001-02-23 17:55:21 +000061#include <libxml/xmlerror.h>
Daniel Veillardd0463562001-10-13 09:15:48 +000062#include <libxml/nanoftp.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000063#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000064
65/* #define DEBUG_FTP 1 */
66#ifdef STANDALONE
67#ifndef DEBUG_FTP
68#define DEBUG_FTP 1
69#endif
70#endif
71
72/**
73 * A couple portability macros
74 */
75#ifndef _WINSOCKAPI_
76#define closesocket(s) close(s)
77#define SOCKET int
78#endif
Daniel Veillardacf7ff02001-10-29 20:21:47 +000079#if defined(VMS) || defined(__VMS)
80#define SOCKLEN_T unsigned int
81#endif
Owen Taylor3473f882001-02-23 17:55:21 +000082
Owen Taylor3473f882001-02-23 17:55:21 +000083#define FTP_COMMAND_OK 200
84#define FTP_SYNTAX_ERROR 500
85#define FTP_GET_PASSWD 331
86#define FTP_BUF_SIZE 512
87
88typedef struct xmlNanoFTPCtxt {
89 char *protocol; /* the protocol name */
90 char *hostname; /* the host name */
91 int port; /* the port */
92 char *path; /* the path within the URL */
93 char *user; /* user string */
94 char *passwd; /* passwd string */
95 struct sockaddr_in ftpAddr; /* the socket address struct */
96 int passive; /* currently we support only passive !!! */
97 SOCKET controlFd; /* the file descriptor for the control socket */
98 SOCKET dataFd; /* the file descriptor for the data socket */
99 int state; /* WRITE / READ / CLOSED */
100 int returnValue; /* the protocol return value */
101 /* buffer for data received from the control connection */
102 char controlBuf[FTP_BUF_SIZE + 1];
103 int controlBufIndex;
104 int controlBufUsed;
105 int controlBufAnswer;
106} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
107
108static int initialized = 0;
109static char *proxy = NULL; /* the proxy name if any */
110static int proxyPort = 0; /* the proxy port if any */
111static char *proxyUser = NULL; /* user for proxy authentication */
112static char *proxyPasswd = NULL;/* passwd for proxy authentication */
113static int proxyType = 0; /* uses TYPE or a@b ? */
114
115/**
116 * xmlNanoFTPInit:
117 *
118 * Initialize the FTP protocol layer.
119 * Currently it just checks for proxy informations,
120 * and get the hostname
121 */
122
123void
124xmlNanoFTPInit(void) {
125 const char *env;
126#ifdef _WINSOCKAPI_
127 WSADATA wsaData;
128#endif
129
130 if (initialized)
131 return;
132
133#ifdef _WINSOCKAPI_
134 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
135 return;
136#endif
137
Owen Taylor3473f882001-02-23 17:55:21 +0000138 proxyPort = 21;
139 env = getenv("no_proxy");
140 if (env != NULL)
141 return;
142 env = getenv("ftp_proxy");
143 if (env != NULL) {
144 xmlNanoFTPScanProxy(env);
145 } else {
146 env = getenv("FTP_PROXY");
147 if (env != NULL) {
148 xmlNanoFTPScanProxy(env);
149 }
150 }
151 env = getenv("ftp_proxy_user");
152 if (env != NULL) {
153 proxyUser = xmlMemStrdup(env);
154 }
155 env = getenv("ftp_proxy_password");
156 if (env != NULL) {
157 proxyPasswd = xmlMemStrdup(env);
158 }
159 initialized = 1;
160}
161
162/**
Daniel Veillarde356c282001-03-10 12:32:04 +0000163 * xmlNanoFTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000164 *
165 * Cleanup the FTP protocol layer. This cleanup proxy informations.
166 */
167
168void
169xmlNanoFTPCleanup(void) {
170 if (proxy != NULL) {
171 xmlFree(proxy);
172 proxy = NULL;
173 }
174 if (proxyUser != NULL) {
175 xmlFree(proxyUser);
176 proxyUser = NULL;
177 }
178 if (proxyPasswd != NULL) {
179 xmlFree(proxyPasswd);
180 proxyPasswd = NULL;
181 }
Owen Taylor3473f882001-02-23 17:55:21 +0000182#ifdef _WINSOCKAPI_
183 if (initialized)
184 WSACleanup();
185#endif
186 initialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000187}
188
189/**
190 * xmlNanoFTPProxy:
191 * @host: the proxy host name
192 * @port: the proxy port
193 * @user: the proxy user name
194 * @passwd: the proxy password
195 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
196 *
197 * Setup the FTP proxy informations.
198 * This can also be done by using ftp_proxy ftp_proxy_user and
199 * ftp_proxy_password environment variables.
200 */
201
202void
203xmlNanoFTPProxy(const char *host, int port, const char *user,
204 const char *passwd, int type) {
205 if (proxy != NULL)
206 xmlFree(proxy);
207 if (proxyUser != NULL)
208 xmlFree(proxyUser);
209 if (proxyPasswd != NULL)
210 xmlFree(proxyPasswd);
211 if (host)
212 proxy = xmlMemStrdup(host);
213 if (user)
214 proxyUser = xmlMemStrdup(user);
215 if (passwd)
216 proxyPasswd = xmlMemStrdup(passwd);
217 proxyPort = port;
218 proxyType = type;
219}
220
221/**
222 * xmlNanoFTPScanURL:
223 * @ctx: an FTP context
224 * @URL: The URL used to initialize the context
225 *
226 * (Re)Initialize an FTP context by parsing the URL and finding
227 * the protocol host port and path it indicates.
228 */
229
230static void
231xmlNanoFTPScanURL(void *ctx, const char *URL) {
232 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
233 const char *cur = URL;
234 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000235 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000236 int port = 0;
237
238 if (ctxt->protocol != NULL) {
239 xmlFree(ctxt->protocol);
240 ctxt->protocol = NULL;
241 }
242 if (ctxt->hostname != NULL) {
243 xmlFree(ctxt->hostname);
244 ctxt->hostname = NULL;
245 }
246 if (ctxt->path != NULL) {
247 xmlFree(ctxt->path);
248 ctxt->path = NULL;
249 }
250 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000251 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000252 while (*cur != 0) {
253 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000254 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000255 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000256 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000257 cur += 3;
258 break;
259 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000260 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000261 }
262 if (*cur == 0) return;
263
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000264 buf[indx] = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000265 /* allow user@ and user:pass@ forms */
266 {
267 const char *p = strchr(cur, '@');
268 if(p) {
269 while(1) {
270 if(cur[0] == ':' || cur[0] == '@') break;
271 buf[indx++] = *cur++;
272 }
273 buf[indx] = 0;
274 ctxt->user = xmlMemStrdup(buf);
275 indx = 0;
276 if(cur[0] == ':') {
277 cur++;
278 while(1) {
279 if(cur[0] == '@') break;
280 buf[indx++] = *cur++;
281 }
282 buf[indx] = 0;
283 ctxt->passwd = xmlMemStrdup(buf);
284 indx = 0;
285 }
286 cur = p+1;
287 }
288 }
289
Owen Taylor3473f882001-02-23 17:55:21 +0000290 while (1) {
291 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000292 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000293 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000294 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000295 cur += 1;
296 while ((*cur >= '0') && (*cur <= '9')) {
297 port *= 10;
298 port += *cur - '0';
299 cur++;
300 }
301 if (port != 0) ctxt->port = port;
302 while ((cur[0] != '/') && (*cur != 0))
303 cur++;
304 break;
305 }
306 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000307 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000308 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000309 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000310 break;
311 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000312 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000313 }
314 if (*cur == 0)
315 ctxt->path = xmlMemStrdup("/");
316 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000317 indx = 0;
318 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000319 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000320 buf[indx++] = *cur++;
321 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000322 ctxt->path = xmlMemStrdup(buf);
323 }
324}
325
326/**
327 * xmlNanoFTPUpdateURL:
328 * @ctx: an FTP context
329 * @URL: The URL used to update the context
330 *
331 * Update an FTP context by parsing the URL and finding
332 * new path it indicates. If there is an error in the
333 * protocol, hostname, port or other information, the
334 * error is raised. It indicates a new connection has to
335 * be established.
336 *
337 * Returns 0 if Ok, -1 in case of error (other host).
338 */
339
340int
341xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
342 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
343 const char *cur = URL;
344 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000345 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000346 int port = 0;
347
348 if (URL == NULL)
349 return(-1);
350 if (ctxt == NULL)
351 return(-1);
352 if (ctxt->protocol == NULL)
353 return(-1);
354 if (ctxt->hostname == NULL)
355 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000356 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000357 while (*cur != 0) {
358 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000359 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000360 if (strcmp(ctxt->protocol, buf))
361 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000362 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000363 cur += 3;
364 break;
365 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000366 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000367 }
368 if (*cur == 0)
369 return(-1);
370
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000371 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000372 while (1) {
373 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000374 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000375 if (strcmp(ctxt->hostname, buf))
376 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000377 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000378 cur += 1;
379 while ((*cur >= '0') && (*cur <= '9')) {
380 port *= 10;
381 port += *cur - '0';
382 cur++;
383 }
384 if (port != ctxt->port)
385 return(-1);
386 while ((cur[0] != '/') && (*cur != 0))
387 cur++;
388 break;
389 }
390 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000391 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000392 if (strcmp(ctxt->hostname, buf))
393 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000394 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000395 break;
396 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000397 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000398 }
399 if (ctxt->path != NULL) {
400 xmlFree(ctxt->path);
401 ctxt->path = NULL;
402 }
403
404 if (*cur == 0)
405 ctxt->path = xmlMemStrdup("/");
406 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000407 indx = 0;
408 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000409 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000410 buf[indx++] = *cur++;
411 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000412 ctxt->path = xmlMemStrdup(buf);
413 }
414 return(0);
415}
416
417/**
418 * xmlNanoFTPScanProxy:
419 * @URL: The proxy URL used to initialize the proxy context
420 *
421 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
422 * the protocol host port it indicates.
423 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
424 * A NULL URL cleans up proxy informations.
425 */
426
427void
428xmlNanoFTPScanProxy(const char *URL) {
429 const char *cur = URL;
430 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000431 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000432 int port = 0;
433
434 if (proxy != NULL) {
435 xmlFree(proxy);
436 proxy = NULL;
437 }
438 if (proxyPort != 0) {
439 proxyPort = 0;
440 }
441#ifdef DEBUG_FTP
442 if (URL == NULL)
443 xmlGenericError(xmlGenericErrorContext, "Removing FTP proxy info\n");
444 else
445 xmlGenericError(xmlGenericErrorContext, "Using FTP proxy %s\n", URL);
446#endif
447 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000448 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000449 while (*cur != 0) {
450 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000451 buf[indx] = 0;
452 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000453 cur += 3;
454 break;
455 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000456 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000457 }
458 if (*cur == 0) return;
459
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000460 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000461 while (1) {
462 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000463 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000464 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000465 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000466 cur += 1;
467 while ((*cur >= '0') && (*cur <= '9')) {
468 port *= 10;
469 port += *cur - '0';
470 cur++;
471 }
472 if (port != 0) proxyPort = port;
473 while ((cur[0] != '/') && (*cur != 0))
474 cur++;
475 break;
476 }
477 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000478 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000479 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000480 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000481 break;
482 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000483 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000484 }
485}
486
487/**
488 * xmlNanoFTPNewCtxt:
489 * @URL: The URL used to initialize the context
490 *
491 * Allocate and initialize a new FTP context.
492 *
493 * Returns an FTP context or NULL in case of error.
494 */
495
496void*
497xmlNanoFTPNewCtxt(const char *URL) {
498 xmlNanoFTPCtxtPtr ret;
499
500 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
501 if (ret == NULL) return(NULL);
502
503 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
504 ret->port = 21;
505 ret->passive = 1;
506 ret->returnValue = 0;
507 ret->controlBufIndex = 0;
508 ret->controlBufUsed = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000509 ret->controlFd = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000510
511 if (URL != NULL)
512 xmlNanoFTPScanURL(ret, URL);
513
514 return(ret);
515}
516
517/**
518 * xmlNanoFTPFreeCtxt:
519 * @ctx: an FTP context
520 *
521 * Frees the context after closing the connection.
522 */
523
524void
525xmlNanoFTPFreeCtxt(void * ctx) {
526 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
527 if (ctxt == NULL) return;
528 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
529 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
530 if (ctxt->path != NULL) xmlFree(ctxt->path);
531 ctxt->passive = 1;
532 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
533 ctxt->controlFd = -1;
534 ctxt->controlBufIndex = -1;
535 ctxt->controlBufUsed = -1;
536 xmlFree(ctxt);
537}
538
539/**
540 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000541 * @buf: the buffer containing the response
542 * @len: the buffer length
543 *
544 * Parsing of the server answer, we just extract the code.
545 *
546 * returns 0 for errors
547 * +XXX for last line of response
548 * -XXX for response to be continued
549 */
550static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000551xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000552 int val = 0;
553
554 if (len < 3) return(-1);
555 if ((*buf >= '0') && (*buf <= '9'))
556 val = val * 10 + (*buf - '0');
557 else
558 return(0);
559 buf++;
560 if ((*buf >= '0') && (*buf <= '9'))
561 val = val * 10 + (*buf - '0');
562 else
563 return(0);
564 buf++;
565 if ((*buf >= '0') && (*buf <= '9'))
566 val = val * 10 + (*buf - '0');
567 else
568 return(0);
569 buf++;
570 if (*buf == '-')
571 return(-val);
572 return(val);
573}
574
575/**
576 * xmlNanoFTPGetMore:
577 * @ctx: an FTP context
578 *
579 * Read more information from the FTP control connection
580 * Returns the number of bytes read, < 0 indicates an error
581 */
582static int
583xmlNanoFTPGetMore(void *ctx) {
584 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
585 int len;
586 int size;
587
588 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
589#ifdef DEBUG_FTP
590 xmlGenericError(xmlGenericErrorContext,
591 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
592 ctxt->controlBufIndex);
593#endif
594 return(-1);
595 }
596
597 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
598#ifdef DEBUG_FTP
599 xmlGenericError(xmlGenericErrorContext,
600 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
601 ctxt->controlBufUsed);
602#endif
603 return(-1);
604 }
605 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
606#ifdef DEBUG_FTP
607 xmlGenericError(xmlGenericErrorContext,
608 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
609 ctxt->controlBufIndex, ctxt->controlBufUsed);
610#endif
611 return(-1);
612 }
613
614 /*
615 * First pack the control buffer
616 */
617 if (ctxt->controlBufIndex > 0) {
618 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
619 ctxt->controlBufUsed - ctxt->controlBufIndex);
620 ctxt->controlBufUsed -= ctxt->controlBufIndex;
621 ctxt->controlBufIndex = 0;
622 }
623 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
624 if (size == 0) {
625#ifdef DEBUG_FTP
626 xmlGenericError(xmlGenericErrorContext,
627 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
628#endif
629 return(0);
630 }
631
632 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000633 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000634 */
635 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
636 size, 0)) < 0) {
637 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
638 ctxt->controlFd = -1;
639 return(-1);
640 }
641#ifdef DEBUG_FTP
642 xmlGenericError(xmlGenericErrorContext,
643 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
644 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
645#endif
646 ctxt->controlBufUsed += len;
647 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
648
649 return(len);
650}
651
652/**
653 * xmlNanoFTPReadResponse:
654 * @ctx: an FTP context
655 *
656 * Read the response from the FTP server after a command.
657 * Returns the code number
658 */
659static int
660xmlNanoFTPReadResponse(void *ctx) {
661 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
662 char *ptr, *end;
663 int len;
664 int res = -1, cur = -1;
665
666get_more:
667 /*
668 * Assumes everything up to controlBuf[controlBufIndex] has been read
669 * and analyzed.
670 */
671 len = xmlNanoFTPGetMore(ctx);
672 if (len < 0) {
673 return(-1);
674 }
675 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
676 return(-1);
677 }
678 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
679 end = &ctxt->controlBuf[ctxt->controlBufUsed];
680
681#ifdef DEBUG_FTP
682 xmlGenericError(xmlGenericErrorContext,
683 "\n<<<\n%s\n--\n", ptr);
684#endif
685 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000686 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000687 if (cur > 0) {
688 /*
689 * Successfully scanned the control code, scratch
690 * till the end of the line, but keep the index to be
691 * able to analyze the result if needed.
692 */
693 res = cur;
694 ptr += 3;
695 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
696 while ((ptr < end) && (*ptr != '\n')) ptr++;
697 if (*ptr == '\n') ptr++;
698 if (*ptr == '\r') ptr++;
699 break;
700 }
701 while ((ptr < end) && (*ptr != '\n')) ptr++;
702 if (ptr >= end) {
703 ctxt->controlBufIndex = ctxt->controlBufUsed;
704 goto get_more;
705 }
706 if (*ptr != '\r') ptr++;
707 }
708
709 if (res < 0) goto get_more;
710 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
711#ifdef DEBUG_FTP
712 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
713 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
714#endif
715
716#ifdef DEBUG_FTP
717 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
718#endif
719 return(res / 100);
720}
721
722/**
723 * xmlNanoFTPGetResponse:
724 * @ctx: an FTP context
725 *
726 * Get the response from the FTP server after a command.
727 * Returns the code number
728 */
729
730int
731xmlNanoFTPGetResponse(void *ctx) {
732 int res;
733
734 res = xmlNanoFTPReadResponse(ctx);
735
736 return(res);
737}
738
739/**
740 * xmlNanoFTPCheckResponse:
741 * @ctx: an FTP context
742 *
743 * Check if there is a response from the FTP server after a command.
744 * Returns the code number, or 0
745 */
746
747int
748xmlNanoFTPCheckResponse(void *ctx) {
749 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
750 fd_set rfd;
751 struct timeval tv;
752
753 tv.tv_sec = 0;
754 tv.tv_usec = 0;
755 FD_ZERO(&rfd);
756 FD_SET(ctxt->controlFd, &rfd);
757 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
758 case 0:
759 return(0);
760 case -1:
761#ifdef DEBUG_FTP
762 perror("select");
763#endif
764 return(-1);
765
766 }
767
768 return(xmlNanoFTPReadResponse(ctx));
769}
770
771/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000772 * Send the user authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000773 */
774
775static int
776xmlNanoFTPSendUser(void *ctx) {
777 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
778 char buf[200];
779 int len;
780 int res;
781
782 if (ctxt->user == NULL)
783 sprintf(buf, "USER anonymous\r\n");
784 else
Owen Taylor3473f882001-02-23 17:55:21 +0000785 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000786 buf[sizeof(buf) - 1] = 0;
787 len = strlen(buf);
788#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000789 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000790#endif
791 res = send(ctxt->controlFd, buf, len, 0);
792 if (res < 0) return(res);
793 return(0);
794}
795
796/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000797 * Send the password authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000798 */
799
800static int
801xmlNanoFTPSendPasswd(void *ctx) {
802 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
803 char buf[200];
804 int len;
805 int res;
806
807 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000808 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000809 else
Owen Taylor3473f882001-02-23 17:55:21 +0000810 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000811 buf[sizeof(buf) - 1] = 0;
812 len = strlen(buf);
813#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000814 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000815#endif
816 res = send(ctxt->controlFd, buf, len, 0);
817 if (res < 0) return(res);
818 return(0);
819}
820
821/**
822 * xmlNanoFTPQuit:
823 * @ctx: an FTP context
824 *
825 * Send a QUIT command to the server
826 *
827 * Returns -1 in case of error, 0 otherwise
828 */
829
830
831int
832xmlNanoFTPQuit(void *ctx) {
833 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
834 char buf[200];
835 int len;
836 int res;
837
838 sprintf(buf, "QUIT\r\n");
839 len = strlen(buf);
840#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000841 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 +0000842#endif
843 res = send(ctxt->controlFd, buf, len, 0);
844 return(0);
845}
846
847/**
848 * xmlNanoFTPConnect:
849 * @ctx: an FTP context
850 *
851 * Tries to open a control connection
852 *
853 * Returns -1 in case of error, 0 otherwise
854 */
855
856int
857xmlNanoFTPConnect(void *ctx) {
858 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
859 struct hostent *hp;
860 int port;
861 int res;
862
863 if (ctxt == NULL)
864 return(-1);
865 if (ctxt->hostname == NULL)
866 return(-1);
867
868 /*
869 * do the blocking DNS query.
870 */
871 if (proxy)
872 hp = gethostbyname(proxy);
873 else
874 hp = gethostbyname(ctxt->hostname);
875 if (hp == NULL)
876 return(-1);
877
878 /*
879 * Prepare the socket
880 */
881 memset(&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
882 ctxt->ftpAddr.sin_family = AF_INET;
883 memcpy(&ctxt->ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
884 if (proxy) {
885 port = proxyPort;
886 } else {
887 port = ctxt->port;
888 }
889 if (port == 0)
890 port = 21;
891 ctxt->ftpAddr.sin_port = htons(port);
892 ctxt->controlFd = socket(AF_INET, SOCK_STREAM, 0);
893 if (ctxt->controlFd < 0)
894 return(-1);
895
896 /*
897 * Do the connect.
898 */
899 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
900 sizeof(struct sockaddr_in)) < 0) {
901 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
902 ctxt->controlFd = -1;
903 return(-1);
904 }
905
906 /*
907 * Wait for the HELLO from the server.
908 */
909 res = xmlNanoFTPGetResponse(ctxt);
910 if (res != 2) {
911 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
912 ctxt->controlFd = -1;
913 return(-1);
914 }
915
916 /*
917 * State diagram for the login operation on the FTP server
918 *
919 * Reference: RFC 959
920 *
921 * 1
922 * +---+ USER +---+------------->+---+
923 * | B |---------->| W | 2 ---->| E |
924 * +---+ +---+------ | -->+---+
925 * | | | | |
926 * 3 | | 4,5 | | |
927 * -------------- ----- | | |
928 * | | | | |
929 * | | | | |
930 * | --------- |
931 * | 1| | | |
932 * V | | | |
933 * +---+ PASS +---+ 2 | ------>+---+
934 * | |---------->| W |------------->| S |
935 * +---+ +---+ ---------->+---+
936 * | | | | |
937 * 3 | |4,5| | |
938 * -------------- -------- |
939 * | | | | |
940 * | | | | |
941 * | -----------
942 * | 1,3| | | |
943 * V | 2| | |
944 * +---+ ACCT +---+-- | ----->+---+
945 * | |---------->| W | 4,5 -------->| F |
946 * +---+ +---+------------->+---+
947 *
948 * Of course in case of using a proxy this get really nasty and is not
949 * standardized at all :-(
950 */
951 if (proxy) {
952 int len;
953 char buf[400];
954
955 if (proxyUser != NULL) {
956 /*
957 * We need proxy auth
958 */
Owen Taylor3473f882001-02-23 17:55:21 +0000959 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +0000960 buf[sizeof(buf) - 1] = 0;
961 len = strlen(buf);
962#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000963 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000964#endif
965 res = send(ctxt->controlFd, buf, len, 0);
966 if (res < 0) {
967 closesocket(ctxt->controlFd);
968 ctxt->controlFd = -1;
969 return(res);
970 }
971 res = xmlNanoFTPGetResponse(ctxt);
972 switch (res) {
973 case 2:
974 if (proxyPasswd == NULL)
975 break;
976 case 3:
977 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000978 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +0000979 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000980 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000981 buf[sizeof(buf) - 1] = 0;
982 len = strlen(buf);
983#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000984 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000985#endif
986 res = send(ctxt->controlFd, buf, len, 0);
987 if (res < 0) {
988 closesocket(ctxt->controlFd);
989 ctxt->controlFd = -1;
990 return(res);
991 }
992 res = xmlNanoFTPGetResponse(ctxt);
993 if (res > 3) {
994 closesocket(ctxt->controlFd);
995 ctxt->controlFd = -1;
996 return(-1);
997 }
998 break;
999 case 1:
1000 break;
1001 case 4:
1002 case 5:
1003 case -1:
1004 default:
1005 closesocket(ctxt->controlFd);
1006 ctxt->controlFd = -1;
1007 return(-1);
1008 }
1009 }
1010
1011 /*
1012 * We assume we don't need more authentication to the proxy
1013 * and that it succeeded :-\
1014 */
1015 switch (proxyType) {
1016 case 0:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001017 /* we will try in sequence */
Owen Taylor3473f882001-02-23 17:55:21 +00001018 case 1:
1019 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001020 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001021 buf[sizeof(buf) - 1] = 0;
1022 len = strlen(buf);
1023#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001024 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001025#endif
1026 res = send(ctxt->controlFd, buf, len, 0);
1027 if (res < 0) {
1028 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1029 ctxt->controlFd = -1;
1030 return(res);
1031 }
1032 res = xmlNanoFTPGetResponse(ctxt);
1033 if (res == 2) {
1034 /* we assume it worked :-\ 1 is error for SITE command */
1035 proxyType = 1;
1036 break;
1037 }
1038 if (proxyType == 1) {
1039 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1040 ctxt->controlFd = -1;
1041 return(-1);
1042 }
1043 case 2:
1044 /* USER user@host command */
1045 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001046 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1047 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001048 else
Owen Taylor3473f882001-02-23 17:55:21 +00001049 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1050 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001051 buf[sizeof(buf) - 1] = 0;
1052 len = strlen(buf);
1053#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001054 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001055#endif
1056 res = send(ctxt->controlFd, buf, len, 0);
1057 if (res < 0) {
1058 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1059 ctxt->controlFd = -1;
1060 return(res);
1061 }
1062 res = xmlNanoFTPGetResponse(ctxt);
1063 if ((res == 1) || (res == 2)) {
1064 /* we assume it worked :-\ */
1065 proxyType = 2;
1066 return(0);
1067 }
1068 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001069 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001070 else
Owen Taylor3473f882001-02-23 17:55:21 +00001071 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001072 buf[sizeof(buf) - 1] = 0;
1073 len = strlen(buf);
1074#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001075 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001076#endif
1077 res = send(ctxt->controlFd, buf, len, 0);
1078 if (res < 0) {
1079 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1080 ctxt->controlFd = -1;
1081 return(res);
1082 }
1083 res = xmlNanoFTPGetResponse(ctxt);
1084 if ((res == 1) || (res == 2)) {
1085 /* we assume it worked :-\ */
1086 proxyType = 2;
1087 return(0);
1088 }
1089 if (proxyType == 2) {
1090 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1091 ctxt->controlFd = -1;
1092 return(-1);
1093 }
1094 case 3:
1095 /*
1096 * If you need support for other Proxy authentication scheme
1097 * send the code or at least the sequence in use.
1098 */
1099 default:
1100 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1101 ctxt->controlFd = -1;
1102 return(-1);
1103 }
1104 }
1105 /*
1106 * Non-proxy handling.
1107 */
1108 res = xmlNanoFTPSendUser(ctxt);
1109 if (res < 0) {
1110 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1111 ctxt->controlFd = -1;
1112 return(-1);
1113 }
1114 res = xmlNanoFTPGetResponse(ctxt);
1115 switch (res) {
1116 case 2:
1117 return(0);
1118 case 3:
1119 break;
1120 case 1:
1121 case 4:
1122 case 5:
1123 case -1:
1124 default:
1125 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1126 ctxt->controlFd = -1;
1127 return(-1);
1128 }
1129 res = xmlNanoFTPSendPasswd(ctxt);
1130 if (res < 0) {
1131 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1132 ctxt->controlFd = -1;
1133 return(-1);
1134 }
1135 res = xmlNanoFTPGetResponse(ctxt);
1136 switch (res) {
1137 case 2:
1138 break;
1139 case 3:
1140 xmlGenericError(xmlGenericErrorContext,
1141 "FTP server asking for ACCNT on anonymous\n");
1142 case 1:
1143 case 4:
1144 case 5:
1145 case -1:
1146 default:
1147 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1148 ctxt->controlFd = -1;
1149 return(-1);
1150 }
1151
1152 return(0);
1153}
1154
1155/**
1156 * xmlNanoFTPConnectTo:
1157 * @server: an FTP server name
1158 * @port: the port (use 21 if 0)
1159 *
1160 * Tries to open a control connection to the given server/port
1161 *
1162 * Returns an fTP context or NULL if it failed
1163 */
1164
1165void*
1166xmlNanoFTPConnectTo(const char *server, int port) {
1167 xmlNanoFTPCtxtPtr ctxt;
1168 int res;
1169
1170 xmlNanoFTPInit();
1171 if (server == NULL)
1172 return(NULL);
1173 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1174 ctxt->hostname = xmlMemStrdup(server);
1175 if (port != 0)
1176 ctxt->port = port;
1177 res = xmlNanoFTPConnect(ctxt);
1178 if (res < 0) {
1179 xmlNanoFTPFreeCtxt(ctxt);
1180 return(NULL);
1181 }
1182 return(ctxt);
1183}
1184
1185/**
1186 * xmlNanoFTPCwd:
1187 * @ctx: an FTP context
1188 * @directory: a directory on the server
1189 *
1190 * Tries to change the remote directory
1191 *
1192 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1193 */
1194
1195int
1196xmlNanoFTPCwd(void *ctx, char *directory) {
1197 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1198 char buf[400];
1199 int len;
1200 int res;
1201
1202 /*
1203 * Expected response code for CWD:
1204 *
1205 * CWD
1206 * 250
1207 * 500, 501, 502, 421, 530, 550
1208 */
Owen Taylor3473f882001-02-23 17:55:21 +00001209 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001210 buf[sizeof(buf) - 1] = 0;
1211 len = strlen(buf);
1212#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001213 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001214#endif
1215 res = send(ctxt->controlFd, buf, len, 0);
1216 if (res < 0) return(res);
1217 res = xmlNanoFTPGetResponse(ctxt);
1218 if (res == 4) {
1219 return(-1);
1220 }
1221 if (res == 2) return(1);
1222 if (res == 5) {
1223 return(0);
1224 }
1225 return(0);
1226}
1227
1228/**
1229 * xmlNanoFTPGetConnection:
1230 * @ctx: an FTP context
1231 *
1232 * Try to open a data connection to the server. Currently only
1233 * passive mode is supported.
1234 *
1235 * Returns -1 incase of error, 0 otherwise
1236 */
1237
1238int
1239xmlNanoFTPGetConnection(void *ctx) {
1240 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1241 char buf[200], *cur;
1242 int len, i;
1243 int res;
1244 unsigned char ad[6], *adp, *portp;
1245 unsigned int temp[6];
1246 struct sockaddr_in dataAddr;
1247 SOCKLEN_T dataAddrLen;
1248
1249 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1250 if (ctxt->dataFd < 0) {
1251 xmlGenericError(xmlGenericErrorContext,
1252 "xmlNanoFTPGetConnection: failed to create socket\n");
1253 return(-1);
1254 }
1255 dataAddrLen = sizeof(dataAddr);
1256 memset(&dataAddr, 0, dataAddrLen);
1257 dataAddr.sin_family = AF_INET;
1258
1259 if (ctxt->passive) {
1260 sprintf(buf, "PASV\r\n");
1261 len = strlen(buf);
1262#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001263 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001264#endif
1265 res = send(ctxt->controlFd, buf, len, 0);
1266 if (res < 0) {
1267 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1268 return(res);
1269 }
1270 res = xmlNanoFTPReadResponse(ctx);
1271 if (res != 2) {
1272 if (res == 5) {
1273 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1274 return(-1);
1275 } else {
1276 /*
1277 * retry with an active connection
1278 */
1279 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1280 ctxt->passive = 0;
1281 }
1282 }
1283 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1284 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1285 if (sscanf(cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1286 &temp[3], &temp[4], &temp[5]) != 6) {
1287 xmlGenericError(xmlGenericErrorContext,
1288 "Invalid answer to PASV\n");
1289 if (ctxt->dataFd != -1) {
1290 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1291 }
1292 return(-1);
1293 }
1294 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1295 memcpy(&dataAddr.sin_addr, &ad[0], 4);
1296 memcpy(&dataAddr.sin_port, &ad[4], 2);
1297 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1298 xmlGenericError(xmlGenericErrorContext,
1299 "Failed to create a data connection\n");
1300 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1301 return (-1);
1302 }
1303 } else {
1304 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1305 dataAddr.sin_port = 0;
1306 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1307 xmlGenericError(xmlGenericErrorContext,
1308 "Failed to bind a port\n");
1309 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1310 return (-1);
1311 }
1312 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1313
1314 if (listen(ctxt->dataFd, 1) < 0) {
1315 xmlGenericError(xmlGenericErrorContext,
1316 "Could not listen on port %d\n",
1317 ntohs(dataAddr.sin_port));
1318 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1319 return (-1);
1320 }
1321 adp = (unsigned char *) &dataAddr.sin_addr;
1322 portp = (unsigned char *) &dataAddr.sin_port;
Owen Taylor3473f882001-02-23 17:55:21 +00001323 snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1324 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1325 portp[0] & 0xff, portp[1] & 0xff);
Owen Taylor3473f882001-02-23 17:55:21 +00001326 buf[sizeof(buf) - 1] = 0;
1327 len = strlen(buf);
1328#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001329 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001330#endif
1331
1332 res = send(ctxt->controlFd, buf, len, 0);
1333 if (res < 0) {
1334 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1335 return(res);
1336 }
1337 res = xmlNanoFTPGetResponse(ctxt);
1338 if (res != 2) {
1339 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1340 return(-1);
1341 }
1342 }
1343 return(ctxt->dataFd);
1344
1345}
1346
1347/**
1348 * xmlNanoFTPCloseConnection:
1349 * @ctx: an FTP context
1350 *
1351 * Close the data connection from the server
1352 *
1353 * Returns -1 incase of error, 0 otherwise
1354 */
1355
1356int
1357xmlNanoFTPCloseConnection(void *ctx) {
1358 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1359 int res;
1360 fd_set rfd, efd;
1361 struct timeval tv;
1362
1363 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1364 tv.tv_sec = 15;
1365 tv.tv_usec = 0;
1366 FD_ZERO(&rfd);
1367 FD_SET(ctxt->controlFd, &rfd);
1368 FD_ZERO(&efd);
1369 FD_SET(ctxt->controlFd, &efd);
1370 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1371 if (res < 0) {
1372#ifdef DEBUG_FTP
1373 perror("select");
1374#endif
1375 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1376 return(-1);
1377 }
1378 if (res == 0) {
1379#ifdef DEBUG_FTP
1380 xmlGenericError(xmlGenericErrorContext,
1381 "xmlNanoFTPCloseConnection: timeout\n");
1382#endif
1383 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1384 } else {
1385 res = xmlNanoFTPGetResponse(ctxt);
1386 if (res != 2) {
1387 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1388 return(-1);
1389 }
1390 }
1391 return(0);
1392}
1393
1394/**
1395 * xmlNanoFTPParseList:
1396 * @list: some data listing received from the server
1397 * @callback: the user callback
1398 * @userData: the user callback data
1399 *
1400 * Parse at most one entry from the listing.
1401 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001402 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001403 */
1404
1405static int
1406xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1407 const char *cur = list;
1408 char filename[151];
1409 char attrib[11];
1410 char owner[11];
1411 char group[11];
1412 char month[4];
1413 int year = 0;
1414 int minute = 0;
1415 int hour = 0;
1416 int day = 0;
1417 unsigned long size = 0;
1418 int links = 0;
1419 int i;
1420
1421 if (!strncmp(cur, "total", 5)) {
1422 cur += 5;
1423 while (*cur == ' ') cur++;
1424 while ((*cur >= '0') && (*cur <= '9'))
1425 links = (links * 10) + (*cur++ - '0');
1426 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1427 cur++;
1428 return(cur - list);
1429 } else if (*list == '+') {
1430 return(0);
1431 } else {
1432 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1433 cur++;
1434 if (*cur == 0) return(0);
1435 i = 0;
1436 while (*cur != ' ') {
1437 if (i < 10)
1438 attrib[i++] = *cur;
1439 cur++;
1440 if (*cur == 0) return(0);
1441 }
1442 attrib[10] = 0;
1443 while (*cur == ' ') cur++;
1444 if (*cur == 0) return(0);
1445 while ((*cur >= '0') && (*cur <= '9'))
1446 links = (links * 10) + (*cur++ - '0');
1447 while (*cur == ' ') cur++;
1448 if (*cur == 0) return(0);
1449 i = 0;
1450 while (*cur != ' ') {
1451 if (i < 10)
1452 owner[i++] = *cur;
1453 cur++;
1454 if (*cur == 0) return(0);
1455 }
1456 owner[i] = 0;
1457 while (*cur == ' ') cur++;
1458 if (*cur == 0) return(0);
1459 i = 0;
1460 while (*cur != ' ') {
1461 if (i < 10)
1462 group[i++] = *cur;
1463 cur++;
1464 if (*cur == 0) return(0);
1465 }
1466 group[i] = 0;
1467 while (*cur == ' ') cur++;
1468 if (*cur == 0) return(0);
1469 while ((*cur >= '0') && (*cur <= '9'))
1470 size = (size * 10) + (*cur++ - '0');
1471 while (*cur == ' ') cur++;
1472 if (*cur == 0) return(0);
1473 i = 0;
1474 while (*cur != ' ') {
1475 if (i < 3)
1476 month[i++] = *cur;
1477 cur++;
1478 if (*cur == 0) return(0);
1479 }
1480 month[i] = 0;
1481 while (*cur == ' ') cur++;
1482 if (*cur == 0) return(0);
1483 while ((*cur >= '0') && (*cur <= '9'))
1484 day = (day * 10) + (*cur++ - '0');
1485 while (*cur == ' ') cur++;
1486 if (*cur == 0) return(0);
1487 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1488 if ((cur[1] == ':') || (cur[2] == ':')) {
1489 while ((*cur >= '0') && (*cur <= '9'))
1490 hour = (hour * 10) + (*cur++ - '0');
1491 if (*cur == ':') cur++;
1492 while ((*cur >= '0') && (*cur <= '9'))
1493 minute = (minute * 10) + (*cur++ - '0');
1494 } else {
1495 while ((*cur >= '0') && (*cur <= '9'))
1496 year = (year * 10) + (*cur++ - '0');
1497 }
1498 while (*cur == ' ') cur++;
1499 if (*cur == 0) return(0);
1500 i = 0;
1501 while ((*cur != '\n') && (*cur != '\r')) {
1502 if (i < 150)
1503 filename[i++] = *cur;
1504 cur++;
1505 if (*cur == 0) return(0);
1506 }
1507 filename[i] = 0;
1508 if ((*cur != '\n') && (*cur != '\r'))
1509 return(0);
1510 while ((*cur == '\n') || (*cur == '\r'))
1511 cur++;
1512 }
1513 if (callback != NULL) {
1514 callback(userData, filename, attrib, owner, group, size, links,
1515 year, month, day, hour, minute);
1516 }
1517 return(cur - list);
1518}
1519
1520/**
1521 * xmlNanoFTPList:
1522 * @ctx: an FTP context
1523 * @callback: the user callback
1524 * @userData: the user callback data
1525 * @filename: optional files to list
1526 *
1527 * Do a listing on the server. All files info are passed back
1528 * in the callbacks.
1529 *
1530 * Returns -1 incase of error, 0 otherwise
1531 */
1532
1533int
1534xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1535 char *filename) {
1536 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1537 char buf[4096 + 1];
1538 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001539 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001540 fd_set rfd, efd;
1541 struct timeval tv;
1542
1543 if (filename == NULL) {
1544 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1545 return(-1);
1546 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1547 if (ctxt->dataFd == -1)
1548 return(-1);
1549 sprintf(buf, "LIST -L\r\n");
1550 } else {
1551 if (filename[0] != '/') {
1552 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1553 return(-1);
1554 }
1555 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1556 if (ctxt->dataFd == -1)
1557 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001558 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001559 }
1560 buf[sizeof(buf) - 1] = 0;
1561 len = strlen(buf);
1562#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001563 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001564#endif
1565 res = send(ctxt->controlFd, buf, len, 0);
1566 if (res < 0) {
1567 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1568 return(res);
1569 }
1570 res = xmlNanoFTPReadResponse(ctxt);
1571 if (res != 1) {
1572 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1573 return(-res);
1574 }
1575
1576 do {
1577 tv.tv_sec = 1;
1578 tv.tv_usec = 0;
1579 FD_ZERO(&rfd);
1580 FD_SET(ctxt->dataFd, &rfd);
1581 FD_ZERO(&efd);
1582 FD_SET(ctxt->dataFd, &efd);
1583 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1584 if (res < 0) {
1585#ifdef DEBUG_FTP
1586 perror("select");
1587#endif
1588 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1589 return(-1);
1590 }
1591 if (res == 0) {
1592 res = xmlNanoFTPCheckResponse(ctxt);
1593 if (res < 0) {
1594 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1595 ctxt->dataFd = -1;
1596 return(-1);
1597 }
1598 if (res == 2) {
1599 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1600 return(0);
1601 }
1602
1603 continue;
1604 }
1605
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001606 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001607#ifdef DEBUG_FTP
1608 perror("recv");
1609#endif
1610 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1611 ctxt->dataFd = -1;
1612 return(-1);
1613 }
1614#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001615 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001616#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001617 indx += len;
1618 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001619 base = 0;
1620 do {
1621 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1622 base += res;
1623 } while (res > 0);
1624
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001625 memmove(&buf[0], &buf[base], indx - base);
1626 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001627 } while (len != 0);
1628 xmlNanoFTPCloseConnection(ctxt);
1629 return(0);
1630}
1631
1632/**
1633 * xmlNanoFTPGetSocket:
1634 * @ctx: an FTP context
1635 * @filename: the file to retrieve (or NULL if path is in context).
1636 *
1637 * Initiate fetch of the given file from the server.
1638 *
1639 * Returns the socket for the data connection, or <0 in case of error
1640 */
1641
1642
1643int
1644xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1645 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1646 char buf[300];
1647 int res, len;
1648 if ((filename == NULL) && (ctxt->path == NULL))
1649 return(-1);
1650 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1651 if (ctxt->dataFd == -1)
1652 return(-1);
1653
1654 sprintf(buf, "TYPE I\r\n");
1655 len = strlen(buf);
1656#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001657 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001658#endif
1659 res = send(ctxt->controlFd, buf, len, 0);
1660 if (res < 0) {
1661 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1662 return(res);
1663 }
1664 res = xmlNanoFTPReadResponse(ctxt);
1665 if (res != 2) {
1666 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1667 return(-res);
1668 }
1669 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001670 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001671 else
Owen Taylor3473f882001-02-23 17:55:21 +00001672 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001673 buf[sizeof(buf) - 1] = 0;
1674 len = strlen(buf);
1675#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001676 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001677#endif
1678 res = send(ctxt->controlFd, buf, len, 0);
1679 if (res < 0) {
1680 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1681 return(res);
1682 }
1683 res = xmlNanoFTPReadResponse(ctxt);
1684 if (res != 1) {
1685 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1686 return(-res);
1687 }
1688 return(ctxt->dataFd);
1689}
1690
1691/**
1692 * xmlNanoFTPGet:
1693 * @ctx: an FTP context
1694 * @callback: the user callback
1695 * @userData: the user callback data
1696 * @filename: the file to retrieve
1697 *
1698 * Fetch the given file from the server. All data are passed back
1699 * in the callbacks. The last callback has a size of 0 block.
1700 *
1701 * Returns -1 incase of error, 0 otherwise
1702 */
1703
1704int
1705xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1706 const char *filename) {
1707 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1708 char buf[4096];
1709 int len = 0, res;
1710 fd_set rfd;
1711 struct timeval tv;
1712
1713 if ((filename == NULL) && (ctxt->path == NULL))
1714 return(-1);
1715 if (callback == NULL)
1716 return(-1);
1717 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1718 return(-1);
1719
1720 do {
1721 tv.tv_sec = 1;
1722 tv.tv_usec = 0;
1723 FD_ZERO(&rfd);
1724 FD_SET(ctxt->dataFd, &rfd);
1725 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1726 if (res < 0) {
1727#ifdef DEBUG_FTP
1728 perror("select");
1729#endif
1730 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1731 return(-1);
1732 }
1733 if (res == 0) {
1734 res = xmlNanoFTPCheckResponse(ctxt);
1735 if (res < 0) {
1736 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1737 ctxt->dataFd = -1;
1738 return(-1);
1739 }
1740 if (res == 2) {
1741 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1742 return(0);
1743 }
1744
1745 continue;
1746 }
1747 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1748 callback(userData, buf, len);
1749 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1750 return(-1);
1751 }
1752 callback(userData, buf, len);
1753 } while (len != 0);
1754
1755 return(xmlNanoFTPCloseConnection(ctxt));
1756}
1757
1758/**
1759 * xmlNanoFTPRead:
1760 * @ctx: the FTP context
1761 * @dest: a buffer
1762 * @len: the buffer length
1763 *
1764 * This function tries to read @len bytes from the existing FTP connection
1765 * and saves them in @dest. This is a blocking call.
1766 *
1767 * Returns the number of byte read. 0 is an indication of an end of connection.
1768 * -1 indicates a parameter error.
1769 */
1770int
1771xmlNanoFTPRead(void *ctx, void *dest, int len) {
1772 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1773
1774 if (ctx == NULL) return(-1);
1775 if (ctxt->dataFd < 0) return(0);
1776 if (dest == NULL) return(-1);
1777 if (len <= 0) return(0);
1778
1779 len = recv(ctxt->dataFd, dest, len, 0);
1780#ifdef DEBUG_FTP
1781 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1782#endif
1783 if (len <= 0) {
1784 xmlNanoFTPCloseConnection(ctxt);
1785 }
1786 return(len);
1787}
1788
1789/**
1790 * xmlNanoFTPOpen:
1791 * @URL: the URL to the resource
1792 *
1793 * Start to fetch the given ftp:// resource
1794 *
1795 * Returns an FTP context, or NULL
1796 */
1797
1798void*
1799xmlNanoFTPOpen(const char *URL) {
1800 xmlNanoFTPCtxtPtr ctxt;
1801 int sock;
1802
1803 xmlNanoFTPInit();
1804 if (URL == NULL) return(NULL);
1805 if (strncmp("ftp://", URL, 6)) return(NULL);
1806
1807 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
1808 if (ctxt == NULL) return(NULL);
1809 if (xmlNanoFTPConnect(ctxt) < 0) {
1810 xmlNanoFTPFreeCtxt(ctxt);
1811 return(NULL);
1812 }
1813 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1814 if (sock < 0) {
1815 xmlNanoFTPFreeCtxt(ctxt);
1816 return(NULL);
1817 }
1818 return(ctxt);
1819}
1820
1821/**
1822 * xmlNanoFTPClose:
1823 * @ctx: an FTP context
1824 *
1825 * Close the connection and both control and transport
1826 *
1827 * Returns -1 incase of error, 0 otherwise
1828 */
1829
1830int
1831xmlNanoFTPClose(void *ctx) {
1832 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1833
1834 if (ctxt == NULL)
1835 return(-1);
1836
1837 if (ctxt->dataFd >= 0) {
1838 closesocket(ctxt->dataFd);
1839 ctxt->dataFd = -1;
1840 }
1841 if (ctxt->controlFd >= 0) {
1842 xmlNanoFTPQuit(ctxt);
1843 closesocket(ctxt->controlFd);
1844 ctxt->controlFd = -1;
1845 }
1846 xmlNanoFTPFreeCtxt(ctxt);
1847 return(0);
1848}
1849
1850#ifdef STANDALONE
1851/************************************************************************
1852 * *
1853 * Basic test in Standalone mode *
1854 * *
1855 ************************************************************************/
1856void ftpList(void *userData, const char *filename, const char* attrib,
1857 const char *owner, const char *group, unsigned long size, int links,
1858 int year, const char *month, int day, int hour, int minute) {
1859 xmlGenericError(xmlGenericErrorContext,
1860 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1861}
1862void ftpData(void *userData, const char *data, int len) {
1863 if (userData == NULL) return;
1864 if (len <= 0) {
1865 fclose(userData);
1866 return;
1867 }
1868 fwrite(data, len, 1, userData);
1869}
1870
1871int main(int argc, char **argv) {
1872 void *ctxt;
1873 FILE *output;
1874 char *tstfile = NULL;
1875
1876 xmlNanoFTPInit();
1877 if (argc > 1) {
1878 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1879 if (xmlNanoFTPConnect(ctxt) < 0) {
1880 xmlGenericError(xmlGenericErrorContext,
1881 "Couldn't connect to %s\n", argv[1]);
1882 exit(1);
1883 }
1884 if (argc > 2)
1885 tstfile = argv[2];
1886 } else
1887 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1888 if (ctxt == NULL) {
1889 xmlGenericError(xmlGenericErrorContext,
1890 "Couldn't connect to localhost\n");
1891 exit(1);
1892 }
1893 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
1894 output = fopen("/tmp/tstdata", "w");
1895 if (output != NULL) {
1896 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
1897 xmlGenericError(xmlGenericErrorContext,
1898 "Failed to get file\n");
1899
1900 }
1901 xmlNanoFTPClose(ctxt);
1902 xmlMemoryDump();
1903 exit(0);
1904}
1905#endif /* STANDALONE */
1906#else /* !LIBXML_FTP_ENABLED */
1907#ifdef STANDALONE
1908#include <stdio.h>
1909int main(int argc, char **argv) {
1910 xmlGenericError(xmlGenericErrorContext,
1911 "%s : FTP support not compiled in\n", argv[0]);
1912 return(0);
1913}
1914#endif /* STANDALONE */
1915#endif /* LIBXML_FTP_ENABLED */