blob: 9af52c05cae5e7044ded0b6b6431e40e783b03d4 [file] [log] [blame]
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001/**
Daniel Veillardda07c342000-01-25 18:31:22 +00002 * ftp.c: basic handling of an FTP command connection to check for
3 * directory availability. No transfer is needed.
4 *
5 * Reference: RFC 959
6 */
7
8#ifdef WIN32
9#include "win32config.h"
10#else
11#include "config.h"
12#endif
13
14#include <stdio.h>
15#include <string.h>
16
17#ifdef HAVE_CTYPE_H
18#include <ctype.h>
19#endif
20#ifdef HAVE_UNISTD_H
21#include <unistd.h>
22#endif
23
24#include <sys/types.h>
25#ifdef HAVE_SYS_TIME_H
26#include <sys/time.h>
27#endif
28#ifdef HAVE_SYS_SOCKET_H
29#include <sys/socket.h>
30#endif
James Henstridgef3be9312000-01-28 13:59:21 +000031#ifdef HAVE_NETINET_IN_H
32#include <netinet/in.h>
33#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +000034#ifdef HAVE_ARPA_INET_H
35#include <arpa/inet.h>
36#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000037#ifdef HAVE_NETDB_H
38#include <netdb.h>
39#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +000040#ifdef HAVE_FCNTL_H
41#include <fcntl.h>
42#endif
43#ifdef HAVE_ERRNO_H
44#include <errno.h>
45#endif
46#ifdef HAVE_SYS_TIME_H
47#include <sys/time.h>
48#endif
49#ifdef HAVE_SYS_SELECT_H
50#include <sys/select.h>
51#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000052#ifdef HAVE_RESOLV_H
53#include <resolv.h>
54#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +000055#ifdef HAVE_STDLIB_H
56#include <stdlib.h>
57#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000058
59#include "xmlmemory.h"
60#include "nanoftp.h"
61
62/* #define DEBUG_FTP 1 */
63#ifdef STANDALONE
Daniel Veillarde41f2b72000-01-30 20:00:07 +000064#ifndef DEBUG_FTP
Daniel Veillardda07c342000-01-25 18:31:22 +000065#define DEBUG_FTP 1
66#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +000067#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000068
69static char hostname[100];
70
71#define FTP_COMMAND_OK 200
72#define FTP_SYNTAX_ERROR 500
73#define FTP_GET_PASSWD 331
74
75typedef struct xmlNanoFTPCtxt {
76 char *protocol; /* the protocol name */
77 char *hostname; /* the host name */
78 int port; /* the port */
79 char *path; /* the path within the URL */
80 char *user; /* user string */
81 char *passwd; /* passwd string */
82 struct sockaddr_in ftpAddr; /* the socket address struct */
83 int passive; /* currently we support only passive !!! */
84 int controlFd; /* the file descriptor for the control socket */
85 int dataFd; /* the file descriptor for the data socket */
86 int state; /* WRITE / READ / CLOSED */
87 int returnValue; /* the protocol return value */
88} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
89
Daniel Veillarde41f2b72000-01-30 20:00:07 +000090static int initialized = 0;
91static char *proxy = NULL; /* the proxy name if any */
92static int proxyPort = 0; /* the proxy port if any */
93static char *proxyUser = NULL; /* user for proxy authentication */
94static char *proxyPasswd = NULL;/* passwd for proxy authentication */
95static int proxyType = 0; /* uses TYPE or a@b ? */
96
97/**
98 * xmlNanoFTPInit:
99 *
100 * Initialize the FTP protocol layer.
101 * Currently it just checks for proxy informations,
102 * and get the hostname
103 */
104
105void
106xmlNanoFTPInit(void) {
107 const char *env;
108
109 if (initialized)
110 return;
111
112 gethostname(hostname, sizeof(hostname));
113
114 proxyPort = 21;
115 env = getenv("no_proxy");
116 if (env != NULL)
117 return;
118 env = getenv("ftp_proxy");
119 if (env != NULL) {
120 xmlNanoFTPScanProxy(env);
121 } else {
122 env = getenv("FTP_PROXY");
123 if (env != NULL) {
124 xmlNanoFTPScanProxy(env);
125 }
126 }
127 env = getenv("ftp_proxy_user");
128 if (env != NULL) {
129 proxyUser = xmlMemStrdup(env);
130 }
131 env = getenv("ftp_proxy_password");
132 if (env != NULL) {
133 proxyPasswd = xmlMemStrdup(env);
134 }
135 initialized = 1;
136}
137
138/**
139 * xmlNanoFTPClenup:
140 *
141 * Cleanup the FTP protocol layer. This cleanup proxy informations.
142 */
143
144void
145xmlNanoFTPCleanup(void) {
146 if (proxy != NULL) {
147 xmlFree(proxy);
148 proxy = NULL;
149 }
150 if (proxyUser != NULL) {
151 xmlFree(proxyUser);
152 proxyUser = NULL;
153 }
154 if (proxyPasswd != NULL) {
155 xmlFree(proxyPasswd);
156 proxyPasswd = NULL;
157 }
158 hostname[0] = 0;
159 initialized = 0;
160 return;
161}
162
163/**
164 * xmlNanoFTPProxy:
165 * @host: the proxy host name
166 * @port: the proxy port
167 * @user: the proxy user name
168 * @passwd: the proxy password
169 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
170 *
171 * Setup the FTP proxy informations.
172 * This can also be done by using ftp_proxy ftp_proxy_user and
173 * ftp_proxy_password environment variables.
174 */
175
176void
177xmlNanoFTPProxy(const char *host, int port, const char *user,
178 const char *passwd, int type) {
179 if (proxy != NULL)
180 xmlFree(proxy);
181 if (proxyUser != NULL)
182 xmlFree(proxyUser);
183 if (proxyPasswd != NULL)
184 xmlFree(proxyPasswd);
185 if (host)
186 proxy = xmlMemStrdup(host);
187 if (user)
188 proxyUser = xmlMemStrdup(user);
189 if (passwd)
190 proxyPasswd = xmlMemStrdup(passwd);
191 proxyPort = port;
192 proxyType = type;
193}
194
Daniel Veillardda07c342000-01-25 18:31:22 +0000195/**
196 * xmlNanoFTPScanURL:
197 * @ctx: an FTP context
198 * @URL: The URL used to initialize the context
199 *
200 * (Re)Initialize an FTP context by parsing the URL and finding
201 * the protocol host port and path it indicates.
202 */
203
204static void
205xmlNanoFTPScanURL(void *ctx, const char *URL) {
206 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
207 const char *cur = URL;
208 char buf[4096];
209 int index = 0;
210 int port = 0;
211
212 if (ctxt->protocol != NULL) {
213 xmlFree(ctxt->protocol);
214 ctxt->protocol = NULL;
215 }
216 if (ctxt->hostname != NULL) {
217 xmlFree(ctxt->hostname);
218 ctxt->hostname = NULL;
219 }
220 if (ctxt->path != NULL) {
221 xmlFree(ctxt->path);
222 ctxt->path = NULL;
223 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000224 if (URL == NULL) return;
Daniel Veillardda07c342000-01-25 18:31:22 +0000225 buf[index] = 0;
226 while (*cur != 0) {
227 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
228 buf[index] = 0;
229 ctxt->protocol = xmlMemStrdup(buf);
230 index = 0;
231 cur += 3;
232 break;
233 }
234 buf[index++] = *cur++;
235 }
236 if (*cur == 0) return;
237
238 buf[index] = 0;
239 while (1) {
240 if (cur[0] == ':') {
241 buf[index] = 0;
242 ctxt->hostname = xmlMemStrdup(buf);
243 index = 0;
244 cur += 1;
245 while ((*cur >= '0') && (*cur <= '9')) {
246 port *= 10;
247 port += *cur - '0';
248 cur++;
249 }
250 if (port != 0) ctxt->port = port;
251 while ((cur[0] != '/') && (*cur != 0))
252 cur++;
253 break;
254 }
255 if ((*cur == '/') || (*cur == 0)) {
256 buf[index] = 0;
257 ctxt->hostname = xmlMemStrdup(buf);
258 index = 0;
259 break;
260 }
261 buf[index++] = *cur++;
262 }
263 if (*cur == 0)
264 ctxt->path = xmlMemStrdup("/");
265 else {
266 buf[index] = 0;
267 while (*cur != 0) {
268 if ((cur[0] == '#') || (cur[0] == '?'))
269 break;
270 buf[index++] = *cur++;
271 }
272 buf[index] = 0;
273 ctxt->path = xmlMemStrdup(buf);
274 }
275}
276
277/**
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000278 * xmlNanoFTPUpdateURL:
279 * @ctx: an FTP context
280 * @URL: The URL used to update the context
281 *
282 * Update an FTP context by parsing the URL and finding
283 * new path it indicates. If there is an error in the
284 * protocol, hostname, port or other information, the
285 * error is raised. It indicates a new connection has to
286 * be established.
287 *
288 * Returns 0 if Ok, -1 in case of error (other host).
289 */
290
291int
292xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
293 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
294 const char *cur = URL;
295 char buf[4096];
296 int index = 0;
297 int port = 0;
298
299 if (URL == NULL)
300 return(-1);
301 if (ctxt == NULL)
302 return(-1);
303 if (ctxt->protocol == NULL)
304 return(-1);
305 if (ctxt->hostname == NULL)
306 return(-1);
307 buf[index] = 0;
308 while (*cur != 0) {
309 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
310 buf[index] = 0;
311 if (strcmp(ctxt->protocol, buf))
312 return(-1);
313 index = 0;
314 cur += 3;
315 break;
316 }
317 buf[index++] = *cur++;
318 }
319 if (*cur == 0)
320 return(-1);
321
322 buf[index] = 0;
323 while (1) {
324 if (cur[0] == ':') {
325 buf[index] = 0;
326 if (strcmp(ctxt->hostname, buf))
327 return(-1);
328 index = 0;
329 cur += 1;
330 while ((*cur >= '0') && (*cur <= '9')) {
331 port *= 10;
332 port += *cur - '0';
333 cur++;
334 }
335 if (port != ctxt->port)
336 return(-1);
337 while ((cur[0] != '/') && (*cur != 0))
338 cur++;
339 break;
340 }
341 if ((*cur == '/') || (*cur == 0)) {
342 buf[index] = 0;
343 if (strcmp(ctxt->hostname, buf))
344 return(-1);
345 index = 0;
346 break;
347 }
348 buf[index++] = *cur++;
349 }
350 if (ctxt->path != NULL) {
351 xmlFree(ctxt->path);
352 ctxt->path = NULL;
353 }
354
355 if (*cur == 0)
356 ctxt->path = xmlMemStrdup("/");
357 else {
358 buf[index] = 0;
359 while (*cur != 0) {
360 if ((cur[0] == '#') || (cur[0] == '?'))
361 break;
362 buf[index++] = *cur++;
363 }
364 buf[index] = 0;
365 ctxt->path = xmlMemStrdup(buf);
366 }
367 return(0);
368}
369
370/**
371 * xmlNanoFTPScanProxy:
372 * @URL: The proxy URL used to initialize the proxy context
373 *
374 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
375 * the protocol host port it indicates.
376 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
377 * A NULL URL cleans up proxy informations.
378 */
379
380void
381xmlNanoFTPScanProxy(const char *URL) {
382 const char *cur = URL;
383 char buf[4096];
384 int index = 0;
385 int port = 0;
386
387 if (proxy != NULL) {
388 xmlFree(proxy);
389 proxy = NULL;
390 }
391 if (proxyPort != 0) {
392 proxyPort = 0;
393 }
394#ifdef DEBUG_FTP
395 if (URL == NULL)
396 printf("Removing FTP proxy info\n");
397 else
398 printf("Using FTP proxy %s\n", URL);
399#endif
400 if (URL == NULL) return;
401 buf[index] = 0;
402 while (*cur != 0) {
403 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
404 buf[index] = 0;
405 index = 0;
406 cur += 3;
407 break;
408 }
409 buf[index++] = *cur++;
410 }
411 if (*cur == 0) return;
412
413 buf[index] = 0;
414 while (1) {
415 if (cur[0] == ':') {
416 buf[index] = 0;
417 proxy = xmlMemStrdup(buf);
418 index = 0;
419 cur += 1;
420 while ((*cur >= '0') && (*cur <= '9')) {
421 port *= 10;
422 port += *cur - '0';
423 cur++;
424 }
425 if (port != 0) proxyPort = port;
426 while ((cur[0] != '/') && (*cur != 0))
427 cur++;
428 break;
429 }
430 if ((*cur == '/') || (*cur == 0)) {
431 buf[index] = 0;
432 proxy = xmlMemStrdup(buf);
433 index = 0;
434 break;
435 }
436 buf[index++] = *cur++;
437 }
438}
439
440/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000441 * xmlNanoFTPNewCtxt:
442 * @URL: The URL used to initialize the context
443 *
444 * Allocate and initialize a new FTP context.
445 *
446 * Returns an FTP context or NULL in case of error.
447 */
448
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000449void *
Daniel Veillardda07c342000-01-25 18:31:22 +0000450xmlNanoFTPNewCtxt(const char *URL) {
451 xmlNanoFTPCtxtPtr ret;
452
453 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
454 if (ret == NULL) return(NULL);
455
456 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
457 ret->port = 21;
458 ret->passive = 1;
459 ret->returnValue = 0;
460
461 if (URL != NULL)
462 xmlNanoFTPScanURL(ret, URL);
463
464 return(ret);
465}
466
467/**
468 * xmlNanoFTPFreeCtxt:
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000469 * @ctx: an FTP context
Daniel Veillardda07c342000-01-25 18:31:22 +0000470 *
471 * Frees the context after closing the connection.
472 */
473
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000474void
475xmlNanoFTPFreeCtxt(void * ctx) {
476 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
Daniel Veillardda07c342000-01-25 18:31:22 +0000477 if (ctxt == NULL) return;
478 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
479 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
480 if (ctxt->path != NULL) xmlFree(ctxt->path);
481 ctxt->passive = 1;
482 if (ctxt->controlFd >= 0) close(ctxt->controlFd);
483 ctxt->controlFd = -1;
484 xmlFree(ctxt);
485}
486
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000487/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000488 * Parsing of the server answer, we just extract the code.
489 * return 0 for errors
490 * +XXX for last line of response
491 * -XXX for response to be continued
492 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000493static int
Daniel Veillardda07c342000-01-25 18:31:22 +0000494xmlNanoFTPParseResponse(void *ctx, char *buf, int len) {
495 int val = 0;
496
497 if (len < 3) return(-1);
498 if ((*buf >= '0') && (*buf <= '9'))
499 val = val * 10 + (*buf - '0');
500 else
501 return(0);
502 buf++;
503 if ((*buf >= '0') && (*buf <= '9'))
504 val = val * 10 + (*buf - '0');
505 else
506 return(0);
507 buf++;
508 if ((*buf >= '0') && (*buf <= '9'))
509 val = val * 10 + (*buf - '0');
510 else
511 return(0);
512 buf++;
513 if (*buf == '-')
514 return(-val);
515 return(val);
516}
517
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000518/**
519 * xmlNanoFTPReadResponse:
520 * @ctx: an FTP context
521 * @buf: buffer to read in
522 * @size: buffer length
523 *
Daniel Veillardda07c342000-01-25 18:31:22 +0000524 * Read the response from the FTP server after a command.
525 * Returns the code number
Daniel Veillardda07c342000-01-25 18:31:22 +0000526 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000527static int
Daniel Veillardda07c342000-01-25 18:31:22 +0000528xmlNanoFTPReadResponse(void *ctx, char *buf, int size) {
529 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
530 char *ptr, *end;
531 int len;
532 int res = -1;
533
534 if (size <= 0) return(-1);
535
536get_more:
537 if ((len = recv(ctxt->controlFd, buf, size - 1, 0)) < 0) {
538 close(ctxt->controlFd); ctxt->controlFd = -1;
539 ctxt->controlFd = -1;
540 return(-1);
541 }
542 if (len == 0) {
543 return(-1);
544 }
545
546 end = &buf[len];
547 *end = 0;
548#ifdef DEBUG_FTP
549 printf(buf);
550#endif
551 ptr = buf;
552 while (ptr < end) {
553 res = xmlNanoFTPParseResponse(ctxt, ptr, end - ptr);
554 if (res > 0) break;
555 if (res == 0) {
556#ifdef DEBUG_FTP
557 fprintf(stderr, "xmlNanoFTPReadResponse failed: %s\n", ptr);
558#endif
559 return(-1);
560 }
561 while ((ptr < end) && (*ptr != '\n')) ptr++;
562 if (ptr >= end) {
563#ifdef DEBUG_FTP
564 fprintf(stderr, "xmlNanoFTPReadResponse: unexpected end %s\n", buf);
565#endif
566 return((-res) / 100);
567 }
568 if (*ptr != '\r') ptr++;
569 }
570
571 if (res < 0) goto get_more;
572
573#ifdef DEBUG_FTP
574 printf("Got %d\n", res);
575#endif
576 return(res / 100);
577}
578
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000579/**
580 * xmlNanoFTPGetResponse:
581 * @ctx: an FTP context
582 *
Daniel Veillardda07c342000-01-25 18:31:22 +0000583 * Get the response from the FTP server after a command.
584 * Returns the code number
Daniel Veillardda07c342000-01-25 18:31:22 +0000585 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000586
Daniel Veillardda07c342000-01-25 18:31:22 +0000587int
588xmlNanoFTPGetResponse(void *ctx) {
589 char buf[16 * 1024 + 1];
590
591/**************
592 fd_set rfd;
593 struct timeval tv;
594 int res;
595
596 tv.tv_sec = 10;
597 tv.tv_usec = 0;
598 FD_ZERO(&rfd);
599 FD_SET(ctxt->controlFd, &rfd);
600 res = select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv);
601 if (res <= 0) return(res);
602 **************/
603
604 return(xmlNanoFTPReadResponse(ctx, buf, 16 * 1024));
605}
606
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000607/**
608 * xmlNanoFTPCheckResponse:
609 * @ctx: an FTP context
610 *
Daniel Veillardda07c342000-01-25 18:31:22 +0000611 * Check if there is a response from the FTP server after a command.
612 * Returns the code number, or 0
613 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000614
Daniel Veillardda07c342000-01-25 18:31:22 +0000615int
616xmlNanoFTPCheckResponse(void *ctx) {
617 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
618 char buf[1024 + 1];
619 fd_set rfd;
620 struct timeval tv;
621
622 tv.tv_sec = 0;
623 tv.tv_usec = 0;
624 FD_ZERO(&rfd);
625 FD_SET(ctxt->controlFd, &rfd);
626 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
627 case 0:
628 return(0);
629 case -1:
630#ifdef DEBUG_FTP
631 perror("select");
632#endif
633 return(-1);
634
635 }
636
637 return(xmlNanoFTPReadResponse(ctx, buf, 1024));
638}
639
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000640/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000641 * Send the user authentification
642 */
643
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000644static int
645xmlNanoFTPSendUser(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000646 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
647 char buf[200];
648 int len;
649 int res;
650
651 if (ctxt->user == NULL)
652 len = snprintf(buf, sizeof(buf), "USER anonymous\r\n");
653 else
654 len = snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
655#ifdef DEBUG_FTP
656 printf(buf);
657#endif
658 res = send(ctxt->controlFd, buf, len, 0);
659 if (res < 0) return(res);
660 return(0);
661}
662
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000663/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000664 * Send the password authentification
665 */
666
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000667static int
668xmlNanoFTPSendPasswd(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000669 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
670 char buf[200];
671 int len;
672 int res;
673
674 if (ctxt->passwd == NULL)
675 len = snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
676 else
677 len = snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
678#ifdef DEBUG_FTP
679 printf(buf);
680#endif
681 res = send(ctxt->controlFd, buf, len, 0);
682 if (res < 0) return(res);
683 return(0);
684}
685
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000686/**
687 * xmlNanoFTPQuit:
688 * @ctx: an FTP context
689 *
690 * Send a QUIT command to the server
691 *
692 * Returns -1 in case of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +0000693 */
694
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000695
Daniel Veillardda07c342000-01-25 18:31:22 +0000696int
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000697xmlNanoFTPQuit(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000698 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
699 char buf[200];
700 int len;
701 int res;
702
703 len = snprintf(buf, sizeof(buf), "QUIT\r\n");
704#ifdef DEBUG_FTP
705 printf(buf);
706#endif
707 res = send(ctxt->controlFd, buf, len, 0);
708 return(0);
709}
710
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000711/**
712 * xmlNanoFTPConnect:
713 * @ctx: an FTP context
714 *
715 * Tries to open a control connection
716 *
717 * Returns -1 in case of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +0000718 */
719
720int
721xmlNanoFTPConnect(void *ctx) {
722 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
723 struct hostent *hp;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000724 int port;
Daniel Veillardda07c342000-01-25 18:31:22 +0000725 int res;
726
727 if (ctxt == NULL)
728 return(-1);
729 if (ctxt->hostname == NULL)
730 return(-1);
731
732 /*
733 * do the blocking DNS query.
734 */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000735 if (proxy)
736 hp = gethostbyname(proxy);
737 else
738 hp = gethostbyname(ctxt->hostname);
Daniel Veillardda07c342000-01-25 18:31:22 +0000739 if (hp == NULL)
740 return(-1);
741
742 /*
743 * Prepare the socket
744 */
745 memset(&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
746 ctxt->ftpAddr.sin_family = AF_INET;
747 memcpy(&ctxt->ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000748 if (proxy) {
749 port = proxyPort;
750 } else {
751 port = ctxt->port;
752 }
753 if (port == 0)
754 port = 21;
755 ctxt->ftpAddr.sin_port = htons(port);
Daniel Veillardda07c342000-01-25 18:31:22 +0000756 ctxt->controlFd = socket(AF_INET, SOCK_STREAM, 0);
757 if (ctxt->controlFd < 0)
758 return(-1);
759
760 /*
761 * Do the connect.
762 */
763 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
764 sizeof(struct sockaddr_in)) < 0) {
765 close(ctxt->controlFd); ctxt->controlFd = -1;
766 ctxt->controlFd = -1;
767 return(-1);
768 }
769
770 /*
771 * Wait for the HELLO from the server.
772 */
773 res = xmlNanoFTPGetResponse(ctxt);
774 if (res != 2) {
775 close(ctxt->controlFd); ctxt->controlFd = -1;
776 ctxt->controlFd = -1;
777 return(-1);
778 }
779
780 /*
781 * State diagram for the login operation on the FTP server
782 *
783 * Reference: RFC 959
784 *
785 * 1
786 * +---+ USER +---+------------->+---+
787 * | B |---------->| W | 2 ---->| E |
788 * +---+ +---+------ | -->+---+
789 * | | | | |
790 * 3 | | 4,5 | | |
791 * -------------- ----- | | |
792 * | | | | |
793 * | | | | |
794 * | --------- |
795 * | 1| | | |
796 * V | | | |
797 * +---+ PASS +---+ 2 | ------>+---+
798 * | |---------->| W |------------->| S |
799 * +---+ +---+ ---------->+---+
800 * | | | | |
801 * 3 | |4,5| | |
802 * -------------- -------- |
803 * | | | | |
804 * | | | | |
805 * | -----------
806 * | 1,3| | | |
807 * V | 2| | |
808 * +---+ ACCT +---+-- | ----->+---+
809 * | |---------->| W | 4,5 -------->| F |
810 * +---+ +---+------------->+---+
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000811 *
812 * Of course in case of using a proxy this get really nasty and is not
813 * standardized at all :-(
814 */
815 if (proxy) {
816 int len;
817 char buf[400];
818
819 if (proxyUser != NULL) {
820 /*
821 * We need proxy auth
822 */
823 len = snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
824#ifdef DEBUG_FTP
825 printf(buf);
826#endif
827 res = send(ctxt->controlFd, buf, len, 0);
828 if (res < 0) {
829 close(ctxt->controlFd);
830 ctxt->controlFd = -1;
831 return(res);
832 }
833 res = xmlNanoFTPGetResponse(ctxt);
834 switch (res) {
835 case 2:
836 if (proxyPasswd == NULL)
837 break;
838 case 3:
839 if (proxyPasswd != NULL)
840 len = snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
841 else
842 len = snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n",
843 hostname);
844#ifdef DEBUG_FTP
845 printf(buf);
846#endif
847 res = send(ctxt->controlFd, buf, len, 0);
848 if (res < 0) {
849 close(ctxt->controlFd);
850 ctxt->controlFd = -1;
851 return(res);
852 }
853 res = xmlNanoFTPGetResponse(ctxt);
854 if (res > 3) {
855 close(ctxt->controlFd);
856 ctxt->controlFd = -1;
857 return(-1);
858 }
859 break;
860 case 1:
861 break;
862 case 4:
863 case 5:
864 case -1:
865 default:
866 close(ctxt->controlFd);
867 ctxt->controlFd = -1;
868 return(-1);
869 }
870 }
871
872 /*
873 * We assume we don't need more authentication to the proxy
874 * and that it succeeded :-\
875 */
876 switch (proxyType) {
877 case 0:
878 /* we will try in seqence */
879 case 1:
880 /* Using SITE command */
881 len = snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
882#ifdef DEBUG_FTP
883 printf(buf);
884#endif
885 res = send(ctxt->controlFd, buf, len, 0);
886 if (res < 0) {
887 close(ctxt->controlFd); ctxt->controlFd = -1;
888 ctxt->controlFd = -1;
889 return(res);
890 }
891 res = xmlNanoFTPGetResponse(ctxt);
892 if (res == 2) {
893 /* we assume it worked :-\ 1 is error for SITE command */
894 proxyType = 1;
895 break;
896 }
897 if (proxyType == 1) {
898 close(ctxt->controlFd); ctxt->controlFd = -1;
899 ctxt->controlFd = -1;
900 return(-1);
901 }
902 case 2:
903 /* USER user@host command */
904 if (ctxt->user == NULL)
905 len = snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
906 ctxt->hostname);
907 else
908 len = snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
909 ctxt->user, ctxt->hostname);
910#ifdef DEBUG_FTP
911 printf(buf);
912#endif
913 res = send(ctxt->controlFd, buf, len, 0);
914 if (res < 0) {
915 close(ctxt->controlFd); ctxt->controlFd = -1;
916 ctxt->controlFd = -1;
917 return(res);
918 }
919 res = xmlNanoFTPGetResponse(ctxt);
920 if ((res == 1) || (res == 2)) {
921 /* we assume it worked :-\ */
922 proxyType = 2;
923 return(0);
924 }
925 if (ctxt->passwd == NULL)
926 len = snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
927 else
928 len = snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
929#ifdef DEBUG_FTP
930 printf(buf);
931#endif
932 res = send(ctxt->controlFd, buf, len, 0);
933 if (res < 0) {
934 close(ctxt->controlFd); ctxt->controlFd = -1;
935 ctxt->controlFd = -1;
936 return(res);
937 }
938 res = xmlNanoFTPGetResponse(ctxt);
939 if ((res == 1) || (res == 2)) {
940 /* we assume it worked :-\ */
941 proxyType = 2;
942 return(0);
943 }
944 if (proxyType == 2) {
945 close(ctxt->controlFd); ctxt->controlFd = -1;
946 ctxt->controlFd = -1;
947 return(-1);
948 }
949 case 3:
950 /*
951 * If you need support for other Proxy authentication scheme
952 * send the code or at least the sequence in use.
953 */
954 default:
955 close(ctxt->controlFd); ctxt->controlFd = -1;
956 ctxt->controlFd = -1;
957 return(-1);
958 }
959 }
960 /*
961 * Non-proxy handling.
Daniel Veillardda07c342000-01-25 18:31:22 +0000962 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000963 res = xmlNanoFTPSendUser(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +0000964 if (res < 0) {
965 close(ctxt->controlFd); ctxt->controlFd = -1;
966 ctxt->controlFd = -1;
967 return(-1);
968 }
969 res = xmlNanoFTPGetResponse(ctxt);
970 switch (res) {
971 case 2:
972 return(0);
973 case 3:
974 break;
975 case 1:
976 case 4:
977 case 5:
978 case -1:
979 default:
980 close(ctxt->controlFd); ctxt->controlFd = -1;
981 ctxt->controlFd = -1;
982 return(-1);
983 }
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000984 res = xmlNanoFTPSendPasswd(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +0000985 if (res < 0) {
986 close(ctxt->controlFd); ctxt->controlFd = -1;
987 ctxt->controlFd = -1;
988 return(-1);
989 }
990 res = xmlNanoFTPGetResponse(ctxt);
991 switch (res) {
992 case 2:
993 return(0);
994 case 3:
995 fprintf(stderr, "FTP server asking for ACCNT on anonymous\n");
996 case 1:
997 case 4:
998 case 5:
999 case -1:
1000 default:
1001 close(ctxt->controlFd); ctxt->controlFd = -1;
1002 ctxt->controlFd = -1;
1003 return(-1);
1004 }
1005
1006 return(0);
1007}
1008
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001009/**
1010 * xmlNanoFTPConnectTo:
1011 * @server: an FTP server name
1012 * @directory: the port (use 21 if 0)
1013 *
1014 * Tries to open a control connection to the given server/port
1015 *
1016 * Returns and fTP context or NULL if it failed
Daniel Veillardda07c342000-01-25 18:31:22 +00001017 */
1018
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001019
Daniel Veillardda07c342000-01-25 18:31:22 +00001020void *
1021xmlNanoFTPConnectTo(const char *server, int port) {
1022 xmlNanoFTPCtxtPtr ctxt;
1023 int res;
1024
1025 xmlNanoFTPInit();
1026 if (server == NULL)
1027 return(NULL);
1028 ctxt = xmlNanoFTPNewCtxt(NULL);
1029 ctxt->hostname = xmlMemStrdup(server);
1030 if (port != 0)
1031 ctxt->port = port;
1032 res = xmlNanoFTPConnect(ctxt);
1033 if (res < 0) {
1034 xmlNanoFTPFreeCtxt(ctxt);
1035 return(NULL);
1036 }
1037 return(ctxt);
1038}
1039
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001040/**
1041 * xmlNanoFTPGetConnection:
1042 * @ctx: an FTP context
1043 * @directory: a directory on the server
1044 *
1045 * Tries to change the remote directory
1046 *
1047 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
Daniel Veillardda07c342000-01-25 18:31:22 +00001048 */
1049
1050int
1051xmlNanoFTPCwd(void *ctx, char *directory) {
1052 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1053 char buf[400];
1054 int len;
1055 int res;
1056
1057 /*
1058 * Expected response code for CWD:
1059 *
1060 * CWD
1061 * 250
1062 * 500, 501, 502, 421, 530, 550
1063 */
1064 len = snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
1065#ifdef DEBUG_FTP
1066 printf(buf);
1067#endif
1068 res = send(ctxt->controlFd, buf, len, 0);
1069 if (res < 0) return(res);
1070 res = xmlNanoFTPGetResponse(ctxt);
1071 if (res == 4) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001072 return(-1);
1073 }
1074 if (res == 2) return(1);
1075 if (res == 5) {
1076 return(0);
1077 }
1078 return(0);
1079}
1080
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001081/**
1082 * xmlNanoFTPGetConnection:
1083 * @ctx: an FTP context
1084 *
1085 * Try to open a data connection to the server. Currently only
1086 * passive mode is supported.
1087 *
1088 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001089 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001090
Daniel Veillardda07c342000-01-25 18:31:22 +00001091int
1092xmlNanoFTPGetConnection(void *ctx) {
1093 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1094 char buf[200], *cur;
1095 int len, i;
1096 int res;
1097 unsigned char ad[6], *adp, *portp;
1098 unsigned int temp[6];
1099 struct sockaddr_in dataAddr;
1100 size_t dataAddrLen;
1101
1102 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1103 if (ctxt->dataFd < 0) {
1104 fprintf(stderr, "xmlNanoFTPGetConnection: failed to create socket\n");
1105 }
1106 dataAddrLen = sizeof(dataAddr);
1107 memset(&dataAddr, 0, dataAddrLen);
1108 dataAddr.sin_family = AF_INET;
1109
1110 if (ctxt->passive) {
1111 len = snprintf(buf, sizeof(buf), "PASV\r\n");
1112#ifdef DEBUG_FTP
1113 printf(buf);
1114#endif
1115 res = send(ctxt->controlFd, buf, len, 0);
1116 if (res < 0) {
1117 close(ctxt->dataFd); ctxt->dataFd = -1;
1118 return(res);
1119 }
1120 res = xmlNanoFTPReadResponse(ctx, buf, sizeof(buf) -1);
1121 if (res != 2) {
1122 if (res == 5) {
1123 close(ctxt->dataFd); ctxt->dataFd = -1;
1124 return(-1);
1125 } else {
1126 /*
1127 * retry with an active connection
1128 */
1129 close(ctxt->dataFd); ctxt->dataFd = -1;
1130 ctxt->passive = 0;
1131 }
1132 }
1133 cur = &buf[4];
1134 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1135 if (sscanf(cur, "%d,%d,%d,%d,%d,%d", &temp[0], &temp[1], &temp[2],
1136 &temp[3], &temp[4], &temp[5]) != 6) {
1137 fprintf(stderr, "Invalid answer to PASV\n");
1138 close(ctxt->dataFd); ctxt->dataFd = -1;
1139 return(-1);
1140 }
1141 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1142 memcpy(&dataAddr.sin_addr, &ad[0], 4);
1143 memcpy(&dataAddr.sin_port, &ad[4], 2);
1144 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1145 fprintf(stderr, "Failed to create a data connection\n");
1146 close(ctxt->dataFd); ctxt->dataFd = -1;
1147 return (-1);
1148 }
1149 } else {
1150 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1151 dataAddr.sin_port = 0;
1152 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1153 fprintf(stderr, "Failed to bind a port\n");
1154 close(ctxt->dataFd); ctxt->dataFd = -1;
1155 return (-1);
1156 }
1157 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1158
1159 if (listen(ctxt->dataFd, 1) < 0) {
1160 fprintf(stderr, "Could not listen on port %d\n",
1161 ntohs(dataAddr.sin_port));
1162 close(ctxt->dataFd); ctxt->dataFd = -1;
1163 return (-1);
1164 }
1165 adp = (unsigned char *) &dataAddr.sin_addr;
1166 portp = (unsigned char *) &dataAddr.sin_port;
1167 len = snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1168 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1169 portp[0] & 0xff, portp[1] & 0xff);
1170 buf[sizeof(buf) - 1] = 0;
1171#ifdef DEBUG_FTP
1172 printf(buf);
1173#endif
1174
1175 res = send(ctxt->controlFd, buf, len, 0);
1176 if (res < 0) {
1177 close(ctxt->dataFd); ctxt->dataFd = -1;
1178 return(res);
1179 }
1180 res = xmlNanoFTPGetResponse(ctxt);
1181 if (res != 2) {
1182 close(ctxt->dataFd); ctxt->dataFd = -1;
1183 return(-1);
1184 }
1185 }
1186 return(ctxt->dataFd);
1187
1188}
1189
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001190/**
1191 * xmlNanoFTPCloseConnection:
1192 * @ctx: an FTP context
1193 *
1194 * Close the data connection from the server
1195 *
1196 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001197 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001198
Daniel Veillardda07c342000-01-25 18:31:22 +00001199int
1200xmlNanoFTPCloseConnection(void *ctx) {
1201 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1202 int res;
1203
1204 close(ctxt->dataFd); ctxt->dataFd = -1;
1205 res = xmlNanoFTPGetResponse(ctxt);
1206 if (res != 2) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001207 close(ctxt->controlFd); ctxt->controlFd = -1;
1208 return(-1);
1209 }
1210 return(0);
1211}
1212
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001213/**
1214 * xmlNanoFTPParseList:
1215 * @list: some data listing received from the server
1216 * @callback: the user callback
1217 * @userData: the user callback data
1218 *
1219 * Parse at most one entry from the listing.
1220 *
1221 * Returns -1 incase of error, the lenght of data parsed otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001222 */
1223
1224static int
1225xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1226 const char *cur = list;
1227 char filename[151];
1228 char attrib[11];
1229 char owner[11];
1230 char group[11];
1231 char month[4];
1232 int year = 0;
1233 int minute = 0;
1234 int hour = 0;
1235 int day = 0;
1236 unsigned long size = 0;
1237 int links = 0;
1238 int i;
1239
1240 if (!strncmp(cur, "total", 5)) {
1241 cur += 5;
1242 while (*cur == ' ') cur++;
1243 while ((*cur >= '0') && (*cur <= '9'))
1244 links = (links * 10) + (*cur++ - '0');
1245 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1246 cur++;
1247 return(cur - list);
1248 } else if (*list == '+') {
1249 return(0);
1250 } else {
1251 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1252 cur++;
1253 if (*cur == 0) return(0);
1254 i = 0;
1255 while (*cur != ' ') {
1256 if (i < 10)
1257 attrib[i++] = *cur;
1258 cur++;
1259 if (*cur == 0) return(0);
1260 }
1261 attrib[10] = 0;
1262 while (*cur == ' ') cur++;
1263 if (*cur == 0) return(0);
1264 while ((*cur >= '0') && (*cur <= '9'))
1265 links = (links * 10) + (*cur++ - '0');
1266 while (*cur == ' ') cur++;
1267 if (*cur == 0) return(0);
1268 i = 0;
1269 while (*cur != ' ') {
1270 if (i < 10)
1271 owner[i++] = *cur;
1272 cur++;
1273 if (*cur == 0) return(0);
1274 }
1275 owner[i] = 0;
1276 while (*cur == ' ') cur++;
1277 if (*cur == 0) return(0);
1278 i = 0;
1279 while (*cur != ' ') {
1280 if (i < 10)
1281 group[i++] = *cur;
1282 cur++;
1283 if (*cur == 0) return(0);
1284 }
1285 group[i] = 0;
1286 while (*cur == ' ') cur++;
1287 if (*cur == 0) return(0);
1288 while ((*cur >= '0') && (*cur <= '9'))
1289 size = (size * 10) + (*cur++ - '0');
1290 while (*cur == ' ') cur++;
1291 if (*cur == 0) return(0);
1292 i = 0;
1293 while (*cur != ' ') {
1294 if (i < 3)
1295 month[i++] = *cur;
1296 cur++;
1297 if (*cur == 0) return(0);
1298 }
1299 month[i] = 0;
1300 while (*cur == ' ') cur++;
1301 if (*cur == 0) return(0);
1302 while ((*cur >= '0') && (*cur <= '9'))
1303 day = (day * 10) + (*cur++ - '0');
1304 while (*cur == ' ') cur++;
1305 if (*cur == 0) return(0);
1306 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1307 if ((cur[1] == ':') || (cur[2] == ':')) {
1308 while ((*cur >= '0') && (*cur <= '9'))
1309 hour = (hour * 10) + (*cur++ - '0');
1310 if (*cur == ':') cur++;
1311 while ((*cur >= '0') && (*cur <= '9'))
1312 minute = (minute * 10) + (*cur++ - '0');
1313 } else {
1314 while ((*cur >= '0') && (*cur <= '9'))
1315 year = (year * 10) + (*cur++ - '0');
1316 }
1317 while (*cur == ' ') cur++;
1318 if (*cur == 0) return(0);
1319 i = 0;
1320 while ((*cur != '\n') && (*cur != '\r')) {
1321 if (i < 150)
1322 filename[i++] = *cur;
1323 cur++;
1324 if (*cur == 0) return(0);
1325 }
1326 filename[i] = 0;
1327 if ((*cur != '\n') && (*cur != '\r'))
1328 return(0);
1329 while ((*cur == '\n') || (*cur == '\r'))
1330 cur++;
1331 }
1332 if (callback != NULL) {
1333 callback(userData, filename, attrib, owner, group, size, links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001334 year, month, day, hour, minute);
Daniel Veillardda07c342000-01-25 18:31:22 +00001335 }
1336 return(cur - list);
1337}
1338
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001339/**
1340 * xmlNanoFTPList:
1341 * @ctx: an FTP context
1342 * @callback: the user callback
1343 * @userData: the user callback data
1344 * @filename: optional files to list
1345 *
1346 * Do a listing on the server. All files info are passed back
1347 * in the callbacks.
1348 *
1349 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001350 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001351
Daniel Veillardda07c342000-01-25 18:31:22 +00001352int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001353xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1354 char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001355 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1356 char buf[4096 + 1];
1357 int len, res;
1358 int index = 0, base;
1359 fd_set rfd, efd;
1360 struct timeval tv;
1361
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001362 if (filename == NULL) {
1363 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1364 return(-1);
1365 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001366 len = snprintf(buf, sizeof(buf), "LIST -L\r\n");
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001367 } else {
1368 if (filename[0] != '/') {
1369 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1370 return(-1);
1371 }
1372 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1373 len = snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
1374 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001375#ifdef DEBUG_FTP
1376 printf(buf);
1377#endif
1378 res = send(ctxt->controlFd, buf, len, 0);
1379 if (res < 0) {
1380 close(ctxt->dataFd); ctxt->dataFd = -1;
1381 return(res);
1382 }
1383 res = xmlNanoFTPReadResponse(ctxt, buf, sizeof(buf) -1);
1384 if (res != 1) {
1385 close(ctxt->dataFd); ctxt->dataFd = -1;
1386 return(-res);
1387 }
1388
1389 do {
1390 tv.tv_sec = 1;
1391 tv.tv_usec = 0;
1392 FD_ZERO(&rfd);
1393 FD_SET(ctxt->dataFd, &rfd);
1394 FD_ZERO(&efd);
1395 FD_SET(ctxt->dataFd, &efd);
1396 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1397 if (res < 0) {
1398#ifdef DEBUG_FTP
1399 perror("select");
1400#endif
1401 close(ctxt->dataFd); ctxt->dataFd = -1;
1402 return(-1);
1403 }
1404 if (res == 0) {
1405 res = xmlNanoFTPCheckResponse(ctxt);
1406 if (res < 0) {
1407 close(ctxt->dataFd); ctxt->dataFd = -1;
1408 ctxt->dataFd = -1;
1409 return(-1);
1410 }
1411 if (res == 2) {
1412 close(ctxt->dataFd); ctxt->dataFd = -1;
1413 return(0);
1414 }
1415
1416 continue;
1417 }
1418
1419 if ((len = read(ctxt->dataFd, &buf[index], sizeof(buf) - (index + 1))) < 0) {
1420#ifdef DEBUG_FTP
1421 perror("read");
1422#endif
1423 close(ctxt->dataFd); ctxt->dataFd = -1;
1424 ctxt->dataFd = -1;
1425 return(-1);
1426 }
1427#ifdef DEBUG_FTP
1428 write(1, &buf[index], len);
1429#endif
1430 index += len;
1431 buf[index] = 0;
1432 base = 0;
1433 do {
1434 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1435 base += res;
1436 } while (res > 0);
1437
1438 memmove(&buf[0], &buf[base], index - base);
1439 index -= base;
1440 } while (len != 0);
1441 xmlNanoFTPCloseConnection(ctxt);
1442 return(0);
1443}
1444
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001445/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001446 * xmlNanoFTPGetSocket:
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001447 * @ctx: an FTP context
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001448 * @filename: the file to retrieve (or NULL if path is in context).
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001449 *
1450 * Initiate fetch of the given file from the server.
1451 *
1452 * Returns the socket for the data connection, or <0 in case of error
Daniel Veillardda07c342000-01-25 18:31:22 +00001453 */
1454
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001455
Daniel Veillardda07c342000-01-25 18:31:22 +00001456int
1457xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1458 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1459 char buf[300];
1460 int res, len;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001461 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001462 return(-1);
1463 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1464
1465 len = snprintf(buf, sizeof(buf), "TYPE I\r\n");
1466#ifdef DEBUG_FTP
1467 printf(buf);
1468#endif
1469 res = send(ctxt->controlFd, buf, len, 0);
1470 if (res < 0) {
1471 close(ctxt->dataFd); ctxt->dataFd = -1;
1472 return(res);
1473 }
1474 res = xmlNanoFTPReadResponse(ctxt, buf, sizeof(buf) -1);
1475 if (res != 2) {
1476 close(ctxt->dataFd); ctxt->dataFd = -1;
1477 return(-res);
1478 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001479 if (filename == NULL)
1480 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
1481 else
1482 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Daniel Veillardda07c342000-01-25 18:31:22 +00001483#ifdef DEBUG_FTP
1484 printf(buf);
1485#endif
1486 res = send(ctxt->controlFd, buf, len, 0);
1487 if (res < 0) {
1488 close(ctxt->dataFd); ctxt->dataFd = -1;
1489 return(res);
1490 }
1491 res = xmlNanoFTPReadResponse(ctxt, buf, sizeof(buf) -1);
1492 if (res != 1) {
1493 close(ctxt->dataFd); ctxt->dataFd = -1;
1494 return(-res);
1495 }
1496 return(ctxt->dataFd);
1497}
1498
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001499/**
1500 * xmlNanoFTPGet:
1501 * @ctx: an FTP context
1502 * @callback: the user callback
1503 * @userData: the user callback data
1504 * @filename: the file to retrieve
1505 *
1506 * Fetch the given file from the server. All data are passed back
1507 * in the callbacks. The last callback has a size of 0 block.
1508 *
1509 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001510 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001511
Daniel Veillardda07c342000-01-25 18:31:22 +00001512int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001513xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1514 const char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001515 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1516 char buf[4096];
1517 int len = 0, res;
1518 fd_set rfd;
1519 struct timeval tv;
1520
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001521 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001522 return(-1);
1523 if (callback == NULL)
1524 return(-1);
1525 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1526 return(-1);
1527
1528 do {
1529 tv.tv_sec = 1;
1530 tv.tv_usec = 0;
1531 FD_ZERO(&rfd);
1532 FD_SET(ctxt->dataFd, &rfd);
1533 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1534 if (res < 0) {
1535#ifdef DEBUG_FTP
1536 perror("select");
1537#endif
1538 close(ctxt->dataFd); ctxt->dataFd = -1;
1539 return(-1);
1540 }
1541 if (res == 0) {
1542 res = xmlNanoFTPCheckResponse(ctxt);
1543 if (res < 0) {
1544 close(ctxt->dataFd); ctxt->dataFd = -1;
1545 ctxt->dataFd = -1;
1546 return(-1);
1547 }
1548 if (res == 2) {
1549 close(ctxt->dataFd); ctxt->dataFd = -1;
1550 return(0);
1551 }
1552
1553 continue;
1554 }
1555 if ((len = read(ctxt->dataFd, &buf, sizeof(buf))) < 0) {
1556 callback(userData, buf, len);
1557 close(ctxt->dataFd); ctxt->dataFd = -1;
1558 return(-1);
1559 }
1560 callback(userData, buf, len);
1561 } while (len != 0);
1562
1563 return(xmlNanoFTPCloseConnection(ctxt));
1564}
1565
1566/**
1567 * xmlNanoFTPRead:
1568 * @ctx: the FTP context
1569 * @dest: a buffer
1570 * @len: the buffer length
1571 *
1572 * This function tries to read @len bytes from the existing FTP connection
1573 * and saves them in @dest. This is a blocking call.
1574 *
1575 * Returns the number of byte read. 0 is an indication of an end of connection.
1576 * -1 indicates a parameter error.
1577 */
1578int
1579xmlNanoFTPRead(void *ctx, void *dest, int len) {
1580 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1581
1582 if (ctx == NULL) return(-1);
1583 if (ctxt->dataFd < 0) return(0);
1584 if (dest == NULL) return(-1);
1585 if (len <= 0) return(0);
1586
1587 len = read(ctxt->dataFd, dest, len);
1588#ifdef DEBUG_FTP
1589 printf("Read %d bytes\n", len);
1590#endif
1591 if (len <= 0) {
1592 xmlNanoFTPCloseConnection(ctxt);
1593 }
1594 return(len);
1595}
1596
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001597/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001598 * xmlNanoFTPOpen:
1599 * @URL: the URL to the resource
1600 *
1601 * Start to fetch the given ftp:// resource
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001602 *
1603 * Returns an FTP context, or NULL
Daniel Veillardda07c342000-01-25 18:31:22 +00001604 */
1605
1606void *
1607xmlNanoFTPOpen(const char *URL) {
1608 xmlNanoFTPCtxtPtr ctxt;
1609 int sock;
1610
1611 xmlNanoFTPInit();
1612 if (URL == NULL) return(NULL);
1613 if (strncmp("ftp://", URL, 6)) return(NULL);
1614
1615 ctxt = xmlNanoFTPNewCtxt(URL);
1616 if (ctxt == NULL) return(NULL);
1617 if (xmlNanoFTPConnect(ctxt) < 0) {
1618 xmlNanoFTPFreeCtxt(ctxt);
1619 return(NULL);
1620 }
1621 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1622 if (sock < 0) {
1623 xmlNanoFTPFreeCtxt(ctxt);
1624 return(NULL);
1625 }
1626 return(ctxt);
1627}
1628
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001629/**
1630 * xmlNanoFTPClose:
1631 * @ctx: an FTP context
1632 *
1633 * Close the connection and both control and transport
1634 *
1635 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001636 */
1637
1638int
1639xmlNanoFTPClose(void *ctx) {
1640 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1641
1642 if (ctxt == NULL)
1643 return(-1);
1644
1645 if (ctxt->dataFd >= 0) {
1646 close(ctxt->dataFd);
1647 ctxt->dataFd = -1;
1648 }
1649 if (ctxt->controlFd >= 0) {
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001650 xmlNanoFTPQuit(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001651 close(ctxt->controlFd);
1652 ctxt->controlFd = -1;
1653 }
1654 xmlNanoFTPFreeCtxt(ctxt);
1655 return(0);
1656}
1657
1658#ifdef STANDALONE
1659/************************************************************************
1660 * *
1661 * Basic test in Standalone mode *
1662 * *
1663 ************************************************************************/
1664void ftpList(void *userData, const char *filename, const char* attrib,
1665 const char *owner, const char *group, unsigned long size, int links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001666 int year, const char *month, int day, int hour, int minute) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001667 printf("%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1668}
1669void ftpData(void *userData, const char *data, int len) {
1670 if (userData == NULL) return;
1671 if (len <= 0) {
1672 fclose(userData);
1673 return;
1674 }
1675 fwrite(data, len, 1, userData);
1676}
1677
1678int main(int argc, char **argv) {
1679 void *ctxt;
1680 FILE *output;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001681 char *tstfile = NULL;
Daniel Veillardda07c342000-01-25 18:31:22 +00001682
1683 xmlNanoFTPInit();
1684 if (argc > 1) {
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001685 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1686 if (xmlNanoFTPConnect(ctxt) < 0) {
1687 fprintf(stderr, "Couldn't connect to %s\n", argv[1]);
1688 exit(1);
1689 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001690 if (argc > 2)
1691 tstfile = argv[2];
1692 } else
1693 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1694 if (ctxt == NULL) {
1695 fprintf(stderr, "Couldn't connect to localhost\n");
1696 exit(1);
1697 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001698 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
Daniel Veillardda07c342000-01-25 18:31:22 +00001699 output = fopen("/tmp/tstdata", "w");
1700 if (output != NULL) {
1701 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001702 fprintf(stderr, "Failed to get file\n");
Daniel Veillardda07c342000-01-25 18:31:22 +00001703
1704 }
1705 xmlNanoFTPClose(ctxt);
1706 xmlMemoryDump();
1707 exit(0);
1708}
1709#endif /* STANDALONE */