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