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