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 information 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 information, 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 information. 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 information. 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 information. 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 ctxt->port = port; 1255 res = xmlNanoFTPConnect(ctxt); 1256 if (res < 0) { 1257 xmlNanoFTPFreeCtxt(ctxt); 1258 return(NULL); 1259 } 1260 return(ctxt); 1261 } 1262 1263 /** 1264 * xmlNanoFTPCwd: 1265 * @ctx: an FTP context 1266 * @directory: a directory on the server 1267 * 1268 * Tries to change the remote directory 1269 * 1270 * Returns -1 in case of error, 1 if CWD worked, 0 if it failed 1271 */ 1272 1273 int 1274 xmlNanoFTPCwd(void *ctx, const char *directory) { 1275 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1276 char buf[400]; 1277 int len; 1278 int res; 1279 1280 if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1); 1281 if (directory == NULL) return 0; 1282 1283 /* 1284 * Expected response code for CWD: 1285 * 1286 * CWD 1287 * 250 1288 * 500, 501, 502, 421, 530, 550 1289 */ 1290 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory); 1291 buf[sizeof(buf) - 1] = 0; 1292 len = strlen(buf); 1293 #ifdef DEBUG_FTP 1294 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1295 #endif 1296 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1297 if (res < 0) { 1298 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1299 return(res); 1300 } 1301 res = xmlNanoFTPGetResponse(ctxt); 1302 if (res == 4) { 1303 return(-1); 1304 } 1305 if (res == 2) return(1); 1306 if (res == 5) { 1307 return(0); 1308 } 1309 return(0); 1310 } 1311 1312 /** 1313 * xmlNanoFTPDele: 1314 * @ctx: an FTP context 1315 * @file: a file or directory on the server 1316 * 1317 * Tries to delete an item (file or directory) from server 1318 * 1319 * Returns -1 in case of error, 1 if DELE worked, 0 if it failed 1320 */ 1321 1322 int 1323 xmlNanoFTPDele(void *ctx, const char *file) { 1324 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1325 char buf[400]; 1326 int len; 1327 int res; 1328 1329 if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET) || 1330 (file == NULL)) return(-1); 1331 1332 /* 1333 * Expected response code for DELE: 1334 * 1335 * DELE 1336 * 250 1337 * 450, 550 1338 * 500, 501, 502, 421, 530 1339 */ 1340 1341 snprintf(buf, sizeof(buf), "DELE %s\r\n", file); 1342 buf[sizeof(buf) - 1] = 0; 1343 len = strlen(buf); 1344 #ifdef DEBUG_FTP 1345 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1346 #endif 1347 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1348 if (res < 0) { 1349 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1350 return(res); 1351 } 1352 res = xmlNanoFTPGetResponse(ctxt); 1353 if (res == 4) { 1354 return(-1); 1355 } 1356 if (res == 2) return(1); 1357 if (res == 5) { 1358 return(0); 1359 } 1360 return(0); 1361 } 1362 /** 1363 * xmlNanoFTPGetConnection: 1364 * @ctx: an FTP context 1365 * 1366 * Try to open a data connection to the server. Currently only 1367 * passive mode is supported. 1368 * 1369 * Returns -1 in case of error, 0 otherwise 1370 */ 1371 1372 SOCKET 1373 xmlNanoFTPGetConnection(void *ctx) { 1374 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1375 char buf[200], *cur; 1376 int len, i; 1377 int res; 1378 unsigned char ad[6], *adp, *portp; 1379 unsigned int temp[6]; 1380 #ifdef SUPPORT_IP6 1381 struct sockaddr_storage dataAddr; 1382 #else 1383 struct sockaddr_in dataAddr; 1384 #endif 1385 XML_SOCKLEN_T dataAddrLen; 1386 1387 if (ctxt == NULL) return INVALID_SOCKET; 1388 1389 memset (&dataAddr, 0, sizeof(dataAddr)); 1390 #ifdef SUPPORT_IP6 1391 if ((ctxt->ftpAddr).ss_family == AF_INET6) { 1392 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP); 1393 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6; 1394 dataAddrLen = sizeof(struct sockaddr_in6); 1395 } else 1396 #endif 1397 { 1398 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 1399 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET; 1400 dataAddrLen = sizeof (struct sockaddr_in); 1401 } 1402 1403 if (ctxt->dataFd == INVALID_SOCKET) { 1404 __xmlIOErr(XML_FROM_FTP, 0, "socket failed"); 1405 return INVALID_SOCKET; 1406 } 1407 1408 if (ctxt->passive) { 1409 #ifdef SUPPORT_IP6 1410 if ((ctxt->ftpAddr).ss_family == AF_INET6) 1411 snprintf (buf, sizeof(buf), "EPSV\r\n"); 1412 else 1413 #endif 1414 snprintf (buf, sizeof(buf), "PASV\r\n"); 1415 len = strlen (buf); 1416 #ifdef DEBUG_FTP 1417 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1418 #endif 1419 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1420 if (res < 0) { 1421 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1422 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1423 return INVALID_SOCKET; 1424 } 1425 res = xmlNanoFTPReadResponse(ctx); 1426 if (res != 2) { 1427 if (res == 5) { 1428 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1429 return INVALID_SOCKET; 1430 } else { 1431 /* 1432 * retry with an active connection 1433 */ 1434 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1435 ctxt->passive = 0; 1436 } 1437 } 1438 cur = &ctxt->controlBuf[ctxt->controlBufAnswer]; 1439 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++; 1440 #ifdef SUPPORT_IP6 1441 if ((ctxt->ftpAddr).ss_family == AF_INET6) { 1442 if (sscanf (cur, "%u", &temp[0]) != 1) { 1443 __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER, 1444 "Invalid answer to EPSV\n"); 1445 if (ctxt->dataFd != INVALID_SOCKET) { 1446 closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1447 } 1448 return INVALID_SOCKET; 1449 } 1450 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr)); 1451 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]); 1452 } 1453 else 1454 #endif 1455 { 1456 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2], 1457 &temp[3], &temp[4], &temp[5]) != 6) { 1458 __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER, 1459 "Invalid answer to PASV\n"); 1460 if (ctxt->dataFd != INVALID_SOCKET) { 1461 closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1462 } 1463 return INVALID_SOCKET; 1464 } 1465 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff); 1466 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4); 1467 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2); 1468 } 1469 1470 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) { 1471 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection"); 1472 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1473 return INVALID_SOCKET; 1474 } 1475 } else { 1476 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen); 1477 #ifdef SUPPORT_IP6 1478 if ((ctxt->ftpAddr).ss_family == AF_INET6) 1479 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0; 1480 else 1481 #endif 1482 ((struct sockaddr_in *)&dataAddr)->sin_port = 0; 1483 1484 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) { 1485 __xmlIOErr(XML_FROM_FTP, 0, "bind failed"); 1486 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1487 return INVALID_SOCKET; 1488 } 1489 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen); 1490 1491 if (listen(ctxt->dataFd, 1) < 0) { 1492 __xmlIOErr(XML_FROM_FTP, 0, "listen failed"); 1493 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1494 return INVALID_SOCKET; 1495 } 1496 #ifdef SUPPORT_IP6 1497 if ((ctxt->ftpAddr).ss_family == AF_INET6) { 1498 char buf6[INET6_ADDRSTRLEN]; 1499 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr, 1500 buf6, INET6_ADDRSTRLEN); 1501 adp = (unsigned char *) buf6; 1502 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port; 1503 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp); 1504 } else 1505 #endif 1506 { 1507 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr; 1508 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port; 1509 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n", 1510 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff, 1511 portp[0] & 0xff, portp[1] & 0xff); 1512 } 1513 1514 buf[sizeof(buf) - 1] = 0; 1515 len = strlen(buf); 1516 #ifdef DEBUG_FTP 1517 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1518 #endif 1519 1520 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1521 if (res < 0) { 1522 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1523 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1524 return INVALID_SOCKET; 1525 } 1526 res = xmlNanoFTPGetResponse(ctxt); 1527 if (res != 2) { 1528 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1529 return INVALID_SOCKET; 1530 } 1531 } 1532 return(ctxt->dataFd); 1533 1534 } 1535 1536 /** 1537 * xmlNanoFTPCloseConnection: 1538 * @ctx: an FTP context 1539 * 1540 * Close the data connection from the server 1541 * 1542 * Returns -1 in case of error, 0 otherwise 1543 */ 1544 1545 int 1546 xmlNanoFTPCloseConnection(void *ctx) { 1547 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1548 int res; 1549 fd_set rfd, efd; 1550 struct timeval tv; 1551 1552 if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1); 1553 1554 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1555 tv.tv_sec = 15; 1556 tv.tv_usec = 0; 1557 FD_ZERO(&rfd); 1558 FD_SET(ctxt->controlFd, &rfd); 1559 FD_ZERO(&efd); 1560 FD_SET(ctxt->controlFd, &efd); 1561 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv); 1562 if (res < 0) { 1563 #ifdef DEBUG_FTP 1564 perror("select"); 1565 #endif 1566 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1567 return(-1); 1568 } 1569 if (res == 0) { 1570 #ifdef DEBUG_FTP 1571 xmlGenericError(xmlGenericErrorContext, 1572 "xmlNanoFTPCloseConnection: timeout\n"); 1573 #endif 1574 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1575 } else { 1576 res = xmlNanoFTPGetResponse(ctxt); 1577 if (res != 2) { 1578 closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET; 1579 return(-1); 1580 } 1581 } 1582 return(0); 1583 } 1584 1585 /** 1586 * xmlNanoFTPParseList: 1587 * @list: some data listing received from the server 1588 * @callback: the user callback 1589 * @userData: the user callback data 1590 * 1591 * Parse at most one entry from the listing. 1592 * 1593 * Returns -1 in case of error, the length of data parsed otherwise 1594 */ 1595 1596 static int 1597 xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) { 1598 const char *cur = list; 1599 char filename[151]; 1600 char attrib[11]; 1601 char owner[11]; 1602 char group[11]; 1603 char month[4]; 1604 int year = 0; 1605 int minute = 0; 1606 int hour = 0; 1607 int day = 0; 1608 unsigned long size = 0; 1609 int links = 0; 1610 int i; 1611 1612 if (!strncmp(cur, "total", 5)) { 1613 cur += 5; 1614 while (*cur == ' ') cur++; 1615 while ((*cur >= '0') && (*cur <= '9')) 1616 links = (links * 10) + (*cur++ - '0'); 1617 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r')) 1618 cur++; 1619 return(cur - list); 1620 } else if (*list == '+') { 1621 return(0); 1622 } else { 1623 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r')) 1624 cur++; 1625 if (*cur == 0) return(0); 1626 i = 0; 1627 while (*cur != ' ') { 1628 if (i < 10) 1629 attrib[i++] = *cur; 1630 cur++; 1631 if (*cur == 0) return(0); 1632 } 1633 attrib[10] = 0; 1634 while (*cur == ' ') cur++; 1635 if (*cur == 0) return(0); 1636 while ((*cur >= '0') && (*cur <= '9')) 1637 links = (links * 10) + (*cur++ - '0'); 1638 while (*cur == ' ') cur++; 1639 if (*cur == 0) return(0); 1640 i = 0; 1641 while (*cur != ' ') { 1642 if (i < 10) 1643 owner[i++] = *cur; 1644 cur++; 1645 if (*cur == 0) return(0); 1646 } 1647 owner[i] = 0; 1648 while (*cur == ' ') cur++; 1649 if (*cur == 0) return(0); 1650 i = 0; 1651 while (*cur != ' ') { 1652 if (i < 10) 1653 group[i++] = *cur; 1654 cur++; 1655 if (*cur == 0) return(0); 1656 } 1657 group[i] = 0; 1658 while (*cur == ' ') cur++; 1659 if (*cur == 0) return(0); 1660 while ((*cur >= '0') && (*cur <= '9')) 1661 size = (size * 10) + (*cur++ - '0'); 1662 while (*cur == ' ') cur++; 1663 if (*cur == 0) return(0); 1664 i = 0; 1665 while (*cur != ' ') { 1666 if (i < 3) 1667 month[i++] = *cur; 1668 cur++; 1669 if (*cur == 0) return(0); 1670 } 1671 month[i] = 0; 1672 while (*cur == ' ') cur++; 1673 if (*cur == 0) return(0); 1674 while ((*cur >= '0') && (*cur <= '9')) 1675 day = (day * 10) + (*cur++ - '0'); 1676 while (*cur == ' ') cur++; 1677 if (*cur == 0) return(0); 1678 if ((cur[1] == 0) || (cur[2] == 0)) return(0); 1679 if ((cur[1] == ':') || (cur[2] == ':')) { 1680 while ((*cur >= '0') && (*cur <= '9')) 1681 hour = (hour * 10) + (*cur++ - '0'); 1682 if (*cur == ':') cur++; 1683 while ((*cur >= '0') && (*cur <= '9')) 1684 minute = (minute * 10) + (*cur++ - '0'); 1685 } else { 1686 while ((*cur >= '0') && (*cur <= '9')) 1687 year = (year * 10) + (*cur++ - '0'); 1688 } 1689 while (*cur == ' ') cur++; 1690 if (*cur == 0) return(0); 1691 i = 0; 1692 while ((*cur != '\n') && (*cur != '\r')) { 1693 if (i < 150) 1694 filename[i++] = *cur; 1695 cur++; 1696 if (*cur == 0) return(0); 1697 } 1698 filename[i] = 0; 1699 if ((*cur != '\n') && (*cur != '\r')) 1700 return(0); 1701 while ((*cur == '\n') || (*cur == '\r')) 1702 cur++; 1703 } 1704 if (callback != NULL) { 1705 callback(userData, filename, attrib, owner, group, size, links, 1706 year, month, day, hour, minute); 1707 } 1708 return(cur - list); 1709 } 1710 1711 /** 1712 * xmlNanoFTPList: 1713 * @ctx: an FTP context 1714 * @callback: the user callback 1715 * @userData: the user callback data 1716 * @filename: optional files to list 1717 * 1718 * Do a listing on the server. All files info are passed back 1719 * in the callbacks. 1720 * 1721 * Returns -1 in case of error, 0 otherwise 1722 */ 1723 1724 int 1725 xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData, 1726 const char *filename) { 1727 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1728 char buf[4096 + 1]; 1729 int len, res; 1730 int indx = 0, base; 1731 fd_set rfd, efd; 1732 struct timeval tv; 1733 1734 if (ctxt == NULL) return (-1); 1735 if (filename == NULL) { 1736 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1) 1737 return(-1); 1738 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt); 1739 if (ctxt->dataFd == INVALID_SOCKET) 1740 return(-1); 1741 snprintf(buf, sizeof(buf), "LIST -L\r\n"); 1742 } else { 1743 if (filename[0] != '/') { 1744 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1) 1745 return(-1); 1746 } 1747 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt); 1748 if (ctxt->dataFd == INVALID_SOCKET) 1749 return(-1); 1750 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename); 1751 } 1752 buf[sizeof(buf) - 1] = 0; 1753 len = strlen(buf); 1754 #ifdef DEBUG_FTP 1755 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1756 #endif 1757 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1758 if (res < 0) { 1759 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1760 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1761 return(res); 1762 } 1763 res = xmlNanoFTPReadResponse(ctxt); 1764 if (res != 1) { 1765 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1766 return(-res); 1767 } 1768 1769 do { 1770 tv.tv_sec = 1; 1771 tv.tv_usec = 0; 1772 FD_ZERO(&rfd); 1773 FD_SET(ctxt->dataFd, &rfd); 1774 FD_ZERO(&efd); 1775 FD_SET(ctxt->dataFd, &efd); 1776 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv); 1777 if (res < 0) { 1778 #ifdef DEBUG_FTP 1779 perror("select"); 1780 #endif 1781 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1782 return(-1); 1783 } 1784 if (res == 0) { 1785 res = xmlNanoFTPCheckResponse(ctxt); 1786 if (res < 0) { 1787 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1788 ctxt->dataFd = INVALID_SOCKET; 1789 return(-1); 1790 } 1791 if (res == 2) { 1792 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1793 return(0); 1794 } 1795 1796 continue; 1797 } 1798 1799 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) { 1800 __xmlIOErr(XML_FROM_FTP, 0, "recv"); 1801 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1802 ctxt->dataFd = INVALID_SOCKET; 1803 return(-1); 1804 } 1805 #ifdef DEBUG_FTP 1806 write(1, &buf[indx], len); 1807 #endif 1808 indx += len; 1809 buf[indx] = 0; 1810 base = 0; 1811 do { 1812 res = xmlNanoFTPParseList(&buf[base], callback, userData); 1813 base += res; 1814 } while (res > 0); 1815 1816 memmove(&buf[0], &buf[base], indx - base); 1817 indx -= base; 1818 } while (len != 0); 1819 xmlNanoFTPCloseConnection(ctxt); 1820 return(0); 1821 } 1822 1823 /** 1824 * xmlNanoFTPGetSocket: 1825 * @ctx: an FTP context 1826 * @filename: the file to retrieve (or NULL if path is in context). 1827 * 1828 * Initiate fetch of the given file from the server. 1829 * 1830 * Returns the socket for the data connection, or <0 in case of error 1831 */ 1832 1833 1834 SOCKET 1835 xmlNanoFTPGetSocket(void *ctx, const char *filename) { 1836 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1837 char buf[300]; 1838 int res, len; 1839 if (ctx == NULL) 1840 return INVALID_SOCKET; 1841 if ((filename == NULL) && (ctxt->path == NULL)) 1842 return INVALID_SOCKET; 1843 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt); 1844 if (ctxt->dataFd == INVALID_SOCKET) 1845 return INVALID_SOCKET; 1846 1847 snprintf(buf, sizeof(buf), "TYPE I\r\n"); 1848 len = strlen(buf); 1849 #ifdef DEBUG_FTP 1850 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1851 #endif 1852 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1853 if (res < 0) { 1854 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1855 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1856 return INVALID_SOCKET; 1857 } 1858 res = xmlNanoFTPReadResponse(ctxt); 1859 if (res != 2) { 1860 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1861 return INVALID_SOCKET; 1862 } 1863 if (filename == NULL) 1864 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path); 1865 else 1866 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename); 1867 buf[sizeof(buf) - 1] = 0; 1868 len = strlen(buf); 1869 #ifdef DEBUG_FTP 1870 xmlGenericError(xmlGenericErrorContext, "%s", buf); 1871 #endif 1872 res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0); 1873 if (res < 0) { 1874 __xmlIOErr(XML_FROM_FTP, 0, "send failed"); 1875 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1876 return INVALID_SOCKET; 1877 } 1878 res = xmlNanoFTPReadResponse(ctxt); 1879 if (res != 1) { 1880 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1881 return INVALID_SOCKET; 1882 } 1883 return(ctxt->dataFd); 1884 } 1885 1886 /** 1887 * xmlNanoFTPGet: 1888 * @ctx: an FTP context 1889 * @callback: the user callback 1890 * @userData: the user callback data 1891 * @filename: the file to retrieve 1892 * 1893 * Fetch the given file from the server. All data are passed back 1894 * in the callbacks. The last callback has a size of 0 block. 1895 * 1896 * Returns -1 in case of error, 0 otherwise 1897 */ 1898 1899 int 1900 xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData, 1901 const char *filename) { 1902 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1903 char buf[4096]; 1904 int len = 0, res; 1905 fd_set rfd; 1906 struct timeval tv; 1907 1908 if (ctxt == NULL) return(-1); 1909 if ((filename == NULL) && (ctxt->path == NULL)) 1910 return(-1); 1911 if (callback == NULL) 1912 return(-1); 1913 if (xmlNanoFTPGetSocket(ctxt, filename) == INVALID_SOCKET) 1914 return(-1); 1915 1916 do { 1917 tv.tv_sec = 1; 1918 tv.tv_usec = 0; 1919 FD_ZERO(&rfd); 1920 FD_SET(ctxt->dataFd, &rfd); 1921 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv); 1922 if (res < 0) { 1923 #ifdef DEBUG_FTP 1924 perror("select"); 1925 #endif 1926 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1927 return(-1); 1928 } 1929 if (res == 0) { 1930 res = xmlNanoFTPCheckResponse(ctxt); 1931 if (res < 0) { 1932 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1933 ctxt->dataFd = INVALID_SOCKET; 1934 return(-1); 1935 } 1936 if (res == 2) { 1937 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1938 return(0); 1939 } 1940 1941 continue; 1942 } 1943 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) { 1944 __xmlIOErr(XML_FROM_FTP, 0, "recv failed"); 1945 callback(userData, buf, len); 1946 closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET; 1947 return(-1); 1948 } 1949 callback(userData, buf, len); 1950 } while (len != 0); 1951 1952 return(xmlNanoFTPCloseConnection(ctxt)); 1953 } 1954 1955 /** 1956 * xmlNanoFTPRead: 1957 * @ctx: the FTP context 1958 * @dest: a buffer 1959 * @len: the buffer length 1960 * 1961 * This function tries to read @len bytes from the existing FTP connection 1962 * and saves them in @dest. This is a blocking call. 1963 * 1964 * Returns the number of byte read. 0 is an indication of an end of connection. 1965 * -1 indicates a parameter error. 1966 */ 1967 int 1968 xmlNanoFTPRead(void *ctx, void *dest, int len) { 1969 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 1970 1971 if (ctx == NULL) return(-1); 1972 if (ctxt->dataFd == INVALID_SOCKET) return(0); 1973 if (dest == NULL) return(-1); 1974 if (len <= 0) return(0); 1975 1976 len = recv(ctxt->dataFd, dest, len, 0); 1977 if (len <= 0) { 1978 if (len < 0) 1979 __xmlIOErr(XML_FROM_FTP, 0, "recv failed"); 1980 xmlNanoFTPCloseConnection(ctxt); 1981 } 1982 #ifdef DEBUG_FTP 1983 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len); 1984 #endif 1985 return(len); 1986 } 1987 1988 /** 1989 * xmlNanoFTPOpen: 1990 * @URL: the URL to the resource 1991 * 1992 * Start to fetch the given ftp:// resource 1993 * 1994 * Returns an FTP context, or NULL 1995 */ 1996 1997 void* 1998 xmlNanoFTPOpen(const char *URL) { 1999 xmlNanoFTPCtxtPtr ctxt; 2000 SOCKET sock; 2001 2002 xmlNanoFTPInit(); 2003 if (URL == NULL) return(NULL); 2004 if (strncmp("ftp://", URL, 6)) return(NULL); 2005 2006 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL); 2007 if (ctxt == NULL) return(NULL); 2008 if (xmlNanoFTPConnect(ctxt) < 0) { 2009 xmlNanoFTPFreeCtxt(ctxt); 2010 return(NULL); 2011 } 2012 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path); 2013 if (sock == INVALID_SOCKET) { 2014 xmlNanoFTPFreeCtxt(ctxt); 2015 return(NULL); 2016 } 2017 return(ctxt); 2018 } 2019 2020 /** 2021 * xmlNanoFTPClose: 2022 * @ctx: an FTP context 2023 * 2024 * Close the connection and both control and transport 2025 * 2026 * Returns -1 in case of error, 0 otherwise 2027 */ 2028 2029 int 2030 xmlNanoFTPClose(void *ctx) { 2031 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx; 2032 2033 if (ctxt == NULL) 2034 return(-1); 2035 2036 if (ctxt->dataFd != INVALID_SOCKET) { 2037 closesocket(ctxt->dataFd); 2038 ctxt->dataFd = INVALID_SOCKET; 2039 } 2040 if (ctxt->controlFd != INVALID_SOCKET) { 2041 xmlNanoFTPQuit(ctxt); 2042 closesocket(ctxt->controlFd); 2043 ctxt->controlFd = INVALID_SOCKET; 2044 } 2045 xmlNanoFTPFreeCtxt(ctxt); 2046 return(0); 2047 } 2048 2049 #ifdef STANDALONE 2050 /************************************************************************ 2051 * * 2052 * Basic test in Standalone mode * 2053 * * 2054 ************************************************************************/ 2055 static 2056 void ftpList(void *userData, const char *filename, const char* attrib, 2057 const char *owner, const char *group, unsigned long size, int links, 2058 int year, const char *month, int day, int hour, int minute) { 2059 xmlGenericError(xmlGenericErrorContext, 2060 "%s %s %s %ld %s\n", attrib, owner, group, size, filename); 2061 } 2062 static 2063 void ftpData(void *userData, const char *data, int len) { 2064 if (userData == NULL) return; 2065 if (len <= 0) { 2066 fclose((FILE*)userData); 2067 return; 2068 } 2069 fwrite(data, len, 1, (FILE*)userData); 2070 } 2071 2072 int main(int argc, char **argv) { 2073 void *ctxt; 2074 FILE *output; 2075 char *tstfile = NULL; 2076 2077 xmlNanoFTPInit(); 2078 if (argc > 1) { 2079 ctxt = xmlNanoFTPNewCtxt(argv[1]); 2080 if (xmlNanoFTPConnect(ctxt) < 0) { 2081 xmlGenericError(xmlGenericErrorContext, 2082 "Couldn't connect to %s\n", argv[1]); 2083 exit(1); 2084 } 2085 if (argc > 2) 2086 tstfile = argv[2]; 2087 } else 2088 ctxt = xmlNanoFTPConnectTo("localhost", 0); 2089 if (ctxt == NULL) { 2090 xmlGenericError(xmlGenericErrorContext, 2091 "Couldn't connect to localhost\n"); 2092 exit(1); 2093 } 2094 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile); 2095 output = fopen("/tmp/tstdata", "w"); 2096 if (output != NULL) { 2097 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0) 2098 xmlGenericError(xmlGenericErrorContext, 2099 "Failed to get file\n"); 2100 2101 } 2102 xmlNanoFTPClose(ctxt); 2103 xmlMemoryDump(); 2104 exit(0); 2105 } 2106 #endif /* STANDALONE */ 2107 #else /* !LIBXML_FTP_ENABLED */ 2108 #ifdef STANDALONE 2109 #include <stdio.h> 2110 int main(int argc, char **argv) { 2111 xmlGenericError(xmlGenericErrorContext, 2112 "%s : FTP support not compiled in\n", argv[0]); 2113 return(0); 2114 } 2115 #endif /* STANDALONE */ 2116 #endif /* LIBXML_FTP_ENABLED */ 2117 #define bottom_nanoftp 2118 #include "elfgcchack.h" 2119