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, SEND_ARG2_CAST 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, SEND_ARG2_CAST 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, SEND_ARG2_CAST 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 if (result) 912 freeaddrinfo (result); 913 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch"); 914 return (-1); 915 } 916 if (tmp->ai_family == AF_INET6) { 917 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen); 918 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port); 919 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0); 920 } 921 else { 922 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen); 923 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port); 924 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0); 925 } 926 addrlen = tmp->ai_addrlen; 927 freeaddrinfo (result); 928 } 929 else 930#endif 931 { 932 if (proxy) 933 hp = gethostbyname (GETHOSTBYNAME_ARG_CAST proxy); 934 else 935 hp = gethostbyname (GETHOSTBYNAME_ARG_CAST ctxt->hostname); 936 if (hp == NULL) { 937 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed"); 938 return (-1); 939 } 940 if ((unsigned int) hp->h_length > 941 sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) { 942 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch"); 943 return (-1); 944 } 945 946 /* 947 * Prepare the socket 948 */ 949 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET; 950 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr, 951 hp->h_addr_list[0], hp->h_length); 952 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = 953 (unsigned short)htons ((unsigned short)port); 954 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0); 955 addrlen = sizeof (struct sockaddr_in); 956 } 957 958 if (ctxt->controlFd == INVALID_SOCKET) { 959 __xmlIOErr(XML_FROM_FTP, 0, "socket failed"); 960 return(-1); 961 } 962 963 /* 964 * Do the connect. 965 */ 966 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr, 967 addrlen) < 0) { 968 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection"); 969 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 970 ctxt->controlFd = INVALID_SOCKET; 971 return(-1); 972 } 973 974 /* 975 * Wait for the HELLO from the server. 976 */ 977 res = xmlNanoFTPGetResponse(ctxt); 978 if (res != 2) { 979 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 980 ctxt->controlFd = INVALID_SOCKET; 981 return(-1); 982 } 983 984 /* 985 * State diagram for the login operation on the FTP server 986 * 987 * Reference: RFC 959 988 * 989 * 1 990 * +---+ USER +---+------------->+---+ 991 * | B |---------->| W | 2 ---->| E | 992 * +---+ +---+------ | -->+---+ 993 * | | | | | 994 * 3 | | 4,5 | | | 995 * -------------- ----- | | | 996 * | | | | | 997 * | | | | | 998 * | --------- | 999 * | 1| | | | 1000 * V | | | | 1001 * +---+ PASS +---+ 2 | ------>+---+ 1002 * | |---------->| W |------------->| S | 1003 * +---+ +---+ ---------->+---+ 1004 * | | | | | 1005 * 3 | |4,5| | | 1006 * -------------- -------- | 1007 * | | | | | 1008 * | | | | | 1009 * | ----------- 1010 * | 1,3| | | | 1011 * V | 2| | | 1012 * +---+ ACCT +---+-- | ----->+---+ 1013 * | |---------->| W | 4,5 -------->| F | 1014 * +---+ +---+------------->+---+ 1015 * 1016 * Of course in case of using a proxy this get really nasty and is not 1017 * standardized at all :-( 1018 */ 1019 if (proxy) { 1020 int len; 1021 char buf[400]; 1022 1023 if (proxyUser != NULL) { 1024 /* 1025 * We need proxy auth 1026 */ 1027 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser); 1028 buf[sizeof(buf) - 1] = 0; 1029 len = strlen(buf); 1030#ifdef DEBUG_FTP 1031 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1032#endif 1033 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1034 if (res < 0) { 1035 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1036 closesocket(ctxt->controlFd); 1037 ctxt->controlFd = INVALID_SOCKET; 1038 return(res); 1039 } 1040 res = xmlNanoFTPGetResponse(ctxt); 1041 switch (res) { 1042 case 2: 1043 if (proxyPasswd == NULL) 1044 break; 1045 case 3: 1046 if (proxyPasswd != NULL) 1047 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd); 1048 else 1049 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n"); 1050 buf[sizeof(buf) - 1] = 0; 1051 len = strlen(buf); 1052#ifdef DEBUG_FTP 1053 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1054#endif 1055 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1056 if (res < 0) { 1057 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1058 closesocket(ctxt->controlFd); 1059 ctxt->controlFd = INVALID_SOCKET; 1060 return(res); 1061 } 1062 res = xmlNanoFTPGetResponse(ctxt); 1063 if (res > 3) { 1064 closesocket(ctxt->controlFd); 1065 ctxt->controlFd = INVALID_SOCKET; 1066 return(-1); 1067 } 1068 break; 1069 case 1: 1070 break; 1071 case 4: 1072 case 5: 1073 case -1: 1074 default: 1075 closesocket(ctxt->controlFd); 1076 ctxt->controlFd = INVALID_SOCKET; 1077 return(-1); 1078 } 1079 } 1080 1081 /* 1082 * We assume we don't need more authentication to the proxy 1083 * and that it succeeded :-\ 1084 */ 1085 switch (proxyType) { 1086 case 0: 1087 /* we will try in sequence */ 1088 case 1: 1089 /* Using SITE command */ 1090 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname); 1091 buf[sizeof(buf) - 1] = 0; 1092 len = strlen(buf); 1093#ifdef DEBUG_FTP 1094 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1095#endif 1096 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1097 if (res < 0) { 1098 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1099 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1100 ctxt->controlFd = INVALID_SOCKET; 1101 return(res); 1102 } 1103 res = xmlNanoFTPGetResponse(ctxt); 1104 if (res == 2) { 1105 /* we assume it worked :-\ 1 is error for SITE command */ 1106 proxyType = 1; 1107 break; 1108 } 1109 if (proxyType == 1) { 1110 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1111 ctxt->controlFd = INVALID_SOCKET; 1112 return(-1); 1113 } 1114 case 2: 1115 /* USER user@host command */ 1116 if (ctxt->user == NULL) 1117 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n", 1118 ctxt->hostname); 1119 else 1120 snprintf(buf, sizeof(buf), "USER %s@%s\r\n", 1121 ctxt->user, ctxt->hostname); 1122 buf[sizeof(buf) - 1] = 0; 1123 len = strlen(buf); 1124#ifdef DEBUG_FTP 1125 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1126#endif 1127 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1128 if (res < 0) { 1129 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1130 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1131 ctxt->controlFd = INVALID_SOCKET; 1132 return(res); 1133 } 1134 res = xmlNanoFTPGetResponse(ctxt); 1135 if ((res == 1) || (res == 2)) { 1136 /* we assume it worked :-\ */ 1137 proxyType = 2; 1138 return(0); 1139 } 1140 if (ctxt->passwd == NULL) 1141 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n"); 1142 else 1143 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd); 1144 buf[sizeof(buf) - 1] = 0; 1145 len = strlen(buf); 1146#ifdef DEBUG_FTP 1147 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1148#endif 1149 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1150 if (res < 0) { 1151 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1152 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1153 ctxt->controlFd = INVALID_SOCKET; 1154 return(res); 1155 } 1156 res = xmlNanoFTPGetResponse(ctxt); 1157 if ((res == 1) || (res == 2)) { 1158 /* we assume it worked :-\ */ 1159 proxyType = 2; 1160 return(0); 1161 } 1162 if (proxyType == 2) { 1163 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1164 ctxt->controlFd = INVALID_SOCKET; 1165 return(-1); 1166 } 1167 case 3: 1168 /* 1169 * If you need support for other Proxy authentication scheme 1170 * send the code or at least the sequence in use. 1171 */ 1172 default: 1173 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1174 ctxt->controlFd = INVALID_SOCKET; 1175 return(-1); 1176 } 1177 } 1178 /* 1179 * Non-proxy handling. 1180 */ 1181 res = xmlNanoFTPSendUser(ctxt); 1182 if (res < 0) { 1183 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1184 ctxt->controlFd = INVALID_SOCKET; 1185 return(-1); 1186 } 1187 res = xmlNanoFTPGetResponse(ctxt); 1188 switch (res) { 1189 case 2: 1190 return(0); 1191 case 3: 1192 break; 1193 case 1: 1194 case 4: 1195 case 5: 1196 case -1: 1197 default: 1198 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1199 ctxt->controlFd = INVALID_SOCKET; 1200 return(-1); 1201 } 1202 res = xmlNanoFTPSendPasswd(ctxt); 1203 if (res < 0) { 1204 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1205 ctxt->controlFd = INVALID_SOCKET; 1206 return(-1); 1207 } 1208 res = xmlNanoFTPGetResponse(ctxt); 1209 switch (res) { 1210 case 2: 1211 break; 1212 case 3: 1213 __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT, 1214 "FTP server asking for ACCNT on anonymous\n"); 1215 case 1: 1216 case 4: 1217 case 5: 1218 case -1: 1219 default: 1220 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1221 ctxt->controlFd = INVALID_SOCKET; 1222 return(-1); 1223 } 1224 1225 return(0); 1226} 1227 1228/** 1229 * xmlNanoFTPConnectTo: 1230 * @server: an FTP server name 1231 * @port: the port (use 21 if 0) 1232 * 1233 * Tries to open a control connection to the given server/port 1234 * 1235 * Returns an fTP context or NULL if it failed 1236 */ 1237 1238void* 1239xmlNanoFTPConnectTo(const char *server, int port) { 1240 xmlNanoFTPCtxtPtr ctxt; 1241 int res; 1242 1243 xmlNanoFTPInit(); 1244 if (server == NULL) 1245 return(NULL); 1246 if (port <= 0) 1247 return(NULL); 1248 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL); 1249 if (ctxt == NULL) 1250 return(NULL); 1251 ctxt->hostname = xmlMemStrdup(server); 1252 if (ctxt->hostname == NULL) { 1253 xmlNanoFTPFreeCtxt(ctxt); 1254 return(NULL); 1255 } 1256 if (port != 0) 1257 ctxt->port = port; 1258 res = xmlNanoFTPConnect(ctxt); 1259 if (res < 0) { 1260 xmlNanoFTPFreeCtxt(ctxt); 1261 return(NULL); 1262 } 1263 return(ctxt); 1264} 1265 1266/** 1267 * xmlNanoFTPCwd: 1268 * @ctx: an FTP context 1269 * @directory: a directory on the server 1270 * 1271 * Tries to change the remote directory 1272 * 1273 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed 1274 */ 1275 1276int 1277xmlNanoFTPCwd(void *ctx, const char *directory) { 1278 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1279 char buf[400]; 1280 int len; 1281 int res; 1282 1283 if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1); 1284 if (directory == NULL) return 0; 1285 1286 /* 1287 * Expected response code for CWD: 1288 * 1289 * CWD 1290 * 250 1291 * 500, 501, 502, 421, 530, 550 1292 */ 1293 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory); 1294 buf[sizeof(buf) - 1] = 0; 1295 len = strlen(buf); 1296#ifdef DEBUG_FTP 1297 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1298#endif 1299 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1300 if (res < 0) { 1301 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1302 return(res); 1303 } 1304 res = xmlNanoFTPGetResponse(ctxt); 1305 if (res == 4) { 1306 return(-1); 1307 } 1308 if (res == 2) return(1); 1309 if (res == 5) { 1310 return(0); 1311 } 1312 return(0); 1313} 1314 1315/** 1316 * xmlNanoFTPDele: 1317 * @ctx: an FTP context 1318 * @file: a file or directory on the server 1319 * 1320 * Tries to delete an item (file or directory) from server 1321 * 1322 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed 1323 */ 1324 1325int 1326xmlNanoFTPDele(void *ctx, const char *file) { 1327 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1328 char buf[400]; 1329 int len; 1330 int res; 1331 1332 if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET) || 1333 (file == NULL)) return(-1); 1334 1335 /* 1336 * Expected response code for DELE: 1337 * 1338 * DELE 1339 * 250 1340 * 450, 550 1341 * 500, 501, 502, 421, 530 1342 */ 1343 1344 snprintf(buf, sizeof(buf), "DELE %s\r\n", file); 1345 buf[sizeof(buf) - 1] = 0; 1346 len = strlen(buf); 1347#ifdef DEBUG_FTP 1348 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1349#endif 1350 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1351 if (res < 0) { 1352 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1353 return(res); 1354 } 1355 res = xmlNanoFTPGetResponse(ctxt); 1356 if (res == 4) { 1357 return(-1); 1358 } 1359 if (res == 2) return(1); 1360 if (res == 5) { 1361 return(0); 1362 } 1363 return(0); 1364} 1365/** 1366 * xmlNanoFTPGetConnection: 1367 * @ctx: an FTP context 1368 * 1369 * Try to open a data connection to the server. Currently only 1370 * passive mode is supported. 1371 * 1372 * Returns -1 incase of error, 0 otherwise 1373 */ 1374 1375SOCKET 1376xmlNanoFTPGetConnection(void *ctx) { 1377 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1378 char buf[200], *cur; 1379 int len, i; 1380 int res; 1381 unsigned char ad[6], *adp, *portp; 1382 unsigned int temp[6]; 1383#ifdef SUPPORT_IP6 1384 struct sockaddr_storage dataAddr; 1385#else 1386 struct sockaddr_in dataAddr; 1387#endif 1388 XML_SOCKLEN_T dataAddrLen; 1389 1390 if (ctxt == NULL) return INVALID_SOCKET; 1391 1392 memset (&dataAddr, 0, sizeof(dataAddr)); 1393#ifdef SUPPORT_IP6 1394 if ((ctxt->ftpAddr).ss_family == AF_INET6) { 1395 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP); 1396 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6; 1397 dataAddrLen = sizeof(struct sockaddr_in6); 1398 } else 1399#endif 1400 { 1401 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 1402 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET; 1403 dataAddrLen = sizeof (struct sockaddr_in); 1404 } 1405 1406 if (ctxt->dataFd == INVALID_SOCKET) { 1407 __xmlIOErr(XML_FROM_FTP, 0, "socket failed"); 1408 return INVALID_SOCKET; 1409 } 1410 1411 if (ctxt->passive) { 1412#ifdef SUPPORT_IP6 1413 if ((ctxt->ftpAddr).ss_family == AF_INET6) 1414 snprintf (buf, sizeof(buf), "EPSV\r\n"); 1415 else 1416#endif 1417 snprintf (buf, sizeof(buf), "PASV\r\n"); 1418 len = strlen (buf); 1419#ifdef DEBUG_FTP 1420 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1421#endif 1422 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1423 if (res < 0) { 1424 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1425 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1426 return INVALID_SOCKET; 1427 } 1428 res = xmlNanoFTPReadResponse(ctx); 1429 if (res != 2) { 1430 if (res == 5) { 1431 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1432 return INVALID_SOCKET; 1433 } else { 1434 /* 1435 * retry with an active connection 1436 */ 1437 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1438 ctxt->passive = 0; 1439 } 1440 } 1441 cur = &ctxt->controlBuf[ctxt->controlBufAnswer]; 1442 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++; 1443#ifdef SUPPORT_IP6 1444 if ((ctxt->ftpAddr).ss_family == AF_INET6) { 1445 if (sscanf (cur, "%u", &temp[0]) != 1) { 1446 __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER, 1447 "Invalid answer to EPSV\n"); 1448 if (ctxt->dataFd != INVALID_SOCKET) { 1449 closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1450 } 1451 return INVALID_SOCKET; 1452 } 1453 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr)); 1454 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]); 1455 } 1456 else 1457#endif 1458 { 1459 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2], 1460 &temp[3], &temp[4], &temp[5]) != 6) { 1461 __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER, 1462 "Invalid answer to PASV\n"); 1463 if (ctxt->dataFd != INVALID_SOCKET) { 1464 closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1465 } 1466 return INVALID_SOCKET; 1467 } 1468 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff); 1469 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4); 1470 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2); 1471 } 1472 1473 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) { 1474 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection"); 1475 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1476 return INVALID_SOCKET; 1477 } 1478 } else { 1479 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen); 1480#ifdef SUPPORT_IP6 1481 if ((ctxt->ftpAddr).ss_family == AF_INET6) 1482 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0; 1483 else 1484#endif 1485 ((struct sockaddr_in *)&dataAddr)->sin_port = 0; 1486 1487 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) { 1488 __xmlIOErr(XML_FROM_FTP, 0, "bind failed"); 1489 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1490 return INVALID_SOCKET; 1491 } 1492 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen); 1493 1494 if (listen(ctxt->dataFd, 1) < 0) { 1495 __xmlIOErr(XML_FROM_FTP, 0, "listen failed"); 1496 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1497 return INVALID_SOCKET; 1498 } 1499#ifdef SUPPORT_IP6 1500 if ((ctxt->ftpAddr).ss_family == AF_INET6) { 1501 char buf6[INET6_ADDRSTRLEN]; 1502 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr, 1503 buf6, INET6_ADDRSTRLEN); 1504 adp = (unsigned char *) buf6; 1505 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port; 1506 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp); 1507 } else 1508#endif 1509 { 1510 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr; 1511 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port; 1512 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n", 1513 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff, 1514 portp[0] & 0xff, portp[1] & 0xff); 1515 } 1516 1517 buf[sizeof(buf) - 1] = 0; 1518 len = strlen(buf); 1519#ifdef DEBUG_FTP 1520 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1521#endif 1522 1523 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1524 if (res < 0) { 1525 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1526 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1527 return INVALID_SOCKET; 1528 } 1529 res = xmlNanoFTPGetResponse(ctxt); 1530 if (res != 2) { 1531 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1532 return INVALID_SOCKET; 1533 } 1534 } 1535 return(ctxt->dataFd); 1536 1537} 1538 1539/** 1540 * xmlNanoFTPCloseConnection: 1541 * @ctx: an FTP context 1542 * 1543 * Close the data connection from the server 1544 * 1545 * Returns -1 incase of error, 0 otherwise 1546 */ 1547 1548int 1549xmlNanoFTPCloseConnection(void *ctx) { 1550 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1551 int res; 1552 fd_set rfd, efd; 1553 struct timeval tv; 1554 1555 if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1); 1556 1557 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1558 tv.tv_sec = 15; 1559 tv.tv_usec = 0; 1560 FD_ZERO(&rfd); 1561 FD_SET(ctxt->controlFd, &rfd); 1562 FD_ZERO(&efd); 1563 FD_SET(ctxt->controlFd, &efd); 1564 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv); 1565 if (res < 0) { 1566#ifdef DEBUG_FTP 1567 perror("select"); 1568#endif 1569 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1570 return(-1); 1571 } 1572 if (res == 0) { 1573#ifdef DEBUG_FTP 1574 xmlGenericError(xmlGenericErrorContext, 1575 "xmlNanoFTPCloseConnection: timeout\n"); 1576#endif 1577 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1578 } else { 1579 res = xmlNanoFTPGetResponse(ctxt); 1580 if (res != 2) { 1581 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1582 return(-1); 1583 } 1584 } 1585 return(0); 1586} 1587 1588/** 1589 * xmlNanoFTPParseList: 1590 * @list: some data listing received from the server 1591 * @callback: the user callback 1592 * @userData: the user callback data 1593 * 1594 * Parse at most one entry from the listing. 1595 * 1596 * Returns -1 incase of error, the length of data parsed otherwise 1597 */ 1598 1599static int 1600xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) { 1601 const char *cur = list; 1602 char filename[151]; 1603 char attrib[11]; 1604 char owner[11]; 1605 char group[11]; 1606 char month[4]; 1607 int year = 0; 1608 int minute = 0; 1609 int hour = 0; 1610 int day = 0; 1611 unsigned long size = 0; 1612 int links = 0; 1613 int i; 1614 1615 if (!strncmp(cur, "total", 5)) { 1616 cur += 5; 1617 while (*cur == ' ') cur++; 1618 while ((*cur >= '0') && (*cur <= '9')) 1619 links = (links * 10) + (*cur++ - '0'); 1620 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r')) 1621 cur++; 1622 return(cur - list); 1623 } else if (*list == '+') { 1624 return(0); 1625 } else { 1626 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r')) 1627 cur++; 1628 if (*cur == 0) return(0); 1629 i = 0; 1630 while (*cur != ' ') { 1631 if (i < 10) 1632 attrib[i++] = *cur; 1633 cur++; 1634 if (*cur == 0) return(0); 1635 } 1636 attrib[10] = 0; 1637 while (*cur == ' ') cur++; 1638 if (*cur == 0) return(0); 1639 while ((*cur >= '0') && (*cur <= '9')) 1640 links = (links * 10) + (*cur++ - '0'); 1641 while (*cur == ' ') cur++; 1642 if (*cur == 0) return(0); 1643 i = 0; 1644 while (*cur != ' ') { 1645 if (i < 10) 1646 owner[i++] = *cur; 1647 cur++; 1648 if (*cur == 0) return(0); 1649 } 1650 owner[i] = 0; 1651 while (*cur == ' ') cur++; 1652 if (*cur == 0) return(0); 1653 i = 0; 1654 while (*cur != ' ') { 1655 if (i < 10) 1656 group[i++] = *cur; 1657 cur++; 1658 if (*cur == 0) return(0); 1659 } 1660 group[i] = 0; 1661 while (*cur == ' ') cur++; 1662 if (*cur == 0) return(0); 1663 while ((*cur >= '0') && (*cur <= '9')) 1664 size = (size * 10) + (*cur++ - '0'); 1665 while (*cur == ' ') cur++; 1666 if (*cur == 0) return(0); 1667 i = 0; 1668 while (*cur != ' ') { 1669 if (i < 3) 1670 month[i++] = *cur; 1671 cur++; 1672 if (*cur == 0) return(0); 1673 } 1674 month[i] = 0; 1675 while (*cur == ' ') cur++; 1676 if (*cur == 0) return(0); 1677 while ((*cur >= '0') && (*cur <= '9')) 1678 day = (day * 10) + (*cur++ - '0'); 1679 while (*cur == ' ') cur++; 1680 if (*cur == 0) return(0); 1681 if ((cur[1] == 0) || (cur[2] == 0)) return(0); 1682 if ((cur[1] == ':') || (cur[2] == ':')) { 1683 while ((*cur >= '0') && (*cur <= '9')) 1684 hour = (hour * 10) + (*cur++ - '0'); 1685 if (*cur == ':') cur++; 1686 while ((*cur >= '0') && (*cur <= '9')) 1687 minute = (minute * 10) + (*cur++ - '0'); 1688 } else { 1689 while ((*cur >= '0') && (*cur <= '9')) 1690 year = (year * 10) + (*cur++ - '0'); 1691 } 1692 while (*cur == ' ') cur++; 1693 if (*cur == 0) return(0); 1694 i = 0; 1695 while ((*cur != '\n') && (*cur != '\r')) { 1696 if (i < 150) 1697 filename[i++] = *cur; 1698 cur++; 1699 if (*cur == 0) return(0); 1700 } 1701 filename[i] = 0; 1702 if ((*cur != '\n') && (*cur != '\r')) 1703 return(0); 1704 while ((*cur == '\n') || (*cur == '\r')) 1705 cur++; 1706 } 1707 if (callback != NULL) { 1708 callback(userData, filename, attrib, owner, group, size, links, 1709 year, month, day, hour, minute); 1710 } 1711 return(cur - list); 1712} 1713 1714/** 1715 * xmlNanoFTPList: 1716 * @ctx: an FTP context 1717 * @callback: the user callback 1718 * @userData: the user callback data 1719 * @filename: optional files to list 1720 * 1721 * Do a listing on the server. All files info are passed back 1722 * in the callbacks. 1723 * 1724 * Returns -1 incase of error, 0 otherwise 1725 */ 1726 1727int 1728xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData, 1729 const char *filename) { 1730 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1731 char buf[4096 + 1]; 1732 int len, res; 1733 int indx = 0, base; 1734 fd_set rfd, efd; 1735 struct timeval tv; 1736 1737 if (ctxt == NULL) return (-1); 1738 if (filename == NULL) { 1739 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1) 1740 return(-1); 1741 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt); 1742 if (ctxt->dataFd == INVALID_SOCKET) 1743 return(-1); 1744 snprintf(buf, sizeof(buf), "LIST -L\r\n"); 1745 } else { 1746 if (filename[0] != '/') { 1747 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1) 1748 return(-1); 1749 } 1750 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt); 1751 if (ctxt->dataFd == INVALID_SOCKET) 1752 return(-1); 1753 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename); 1754 } 1755 buf[sizeof(buf) - 1] = 0; 1756 len = strlen(buf); 1757#ifdef DEBUG_FTP 1758 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1759#endif 1760 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1761 if (res < 0) { 1762 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1763 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1764 return(res); 1765 } 1766 res = xmlNanoFTPReadResponse(ctxt); 1767 if (res != 1) { 1768 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1769 return(-res); 1770 } 1771 1772 do { 1773 tv.tv_sec = 1; 1774 tv.tv_usec = 0; 1775 FD_ZERO(&rfd); 1776 FD_SET(ctxt->dataFd, &rfd); 1777 FD_ZERO(&efd); 1778 FD_SET(ctxt->dataFd, &efd); 1779 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv); 1780 if (res < 0) { 1781#ifdef DEBUG_FTP 1782 perror("select"); 1783#endif 1784 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1785 return(-1); 1786 } 1787 if (res == 0) { 1788 res = xmlNanoFTPCheckResponse(ctxt); 1789 if (res < 0) { 1790 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1791 ctxt->dataFd = INVALID_SOCKET; 1792 return(-1); 1793 } 1794 if (res == 2) { 1795 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1796 return(0); 1797 } 1798 1799 continue; 1800 } 1801 1802 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) { 1803 __xmlIOErr(XML_FROM_FTP, 0, "recv"); 1804 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1805 ctxt->dataFd = INVALID_SOCKET; 1806 return(-1); 1807 } 1808#ifdef DEBUG_FTP 1809 write(1, &buf[indx], len); 1810#endif 1811 indx += len; 1812 buf[indx] = 0; 1813 base = 0; 1814 do { 1815 res = xmlNanoFTPParseList(&buf[base], callback, userData); 1816 base += res; 1817 } while (res > 0); 1818 1819 memmove(&buf[0], &buf[base], indx - base); 1820 indx -= base; 1821 } while (len != 0); 1822 xmlNanoFTPCloseConnection(ctxt); 1823 return(0); 1824} 1825 1826/** 1827 * xmlNanoFTPGetSocket: 1828 * @ctx: an FTP context 1829 * @filename: the file to retrieve (or NULL if path is in context). 1830 * 1831 * Initiate fetch of the given file from the server. 1832 * 1833 * Returns the socket for the data connection, or <0 in case of error 1834 */ 1835 1836 1837SOCKET 1838xmlNanoFTPGetSocket(void *ctx, const char *filename) { 1839 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1840 char buf[300]; 1841 int res, len; 1842 if (ctx == NULL) 1843 return INVALID_SOCKET; 1844 if ((filename == NULL) && (ctxt->path == NULL)) 1845 return INVALID_SOCKET; 1846 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt); 1847 if (ctxt->dataFd == INVALID_SOCKET) 1848 return INVALID_SOCKET; 1849 1850 snprintf(buf, sizeof(buf), "TYPE I\r\n"); 1851 len = strlen(buf); 1852#ifdef DEBUG_FTP 1853 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1854#endif 1855 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1856 if (res < 0) { 1857 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1858 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1859 return INVALID_SOCKET; 1860 } 1861 res = xmlNanoFTPReadResponse(ctxt); 1862 if (res != 2) { 1863 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1864 return INVALID_SOCKET; 1865 } 1866 if (filename == NULL) 1867 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path); 1868 else 1869 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename); 1870 buf[sizeof(buf) - 1] = 0; 1871 len = strlen(buf); 1872#ifdef DEBUG_FTP 1873 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1874#endif 1875 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1876 if (res < 0) { 1877 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1878 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1879 return INVALID_SOCKET; 1880 } 1881 res = xmlNanoFTPReadResponse(ctxt); 1882 if (res != 1) { 1883 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1884 return INVALID_SOCKET; 1885 } 1886 return(ctxt->dataFd); 1887} 1888 1889/** 1890 * xmlNanoFTPGet: 1891 * @ctx: an FTP context 1892 * @callback: the user callback 1893 * @userData: the user callback data 1894 * @filename: the file to retrieve 1895 * 1896 * Fetch the given file from the server. All data are passed back 1897 * in the callbacks. The last callback has a size of 0 block. 1898 * 1899 * Returns -1 incase of error, 0 otherwise 1900 */ 1901 1902int 1903xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData, 1904 const char *filename) { 1905 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1906 char buf[4096]; 1907 int len = 0, res; 1908 fd_set rfd; 1909 struct timeval tv; 1910 1911 if (ctxt == NULL) return(-1); 1912 if ((filename == NULL) && (ctxt->path == NULL)) 1913 return(-1); 1914 if (callback == NULL) 1915 return(-1); 1916 if (xmlNanoFTPGetSocket(ctxt, filename) == INVALID_SOCKET) 1917 return(-1); 1918 1919 do { 1920 tv.tv_sec = 1; 1921 tv.tv_usec = 0; 1922 FD_ZERO(&rfd); 1923 FD_SET(ctxt->dataFd, &rfd); 1924 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv); 1925 if (res < 0) { 1926#ifdef DEBUG_FTP 1927 perror("select"); 1928#endif 1929 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1930 return(-1); 1931 } 1932 if (res == 0) { 1933 res = xmlNanoFTPCheckResponse(ctxt); 1934 if (res < 0) { 1935 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1936 ctxt->dataFd = INVALID_SOCKET; 1937 return(-1); 1938 } 1939 if (res == 2) { 1940 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1941 return(0); 1942 } 1943 1944 continue; 1945 } 1946 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) { 1947 __xmlIOErr(XML_FROM_FTP, 0, "recv failed"); 1948 callback(userData, buf, len); 1949 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1950 return(-1); 1951 } 1952 callback(userData, buf, len); 1953 } while (len != 0); 1954 1955 return(xmlNanoFTPCloseConnection(ctxt)); 1956} 1957 1958/** 1959 * xmlNanoFTPRead: 1960 * @ctx: the FTP context 1961 * @dest: a buffer 1962 * @len: the buffer length 1963 * 1964 * This function tries to read @len bytes from the existing FTP connection 1965 * and saves them in @dest. This is a blocking call. 1966 * 1967 * Returns the number of byte read. 0 is an indication of an end of connection. 1968 * -1 indicates a parameter error. 1969 */ 1970int 1971xmlNanoFTPRead(void *ctx, void *dest, int len) { 1972 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1973 1974 if (ctx == NULL) return(-1); 1975 if (ctxt->dataFd == INVALID_SOCKET) return(0); 1976 if (dest == NULL) return(-1); 1977 if (len <= 0) return(0); 1978 1979 len = recv(ctxt->dataFd, dest, len, 0); 1980 if (len <= 0) { 1981 if (len < 0) 1982 __xmlIOErr(XML_FROM_FTP, 0, "recv failed"); 1983 xmlNanoFTPCloseConnection(ctxt); 1984 } 1985#ifdef DEBUG_FTP 1986 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len); 1987#endif 1988 return(len); 1989} 1990 1991/** 1992 * xmlNanoFTPOpen: 1993 * @URL: the URL to the resource 1994 * 1995 * Start to fetch the given ftp:// resource 1996 * 1997 * Returns an FTP context, or NULL 1998 */ 1999 2000void* 2001xmlNanoFTPOpen(const char *URL) { 2002 xmlNanoFTPCtxtPtr ctxt; 2003 SOCKET sock; 2004 2005 xmlNanoFTPInit(); 2006 if (URL == NULL) return(NULL); 2007 if (strncmp("ftp://", URL, 6)) return(NULL); 2008 2009 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL); 2010 if (ctxt == NULL) return(NULL); 2011 if (xmlNanoFTPConnect(ctxt) < 0) { 2012 xmlNanoFTPFreeCtxt(ctxt); 2013 return(NULL); 2014 } 2015 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path); 2016 if (sock == INVALID_SOCKET) { 2017 xmlNanoFTPFreeCtxt(ctxt); 2018 return(NULL); 2019 } 2020 return(ctxt); 2021} 2022 2023/** 2024 * xmlNanoFTPClose: 2025 * @ctx: an FTP context 2026 * 2027 * Close the connection and both control and transport 2028 * 2029 * Returns -1 incase of error, 0 otherwise 2030 */ 2031 2032int 2033xmlNanoFTPClose(void *ctx) { 2034 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 2035 2036 if (ctxt == NULL) 2037 return(-1); 2038 2039 if (ctxt->dataFd != INVALID_SOCKET) { 2040 closesocket(ctxt->dataFd); 2041 ctxt->dataFd = INVALID_SOCKET; 2042 } 2043 if (ctxt->controlFd != INVALID_SOCKET) { 2044 xmlNanoFTPQuit(ctxt); 2045 closesocket(ctxt->controlFd); 2046 ctxt->controlFd = INVALID_SOCKET; 2047 } 2048 xmlNanoFTPFreeCtxt(ctxt); 2049 return(0); 2050} 2051 2052#ifdef STANDALONE 2053/************************************************************************ 2054 * * 2055 * Basic test in Standalone mode * 2056 * * 2057 ************************************************************************/ 2058static 2059void ftpList(void *userData, const char *filename, const char* attrib, 2060 const char *owner, const char *group, unsigned long size, int links, 2061 int year, const char *month, int day, int hour, int minute) { 2062 xmlGenericError(xmlGenericErrorContext, 2063 "%s %s %s %ld %s\n", attrib, owner, group, size, filename); 2064} 2065static 2066void ftpData(void *userData, const char *data, int len) { 2067 if (userData == NULL) return; 2068 if (len <= 0) { 2069 fclose((FILE*)userData); 2070 return; 2071 } 2072 fwrite(data, len, 1, (FILE*)userData); 2073} 2074 2075int main(int argc, char **argv) { 2076 void *ctxt; 2077 FILE *output; 2078 char *tstfile = NULL; 2079 2080 xmlNanoFTPInit(); 2081 if (argc > 1) { 2082 ctxt = xmlNanoFTPNewCtxt(argv[1]); 2083 if (xmlNanoFTPConnect(ctxt) < 0) { 2084 xmlGenericError(xmlGenericErrorContext, 2085 "Couldn't connect to %s\n", argv[1]); 2086 exit(1); 2087 } 2088 if (argc > 2) 2089 tstfile = argv[2]; 2090 } else 2091 ctxt = xmlNanoFTPConnectTo("localhost", 0); 2092 if (ctxt == NULL) { 2093 xmlGenericError(xmlGenericErrorContext, 2094 "Couldn't connect to localhost\n"); 2095 exit(1); 2096 } 2097 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile); 2098 output = fopen("/tmp/tstdata", "w"); 2099 if (output != NULL) { 2100 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0) 2101 xmlGenericError(xmlGenericErrorContext, 2102 "Failed to get file\n"); 2103 2104 } 2105 xmlNanoFTPClose(ctxt); 2106 xmlMemoryDump(); 2107 exit(0); 2108} 2109#endif /* STANDALONE */ 2110#else /* !LIBXML_FTP_ENABLED */ 2111#ifdef STANDALONE 2112#include <stdio.h> 2113int main(int argc, char **argv) { 2114 xmlGenericError(xmlGenericErrorContext, 2115 "%s : FTP support not compiled in\n", argv[0]); 2116 return(0); 2117} 2118#endif /* STANDALONE */ 2119#endif /* LIBXML_FTP_ENABLED */ 2120#define bottom_nanoftp 2121#include "elfgcchack.h" 2122