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