2 * nanoftp.c: basic FTP client support
11 #define HAVE_SYS_SOCKET_H
12 #define HAVE_NETINET_IN_H
14 #define HAVE_SYS_TIME_H
22 #ifdef LIBXML_FTP_ENABLED
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
49 #ifdef HAVE_SYS_TIME_H
52 #ifdef HAVE_SYS_SELECT_H
53 #include <sys/select.h>
55 #ifdef HAVE_SYS_SOCKET_H
56 #include <sys/socket.h>
58 #ifdef HAVE_SYS_TYPES_H
59 #include <sys/types.h>
65 #include <libxml/xmlmemory.h>
66 #include <libxml/parser.h>
67 #include <libxml/xmlerror.h>
68 #include <libxml/uri.h>
69 #include <libxml/nanoftp.h>
70 #include <libxml/globals.h>
72 /* #define DEBUG_FTP 1 */
82 #include <wsockcompat.h>
85 #define XML_SOCKLEN_T unsigned int
89 * A couple portability macros
93 #define closesocket(s) close(s)
100 #define PF_INET AF_INET
105 #define ss_family __ss_family
108 #ifndef XML_SOCKLEN_T
109 #define XML_SOCKLEN_T unsigned int
112 #define FTP_COMMAND_OK 200
113 #define FTP_SYNTAX_ERROR 500
114 #define FTP_GET_PASSWD 331
115 #define FTP_BUF_SIZE 1024
117 #define XML_NANO_MAX_URLBUF 4096
119 typedef struct xmlNanoFTPCtxt {
120 char *protocol; /* the protocol name */
121 char *hostname; /* the host name */
122 int port; /* the port */
123 char *path; /* the path within the URL */
124 char *user; /* user string */
125 char *passwd; /* passwd string */
127 struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
129 struct sockaddr_in ftpAddr; /* the socket address struct */
131 int passive; /* currently we support only passive !!! */
132 SOCKET controlFd; /* the file descriptor for the control socket */
133 SOCKET dataFd; /* the file descriptor for the data socket */
134 int state; /* WRITE / READ / CLOSED */
135 int returnValue; /* the protocol return value */
136 /* buffer for data received from the control connection */
137 char controlBuf[FTP_BUF_SIZE + 1];
140 int controlBufAnswer;
141 } xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
143 static int initialized = 0;
144 static char *proxy = NULL; /* the proxy name if any */
145 static int proxyPort = 0; /* the proxy port if any */
146 static char *proxyUser = NULL; /* user for proxy authentication */
147 static char *proxyPasswd = NULL;/* passwd for proxy authentication */
148 static int proxyType = 0; /* uses TYPE or a@b ? */
152 int have_ipv6(void) {
155 s = socket (AF_INET6, SOCK_STREAM, 0);
166 * @extra: extra informations
168 * Handle an out of memory condition
171 xmlFTPErrMemory(const char *extra)
173 __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
179 * Initialize the FTP protocol layer.
180 * Currently it just checks for proxy informations,
181 * and get the hostname
185 xmlNanoFTPInit(void) {
195 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
200 env = getenv("no_proxy");
201 if (env && ((env[0] == '*' ) && (env[1] == 0)))
203 env = getenv("ftp_proxy");
205 xmlNanoFTPScanProxy(env);
207 env = getenv("FTP_PROXY");
209 xmlNanoFTPScanProxy(env);
212 env = getenv("ftp_proxy_user");
214 proxyUser = xmlMemStrdup(env);
216 env = getenv("ftp_proxy_password");
218 proxyPasswd = xmlMemStrdup(env);
226 * Cleanup the FTP protocol layer. This cleanup proxy informations.
230 xmlNanoFTPCleanup(void) {
235 if (proxyUser != NULL) {
239 if (proxyPasswd != NULL) {
240 xmlFree(proxyPasswd);
252 * @host: the proxy host name
253 * @port: the proxy port
254 * @user: the proxy user name
255 * @passwd: the proxy password
256 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
258 * Setup the FTP proxy informations.
259 * This can also be done by using ftp_proxy ftp_proxy_user and
260 * ftp_proxy_password environment variables.
264 xmlNanoFTPProxy(const char *host, int port, const char *user,
265 const char *passwd, int type) {
270 if (proxyUser != NULL) {
274 if (proxyPasswd != NULL) {
275 xmlFree(proxyPasswd);
279 proxy = xmlMemStrdup(host);
281 proxyUser = xmlMemStrdup(user);
283 proxyPasswd = xmlMemStrdup(passwd);
290 * @ctx: an FTP context
291 * @URL: The URL used to initialize the context
293 * (Re)Initialize an FTP context by parsing the URL and finding
294 * the protocol host port and path it indicates.
298 xmlNanoFTPScanURL(void *ctx, const char *URL) {
299 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
303 * Clear any existing data from the context
305 if (ctxt->protocol != NULL) {
306 xmlFree(ctxt->protocol);
307 ctxt->protocol = NULL;
309 if (ctxt->hostname != NULL) {
310 xmlFree(ctxt->hostname);
311 ctxt->hostname = NULL;
313 if (ctxt->path != NULL) {
317 if (URL == NULL) return;
319 uri = xmlParseURIRaw(URL, 1);
323 if ((uri->scheme == NULL) || (uri->server == NULL)) {
328 ctxt->protocol = xmlMemStrdup(uri->scheme);
329 ctxt->hostname = xmlMemStrdup(uri->server);
330 if (uri->path != NULL)
331 ctxt->path = xmlMemStrdup(uri->path);
333 ctxt->path = xmlMemStrdup("/");
335 ctxt->port = uri->port;
337 if (uri->user != NULL) {
339 if ((cptr=strchr(uri->user, ':')) == NULL)
340 ctxt->user = xmlMemStrdup(uri->user);
342 ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
344 ctxt->passwd = xmlMemStrdup(cptr+1);
353 * xmlNanoFTPUpdateURL:
354 * @ctx: an FTP context
355 * @URL: The URL used to update the context
357 * Update an FTP context by parsing the URL and finding
358 * new path it indicates. If there is an error in the
359 * protocol, hostname, port or other information, the
360 * error is raised. It indicates a new connection has to
363 * Returns 0 if Ok, -1 in case of error (other host).
367 xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
368 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
375 if (ctxt->protocol == NULL)
377 if (ctxt->hostname == NULL)
380 uri = xmlParseURIRaw(URL, 1);
384 if ((uri->scheme == NULL) || (uri->server == NULL)) {
388 if ((strcmp(ctxt->protocol, uri->scheme)) ||
389 (strcmp(ctxt->hostname, uri->server)) ||
390 ((uri->port != 0) && (ctxt->port != uri->port))) {
396 ctxt->port = uri->port;
398 if (ctxt->path != NULL) {
403 if (uri->path == NULL)
404 ctxt->path = xmlMemStrdup("/");
406 ctxt->path = xmlMemStrdup(uri->path);
414 * xmlNanoFTPScanProxy:
415 * @URL: The proxy URL used to initialize the proxy context
417 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
418 * the protocol host port it indicates.
419 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
420 * A NULL URL cleans up proxy informations.
424 xmlNanoFTPScanProxy(const char *URL) {
435 xmlGenericError(xmlGenericErrorContext,
436 "Removing FTP proxy info\n");
438 xmlGenericError(xmlGenericErrorContext,
439 "Using FTP proxy %s\n", URL);
441 if (URL == NULL) return;
443 uri = xmlParseURIRaw(URL, 1);
444 if ((uri == NULL) || (uri->scheme == NULL) ||
445 (strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
446 __xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
452 proxy = xmlMemStrdup(uri->server);
454 proxyPort = uri->port;
461 * @URL: The URL used to initialize the context
463 * Allocate and initialize a new FTP context.
465 * Returns an FTP context or NULL in case of error.
469 xmlNanoFTPNewCtxt(const char *URL) {
470 xmlNanoFTPCtxtPtr ret;
473 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
475 xmlFTPErrMemory("allocating FTP context");
479 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
482 ret->returnValue = 0;
483 ret->controlBufIndex = 0;
484 ret->controlBufUsed = 0;
487 unescaped = xmlURIUnescapeString(URL, 0, NULL);
488 if (unescaped != NULL) {
489 xmlNanoFTPScanURL(ret, unescaped);
491 } else if (URL != NULL)
492 xmlNanoFTPScanURL(ret, URL);
498 * xmlNanoFTPFreeCtxt:
499 * @ctx: an FTP context
501 * Frees the context after closing the connection.
505 xmlNanoFTPFreeCtxt(void * ctx) {
506 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
507 if (ctxt == NULL) return;
508 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
509 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
510 if (ctxt->path != NULL) xmlFree(ctxt->path);
512 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
513 ctxt->controlFd = -1;
514 ctxt->controlBufIndex = -1;
515 ctxt->controlBufUsed = -1;
520 * xmlNanoFTPParseResponse:
521 * @buf: the buffer containing the response
522 * @len: the buffer length
524 * Parsing of the server answer, we just extract the code.
526 * returns 0 for errors
527 * +XXX for last line of response
528 * -XXX for response to be continued
531 xmlNanoFTPParseResponse(char *buf, int len) {
534 if (len < 3) return(-1);
535 if ((*buf >= '0') && (*buf <= '9'))
536 val = val * 10 + (*buf - '0');
540 if ((*buf >= '0') && (*buf <= '9'))
541 val = val * 10 + (*buf - '0');
545 if ((*buf >= '0') && (*buf <= '9'))
546 val = val * 10 + (*buf - '0');
557 * @ctx: an FTP context
559 * Read more information from the FTP control connection
560 * Returns the number of bytes read, < 0 indicates an error
563 xmlNanoFTPGetMore(void *ctx) {
564 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
568 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
570 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
572 xmlGenericError(xmlGenericErrorContext,
573 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
574 ctxt->controlBufIndex);
579 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
581 xmlGenericError(xmlGenericErrorContext,
582 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
583 ctxt->controlBufUsed);
587 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
589 xmlGenericError(xmlGenericErrorContext,
590 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
591 ctxt->controlBufIndex, ctxt->controlBufUsed);
597 * First pack the control buffer
599 if (ctxt->controlBufIndex > 0) {
600 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
601 ctxt->controlBufUsed - ctxt->controlBufIndex);
602 ctxt->controlBufUsed -= ctxt->controlBufIndex;
603 ctxt->controlBufIndex = 0;
605 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
608 xmlGenericError(xmlGenericErrorContext,
609 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
615 * Read the amount left on the control connection
617 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
619 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
620 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
621 ctxt->controlFd = -1;
625 xmlGenericError(xmlGenericErrorContext,
626 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
627 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
629 ctxt->controlBufUsed += len;
630 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
636 * xmlNanoFTPReadResponse:
637 * @ctx: an FTP context
639 * Read the response from the FTP server after a command.
640 * Returns the code number
643 xmlNanoFTPReadResponse(void *ctx) {
644 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
647 int res = -1, cur = -1;
649 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
653 * Assumes everything up to controlBuf[controlBufIndex] has been read
656 len = xmlNanoFTPGetMore(ctx);
660 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
663 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
664 end = &ctxt->controlBuf[ctxt->controlBufUsed];
667 xmlGenericError(xmlGenericErrorContext,
668 "\n<<<\n%s\n--\n", ptr);
671 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
674 * Successfully scanned the control code, scratch
675 * till the end of the line, but keep the index to be
676 * able to analyze the result if needed.
680 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
681 while ((ptr < end) && (*ptr != '\n')) ptr++;
682 if (*ptr == '\n') ptr++;
683 if (*ptr == '\r') ptr++;
686 while ((ptr < end) && (*ptr != '\n')) ptr++;
688 ctxt->controlBufIndex = ctxt->controlBufUsed;
691 if (*ptr != '\r') ptr++;
694 if (res < 0) goto get_more;
695 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
697 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
698 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
702 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
708 * xmlNanoFTPGetResponse:
709 * @ctx: an FTP context
711 * Get the response from the FTP server after a command.
712 * Returns the code number
716 xmlNanoFTPGetResponse(void *ctx) {
719 res = xmlNanoFTPReadResponse(ctx);
725 * xmlNanoFTPCheckResponse:
726 * @ctx: an FTP context
728 * Check if there is a response from the FTP server after a command.
729 * Returns the code number, or 0
733 xmlNanoFTPCheckResponse(void *ctx) {
734 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
738 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
742 FD_SET(ctxt->controlFd, &rfd);
743 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
747 __xmlIOErr(XML_FROM_FTP, 0, "select");
752 return(xmlNanoFTPReadResponse(ctx));
756 * Send the user authentication
760 xmlNanoFTPSendUser(void *ctx) {
761 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
766 if (ctxt->user == NULL)
767 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
769 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
770 buf[sizeof(buf) - 1] = 0;
773 xmlGenericError(xmlGenericErrorContext, "%s", buf);
775 res = send(ctxt->controlFd, buf, len, 0);
777 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
784 * Send the password authentication
788 xmlNanoFTPSendPasswd(void *ctx) {
789 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
794 if (ctxt->passwd == NULL)
795 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
797 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
798 buf[sizeof(buf) - 1] = 0;
801 xmlGenericError(xmlGenericErrorContext, "%s", buf);
803 res = send(ctxt->controlFd, buf, len, 0);
805 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
813 * @ctx: an FTP context
815 * Send a QUIT command to the server
817 * Returns -1 in case of error, 0 otherwise
822 xmlNanoFTPQuit(void *ctx) {
823 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
827 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
829 snprintf(buf, sizeof(buf), "QUIT\r\n");
832 xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
834 res = send(ctxt->controlFd, buf, len, 0);
836 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
844 * @ctx: an FTP context
846 * Tries to open a control connection
848 * Returns -1 in case of error, 0 otherwise
852 xmlNanoFTPConnect(void *ctx) {
853 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
857 int addrlen = sizeof (struct sockaddr_in);
861 if (ctxt->hostname == NULL)
865 * do the blocking DNS query.
875 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
879 struct addrinfo hints, *tmp, *result;
882 memset (&hints, 0, sizeof(hints));
883 hints.ai_socktype = SOCK_STREAM;
886 if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
887 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
892 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
893 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
897 for (tmp = result; tmp; tmp = tmp->ai_next)
898 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
903 freeaddrinfo (result);
904 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
907 if (tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
908 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
911 if (tmp->ai_family == AF_INET6) {
912 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
913 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
914 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
917 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
918 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
919 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
921 addrlen = tmp->ai_addrlen;
922 freeaddrinfo (result);
928 hp = gethostbyname (proxy);
930 hp = gethostbyname (ctxt->hostname);
932 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
935 if ((unsigned int) hp->h_length >
936 sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
937 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
944 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
945 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
946 hp->h_addr_list[0], hp->h_length);
947 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = (u_short)htons ((unsigned short)port);
948 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
949 addrlen = sizeof (struct sockaddr_in);
952 if (ctxt->controlFd < 0) {
953 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
960 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
962 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
963 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
964 ctxt->controlFd = -1;
969 * Wait for the HELLO from the server.
971 res = xmlNanoFTPGetResponse(ctxt);
973 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
974 ctxt->controlFd = -1;
979 * State diagram for the login operation on the FTP server
984 * +---+ USER +---+------------->+---+
985 * | B |---------->| W | 2 ---->| E |
986 * +---+ +---+------ | -->+---+
989 * -------------- ----- | | |
995 * +---+ PASS +---+ 2 | ------>+---+
996 * | |---------->| W |------------->| S |
997 * +---+ +---+ ---------->+---+
1000 * -------------- -------- |
1006 * +---+ ACCT +---+-- | ----->+---+
1007 * | |---------->| W | 4,5 -------->| F |
1008 * +---+ +---+------------->+---+
1010 * Of course in case of using a proxy this get really nasty and is not
1011 * standardized at all :-(
1017 if (proxyUser != NULL) {
1019 * We need proxy auth
1021 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
1022 buf[sizeof(buf) - 1] = 0;
1025 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1027 res = send(ctxt->controlFd, buf, len, 0);
1029 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1030 closesocket(ctxt->controlFd);
1031 ctxt->controlFd = -1;
1034 res = xmlNanoFTPGetResponse(ctxt);
1037 if (proxyPasswd == NULL)
1040 if (proxyPasswd != NULL)
1041 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
1043 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1044 buf[sizeof(buf) - 1] = 0;
1047 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1049 res = send(ctxt->controlFd, buf, len, 0);
1051 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1052 closesocket(ctxt->controlFd);
1053 ctxt->controlFd = -1;
1056 res = xmlNanoFTPGetResponse(ctxt);
1058 closesocket(ctxt->controlFd);
1059 ctxt->controlFd = -1;
1069 closesocket(ctxt->controlFd);
1070 ctxt->controlFd = -1;
1076 * We assume we don't need more authentication to the proxy
1077 * and that it succeeded :-\
1079 switch (proxyType) {
1081 /* we will try in sequence */
1083 /* Using SITE command */
1084 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
1085 buf[sizeof(buf) - 1] = 0;
1088 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1090 res = send(ctxt->controlFd, buf, len, 0);
1092 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1093 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1094 ctxt->controlFd = -1;
1097 res = xmlNanoFTPGetResponse(ctxt);
1099 /* we assume it worked :-\ 1 is error for SITE command */
1103 if (proxyType == 1) {
1104 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1105 ctxt->controlFd = -1;
1109 /* USER user@host command */
1110 if (ctxt->user == NULL)
1111 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1114 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1115 ctxt->user, ctxt->hostname);
1116 buf[sizeof(buf) - 1] = 0;
1119 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1121 res = send(ctxt->controlFd, buf, len, 0);
1123 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1124 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1125 ctxt->controlFd = -1;
1128 res = xmlNanoFTPGetResponse(ctxt);
1129 if ((res == 1) || (res == 2)) {
1130 /* we assume it worked :-\ */
1134 if (ctxt->passwd == NULL)
1135 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1137 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
1138 buf[sizeof(buf) - 1] = 0;
1141 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1143 res = send(ctxt->controlFd, buf, len, 0);
1145 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1146 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1147 ctxt->controlFd = -1;
1150 res = xmlNanoFTPGetResponse(ctxt);
1151 if ((res == 1) || (res == 2)) {
1152 /* we assume it worked :-\ */
1156 if (proxyType == 2) {
1157 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1158 ctxt->controlFd = -1;
1163 * If you need support for other Proxy authentication scheme
1164 * send the code or at least the sequence in use.
1167 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1168 ctxt->controlFd = -1;
1173 * Non-proxy handling.
1175 res = xmlNanoFTPSendUser(ctxt);
1177 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1178 ctxt->controlFd = -1;
1181 res = xmlNanoFTPGetResponse(ctxt);
1192 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1193 ctxt->controlFd = -1;
1196 res = xmlNanoFTPSendPasswd(ctxt);
1198 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1199 ctxt->controlFd = -1;
1202 res = xmlNanoFTPGetResponse(ctxt);
1207 __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
1208 "FTP server asking for ACCNT on anonymous\n");
1214 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1215 ctxt->controlFd = -1;
1223 * xmlNanoFTPConnectTo:
1224 * @server: an FTP server name
1225 * @port: the port (use 21 if 0)
1227 * Tries to open a control connection to the given server/port
1229 * Returns an fTP context or NULL if it failed
1233 xmlNanoFTPConnectTo(const char *server, int port) {
1234 xmlNanoFTPCtxtPtr ctxt;
1242 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1243 ctxt->hostname = xmlMemStrdup(server);
1246 res = xmlNanoFTPConnect(ctxt);
1248 xmlNanoFTPFreeCtxt(ctxt);
1256 * @ctx: an FTP context
1257 * @directory: a directory on the server
1259 * Tries to change the remote directory
1261 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1265 xmlNanoFTPCwd(void *ctx, const char *directory) {
1266 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1271 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1272 if (directory == NULL) return 0;
1275 * Expected response code for CWD:
1279 * 500, 501, 502, 421, 530, 550
1281 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
1282 buf[sizeof(buf) - 1] = 0;
1285 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1287 res = send(ctxt->controlFd, buf, len, 0);
1289 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1292 res = xmlNanoFTPGetResponse(ctxt);
1296 if (res == 2) return(1);
1305 * @ctx: an FTP context
1306 * @file: a file or directory on the server
1308 * Tries to delete an item (file or directory) from server
1310 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1314 xmlNanoFTPDele(void *ctx, const char *file) {
1315 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1320 if ((ctxt == NULL) || (ctxt->controlFd < 0) || (file == NULL)) return(-1);
1321 if (file == NULL) return (0);
1324 * Expected response code for DELE:
1329 * 500, 501, 502, 421, 530
1332 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1333 buf[sizeof(buf) - 1] = 0;
1336 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1338 res = send(ctxt->controlFd, buf, len, 0);
1340 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1343 res = xmlNanoFTPGetResponse(ctxt);
1347 if (res == 2) return(1);
1354 * xmlNanoFTPGetConnection:
1355 * @ctx: an FTP context
1357 * Try to open a data connection to the server. Currently only
1358 * passive mode is supported.
1360 * Returns -1 incase of error, 0 otherwise
1364 xmlNanoFTPGetConnection(void *ctx) {
1365 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1366 char buf[200], *cur;
1369 unsigned char ad[6], *adp, *portp;
1370 unsigned int temp[6];
1372 struct sockaddr_storage dataAddr;
1374 struct sockaddr_in dataAddr;
1376 XML_SOCKLEN_T dataAddrLen;
1378 if (ctxt == NULL) return(-1);
1380 memset (&dataAddr, 0, sizeof(dataAddr));
1382 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1383 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1384 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1385 dataAddrLen = sizeof(struct sockaddr_in6);
1389 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1390 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1391 dataAddrLen = sizeof (struct sockaddr_in);
1394 if (ctxt->dataFd < 0) {
1395 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
1399 if (ctxt->passive) {
1401 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1402 snprintf (buf, sizeof(buf), "EPSV\r\n");
1405 snprintf (buf, sizeof(buf), "PASV\r\n");
1408 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1410 res = send(ctxt->controlFd, buf, len, 0);
1412 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1413 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1416 res = xmlNanoFTPReadResponse(ctx);
1419 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1423 * retry with an active connection
1425 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1429 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1430 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1432 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1433 if (sscanf (cur, "%u", &temp[0]) != 1) {
1434 __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
1435 "Invalid answer to EPSV\n");
1436 if (ctxt->dataFd != -1) {
1437 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1441 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1442 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
1447 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1448 &temp[3], &temp[4], &temp[5]) != 6) {
1449 __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
1450 "Invalid answer to PASV\n");
1451 if (ctxt->dataFd != -1) {
1452 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1456 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1457 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1458 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1461 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1462 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
1463 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1467 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1469 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1470 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1473 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1475 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1476 __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
1477 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1480 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1482 if (listen(ctxt->dataFd, 1) < 0) {
1483 __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
1484 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1488 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1489 char buf6[INET6_ADDRSTRLEN];
1490 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1491 buf6, INET6_ADDRSTRLEN);
1492 adp = (unsigned char *) buf6;
1493 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1494 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1498 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1499 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1500 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1501 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1502 portp[0] & 0xff, portp[1] & 0xff);
1505 buf[sizeof(buf) - 1] = 0;
1508 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1511 res = send(ctxt->controlFd, buf, len, 0);
1513 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1514 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1517 res = xmlNanoFTPGetResponse(ctxt);
1519 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1523 return(ctxt->dataFd);
1528 * xmlNanoFTPCloseConnection:
1529 * @ctx: an FTP context
1531 * Close the data connection from the server
1533 * Returns -1 incase of error, 0 otherwise
1537 xmlNanoFTPCloseConnection(void *ctx) {
1538 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1543 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1545 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1549 FD_SET(ctxt->controlFd, &rfd);
1551 FD_SET(ctxt->controlFd, &efd);
1552 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1557 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1562 xmlGenericError(xmlGenericErrorContext,
1563 "xmlNanoFTPCloseConnection: timeout\n");
1565 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1567 res = xmlNanoFTPGetResponse(ctxt);
1569 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1577 * xmlNanoFTPParseList:
1578 * @list: some data listing received from the server
1579 * @callback: the user callback
1580 * @userData: the user callback data
1582 * Parse at most one entry from the listing.
1584 * Returns -1 incase of error, the length of data parsed otherwise
1588 xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1589 const char *cur = list;
1599 unsigned long size = 0;
1603 if (!strncmp(cur, "total", 5)) {
1605 while (*cur == ' ') cur++;
1606 while ((*cur >= '0') && (*cur <= '9'))
1607 links = (links * 10) + (*cur++ - '0');
1608 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1611 } else if (*list == '+') {
1614 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1616 if (*cur == 0) return(0);
1618 while (*cur != ' ') {
1622 if (*cur == 0) return(0);
1625 while (*cur == ' ') cur++;
1626 if (*cur == 0) return(0);
1627 while ((*cur >= '0') && (*cur <= '9'))
1628 links = (links * 10) + (*cur++ - '0');
1629 while (*cur == ' ') cur++;
1630 if (*cur == 0) return(0);
1632 while (*cur != ' ') {
1636 if (*cur == 0) return(0);
1639 while (*cur == ' ') cur++;
1640 if (*cur == 0) return(0);
1642 while (*cur != ' ') {
1646 if (*cur == 0) return(0);
1649 while (*cur == ' ') cur++;
1650 if (*cur == 0) return(0);
1651 while ((*cur >= '0') && (*cur <= '9'))
1652 size = (size * 10) + (*cur++ - '0');
1653 while (*cur == ' ') cur++;
1654 if (*cur == 0) return(0);
1656 while (*cur != ' ') {
1660 if (*cur == 0) return(0);
1663 while (*cur == ' ') cur++;
1664 if (*cur == 0) return(0);
1665 while ((*cur >= '0') && (*cur <= '9'))
1666 day = (day * 10) + (*cur++ - '0');
1667 while (*cur == ' ') cur++;
1668 if (*cur == 0) return(0);
1669 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1670 if ((cur[1] == ':') || (cur[2] == ':')) {
1671 while ((*cur >= '0') && (*cur <= '9'))
1672 hour = (hour * 10) + (*cur++ - '0');
1673 if (*cur == ':') cur++;
1674 while ((*cur >= '0') && (*cur <= '9'))
1675 minute = (minute * 10) + (*cur++ - '0');
1677 while ((*cur >= '0') && (*cur <= '9'))
1678 year = (year * 10) + (*cur++ - '0');
1680 while (*cur == ' ') cur++;
1681 if (*cur == 0) return(0);
1683 while ((*cur != '\n') && (*cur != '\r')) {
1685 filename[i++] = *cur;
1687 if (*cur == 0) return(0);
1690 if ((*cur != '\n') && (*cur != '\r'))
1692 while ((*cur == '\n') || (*cur == '\r'))
1695 if (callback != NULL) {
1696 callback(userData, filename, attrib, owner, group, size, links,
1697 year, month, day, hour, minute);
1704 * @ctx: an FTP context
1705 * @callback: the user callback
1706 * @userData: the user callback data
1707 * @filename: optional files to list
1709 * Do a listing on the server. All files info are passed back
1712 * Returns -1 incase of error, 0 otherwise
1716 xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1717 const char *filename) {
1718 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1725 if (ctxt == NULL) return (-1);
1726 if (filename == NULL) {
1727 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1729 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1730 if (ctxt->dataFd == -1)
1732 snprintf(buf, sizeof(buf), "LIST -L\r\n");
1734 if (filename[0] != '/') {
1735 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1738 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1739 if (ctxt->dataFd == -1)
1741 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
1743 buf[sizeof(buf) - 1] = 0;
1746 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1748 res = send(ctxt->controlFd, buf, len, 0);
1750 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1751 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1754 res = xmlNanoFTPReadResponse(ctxt);
1756 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1764 FD_SET(ctxt->dataFd, &rfd);
1766 FD_SET(ctxt->dataFd, &efd);
1767 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1772 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1776 res = xmlNanoFTPCheckResponse(ctxt);
1778 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1783 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1790 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
1791 __xmlIOErr(XML_FROM_FTP, 0, "recv");
1792 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1797 write(1, &buf[indx], len);
1803 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1807 memmove(&buf[0], &buf[base], indx - base);
1810 xmlNanoFTPCloseConnection(ctxt);
1815 * xmlNanoFTPGetSocket:
1816 * @ctx: an FTP context
1817 * @filename: the file to retrieve (or NULL if path is in context).
1819 * Initiate fetch of the given file from the server.
1821 * Returns the socket for the data connection, or <0 in case of error
1826 xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1827 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1832 if ((filename == NULL) && (ctxt->path == NULL))
1834 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1835 if (ctxt->dataFd == -1)
1838 snprintf(buf, sizeof(buf), "TYPE I\r\n");
1841 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1843 res = send(ctxt->controlFd, buf, len, 0);
1845 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1846 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1849 res = xmlNanoFTPReadResponse(ctxt);
1851 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1854 if (filename == NULL)
1855 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
1857 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
1858 buf[sizeof(buf) - 1] = 0;
1861 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1863 res = send(ctxt->controlFd, buf, len, 0);
1865 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1866 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1869 res = xmlNanoFTPReadResponse(ctxt);
1871 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1874 return(ctxt->dataFd);
1879 * @ctx: an FTP context
1880 * @callback: the user callback
1881 * @userData: the user callback data
1882 * @filename: the file to retrieve
1884 * Fetch the given file from the server. All data are passed back
1885 * in the callbacks. The last callback has a size of 0 block.
1887 * Returns -1 incase of error, 0 otherwise
1891 xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1892 const char *filename) {
1893 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1899 if (ctxt == NULL) return(-1);
1900 if ((filename == NULL) && (ctxt->path == NULL))
1902 if (callback == NULL)
1904 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1911 FD_SET(ctxt->dataFd, &rfd);
1912 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1917 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1921 res = xmlNanoFTPCheckResponse(ctxt);
1923 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1928 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1934 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1935 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1936 callback(userData, buf, len);
1937 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1940 callback(userData, buf, len);
1943 return(xmlNanoFTPCloseConnection(ctxt));
1948 * @ctx: the FTP context
1950 * @len: the buffer length
1952 * This function tries to read @len bytes from the existing FTP connection
1953 * and saves them in @dest. This is a blocking call.
1955 * Returns the number of byte read. 0 is an indication of an end of connection.
1956 * -1 indicates a parameter error.
1959 xmlNanoFTPRead(void *ctx, void *dest, int len) {
1960 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1962 if (ctx == NULL) return(-1);
1963 if (ctxt->dataFd < 0) return(0);
1964 if (dest == NULL) return(-1);
1965 if (len <= 0) return(0);
1967 len = recv(ctxt->dataFd, dest, len, 0);
1970 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1971 xmlNanoFTPCloseConnection(ctxt);
1974 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1981 * @URL: the URL to the resource
1983 * Start to fetch the given ftp:// resource
1985 * Returns an FTP context, or NULL
1989 xmlNanoFTPOpen(const char *URL) {
1990 xmlNanoFTPCtxtPtr ctxt;
1994 if (URL == NULL) return(NULL);
1995 if (strncmp("ftp://", URL, 6)) return(NULL);
1997 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
1998 if (ctxt == NULL) return(NULL);
1999 if (xmlNanoFTPConnect(ctxt) < 0) {
2000 xmlNanoFTPFreeCtxt(ctxt);
2003 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2005 xmlNanoFTPFreeCtxt(ctxt);
2013 * @ctx: an FTP context
2015 * Close the connection and both control and transport
2017 * Returns -1 incase of error, 0 otherwise
2021 xmlNanoFTPClose(void *ctx) {
2022 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2027 if (ctxt->dataFd >= 0) {
2028 closesocket(ctxt->dataFd);
2031 if (ctxt->controlFd >= 0) {
2032 xmlNanoFTPQuit(ctxt);
2033 closesocket(ctxt->controlFd);
2034 ctxt->controlFd = -1;
2036 xmlNanoFTPFreeCtxt(ctxt);
2041 /************************************************************************
2043 * Basic test in Standalone mode *
2045 ************************************************************************/
2047 void ftpList(void *userData, const char *filename, const char* attrib,
2048 const char *owner, const char *group, unsigned long size, int links,
2049 int year, const char *month, int day, int hour, int minute) {
2050 xmlGenericError(xmlGenericErrorContext,
2051 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2054 void ftpData(void *userData, const char *data, int len) {
2055 if (userData == NULL) return;
2057 fclose((FILE*)userData);
2060 fwrite(data, len, 1, (FILE*)userData);
2063 int main(int argc, char **argv) {
2066 char *tstfile = NULL;
2070 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2071 if (xmlNanoFTPConnect(ctxt) < 0) {
2072 xmlGenericError(xmlGenericErrorContext,
2073 "Couldn't connect to %s\n", argv[1]);
2079 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2081 xmlGenericError(xmlGenericErrorContext,
2082 "Couldn't connect to localhost\n");
2085 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2086 output = fopen("/tmp/tstdata", "w");
2087 if (output != NULL) {
2088 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2089 xmlGenericError(xmlGenericErrorContext,
2090 "Failed to get file\n");
2093 xmlNanoFTPClose(ctxt);
2097 #endif /* STANDALONE */
2098 #else /* !LIBXML_FTP_ENABLED */
2101 int main(int argc, char **argv) {
2102 xmlGenericError(xmlGenericErrorContext,
2103 "%s : FTP support not compiled in\n", argv[0]);
2106 #endif /* STANDALONE */
2107 #endif /* LIBXML_FTP_ENABLED */
2108 #define bottom_nanoftp
2109 #include "elfgcchack.h"