blob: 43ac8ac36f4b2ad6da6e511f6664ef70ac428d7d [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>
84#undef SOCKLEN_T
85#define SOCKLEN_T unsigned int
86#endif
87
88
Owen Taylor3473f882001-02-23 17:55:21 +000089/**
90 * A couple portability macros
91 */
92#ifndef _WINSOCKAPI_
93#define closesocket(s) close(s)
94#define SOCKET int
95#endif
Daniel Veillardacf7ff02001-10-29 20:21:47 +000096#if defined(VMS) || defined(__VMS)
97#define SOCKLEN_T unsigned int
98#endif
Owen Taylor3473f882001-02-23 17:55:21 +000099
Owen Taylor3473f882001-02-23 17:55:21 +0000100#define FTP_COMMAND_OK 200
101#define FTP_SYNTAX_ERROR 500
102#define FTP_GET_PASSWD 331
103#define FTP_BUF_SIZE 512
104
105typedef struct xmlNanoFTPCtxt {
106 char *protocol; /* the protocol name */
107 char *hostname; /* the host name */
108 int port; /* the port */
109 char *path; /* the path within the URL */
110 char *user; /* user string */
111 char *passwd; /* passwd string */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000112#ifdef SUPPORT_IP6
113 struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
114#else
Owen Taylor3473f882001-02-23 17:55:21 +0000115 struct sockaddr_in ftpAddr; /* the socket address struct */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000116#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000117 int passive; /* currently we support only passive !!! */
118 SOCKET controlFd; /* the file descriptor for the control socket */
119 SOCKET dataFd; /* the file descriptor for the data socket */
120 int state; /* WRITE / READ / CLOSED */
121 int returnValue; /* the protocol return value */
122 /* buffer for data received from the control connection */
123 char controlBuf[FTP_BUF_SIZE + 1];
124 int controlBufIndex;
125 int controlBufUsed;
126 int controlBufAnswer;
127} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
128
129static int initialized = 0;
130static char *proxy = NULL; /* the proxy name if any */
131static int proxyPort = 0; /* the proxy port if any */
132static char *proxyUser = NULL; /* user for proxy authentication */
133static char *proxyPasswd = NULL;/* passwd for proxy authentication */
134static int proxyType = 0; /* uses TYPE or a@b ? */
135
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000136#ifdef SUPPORT_IP6
Daniel Veillard2db8c122003-07-08 12:16:59 +0000137static
138int have_ipv6(void) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000139 int s;
140
141 s = socket (AF_INET6, SOCK_STREAM, 0);
142 if (s != -1) {
143 close (s);
144 return (1);
145 }
146 return (0);
147}
148#endif
149
Owen Taylor3473f882001-02-23 17:55:21 +0000150/**
151 * xmlNanoFTPInit:
152 *
153 * Initialize the FTP protocol layer.
154 * Currently it just checks for proxy informations,
155 * and get the hostname
156 */
157
158void
159xmlNanoFTPInit(void) {
160 const char *env;
161#ifdef _WINSOCKAPI_
162 WSADATA wsaData;
163#endif
164
165 if (initialized)
166 return;
167
168#ifdef _WINSOCKAPI_
169 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
170 return;
171#endif
172
Owen Taylor3473f882001-02-23 17:55:21 +0000173 proxyPort = 21;
174 env = getenv("no_proxy");
175 if (env != NULL)
176 return;
177 env = getenv("ftp_proxy");
178 if (env != NULL) {
179 xmlNanoFTPScanProxy(env);
180 } else {
181 env = getenv("FTP_PROXY");
182 if (env != NULL) {
183 xmlNanoFTPScanProxy(env);
184 }
185 }
186 env = getenv("ftp_proxy_user");
187 if (env != NULL) {
188 proxyUser = xmlMemStrdup(env);
189 }
190 env = getenv("ftp_proxy_password");
191 if (env != NULL) {
192 proxyPasswd = xmlMemStrdup(env);
193 }
194 initialized = 1;
195}
196
197/**
Daniel Veillarde356c282001-03-10 12:32:04 +0000198 * xmlNanoFTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000199 *
200 * Cleanup the FTP protocol layer. This cleanup proxy informations.
201 */
202
203void
204xmlNanoFTPCleanup(void) {
205 if (proxy != NULL) {
206 xmlFree(proxy);
207 proxy = NULL;
208 }
209 if (proxyUser != NULL) {
210 xmlFree(proxyUser);
211 proxyUser = NULL;
212 }
213 if (proxyPasswd != NULL) {
214 xmlFree(proxyPasswd);
215 proxyPasswd = NULL;
216 }
Owen Taylor3473f882001-02-23 17:55:21 +0000217#ifdef _WINSOCKAPI_
218 if (initialized)
219 WSACleanup();
220#endif
221 initialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000222}
223
224/**
225 * xmlNanoFTPProxy:
226 * @host: the proxy host name
227 * @port: the proxy port
228 * @user: the proxy user name
229 * @passwd: the proxy password
230 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
231 *
232 * Setup the FTP proxy informations.
233 * This can also be done by using ftp_proxy ftp_proxy_user and
234 * ftp_proxy_password environment variables.
235 */
236
237void
238xmlNanoFTPProxy(const char *host, int port, const char *user,
239 const char *passwd, int type) {
240 if (proxy != NULL)
241 xmlFree(proxy);
242 if (proxyUser != NULL)
243 xmlFree(proxyUser);
244 if (proxyPasswd != NULL)
245 xmlFree(proxyPasswd);
246 if (host)
247 proxy = xmlMemStrdup(host);
248 if (user)
249 proxyUser = xmlMemStrdup(user);
250 if (passwd)
251 proxyPasswd = xmlMemStrdup(passwd);
252 proxyPort = port;
253 proxyType = type;
254}
255
256/**
257 * xmlNanoFTPScanURL:
258 * @ctx: an FTP context
259 * @URL: The URL used to initialize the context
260 *
261 * (Re)Initialize an FTP context by parsing the URL and finding
262 * the protocol host port and path it indicates.
263 */
264
265static void
266xmlNanoFTPScanURL(void *ctx, const char *URL) {
267 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
268 const char *cur = URL;
269 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000270 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000271 int port = 0;
272
273 if (ctxt->protocol != NULL) {
274 xmlFree(ctxt->protocol);
275 ctxt->protocol = NULL;
276 }
277 if (ctxt->hostname != NULL) {
278 xmlFree(ctxt->hostname);
279 ctxt->hostname = NULL;
280 }
281 if (ctxt->path != NULL) {
282 xmlFree(ctxt->path);
283 ctxt->path = NULL;
284 }
285 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000286 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000287 while (*cur != 0) {
288 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000289 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000290 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000291 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000292 cur += 3;
293 break;
294 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000295 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000296 }
297 if (*cur == 0) return;
298
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000299 buf[indx] = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000300 /* allow user@ and user:pass@ forms */
301 {
302 const char *p = strchr(cur, '@');
303 if(p) {
304 while(1) {
305 if(cur[0] == ':' || cur[0] == '@') break;
306 buf[indx++] = *cur++;
307 }
308 buf[indx] = 0;
309 ctxt->user = xmlMemStrdup(buf);
310 indx = 0;
311 if(cur[0] == ':') {
312 cur++;
313 while(1) {
314 if(cur[0] == '@') break;
315 buf[indx++] = *cur++;
316 }
317 buf[indx] = 0;
318 ctxt->passwd = xmlMemStrdup(buf);
319 indx = 0;
320 }
321 cur = p+1;
322 }
323 }
324
Owen Taylor3473f882001-02-23 17:55:21 +0000325 while (1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000326 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
327 (!strchr (cur, '[') && strchr (cur, ']'))) {
328 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
329 "Syntax Error\n");
330 return;
331 }
332
333 if (cur[0] == '[') {
334 cur++;
335 while (cur[0] != ']')
336 buf[indx++] = *cur++;
337
338 if (!strchr (buf, ':')) {
339 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
340 "Use [IPv6]/IPv4 format\n");
341 return;
342 }
343
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000344 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000345 ctxt->hostname = xmlMemStrdup (buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000346 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000347 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000348 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000349 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000350 while (*cur >= '0' && *cur <= '9') {
351 port *= 10;
352 port += *cur - '0';
353 cur++;
354 }
355
356 if (port != 0) ctxt->port = port;
357 while ((cur[0] != '/') && (*cur != 0))
358 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000359 }
Owen Taylor3473f882001-02-23 17:55:21 +0000360 break;
361 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000362 else { /* address is an IPv4 one*/
363 if (cur[0] == ':') {
364 buf[indx] = 0;
365 ctxt->hostname = xmlMemStrdup (buf);
366 indx = 0;
367 cur += 1;
368 while ((*cur >= '0') && (*cur <= '9')) {
369 port *= 10;
370 port += *cur - '0';
371 cur++;
372 }
373 if (port != 0) ctxt->port = port;
374 while ((cur[0] != '/') && (*cur != 0))
375 cur++;
376 break;
377 }
378 if ((*cur == '/') || (*cur == 0)) {
379 buf[indx] = 0;
380 ctxt->hostname = xmlMemStrdup (buf);
381 indx = 0;
382 break;
383 }
Owen Taylor3473f882001-02-23 17:55:21 +0000384 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000385 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000386 }
387 if (*cur == 0)
388 ctxt->path = xmlMemStrdup("/");
389 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000390 indx = 0;
391 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000392 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000393 buf[indx++] = *cur++;
394 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000395 ctxt->path = xmlMemStrdup(buf);
396 }
397}
398
399/**
400 * xmlNanoFTPUpdateURL:
401 * @ctx: an FTP context
402 * @URL: The URL used to update the context
403 *
404 * Update an FTP context by parsing the URL and finding
405 * new path it indicates. If there is an error in the
406 * protocol, hostname, port or other information, the
407 * error is raised. It indicates a new connection has to
408 * be established.
409 *
410 * Returns 0 if Ok, -1 in case of error (other host).
411 */
412
413int
414xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
415 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
416 const char *cur = URL;
417 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000418 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000419 int port = 0;
420
421 if (URL == NULL)
422 return(-1);
423 if (ctxt == NULL)
424 return(-1);
425 if (ctxt->protocol == NULL)
426 return(-1);
427 if (ctxt->hostname == NULL)
428 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000429 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000430 while (*cur != 0) {
431 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000432 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000433 if (strcmp(ctxt->protocol, buf))
434 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000435 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000436 cur += 3;
437 break;
438 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000439 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000440 }
441 if (*cur == 0)
442 return(-1);
443
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000444 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000445 while (1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000446 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
447 (!strchr (cur, '[') && strchr (cur, ']'))) {
448 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
449 "Syntax Error\n");
450 return (-1);
451 }
452
453 if (cur[0] == '[') {
454 cur++;
455 while (cur[0] != ']')
456 buf[indx++] = *cur++;
457
458 if (!strchr (buf, ':')) {
459 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
460 "Use [IPv6]/IPv4 format\n");
461 return (-1);
462 }
463
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000464 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000465 if (strcmp (ctxt->hostname, buf))
466 return (-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000467 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000468 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000469 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000470 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000471 while (*cur >= '0' && *cur <= '9') {
472 port *= 10;
473 port += *cur - '0';
474 cur++;
475 }
476
477 if (port != ctxt->port)
478 return (-1);
479 while ((cur[0] != '/') && (*cur != 0))
480 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000481 }
Owen Taylor3473f882001-02-23 17:55:21 +0000482 break;
483 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000484 else {
485 if (cur[0] == ':') {
486 buf[indx] = 0;
487 if (strcmp (ctxt->hostname, buf))
488 return (-1);
489 indx = 0;
490 cur += 1;
491 while ((*cur >= '0') && (*cur <= '9')) {
492 port *= 10;
493 port += *cur - '0';
494 cur++;
495 }
496 if (port != ctxt->port)
497 return (-1);
498 while ((cur[0] != '/') && (*cur != 0))
499 cur++;
500 break;
501 }
502 if ((*cur == '/') || (*cur == 0)) {
503 buf[indx] = 0;
504 if (strcmp (ctxt->hostname, buf))
505 return (-1);
506 indx = 0;
507 break;
508 }
Owen Taylor3473f882001-02-23 17:55:21 +0000509 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000510 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000511 }
512 if (ctxt->path != NULL) {
513 xmlFree(ctxt->path);
514 ctxt->path = NULL;
515 }
516
517 if (*cur == 0)
518 ctxt->path = xmlMemStrdup("/");
519 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000520 indx = 0;
521 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000522 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000523 buf[indx++] = *cur++;
524 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000525 ctxt->path = xmlMemStrdup(buf);
526 }
527 return(0);
528}
529
530/**
531 * xmlNanoFTPScanProxy:
532 * @URL: The proxy URL used to initialize the proxy context
533 *
534 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
535 * the protocol host port it indicates.
536 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
537 * A NULL URL cleans up proxy informations.
538 */
539
540void
541xmlNanoFTPScanProxy(const char *URL) {
542 const char *cur = URL;
543 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000544 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000545 int port = 0;
546
547 if (proxy != NULL) {
548 xmlFree(proxy);
549 proxy = NULL;
550 }
551 if (proxyPort != 0) {
552 proxyPort = 0;
553 }
554#ifdef DEBUG_FTP
555 if (URL == NULL)
556 xmlGenericError(xmlGenericErrorContext, "Removing FTP proxy info\n");
557 else
558 xmlGenericError(xmlGenericErrorContext, "Using FTP proxy %s\n", URL);
559#endif
560 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000561 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000562 while (*cur != 0) {
563 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000564 buf[indx] = 0;
565 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000566 cur += 3;
567 break;
568 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000569 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000570 }
571 if (*cur == 0) return;
572
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000573 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000574 while (1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000575 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
576 (!strchr (cur, '[') && strchr (cur, ']'))) {
577 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
578 "Syntax error\n");
579 return;
580 }
581
582 if (cur[0] == '[') {
583 cur++;
584 while (cur[0] != ']')
585 buf[indx++] = *cur++;
586 if (!strchr (buf, ':')) {
587 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
588 "Use [IPv6]/IPv4 format\n");
589 return;
590 }
591
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000592 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000593 proxy = xmlMemStrdup (buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000594 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000595 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000596 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000597 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000598 while (*cur >= '0' && *cur <= '9') {
599 port *= 10;
600 port += *cur - '0';
601 cur++;
602 }
603
604 if (port != 0) proxyPort = port;
605 while ((cur[0] != '/') && (*cur != 0))
606 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000607 }
Owen Taylor3473f882001-02-23 17:55:21 +0000608 break;
609 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000610 else {
611 if (cur[0] == ':') {
612 buf[indx] = 0;
613 proxy = xmlMemStrdup (buf);
614 indx = 0;
615 cur += 1;
616 while ((*cur >= '0') && (*cur <= '9')) {
617 port *= 10;
618 port += *cur - '0';
619 cur++;
620 }
621 if (port != 0) proxyPort = port;
622 while ((cur[0] != '/') && (*cur != 0))
623 cur++;
624 break;
625 }
626 if ((*cur == '/') || (*cur == 0)) {
627 buf[indx] = 0;
628 proxy = xmlMemStrdup (buf);
629 indx = 0;
630 break;
631 }
Owen Taylor3473f882001-02-23 17:55:21 +0000632 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000633 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000634 }
635}
636
637/**
638 * xmlNanoFTPNewCtxt:
639 * @URL: The URL used to initialize the context
640 *
641 * Allocate and initialize a new FTP context.
642 *
643 * Returns an FTP context or NULL in case of error.
644 */
645
646void*
647xmlNanoFTPNewCtxt(const char *URL) {
648 xmlNanoFTPCtxtPtr ret;
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000649 char *unescaped;
Owen Taylor3473f882001-02-23 17:55:21 +0000650
651 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
652 if (ret == NULL) return(NULL);
653
654 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
655 ret->port = 21;
656 ret->passive = 1;
657 ret->returnValue = 0;
658 ret->controlBufIndex = 0;
659 ret->controlBufUsed = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000660 ret->controlFd = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000661
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000662 unescaped = xmlURIUnescapeString(URL, 0, NULL);
663 if (unescaped != NULL)
664 xmlNanoFTPScanURL(ret, unescaped);
665 else if (URL != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000666 xmlNanoFTPScanURL(ret, URL);
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000667 xmlFree(unescaped);
Owen Taylor3473f882001-02-23 17:55:21 +0000668
669 return(ret);
670}
671
672/**
673 * xmlNanoFTPFreeCtxt:
674 * @ctx: an FTP context
675 *
676 * Frees the context after closing the connection.
677 */
678
679void
680xmlNanoFTPFreeCtxt(void * ctx) {
681 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
682 if (ctxt == NULL) return;
683 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
684 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
685 if (ctxt->path != NULL) xmlFree(ctxt->path);
686 ctxt->passive = 1;
687 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
688 ctxt->controlFd = -1;
689 ctxt->controlBufIndex = -1;
690 ctxt->controlBufUsed = -1;
691 xmlFree(ctxt);
692}
693
694/**
695 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000696 * @buf: the buffer containing the response
697 * @len: the buffer length
698 *
699 * Parsing of the server answer, we just extract the code.
700 *
701 * returns 0 for errors
702 * +XXX for last line of response
703 * -XXX for response to be continued
704 */
705static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000706xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000707 int val = 0;
708
709 if (len < 3) return(-1);
710 if ((*buf >= '0') && (*buf <= '9'))
711 val = val * 10 + (*buf - '0');
712 else
713 return(0);
714 buf++;
715 if ((*buf >= '0') && (*buf <= '9'))
716 val = val * 10 + (*buf - '0');
717 else
718 return(0);
719 buf++;
720 if ((*buf >= '0') && (*buf <= '9'))
721 val = val * 10 + (*buf - '0');
722 else
723 return(0);
724 buf++;
725 if (*buf == '-')
726 return(-val);
727 return(val);
728}
729
730/**
731 * xmlNanoFTPGetMore:
732 * @ctx: an FTP context
733 *
734 * Read more information from the FTP control connection
735 * Returns the number of bytes read, < 0 indicates an error
736 */
737static int
738xmlNanoFTPGetMore(void *ctx) {
739 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
740 int len;
741 int size;
742
743 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
744#ifdef DEBUG_FTP
745 xmlGenericError(xmlGenericErrorContext,
746 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
747 ctxt->controlBufIndex);
748#endif
749 return(-1);
750 }
751
752 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
753#ifdef DEBUG_FTP
754 xmlGenericError(xmlGenericErrorContext,
755 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
756 ctxt->controlBufUsed);
757#endif
758 return(-1);
759 }
760 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
761#ifdef DEBUG_FTP
762 xmlGenericError(xmlGenericErrorContext,
763 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
764 ctxt->controlBufIndex, ctxt->controlBufUsed);
765#endif
766 return(-1);
767 }
768
769 /*
770 * First pack the control buffer
771 */
772 if (ctxt->controlBufIndex > 0) {
773 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
774 ctxt->controlBufUsed - ctxt->controlBufIndex);
775 ctxt->controlBufUsed -= ctxt->controlBufIndex;
776 ctxt->controlBufIndex = 0;
777 }
778 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
779 if (size == 0) {
780#ifdef DEBUG_FTP
781 xmlGenericError(xmlGenericErrorContext,
782 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
783#endif
784 return(0);
785 }
786
787 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000788 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000789 */
790 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
791 size, 0)) < 0) {
792 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
793 ctxt->controlFd = -1;
794 return(-1);
795 }
796#ifdef DEBUG_FTP
797 xmlGenericError(xmlGenericErrorContext,
798 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
799 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
800#endif
801 ctxt->controlBufUsed += len;
802 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
803
804 return(len);
805}
806
807/**
808 * xmlNanoFTPReadResponse:
809 * @ctx: an FTP context
810 *
811 * Read the response from the FTP server after a command.
812 * Returns the code number
813 */
814static int
815xmlNanoFTPReadResponse(void *ctx) {
816 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
817 char *ptr, *end;
818 int len;
819 int res = -1, cur = -1;
820
821get_more:
822 /*
823 * Assumes everything up to controlBuf[controlBufIndex] has been read
824 * and analyzed.
825 */
826 len = xmlNanoFTPGetMore(ctx);
827 if (len < 0) {
828 return(-1);
829 }
830 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
831 return(-1);
832 }
833 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
834 end = &ctxt->controlBuf[ctxt->controlBufUsed];
835
836#ifdef DEBUG_FTP
837 xmlGenericError(xmlGenericErrorContext,
838 "\n<<<\n%s\n--\n", ptr);
839#endif
840 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000841 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000842 if (cur > 0) {
843 /*
844 * Successfully scanned the control code, scratch
845 * till the end of the line, but keep the index to be
846 * able to analyze the result if needed.
847 */
848 res = cur;
849 ptr += 3;
850 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
851 while ((ptr < end) && (*ptr != '\n')) ptr++;
852 if (*ptr == '\n') ptr++;
853 if (*ptr == '\r') ptr++;
854 break;
855 }
856 while ((ptr < end) && (*ptr != '\n')) ptr++;
857 if (ptr >= end) {
858 ctxt->controlBufIndex = ctxt->controlBufUsed;
859 goto get_more;
860 }
861 if (*ptr != '\r') ptr++;
862 }
863
864 if (res < 0) goto get_more;
865 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
866#ifdef DEBUG_FTP
867 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
868 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
869#endif
870
871#ifdef DEBUG_FTP
872 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
873#endif
874 return(res / 100);
875}
876
877/**
878 * xmlNanoFTPGetResponse:
879 * @ctx: an FTP context
880 *
881 * Get the response from the FTP server after a command.
882 * Returns the code number
883 */
884
885int
886xmlNanoFTPGetResponse(void *ctx) {
887 int res;
888
889 res = xmlNanoFTPReadResponse(ctx);
890
891 return(res);
892}
893
894/**
895 * xmlNanoFTPCheckResponse:
896 * @ctx: an FTP context
897 *
898 * Check if there is a response from the FTP server after a command.
899 * Returns the code number, or 0
900 */
901
902int
903xmlNanoFTPCheckResponse(void *ctx) {
904 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
905 fd_set rfd;
906 struct timeval tv;
907
908 tv.tv_sec = 0;
909 tv.tv_usec = 0;
910 FD_ZERO(&rfd);
911 FD_SET(ctxt->controlFd, &rfd);
912 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
913 case 0:
914 return(0);
915 case -1:
916#ifdef DEBUG_FTP
917 perror("select");
918#endif
919 return(-1);
920
921 }
922
923 return(xmlNanoFTPReadResponse(ctx));
924}
925
926/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000927 * Send the user authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000928 */
929
930static int
931xmlNanoFTPSendUser(void *ctx) {
932 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
933 char buf[200];
934 int len;
935 int res;
936
937 if (ctxt->user == NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000938 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000939 else
Owen Taylor3473f882001-02-23 17:55:21 +0000940 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000941 buf[sizeof(buf) - 1] = 0;
942 len = strlen(buf);
943#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000944 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000945#endif
946 res = send(ctxt->controlFd, buf, len, 0);
947 if (res < 0) return(res);
948 return(0);
949}
950
951/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000952 * Send the password authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000953 */
954
955static int
956xmlNanoFTPSendPasswd(void *ctx) {
957 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
958 char buf[200];
959 int len;
960 int res;
961
962 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000963 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000964 else
Owen Taylor3473f882001-02-23 17:55:21 +0000965 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000966 buf[sizeof(buf) - 1] = 0;
967 len = strlen(buf);
968#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000969 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000970#endif
971 res = send(ctxt->controlFd, buf, len, 0);
972 if (res < 0) return(res);
973 return(0);
974}
975
976/**
977 * xmlNanoFTPQuit:
978 * @ctx: an FTP context
979 *
980 * Send a QUIT command to the server
981 *
982 * Returns -1 in case of error, 0 otherwise
983 */
984
985
986int
987xmlNanoFTPQuit(void *ctx) {
988 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
989 char buf[200];
990 int len;
Owen Taylor3473f882001-02-23 17:55:21 +0000991
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000992 snprintf(buf, sizeof(buf), "QUIT\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000993 len = strlen(buf);
994#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000995 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 +0000996#endif
William M. Brack78637da2003-07-31 14:47:38 +0000997 send(ctxt->controlFd, buf, len, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000998 return(0);
999}
1000
1001/**
1002 * xmlNanoFTPConnect:
1003 * @ctx: an FTP context
1004 *
1005 * Tries to open a control connection
1006 *
1007 * Returns -1 in case of error, 0 otherwise
1008 */
1009
1010int
1011xmlNanoFTPConnect(void *ctx) {
1012 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1013 struct hostent *hp;
1014 int port;
1015 int res;
Daniel Veillard2db8c122003-07-08 12:16:59 +00001016 int addrlen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001017
1018 if (ctxt == NULL)
1019 return(-1);
1020 if (ctxt->hostname == NULL)
1021 return(-1);
1022
1023 /*
1024 * do the blocking DNS query.
1025 */
Owen Taylor3473f882001-02-23 17:55:21 +00001026 if (proxy) {
1027 port = proxyPort;
1028 } else {
1029 port = ctxt->port;
1030 }
1031 if (port == 0)
1032 port = 21;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001033
1034 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
1035
1036#ifdef SUPPORT_IP6
1037 if (have_ipv6 ()) {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001038 struct addrinfo hints, *tmp, *result;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001039
1040 result = NULL;
1041 memset (&hints, 0, sizeof(hints));
1042 hints.ai_socktype = SOCK_STREAM;
1043
1044 if (proxy) {
1045 if (getaddrinfo (proxy, NULL, &hints, &result) != 0)
1046 return (-1);
1047 }
1048 else
1049 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0)
1050 return (-1);
1051
Daniel Veillard2db8c122003-07-08 12:16:59 +00001052 for (tmp = result; tmp; tmp = tmp->ai_next)
1053 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001054 break;
1055
Daniel Veillard3dc93a42003-07-10 14:04:33 +00001056 if (!tmp) {
1057 if (result)
1058 freeaddrinfo (result);
1059 return (-1);
1060 }
1061 else {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001062 if (tmp->ai_family == AF_INET6) {
1063 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001064 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
1065 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
1066 }
1067 else {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001068 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001069 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
1070 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1071 }
Daniel Veillard2db8c122003-07-08 12:16:59 +00001072 addrlen = tmp->ai_addrlen;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001073 freeaddrinfo (result);
1074 }
1075 }
1076 else
1077#endif
1078 {
1079 if (proxy)
1080 hp = gethostbyname (proxy);
1081 else
1082 hp = gethostbyname (ctxt->hostname);
1083 if (hp == NULL)
1084 return (-1);
1085
1086 /*
1087 * Prepare the socket
1088 */
1089 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
1090 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
1091 hp->h_addr_list[0], hp->h_length);
1092 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = htons (port);
1093 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1094 addrlen = sizeof (struct sockaddr_in);
1095 }
1096
Owen Taylor3473f882001-02-23 17:55:21 +00001097 if (ctxt->controlFd < 0)
1098 return(-1);
1099
1100 /*
1101 * Do the connect.
1102 */
1103 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001104 addrlen) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001105 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1106 ctxt->controlFd = -1;
1107 return(-1);
1108 }
1109
1110 /*
1111 * Wait for the HELLO from the server.
1112 */
1113 res = xmlNanoFTPGetResponse(ctxt);
1114 if (res != 2) {
1115 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1116 ctxt->controlFd = -1;
1117 return(-1);
1118 }
1119
1120 /*
1121 * State diagram for the login operation on the FTP server
1122 *
1123 * Reference: RFC 959
1124 *
1125 * 1
1126 * +---+ USER +---+------------->+---+
1127 * | B |---------->| W | 2 ---->| E |
1128 * +---+ +---+------ | -->+---+
1129 * | | | | |
1130 * 3 | | 4,5 | | |
1131 * -------------- ----- | | |
1132 * | | | | |
1133 * | | | | |
1134 * | --------- |
1135 * | 1| | | |
1136 * V | | | |
1137 * +---+ PASS +---+ 2 | ------>+---+
1138 * | |---------->| W |------------->| S |
1139 * +---+ +---+ ---------->+---+
1140 * | | | | |
1141 * 3 | |4,5| | |
1142 * -------------- -------- |
1143 * | | | | |
1144 * | | | | |
1145 * | -----------
1146 * | 1,3| | | |
1147 * V | 2| | |
1148 * +---+ ACCT +---+-- | ----->+---+
1149 * | |---------->| W | 4,5 -------->| F |
1150 * +---+ +---+------------->+---+
1151 *
1152 * Of course in case of using a proxy this get really nasty and is not
1153 * standardized at all :-(
1154 */
1155 if (proxy) {
1156 int len;
1157 char buf[400];
1158
1159 if (proxyUser != NULL) {
1160 /*
1161 * We need proxy auth
1162 */
Owen Taylor3473f882001-02-23 17:55:21 +00001163 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +00001164 buf[sizeof(buf) - 1] = 0;
1165 len = strlen(buf);
1166#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001167 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001168#endif
1169 res = send(ctxt->controlFd, buf, len, 0);
1170 if (res < 0) {
1171 closesocket(ctxt->controlFd);
1172 ctxt->controlFd = -1;
1173 return(res);
1174 }
1175 res = xmlNanoFTPGetResponse(ctxt);
1176 switch (res) {
1177 case 2:
1178 if (proxyPasswd == NULL)
1179 break;
1180 case 3:
1181 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001182 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +00001183 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001184 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001185 buf[sizeof(buf) - 1] = 0;
1186 len = strlen(buf);
1187#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001188 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001189#endif
1190 res = send(ctxt->controlFd, buf, len, 0);
1191 if (res < 0) {
1192 closesocket(ctxt->controlFd);
1193 ctxt->controlFd = -1;
1194 return(res);
1195 }
1196 res = xmlNanoFTPGetResponse(ctxt);
1197 if (res > 3) {
1198 closesocket(ctxt->controlFd);
1199 ctxt->controlFd = -1;
1200 return(-1);
1201 }
1202 break;
1203 case 1:
1204 break;
1205 case 4:
1206 case 5:
1207 case -1:
1208 default:
1209 closesocket(ctxt->controlFd);
1210 ctxt->controlFd = -1;
1211 return(-1);
1212 }
1213 }
1214
1215 /*
1216 * We assume we don't need more authentication to the proxy
1217 * and that it succeeded :-\
1218 */
1219 switch (proxyType) {
1220 case 0:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001221 /* we will try in sequence */
Owen Taylor3473f882001-02-23 17:55:21 +00001222 case 1:
1223 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001224 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001225 buf[sizeof(buf) - 1] = 0;
1226 len = strlen(buf);
1227#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001228 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001229#endif
1230 res = send(ctxt->controlFd, buf, len, 0);
1231 if (res < 0) {
1232 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1233 ctxt->controlFd = -1;
1234 return(res);
1235 }
1236 res = xmlNanoFTPGetResponse(ctxt);
1237 if (res == 2) {
1238 /* we assume it worked :-\ 1 is error for SITE command */
1239 proxyType = 1;
1240 break;
1241 }
1242 if (proxyType == 1) {
1243 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1244 ctxt->controlFd = -1;
1245 return(-1);
1246 }
1247 case 2:
1248 /* USER user@host command */
1249 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001250 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1251 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001252 else
Owen Taylor3473f882001-02-23 17:55:21 +00001253 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1254 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001255 buf[sizeof(buf) - 1] = 0;
1256 len = strlen(buf);
1257#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001258 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001259#endif
1260 res = send(ctxt->controlFd, buf, len, 0);
1261 if (res < 0) {
1262 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1263 ctxt->controlFd = -1;
1264 return(res);
1265 }
1266 res = xmlNanoFTPGetResponse(ctxt);
1267 if ((res == 1) || (res == 2)) {
1268 /* we assume it worked :-\ */
1269 proxyType = 2;
1270 return(0);
1271 }
1272 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001273 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001274 else
Owen Taylor3473f882001-02-23 17:55:21 +00001275 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001276 buf[sizeof(buf) - 1] = 0;
1277 len = strlen(buf);
1278#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001279 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001280#endif
1281 res = send(ctxt->controlFd, buf, len, 0);
1282 if (res < 0) {
1283 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1284 ctxt->controlFd = -1;
1285 return(res);
1286 }
1287 res = xmlNanoFTPGetResponse(ctxt);
1288 if ((res == 1) || (res == 2)) {
1289 /* we assume it worked :-\ */
1290 proxyType = 2;
1291 return(0);
1292 }
1293 if (proxyType == 2) {
1294 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1295 ctxt->controlFd = -1;
1296 return(-1);
1297 }
1298 case 3:
1299 /*
1300 * If you need support for other Proxy authentication scheme
1301 * send the code or at least the sequence in use.
1302 */
1303 default:
1304 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1305 ctxt->controlFd = -1;
1306 return(-1);
1307 }
1308 }
1309 /*
1310 * Non-proxy handling.
1311 */
1312 res = xmlNanoFTPSendUser(ctxt);
1313 if (res < 0) {
1314 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1315 ctxt->controlFd = -1;
1316 return(-1);
1317 }
1318 res = xmlNanoFTPGetResponse(ctxt);
1319 switch (res) {
1320 case 2:
1321 return(0);
1322 case 3:
1323 break;
1324 case 1:
1325 case 4:
1326 case 5:
1327 case -1:
1328 default:
1329 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1330 ctxt->controlFd = -1;
1331 return(-1);
1332 }
1333 res = xmlNanoFTPSendPasswd(ctxt);
1334 if (res < 0) {
1335 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1336 ctxt->controlFd = -1;
1337 return(-1);
1338 }
1339 res = xmlNanoFTPGetResponse(ctxt);
1340 switch (res) {
1341 case 2:
1342 break;
1343 case 3:
1344 xmlGenericError(xmlGenericErrorContext,
1345 "FTP server asking for ACCNT on anonymous\n");
1346 case 1:
1347 case 4:
1348 case 5:
1349 case -1:
1350 default:
1351 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1352 ctxt->controlFd = -1;
1353 return(-1);
1354 }
1355
1356 return(0);
1357}
1358
1359/**
1360 * xmlNanoFTPConnectTo:
1361 * @server: an FTP server name
1362 * @port: the port (use 21 if 0)
1363 *
1364 * Tries to open a control connection to the given server/port
1365 *
1366 * Returns an fTP context or NULL if it failed
1367 */
1368
1369void*
1370xmlNanoFTPConnectTo(const char *server, int port) {
1371 xmlNanoFTPCtxtPtr ctxt;
1372 int res;
1373
1374 xmlNanoFTPInit();
1375 if (server == NULL)
1376 return(NULL);
1377 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1378 ctxt->hostname = xmlMemStrdup(server);
1379 if (port != 0)
1380 ctxt->port = port;
1381 res = xmlNanoFTPConnect(ctxt);
1382 if (res < 0) {
1383 xmlNanoFTPFreeCtxt(ctxt);
1384 return(NULL);
1385 }
1386 return(ctxt);
1387}
1388
1389/**
1390 * xmlNanoFTPCwd:
1391 * @ctx: an FTP context
1392 * @directory: a directory on the server
1393 *
1394 * Tries to change the remote directory
1395 *
1396 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1397 */
1398
1399int
1400xmlNanoFTPCwd(void *ctx, char *directory) {
1401 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1402 char buf[400];
1403 int len;
1404 int res;
1405
1406 /*
1407 * Expected response code for CWD:
1408 *
1409 * CWD
1410 * 250
1411 * 500, 501, 502, 421, 530, 550
1412 */
Owen Taylor3473f882001-02-23 17:55:21 +00001413 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001414 buf[sizeof(buf) - 1] = 0;
1415 len = strlen(buf);
1416#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001417 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001418#endif
1419 res = send(ctxt->controlFd, buf, len, 0);
1420 if (res < 0) return(res);
1421 res = xmlNanoFTPGetResponse(ctxt);
1422 if (res == 4) {
1423 return(-1);
1424 }
1425 if (res == 2) return(1);
1426 if (res == 5) {
1427 return(0);
1428 }
1429 return(0);
1430}
1431
1432/**
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001433 * xmlNanoFTPDele:
1434 * @ctx: an FTP context
1435 * @file: a file or directory on the server
1436 *
1437 * Tries to delete an item (file or directory) from server
1438 *
1439 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1440 */
1441
1442int
1443xmlNanoFTPDele(void *ctx, char *file) {
1444 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1445 char buf[400];
1446 int len;
1447 int res;
1448
1449 /*
1450 * Expected response code for DELE:
1451 *
1452 * DELE
1453 * 250
1454 * 450, 550
1455 * 500, 501, 502, 421, 530
1456 */
1457
1458 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1459 buf[sizeof(buf) - 1] = 0;
1460 len = strlen(buf);
1461#ifdef DEBUG_FTP
1462 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1463#endif
1464 res = send(ctxt->controlFd, buf, len, 0);
1465 if (res < 0) return(res);
1466 res = xmlNanoFTPGetResponse(ctxt);
1467 if (res == 4) {
1468 return(-1);
1469 }
1470 if (res == 2) return(1);
1471 if (res == 5) {
1472 return(0);
1473 }
1474 return(0);
1475}
1476/**
Owen Taylor3473f882001-02-23 17:55:21 +00001477 * xmlNanoFTPGetConnection:
1478 * @ctx: an FTP context
1479 *
1480 * Try to open a data connection to the server. Currently only
1481 * passive mode is supported.
1482 *
1483 * Returns -1 incase of error, 0 otherwise
1484 */
1485
1486int
1487xmlNanoFTPGetConnection(void *ctx) {
1488 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1489 char buf[200], *cur;
1490 int len, i;
1491 int res;
1492 unsigned char ad[6], *adp, *portp;
1493 unsigned int temp[6];
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001494#ifdef SUPPORT_IP6
1495 struct sockaddr_storage dataAddr;
1496#else
Owen Taylor3473f882001-02-23 17:55:21 +00001497 struct sockaddr_in dataAddr;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001498#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001499 SOCKLEN_T dataAddrLen;
1500
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001501 memset (&dataAddr, 0, sizeof(dataAddr));
1502#ifdef SUPPORT_IP6
1503 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1504 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1505 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1506 dataAddrLen = sizeof(struct sockaddr_in6);
1507 } else
1508#endif
1509 {
1510 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1511 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1512 dataAddrLen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001513 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001514
1515 if (ctxt->dataFd < 0) {
1516 xmlGenericError (xmlGenericErrorContext,
1517 "xmlNanoFTPGetConnection: failed to create socket\n");
1518 return (-1);
1519 }
Owen Taylor3473f882001-02-23 17:55:21 +00001520
1521 if (ctxt->passive) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001522#ifdef SUPPORT_IP6
1523 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1524 snprintf (buf, sizeof(buf), "EPSV\r\n");
1525 else
1526#endif
1527 snprintf (buf, sizeof(buf), "PASV\r\n");
1528 len = strlen (buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001529#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001530 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001531#endif
1532 res = send(ctxt->controlFd, buf, len, 0);
1533 if (res < 0) {
1534 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1535 return(res);
1536 }
1537 res = xmlNanoFTPReadResponse(ctx);
1538 if (res != 2) {
1539 if (res == 5) {
1540 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1541 return(-1);
1542 } else {
1543 /*
1544 * retry with an active connection
1545 */
1546 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1547 ctxt->passive = 0;
1548 }
1549 }
1550 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1551 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001552#ifdef SUPPORT_IP6
1553 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1554 if (sscanf (cur, "%u", &temp[0]) != 1) {
1555 xmlGenericError (xmlGenericErrorContext,
1556 "Invalid answer to EPSV\n");
1557 if (ctxt->dataFd != -1) {
1558 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1559 }
1560 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001561 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001562 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1563 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
Owen Taylor3473f882001-02-23 17:55:21 +00001564 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001565 else
1566#endif
1567 {
1568 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1569 &temp[3], &temp[4], &temp[5]) != 6) {
1570 xmlGenericError (xmlGenericErrorContext,
1571 "Invalid answer to PASV\n");
1572 if (ctxt->dataFd != -1) {
1573 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1574 }
1575 return (-1);
1576 }
1577 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1578 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1579 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1580 }
1581
Owen Taylor3473f882001-02-23 17:55:21 +00001582 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1583 xmlGenericError(xmlGenericErrorContext,
1584 "Failed to create a data connection\n");
1585 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1586 return (-1);
1587 }
1588 } else {
1589 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001590#ifdef SUPPORT_IP6
1591 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1592 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1593 else
1594#endif
1595 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1596
Owen Taylor3473f882001-02-23 17:55:21 +00001597 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1598 xmlGenericError(xmlGenericErrorContext,
1599 "Failed to bind a port\n");
1600 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1601 return (-1);
1602 }
1603 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1604
1605 if (listen(ctxt->dataFd, 1) < 0) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001606#ifdef SUPPORT_IP6
1607 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1608 xmlGenericError (xmlGenericErrorContext,
1609 "Could not listen on port %d\n",
1610 ntohs (((struct sockaddr_in6 *)&dataAddr)->sin6_port));
1611 else
1612#endif
1613 xmlGenericError (xmlGenericErrorContext,
1614 "Could not listen on port %d\n",
1615 ntohs (((struct sockaddr_in *)&dataAddr)->sin_port));
Owen Taylor3473f882001-02-23 17:55:21 +00001616 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1617 return (-1);
1618 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001619#ifdef SUPPORT_IP6
1620 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1621 char buf6[INET6_ADDRSTRLEN];
1622 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1623 buf6, INET6_ADDRSTRLEN);
Daniel Veillard2db8c122003-07-08 12:16:59 +00001624 adp = (unsigned char *) buf6;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001625 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1626 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1627 } else
1628#endif
1629 {
1630 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1631 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1632 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1633 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1634 portp[0] & 0xff, portp[1] & 0xff);
1635 }
1636
Owen Taylor3473f882001-02-23 17:55:21 +00001637 buf[sizeof(buf) - 1] = 0;
1638 len = strlen(buf);
1639#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001640 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001641#endif
1642
1643 res = send(ctxt->controlFd, buf, len, 0);
1644 if (res < 0) {
1645 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1646 return(res);
1647 }
1648 res = xmlNanoFTPGetResponse(ctxt);
1649 if (res != 2) {
1650 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1651 return(-1);
1652 }
1653 }
1654 return(ctxt->dataFd);
1655
1656}
1657
1658/**
1659 * xmlNanoFTPCloseConnection:
1660 * @ctx: an FTP context
1661 *
1662 * Close the data connection from the server
1663 *
1664 * Returns -1 incase of error, 0 otherwise
1665 */
1666
1667int
1668xmlNanoFTPCloseConnection(void *ctx) {
1669 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1670 int res;
1671 fd_set rfd, efd;
1672 struct timeval tv;
1673
1674 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1675 tv.tv_sec = 15;
1676 tv.tv_usec = 0;
1677 FD_ZERO(&rfd);
1678 FD_SET(ctxt->controlFd, &rfd);
1679 FD_ZERO(&efd);
1680 FD_SET(ctxt->controlFd, &efd);
1681 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1682 if (res < 0) {
1683#ifdef DEBUG_FTP
1684 perror("select");
1685#endif
1686 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1687 return(-1);
1688 }
1689 if (res == 0) {
1690#ifdef DEBUG_FTP
1691 xmlGenericError(xmlGenericErrorContext,
1692 "xmlNanoFTPCloseConnection: timeout\n");
1693#endif
1694 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1695 } else {
1696 res = xmlNanoFTPGetResponse(ctxt);
1697 if (res != 2) {
1698 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1699 return(-1);
1700 }
1701 }
1702 return(0);
1703}
1704
1705/**
1706 * xmlNanoFTPParseList:
1707 * @list: some data listing received from the server
1708 * @callback: the user callback
1709 * @userData: the user callback data
1710 *
1711 * Parse at most one entry from the listing.
1712 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001713 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001714 */
1715
1716static int
1717xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1718 const char *cur = list;
1719 char filename[151];
1720 char attrib[11];
1721 char owner[11];
1722 char group[11];
1723 char month[4];
1724 int year = 0;
1725 int minute = 0;
1726 int hour = 0;
1727 int day = 0;
1728 unsigned long size = 0;
1729 int links = 0;
1730 int i;
1731
1732 if (!strncmp(cur, "total", 5)) {
1733 cur += 5;
1734 while (*cur == ' ') cur++;
1735 while ((*cur >= '0') && (*cur <= '9'))
1736 links = (links * 10) + (*cur++ - '0');
1737 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1738 cur++;
1739 return(cur - list);
1740 } else if (*list == '+') {
1741 return(0);
1742 } else {
1743 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1744 cur++;
1745 if (*cur == 0) return(0);
1746 i = 0;
1747 while (*cur != ' ') {
1748 if (i < 10)
1749 attrib[i++] = *cur;
1750 cur++;
1751 if (*cur == 0) return(0);
1752 }
1753 attrib[10] = 0;
1754 while (*cur == ' ') cur++;
1755 if (*cur == 0) return(0);
1756 while ((*cur >= '0') && (*cur <= '9'))
1757 links = (links * 10) + (*cur++ - '0');
1758 while (*cur == ' ') cur++;
1759 if (*cur == 0) return(0);
1760 i = 0;
1761 while (*cur != ' ') {
1762 if (i < 10)
1763 owner[i++] = *cur;
1764 cur++;
1765 if (*cur == 0) return(0);
1766 }
1767 owner[i] = 0;
1768 while (*cur == ' ') cur++;
1769 if (*cur == 0) return(0);
1770 i = 0;
1771 while (*cur != ' ') {
1772 if (i < 10)
1773 group[i++] = *cur;
1774 cur++;
1775 if (*cur == 0) return(0);
1776 }
1777 group[i] = 0;
1778 while (*cur == ' ') cur++;
1779 if (*cur == 0) return(0);
1780 while ((*cur >= '0') && (*cur <= '9'))
1781 size = (size * 10) + (*cur++ - '0');
1782 while (*cur == ' ') cur++;
1783 if (*cur == 0) return(0);
1784 i = 0;
1785 while (*cur != ' ') {
1786 if (i < 3)
1787 month[i++] = *cur;
1788 cur++;
1789 if (*cur == 0) return(0);
1790 }
1791 month[i] = 0;
1792 while (*cur == ' ') cur++;
1793 if (*cur == 0) return(0);
1794 while ((*cur >= '0') && (*cur <= '9'))
1795 day = (day * 10) + (*cur++ - '0');
1796 while (*cur == ' ') cur++;
1797 if (*cur == 0) return(0);
1798 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1799 if ((cur[1] == ':') || (cur[2] == ':')) {
1800 while ((*cur >= '0') && (*cur <= '9'))
1801 hour = (hour * 10) + (*cur++ - '0');
1802 if (*cur == ':') cur++;
1803 while ((*cur >= '0') && (*cur <= '9'))
1804 minute = (minute * 10) + (*cur++ - '0');
1805 } else {
1806 while ((*cur >= '0') && (*cur <= '9'))
1807 year = (year * 10) + (*cur++ - '0');
1808 }
1809 while (*cur == ' ') cur++;
1810 if (*cur == 0) return(0);
1811 i = 0;
1812 while ((*cur != '\n') && (*cur != '\r')) {
1813 if (i < 150)
1814 filename[i++] = *cur;
1815 cur++;
1816 if (*cur == 0) return(0);
1817 }
1818 filename[i] = 0;
1819 if ((*cur != '\n') && (*cur != '\r'))
1820 return(0);
1821 while ((*cur == '\n') || (*cur == '\r'))
1822 cur++;
1823 }
1824 if (callback != NULL) {
1825 callback(userData, filename, attrib, owner, group, size, links,
1826 year, month, day, hour, minute);
1827 }
1828 return(cur - list);
1829}
1830
1831/**
1832 * xmlNanoFTPList:
1833 * @ctx: an FTP context
1834 * @callback: the user callback
1835 * @userData: the user callback data
1836 * @filename: optional files to list
1837 *
1838 * Do a listing on the server. All files info are passed back
1839 * in the callbacks.
1840 *
1841 * Returns -1 incase of error, 0 otherwise
1842 */
1843
1844int
1845xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1846 char *filename) {
1847 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1848 char buf[4096 + 1];
1849 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001850 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001851 fd_set rfd, efd;
1852 struct timeval tv;
1853
1854 if (filename == NULL) {
1855 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1856 return(-1);
1857 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1858 if (ctxt->dataFd == -1)
1859 return(-1);
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001860 snprintf(buf, sizeof(buf), "LIST -L\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001861 } else {
1862 if (filename[0] != '/') {
1863 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1864 return(-1);
1865 }
1866 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1867 if (ctxt->dataFd == -1)
1868 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001869 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001870 }
1871 buf[sizeof(buf) - 1] = 0;
1872 len = strlen(buf);
1873#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001874 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001875#endif
1876 res = send(ctxt->controlFd, buf, len, 0);
1877 if (res < 0) {
1878 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1879 return(res);
1880 }
1881 res = xmlNanoFTPReadResponse(ctxt);
1882 if (res != 1) {
1883 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1884 return(-res);
1885 }
1886
1887 do {
1888 tv.tv_sec = 1;
1889 tv.tv_usec = 0;
1890 FD_ZERO(&rfd);
1891 FD_SET(ctxt->dataFd, &rfd);
1892 FD_ZERO(&efd);
1893 FD_SET(ctxt->dataFd, &efd);
1894 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1895 if (res < 0) {
1896#ifdef DEBUG_FTP
1897 perror("select");
1898#endif
1899 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1900 return(-1);
1901 }
1902 if (res == 0) {
1903 res = xmlNanoFTPCheckResponse(ctxt);
1904 if (res < 0) {
1905 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1906 ctxt->dataFd = -1;
1907 return(-1);
1908 }
1909 if (res == 2) {
1910 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1911 return(0);
1912 }
1913
1914 continue;
1915 }
1916
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001917 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001918#ifdef DEBUG_FTP
1919 perror("recv");
1920#endif
1921 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1922 ctxt->dataFd = -1;
1923 return(-1);
1924 }
1925#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001926 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001927#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001928 indx += len;
1929 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001930 base = 0;
1931 do {
1932 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1933 base += res;
1934 } while (res > 0);
1935
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001936 memmove(&buf[0], &buf[base], indx - base);
1937 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001938 } while (len != 0);
1939 xmlNanoFTPCloseConnection(ctxt);
1940 return(0);
1941}
1942
1943/**
1944 * xmlNanoFTPGetSocket:
1945 * @ctx: an FTP context
1946 * @filename: the file to retrieve (or NULL if path is in context).
1947 *
1948 * Initiate fetch of the given file from the server.
1949 *
1950 * Returns the socket for the data connection, or <0 in case of error
1951 */
1952
1953
1954int
1955xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1956 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1957 char buf[300];
1958 int res, len;
1959 if ((filename == NULL) && (ctxt->path == NULL))
1960 return(-1);
1961 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1962 if (ctxt->dataFd == -1)
1963 return(-1);
1964
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001965 snprintf(buf, sizeof(buf), "TYPE I\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001966 len = strlen(buf);
1967#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001968 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001969#endif
1970 res = send(ctxt->controlFd, buf, len, 0);
1971 if (res < 0) {
1972 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1973 return(res);
1974 }
1975 res = xmlNanoFTPReadResponse(ctxt);
1976 if (res != 2) {
1977 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1978 return(-res);
1979 }
1980 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001981 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001982 else
Owen Taylor3473f882001-02-23 17:55:21 +00001983 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001984 buf[sizeof(buf) - 1] = 0;
1985 len = strlen(buf);
1986#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001987 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001988#endif
1989 res = send(ctxt->controlFd, buf, len, 0);
1990 if (res < 0) {
1991 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1992 return(res);
1993 }
1994 res = xmlNanoFTPReadResponse(ctxt);
1995 if (res != 1) {
1996 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1997 return(-res);
1998 }
1999 return(ctxt->dataFd);
2000}
2001
2002/**
2003 * xmlNanoFTPGet:
2004 * @ctx: an FTP context
2005 * @callback: the user callback
2006 * @userData: the user callback data
2007 * @filename: the file to retrieve
2008 *
2009 * Fetch the given file from the server. All data are passed back
2010 * in the callbacks. The last callback has a size of 0 block.
2011 *
2012 * Returns -1 incase of error, 0 otherwise
2013 */
2014
2015int
2016xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
2017 const char *filename) {
2018 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2019 char buf[4096];
2020 int len = 0, res;
2021 fd_set rfd;
2022 struct timeval tv;
2023
2024 if ((filename == NULL) && (ctxt->path == NULL))
2025 return(-1);
2026 if (callback == NULL)
2027 return(-1);
2028 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
2029 return(-1);
2030
2031 do {
2032 tv.tv_sec = 1;
2033 tv.tv_usec = 0;
2034 FD_ZERO(&rfd);
2035 FD_SET(ctxt->dataFd, &rfd);
2036 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
2037 if (res < 0) {
2038#ifdef DEBUG_FTP
2039 perror("select");
2040#endif
2041 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2042 return(-1);
2043 }
2044 if (res == 0) {
2045 res = xmlNanoFTPCheckResponse(ctxt);
2046 if (res < 0) {
2047 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2048 ctxt->dataFd = -1;
2049 return(-1);
2050 }
2051 if (res == 2) {
2052 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2053 return(0);
2054 }
2055
2056 continue;
2057 }
2058 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
2059 callback(userData, buf, len);
2060 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2061 return(-1);
2062 }
2063 callback(userData, buf, len);
2064 } while (len != 0);
2065
2066 return(xmlNanoFTPCloseConnection(ctxt));
2067}
2068
2069/**
2070 * xmlNanoFTPRead:
2071 * @ctx: the FTP context
2072 * @dest: a buffer
2073 * @len: the buffer length
2074 *
2075 * This function tries to read @len bytes from the existing FTP connection
2076 * and saves them in @dest. This is a blocking call.
2077 *
2078 * Returns the number of byte read. 0 is an indication of an end of connection.
2079 * -1 indicates a parameter error.
2080 */
2081int
2082xmlNanoFTPRead(void *ctx, void *dest, int len) {
2083 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2084
2085 if (ctx == NULL) return(-1);
2086 if (ctxt->dataFd < 0) return(0);
2087 if (dest == NULL) return(-1);
2088 if (len <= 0) return(0);
2089
2090 len = recv(ctxt->dataFd, dest, len, 0);
2091#ifdef DEBUG_FTP
2092 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
2093#endif
2094 if (len <= 0) {
2095 xmlNanoFTPCloseConnection(ctxt);
2096 }
2097 return(len);
2098}
2099
2100/**
2101 * xmlNanoFTPOpen:
2102 * @URL: the URL to the resource
2103 *
2104 * Start to fetch the given ftp:// resource
2105 *
2106 * Returns an FTP context, or NULL
2107 */
2108
2109void*
2110xmlNanoFTPOpen(const char *URL) {
2111 xmlNanoFTPCtxtPtr ctxt;
2112 int sock;
2113
2114 xmlNanoFTPInit();
2115 if (URL == NULL) return(NULL);
2116 if (strncmp("ftp://", URL, 6)) return(NULL);
2117
2118 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2119 if (ctxt == NULL) return(NULL);
2120 if (xmlNanoFTPConnect(ctxt) < 0) {
2121 xmlNanoFTPFreeCtxt(ctxt);
2122 return(NULL);
2123 }
2124 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2125 if (sock < 0) {
2126 xmlNanoFTPFreeCtxt(ctxt);
2127 return(NULL);
2128 }
2129 return(ctxt);
2130}
2131
2132/**
2133 * xmlNanoFTPClose:
2134 * @ctx: an FTP context
2135 *
2136 * Close the connection and both control and transport
2137 *
2138 * Returns -1 incase of error, 0 otherwise
2139 */
2140
2141int
2142xmlNanoFTPClose(void *ctx) {
2143 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2144
2145 if (ctxt == NULL)
2146 return(-1);
2147
2148 if (ctxt->dataFd >= 0) {
2149 closesocket(ctxt->dataFd);
2150 ctxt->dataFd = -1;
2151 }
2152 if (ctxt->controlFd >= 0) {
2153 xmlNanoFTPQuit(ctxt);
2154 closesocket(ctxt->controlFd);
2155 ctxt->controlFd = -1;
2156 }
2157 xmlNanoFTPFreeCtxt(ctxt);
2158 return(0);
2159}
2160
2161#ifdef STANDALONE
2162/************************************************************************
2163 * *
2164 * Basic test in Standalone mode *
2165 * *
2166 ************************************************************************/
Daniel Veillard01c13b52002-12-10 15:19:08 +00002167static
Owen Taylor3473f882001-02-23 17:55:21 +00002168void ftpList(void *userData, const char *filename, const char* attrib,
2169 const char *owner, const char *group, unsigned long size, int links,
2170 int year, const char *month, int day, int hour, int minute) {
2171 xmlGenericError(xmlGenericErrorContext,
2172 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2173}
Daniel Veillard01c13b52002-12-10 15:19:08 +00002174static
Owen Taylor3473f882001-02-23 17:55:21 +00002175void ftpData(void *userData, const char *data, int len) {
2176 if (userData == NULL) return;
2177 if (len <= 0) {
2178 fclose(userData);
2179 return;
2180 }
2181 fwrite(data, len, 1, userData);
2182}
2183
2184int main(int argc, char **argv) {
2185 void *ctxt;
2186 FILE *output;
2187 char *tstfile = NULL;
2188
2189 xmlNanoFTPInit();
2190 if (argc > 1) {
2191 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2192 if (xmlNanoFTPConnect(ctxt) < 0) {
2193 xmlGenericError(xmlGenericErrorContext,
2194 "Couldn't connect to %s\n", argv[1]);
2195 exit(1);
2196 }
2197 if (argc > 2)
2198 tstfile = argv[2];
2199 } else
2200 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2201 if (ctxt == NULL) {
2202 xmlGenericError(xmlGenericErrorContext,
2203 "Couldn't connect to localhost\n");
2204 exit(1);
2205 }
2206 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2207 output = fopen("/tmp/tstdata", "w");
2208 if (output != NULL) {
2209 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2210 xmlGenericError(xmlGenericErrorContext,
2211 "Failed to get file\n");
2212
2213 }
2214 xmlNanoFTPClose(ctxt);
2215 xmlMemoryDump();
2216 exit(0);
2217}
2218#endif /* STANDALONE */
2219#else /* !LIBXML_FTP_ENABLED */
2220#ifdef STANDALONE
2221#include <stdio.h>
2222int main(int argc, char **argv) {
2223 xmlGenericError(xmlGenericErrorContext,
2224 "%s : FTP support not compiled in\n", argv[0]);
2225 return(0);
2226}
2227#endif /* STANDALONE */
2228#endif /* LIBXML_FTP_ENABLED */