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