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