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