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 |