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 SOCKLEN_T unsigned int
90 * A couple portability macros
93 #define closesocket(s) close(s)
96 #if defined(VMS) || defined(__VMS)
97 #define SOCKLEN_T unsigned int
100 #define FTP_COMMAND_OK 200
101 #define FTP_SYNTAX_ERROR 500
102 #define FTP_GET_PASSWD 331
103 #define FTP_BUF_SIZE 512
105 typedef struct xmlNanoFTPCtxt {
106 char *protocol; /* the protocol name */
107 char *hostname; /* the host name */
108 int port; /* the port */
109 char *path; /* the path within the URL */
110 char *user; /* user string */
111 char *passwd; /* passwd string */
113 struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
115 struct sockaddr_in ftpAddr; /* the socket address struct */
117 int passive; /* currently we support only passive !!! */
118 SOCKET controlFd; /* the file descriptor for the control socket */
119 SOCKET dataFd; /* the file descriptor for the data socket */
120 int state; /* WRITE / READ / CLOSED */
121 int returnValue; /* the protocol return value */
122 /* buffer for data received from the control connection */
123 char controlBuf[FTP_BUF_SIZE + 1];
126 int controlBufAnswer;
127 } xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
129 static int initialized = 0;
130 static char *proxy = NULL; /* the proxy name if any */
131 static int proxyPort = 0; /* the proxy port if any */
132 static char *proxyUser = NULL; /* user for proxy authentication */
133 static char *proxyPasswd = NULL;/* passwd for proxy authentication */
134 static int proxyType = 0; /* uses TYPE or a@b ? */
138 int have_ipv6(void) {
141 s = socket (AF_INET6, SOCK_STREAM, 0);
153 * Initialize the FTP protocol layer.
154 * Currently it just checks for proxy informations,
155 * and get the hostname
159 xmlNanoFTPInit(void) {
169 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
174 env = getenv("no_proxy");
177 env = getenv("ftp_proxy");
179 xmlNanoFTPScanProxy(env);
181 env = getenv("FTP_PROXY");
183 xmlNanoFTPScanProxy(env);
186 env = getenv("ftp_proxy_user");
188 proxyUser = xmlMemStrdup(env);
190 env = getenv("ftp_proxy_password");
192 proxyPasswd = xmlMemStrdup(env);
200 * Cleanup the FTP protocol layer. This cleanup proxy informations.
204 xmlNanoFTPCleanup(void) {
209 if (proxyUser != NULL) {
213 if (proxyPasswd != NULL) {
214 xmlFree(proxyPasswd);
226 * @host: the proxy host name
227 * @port: the proxy port
228 * @user: the proxy user name
229 * @passwd: the proxy password
230 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
232 * Setup the FTP proxy informations.
233 * This can also be done by using ftp_proxy ftp_proxy_user and
234 * ftp_proxy_password environment variables.
238 xmlNanoFTPProxy(const char *host, int port, const char *user,
239 const char *passwd, int type) {
242 if (proxyUser != NULL)
244 if (proxyPasswd != NULL)
245 xmlFree(proxyPasswd);
247 proxy = xmlMemStrdup(host);
249 proxyUser = xmlMemStrdup(user);
251 proxyPasswd = xmlMemStrdup(passwd);
258 * @ctx: an FTP context
259 * @URL: The URL used to initialize the context
261 * (Re)Initialize an FTP context by parsing the URL and finding
262 * the protocol host port and path it indicates.
266 xmlNanoFTPScanURL(void *ctx, const char *URL) {
267 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
268 const char *cur = URL;
273 if (ctxt->protocol != NULL) {
274 xmlFree(ctxt->protocol);
275 ctxt->protocol = NULL;
277 if (ctxt->hostname != NULL) {
278 xmlFree(ctxt->hostname);
279 ctxt->hostname = NULL;
281 if (ctxt->path != NULL) {
285 if (URL == NULL) return;
288 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
290 ctxt->protocol = xmlMemStrdup(buf);
295 buf[indx++] = *cur++;
297 if (*cur == 0) return;
300 /* allow user@ and user:pass@ forms */
302 const char *p = strchr(cur, '@');
305 if(cur[0] == ':' || cur[0] == '@') break;
306 buf[indx++] = *cur++;
309 ctxt->user = xmlMemStrdup(buf);
314 if(cur[0] == '@') break;
315 buf[indx++] = *cur++;
318 ctxt->passwd = xmlMemStrdup(buf);
326 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
327 (!strchr (cur, '[') && strchr (cur, ']'))) {
328 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
335 while (cur[0] != ']')
336 buf[indx++] = *cur++;
338 if (!strchr (buf, ':')) {
339 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
340 "Use [IPv6]/IPv4 format\n");
345 ctxt->hostname = xmlMemStrdup (buf);
350 while (*cur >= '0' && *cur <= '9') {
356 if (port != 0) ctxt->port = port;
357 while ((cur[0] != '/') && (*cur != 0))
362 else { /* address is an IPv4 one*/
365 ctxt->hostname = xmlMemStrdup (buf);
368 while ((*cur >= '0') && (*cur <= '9')) {
373 if (port != 0) ctxt->port = port;
374 while ((cur[0] != '/') && (*cur != 0))
378 if ((*cur == '/') || (*cur == 0)) {
380 ctxt->hostname = xmlMemStrdup (buf);
385 buf[indx++] = *cur++;
388 ctxt->path = xmlMemStrdup("/");
393 buf[indx++] = *cur++;
395 ctxt->path = xmlMemStrdup(buf);
400 * xmlNanoFTPUpdateURL:
401 * @ctx: an FTP context
402 * @URL: The URL used to update the context
404 * Update an FTP context by parsing the URL and finding
405 * new path it indicates. If there is an error in the
406 * protocol, hostname, port or other information, the
407 * error is raised. It indicates a new connection has to
410 * Returns 0 if Ok, -1 in case of error (other host).
414 xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
415 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
416 const char *cur = URL;
425 if (ctxt->protocol == NULL)
427 if (ctxt->hostname == NULL)
431 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
433 if (strcmp(ctxt->protocol, buf))
439 buf[indx++] = *cur++;
446 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
447 (!strchr (cur, '[') && strchr (cur, ']'))) {
448 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
455 while (cur[0] != ']')
456 buf[indx++] = *cur++;
458 if (!strchr (buf, ':')) {
459 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
460 "Use [IPv6]/IPv4 format\n");
465 if (strcmp (ctxt->hostname, buf))
471 while (*cur >= '0' && *cur <= '9') {
477 if (port != ctxt->port)
479 while ((cur[0] != '/') && (*cur != 0))
487 if (strcmp (ctxt->hostname, buf))
491 while ((*cur >= '0') && (*cur <= '9')) {
496 if (port != ctxt->port)
498 while ((cur[0] != '/') && (*cur != 0))
502 if ((*cur == '/') || (*cur == 0)) {
504 if (strcmp (ctxt->hostname, buf))
510 buf[indx++] = *cur++;
512 if (ctxt->path != NULL) {
518 ctxt->path = xmlMemStrdup("/");
523 buf[indx++] = *cur++;
525 ctxt->path = xmlMemStrdup(buf);
531 * xmlNanoFTPScanProxy:
532 * @URL: The proxy URL used to initialize the proxy context
534 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
535 * the protocol host port it indicates.
536 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
537 * A NULL URL cleans up proxy informations.
541 xmlNanoFTPScanProxy(const char *URL) {
542 const char *cur = URL;
551 if (proxyPort != 0) {
556 xmlGenericError(xmlGenericErrorContext, "Removing FTP proxy info\n");
558 xmlGenericError(xmlGenericErrorContext, "Using FTP proxy %s\n", URL);
560 if (URL == NULL) return;
563 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
569 buf[indx++] = *cur++;
571 if (*cur == 0) return;
575 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
576 (!strchr (cur, '[') && strchr (cur, ']'))) {
577 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
584 while (cur[0] != ']')
585 buf[indx++] = *cur++;
586 if (!strchr (buf, ':')) {
587 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
588 "Use [IPv6]/IPv4 format\n");
593 proxy = xmlMemStrdup (buf);
598 while (*cur >= '0' && *cur <= '9') {
604 if (port != 0) proxyPort = port;
605 while ((cur[0] != '/') && (*cur != 0))
613 proxy = xmlMemStrdup (buf);
616 while ((*cur >= '0') && (*cur <= '9')) {
621 if (port != 0) proxyPort = port;
622 while ((cur[0] != '/') && (*cur != 0))
626 if ((*cur == '/') || (*cur == 0)) {
628 proxy = xmlMemStrdup (buf);
633 buf[indx++] = *cur++;
639 * @URL: The URL used to initialize the context
641 * Allocate and initialize a new FTP context.
643 * Returns an FTP context or NULL in case of error.
647 xmlNanoFTPNewCtxt(const char *URL) {
648 xmlNanoFTPCtxtPtr ret;
651 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
652 if (ret == NULL) return(NULL);
654 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
657 ret->returnValue = 0;
658 ret->controlBufIndex = 0;
659 ret->controlBufUsed = 0;
662 unescaped = xmlURIUnescapeString(URL, 0, NULL);
663 if (unescaped != NULL)
664 xmlNanoFTPScanURL(ret, unescaped);
665 else if (URL != NULL)
666 xmlNanoFTPScanURL(ret, URL);
673 * xmlNanoFTPFreeCtxt:
674 * @ctx: an FTP context
676 * Frees the context after closing the connection.
680 xmlNanoFTPFreeCtxt(void * ctx) {
681 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
682 if (ctxt == NULL) return;
683 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
684 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
685 if (ctxt->path != NULL) xmlFree(ctxt->path);
687 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
688 ctxt->controlFd = -1;
689 ctxt->controlBufIndex = -1;
690 ctxt->controlBufUsed = -1;
695 * xmlNanoFTPParseResponse:
696 * @buf: the buffer containing the response
697 * @len: the buffer length
699 * Parsing of the server answer, we just extract the code.
701 * returns 0 for errors
702 * +XXX for last line of response
703 * -XXX for response to be continued
706 xmlNanoFTPParseResponse(char *buf, int len) {
709 if (len < 3) return(-1);
710 if ((*buf >= '0') && (*buf <= '9'))
711 val = val * 10 + (*buf - '0');
715 if ((*buf >= '0') && (*buf <= '9'))
716 val = val * 10 + (*buf - '0');
720 if ((*buf >= '0') && (*buf <= '9'))
721 val = val * 10 + (*buf - '0');
732 * @ctx: an FTP context
734 * Read more information from the FTP control connection
735 * Returns the number of bytes read, < 0 indicates an error
738 xmlNanoFTPGetMore(void *ctx) {
739 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
743 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
745 xmlGenericError(xmlGenericErrorContext,
746 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
747 ctxt->controlBufIndex);
752 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
754 xmlGenericError(xmlGenericErrorContext,
755 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
756 ctxt->controlBufUsed);
760 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
762 xmlGenericError(xmlGenericErrorContext,
763 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
764 ctxt->controlBufIndex, ctxt->controlBufUsed);
770 * First pack the control buffer
772 if (ctxt->controlBufIndex > 0) {
773 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
774 ctxt->controlBufUsed - ctxt->controlBufIndex);
775 ctxt->controlBufUsed -= ctxt->controlBufIndex;
776 ctxt->controlBufIndex = 0;
778 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
781 xmlGenericError(xmlGenericErrorContext,
782 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
788 * Read the amount left on the control connection
790 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
792 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
793 ctxt->controlFd = -1;
797 xmlGenericError(xmlGenericErrorContext,
798 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
799 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
801 ctxt->controlBufUsed += len;
802 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
808 * xmlNanoFTPReadResponse:
809 * @ctx: an FTP context
811 * Read the response from the FTP server after a command.
812 * Returns the code number
815 xmlNanoFTPReadResponse(void *ctx) {
816 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
819 int res = -1, cur = -1;
823 * Assumes everything up to controlBuf[controlBufIndex] has been read
826 len = xmlNanoFTPGetMore(ctx);
830 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
833 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
834 end = &ctxt->controlBuf[ctxt->controlBufUsed];
837 xmlGenericError(xmlGenericErrorContext,
838 "\n<<<\n%s\n--\n", ptr);
841 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
844 * Successfully scanned the control code, scratch
845 * till the end of the line, but keep the index to be
846 * able to analyze the result if needed.
850 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
851 while ((ptr < end) && (*ptr != '\n')) ptr++;
852 if (*ptr == '\n') ptr++;
853 if (*ptr == '\r') ptr++;
856 while ((ptr < end) && (*ptr != '\n')) ptr++;
858 ctxt->controlBufIndex = ctxt->controlBufUsed;
861 if (*ptr != '\r') ptr++;
864 if (res < 0) goto get_more;
865 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
867 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
868 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
872 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
878 * xmlNanoFTPGetResponse:
879 * @ctx: an FTP context
881 * Get the response from the FTP server after a command.
882 * Returns the code number
886 xmlNanoFTPGetResponse(void *ctx) {
889 res = xmlNanoFTPReadResponse(ctx);
895 * xmlNanoFTPCheckResponse:
896 * @ctx: an FTP context
898 * Check if there is a response from the FTP server after a command.
899 * Returns the code number, or 0
903 xmlNanoFTPCheckResponse(void *ctx) {
904 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
911 FD_SET(ctxt->controlFd, &rfd);
912 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
923 return(xmlNanoFTPReadResponse(ctx));
927 * Send the user authentication
931 xmlNanoFTPSendUser(void *ctx) {
932 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
937 if (ctxt->user == NULL)
938 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
940 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
941 buf[sizeof(buf) - 1] = 0;
944 xmlGenericError(xmlGenericErrorContext, "%s", buf);
946 res = send(ctxt->controlFd, buf, len, 0);
947 if (res < 0) return(res);
952 * Send the password authentication
956 xmlNanoFTPSendPasswd(void *ctx) {
957 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
962 if (ctxt->passwd == NULL)
963 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
965 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
966 buf[sizeof(buf) - 1] = 0;
969 xmlGenericError(xmlGenericErrorContext, "%s", buf);
971 res = send(ctxt->controlFd, buf, len, 0);
972 if (res < 0) return(res);
978 * @ctx: an FTP context
980 * Send a QUIT command to the server
982 * Returns -1 in case of error, 0 otherwise
987 xmlNanoFTPQuit(void *ctx) {
988 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
992 snprintf(buf, sizeof(buf), "QUIT\r\n");
995 xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
997 send(ctxt->controlFd, buf, len, 0);
1002 * xmlNanoFTPConnect:
1003 * @ctx: an FTP context
1005 * Tries to open a control connection
1007 * Returns -1 in case of error, 0 otherwise
1011 xmlNanoFTPConnect(void *ctx) {
1012 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1016 int addrlen = sizeof (struct sockaddr_in);
1020 if (ctxt->hostname == NULL)
1024 * do the blocking DNS query.
1034 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
1038 struct addrinfo hints, *tmp, *result;
1041 memset (&hints, 0, sizeof(hints));
1042 hints.ai_socktype = SOCK_STREAM;
1045 if (getaddrinfo (proxy, NULL, &hints, &result) != 0)
1049 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0)
1052 for (tmp = result; tmp; tmp = tmp->ai_next)
1053 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
1058 freeaddrinfo (result);
1062 if (tmp->ai_family == AF_INET6) {
1063 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
1064 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
1065 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
1068 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
1069 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
1070 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1072 addrlen = tmp->ai_addrlen;
1073 freeaddrinfo (result);
1080 hp = gethostbyname (proxy);
1082 hp = gethostbyname (ctxt->hostname);
1087 * Prepare the socket
1089 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
1090 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
1091 hp->h_addr_list[0], hp->h_length);
1092 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = htons (port);
1093 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1094 addrlen = sizeof (struct sockaddr_in);
1097 if (ctxt->controlFd < 0)
1103 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
1105 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1106 ctxt->controlFd = -1;
1111 * Wait for the HELLO from the server.
1113 res = xmlNanoFTPGetResponse(ctxt);
1115 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1116 ctxt->controlFd = -1;
1121 * State diagram for the login operation on the FTP server
1123 * Reference: RFC 959
1126 * +---+ USER +---+------------->+---+
1127 * | B |---------->| W | 2 ---->| E |
1128 * +---+ +---+------ | -->+---+
1131 * -------------- ----- | | |
1137 * +---+ PASS +---+ 2 | ------>+---+
1138 * | |---------->| W |------------->| S |
1139 * +---+ +---+ ---------->+---+
1142 * -------------- -------- |
1148 * +---+ ACCT +---+-- | ----->+---+
1149 * | |---------->| W | 4,5 -------->| F |
1150 * +---+ +---+------------->+---+
1152 * Of course in case of using a proxy this get really nasty and is not
1153 * standardized at all :-(
1159 if (proxyUser != NULL) {
1161 * We need proxy auth
1163 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
1164 buf[sizeof(buf) - 1] = 0;
1167 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1169 res = send(ctxt->controlFd, buf, len, 0);
1171 closesocket(ctxt->controlFd);
1172 ctxt->controlFd = -1;
1175 res = xmlNanoFTPGetResponse(ctxt);
1178 if (proxyPasswd == NULL)
1181 if (proxyPasswd != NULL)
1182 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
1184 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1185 buf[sizeof(buf) - 1] = 0;
1188 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1190 res = send(ctxt->controlFd, buf, len, 0);
1192 closesocket(ctxt->controlFd);
1193 ctxt->controlFd = -1;
1196 res = xmlNanoFTPGetResponse(ctxt);
1198 closesocket(ctxt->controlFd);
1199 ctxt->controlFd = -1;
1209 closesocket(ctxt->controlFd);
1210 ctxt->controlFd = -1;
1216 * We assume we don't need more authentication to the proxy
1217 * and that it succeeded :-\
1219 switch (proxyType) {
1221 /* we will try in sequence */
1223 /* Using SITE command */
1224 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
1225 buf[sizeof(buf) - 1] = 0;
1228 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1230 res = send(ctxt->controlFd, buf, len, 0);
1232 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1233 ctxt->controlFd = -1;
1236 res = xmlNanoFTPGetResponse(ctxt);
1238 /* we assume it worked :-\ 1 is error for SITE command */
1242 if (proxyType == 1) {
1243 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1244 ctxt->controlFd = -1;
1248 /* USER user@host command */
1249 if (ctxt->user == NULL)
1250 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1253 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1254 ctxt->user, ctxt->hostname);
1255 buf[sizeof(buf) - 1] = 0;
1258 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1260 res = send(ctxt->controlFd, buf, len, 0);
1262 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1263 ctxt->controlFd = -1;
1266 res = xmlNanoFTPGetResponse(ctxt);
1267 if ((res == 1) || (res == 2)) {
1268 /* we assume it worked :-\ */
1272 if (ctxt->passwd == NULL)
1273 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1275 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
1276 buf[sizeof(buf) - 1] = 0;
1279 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1281 res = send(ctxt->controlFd, buf, len, 0);
1283 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1284 ctxt->controlFd = -1;
1287 res = xmlNanoFTPGetResponse(ctxt);
1288 if ((res == 1) || (res == 2)) {
1289 /* we assume it worked :-\ */
1293 if (proxyType == 2) {
1294 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1295 ctxt->controlFd = -1;
1300 * If you need support for other Proxy authentication scheme
1301 * send the code or at least the sequence in use.
1304 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1305 ctxt->controlFd = -1;
1310 * Non-proxy handling.
1312 res = xmlNanoFTPSendUser(ctxt);
1314 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1315 ctxt->controlFd = -1;
1318 res = xmlNanoFTPGetResponse(ctxt);
1329 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1330 ctxt->controlFd = -1;
1333 res = xmlNanoFTPSendPasswd(ctxt);
1335 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1336 ctxt->controlFd = -1;
1339 res = xmlNanoFTPGetResponse(ctxt);
1344 xmlGenericError(xmlGenericErrorContext,
1345 "FTP server asking for ACCNT on anonymous\n");
1351 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1352 ctxt->controlFd = -1;
1360 * xmlNanoFTPConnectTo:
1361 * @server: an FTP server name
1362 * @port: the port (use 21 if 0)
1364 * Tries to open a control connection to the given server/port
1366 * Returns an fTP context or NULL if it failed
1370 xmlNanoFTPConnectTo(const char *server, int port) {
1371 xmlNanoFTPCtxtPtr ctxt;
1377 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1378 ctxt->hostname = xmlMemStrdup(server);
1381 res = xmlNanoFTPConnect(ctxt);
1383 xmlNanoFTPFreeCtxt(ctxt);
1391 * @ctx: an FTP context
1392 * @directory: a directory on the server
1394 * Tries to change the remote directory
1396 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1400 xmlNanoFTPCwd(void *ctx, char *directory) {
1401 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1407 * Expected response code for CWD:
1411 * 500, 501, 502, 421, 530, 550
1413 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
1414 buf[sizeof(buf) - 1] = 0;
1417 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1419 res = send(ctxt->controlFd, buf, len, 0);
1420 if (res < 0) return(res);
1421 res = xmlNanoFTPGetResponse(ctxt);
1425 if (res == 2) return(1);
1434 * @ctx: an FTP context
1435 * @file: a file or directory on the server
1437 * Tries to delete an item (file or directory) from server
1439 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1443 xmlNanoFTPDele(void *ctx, char *file) {
1444 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1450 * Expected response code for DELE:
1455 * 500, 501, 502, 421, 530
1458 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1459 buf[sizeof(buf) - 1] = 0;
1462 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1464 res = send(ctxt->controlFd, buf, len, 0);
1465 if (res < 0) return(res);
1466 res = xmlNanoFTPGetResponse(ctxt);
1470 if (res == 2) return(1);
1477 * xmlNanoFTPGetConnection:
1478 * @ctx: an FTP context
1480 * Try to open a data connection to the server. Currently only
1481 * passive mode is supported.
1483 * Returns -1 incase of error, 0 otherwise
1487 xmlNanoFTPGetConnection(void *ctx) {
1488 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1489 char buf[200], *cur;
1492 unsigned char ad[6], *adp, *portp;
1493 unsigned int temp[6];
1495 struct sockaddr_storage dataAddr;
1497 struct sockaddr_in dataAddr;
1499 SOCKLEN_T dataAddrLen;
1501 memset (&dataAddr, 0, sizeof(dataAddr));
1503 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1504 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1505 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1506 dataAddrLen = sizeof(struct sockaddr_in6);
1510 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1511 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1512 dataAddrLen = sizeof (struct sockaddr_in);
1515 if (ctxt->dataFd < 0) {
1516 xmlGenericError (xmlGenericErrorContext,
1517 "xmlNanoFTPGetConnection: failed to create socket\n");
1521 if (ctxt->passive) {
1523 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1524 snprintf (buf, sizeof(buf), "EPSV\r\n");
1527 snprintf (buf, sizeof(buf), "PASV\r\n");
1530 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1532 res = send(ctxt->controlFd, buf, len, 0);
1534 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1537 res = xmlNanoFTPReadResponse(ctx);
1540 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1544 * retry with an active connection
1546 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1550 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1551 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1553 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1554 if (sscanf (cur, "%u", &temp[0]) != 1) {
1555 xmlGenericError (xmlGenericErrorContext,
1556 "Invalid answer to EPSV\n");
1557 if (ctxt->dataFd != -1) {
1558 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1562 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1563 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
1568 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1569 &temp[3], &temp[4], &temp[5]) != 6) {
1570 xmlGenericError (xmlGenericErrorContext,
1571 "Invalid answer to PASV\n");
1572 if (ctxt->dataFd != -1) {
1573 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1577 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1578 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1579 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1582 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1583 xmlGenericError(xmlGenericErrorContext,
1584 "Failed to create a data connection\n");
1585 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1589 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1591 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1592 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1595 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1597 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1598 xmlGenericError(xmlGenericErrorContext,
1599 "Failed to bind a port\n");
1600 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1603 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1605 if (listen(ctxt->dataFd, 1) < 0) {
1607 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1608 xmlGenericError (xmlGenericErrorContext,
1609 "Could not listen on port %d\n",
1610 ntohs (((struct sockaddr_in6 *)&dataAddr)->sin6_port));
1613 xmlGenericError (xmlGenericErrorContext,
1614 "Could not listen on port %d\n",
1615 ntohs (((struct sockaddr_in *)&dataAddr)->sin_port));
1616 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1620 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1621 char buf6[INET6_ADDRSTRLEN];
1622 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1623 buf6, INET6_ADDRSTRLEN);
1624 adp = (unsigned char *) buf6;
1625 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1626 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1630 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1631 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1632 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1633 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1634 portp[0] & 0xff, portp[1] & 0xff);
1637 buf[sizeof(buf) - 1] = 0;
1640 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1643 res = send(ctxt->controlFd, buf, len, 0);
1645 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1648 res = xmlNanoFTPGetResponse(ctxt);
1650 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1654 return(ctxt->dataFd);
1659 * xmlNanoFTPCloseConnection:
1660 * @ctx: an FTP context
1662 * Close the data connection from the server
1664 * Returns -1 incase of error, 0 otherwise
1668 xmlNanoFTPCloseConnection(void *ctx) {
1669 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1674 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1678 FD_SET(ctxt->controlFd, &rfd);
1680 FD_SET(ctxt->controlFd, &efd);
1681 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1686 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1691 xmlGenericError(xmlGenericErrorContext,
1692 "xmlNanoFTPCloseConnection: timeout\n");
1694 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1696 res = xmlNanoFTPGetResponse(ctxt);
1698 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1706 * xmlNanoFTPParseList:
1707 * @list: some data listing received from the server
1708 * @callback: the user callback
1709 * @userData: the user callback data
1711 * Parse at most one entry from the listing.
1713 * Returns -1 incase of error, the length of data parsed otherwise
1717 xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1718 const char *cur = list;
1728 unsigned long size = 0;
1732 if (!strncmp(cur, "total", 5)) {
1734 while (*cur == ' ') cur++;
1735 while ((*cur >= '0') && (*cur <= '9'))
1736 links = (links * 10) + (*cur++ - '0');
1737 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1740 } else if (*list == '+') {
1743 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1745 if (*cur == 0) return(0);
1747 while (*cur != ' ') {
1751 if (*cur == 0) return(0);
1754 while (*cur == ' ') cur++;
1755 if (*cur == 0) return(0);
1756 while ((*cur >= '0') && (*cur <= '9'))
1757 links = (links * 10) + (*cur++ - '0');
1758 while (*cur == ' ') cur++;
1759 if (*cur == 0) return(0);
1761 while (*cur != ' ') {
1765 if (*cur == 0) return(0);
1768 while (*cur == ' ') cur++;
1769 if (*cur == 0) return(0);
1771 while (*cur != ' ') {
1775 if (*cur == 0) return(0);
1778 while (*cur == ' ') cur++;
1779 if (*cur == 0) return(0);
1780 while ((*cur >= '0') && (*cur <= '9'))
1781 size = (size * 10) + (*cur++ - '0');
1782 while (*cur == ' ') cur++;
1783 if (*cur == 0) return(0);
1785 while (*cur != ' ') {
1789 if (*cur == 0) return(0);
1792 while (*cur == ' ') cur++;
1793 if (*cur == 0) return(0);
1794 while ((*cur >= '0') && (*cur <= '9'))
1795 day = (day * 10) + (*cur++ - '0');
1796 while (*cur == ' ') cur++;
1797 if (*cur == 0) return(0);
1798 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1799 if ((cur[1] == ':') || (cur[2] == ':')) {
1800 while ((*cur >= '0') && (*cur <= '9'))
1801 hour = (hour * 10) + (*cur++ - '0');
1802 if (*cur == ':') cur++;
1803 while ((*cur >= '0') && (*cur <= '9'))
1804 minute = (minute * 10) + (*cur++ - '0');
1806 while ((*cur >= '0') && (*cur <= '9'))
1807 year = (year * 10) + (*cur++ - '0');
1809 while (*cur == ' ') cur++;
1810 if (*cur == 0) return(0);
1812 while ((*cur != '\n') && (*cur != '\r')) {
1814 filename[i++] = *cur;
1816 if (*cur == 0) return(0);
1819 if ((*cur != '\n') && (*cur != '\r'))
1821 while ((*cur == '\n') || (*cur == '\r'))
1824 if (callback != NULL) {
1825 callback(userData, filename, attrib, owner, group, size, links,
1826 year, month, day, hour, minute);
1833 * @ctx: an FTP context
1834 * @callback: the user callback
1835 * @userData: the user callback data
1836 * @filename: optional files to list
1838 * Do a listing on the server. All files info are passed back
1841 * Returns -1 incase of error, 0 otherwise
1845 xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1847 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1854 if (filename == NULL) {
1855 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1857 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1858 if (ctxt->dataFd == -1)
1860 snprintf(buf, sizeof(buf), "LIST -L\r\n");
1862 if (filename[0] != '/') {
1863 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1866 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1867 if (ctxt->dataFd == -1)
1869 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
1871 buf[sizeof(buf) - 1] = 0;
1874 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1876 res = send(ctxt->controlFd, buf, len, 0);
1878 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1881 res = xmlNanoFTPReadResponse(ctxt);
1883 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1891 FD_SET(ctxt->dataFd, &rfd);
1893 FD_SET(ctxt->dataFd, &efd);
1894 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1899 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1903 res = xmlNanoFTPCheckResponse(ctxt);
1905 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1910 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1917 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
1921 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1926 write(1, &buf[indx], len);
1932 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1936 memmove(&buf[0], &buf[base], indx - base);
1939 xmlNanoFTPCloseConnection(ctxt);
1944 * xmlNanoFTPGetSocket:
1945 * @ctx: an FTP context
1946 * @filename: the file to retrieve (or NULL if path is in context).
1948 * Initiate fetch of the given file from the server.
1950 * Returns the socket for the data connection, or <0 in case of error
1955 xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1956 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1959 if ((filename == NULL) && (ctxt->path == NULL))
1961 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1962 if (ctxt->dataFd == -1)
1965 snprintf(buf, sizeof(buf), "TYPE I\r\n");
1968 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1970 res = send(ctxt->controlFd, buf, len, 0);
1972 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1975 res = xmlNanoFTPReadResponse(ctxt);
1977 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1980 if (filename == NULL)
1981 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
1983 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
1984 buf[sizeof(buf) - 1] = 0;
1987 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1989 res = send(ctxt->controlFd, buf, len, 0);
1991 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1994 res = xmlNanoFTPReadResponse(ctxt);
1996 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1999 return(ctxt->dataFd);
2004 * @ctx: an FTP context
2005 * @callback: the user callback
2006 * @userData: the user callback data
2007 * @filename: the file to retrieve
2009 * Fetch the given file from the server. All data are passed back
2010 * in the callbacks. The last callback has a size of 0 block.
2012 * Returns -1 incase of error, 0 otherwise
2016 xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
2017 const char *filename) {
2018 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2024 if ((filename == NULL) && (ctxt->path == NULL))
2026 if (callback == NULL)
2028 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
2035 FD_SET(ctxt->dataFd, &rfd);
2036 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
2041 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2045 res = xmlNanoFTPCheckResponse(ctxt);
2047 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2052 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2058 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
2059 callback(userData, buf, len);
2060 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2063 callback(userData, buf, len);
2066 return(xmlNanoFTPCloseConnection(ctxt));
2071 * @ctx: the FTP context
2073 * @len: the buffer length
2075 * This function tries to read @len bytes from the existing FTP connection
2076 * and saves them in @dest. This is a blocking call.
2078 * Returns the number of byte read. 0 is an indication of an end of connection.
2079 * -1 indicates a parameter error.
2082 xmlNanoFTPRead(void *ctx, void *dest, int len) {
2083 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2085 if (ctx == NULL) return(-1);
2086 if (ctxt->dataFd < 0) return(0);
2087 if (dest == NULL) return(-1);
2088 if (len <= 0) return(0);
2090 len = recv(ctxt->dataFd, dest, len, 0);
2092 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
2095 xmlNanoFTPCloseConnection(ctxt);
2102 * @URL: the URL to the resource
2104 * Start to fetch the given ftp:// resource
2106 * Returns an FTP context, or NULL
2110 xmlNanoFTPOpen(const char *URL) {
2111 xmlNanoFTPCtxtPtr ctxt;
2115 if (URL == NULL) return(NULL);
2116 if (strncmp("ftp://", URL, 6)) return(NULL);
2118 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2119 if (ctxt == NULL) return(NULL);
2120 if (xmlNanoFTPConnect(ctxt) < 0) {
2121 xmlNanoFTPFreeCtxt(ctxt);
2124 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2126 xmlNanoFTPFreeCtxt(ctxt);
2134 * @ctx: an FTP context
2136 * Close the connection and both control and transport
2138 * Returns -1 incase of error, 0 otherwise
2142 xmlNanoFTPClose(void *ctx) {
2143 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2148 if (ctxt->dataFd >= 0) {
2149 closesocket(ctxt->dataFd);
2152 if (ctxt->controlFd >= 0) {
2153 xmlNanoFTPQuit(ctxt);
2154 closesocket(ctxt->controlFd);
2155 ctxt->controlFd = -1;
2157 xmlNanoFTPFreeCtxt(ctxt);
2162 /************************************************************************
2164 * Basic test in Standalone mode *
2166 ************************************************************************/
2168 void ftpList(void *userData, const char *filename, const char* attrib,
2169 const char *owner, const char *group, unsigned long size, int links,
2170 int year, const char *month, int day, int hour, int minute) {
2171 xmlGenericError(xmlGenericErrorContext,
2172 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2175 void ftpData(void *userData, const char *data, int len) {
2176 if (userData == NULL) return;
2181 fwrite(data, len, 1, userData);
2184 int main(int argc, char **argv) {
2187 char *tstfile = NULL;
2191 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2192 if (xmlNanoFTPConnect(ctxt) < 0) {
2193 xmlGenericError(xmlGenericErrorContext,
2194 "Couldn't connect to %s\n", argv[1]);
2200 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2202 xmlGenericError(xmlGenericErrorContext,
2203 "Couldn't connect to localhost\n");
2206 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2207 output = fopen("/tmp/tstdata", "w");
2208 if (output != NULL) {
2209 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2210 xmlGenericError(xmlGenericErrorContext,
2211 "Failed to get file\n");
2214 xmlNanoFTPClose(ctxt);
2218 #endif /* STANDALONE */
2219 #else /* !LIBXML_FTP_ENABLED */
2222 int main(int argc, char **argv) {
2223 xmlGenericError(xmlGenericErrorContext,
2224 "%s : FTP support not compiled in\n", argv[0]);
2227 #endif /* STANDALONE */
2228 #endif /* LIBXML_FTP_ENABLED */