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