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