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