blob: 050b8b0831eaf09eb73db4128b98b37b99bab364 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * nanoftp.c: basic FTP client support
3 *
4 * Reference: RFC 959
5 */
6
7#ifdef TESTING
8#define STANDALONE
9#define HAVE_STDLIB_H
10#define HAVE_UNISTD_H
11#define HAVE_SYS_SOCKET_H
12#define HAVE_NETINET_IN_H
13#define HAVE_NETDB_H
14#define HAVE_SYS_TIME_H
Bjorn Reese70a9da52001-04-21 16:57:29 +000015#include <libxml/xmlversion.h>
16#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 */
23#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000024#endif /* STANDALONE */
25
Owen Taylor3473f882001-02-23 17:55:21 +000026
27#ifdef LIBXML_FTP_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +000028#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
Owen Taylor3473f882001-02-23 17:55:21 +0000764 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000765 buf[sizeof(buf) - 1] = 0;
766 len = strlen(buf);
767#ifdef DEBUG_FTP
768 xmlGenericError(xmlGenericErrorContext, buf);
769#endif
770 res = send(ctxt->controlFd, buf, len, 0);
771 if (res < 0) return(res);
772 return(0);
773}
774
775/**
776 * Send the password authentification
777 */
778
779static int
780xmlNanoFTPSendPasswd(void *ctx) {
781 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
782 char buf[200];
783 int len;
784 int res;
785
786 if (ctxt->passwd == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000787 snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
Owen Taylor3473f882001-02-23 17:55:21 +0000788 else
Owen Taylor3473f882001-02-23 17:55:21 +0000789 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000790 buf[sizeof(buf) - 1] = 0;
791 len = strlen(buf);
792#ifdef DEBUG_FTP
793 xmlGenericError(xmlGenericErrorContext, buf);
794#endif
795 res = send(ctxt->controlFd, buf, len, 0);
796 if (res < 0) return(res);
797 return(0);
798}
799
800/**
801 * xmlNanoFTPQuit:
802 * @ctx: an FTP context
803 *
804 * Send a QUIT command to the server
805 *
806 * Returns -1 in case of error, 0 otherwise
807 */
808
809
810int
811xmlNanoFTPQuit(void *ctx) {
812 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
813 char buf[200];
814 int len;
815 int res;
816
817 sprintf(buf, "QUIT\r\n");
818 len = strlen(buf);
819#ifdef DEBUG_FTP
820 xmlGenericError(xmlGenericErrorContext, buf);
821#endif
822 res = send(ctxt->controlFd, buf, len, 0);
823 return(0);
824}
825
826/**
827 * xmlNanoFTPConnect:
828 * @ctx: an FTP context
829 *
830 * Tries to open a control connection
831 *
832 * Returns -1 in case of error, 0 otherwise
833 */
834
835int
836xmlNanoFTPConnect(void *ctx) {
837 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
838 struct hostent *hp;
839 int port;
840 int res;
841
842 if (ctxt == NULL)
843 return(-1);
844 if (ctxt->hostname == NULL)
845 return(-1);
846
847 /*
848 * do the blocking DNS query.
849 */
850 if (proxy)
851 hp = gethostbyname(proxy);
852 else
853 hp = gethostbyname(ctxt->hostname);
854 if (hp == NULL)
855 return(-1);
856
857 /*
858 * Prepare the socket
859 */
860 memset(&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
861 ctxt->ftpAddr.sin_family = AF_INET;
862 memcpy(&ctxt->ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
863 if (proxy) {
864 port = proxyPort;
865 } else {
866 port = ctxt->port;
867 }
868 if (port == 0)
869 port = 21;
870 ctxt->ftpAddr.sin_port = htons(port);
871 ctxt->controlFd = socket(AF_INET, SOCK_STREAM, 0);
872 if (ctxt->controlFd < 0)
873 return(-1);
874
875 /*
876 * Do the connect.
877 */
878 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
879 sizeof(struct sockaddr_in)) < 0) {
880 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
881 ctxt->controlFd = -1;
882 return(-1);
883 }
884
885 /*
886 * Wait for the HELLO from the server.
887 */
888 res = xmlNanoFTPGetResponse(ctxt);
889 if (res != 2) {
890 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
891 ctxt->controlFd = -1;
892 return(-1);
893 }
894
895 /*
896 * State diagram for the login operation on the FTP server
897 *
898 * Reference: RFC 959
899 *
900 * 1
901 * +---+ USER +---+------------->+---+
902 * | B |---------->| W | 2 ---->| E |
903 * +---+ +---+------ | -->+---+
904 * | | | | |
905 * 3 | | 4,5 | | |
906 * -------------- ----- | | |
907 * | | | | |
908 * | | | | |
909 * | --------- |
910 * | 1| | | |
911 * V | | | |
912 * +---+ PASS +---+ 2 | ------>+---+
913 * | |---------->| W |------------->| S |
914 * +---+ +---+ ---------->+---+
915 * | | | | |
916 * 3 | |4,5| | |
917 * -------------- -------- |
918 * | | | | |
919 * | | | | |
920 * | -----------
921 * | 1,3| | | |
922 * V | 2| | |
923 * +---+ ACCT +---+-- | ----->+---+
924 * | |---------->| W | 4,5 -------->| F |
925 * +---+ +---+------------->+---+
926 *
927 * Of course in case of using a proxy this get really nasty and is not
928 * standardized at all :-(
929 */
930 if (proxy) {
931 int len;
932 char buf[400];
933
934 if (proxyUser != NULL) {
935 /*
936 * We need proxy auth
937 */
Owen Taylor3473f882001-02-23 17:55:21 +0000938 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +0000939 buf[sizeof(buf) - 1] = 0;
940 len = strlen(buf);
941#ifdef DEBUG_FTP
942 xmlGenericError(xmlGenericErrorContext, buf);
943#endif
944 res = send(ctxt->controlFd, buf, len, 0);
945 if (res < 0) {
946 closesocket(ctxt->controlFd);
947 ctxt->controlFd = -1;
948 return(res);
949 }
950 res = xmlNanoFTPGetResponse(ctxt);
951 switch (res) {
952 case 2:
953 if (proxyPasswd == NULL)
954 break;
955 case 3:
956 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000957 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +0000958 else
Owen Taylor3473f882001-02-23 17:55:21 +0000959 snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n",
960 hostname);
Owen Taylor3473f882001-02-23 17:55:21 +0000961 buf[sizeof(buf) - 1] = 0;
962 len = strlen(buf);
963#ifdef DEBUG_FTP
964 xmlGenericError(xmlGenericErrorContext, buf);
965#endif
966 res = send(ctxt->controlFd, buf, len, 0);
967 if (res < 0) {
968 closesocket(ctxt->controlFd);
969 ctxt->controlFd = -1;
970 return(res);
971 }
972 res = xmlNanoFTPGetResponse(ctxt);
973 if (res > 3) {
974 closesocket(ctxt->controlFd);
975 ctxt->controlFd = -1;
976 return(-1);
977 }
978 break;
979 case 1:
980 break;
981 case 4:
982 case 5:
983 case -1:
984 default:
985 closesocket(ctxt->controlFd);
986 ctxt->controlFd = -1;
987 return(-1);
988 }
989 }
990
991 /*
992 * We assume we don't need more authentication to the proxy
993 * and that it succeeded :-\
994 */
995 switch (proxyType) {
996 case 0:
997 /* we will try in seqence */
998 case 1:
999 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001000 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001001 buf[sizeof(buf) - 1] = 0;
1002 len = strlen(buf);
1003#ifdef DEBUG_FTP
1004 xmlGenericError(xmlGenericErrorContext, buf);
1005#endif
1006 res = send(ctxt->controlFd, buf, len, 0);
1007 if (res < 0) {
1008 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1009 ctxt->controlFd = -1;
1010 return(res);
1011 }
1012 res = xmlNanoFTPGetResponse(ctxt);
1013 if (res == 2) {
1014 /* we assume it worked :-\ 1 is error for SITE command */
1015 proxyType = 1;
1016 break;
1017 }
1018 if (proxyType == 1) {
1019 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1020 ctxt->controlFd = -1;
1021 return(-1);
1022 }
1023 case 2:
1024 /* USER user@host command */
1025 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001026 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1027 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001028 else
Owen Taylor3473f882001-02-23 17:55:21 +00001029 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1030 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001031 buf[sizeof(buf) - 1] = 0;
1032 len = strlen(buf);
1033#ifdef DEBUG_FTP
1034 xmlGenericError(xmlGenericErrorContext, buf);
1035#endif
1036 res = send(ctxt->controlFd, buf, len, 0);
1037 if (res < 0) {
1038 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1039 ctxt->controlFd = -1;
1040 return(res);
1041 }
1042 res = xmlNanoFTPGetResponse(ctxt);
1043 if ((res == 1) || (res == 2)) {
1044 /* we assume it worked :-\ */
1045 proxyType = 2;
1046 return(0);
1047 }
1048 if (ctxt->passwd == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001049 snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001050 else
Owen Taylor3473f882001-02-23 17:55:21 +00001051 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001052 buf[sizeof(buf) - 1] = 0;
1053 len = strlen(buf);
1054#ifdef DEBUG_FTP
1055 xmlGenericError(xmlGenericErrorContext, buf);
1056#endif
1057 res = send(ctxt->controlFd, buf, len, 0);
1058 if (res < 0) {
1059 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1060 ctxt->controlFd = -1;
1061 return(res);
1062 }
1063 res = xmlNanoFTPGetResponse(ctxt);
1064 if ((res == 1) || (res == 2)) {
1065 /* we assume it worked :-\ */
1066 proxyType = 2;
1067 return(0);
1068 }
1069 if (proxyType == 2) {
1070 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1071 ctxt->controlFd = -1;
1072 return(-1);
1073 }
1074 case 3:
1075 /*
1076 * If you need support for other Proxy authentication scheme
1077 * send the code or at least the sequence in use.
1078 */
1079 default:
1080 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1081 ctxt->controlFd = -1;
1082 return(-1);
1083 }
1084 }
1085 /*
1086 * Non-proxy handling.
1087 */
1088 res = xmlNanoFTPSendUser(ctxt);
1089 if (res < 0) {
1090 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1091 ctxt->controlFd = -1;
1092 return(-1);
1093 }
1094 res = xmlNanoFTPGetResponse(ctxt);
1095 switch (res) {
1096 case 2:
1097 return(0);
1098 case 3:
1099 break;
1100 case 1:
1101 case 4:
1102 case 5:
1103 case -1:
1104 default:
1105 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1106 ctxt->controlFd = -1;
1107 return(-1);
1108 }
1109 res = xmlNanoFTPSendPasswd(ctxt);
1110 if (res < 0) {
1111 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1112 ctxt->controlFd = -1;
1113 return(-1);
1114 }
1115 res = xmlNanoFTPGetResponse(ctxt);
1116 switch (res) {
1117 case 2:
1118 break;
1119 case 3:
1120 xmlGenericError(xmlGenericErrorContext,
1121 "FTP server asking for ACCNT on anonymous\n");
1122 case 1:
1123 case 4:
1124 case 5:
1125 case -1:
1126 default:
1127 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1128 ctxt->controlFd = -1;
1129 return(-1);
1130 }
1131
1132 return(0);
1133}
1134
1135/**
1136 * xmlNanoFTPConnectTo:
1137 * @server: an FTP server name
1138 * @port: the port (use 21 if 0)
1139 *
1140 * Tries to open a control connection to the given server/port
1141 *
1142 * Returns an fTP context or NULL if it failed
1143 */
1144
1145void*
1146xmlNanoFTPConnectTo(const char *server, int port) {
1147 xmlNanoFTPCtxtPtr ctxt;
1148 int res;
1149
1150 xmlNanoFTPInit();
1151 if (server == NULL)
1152 return(NULL);
1153 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1154 ctxt->hostname = xmlMemStrdup(server);
1155 if (port != 0)
1156 ctxt->port = port;
1157 res = xmlNanoFTPConnect(ctxt);
1158 if (res < 0) {
1159 xmlNanoFTPFreeCtxt(ctxt);
1160 return(NULL);
1161 }
1162 return(ctxt);
1163}
1164
1165/**
1166 * xmlNanoFTPCwd:
1167 * @ctx: an FTP context
1168 * @directory: a directory on the server
1169 *
1170 * Tries to change the remote directory
1171 *
1172 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1173 */
1174
1175int
1176xmlNanoFTPCwd(void *ctx, char *directory) {
1177 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1178 char buf[400];
1179 int len;
1180 int res;
1181
1182 /*
1183 * Expected response code for CWD:
1184 *
1185 * CWD
1186 * 250
1187 * 500, 501, 502, 421, 530, 550
1188 */
Owen Taylor3473f882001-02-23 17:55:21 +00001189 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001190 buf[sizeof(buf) - 1] = 0;
1191 len = strlen(buf);
1192#ifdef DEBUG_FTP
1193 xmlGenericError(xmlGenericErrorContext, buf);
1194#endif
1195 res = send(ctxt->controlFd, buf, len, 0);
1196 if (res < 0) return(res);
1197 res = xmlNanoFTPGetResponse(ctxt);
1198 if (res == 4) {
1199 return(-1);
1200 }
1201 if (res == 2) return(1);
1202 if (res == 5) {
1203 return(0);
1204 }
1205 return(0);
1206}
1207
1208/**
1209 * xmlNanoFTPGetConnection:
1210 * @ctx: an FTP context
1211 *
1212 * Try to open a data connection to the server. Currently only
1213 * passive mode is supported.
1214 *
1215 * Returns -1 incase of error, 0 otherwise
1216 */
1217
1218int
1219xmlNanoFTPGetConnection(void *ctx) {
1220 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1221 char buf[200], *cur;
1222 int len, i;
1223 int res;
1224 unsigned char ad[6], *adp, *portp;
1225 unsigned int temp[6];
1226 struct sockaddr_in dataAddr;
1227 SOCKLEN_T dataAddrLen;
1228
1229 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1230 if (ctxt->dataFd < 0) {
1231 xmlGenericError(xmlGenericErrorContext,
1232 "xmlNanoFTPGetConnection: failed to create socket\n");
1233 return(-1);
1234 }
1235 dataAddrLen = sizeof(dataAddr);
1236 memset(&dataAddr, 0, dataAddrLen);
1237 dataAddr.sin_family = AF_INET;
1238
1239 if (ctxt->passive) {
1240 sprintf(buf, "PASV\r\n");
1241 len = strlen(buf);
1242#ifdef DEBUG_FTP
1243 xmlGenericError(xmlGenericErrorContext, buf);
1244#endif
1245 res = send(ctxt->controlFd, buf, len, 0);
1246 if (res < 0) {
1247 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1248 return(res);
1249 }
1250 res = xmlNanoFTPReadResponse(ctx);
1251 if (res != 2) {
1252 if (res == 5) {
1253 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1254 return(-1);
1255 } else {
1256 /*
1257 * retry with an active connection
1258 */
1259 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1260 ctxt->passive = 0;
1261 }
1262 }
1263 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1264 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1265 if (sscanf(cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1266 &temp[3], &temp[4], &temp[5]) != 6) {
1267 xmlGenericError(xmlGenericErrorContext,
1268 "Invalid answer to PASV\n");
1269 if (ctxt->dataFd != -1) {
1270 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1271 }
1272 return(-1);
1273 }
1274 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1275 memcpy(&dataAddr.sin_addr, &ad[0], 4);
1276 memcpy(&dataAddr.sin_port, &ad[4], 2);
1277 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1278 xmlGenericError(xmlGenericErrorContext,
1279 "Failed to create a data connection\n");
1280 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1281 return (-1);
1282 }
1283 } else {
1284 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1285 dataAddr.sin_port = 0;
1286 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1287 xmlGenericError(xmlGenericErrorContext,
1288 "Failed to bind a port\n");
1289 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1290 return (-1);
1291 }
1292 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1293
1294 if (listen(ctxt->dataFd, 1) < 0) {
1295 xmlGenericError(xmlGenericErrorContext,
1296 "Could not listen on port %d\n",
1297 ntohs(dataAddr.sin_port));
1298 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1299 return (-1);
1300 }
1301 adp = (unsigned char *) &dataAddr.sin_addr;
1302 portp = (unsigned char *) &dataAddr.sin_port;
Owen Taylor3473f882001-02-23 17:55:21 +00001303 snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1304 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1305 portp[0] & 0xff, portp[1] & 0xff);
Owen Taylor3473f882001-02-23 17:55:21 +00001306 buf[sizeof(buf) - 1] = 0;
1307 len = strlen(buf);
1308#ifdef DEBUG_FTP
1309 xmlGenericError(xmlGenericErrorContext, buf);
1310#endif
1311
1312 res = send(ctxt->controlFd, buf, len, 0);
1313 if (res < 0) {
1314 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1315 return(res);
1316 }
1317 res = xmlNanoFTPGetResponse(ctxt);
1318 if (res != 2) {
1319 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1320 return(-1);
1321 }
1322 }
1323 return(ctxt->dataFd);
1324
1325}
1326
1327/**
1328 * xmlNanoFTPCloseConnection:
1329 * @ctx: an FTP context
1330 *
1331 * Close the data connection from the server
1332 *
1333 * Returns -1 incase of error, 0 otherwise
1334 */
1335
1336int
1337xmlNanoFTPCloseConnection(void *ctx) {
1338 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1339 int res;
1340 fd_set rfd, efd;
1341 struct timeval tv;
1342
1343 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1344 tv.tv_sec = 15;
1345 tv.tv_usec = 0;
1346 FD_ZERO(&rfd);
1347 FD_SET(ctxt->controlFd, &rfd);
1348 FD_ZERO(&efd);
1349 FD_SET(ctxt->controlFd, &efd);
1350 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1351 if (res < 0) {
1352#ifdef DEBUG_FTP
1353 perror("select");
1354#endif
1355 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1356 return(-1);
1357 }
1358 if (res == 0) {
1359#ifdef DEBUG_FTP
1360 xmlGenericError(xmlGenericErrorContext,
1361 "xmlNanoFTPCloseConnection: timeout\n");
1362#endif
1363 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1364 } else {
1365 res = xmlNanoFTPGetResponse(ctxt);
1366 if (res != 2) {
1367 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1368 return(-1);
1369 }
1370 }
1371 return(0);
1372}
1373
1374/**
1375 * xmlNanoFTPParseList:
1376 * @list: some data listing received from the server
1377 * @callback: the user callback
1378 * @userData: the user callback data
1379 *
1380 * Parse at most one entry from the listing.
1381 *
1382 * Returns -1 incase of error, the lenght of data parsed otherwise
1383 */
1384
1385static int
1386xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1387 const char *cur = list;
1388 char filename[151];
1389 char attrib[11];
1390 char owner[11];
1391 char group[11];
1392 char month[4];
1393 int year = 0;
1394 int minute = 0;
1395 int hour = 0;
1396 int day = 0;
1397 unsigned long size = 0;
1398 int links = 0;
1399 int i;
1400
1401 if (!strncmp(cur, "total", 5)) {
1402 cur += 5;
1403 while (*cur == ' ') cur++;
1404 while ((*cur >= '0') && (*cur <= '9'))
1405 links = (links * 10) + (*cur++ - '0');
1406 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1407 cur++;
1408 return(cur - list);
1409 } else if (*list == '+') {
1410 return(0);
1411 } else {
1412 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1413 cur++;
1414 if (*cur == 0) return(0);
1415 i = 0;
1416 while (*cur != ' ') {
1417 if (i < 10)
1418 attrib[i++] = *cur;
1419 cur++;
1420 if (*cur == 0) return(0);
1421 }
1422 attrib[10] = 0;
1423 while (*cur == ' ') cur++;
1424 if (*cur == 0) return(0);
1425 while ((*cur >= '0') && (*cur <= '9'))
1426 links = (links * 10) + (*cur++ - '0');
1427 while (*cur == ' ') cur++;
1428 if (*cur == 0) return(0);
1429 i = 0;
1430 while (*cur != ' ') {
1431 if (i < 10)
1432 owner[i++] = *cur;
1433 cur++;
1434 if (*cur == 0) return(0);
1435 }
1436 owner[i] = 0;
1437 while (*cur == ' ') cur++;
1438 if (*cur == 0) return(0);
1439 i = 0;
1440 while (*cur != ' ') {
1441 if (i < 10)
1442 group[i++] = *cur;
1443 cur++;
1444 if (*cur == 0) return(0);
1445 }
1446 group[i] = 0;
1447 while (*cur == ' ') cur++;
1448 if (*cur == 0) return(0);
1449 while ((*cur >= '0') && (*cur <= '9'))
1450 size = (size * 10) + (*cur++ - '0');
1451 while (*cur == ' ') cur++;
1452 if (*cur == 0) return(0);
1453 i = 0;
1454 while (*cur != ' ') {
1455 if (i < 3)
1456 month[i++] = *cur;
1457 cur++;
1458 if (*cur == 0) return(0);
1459 }
1460 month[i] = 0;
1461 while (*cur == ' ') cur++;
1462 if (*cur == 0) return(0);
1463 while ((*cur >= '0') && (*cur <= '9'))
1464 day = (day * 10) + (*cur++ - '0');
1465 while (*cur == ' ') cur++;
1466 if (*cur == 0) return(0);
1467 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1468 if ((cur[1] == ':') || (cur[2] == ':')) {
1469 while ((*cur >= '0') && (*cur <= '9'))
1470 hour = (hour * 10) + (*cur++ - '0');
1471 if (*cur == ':') cur++;
1472 while ((*cur >= '0') && (*cur <= '9'))
1473 minute = (minute * 10) + (*cur++ - '0');
1474 } else {
1475 while ((*cur >= '0') && (*cur <= '9'))
1476 year = (year * 10) + (*cur++ - '0');
1477 }
1478 while (*cur == ' ') cur++;
1479 if (*cur == 0) return(0);
1480 i = 0;
1481 while ((*cur != '\n') && (*cur != '\r')) {
1482 if (i < 150)
1483 filename[i++] = *cur;
1484 cur++;
1485 if (*cur == 0) return(0);
1486 }
1487 filename[i] = 0;
1488 if ((*cur != '\n') && (*cur != '\r'))
1489 return(0);
1490 while ((*cur == '\n') || (*cur == '\r'))
1491 cur++;
1492 }
1493 if (callback != NULL) {
1494 callback(userData, filename, attrib, owner, group, size, links,
1495 year, month, day, hour, minute);
1496 }
1497 return(cur - list);
1498}
1499
1500/**
1501 * xmlNanoFTPList:
1502 * @ctx: an FTP context
1503 * @callback: the user callback
1504 * @userData: the user callback data
1505 * @filename: optional files to list
1506 *
1507 * Do a listing on the server. All files info are passed back
1508 * in the callbacks.
1509 *
1510 * Returns -1 incase of error, 0 otherwise
1511 */
1512
1513int
1514xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1515 char *filename) {
1516 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1517 char buf[4096 + 1];
1518 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001519 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001520 fd_set rfd, efd;
1521 struct timeval tv;
1522
1523 if (filename == NULL) {
1524 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1525 return(-1);
1526 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1527 if (ctxt->dataFd == -1)
1528 return(-1);
1529 sprintf(buf, "LIST -L\r\n");
1530 } else {
1531 if (filename[0] != '/') {
1532 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1533 return(-1);
1534 }
1535 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1536 if (ctxt->dataFd == -1)
1537 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001538 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001539 }
1540 buf[sizeof(buf) - 1] = 0;
1541 len = strlen(buf);
1542#ifdef DEBUG_FTP
1543 xmlGenericError(xmlGenericErrorContext, buf);
1544#endif
1545 res = send(ctxt->controlFd, buf, len, 0);
1546 if (res < 0) {
1547 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1548 return(res);
1549 }
1550 res = xmlNanoFTPReadResponse(ctxt);
1551 if (res != 1) {
1552 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1553 return(-res);
1554 }
1555
1556 do {
1557 tv.tv_sec = 1;
1558 tv.tv_usec = 0;
1559 FD_ZERO(&rfd);
1560 FD_SET(ctxt->dataFd, &rfd);
1561 FD_ZERO(&efd);
1562 FD_SET(ctxt->dataFd, &efd);
1563 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1564 if (res < 0) {
1565#ifdef DEBUG_FTP
1566 perror("select");
1567#endif
1568 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1569 return(-1);
1570 }
1571 if (res == 0) {
1572 res = xmlNanoFTPCheckResponse(ctxt);
1573 if (res < 0) {
1574 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1575 ctxt->dataFd = -1;
1576 return(-1);
1577 }
1578 if (res == 2) {
1579 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1580 return(0);
1581 }
1582
1583 continue;
1584 }
1585
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001586 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001587#ifdef DEBUG_FTP
1588 perror("recv");
1589#endif
1590 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1591 ctxt->dataFd = -1;
1592 return(-1);
1593 }
1594#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001595 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001596#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001597 indx += len;
1598 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001599 base = 0;
1600 do {
1601 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1602 base += res;
1603 } while (res > 0);
1604
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001605 memmove(&buf[0], &buf[base], indx - base);
1606 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001607 } while (len != 0);
1608 xmlNanoFTPCloseConnection(ctxt);
1609 return(0);
1610}
1611
1612/**
1613 * xmlNanoFTPGetSocket:
1614 * @ctx: an FTP context
1615 * @filename: the file to retrieve (or NULL if path is in context).
1616 *
1617 * Initiate fetch of the given file from the server.
1618 *
1619 * Returns the socket for the data connection, or <0 in case of error
1620 */
1621
1622
1623int
1624xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1625 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1626 char buf[300];
1627 int res, len;
1628 if ((filename == NULL) && (ctxt->path == NULL))
1629 return(-1);
1630 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1631 if (ctxt->dataFd == -1)
1632 return(-1);
1633
1634 sprintf(buf, "TYPE I\r\n");
1635 len = strlen(buf);
1636#ifdef DEBUG_FTP
1637 xmlGenericError(xmlGenericErrorContext, buf);
1638#endif
1639 res = send(ctxt->controlFd, buf, len, 0);
1640 if (res < 0) {
1641 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1642 return(res);
1643 }
1644 res = xmlNanoFTPReadResponse(ctxt);
1645 if (res != 2) {
1646 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1647 return(-res);
1648 }
1649 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001650 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001651 else
Owen Taylor3473f882001-02-23 17:55:21 +00001652 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001653 buf[sizeof(buf) - 1] = 0;
1654 len = strlen(buf);
1655#ifdef DEBUG_FTP
1656 xmlGenericError(xmlGenericErrorContext, buf);
1657#endif
1658 res = send(ctxt->controlFd, buf, len, 0);
1659 if (res < 0) {
1660 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1661 return(res);
1662 }
1663 res = xmlNanoFTPReadResponse(ctxt);
1664 if (res != 1) {
1665 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1666 return(-res);
1667 }
1668 return(ctxt->dataFd);
1669}
1670
1671/**
1672 * xmlNanoFTPGet:
1673 * @ctx: an FTP context
1674 * @callback: the user callback
1675 * @userData: the user callback data
1676 * @filename: the file to retrieve
1677 *
1678 * Fetch the given file from the server. All data are passed back
1679 * in the callbacks. The last callback has a size of 0 block.
1680 *
1681 * Returns -1 incase of error, 0 otherwise
1682 */
1683
1684int
1685xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1686 const char *filename) {
1687 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1688 char buf[4096];
1689 int len = 0, res;
1690 fd_set rfd;
1691 struct timeval tv;
1692
1693 if ((filename == NULL) && (ctxt->path == NULL))
1694 return(-1);
1695 if (callback == NULL)
1696 return(-1);
1697 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1698 return(-1);
1699
1700 do {
1701 tv.tv_sec = 1;
1702 tv.tv_usec = 0;
1703 FD_ZERO(&rfd);
1704 FD_SET(ctxt->dataFd, &rfd);
1705 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1706 if (res < 0) {
1707#ifdef DEBUG_FTP
1708 perror("select");
1709#endif
1710 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1711 return(-1);
1712 }
1713 if (res == 0) {
1714 res = xmlNanoFTPCheckResponse(ctxt);
1715 if (res < 0) {
1716 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1717 ctxt->dataFd = -1;
1718 return(-1);
1719 }
1720 if (res == 2) {
1721 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1722 return(0);
1723 }
1724
1725 continue;
1726 }
1727 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1728 callback(userData, buf, len);
1729 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1730 return(-1);
1731 }
1732 callback(userData, buf, len);
1733 } while (len != 0);
1734
1735 return(xmlNanoFTPCloseConnection(ctxt));
1736}
1737
1738/**
1739 * xmlNanoFTPRead:
1740 * @ctx: the FTP context
1741 * @dest: a buffer
1742 * @len: the buffer length
1743 *
1744 * This function tries to read @len bytes from the existing FTP connection
1745 * and saves them in @dest. This is a blocking call.
1746 *
1747 * Returns the number of byte read. 0 is an indication of an end of connection.
1748 * -1 indicates a parameter error.
1749 */
1750int
1751xmlNanoFTPRead(void *ctx, void *dest, int len) {
1752 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1753
1754 if (ctx == NULL) return(-1);
1755 if (ctxt->dataFd < 0) return(0);
1756 if (dest == NULL) return(-1);
1757 if (len <= 0) return(0);
1758
1759 len = recv(ctxt->dataFd, dest, len, 0);
1760#ifdef DEBUG_FTP
1761 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1762#endif
1763 if (len <= 0) {
1764 xmlNanoFTPCloseConnection(ctxt);
1765 }
1766 return(len);
1767}
1768
1769/**
1770 * xmlNanoFTPOpen:
1771 * @URL: the URL to the resource
1772 *
1773 * Start to fetch the given ftp:// resource
1774 *
1775 * Returns an FTP context, or NULL
1776 */
1777
1778void*
1779xmlNanoFTPOpen(const char *URL) {
1780 xmlNanoFTPCtxtPtr ctxt;
1781 int sock;
1782
1783 xmlNanoFTPInit();
1784 if (URL == NULL) return(NULL);
1785 if (strncmp("ftp://", URL, 6)) return(NULL);
1786
1787 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
1788 if (ctxt == NULL) return(NULL);
1789 if (xmlNanoFTPConnect(ctxt) < 0) {
1790 xmlNanoFTPFreeCtxt(ctxt);
1791 return(NULL);
1792 }
1793 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1794 if (sock < 0) {
1795 xmlNanoFTPFreeCtxt(ctxt);
1796 return(NULL);
1797 }
1798 return(ctxt);
1799}
1800
1801/**
1802 * xmlNanoFTPClose:
1803 * @ctx: an FTP context
1804 *
1805 * Close the connection and both control and transport
1806 *
1807 * Returns -1 incase of error, 0 otherwise
1808 */
1809
1810int
1811xmlNanoFTPClose(void *ctx) {
1812 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1813
1814 if (ctxt == NULL)
1815 return(-1);
1816
1817 if (ctxt->dataFd >= 0) {
1818 closesocket(ctxt->dataFd);
1819 ctxt->dataFd = -1;
1820 }
1821 if (ctxt->controlFd >= 0) {
1822 xmlNanoFTPQuit(ctxt);
1823 closesocket(ctxt->controlFd);
1824 ctxt->controlFd = -1;
1825 }
1826 xmlNanoFTPFreeCtxt(ctxt);
1827 return(0);
1828}
1829
1830#ifdef STANDALONE
1831/************************************************************************
1832 * *
1833 * Basic test in Standalone mode *
1834 * *
1835 ************************************************************************/
1836void ftpList(void *userData, const char *filename, const char* attrib,
1837 const char *owner, const char *group, unsigned long size, int links,
1838 int year, const char *month, int day, int hour, int minute) {
1839 xmlGenericError(xmlGenericErrorContext,
1840 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1841}
1842void ftpData(void *userData, const char *data, int len) {
1843 if (userData == NULL) return;
1844 if (len <= 0) {
1845 fclose(userData);
1846 return;
1847 }
1848 fwrite(data, len, 1, userData);
1849}
1850
1851int main(int argc, char **argv) {
1852 void *ctxt;
1853 FILE *output;
1854 char *tstfile = NULL;
1855
1856 xmlNanoFTPInit();
1857 if (argc > 1) {
1858 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1859 if (xmlNanoFTPConnect(ctxt) < 0) {
1860 xmlGenericError(xmlGenericErrorContext,
1861 "Couldn't connect to %s\n", argv[1]);
1862 exit(1);
1863 }
1864 if (argc > 2)
1865 tstfile = argv[2];
1866 } else
1867 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1868 if (ctxt == NULL) {
1869 xmlGenericError(xmlGenericErrorContext,
1870 "Couldn't connect to localhost\n");
1871 exit(1);
1872 }
1873 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
1874 output = fopen("/tmp/tstdata", "w");
1875 if (output != NULL) {
1876 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
1877 xmlGenericError(xmlGenericErrorContext,
1878 "Failed to get file\n");
1879
1880 }
1881 xmlNanoFTPClose(ctxt);
1882 xmlMemoryDump();
1883 exit(0);
1884}
1885#endif /* STANDALONE */
1886#else /* !LIBXML_FTP_ENABLED */
1887#ifdef STANDALONE
1888#include <stdio.h>
1889int main(int argc, char **argv) {
1890 xmlGenericError(xmlGenericErrorContext,
1891 "%s : FTP support not compiled in\n", argv[0]);
1892 return(0);
1893}
1894#endif /* STANDALONE */
1895#endif /* LIBXML_FTP_ENABLED */