blob: dec980f613f0eb286e7f111de35f870822685fab [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
Daniel Veillardacf7ff02001-10-29 20:21:47 +000097#if defined(VMS) || defined(__VMS)
Daniel Veillardc284c642005-03-31 10:24:24 +000098#define XML_SOCKLEN_T unsigned int
Daniel Veillardacf7ff02001-10-29 20:21:47 +000099#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000100
Daniel Veillard89f7f272003-09-29 13:29:09 +0000101#ifdef __BEOS__
102#ifndef PF_INET
103#define PF_INET AF_INET
104#endif
105#endif
106
Daniel Veillarddb439252005-01-15 17:18:53 +0000107#ifdef _AIX
108#define ss_family __ss_family
109#endif
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000110
Owen Taylor3473f882001-02-23 17:55:21 +0000111#define FTP_COMMAND_OK 200
112#define FTP_SYNTAX_ERROR 500
113#define FTP_GET_PASSWD 331
114#define FTP_BUF_SIZE 512
115
William M. Brack030a7a12004-02-10 12:48:57 +0000116#define XML_NANO_MAX_URLBUF 4096
117
Owen Taylor3473f882001-02-23 17:55:21 +0000118typedef struct xmlNanoFTPCtxt {
119 char *protocol; /* the protocol name */
120 char *hostname; /* the host name */
121 int port; /* the port */
122 char *path; /* the path within the URL */
123 char *user; /* user string */
124 char *passwd; /* passwd string */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000125#ifdef SUPPORT_IP6
126 struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
127#else
Owen Taylor3473f882001-02-23 17:55:21 +0000128 struct sockaddr_in ftpAddr; /* the socket address struct */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000129#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000130 int passive; /* currently we support only passive !!! */
131 SOCKET controlFd; /* the file descriptor for the control socket */
132 SOCKET dataFd; /* the file descriptor for the data socket */
133 int state; /* WRITE / READ / CLOSED */
134 int returnValue; /* the protocol return value */
135 /* buffer for data received from the control connection */
136 char controlBuf[FTP_BUF_SIZE + 1];
137 int controlBufIndex;
138 int controlBufUsed;
139 int controlBufAnswer;
140} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
141
142static int initialized = 0;
143static char *proxy = NULL; /* the proxy name if any */
144static int proxyPort = 0; /* the proxy port if any */
145static char *proxyUser = NULL; /* user for proxy authentication */
146static char *proxyPasswd = NULL;/* passwd for proxy authentication */
147static int proxyType = 0; /* uses TYPE or a@b ? */
148
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000149#ifdef SUPPORT_IP6
Daniel Veillard2db8c122003-07-08 12:16:59 +0000150static
151int have_ipv6(void) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000152 int s;
153
154 s = socket (AF_INET6, SOCK_STREAM, 0);
155 if (s != -1) {
156 close (s);
157 return (1);
158 }
159 return (0);
160}
161#endif
162
Owen Taylor3473f882001-02-23 17:55:21 +0000163/**
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000164 * xmlFTPErrMemory:
165 * @extra: extra informations
166 *
167 * Handle an out of memory condition
168 */
169static void
170xmlFTPErrMemory(const char *extra)
171{
172 __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
173}
174
175/**
Owen Taylor3473f882001-02-23 17:55:21 +0000176 * xmlNanoFTPInit:
177 *
178 * Initialize the FTP protocol layer.
179 * Currently it just checks for proxy informations,
180 * and get the hostname
181 */
182
183void
184xmlNanoFTPInit(void) {
185 const char *env;
186#ifdef _WINSOCKAPI_
187 WSADATA wsaData;
188#endif
189
190 if (initialized)
191 return;
192
193#ifdef _WINSOCKAPI_
194 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
195 return;
196#endif
197
Owen Taylor3473f882001-02-23 17:55:21 +0000198 proxyPort = 21;
199 env = getenv("no_proxy");
Daniel Veillard29b17482004-08-16 00:39:03 +0000200 if (env && ((env[0] == '*' ) && (env[1] == 0)))
Owen Taylor3473f882001-02-23 17:55:21 +0000201 return;
202 env = getenv("ftp_proxy");
203 if (env != NULL) {
204 xmlNanoFTPScanProxy(env);
205 } else {
206 env = getenv("FTP_PROXY");
207 if (env != NULL) {
208 xmlNanoFTPScanProxy(env);
209 }
210 }
211 env = getenv("ftp_proxy_user");
212 if (env != NULL) {
213 proxyUser = xmlMemStrdup(env);
214 }
215 env = getenv("ftp_proxy_password");
216 if (env != NULL) {
217 proxyPasswd = xmlMemStrdup(env);
218 }
219 initialized = 1;
220}
221
222/**
Daniel Veillarde356c282001-03-10 12:32:04 +0000223 * xmlNanoFTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000224 *
225 * Cleanup the FTP protocol layer. This cleanup proxy informations.
226 */
227
228void
229xmlNanoFTPCleanup(void) {
230 if (proxy != NULL) {
231 xmlFree(proxy);
232 proxy = NULL;
233 }
234 if (proxyUser != NULL) {
235 xmlFree(proxyUser);
236 proxyUser = NULL;
237 }
238 if (proxyPasswd != NULL) {
239 xmlFree(proxyPasswd);
240 proxyPasswd = NULL;
241 }
Owen Taylor3473f882001-02-23 17:55:21 +0000242#ifdef _WINSOCKAPI_
243 if (initialized)
244 WSACleanup();
245#endif
246 initialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000247}
248
249/**
250 * xmlNanoFTPProxy:
251 * @host: the proxy host name
252 * @port: the proxy port
253 * @user: the proxy user name
254 * @passwd: the proxy password
255 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
256 *
257 * Setup the FTP proxy informations.
258 * This can also be done by using ftp_proxy ftp_proxy_user and
259 * ftp_proxy_password environment variables.
260 */
261
262void
263xmlNanoFTPProxy(const char *host, int port, const char *user,
264 const char *passwd, int type) {
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000265 if (proxy != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000266 xmlFree(proxy);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000267 proxy = NULL;
268 }
269 if (proxyUser != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000270 xmlFree(proxyUser);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000271 proxyUser = NULL;
272 }
273 if (proxyPasswd != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000274 xmlFree(proxyPasswd);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000275 proxyPasswd = NULL;
276 }
Owen Taylor3473f882001-02-23 17:55:21 +0000277 if (host)
278 proxy = xmlMemStrdup(host);
279 if (user)
280 proxyUser = xmlMemStrdup(user);
281 if (passwd)
282 proxyPasswd = xmlMemStrdup(passwd);
283 proxyPort = port;
284 proxyType = type;
285}
286
287/**
288 * xmlNanoFTPScanURL:
289 * @ctx: an FTP context
290 * @URL: The URL used to initialize the context
291 *
292 * (Re)Initialize an FTP context by parsing the URL and finding
293 * the protocol host port and path it indicates.
294 */
295
296static void
297xmlNanoFTPScanURL(void *ctx, const char *URL) {
298 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
William M. Brack015ccb22005-02-13 08:18:52 +0000299 xmlURIPtr uri;
Owen Taylor3473f882001-02-23 17:55:21 +0000300
William M. Brack015ccb22005-02-13 08:18:52 +0000301 /*
302 * Clear any existing data from the context
303 */
Owen Taylor3473f882001-02-23 17:55:21 +0000304 if (ctxt->protocol != NULL) {
305 xmlFree(ctxt->protocol);
306 ctxt->protocol = NULL;
307 }
308 if (ctxt->hostname != NULL) {
309 xmlFree(ctxt->hostname);
310 ctxt->hostname = NULL;
311 }
312 if (ctxt->path != NULL) {
313 xmlFree(ctxt->path);
314 ctxt->path = NULL;
315 }
316 if (URL == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +0000317
William M. Brack015ccb22005-02-13 08:18:52 +0000318 uri = xmlParseURI(URL);
319 if (uri == NULL)
320 return;
321
322 if ((uri->scheme == NULL) || (uri->server == NULL)) {
323 xmlFreeURI(uri);
324 return;
325 }
326
327 ctxt->protocol = xmlMemStrdup(uri->scheme);
328 ctxt->hostname = xmlMemStrdup(uri->server);
329 if (uri->path != NULL)
330 ctxt->path = xmlMemStrdup(uri->path);
331 else
332 ctxt->path = xmlMemStrdup("/");
333 if (uri->port != 0)
334 ctxt->port = uri->port;
335
336 if (uri->user != NULL) {
337 char *cptr;
338 if ((cptr=strchr(uri->user, ':')) == NULL)
339 ctxt->user = xmlMemStrdup(uri->user);
340 else {
341 ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
342 (cptr - uri->user));
343 ctxt->passwd = xmlMemStrdup(cptr+1);
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000344 }
345 }
346
William M. Brack015ccb22005-02-13 08:18:52 +0000347 xmlFreeURI(uri);
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000348
Owen Taylor3473f882001-02-23 17:55:21 +0000349}
350
351/**
352 * xmlNanoFTPUpdateURL:
353 * @ctx: an FTP context
354 * @URL: The URL used to update the context
355 *
356 * Update an FTP context by parsing the URL and finding
357 * new path it indicates. If there is an error in the
358 * protocol, hostname, port or other information, the
359 * error is raised. It indicates a new connection has to
360 * be established.
361 *
362 * Returns 0 if Ok, -1 in case of error (other host).
363 */
364
365int
366xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
367 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
William M. Brack015ccb22005-02-13 08:18:52 +0000368 xmlURIPtr uri;
Owen Taylor3473f882001-02-23 17:55:21 +0000369
370 if (URL == NULL)
371 return(-1);
372 if (ctxt == NULL)
373 return(-1);
374 if (ctxt->protocol == NULL)
375 return(-1);
376 if (ctxt->hostname == NULL)
377 return(-1);
William M. Brack015ccb22005-02-13 08:18:52 +0000378
379 uri = xmlParseURI(URL);
380 if (uri == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000381 return(-1);
382
William M. Brack015ccb22005-02-13 08:18:52 +0000383 if ((uri->scheme == NULL) || (uri->server == NULL)) {
384 xmlFreeURI(uri);
385 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000386 }
William M. Brack015ccb22005-02-13 08:18:52 +0000387 if ((strcmp(ctxt->protocol, uri->scheme)) ||
388 (strcmp(ctxt->hostname, uri->server)) ||
389 ((uri->port != 0) && (ctxt->port != uri->port))) {
390 xmlFreeURI(uri);
391 return(-1);
392 }
393
394 if (uri->port != 0)
395 ctxt->port = uri->port;
396
Owen Taylor3473f882001-02-23 17:55:21 +0000397 if (ctxt->path != NULL) {
398 xmlFree(ctxt->path);
399 ctxt->path = NULL;
400 }
401
William M. Brack015ccb22005-02-13 08:18:52 +0000402 if (uri->path == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000403 ctxt->path = xmlMemStrdup("/");
William M. Brack015ccb22005-02-13 08:18:52 +0000404 else
405 ctxt->path = xmlMemStrdup(uri->path);
406
407 xmlFreeURI(uri);
408
Owen Taylor3473f882001-02-23 17:55:21 +0000409 return(0);
410}
411
412/**
413 * xmlNanoFTPScanProxy:
414 * @URL: The proxy URL used to initialize the proxy context
415 *
416 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
417 * the protocol host port it indicates.
418 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
419 * A NULL URL cleans up proxy informations.
420 */
421
422void
423xmlNanoFTPScanProxy(const char *URL) {
William M. Brack015ccb22005-02-13 08:18:52 +0000424 xmlURIPtr uri;
Owen Taylor3473f882001-02-23 17:55:21 +0000425
426 if (proxy != NULL) {
427 xmlFree(proxy);
428 proxy = NULL;
429 }
William M. Brack015ccb22005-02-13 08:18:52 +0000430 proxyPort = 0;
431
Owen Taylor3473f882001-02-23 17:55:21 +0000432#ifdef DEBUG_FTP
433 if (URL == NULL)
William M. Brack015ccb22005-02-13 08:18:52 +0000434 xmlGenericError(xmlGenericErrorContext,
435 "Removing FTP proxy info\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000436 else
William M. Brack015ccb22005-02-13 08:18:52 +0000437 xmlGenericError(xmlGenericErrorContext,
438 "Using FTP proxy %s\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +0000439#endif
440 if (URL == NULL) return;
William M. Brack015ccb22005-02-13 08:18:52 +0000441
442 uri = xmlParseURI(URL);
443 if ((uri == NULL) || (uri->scheme == NULL) ||
444 (strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
445 __xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
446 if (uri != NULL)
447 xmlFreeURI(uri);
448 return;
Owen Taylor3473f882001-02-23 17:55:21 +0000449 }
William M. Brack015ccb22005-02-13 08:18:52 +0000450
451 proxy = xmlMemStrdup(uri->server);
452 if (uri->port != 0)
453 proxyPort = uri->port;
Owen Taylor3473f882001-02-23 17:55:21 +0000454
William M. Brack015ccb22005-02-13 08:18:52 +0000455 xmlFreeURI(uri);
Owen Taylor3473f882001-02-23 17:55:21 +0000456}
457
458/**
459 * xmlNanoFTPNewCtxt:
460 * @URL: The URL used to initialize the context
461 *
462 * Allocate and initialize a new FTP context.
463 *
464 * Returns an FTP context or NULL in case of error.
465 */
466
467void*
468xmlNanoFTPNewCtxt(const char *URL) {
469 xmlNanoFTPCtxtPtr ret;
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000470 char *unescaped;
Owen Taylor3473f882001-02-23 17:55:21 +0000471
472 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000473 if (ret == NULL) {
474 xmlFTPErrMemory("allocating FTP context");
475 return(NULL);
476 }
Owen Taylor3473f882001-02-23 17:55:21 +0000477
478 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
479 ret->port = 21;
480 ret->passive = 1;
481 ret->returnValue = 0;
482 ret->controlBufIndex = 0;
483 ret->controlBufUsed = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000484 ret->controlFd = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000485
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000486 unescaped = xmlURIUnescapeString(URL, 0, NULL);
Daniel Veillardb1b3a3e2004-11-03 23:25:47 +0000487 if (unescaped != NULL) {
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000488 xmlNanoFTPScanURL(ret, unescaped);
Daniel Veillardb1b3a3e2004-11-03 23:25:47 +0000489 xmlFree(unescaped);
490 } else if (URL != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000491 xmlNanoFTPScanURL(ret, URL);
492
493 return(ret);
494}
495
496/**
497 * xmlNanoFTPFreeCtxt:
498 * @ctx: an FTP context
499 *
500 * Frees the context after closing the connection.
501 */
502
503void
504xmlNanoFTPFreeCtxt(void * ctx) {
505 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
506 if (ctxt == NULL) return;
507 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
508 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
509 if (ctxt->path != NULL) xmlFree(ctxt->path);
510 ctxt->passive = 1;
511 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
512 ctxt->controlFd = -1;
513 ctxt->controlBufIndex = -1;
514 ctxt->controlBufUsed = -1;
515 xmlFree(ctxt);
516}
517
518/**
519 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000520 * @buf: the buffer containing the response
521 * @len: the buffer length
522 *
523 * Parsing of the server answer, we just extract the code.
524 *
525 * returns 0 for errors
526 * +XXX for last line of response
527 * -XXX for response to be continued
528 */
529static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000530xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000531 int val = 0;
532
533 if (len < 3) return(-1);
534 if ((*buf >= '0') && (*buf <= '9'))
535 val = val * 10 + (*buf - '0');
536 else
537 return(0);
538 buf++;
539 if ((*buf >= '0') && (*buf <= '9'))
540 val = val * 10 + (*buf - '0');
541 else
542 return(0);
543 buf++;
544 if ((*buf >= '0') && (*buf <= '9'))
545 val = val * 10 + (*buf - '0');
546 else
547 return(0);
548 buf++;
549 if (*buf == '-')
550 return(-val);
551 return(val);
552}
553
554/**
555 * xmlNanoFTPGetMore:
556 * @ctx: an FTP context
557 *
558 * Read more information from the FTP control connection
559 * Returns the number of bytes read, < 0 indicates an error
560 */
561static int
562xmlNanoFTPGetMore(void *ctx) {
563 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
564 int len;
565 int size;
566
Daniel Veillard27f20102004-11-05 11:50:11 +0000567 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
568
Owen Taylor3473f882001-02-23 17:55:21 +0000569 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
570#ifdef DEBUG_FTP
571 xmlGenericError(xmlGenericErrorContext,
572 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
573 ctxt->controlBufIndex);
574#endif
575 return(-1);
576 }
577
578 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
579#ifdef DEBUG_FTP
580 xmlGenericError(xmlGenericErrorContext,
581 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
582 ctxt->controlBufUsed);
583#endif
584 return(-1);
585 }
586 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
587#ifdef DEBUG_FTP
588 xmlGenericError(xmlGenericErrorContext,
589 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
590 ctxt->controlBufIndex, ctxt->controlBufUsed);
591#endif
592 return(-1);
593 }
594
595 /*
596 * First pack the control buffer
597 */
598 if (ctxt->controlBufIndex > 0) {
599 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
600 ctxt->controlBufUsed - ctxt->controlBufIndex);
601 ctxt->controlBufUsed -= ctxt->controlBufIndex;
602 ctxt->controlBufIndex = 0;
603 }
604 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
605 if (size == 0) {
606#ifdef DEBUG_FTP
607 xmlGenericError(xmlGenericErrorContext,
608 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
609#endif
610 return(0);
611 }
612
613 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000614 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000615 */
616 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
617 size, 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000618 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000619 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
620 ctxt->controlFd = -1;
621 return(-1);
622 }
623#ifdef DEBUG_FTP
624 xmlGenericError(xmlGenericErrorContext,
625 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
626 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
627#endif
628 ctxt->controlBufUsed += len;
629 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
630
631 return(len);
632}
633
634/**
635 * xmlNanoFTPReadResponse:
636 * @ctx: an FTP context
637 *
638 * Read the response from the FTP server after a command.
639 * Returns the code number
640 */
641static int
642xmlNanoFTPReadResponse(void *ctx) {
643 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
644 char *ptr, *end;
645 int len;
646 int res = -1, cur = -1;
647
Daniel Veillard27f20102004-11-05 11:50:11 +0000648 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
649
Owen Taylor3473f882001-02-23 17:55:21 +0000650get_more:
651 /*
652 * Assumes everything up to controlBuf[controlBufIndex] has been read
653 * and analyzed.
654 */
655 len = xmlNanoFTPGetMore(ctx);
656 if (len < 0) {
657 return(-1);
658 }
659 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
660 return(-1);
661 }
662 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
663 end = &ctxt->controlBuf[ctxt->controlBufUsed];
664
665#ifdef DEBUG_FTP
666 xmlGenericError(xmlGenericErrorContext,
667 "\n<<<\n%s\n--\n", ptr);
668#endif
669 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000670 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000671 if (cur > 0) {
672 /*
673 * Successfully scanned the control code, scratch
674 * till the end of the line, but keep the index to be
675 * able to analyze the result if needed.
676 */
677 res = cur;
678 ptr += 3;
679 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
680 while ((ptr < end) && (*ptr != '\n')) ptr++;
681 if (*ptr == '\n') ptr++;
682 if (*ptr == '\r') ptr++;
683 break;
684 }
685 while ((ptr < end) && (*ptr != '\n')) ptr++;
686 if (ptr >= end) {
687 ctxt->controlBufIndex = ctxt->controlBufUsed;
688 goto get_more;
689 }
690 if (*ptr != '\r') ptr++;
691 }
692
693 if (res < 0) goto get_more;
694 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
695#ifdef DEBUG_FTP
696 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
697 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
698#endif
699
700#ifdef DEBUG_FTP
701 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
702#endif
703 return(res / 100);
704}
705
706/**
707 * xmlNanoFTPGetResponse:
708 * @ctx: an FTP context
709 *
710 * Get the response from the FTP server after a command.
711 * Returns the code number
712 */
713
714int
715xmlNanoFTPGetResponse(void *ctx) {
716 int res;
717
718 res = xmlNanoFTPReadResponse(ctx);
719
720 return(res);
721}
722
723/**
724 * xmlNanoFTPCheckResponse:
725 * @ctx: an FTP context
726 *
727 * Check if there is a response from the FTP server after a command.
728 * Returns the code number, or 0
729 */
730
731int
732xmlNanoFTPCheckResponse(void *ctx) {
733 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
734 fd_set rfd;
735 struct timeval tv;
736
Daniel Veillard27f20102004-11-05 11:50:11 +0000737 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000738 tv.tv_sec = 0;
739 tv.tv_usec = 0;
740 FD_ZERO(&rfd);
741 FD_SET(ctxt->controlFd, &rfd);
742 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
743 case 0:
744 return(0);
745 case -1:
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000746 __xmlIOErr(XML_FROM_FTP, 0, "select");
Owen Taylor3473f882001-02-23 17:55:21 +0000747 return(-1);
748
749 }
750
751 return(xmlNanoFTPReadResponse(ctx));
752}
753
754/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000755 * Send the user authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000756 */
757
758static int
759xmlNanoFTPSendUser(void *ctx) {
760 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
761 char buf[200];
762 int len;
763 int res;
764
765 if (ctxt->user == NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000766 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000767 else
Owen Taylor3473f882001-02-23 17:55:21 +0000768 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000769 buf[sizeof(buf) - 1] = 0;
770 len = strlen(buf);
771#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000772 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000773#endif
774 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000775 if (res < 0) {
776 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
777 return(res);
778 }
Owen Taylor3473f882001-02-23 17:55:21 +0000779 return(0);
780}
781
782/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000783 * Send the password authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000784 */
785
786static int
787xmlNanoFTPSendPasswd(void *ctx) {
788 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
789 char buf[200];
790 int len;
791 int res;
792
793 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000794 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000795 else
Owen Taylor3473f882001-02-23 17:55:21 +0000796 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000797 buf[sizeof(buf) - 1] = 0;
798 len = strlen(buf);
799#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000800 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000801#endif
802 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000803 if (res < 0) {
804 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
805 return(res);
806 }
Owen Taylor3473f882001-02-23 17:55:21 +0000807 return(0);
808}
809
810/**
811 * xmlNanoFTPQuit:
812 * @ctx: an FTP context
813 *
814 * Send a QUIT command to the server
815 *
816 * Returns -1 in case of error, 0 otherwise
817 */
818
819
820int
821xmlNanoFTPQuit(void *ctx) {
822 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
823 char buf[200];
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000824 int len, res;
Owen Taylor3473f882001-02-23 17:55:21 +0000825
Daniel Veillard27f20102004-11-05 11:50:11 +0000826 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
827
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000828 snprintf(buf, sizeof(buf), "QUIT\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000829 len = strlen(buf);
830#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000831 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 +0000832#endif
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000833 res = send(ctxt->controlFd, buf, len, 0);
834 if (res < 0) {
835 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
836 return(res);
837 }
Owen Taylor3473f882001-02-23 17:55:21 +0000838 return(0);
839}
840
841/**
842 * xmlNanoFTPConnect:
843 * @ctx: an FTP context
844 *
845 * Tries to open a control connection
846 *
847 * Returns -1 in case of error, 0 otherwise
848 */
849
850int
851xmlNanoFTPConnect(void *ctx) {
852 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
853 struct hostent *hp;
854 int port;
855 int res;
Daniel Veillard2db8c122003-07-08 12:16:59 +0000856 int addrlen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +0000857
858 if (ctxt == NULL)
859 return(-1);
860 if (ctxt->hostname == NULL)
861 return(-1);
862
863 /*
864 * do the blocking DNS query.
865 */
Owen Taylor3473f882001-02-23 17:55:21 +0000866 if (proxy) {
867 port = proxyPort;
868 } else {
869 port = ctxt->port;
870 }
871 if (port == 0)
872 port = 21;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000873
874 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
875
876#ifdef SUPPORT_IP6
877 if (have_ipv6 ()) {
Daniel Veillard2db8c122003-07-08 12:16:59 +0000878 struct addrinfo hints, *tmp, *result;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000879
880 result = NULL;
881 memset (&hints, 0, sizeof(hints));
882 hints.ai_socktype = SOCK_STREAM;
883
884 if (proxy) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000885 if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
886 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000887 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000888 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000889 }
890 else
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000891 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
892 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000893 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000894 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000895
Daniel Veillard2db8c122003-07-08 12:16:59 +0000896 for (tmp = result; tmp; tmp = tmp->ai_next)
897 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000898 break;
899
Daniel Veillard3dc93a42003-07-10 14:04:33 +0000900 if (!tmp) {
901 if (result)
902 freeaddrinfo (result);
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000903 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillard3dc93a42003-07-10 14:04:33 +0000904 return (-1);
905 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000906 if (tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
907 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
908 return (-1);
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000909 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000910 if (tmp->ai_family == AF_INET6) {
911 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
912 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
913 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
914 }
915 else {
916 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
917 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
918 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
919 }
920 addrlen = tmp->ai_addrlen;
921 freeaddrinfo (result);
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000922 }
923 else
924#endif
925 {
926 if (proxy)
927 hp = gethostbyname (proxy);
928 else
929 hp = gethostbyname (ctxt->hostname);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000930 if (hp == NULL) {
931 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000932 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000933 }
Daniel Veillard6927b102004-10-27 17:29:04 +0000934 if ((unsigned int) hp->h_length >
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000935 sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
936 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
937 return (-1);
938 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000939
Daniel Veillard8e2c9792004-10-27 09:39:50 +0000940 /*
941 * Prepare the socket
942 */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000943 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
944 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
945 hp->h_addr_list[0], hp->h_length);
946 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = htons (port);
947 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
948 addrlen = sizeof (struct sockaddr_in);
949 }
950
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000951 if (ctxt->controlFd < 0) {
952 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000953 return(-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000954 }
Owen Taylor3473f882001-02-23 17:55:21 +0000955
956 /*
957 * Do the connect.
958 */
959 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000960 addrlen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000961 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
Owen Taylor3473f882001-02-23 17:55:21 +0000962 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
963 ctxt->controlFd = -1;
964 return(-1);
965 }
966
967 /*
968 * Wait for the HELLO from the server.
969 */
970 res = xmlNanoFTPGetResponse(ctxt);
971 if (res != 2) {
972 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
973 ctxt->controlFd = -1;
974 return(-1);
975 }
976
977 /*
978 * State diagram for the login operation on the FTP server
979 *
980 * Reference: RFC 959
981 *
982 * 1
983 * +---+ USER +---+------------->+---+
984 * | B |---------->| W | 2 ---->| E |
985 * +---+ +---+------ | -->+---+
986 * | | | | |
987 * 3 | | 4,5 | | |
988 * -------------- ----- | | |
989 * | | | | |
990 * | | | | |
991 * | --------- |
992 * | 1| | | |
993 * V | | | |
994 * +---+ PASS +---+ 2 | ------>+---+
995 * | |---------->| W |------------->| S |
996 * +---+ +---+ ---------->+---+
997 * | | | | |
998 * 3 | |4,5| | |
999 * -------------- -------- |
1000 * | | | | |
1001 * | | | | |
1002 * | -----------
1003 * | 1,3| | | |
1004 * V | 2| | |
1005 * +---+ ACCT +---+-- | ----->+---+
1006 * | |---------->| W | 4,5 -------->| F |
1007 * +---+ +---+------------->+---+
1008 *
1009 * Of course in case of using a proxy this get really nasty and is not
1010 * standardized at all :-(
1011 */
1012 if (proxy) {
1013 int len;
1014 char buf[400];
1015
1016 if (proxyUser != NULL) {
1017 /*
1018 * We need proxy auth
1019 */
Owen Taylor3473f882001-02-23 17:55:21 +00001020 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +00001021 buf[sizeof(buf) - 1] = 0;
1022 len = strlen(buf);
1023#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001024 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001025#endif
1026 res = send(ctxt->controlFd, buf, len, 0);
1027 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001028 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001029 closesocket(ctxt->controlFd);
1030 ctxt->controlFd = -1;
1031 return(res);
1032 }
1033 res = xmlNanoFTPGetResponse(ctxt);
1034 switch (res) {
1035 case 2:
1036 if (proxyPasswd == NULL)
1037 break;
1038 case 3:
1039 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001040 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +00001041 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001042 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001043 buf[sizeof(buf) - 1] = 0;
1044 len = strlen(buf);
1045#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001046 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001047#endif
1048 res = send(ctxt->controlFd, buf, len, 0);
1049 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001050 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001051 closesocket(ctxt->controlFd);
1052 ctxt->controlFd = -1;
1053 return(res);
1054 }
1055 res = xmlNanoFTPGetResponse(ctxt);
1056 if (res > 3) {
1057 closesocket(ctxt->controlFd);
1058 ctxt->controlFd = -1;
1059 return(-1);
1060 }
1061 break;
1062 case 1:
1063 break;
1064 case 4:
1065 case 5:
1066 case -1:
1067 default:
1068 closesocket(ctxt->controlFd);
1069 ctxt->controlFd = -1;
1070 return(-1);
1071 }
1072 }
1073
1074 /*
1075 * We assume we don't need more authentication to the proxy
1076 * and that it succeeded :-\
1077 */
1078 switch (proxyType) {
1079 case 0:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001080 /* we will try in sequence */
Owen Taylor3473f882001-02-23 17:55:21 +00001081 case 1:
1082 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001083 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001084 buf[sizeof(buf) - 1] = 0;
1085 len = strlen(buf);
1086#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001087 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001088#endif
1089 res = send(ctxt->controlFd, buf, len, 0);
1090 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001091 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001092 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1093 ctxt->controlFd = -1;
1094 return(res);
1095 }
1096 res = xmlNanoFTPGetResponse(ctxt);
1097 if (res == 2) {
1098 /* we assume it worked :-\ 1 is error for SITE command */
1099 proxyType = 1;
1100 break;
1101 }
1102 if (proxyType == 1) {
1103 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1104 ctxt->controlFd = -1;
1105 return(-1);
1106 }
1107 case 2:
1108 /* USER user@host command */
1109 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001110 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1111 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001112 else
Owen Taylor3473f882001-02-23 17:55:21 +00001113 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1114 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001115 buf[sizeof(buf) - 1] = 0;
1116 len = strlen(buf);
1117#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001118 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001119#endif
1120 res = send(ctxt->controlFd, buf, len, 0);
1121 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001122 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001123 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1124 ctxt->controlFd = -1;
1125 return(res);
1126 }
1127 res = xmlNanoFTPGetResponse(ctxt);
1128 if ((res == 1) || (res == 2)) {
1129 /* we assume it worked :-\ */
1130 proxyType = 2;
1131 return(0);
1132 }
1133 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001134 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001135 else
Owen Taylor3473f882001-02-23 17:55:21 +00001136 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001137 buf[sizeof(buf) - 1] = 0;
1138 len = strlen(buf);
1139#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001140 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001141#endif
1142 res = send(ctxt->controlFd, buf, len, 0);
1143 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001144 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001145 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1146 ctxt->controlFd = -1;
1147 return(res);
1148 }
1149 res = xmlNanoFTPGetResponse(ctxt);
1150 if ((res == 1) || (res == 2)) {
1151 /* we assume it worked :-\ */
1152 proxyType = 2;
1153 return(0);
1154 }
1155 if (proxyType == 2) {
1156 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1157 ctxt->controlFd = -1;
1158 return(-1);
1159 }
1160 case 3:
1161 /*
1162 * If you need support for other Proxy authentication scheme
1163 * send the code or at least the sequence in use.
1164 */
1165 default:
1166 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1167 ctxt->controlFd = -1;
1168 return(-1);
1169 }
1170 }
1171 /*
1172 * Non-proxy handling.
1173 */
1174 res = xmlNanoFTPSendUser(ctxt);
1175 if (res < 0) {
1176 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1177 ctxt->controlFd = -1;
1178 return(-1);
1179 }
1180 res = xmlNanoFTPGetResponse(ctxt);
1181 switch (res) {
1182 case 2:
1183 return(0);
1184 case 3:
1185 break;
1186 case 1:
1187 case 4:
1188 case 5:
1189 case -1:
1190 default:
1191 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1192 ctxt->controlFd = -1;
1193 return(-1);
1194 }
1195 res = xmlNanoFTPSendPasswd(ctxt);
1196 if (res < 0) {
1197 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1198 ctxt->controlFd = -1;
1199 return(-1);
1200 }
1201 res = xmlNanoFTPGetResponse(ctxt);
1202 switch (res) {
1203 case 2:
1204 break;
1205 case 3:
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001206 __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
1207 "FTP server asking for ACCNT on anonymous\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001208 case 1:
1209 case 4:
1210 case 5:
1211 case -1:
1212 default:
1213 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1214 ctxt->controlFd = -1;
1215 return(-1);
1216 }
1217
1218 return(0);
1219}
1220
1221/**
1222 * xmlNanoFTPConnectTo:
1223 * @server: an FTP server name
1224 * @port: the port (use 21 if 0)
1225 *
1226 * Tries to open a control connection to the given server/port
1227 *
1228 * Returns an fTP context or NULL if it failed
1229 */
1230
1231void*
1232xmlNanoFTPConnectTo(const char *server, int port) {
1233 xmlNanoFTPCtxtPtr ctxt;
1234 int res;
1235
1236 xmlNanoFTPInit();
1237 if (server == NULL)
1238 return(NULL);
Daniel Veillard27f20102004-11-05 11:50:11 +00001239 if (port <= 0)
1240 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001241 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1242 ctxt->hostname = xmlMemStrdup(server);
1243 if (port != 0)
1244 ctxt->port = port;
1245 res = xmlNanoFTPConnect(ctxt);
1246 if (res < 0) {
1247 xmlNanoFTPFreeCtxt(ctxt);
1248 return(NULL);
1249 }
1250 return(ctxt);
1251}
1252
1253/**
1254 * xmlNanoFTPCwd:
1255 * @ctx: an FTP context
1256 * @directory: a directory on the server
1257 *
1258 * Tries to change the remote directory
1259 *
1260 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1261 */
1262
1263int
Daniel Veillard34099b42004-11-04 17:34:35 +00001264xmlNanoFTPCwd(void *ctx, const char *directory) {
Owen Taylor3473f882001-02-23 17:55:21 +00001265 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1266 char buf[400];
1267 int len;
1268 int res;
1269
Daniel Veillard27f20102004-11-05 11:50:11 +00001270 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
William M. Brack015ccb22005-02-13 08:18:52 +00001271 if (directory == NULL) return 0;
Daniel Veillard27f20102004-11-05 11:50:11 +00001272
Owen Taylor3473f882001-02-23 17:55:21 +00001273 /*
1274 * Expected response code for CWD:
1275 *
1276 * CWD
1277 * 250
1278 * 500, 501, 502, 421, 530, 550
1279 */
Owen Taylor3473f882001-02-23 17:55:21 +00001280 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001281 buf[sizeof(buf) - 1] = 0;
1282 len = strlen(buf);
1283#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001284 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001285#endif
1286 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001287 if (res < 0) {
1288 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1289 return(res);
1290 }
Owen Taylor3473f882001-02-23 17:55:21 +00001291 res = xmlNanoFTPGetResponse(ctxt);
1292 if (res == 4) {
1293 return(-1);
1294 }
1295 if (res == 2) return(1);
1296 if (res == 5) {
1297 return(0);
1298 }
1299 return(0);
1300}
1301
1302/**
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001303 * xmlNanoFTPDele:
1304 * @ctx: an FTP context
1305 * @file: a file or directory on the server
1306 *
1307 * Tries to delete an item (file or directory) from server
1308 *
1309 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1310 */
1311
1312int
Daniel Veillard34099b42004-11-04 17:34:35 +00001313xmlNanoFTPDele(void *ctx, const char *file) {
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001314 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1315 char buf[400];
1316 int len;
1317 int res;
1318
Daniel Veillard27f20102004-11-05 11:50:11 +00001319 if ((ctxt == NULL) || (ctxt->controlFd < 0) || (file == NULL)) return(-1);
William M. Brack015ccb22005-02-13 08:18:52 +00001320 if (file == NULL) return (0);
Daniel Veillard27f20102004-11-05 11:50:11 +00001321
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001322 /*
1323 * Expected response code for DELE:
1324 *
1325 * DELE
1326 * 250
1327 * 450, 550
1328 * 500, 501, 502, 421, 530
1329 */
1330
1331 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1332 buf[sizeof(buf) - 1] = 0;
1333 len = strlen(buf);
1334#ifdef DEBUG_FTP
1335 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1336#endif
1337 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001338 if (res < 0) {
1339 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1340 return(res);
1341 }
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001342 res = xmlNanoFTPGetResponse(ctxt);
1343 if (res == 4) {
1344 return(-1);
1345 }
1346 if (res == 2) return(1);
1347 if (res == 5) {
1348 return(0);
1349 }
1350 return(0);
1351}
1352/**
Owen Taylor3473f882001-02-23 17:55:21 +00001353 * xmlNanoFTPGetConnection:
1354 * @ctx: an FTP context
1355 *
1356 * Try to open a data connection to the server. Currently only
1357 * passive mode is supported.
1358 *
1359 * Returns -1 incase of error, 0 otherwise
1360 */
1361
1362int
1363xmlNanoFTPGetConnection(void *ctx) {
1364 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1365 char buf[200], *cur;
1366 int len, i;
1367 int res;
1368 unsigned char ad[6], *adp, *portp;
1369 unsigned int temp[6];
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001370#ifdef SUPPORT_IP6
1371 struct sockaddr_storage dataAddr;
1372#else
Owen Taylor3473f882001-02-23 17:55:21 +00001373 struct sockaddr_in dataAddr;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001374#endif
Daniel Veillardc284c642005-03-31 10:24:24 +00001375 XML_SOCKLEN_T dataAddrLen;
Owen Taylor3473f882001-02-23 17:55:21 +00001376
Daniel Veillard27f20102004-11-05 11:50:11 +00001377 if (ctxt == NULL) return(-1);
1378
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001379 memset (&dataAddr, 0, sizeof(dataAddr));
1380#ifdef SUPPORT_IP6
1381 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1382 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1383 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1384 dataAddrLen = sizeof(struct sockaddr_in6);
1385 } else
1386#endif
1387 {
1388 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1389 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1390 dataAddrLen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001391 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001392
1393 if (ctxt->dataFd < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001394 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001395 return (-1);
1396 }
Owen Taylor3473f882001-02-23 17:55:21 +00001397
1398 if (ctxt->passive) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001399#ifdef SUPPORT_IP6
1400 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1401 snprintf (buf, sizeof(buf), "EPSV\r\n");
1402 else
1403#endif
1404 snprintf (buf, sizeof(buf), "PASV\r\n");
1405 len = strlen (buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001406#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001407 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001408#endif
1409 res = send(ctxt->controlFd, buf, len, 0);
1410 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001411 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001412 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1413 return(res);
1414 }
1415 res = xmlNanoFTPReadResponse(ctx);
1416 if (res != 2) {
1417 if (res == 5) {
1418 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1419 return(-1);
1420 } else {
1421 /*
1422 * retry with an active connection
1423 */
1424 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1425 ctxt->passive = 0;
1426 }
1427 }
1428 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1429 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001430#ifdef SUPPORT_IP6
1431 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1432 if (sscanf (cur, "%u", &temp[0]) != 1) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001433 __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001434 "Invalid answer to EPSV\n");
1435 if (ctxt->dataFd != -1) {
1436 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1437 }
1438 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001439 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001440 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1441 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
Owen Taylor3473f882001-02-23 17:55:21 +00001442 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001443 else
1444#endif
1445 {
1446 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1447 &temp[3], &temp[4], &temp[5]) != 6) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001448 __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001449 "Invalid answer to PASV\n");
1450 if (ctxt->dataFd != -1) {
1451 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1452 }
1453 return (-1);
1454 }
1455 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1456 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1457 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1458 }
1459
Owen Taylor3473f882001-02-23 17:55:21 +00001460 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001461 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
Owen Taylor3473f882001-02-23 17:55:21 +00001462 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1463 return (-1);
1464 }
1465 } else {
1466 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001467#ifdef SUPPORT_IP6
1468 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1469 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1470 else
1471#endif
1472 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1473
Owen Taylor3473f882001-02-23 17:55:21 +00001474 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001475 __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001476 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1477 return (-1);
1478 }
1479 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1480
1481 if (listen(ctxt->dataFd, 1) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001482 __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001483 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1484 return (-1);
1485 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001486#ifdef SUPPORT_IP6
1487 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1488 char buf6[INET6_ADDRSTRLEN];
1489 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1490 buf6, INET6_ADDRSTRLEN);
Daniel Veillard2db8c122003-07-08 12:16:59 +00001491 adp = (unsigned char *) buf6;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001492 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1493 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1494 } else
1495#endif
1496 {
1497 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1498 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1499 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1500 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1501 portp[0] & 0xff, portp[1] & 0xff);
1502 }
1503
Owen Taylor3473f882001-02-23 17:55:21 +00001504 buf[sizeof(buf) - 1] = 0;
1505 len = strlen(buf);
1506#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001507 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001508#endif
1509
1510 res = send(ctxt->controlFd, buf, len, 0);
1511 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001512 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001513 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1514 return(res);
1515 }
1516 res = xmlNanoFTPGetResponse(ctxt);
1517 if (res != 2) {
1518 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1519 return(-1);
1520 }
1521 }
1522 return(ctxt->dataFd);
1523
1524}
1525
1526/**
1527 * xmlNanoFTPCloseConnection:
1528 * @ctx: an FTP context
1529 *
1530 * Close the data connection from the server
1531 *
1532 * Returns -1 incase of error, 0 otherwise
1533 */
1534
1535int
1536xmlNanoFTPCloseConnection(void *ctx) {
1537 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1538 int res;
1539 fd_set rfd, efd;
1540 struct timeval tv;
1541
Daniel Veillard27f20102004-11-05 11:50:11 +00001542 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1543
Owen Taylor3473f882001-02-23 17:55:21 +00001544 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1545 tv.tv_sec = 15;
1546 tv.tv_usec = 0;
1547 FD_ZERO(&rfd);
1548 FD_SET(ctxt->controlFd, &rfd);
1549 FD_ZERO(&efd);
1550 FD_SET(ctxt->controlFd, &efd);
1551 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1552 if (res < 0) {
1553#ifdef DEBUG_FTP
1554 perror("select");
1555#endif
1556 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1557 return(-1);
1558 }
1559 if (res == 0) {
1560#ifdef DEBUG_FTP
1561 xmlGenericError(xmlGenericErrorContext,
1562 "xmlNanoFTPCloseConnection: timeout\n");
1563#endif
1564 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1565 } else {
1566 res = xmlNanoFTPGetResponse(ctxt);
1567 if (res != 2) {
1568 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1569 return(-1);
1570 }
1571 }
1572 return(0);
1573}
1574
1575/**
1576 * xmlNanoFTPParseList:
1577 * @list: some data listing received from the server
1578 * @callback: the user callback
1579 * @userData: the user callback data
1580 *
1581 * Parse at most one entry from the listing.
1582 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001583 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001584 */
1585
1586static int
1587xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1588 const char *cur = list;
1589 char filename[151];
1590 char attrib[11];
1591 char owner[11];
1592 char group[11];
1593 char month[4];
1594 int year = 0;
1595 int minute = 0;
1596 int hour = 0;
1597 int day = 0;
1598 unsigned long size = 0;
1599 int links = 0;
1600 int i;
1601
1602 if (!strncmp(cur, "total", 5)) {
1603 cur += 5;
1604 while (*cur == ' ') cur++;
1605 while ((*cur >= '0') && (*cur <= '9'))
1606 links = (links * 10) + (*cur++ - '0');
1607 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1608 cur++;
1609 return(cur - list);
1610 } else if (*list == '+') {
1611 return(0);
1612 } else {
1613 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1614 cur++;
1615 if (*cur == 0) return(0);
1616 i = 0;
1617 while (*cur != ' ') {
1618 if (i < 10)
1619 attrib[i++] = *cur;
1620 cur++;
1621 if (*cur == 0) return(0);
1622 }
1623 attrib[10] = 0;
1624 while (*cur == ' ') cur++;
1625 if (*cur == 0) return(0);
1626 while ((*cur >= '0') && (*cur <= '9'))
1627 links = (links * 10) + (*cur++ - '0');
1628 while (*cur == ' ') cur++;
1629 if (*cur == 0) return(0);
1630 i = 0;
1631 while (*cur != ' ') {
1632 if (i < 10)
1633 owner[i++] = *cur;
1634 cur++;
1635 if (*cur == 0) return(0);
1636 }
1637 owner[i] = 0;
1638 while (*cur == ' ') cur++;
1639 if (*cur == 0) return(0);
1640 i = 0;
1641 while (*cur != ' ') {
1642 if (i < 10)
1643 group[i++] = *cur;
1644 cur++;
1645 if (*cur == 0) return(0);
1646 }
1647 group[i] = 0;
1648 while (*cur == ' ') cur++;
1649 if (*cur == 0) return(0);
1650 while ((*cur >= '0') && (*cur <= '9'))
1651 size = (size * 10) + (*cur++ - '0');
1652 while (*cur == ' ') cur++;
1653 if (*cur == 0) return(0);
1654 i = 0;
1655 while (*cur != ' ') {
1656 if (i < 3)
1657 month[i++] = *cur;
1658 cur++;
1659 if (*cur == 0) return(0);
1660 }
1661 month[i] = 0;
1662 while (*cur == ' ') cur++;
1663 if (*cur == 0) return(0);
1664 while ((*cur >= '0') && (*cur <= '9'))
1665 day = (day * 10) + (*cur++ - '0');
1666 while (*cur == ' ') cur++;
1667 if (*cur == 0) return(0);
1668 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1669 if ((cur[1] == ':') || (cur[2] == ':')) {
1670 while ((*cur >= '0') && (*cur <= '9'))
1671 hour = (hour * 10) + (*cur++ - '0');
1672 if (*cur == ':') cur++;
1673 while ((*cur >= '0') && (*cur <= '9'))
1674 minute = (minute * 10) + (*cur++ - '0');
1675 } else {
1676 while ((*cur >= '0') && (*cur <= '9'))
1677 year = (year * 10) + (*cur++ - '0');
1678 }
1679 while (*cur == ' ') cur++;
1680 if (*cur == 0) return(0);
1681 i = 0;
1682 while ((*cur != '\n') && (*cur != '\r')) {
1683 if (i < 150)
1684 filename[i++] = *cur;
1685 cur++;
1686 if (*cur == 0) return(0);
1687 }
1688 filename[i] = 0;
1689 if ((*cur != '\n') && (*cur != '\r'))
1690 return(0);
1691 while ((*cur == '\n') || (*cur == '\r'))
1692 cur++;
1693 }
1694 if (callback != NULL) {
1695 callback(userData, filename, attrib, owner, group, size, links,
1696 year, month, day, hour, minute);
1697 }
1698 return(cur - list);
1699}
1700
1701/**
1702 * xmlNanoFTPList:
1703 * @ctx: an FTP context
1704 * @callback: the user callback
1705 * @userData: the user callback data
1706 * @filename: optional files to list
1707 *
1708 * Do a listing on the server. All files info are passed back
1709 * in the callbacks.
1710 *
1711 * Returns -1 incase of error, 0 otherwise
1712 */
1713
1714int
1715xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
Daniel Veillard34099b42004-11-04 17:34:35 +00001716 const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +00001717 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1718 char buf[4096 + 1];
1719 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001720 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001721 fd_set rfd, efd;
1722 struct timeval tv;
1723
William M. Brack015ccb22005-02-13 08:18:52 +00001724 if (ctxt == NULL) return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001725 if (filename == NULL) {
1726 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1727 return(-1);
1728 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1729 if (ctxt->dataFd == -1)
1730 return(-1);
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001731 snprintf(buf, sizeof(buf), "LIST -L\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001732 } else {
1733 if (filename[0] != '/') {
1734 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1735 return(-1);
1736 }
1737 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1738 if (ctxt->dataFd == -1)
1739 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001740 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001741 }
1742 buf[sizeof(buf) - 1] = 0;
1743 len = strlen(buf);
1744#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001745 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001746#endif
1747 res = send(ctxt->controlFd, buf, len, 0);
1748 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001749 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001750 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1751 return(res);
1752 }
1753 res = xmlNanoFTPReadResponse(ctxt);
1754 if (res != 1) {
1755 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1756 return(-res);
1757 }
1758
1759 do {
1760 tv.tv_sec = 1;
1761 tv.tv_usec = 0;
1762 FD_ZERO(&rfd);
1763 FD_SET(ctxt->dataFd, &rfd);
1764 FD_ZERO(&efd);
1765 FD_SET(ctxt->dataFd, &efd);
1766 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1767 if (res < 0) {
1768#ifdef DEBUG_FTP
1769 perror("select");
1770#endif
1771 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1772 return(-1);
1773 }
1774 if (res == 0) {
1775 res = xmlNanoFTPCheckResponse(ctxt);
1776 if (res < 0) {
1777 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1778 ctxt->dataFd = -1;
1779 return(-1);
1780 }
1781 if (res == 2) {
1782 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1783 return(0);
1784 }
1785
1786 continue;
1787 }
1788
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001789 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001790 __xmlIOErr(XML_FROM_FTP, 0, "recv");
Owen Taylor3473f882001-02-23 17:55:21 +00001791 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1792 ctxt->dataFd = -1;
1793 return(-1);
1794 }
1795#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001796 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001797#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001798 indx += len;
1799 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001800 base = 0;
1801 do {
1802 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1803 base += res;
1804 } while (res > 0);
1805
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001806 memmove(&buf[0], &buf[base], indx - base);
1807 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001808 } while (len != 0);
1809 xmlNanoFTPCloseConnection(ctxt);
1810 return(0);
1811}
1812
1813/**
1814 * xmlNanoFTPGetSocket:
1815 * @ctx: an FTP context
1816 * @filename: the file to retrieve (or NULL if path is in context).
1817 *
1818 * Initiate fetch of the given file from the server.
1819 *
1820 * Returns the socket for the data connection, or <0 in case of error
1821 */
1822
1823
1824int
1825xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1826 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1827 char buf[300];
1828 int res, len;
Daniel Veillard27f20102004-11-05 11:50:11 +00001829 if (ctx == NULL)
1830 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001831 if ((filename == NULL) && (ctxt->path == NULL))
1832 return(-1);
1833 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1834 if (ctxt->dataFd == -1)
1835 return(-1);
1836
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001837 snprintf(buf, sizeof(buf), "TYPE I\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001838 len = strlen(buf);
1839#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001840 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001841#endif
1842 res = send(ctxt->controlFd, buf, len, 0);
1843 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001844 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001845 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1846 return(res);
1847 }
1848 res = xmlNanoFTPReadResponse(ctxt);
1849 if (res != 2) {
1850 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1851 return(-res);
1852 }
1853 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001854 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001855 else
Owen Taylor3473f882001-02-23 17:55:21 +00001856 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001857 buf[sizeof(buf) - 1] = 0;
1858 len = strlen(buf);
1859#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001860 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001861#endif
1862 res = send(ctxt->controlFd, buf, len, 0);
1863 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001864 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001865 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1866 return(res);
1867 }
1868 res = xmlNanoFTPReadResponse(ctxt);
1869 if (res != 1) {
1870 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1871 return(-res);
1872 }
1873 return(ctxt->dataFd);
1874}
1875
1876/**
1877 * xmlNanoFTPGet:
1878 * @ctx: an FTP context
1879 * @callback: the user callback
1880 * @userData: the user callback data
1881 * @filename: the file to retrieve
1882 *
1883 * Fetch the given file from the server. All data are passed back
1884 * in the callbacks. The last callback has a size of 0 block.
1885 *
1886 * Returns -1 incase of error, 0 otherwise
1887 */
1888
1889int
1890xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1891 const char *filename) {
1892 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1893 char buf[4096];
1894 int len = 0, res;
1895 fd_set rfd;
1896 struct timeval tv;
1897
William M. Brack015ccb22005-02-13 08:18:52 +00001898 if (ctxt == NULL) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001899 if ((filename == NULL) && (ctxt->path == NULL))
1900 return(-1);
1901 if (callback == NULL)
1902 return(-1);
1903 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1904 return(-1);
1905
1906 do {
1907 tv.tv_sec = 1;
1908 tv.tv_usec = 0;
1909 FD_ZERO(&rfd);
1910 FD_SET(ctxt->dataFd, &rfd);
1911 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1912 if (res < 0) {
1913#ifdef DEBUG_FTP
1914 perror("select");
1915#endif
1916 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1917 return(-1);
1918 }
1919 if (res == 0) {
1920 res = xmlNanoFTPCheckResponse(ctxt);
1921 if (res < 0) {
1922 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1923 ctxt->dataFd = -1;
1924 return(-1);
1925 }
1926 if (res == 2) {
1927 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1928 return(0);
1929 }
1930
1931 continue;
1932 }
1933 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001934 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001935 callback(userData, buf, len);
1936 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1937 return(-1);
1938 }
1939 callback(userData, buf, len);
1940 } while (len != 0);
1941
1942 return(xmlNanoFTPCloseConnection(ctxt));
1943}
1944
1945/**
1946 * xmlNanoFTPRead:
1947 * @ctx: the FTP context
1948 * @dest: a buffer
1949 * @len: the buffer length
1950 *
1951 * This function tries to read @len bytes from the existing FTP connection
1952 * and saves them in @dest. This is a blocking call.
1953 *
1954 * Returns the number of byte read. 0 is an indication of an end of connection.
1955 * -1 indicates a parameter error.
1956 */
1957int
1958xmlNanoFTPRead(void *ctx, void *dest, int len) {
1959 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1960
1961 if (ctx == NULL) return(-1);
1962 if (ctxt->dataFd < 0) return(0);
1963 if (dest == NULL) return(-1);
1964 if (len <= 0) return(0);
1965
1966 len = recv(ctxt->dataFd, dest, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001967 if (len <= 0) {
William M. Brack015ccb22005-02-13 08:18:52 +00001968 if (len < 0)
1969 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001970 xmlNanoFTPCloseConnection(ctxt);
1971 }
Owen Taylor3473f882001-02-23 17:55:21 +00001972#ifdef DEBUG_FTP
1973 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1974#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001975 return(len);
1976}
1977
1978/**
1979 * xmlNanoFTPOpen:
1980 * @URL: the URL to the resource
1981 *
1982 * Start to fetch the given ftp:// resource
1983 *
1984 * Returns an FTP context, or NULL
1985 */
1986
1987void*
1988xmlNanoFTPOpen(const char *URL) {
1989 xmlNanoFTPCtxtPtr ctxt;
1990 int sock;
1991
1992 xmlNanoFTPInit();
1993 if (URL == NULL) return(NULL);
1994 if (strncmp("ftp://", URL, 6)) return(NULL);
1995
1996 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
1997 if (ctxt == NULL) return(NULL);
1998 if (xmlNanoFTPConnect(ctxt) < 0) {
1999 xmlNanoFTPFreeCtxt(ctxt);
2000 return(NULL);
2001 }
2002 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2003 if (sock < 0) {
2004 xmlNanoFTPFreeCtxt(ctxt);
2005 return(NULL);
2006 }
2007 return(ctxt);
2008}
2009
2010/**
2011 * xmlNanoFTPClose:
2012 * @ctx: an FTP context
2013 *
2014 * Close the connection and both control and transport
2015 *
2016 * Returns -1 incase of error, 0 otherwise
2017 */
2018
2019int
2020xmlNanoFTPClose(void *ctx) {
2021 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2022
2023 if (ctxt == NULL)
2024 return(-1);
2025
2026 if (ctxt->dataFd >= 0) {
2027 closesocket(ctxt->dataFd);
2028 ctxt->dataFd = -1;
2029 }
2030 if (ctxt->controlFd >= 0) {
2031 xmlNanoFTPQuit(ctxt);
2032 closesocket(ctxt->controlFd);
2033 ctxt->controlFd = -1;
2034 }
2035 xmlNanoFTPFreeCtxt(ctxt);
2036 return(0);
2037}
2038
2039#ifdef STANDALONE
2040/************************************************************************
2041 * *
2042 * Basic test in Standalone mode *
2043 * *
2044 ************************************************************************/
Daniel Veillard01c13b52002-12-10 15:19:08 +00002045static
Owen Taylor3473f882001-02-23 17:55:21 +00002046void ftpList(void *userData, const char *filename, const char* attrib,
2047 const char *owner, const char *group, unsigned long size, int links,
2048 int year, const char *month, int day, int hour, int minute) {
2049 xmlGenericError(xmlGenericErrorContext,
2050 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2051}
Daniel Veillard01c13b52002-12-10 15:19:08 +00002052static
Owen Taylor3473f882001-02-23 17:55:21 +00002053void ftpData(void *userData, const char *data, int len) {
2054 if (userData == NULL) return;
2055 if (len <= 0) {
Daniel Veillard82cb3192003-10-29 13:39:15 +00002056 fclose((FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002057 return;
2058 }
Daniel Veillard82cb3192003-10-29 13:39:15 +00002059 fwrite(data, len, 1, (FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002060}
2061
2062int main(int argc, char **argv) {
2063 void *ctxt;
2064 FILE *output;
2065 char *tstfile = NULL;
2066
2067 xmlNanoFTPInit();
2068 if (argc > 1) {
2069 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2070 if (xmlNanoFTPConnect(ctxt) < 0) {
2071 xmlGenericError(xmlGenericErrorContext,
2072 "Couldn't connect to %s\n", argv[1]);
2073 exit(1);
2074 }
2075 if (argc > 2)
2076 tstfile = argv[2];
2077 } else
2078 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2079 if (ctxt == NULL) {
2080 xmlGenericError(xmlGenericErrorContext,
2081 "Couldn't connect to localhost\n");
2082 exit(1);
2083 }
2084 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2085 output = fopen("/tmp/tstdata", "w");
2086 if (output != NULL) {
2087 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2088 xmlGenericError(xmlGenericErrorContext,
2089 "Failed to get file\n");
2090
2091 }
2092 xmlNanoFTPClose(ctxt);
2093 xmlMemoryDump();
2094 exit(0);
2095}
2096#endif /* STANDALONE */
2097#else /* !LIBXML_FTP_ENABLED */
2098#ifdef STANDALONE
2099#include <stdio.h>
2100int main(int argc, char **argv) {
2101 xmlGenericError(xmlGenericErrorContext,
2102 "%s : FTP support not compiled in\n", argv[0]);
2103 return(0);
2104}
2105#endif /* STANDALONE */
2106#endif /* LIBXML_FTP_ENABLED */