blob: 338802ebc6f453396e342f243d6924e88e790d67 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
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
Daniel Veillardcbaf3992001-12-31 16:16:02 +000015#else /* TESTING */
Daniel Veillardf3afa7d2001-06-09 13:52:58 +000016#define NEED_SOCKETS
Daniel Veillardcbaf3992001-12-31 16:16:02 +000017#endif /* TESTING */
Owen Taylor3473f882001-02-23 17:55:21 +000018
Daniel Veillard34ce8be2002-03-18 19:37:11 +000019#define IN_LIBXML
Daniel Veillard5e2dace2001-07-18 19:30:27 +000020#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000021
22#ifdef LIBXML_FTP_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +000023#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
Daniel Veillard75eb1ad2003-07-07 14:42:44 +000055#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
Owen Taylor3473f882001-02-23 17:55:21 +000061#ifdef HAVE_STRINGS_H
62#include <strings.h>
63#endif
64
65#include <libxml/xmlmemory.h>
Daniel Veillardd0463562001-10-13 09:15:48 +000066#include <libxml/parser.h>
Owen Taylor3473f882001-02-23 17:55:21 +000067#include <libxml/xmlerror.h>
Daniel Veillardcacbe5d2003-01-10 16:09:51 +000068#include <libxml/uri.h>
Daniel Veillardd0463562001-10-13 09:15:48 +000069#include <libxml/nanoftp.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000070#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000071
72/* #define DEBUG_FTP 1 */
73#ifdef STANDALONE
74#ifndef DEBUG_FTP
75#define DEBUG_FTP 1
76#endif
77#endif
78
Daniel Veillard1638a472003-08-14 01:23:25 +000079
80#ifdef __MINGW32__
81#define _WINSOCKAPI_
82#include <wsockcompat.h>
83#include <winsock2.h>
Daniel Veillardc284c642005-03-31 10:24:24 +000084#undef XML_SOCKLEN_T
85#define XML_SOCKLEN_T unsigned int
Daniel Veillard1638a472003-08-14 01:23:25 +000086#endif
87
Owen Taylor3473f882001-02-23 17:55:21 +000088/**
89 * A couple portability macros
90 */
91#ifndef _WINSOCKAPI_
Daniel Veillarda9cce9c2003-09-29 13:20:24 +000092#ifndef __BEOS__
Owen Taylor3473f882001-02-23 17:55:21 +000093#define closesocket(s) close(s)
Daniel Veillarda9cce9c2003-09-29 13:20:24 +000094#endif
Owen Taylor3473f882001-02-23 17:55:21 +000095#define SOCKET int
96#endif
97
Daniel Veillard89f7f272003-09-29 13:29:09 +000098#ifdef __BEOS__
99#ifndef PF_INET
100#define PF_INET AF_INET
101#endif
102#endif
103
Daniel Veillarddb439252005-01-15 17:18:53 +0000104#ifdef _AIX
105#define ss_family __ss_family
106#endif
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000107
Daniel Veillard6056ae92005-04-05 21:48:57 +0000108#ifndef XML_SOCKLEN_T
109#define XML_SOCKLEN_T unsigned int
110#endif
111
Owen Taylor3473f882001-02-23 17:55:21 +0000112#define FTP_COMMAND_OK 200
113#define FTP_SYNTAX_ERROR 500
114#define FTP_GET_PASSWD 331
115#define FTP_BUF_SIZE 512
116
William M. Brack030a7a12004-02-10 12:48:57 +0000117#define XML_NANO_MAX_URLBUF 4096
118
Owen Taylor3473f882001-02-23 17:55:21 +0000119typedef struct xmlNanoFTPCtxt {
120 char *protocol; /* the protocol name */
121 char *hostname; /* the host name */
122 int port; /* the port */
123 char *path; /* the path within the URL */
124 char *user; /* user string */
125 char *passwd; /* passwd string */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000126#ifdef SUPPORT_IP6
127 struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
128#else
Owen Taylor3473f882001-02-23 17:55:21 +0000129 struct sockaddr_in ftpAddr; /* the socket address struct */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000130#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000131 int passive; /* currently we support only passive !!! */
132 SOCKET controlFd; /* the file descriptor for the control socket */
133 SOCKET dataFd; /* the file descriptor for the data socket */
134 int state; /* WRITE / READ / CLOSED */
135 int returnValue; /* the protocol return value */
136 /* buffer for data received from the control connection */
137 char controlBuf[FTP_BUF_SIZE + 1];
138 int controlBufIndex;
139 int controlBufUsed;
140 int controlBufAnswer;
141} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
142
143static int initialized = 0;
144static char *proxy = NULL; /* the proxy name if any */
145static int proxyPort = 0; /* the proxy port if any */
146static char *proxyUser = NULL; /* user for proxy authentication */
147static char *proxyPasswd = NULL;/* passwd for proxy authentication */
148static int proxyType = 0; /* uses TYPE or a@b ? */
149
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000150#ifdef SUPPORT_IP6
Daniel Veillard2db8c122003-07-08 12:16:59 +0000151static
152int have_ipv6(void) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000153 int s;
154
155 s = socket (AF_INET6, SOCK_STREAM, 0);
156 if (s != -1) {
157 close (s);
158 return (1);
159 }
160 return (0);
161}
162#endif
163
Owen Taylor3473f882001-02-23 17:55:21 +0000164/**
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000165 * xmlFTPErrMemory:
166 * @extra: extra informations
167 *
168 * Handle an out of memory condition
169 */
170static void
171xmlFTPErrMemory(const char *extra)
172{
173 __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
174}
175
176/**
Owen Taylor3473f882001-02-23 17:55:21 +0000177 * xmlNanoFTPInit:
178 *
179 * Initialize the FTP protocol layer.
180 * Currently it just checks for proxy informations,
181 * and get the hostname
182 */
183
184void
185xmlNanoFTPInit(void) {
186 const char *env;
187#ifdef _WINSOCKAPI_
188 WSADATA wsaData;
189#endif
190
191 if (initialized)
192 return;
193
194#ifdef _WINSOCKAPI_
195 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
196 return;
197#endif
198
Owen Taylor3473f882001-02-23 17:55:21 +0000199 proxyPort = 21;
200 env = getenv("no_proxy");
Daniel Veillard29b17482004-08-16 00:39:03 +0000201 if (env && ((env[0] == '*' ) && (env[1] == 0)))
Owen Taylor3473f882001-02-23 17:55:21 +0000202 return;
203 env = getenv("ftp_proxy");
204 if (env != NULL) {
205 xmlNanoFTPScanProxy(env);
206 } else {
207 env = getenv("FTP_PROXY");
208 if (env != NULL) {
209 xmlNanoFTPScanProxy(env);
210 }
211 }
212 env = getenv("ftp_proxy_user");
213 if (env != NULL) {
214 proxyUser = xmlMemStrdup(env);
215 }
216 env = getenv("ftp_proxy_password");
217 if (env != NULL) {
218 proxyPasswd = xmlMemStrdup(env);
219 }
220 initialized = 1;
221}
222
223/**
Daniel Veillarde356c282001-03-10 12:32:04 +0000224 * xmlNanoFTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000225 *
226 * Cleanup the FTP protocol layer. This cleanup proxy informations.
227 */
228
229void
230xmlNanoFTPCleanup(void) {
231 if (proxy != NULL) {
232 xmlFree(proxy);
233 proxy = NULL;
234 }
235 if (proxyUser != NULL) {
236 xmlFree(proxyUser);
237 proxyUser = NULL;
238 }
239 if (proxyPasswd != NULL) {
240 xmlFree(proxyPasswd);
241 proxyPasswd = NULL;
242 }
Owen Taylor3473f882001-02-23 17:55:21 +0000243#ifdef _WINSOCKAPI_
244 if (initialized)
245 WSACleanup();
246#endif
247 initialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000248}
249
250/**
251 * xmlNanoFTPProxy:
252 * @host: the proxy host name
253 * @port: the proxy port
254 * @user: the proxy user name
255 * @passwd: the proxy password
256 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
257 *
258 * Setup the FTP proxy informations.
259 * This can also be done by using ftp_proxy ftp_proxy_user and
260 * ftp_proxy_password environment variables.
261 */
262
263void
264xmlNanoFTPProxy(const char *host, int port, const char *user,
265 const char *passwd, int type) {
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000266 if (proxy != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000267 xmlFree(proxy);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000268 proxy = NULL;
269 }
270 if (proxyUser != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000271 xmlFree(proxyUser);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000272 proxyUser = NULL;
273 }
274 if (proxyPasswd != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000275 xmlFree(proxyPasswd);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000276 proxyPasswd = NULL;
277 }
Owen Taylor3473f882001-02-23 17:55:21 +0000278 if (host)
279 proxy = xmlMemStrdup(host);
280 if (user)
281 proxyUser = xmlMemStrdup(user);
282 if (passwd)
283 proxyPasswd = xmlMemStrdup(passwd);
284 proxyPort = port;
285 proxyType = type;
286}
287
288/**
289 * xmlNanoFTPScanURL:
290 * @ctx: an FTP context
291 * @URL: The URL used to initialize the context
292 *
293 * (Re)Initialize an FTP context by parsing the URL and finding
294 * the protocol host port and path it indicates.
295 */
296
297static void
298xmlNanoFTPScanURL(void *ctx, const char *URL) {
299 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
William M. Brack015ccb22005-02-13 08:18:52 +0000300 xmlURIPtr uri;
Owen Taylor3473f882001-02-23 17:55:21 +0000301
William M. Brack015ccb22005-02-13 08:18:52 +0000302 /*
303 * Clear any existing data from the context
304 */
Owen Taylor3473f882001-02-23 17:55:21 +0000305 if (ctxt->protocol != NULL) {
306 xmlFree(ctxt->protocol);
307 ctxt->protocol = NULL;
308 }
309 if (ctxt->hostname != NULL) {
310 xmlFree(ctxt->hostname);
311 ctxt->hostname = NULL;
312 }
313 if (ctxt->path != NULL) {
314 xmlFree(ctxt->path);
315 ctxt->path = NULL;
316 }
317 if (URL == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +0000318
William M. Brack015ccb22005-02-13 08:18:52 +0000319 uri = xmlParseURI(URL);
320 if (uri == NULL)
321 return;
322
323 if ((uri->scheme == NULL) || (uri->server == NULL)) {
324 xmlFreeURI(uri);
325 return;
326 }
327
328 ctxt->protocol = xmlMemStrdup(uri->scheme);
329 ctxt->hostname = xmlMemStrdup(uri->server);
330 if (uri->path != NULL)
331 ctxt->path = xmlMemStrdup(uri->path);
332 else
333 ctxt->path = xmlMemStrdup("/");
334 if (uri->port != 0)
335 ctxt->port = uri->port;
336
337 if (uri->user != NULL) {
338 char *cptr;
339 if ((cptr=strchr(uri->user, ':')) == NULL)
340 ctxt->user = xmlMemStrdup(uri->user);
341 else {
342 ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
343 (cptr - uri->user));
344 ctxt->passwd = xmlMemStrdup(cptr+1);
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000345 }
346 }
347
William M. Brack015ccb22005-02-13 08:18:52 +0000348 xmlFreeURI(uri);
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000349
Owen Taylor3473f882001-02-23 17:55:21 +0000350}
351
352/**
353 * xmlNanoFTPUpdateURL:
354 * @ctx: an FTP context
355 * @URL: The URL used to update the context
356 *
357 * Update an FTP context by parsing the URL and finding
358 * new path it indicates. If there is an error in the
359 * protocol, hostname, port or other information, the
360 * error is raised. It indicates a new connection has to
361 * be established.
362 *
363 * Returns 0 if Ok, -1 in case of error (other host).
364 */
365
366int
367xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
368 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
William M. Brack015ccb22005-02-13 08:18:52 +0000369 xmlURIPtr uri;
Owen Taylor3473f882001-02-23 17:55:21 +0000370
371 if (URL == NULL)
372 return(-1);
373 if (ctxt == NULL)
374 return(-1);
375 if (ctxt->protocol == NULL)
376 return(-1);
377 if (ctxt->hostname == NULL)
378 return(-1);
William M. Brack015ccb22005-02-13 08:18:52 +0000379
380 uri = xmlParseURI(URL);
381 if (uri == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000382 return(-1);
383
William M. Brack015ccb22005-02-13 08:18:52 +0000384 if ((uri->scheme == NULL) || (uri->server == NULL)) {
385 xmlFreeURI(uri);
386 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000387 }
William M. Brack015ccb22005-02-13 08:18:52 +0000388 if ((strcmp(ctxt->protocol, uri->scheme)) ||
389 (strcmp(ctxt->hostname, uri->server)) ||
390 ((uri->port != 0) && (ctxt->port != uri->port))) {
391 xmlFreeURI(uri);
392 return(-1);
393 }
394
395 if (uri->port != 0)
396 ctxt->port = uri->port;
397
Owen Taylor3473f882001-02-23 17:55:21 +0000398 if (ctxt->path != NULL) {
399 xmlFree(ctxt->path);
400 ctxt->path = NULL;
401 }
402
William M. Brack015ccb22005-02-13 08:18:52 +0000403 if (uri->path == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000404 ctxt->path = xmlMemStrdup("/");
William M. Brack015ccb22005-02-13 08:18:52 +0000405 else
406 ctxt->path = xmlMemStrdup(uri->path);
407
408 xmlFreeURI(uri);
409
Owen Taylor3473f882001-02-23 17:55:21 +0000410 return(0);
411}
412
413/**
414 * xmlNanoFTPScanProxy:
415 * @URL: The proxy URL used to initialize the proxy context
416 *
417 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
418 * the protocol host port it indicates.
419 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
420 * A NULL URL cleans up proxy informations.
421 */
422
423void
424xmlNanoFTPScanProxy(const char *URL) {
William M. Brack015ccb22005-02-13 08:18:52 +0000425 xmlURIPtr uri;
Owen Taylor3473f882001-02-23 17:55:21 +0000426
427 if (proxy != NULL) {
428 xmlFree(proxy);
429 proxy = NULL;
430 }
William M. Brack015ccb22005-02-13 08:18:52 +0000431 proxyPort = 0;
432
Owen Taylor3473f882001-02-23 17:55:21 +0000433#ifdef DEBUG_FTP
434 if (URL == NULL)
William M. Brack015ccb22005-02-13 08:18:52 +0000435 xmlGenericError(xmlGenericErrorContext,
436 "Removing FTP proxy info\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000437 else
William M. Brack015ccb22005-02-13 08:18:52 +0000438 xmlGenericError(xmlGenericErrorContext,
439 "Using FTP proxy %s\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +0000440#endif
441 if (URL == NULL) return;
William M. Brack015ccb22005-02-13 08:18:52 +0000442
443 uri = xmlParseURI(URL);
444 if ((uri == NULL) || (uri->scheme == NULL) ||
445 (strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
446 __xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
447 if (uri != NULL)
448 xmlFreeURI(uri);
449 return;
Owen Taylor3473f882001-02-23 17:55:21 +0000450 }
William M. Brack015ccb22005-02-13 08:18:52 +0000451
452 proxy = xmlMemStrdup(uri->server);
453 if (uri->port != 0)
454 proxyPort = uri->port;
Owen Taylor3473f882001-02-23 17:55:21 +0000455
William M. Brack015ccb22005-02-13 08:18:52 +0000456 xmlFreeURI(uri);
Owen Taylor3473f882001-02-23 17:55:21 +0000457}
458
459/**
460 * xmlNanoFTPNewCtxt:
461 * @URL: The URL used to initialize the context
462 *
463 * Allocate and initialize a new FTP context.
464 *
465 * Returns an FTP context or NULL in case of error.
466 */
467
468void*
469xmlNanoFTPNewCtxt(const char *URL) {
470 xmlNanoFTPCtxtPtr ret;
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000471 char *unescaped;
Owen Taylor3473f882001-02-23 17:55:21 +0000472
473 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000474 if (ret == NULL) {
475 xmlFTPErrMemory("allocating FTP context");
476 return(NULL);
477 }
Owen Taylor3473f882001-02-23 17:55:21 +0000478
479 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
480 ret->port = 21;
481 ret->passive = 1;
482 ret->returnValue = 0;
483 ret->controlBufIndex = 0;
484 ret->controlBufUsed = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000485 ret->controlFd = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000486
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000487 unescaped = xmlURIUnescapeString(URL, 0, NULL);
Daniel Veillardb1b3a3e2004-11-03 23:25:47 +0000488 if (unescaped != NULL) {
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000489 xmlNanoFTPScanURL(ret, unescaped);
Daniel Veillardb1b3a3e2004-11-03 23:25:47 +0000490 xmlFree(unescaped);
491 } else if (URL != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000492 xmlNanoFTPScanURL(ret, URL);
493
494 return(ret);
495}
496
497/**
498 * xmlNanoFTPFreeCtxt:
499 * @ctx: an FTP context
500 *
501 * Frees the context after closing the connection.
502 */
503
504void
505xmlNanoFTPFreeCtxt(void * ctx) {
506 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
507 if (ctxt == NULL) return;
508 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
509 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
510 if (ctxt->path != NULL) xmlFree(ctxt->path);
511 ctxt->passive = 1;
512 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
513 ctxt->controlFd = -1;
514 ctxt->controlBufIndex = -1;
515 ctxt->controlBufUsed = -1;
516 xmlFree(ctxt);
517}
518
519/**
520 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000521 * @buf: the buffer containing the response
522 * @len: the buffer length
523 *
524 * Parsing of the server answer, we just extract the code.
525 *
526 * returns 0 for errors
527 * +XXX for last line of response
528 * -XXX for response to be continued
529 */
530static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000531xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000532 int val = 0;
533
534 if (len < 3) return(-1);
535 if ((*buf >= '0') && (*buf <= '9'))
536 val = val * 10 + (*buf - '0');
537 else
538 return(0);
539 buf++;
540 if ((*buf >= '0') && (*buf <= '9'))
541 val = val * 10 + (*buf - '0');
542 else
543 return(0);
544 buf++;
545 if ((*buf >= '0') && (*buf <= '9'))
546 val = val * 10 + (*buf - '0');
547 else
548 return(0);
549 buf++;
550 if (*buf == '-')
551 return(-val);
552 return(val);
553}
554
555/**
556 * xmlNanoFTPGetMore:
557 * @ctx: an FTP context
558 *
559 * Read more information from the FTP control connection
560 * Returns the number of bytes read, < 0 indicates an error
561 */
562static int
563xmlNanoFTPGetMore(void *ctx) {
564 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
565 int len;
566 int size;
567
Daniel Veillard27f20102004-11-05 11:50:11 +0000568 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
569
Owen Taylor3473f882001-02-23 17:55:21 +0000570 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
571#ifdef DEBUG_FTP
572 xmlGenericError(xmlGenericErrorContext,
573 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
574 ctxt->controlBufIndex);
575#endif
576 return(-1);
577 }
578
579 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
580#ifdef DEBUG_FTP
581 xmlGenericError(xmlGenericErrorContext,
582 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
583 ctxt->controlBufUsed);
584#endif
585 return(-1);
586 }
587 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
588#ifdef DEBUG_FTP
589 xmlGenericError(xmlGenericErrorContext,
590 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
591 ctxt->controlBufIndex, ctxt->controlBufUsed);
592#endif
593 return(-1);
594 }
595
596 /*
597 * First pack the control buffer
598 */
599 if (ctxt->controlBufIndex > 0) {
600 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
601 ctxt->controlBufUsed - ctxt->controlBufIndex);
602 ctxt->controlBufUsed -= ctxt->controlBufIndex;
603 ctxt->controlBufIndex = 0;
604 }
605 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
606 if (size == 0) {
607#ifdef DEBUG_FTP
608 xmlGenericError(xmlGenericErrorContext,
609 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
610#endif
611 return(0);
612 }
613
614 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000615 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000616 */
617 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
618 size, 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000619 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000620 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
621 ctxt->controlFd = -1;
622 return(-1);
623 }
624#ifdef DEBUG_FTP
625 xmlGenericError(xmlGenericErrorContext,
626 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
627 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
628#endif
629 ctxt->controlBufUsed += len;
630 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
631
632 return(len);
633}
634
635/**
636 * xmlNanoFTPReadResponse:
637 * @ctx: an FTP context
638 *
639 * Read the response from the FTP server after a command.
640 * Returns the code number
641 */
642static int
643xmlNanoFTPReadResponse(void *ctx) {
644 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
645 char *ptr, *end;
646 int len;
647 int res = -1, cur = -1;
648
Daniel Veillard27f20102004-11-05 11:50:11 +0000649 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
650
Owen Taylor3473f882001-02-23 17:55:21 +0000651get_more:
652 /*
653 * Assumes everything up to controlBuf[controlBufIndex] has been read
654 * and analyzed.
655 */
656 len = xmlNanoFTPGetMore(ctx);
657 if (len < 0) {
658 return(-1);
659 }
660 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
661 return(-1);
662 }
663 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
664 end = &ctxt->controlBuf[ctxt->controlBufUsed];
665
666#ifdef DEBUG_FTP
667 xmlGenericError(xmlGenericErrorContext,
668 "\n<<<\n%s\n--\n", ptr);
669#endif
670 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000671 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000672 if (cur > 0) {
673 /*
674 * Successfully scanned the control code, scratch
675 * till the end of the line, but keep the index to be
676 * able to analyze the result if needed.
677 */
678 res = cur;
679 ptr += 3;
680 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
681 while ((ptr < end) && (*ptr != '\n')) ptr++;
682 if (*ptr == '\n') ptr++;
683 if (*ptr == '\r') ptr++;
684 break;
685 }
686 while ((ptr < end) && (*ptr != '\n')) ptr++;
687 if (ptr >= end) {
688 ctxt->controlBufIndex = ctxt->controlBufUsed;
689 goto get_more;
690 }
691 if (*ptr != '\r') ptr++;
692 }
693
694 if (res < 0) goto get_more;
695 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
696#ifdef DEBUG_FTP
697 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
698 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
699#endif
700
701#ifdef DEBUG_FTP
702 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
703#endif
704 return(res / 100);
705}
706
707/**
708 * xmlNanoFTPGetResponse:
709 * @ctx: an FTP context
710 *
711 * Get the response from the FTP server after a command.
712 * Returns the code number
713 */
714
715int
716xmlNanoFTPGetResponse(void *ctx) {
717 int res;
718
719 res = xmlNanoFTPReadResponse(ctx);
720
721 return(res);
722}
723
724/**
725 * xmlNanoFTPCheckResponse:
726 * @ctx: an FTP context
727 *
728 * Check if there is a response from the FTP server after a command.
729 * Returns the code number, or 0
730 */
731
732int
733xmlNanoFTPCheckResponse(void *ctx) {
734 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
735 fd_set rfd;
736 struct timeval tv;
737
Daniel Veillard27f20102004-11-05 11:50:11 +0000738 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000739 tv.tv_sec = 0;
740 tv.tv_usec = 0;
741 FD_ZERO(&rfd);
742 FD_SET(ctxt->controlFd, &rfd);
743 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
744 case 0:
745 return(0);
746 case -1:
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000747 __xmlIOErr(XML_FROM_FTP, 0, "select");
Owen Taylor3473f882001-02-23 17:55:21 +0000748 return(-1);
749
750 }
751
752 return(xmlNanoFTPReadResponse(ctx));
753}
754
755/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000756 * Send the user authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000757 */
758
759static int
760xmlNanoFTPSendUser(void *ctx) {
761 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
762 char buf[200];
763 int len;
764 int res;
765
766 if (ctxt->user == NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000767 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000768 else
Owen Taylor3473f882001-02-23 17:55:21 +0000769 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000770 buf[sizeof(buf) - 1] = 0;
771 len = strlen(buf);
772#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000773 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000774#endif
775 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000776 if (res < 0) {
777 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
778 return(res);
779 }
Owen Taylor3473f882001-02-23 17:55:21 +0000780 return(0);
781}
782
783/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000784 * Send the password authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000785 */
786
787static int
788xmlNanoFTPSendPasswd(void *ctx) {
789 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
790 char buf[200];
791 int len;
792 int res;
793
794 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000795 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000796 else
Owen Taylor3473f882001-02-23 17:55:21 +0000797 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000798 buf[sizeof(buf) - 1] = 0;
799 len = strlen(buf);
800#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000801 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000802#endif
803 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000804 if (res < 0) {
805 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
806 return(res);
807 }
Owen Taylor3473f882001-02-23 17:55:21 +0000808 return(0);
809}
810
811/**
812 * xmlNanoFTPQuit:
813 * @ctx: an FTP context
814 *
815 * Send a QUIT command to the server
816 *
817 * Returns -1 in case of error, 0 otherwise
818 */
819
820
821int
822xmlNanoFTPQuit(void *ctx) {
823 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
824 char buf[200];
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000825 int len, res;
Owen Taylor3473f882001-02-23 17:55:21 +0000826
Daniel Veillard27f20102004-11-05 11:50:11 +0000827 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
828
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000829 snprintf(buf, sizeof(buf), "QUIT\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000830 len = strlen(buf);
831#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000832 xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
Owen Taylor3473f882001-02-23 17:55:21 +0000833#endif
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000834 res = send(ctxt->controlFd, buf, len, 0);
835 if (res < 0) {
836 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
837 return(res);
838 }
Owen Taylor3473f882001-02-23 17:55:21 +0000839 return(0);
840}
841
842/**
843 * xmlNanoFTPConnect:
844 * @ctx: an FTP context
845 *
846 * Tries to open a control connection
847 *
848 * Returns -1 in case of error, 0 otherwise
849 */
850
851int
852xmlNanoFTPConnect(void *ctx) {
853 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
854 struct hostent *hp;
855 int port;
856 int res;
Daniel Veillard2db8c122003-07-08 12:16:59 +0000857 int addrlen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +0000858
859 if (ctxt == NULL)
860 return(-1);
861 if (ctxt->hostname == NULL)
862 return(-1);
863
864 /*
865 * do the blocking DNS query.
866 */
Owen Taylor3473f882001-02-23 17:55:21 +0000867 if (proxy) {
868 port = proxyPort;
869 } else {
870 port = ctxt->port;
871 }
872 if (port == 0)
873 port = 21;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000874
875 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
876
877#ifdef SUPPORT_IP6
878 if (have_ipv6 ()) {
Daniel Veillard2db8c122003-07-08 12:16:59 +0000879 struct addrinfo hints, *tmp, *result;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000880
881 result = NULL;
882 memset (&hints, 0, sizeof(hints));
883 hints.ai_socktype = SOCK_STREAM;
884
885 if (proxy) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000886 if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
887 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000888 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000889 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000890 }
891 else
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000892 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
893 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000894 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000895 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000896
Daniel Veillard2db8c122003-07-08 12:16:59 +0000897 for (tmp = result; tmp; tmp = tmp->ai_next)
898 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000899 break;
900
Daniel Veillard3dc93a42003-07-10 14:04:33 +0000901 if (!tmp) {
902 if (result)
903 freeaddrinfo (result);
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000904 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillard3dc93a42003-07-10 14:04:33 +0000905 return (-1);
906 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000907 if (tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
908 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
909 return (-1);
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000910 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000911 if (tmp->ai_family == AF_INET6) {
912 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
913 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
914 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
915 }
916 else {
917 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
918 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
919 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
920 }
921 addrlen = tmp->ai_addrlen;
922 freeaddrinfo (result);
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000923 }
924 else
925#endif
926 {
927 if (proxy)
928 hp = gethostbyname (proxy);
929 else
930 hp = gethostbyname (ctxt->hostname);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000931 if (hp == NULL) {
932 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000933 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000934 }
Daniel Veillard6927b102004-10-27 17:29:04 +0000935 if ((unsigned int) hp->h_length >
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000936 sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
937 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
938 return (-1);
939 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000940
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000941 /*
942 * Prepare the socket
943 */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000944 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
945 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
946 hp->h_addr_list[0], hp->h_length);
947 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = htons (port);
948 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
949 addrlen = sizeof (struct sockaddr_in);
950 }
951
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000952 if (ctxt->controlFd < 0) {
953 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000954 return(-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000955 }
Owen Taylor3473f882001-02-23 17:55:21 +0000956
957 /*
958 * Do the connect.
959 */
960 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000961 addrlen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000962 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
Owen Taylor3473f882001-02-23 17:55:21 +0000963 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
964 ctxt->controlFd = -1;
965 return(-1);
966 }
967
968 /*
969 * Wait for the HELLO from the server.
970 */
971 res = xmlNanoFTPGetResponse(ctxt);
972 if (res != 2) {
973 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
974 ctxt->controlFd = -1;
975 return(-1);
976 }
977
978 /*
979 * State diagram for the login operation on the FTP server
980 *
981 * Reference: RFC 959
982 *
983 * 1
984 * +---+ USER +---+------------->+---+
985 * | B |---------->| W | 2 ---->| E |
986 * +---+ +---+------ | -->+---+
987 * | | | | |
988 * 3 | | 4,5 | | |
989 * -------------- ----- | | |
990 * | | | | |
991 * | | | | |
992 * | --------- |
993 * | 1| | | |
994 * V | | | |
995 * +---+ PASS +---+ 2 | ------>+---+
996 * | |---------->| W |------------->| S |
997 * +---+ +---+ ---------->+---+
998 * | | | | |
999 * 3 | |4,5| | |
1000 * -------------- -------- |
1001 * | | | | |
1002 * | | | | |
1003 * | -----------
1004 * | 1,3| | | |
1005 * V | 2| | |
1006 * +---+ ACCT +---+-- | ----->+---+
1007 * | |---------->| W | 4,5 -------->| F |
1008 * +---+ +---+------------->+---+
1009 *
1010 * Of course in case of using a proxy this get really nasty and is not
1011 * standardized at all :-(
1012 */
1013 if (proxy) {
1014 int len;
1015 char buf[400];
1016
1017 if (proxyUser != NULL) {
1018 /*
1019 * We need proxy auth
1020 */
Owen Taylor3473f882001-02-23 17:55:21 +00001021 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +00001022 buf[sizeof(buf) - 1] = 0;
1023 len = strlen(buf);
1024#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001025 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001026#endif
1027 res = send(ctxt->controlFd, buf, len, 0);
1028 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001029 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001030 closesocket(ctxt->controlFd);
1031 ctxt->controlFd = -1;
1032 return(res);
1033 }
1034 res = xmlNanoFTPGetResponse(ctxt);
1035 switch (res) {
1036 case 2:
1037 if (proxyPasswd == NULL)
1038 break;
1039 case 3:
1040 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001041 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +00001042 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001043 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001044 buf[sizeof(buf) - 1] = 0;
1045 len = strlen(buf);
1046#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001047 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001048#endif
1049 res = send(ctxt->controlFd, buf, len, 0);
1050 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001051 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001052 closesocket(ctxt->controlFd);
1053 ctxt->controlFd = -1;
1054 return(res);
1055 }
1056 res = xmlNanoFTPGetResponse(ctxt);
1057 if (res > 3) {
1058 closesocket(ctxt->controlFd);
1059 ctxt->controlFd = -1;
1060 return(-1);
1061 }
1062 break;
1063 case 1:
1064 break;
1065 case 4:
1066 case 5:
1067 case -1:
1068 default:
1069 closesocket(ctxt->controlFd);
1070 ctxt->controlFd = -1;
1071 return(-1);
1072 }
1073 }
1074
1075 /*
1076 * We assume we don't need more authentication to the proxy
1077 * and that it succeeded :-\
1078 */
1079 switch (proxyType) {
1080 case 0:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001081 /* we will try in sequence */
Owen Taylor3473f882001-02-23 17:55:21 +00001082 case 1:
1083 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001084 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001085 buf[sizeof(buf) - 1] = 0;
1086 len = strlen(buf);
1087#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001088 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001089#endif
1090 res = send(ctxt->controlFd, buf, len, 0);
1091 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001092 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001093 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1094 ctxt->controlFd = -1;
1095 return(res);
1096 }
1097 res = xmlNanoFTPGetResponse(ctxt);
1098 if (res == 2) {
1099 /* we assume it worked :-\ 1 is error for SITE command */
1100 proxyType = 1;
1101 break;
1102 }
1103 if (proxyType == 1) {
1104 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1105 ctxt->controlFd = -1;
1106 return(-1);
1107 }
1108 case 2:
1109 /* USER user@host command */
1110 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001111 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1112 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001113 else
Owen Taylor3473f882001-02-23 17:55:21 +00001114 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1115 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001116 buf[sizeof(buf) - 1] = 0;
1117 len = strlen(buf);
1118#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001119 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001120#endif
1121 res = send(ctxt->controlFd, buf, len, 0);
1122 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001123 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001124 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1125 ctxt->controlFd = -1;
1126 return(res);
1127 }
1128 res = xmlNanoFTPGetResponse(ctxt);
1129 if ((res == 1) || (res == 2)) {
1130 /* we assume it worked :-\ */
1131 proxyType = 2;
1132 return(0);
1133 }
1134 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001135 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001136 else
Owen Taylor3473f882001-02-23 17:55:21 +00001137 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001138 buf[sizeof(buf) - 1] = 0;
1139 len = strlen(buf);
1140#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001141 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001142#endif
1143 res = send(ctxt->controlFd, buf, len, 0);
1144 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001145 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001146 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1147 ctxt->controlFd = -1;
1148 return(res);
1149 }
1150 res = xmlNanoFTPGetResponse(ctxt);
1151 if ((res == 1) || (res == 2)) {
1152 /* we assume it worked :-\ */
1153 proxyType = 2;
1154 return(0);
1155 }
1156 if (proxyType == 2) {
1157 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1158 ctxt->controlFd = -1;
1159 return(-1);
1160 }
1161 case 3:
1162 /*
1163 * If you need support for other Proxy authentication scheme
1164 * send the code or at least the sequence in use.
1165 */
1166 default:
1167 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1168 ctxt->controlFd = -1;
1169 return(-1);
1170 }
1171 }
1172 /*
1173 * Non-proxy handling.
1174 */
1175 res = xmlNanoFTPSendUser(ctxt);
1176 if (res < 0) {
1177 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1178 ctxt->controlFd = -1;
1179 return(-1);
1180 }
1181 res = xmlNanoFTPGetResponse(ctxt);
1182 switch (res) {
1183 case 2:
1184 return(0);
1185 case 3:
1186 break;
1187 case 1:
1188 case 4:
1189 case 5:
1190 case -1:
1191 default:
1192 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1193 ctxt->controlFd = -1;
1194 return(-1);
1195 }
1196 res = xmlNanoFTPSendPasswd(ctxt);
1197 if (res < 0) {
1198 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1199 ctxt->controlFd = -1;
1200 return(-1);
1201 }
1202 res = xmlNanoFTPGetResponse(ctxt);
1203 switch (res) {
1204 case 2:
1205 break;
1206 case 3:
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001207 __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
1208 "FTP server asking for ACCNT on anonymous\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001209 case 1:
1210 case 4:
1211 case 5:
1212 case -1:
1213 default:
1214 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1215 ctxt->controlFd = -1;
1216 return(-1);
1217 }
1218
1219 return(0);
1220}
1221
1222/**
1223 * xmlNanoFTPConnectTo:
1224 * @server: an FTP server name
1225 * @port: the port (use 21 if 0)
1226 *
1227 * Tries to open a control connection to the given server/port
1228 *
1229 * Returns an fTP context or NULL if it failed
1230 */
1231
1232void*
1233xmlNanoFTPConnectTo(const char *server, int port) {
1234 xmlNanoFTPCtxtPtr ctxt;
1235 int res;
1236
1237 xmlNanoFTPInit();
1238 if (server == NULL)
1239 return(NULL);
Daniel Veillard27f20102004-11-05 11:50:11 +00001240 if (port <= 0)
1241 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001242 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1243 ctxt->hostname = xmlMemStrdup(server);
1244 if (port != 0)
1245 ctxt->port = port;
1246 res = xmlNanoFTPConnect(ctxt);
1247 if (res < 0) {
1248 xmlNanoFTPFreeCtxt(ctxt);
1249 return(NULL);
1250 }
1251 return(ctxt);
1252}
1253
1254/**
1255 * xmlNanoFTPCwd:
1256 * @ctx: an FTP context
1257 * @directory: a directory on the server
1258 *
1259 * Tries to change the remote directory
1260 *
1261 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1262 */
1263
1264int
Daniel Veillard34099b42004-11-04 17:34:35 +00001265xmlNanoFTPCwd(void *ctx, const char *directory) {
Owen Taylor3473f882001-02-23 17:55:21 +00001266 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1267 char buf[400];
1268 int len;
1269 int res;
1270
Daniel Veillard27f20102004-11-05 11:50:11 +00001271 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
William M. Brack015ccb22005-02-13 08:18:52 +00001272 if (directory == NULL) return 0;
Daniel Veillard27f20102004-11-05 11:50:11 +00001273
Owen Taylor3473f882001-02-23 17:55:21 +00001274 /*
1275 * Expected response code for CWD:
1276 *
1277 * CWD
1278 * 250
1279 * 500, 501, 502, 421, 530, 550
1280 */
Owen Taylor3473f882001-02-23 17:55:21 +00001281 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001282 buf[sizeof(buf) - 1] = 0;
1283 len = strlen(buf);
1284#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001285 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001286#endif
1287 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001288 if (res < 0) {
1289 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1290 return(res);
1291 }
Owen Taylor3473f882001-02-23 17:55:21 +00001292 res = xmlNanoFTPGetResponse(ctxt);
1293 if (res == 4) {
1294 return(-1);
1295 }
1296 if (res == 2) return(1);
1297 if (res == 5) {
1298 return(0);
1299 }
1300 return(0);
1301}
1302
1303/**
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001304 * xmlNanoFTPDele:
1305 * @ctx: an FTP context
1306 * @file: a file or directory on the server
1307 *
1308 * Tries to delete an item (file or directory) from server
1309 *
1310 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1311 */
1312
1313int
Daniel Veillard34099b42004-11-04 17:34:35 +00001314xmlNanoFTPDele(void *ctx, const char *file) {
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001315 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1316 char buf[400];
1317 int len;
1318 int res;
1319
Daniel Veillard27f20102004-11-05 11:50:11 +00001320 if ((ctxt == NULL) || (ctxt->controlFd < 0) || (file == NULL)) return(-1);
William M. Brack015ccb22005-02-13 08:18:52 +00001321 if (file == NULL) return (0);
Daniel Veillard27f20102004-11-05 11:50:11 +00001322
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001323 /*
1324 * Expected response code for DELE:
1325 *
1326 * DELE
1327 * 250
1328 * 450, 550
1329 * 500, 501, 502, 421, 530
1330 */
1331
1332 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1333 buf[sizeof(buf) - 1] = 0;
1334 len = strlen(buf);
1335#ifdef DEBUG_FTP
1336 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1337#endif
1338 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001339 if (res < 0) {
1340 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1341 return(res);
1342 }
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001343 res = xmlNanoFTPGetResponse(ctxt);
1344 if (res == 4) {
1345 return(-1);
1346 }
1347 if (res == 2) return(1);
1348 if (res == 5) {
1349 return(0);
1350 }
1351 return(0);
1352}
1353/**
Owen Taylor3473f882001-02-23 17:55:21 +00001354 * xmlNanoFTPGetConnection:
1355 * @ctx: an FTP context
1356 *
1357 * Try to open a data connection to the server. Currently only
1358 * passive mode is supported.
1359 *
1360 * Returns -1 incase of error, 0 otherwise
1361 */
1362
1363int
1364xmlNanoFTPGetConnection(void *ctx) {
1365 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1366 char buf[200], *cur;
1367 int len, i;
1368 int res;
1369 unsigned char ad[6], *adp, *portp;
1370 unsigned int temp[6];
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001371#ifdef SUPPORT_IP6
1372 struct sockaddr_storage dataAddr;
1373#else
Owen Taylor3473f882001-02-23 17:55:21 +00001374 struct sockaddr_in dataAddr;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001375#endif
Daniel Veillardc284c642005-03-31 10:24:24 +00001376 XML_SOCKLEN_T dataAddrLen;
Owen Taylor3473f882001-02-23 17:55:21 +00001377
Daniel Veillard27f20102004-11-05 11:50:11 +00001378 if (ctxt == NULL) return(-1);
1379
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001380 memset (&dataAddr, 0, sizeof(dataAddr));
1381#ifdef SUPPORT_IP6
1382 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1383 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1384 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1385 dataAddrLen = sizeof(struct sockaddr_in6);
1386 } else
1387#endif
1388 {
1389 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1390 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1391 dataAddrLen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001392 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001393
1394 if (ctxt->dataFd < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001395 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001396 return (-1);
1397 }
Owen Taylor3473f882001-02-23 17:55:21 +00001398
1399 if (ctxt->passive) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001400#ifdef SUPPORT_IP6
1401 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1402 snprintf (buf, sizeof(buf), "EPSV\r\n");
1403 else
1404#endif
1405 snprintf (buf, sizeof(buf), "PASV\r\n");
1406 len = strlen (buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001407#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001408 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001409#endif
1410 res = send(ctxt->controlFd, buf, len, 0);
1411 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001412 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001413 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1414 return(res);
1415 }
1416 res = xmlNanoFTPReadResponse(ctx);
1417 if (res != 2) {
1418 if (res == 5) {
1419 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1420 return(-1);
1421 } else {
1422 /*
1423 * retry with an active connection
1424 */
1425 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1426 ctxt->passive = 0;
1427 }
1428 }
1429 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1430 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001431#ifdef SUPPORT_IP6
1432 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1433 if (sscanf (cur, "%u", &temp[0]) != 1) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001434 __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001435 "Invalid answer to EPSV\n");
1436 if (ctxt->dataFd != -1) {
1437 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1438 }
1439 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001440 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001441 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1442 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
Owen Taylor3473f882001-02-23 17:55:21 +00001443 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001444 else
1445#endif
1446 {
1447 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1448 &temp[3], &temp[4], &temp[5]) != 6) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001449 __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001450 "Invalid answer to PASV\n");
1451 if (ctxt->dataFd != -1) {
1452 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1453 }
1454 return (-1);
1455 }
1456 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1457 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1458 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1459 }
1460
Owen Taylor3473f882001-02-23 17:55:21 +00001461 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001462 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
Owen Taylor3473f882001-02-23 17:55:21 +00001463 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1464 return (-1);
1465 }
1466 } else {
1467 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001468#ifdef SUPPORT_IP6
1469 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1470 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1471 else
1472#endif
1473 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1474
Owen Taylor3473f882001-02-23 17:55:21 +00001475 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001476 __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001477 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1478 return (-1);
1479 }
1480 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1481
1482 if (listen(ctxt->dataFd, 1) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001483 __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001484 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1485 return (-1);
1486 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001487#ifdef SUPPORT_IP6
1488 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1489 char buf6[INET6_ADDRSTRLEN];
1490 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1491 buf6, INET6_ADDRSTRLEN);
Daniel Veillard2db8c122003-07-08 12:16:59 +00001492 adp = (unsigned char *) buf6;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001493 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1494 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1495 } else
1496#endif
1497 {
1498 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1499 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1500 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1501 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1502 portp[0] & 0xff, portp[1] & 0xff);
1503 }
1504
Owen Taylor3473f882001-02-23 17:55:21 +00001505 buf[sizeof(buf) - 1] = 0;
1506 len = strlen(buf);
1507#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001508 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001509#endif
1510
1511 res = send(ctxt->controlFd, buf, len, 0);
1512 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001513 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001514 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1515 return(res);
1516 }
1517 res = xmlNanoFTPGetResponse(ctxt);
1518 if (res != 2) {
1519 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1520 return(-1);
1521 }
1522 }
1523 return(ctxt->dataFd);
1524
1525}
1526
1527/**
1528 * xmlNanoFTPCloseConnection:
1529 * @ctx: an FTP context
1530 *
1531 * Close the data connection from the server
1532 *
1533 * Returns -1 incase of error, 0 otherwise
1534 */
1535
1536int
1537xmlNanoFTPCloseConnection(void *ctx) {
1538 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1539 int res;
1540 fd_set rfd, efd;
1541 struct timeval tv;
1542
Daniel Veillard27f20102004-11-05 11:50:11 +00001543 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1544
Owen Taylor3473f882001-02-23 17:55:21 +00001545 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1546 tv.tv_sec = 15;
1547 tv.tv_usec = 0;
1548 FD_ZERO(&rfd);
1549 FD_SET(ctxt->controlFd, &rfd);
1550 FD_ZERO(&efd);
1551 FD_SET(ctxt->controlFd, &efd);
1552 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1553 if (res < 0) {
1554#ifdef DEBUG_FTP
1555 perror("select");
1556#endif
1557 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1558 return(-1);
1559 }
1560 if (res == 0) {
1561#ifdef DEBUG_FTP
1562 xmlGenericError(xmlGenericErrorContext,
1563 "xmlNanoFTPCloseConnection: timeout\n");
1564#endif
1565 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1566 } else {
1567 res = xmlNanoFTPGetResponse(ctxt);
1568 if (res != 2) {
1569 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1570 return(-1);
1571 }
1572 }
1573 return(0);
1574}
1575
1576/**
1577 * xmlNanoFTPParseList:
1578 * @list: some data listing received from the server
1579 * @callback: the user callback
1580 * @userData: the user callback data
1581 *
1582 * Parse at most one entry from the listing.
1583 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001584 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001585 */
1586
1587static int
1588xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1589 const char *cur = list;
1590 char filename[151];
1591 char attrib[11];
1592 char owner[11];
1593 char group[11];
1594 char month[4];
1595 int year = 0;
1596 int minute = 0;
1597 int hour = 0;
1598 int day = 0;
1599 unsigned long size = 0;
1600 int links = 0;
1601 int i;
1602
1603 if (!strncmp(cur, "total", 5)) {
1604 cur += 5;
1605 while (*cur == ' ') cur++;
1606 while ((*cur >= '0') && (*cur <= '9'))
1607 links = (links * 10) + (*cur++ - '0');
1608 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1609 cur++;
1610 return(cur - list);
1611 } else if (*list == '+') {
1612 return(0);
1613 } else {
1614 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1615 cur++;
1616 if (*cur == 0) return(0);
1617 i = 0;
1618 while (*cur != ' ') {
1619 if (i < 10)
1620 attrib[i++] = *cur;
1621 cur++;
1622 if (*cur == 0) return(0);
1623 }
1624 attrib[10] = 0;
1625 while (*cur == ' ') cur++;
1626 if (*cur == 0) return(0);
1627 while ((*cur >= '0') && (*cur <= '9'))
1628 links = (links * 10) + (*cur++ - '0');
1629 while (*cur == ' ') cur++;
1630 if (*cur == 0) return(0);
1631 i = 0;
1632 while (*cur != ' ') {
1633 if (i < 10)
1634 owner[i++] = *cur;
1635 cur++;
1636 if (*cur == 0) return(0);
1637 }
1638 owner[i] = 0;
1639 while (*cur == ' ') cur++;
1640 if (*cur == 0) return(0);
1641 i = 0;
1642 while (*cur != ' ') {
1643 if (i < 10)
1644 group[i++] = *cur;
1645 cur++;
1646 if (*cur == 0) return(0);
1647 }
1648 group[i] = 0;
1649 while (*cur == ' ') cur++;
1650 if (*cur == 0) return(0);
1651 while ((*cur >= '0') && (*cur <= '9'))
1652 size = (size * 10) + (*cur++ - '0');
1653 while (*cur == ' ') cur++;
1654 if (*cur == 0) return(0);
1655 i = 0;
1656 while (*cur != ' ') {
1657 if (i < 3)
1658 month[i++] = *cur;
1659 cur++;
1660 if (*cur == 0) return(0);
1661 }
1662 month[i] = 0;
1663 while (*cur == ' ') cur++;
1664 if (*cur == 0) return(0);
1665 while ((*cur >= '0') && (*cur <= '9'))
1666 day = (day * 10) + (*cur++ - '0');
1667 while (*cur == ' ') cur++;
1668 if (*cur == 0) return(0);
1669 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1670 if ((cur[1] == ':') || (cur[2] == ':')) {
1671 while ((*cur >= '0') && (*cur <= '9'))
1672 hour = (hour * 10) + (*cur++ - '0');
1673 if (*cur == ':') cur++;
1674 while ((*cur >= '0') && (*cur <= '9'))
1675 minute = (minute * 10) + (*cur++ - '0');
1676 } else {
1677 while ((*cur >= '0') && (*cur <= '9'))
1678 year = (year * 10) + (*cur++ - '0');
1679 }
1680 while (*cur == ' ') cur++;
1681 if (*cur == 0) return(0);
1682 i = 0;
1683 while ((*cur != '\n') && (*cur != '\r')) {
1684 if (i < 150)
1685 filename[i++] = *cur;
1686 cur++;
1687 if (*cur == 0) return(0);
1688 }
1689 filename[i] = 0;
1690 if ((*cur != '\n') && (*cur != '\r'))
1691 return(0);
1692 while ((*cur == '\n') || (*cur == '\r'))
1693 cur++;
1694 }
1695 if (callback != NULL) {
1696 callback(userData, filename, attrib, owner, group, size, links,
1697 year, month, day, hour, minute);
1698 }
1699 return(cur - list);
1700}
1701
1702/**
1703 * xmlNanoFTPList:
1704 * @ctx: an FTP context
1705 * @callback: the user callback
1706 * @userData: the user callback data
1707 * @filename: optional files to list
1708 *
1709 * Do a listing on the server. All files info are passed back
1710 * in the callbacks.
1711 *
1712 * Returns -1 incase of error, 0 otherwise
1713 */
1714
1715int
1716xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
Daniel Veillard34099b42004-11-04 17:34:35 +00001717 const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +00001718 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1719 char buf[4096 + 1];
1720 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001721 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001722 fd_set rfd, efd;
1723 struct timeval tv;
1724
William M. Brack015ccb22005-02-13 08:18:52 +00001725 if (ctxt == NULL) return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001726 if (filename == NULL) {
1727 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1728 return(-1);
1729 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1730 if (ctxt->dataFd == -1)
1731 return(-1);
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001732 snprintf(buf, sizeof(buf), "LIST -L\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001733 } else {
1734 if (filename[0] != '/') {
1735 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1736 return(-1);
1737 }
1738 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1739 if (ctxt->dataFd == -1)
1740 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001741 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001742 }
1743 buf[sizeof(buf) - 1] = 0;
1744 len = strlen(buf);
1745#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001746 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001747#endif
1748 res = send(ctxt->controlFd, buf, len, 0);
1749 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001750 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001751 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1752 return(res);
1753 }
1754 res = xmlNanoFTPReadResponse(ctxt);
1755 if (res != 1) {
1756 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1757 return(-res);
1758 }
1759
1760 do {
1761 tv.tv_sec = 1;
1762 tv.tv_usec = 0;
1763 FD_ZERO(&rfd);
1764 FD_SET(ctxt->dataFd, &rfd);
1765 FD_ZERO(&efd);
1766 FD_SET(ctxt->dataFd, &efd);
1767 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1768 if (res < 0) {
1769#ifdef DEBUG_FTP
1770 perror("select");
1771#endif
1772 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1773 return(-1);
1774 }
1775 if (res == 0) {
1776 res = xmlNanoFTPCheckResponse(ctxt);
1777 if (res < 0) {
1778 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1779 ctxt->dataFd = -1;
1780 return(-1);
1781 }
1782 if (res == 2) {
1783 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1784 return(0);
1785 }
1786
1787 continue;
1788 }
1789
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001790 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001791 __xmlIOErr(XML_FROM_FTP, 0, "recv");
Owen Taylor3473f882001-02-23 17:55:21 +00001792 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1793 ctxt->dataFd = -1;
1794 return(-1);
1795 }
1796#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001797 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001798#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001799 indx += len;
1800 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001801 base = 0;
1802 do {
1803 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1804 base += res;
1805 } while (res > 0);
1806
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001807 memmove(&buf[0], &buf[base], indx - base);
1808 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001809 } while (len != 0);
1810 xmlNanoFTPCloseConnection(ctxt);
1811 return(0);
1812}
1813
1814/**
1815 * xmlNanoFTPGetSocket:
1816 * @ctx: an FTP context
1817 * @filename: the file to retrieve (or NULL if path is in context).
1818 *
1819 * Initiate fetch of the given file from the server.
1820 *
1821 * Returns the socket for the data connection, or <0 in case of error
1822 */
1823
1824
1825int
1826xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1827 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1828 char buf[300];
1829 int res, len;
Daniel Veillard27f20102004-11-05 11:50:11 +00001830 if (ctx == NULL)
1831 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001832 if ((filename == NULL) && (ctxt->path == NULL))
1833 return(-1);
1834 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1835 if (ctxt->dataFd == -1)
1836 return(-1);
1837
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001838 snprintf(buf, sizeof(buf), "TYPE I\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001839 len = strlen(buf);
1840#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001841 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001842#endif
1843 res = send(ctxt->controlFd, buf, len, 0);
1844 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001845 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001846 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1847 return(res);
1848 }
1849 res = xmlNanoFTPReadResponse(ctxt);
1850 if (res != 2) {
1851 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1852 return(-res);
1853 }
1854 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001855 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001856 else
Owen Taylor3473f882001-02-23 17:55:21 +00001857 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001858 buf[sizeof(buf) - 1] = 0;
1859 len = strlen(buf);
1860#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001861 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001862#endif
1863 res = send(ctxt->controlFd, buf, len, 0);
1864 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001865 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001866 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1867 return(res);
1868 }
1869 res = xmlNanoFTPReadResponse(ctxt);
1870 if (res != 1) {
1871 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1872 return(-res);
1873 }
1874 return(ctxt->dataFd);
1875}
1876
1877/**
1878 * xmlNanoFTPGet:
1879 * @ctx: an FTP context
1880 * @callback: the user callback
1881 * @userData: the user callback data
1882 * @filename: the file to retrieve
1883 *
1884 * Fetch the given file from the server. All data are passed back
1885 * in the callbacks. The last callback has a size of 0 block.
1886 *
1887 * Returns -1 incase of error, 0 otherwise
1888 */
1889
1890int
1891xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1892 const char *filename) {
1893 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1894 char buf[4096];
1895 int len = 0, res;
1896 fd_set rfd;
1897 struct timeval tv;
1898
William M. Brack015ccb22005-02-13 08:18:52 +00001899 if (ctxt == NULL) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001900 if ((filename == NULL) && (ctxt->path == NULL))
1901 return(-1);
1902 if (callback == NULL)
1903 return(-1);
1904 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1905 return(-1);
1906
1907 do {
1908 tv.tv_sec = 1;
1909 tv.tv_usec = 0;
1910 FD_ZERO(&rfd);
1911 FD_SET(ctxt->dataFd, &rfd);
1912 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1913 if (res < 0) {
1914#ifdef DEBUG_FTP
1915 perror("select");
1916#endif
1917 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1918 return(-1);
1919 }
1920 if (res == 0) {
1921 res = xmlNanoFTPCheckResponse(ctxt);
1922 if (res < 0) {
1923 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1924 ctxt->dataFd = -1;
1925 return(-1);
1926 }
1927 if (res == 2) {
1928 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1929 return(0);
1930 }
1931
1932 continue;
1933 }
1934 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001935 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001936 callback(userData, buf, len);
1937 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1938 return(-1);
1939 }
1940 callback(userData, buf, len);
1941 } while (len != 0);
1942
1943 return(xmlNanoFTPCloseConnection(ctxt));
1944}
1945
1946/**
1947 * xmlNanoFTPRead:
1948 * @ctx: the FTP context
1949 * @dest: a buffer
1950 * @len: the buffer length
1951 *
1952 * This function tries to read @len bytes from the existing FTP connection
1953 * and saves them in @dest. This is a blocking call.
1954 *
1955 * Returns the number of byte read. 0 is an indication of an end of connection.
1956 * -1 indicates a parameter error.
1957 */
1958int
1959xmlNanoFTPRead(void *ctx, void *dest, int len) {
1960 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1961
1962 if (ctx == NULL) return(-1);
1963 if (ctxt->dataFd < 0) return(0);
1964 if (dest == NULL) return(-1);
1965 if (len <= 0) return(0);
1966
1967 len = recv(ctxt->dataFd, dest, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001968 if (len <= 0) {
William M. Brack015ccb22005-02-13 08:18:52 +00001969 if (len < 0)
1970 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001971 xmlNanoFTPCloseConnection(ctxt);
1972 }
Owen Taylor3473f882001-02-23 17:55:21 +00001973#ifdef DEBUG_FTP
1974 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1975#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001976 return(len);
1977}
1978
1979/**
1980 * xmlNanoFTPOpen:
1981 * @URL: the URL to the resource
1982 *
1983 * Start to fetch the given ftp:// resource
1984 *
1985 * Returns an FTP context, or NULL
1986 */
1987
1988void*
1989xmlNanoFTPOpen(const char *URL) {
1990 xmlNanoFTPCtxtPtr ctxt;
1991 int sock;
1992
1993 xmlNanoFTPInit();
1994 if (URL == NULL) return(NULL);
1995 if (strncmp("ftp://", URL, 6)) return(NULL);
1996
1997 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
1998 if (ctxt == NULL) return(NULL);
1999 if (xmlNanoFTPConnect(ctxt) < 0) {
2000 xmlNanoFTPFreeCtxt(ctxt);
2001 return(NULL);
2002 }
2003 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2004 if (sock < 0) {
2005 xmlNanoFTPFreeCtxt(ctxt);
2006 return(NULL);
2007 }
2008 return(ctxt);
2009}
2010
2011/**
2012 * xmlNanoFTPClose:
2013 * @ctx: an FTP context
2014 *
2015 * Close the connection and both control and transport
2016 *
2017 * Returns -1 incase of error, 0 otherwise
2018 */
2019
2020int
2021xmlNanoFTPClose(void *ctx) {
2022 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2023
2024 if (ctxt == NULL)
2025 return(-1);
2026
2027 if (ctxt->dataFd >= 0) {
2028 closesocket(ctxt->dataFd);
2029 ctxt->dataFd = -1;
2030 }
2031 if (ctxt->controlFd >= 0) {
2032 xmlNanoFTPQuit(ctxt);
2033 closesocket(ctxt->controlFd);
2034 ctxt->controlFd = -1;
2035 }
2036 xmlNanoFTPFreeCtxt(ctxt);
2037 return(0);
2038}
2039
2040#ifdef STANDALONE
2041/************************************************************************
2042 * *
2043 * Basic test in Standalone mode *
2044 * *
2045 ************************************************************************/
Daniel Veillard01c13b52002-12-10 15:19:08 +00002046static
Owen Taylor3473f882001-02-23 17:55:21 +00002047void ftpList(void *userData, const char *filename, const char* attrib,
2048 const char *owner, const char *group, unsigned long size, int links,
2049 int year, const char *month, int day, int hour, int minute) {
2050 xmlGenericError(xmlGenericErrorContext,
2051 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2052}
Daniel Veillard01c13b52002-12-10 15:19:08 +00002053static
Owen Taylor3473f882001-02-23 17:55:21 +00002054void ftpData(void *userData, const char *data, int len) {
2055 if (userData == NULL) return;
2056 if (len <= 0) {
Daniel Veillard82cb3192003-10-29 13:39:15 +00002057 fclose((FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002058 return;
2059 }
Daniel Veillard82cb3192003-10-29 13:39:15 +00002060 fwrite(data, len, 1, (FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002061}
2062
2063int main(int argc, char **argv) {
2064 void *ctxt;
2065 FILE *output;
2066 char *tstfile = NULL;
2067
2068 xmlNanoFTPInit();
2069 if (argc > 1) {
2070 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2071 if (xmlNanoFTPConnect(ctxt) < 0) {
2072 xmlGenericError(xmlGenericErrorContext,
2073 "Couldn't connect to %s\n", argv[1]);
2074 exit(1);
2075 }
2076 if (argc > 2)
2077 tstfile = argv[2];
2078 } else
2079 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2080 if (ctxt == NULL) {
2081 xmlGenericError(xmlGenericErrorContext,
2082 "Couldn't connect to localhost\n");
2083 exit(1);
2084 }
2085 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2086 output = fopen("/tmp/tstdata", "w");
2087 if (output != NULL) {
2088 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2089 xmlGenericError(xmlGenericErrorContext,
2090 "Failed to get file\n");
2091
2092 }
2093 xmlNanoFTPClose(ctxt);
2094 xmlMemoryDump();
2095 exit(0);
2096}
2097#endif /* STANDALONE */
2098#else /* !LIBXML_FTP_ENABLED */
2099#ifdef STANDALONE
2100#include <stdio.h>
2101int main(int argc, char **argv) {
2102 xmlGenericError(xmlGenericErrorContext,
2103 "%s : FTP support not compiled in\n", argv[0]);
2104 return(0);
2105}
2106#endif /* STANDALONE */
2107#endif /* LIBXML_FTP_ENABLED */
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002108#define bottom_nanoftp
2109#include "elfgcchack.h"