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