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