blob: f4b08d83bcf7b1daf0ab00774f31b875fc4009a2 [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_
Daniel Veillarda9cce9c2003-09-29 13:20:24 +000093#ifndef __BEOS__
Owen Taylor3473f882001-02-23 17:55:21 +000094#define closesocket(s) close(s)
Daniel Veillarda9cce9c2003-09-29 13:20:24 +000095#endif
Owen Taylor3473f882001-02-23 17:55:21 +000096#define SOCKET int
97#endif
Daniel Veillardacf7ff02001-10-29 20:21:47 +000098#if defined(VMS) || defined(__VMS)
99#define SOCKLEN_T unsigned int
100#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000101
Daniel Veillard89f7f272003-09-29 13:29:09 +0000102#ifdef __BEOS__
103#ifndef PF_INET
104#define PF_INET AF_INET
105#endif
106#endif
107
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000108
Owen Taylor3473f882001-02-23 17:55:21 +0000109#define FTP_COMMAND_OK 200
110#define FTP_SYNTAX_ERROR 500
111#define FTP_GET_PASSWD 331
112#define FTP_BUF_SIZE 512
113
114typedef struct xmlNanoFTPCtxt {
115 char *protocol; /* the protocol name */
116 char *hostname; /* the host name */
117 int port; /* the port */
118 char *path; /* the path within the URL */
119 char *user; /* user string */
120 char *passwd; /* passwd string */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000121#ifdef SUPPORT_IP6
122 struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
123#else
Owen Taylor3473f882001-02-23 17:55:21 +0000124 struct sockaddr_in ftpAddr; /* the socket address struct */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000125#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000126 int passive; /* currently we support only passive !!! */
127 SOCKET controlFd; /* the file descriptor for the control socket */
128 SOCKET dataFd; /* the file descriptor for the data socket */
129 int state; /* WRITE / READ / CLOSED */
130 int returnValue; /* the protocol return value */
131 /* buffer for data received from the control connection */
132 char controlBuf[FTP_BUF_SIZE + 1];
133 int controlBufIndex;
134 int controlBufUsed;
135 int controlBufAnswer;
136} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
137
138static int initialized = 0;
139static char *proxy = NULL; /* the proxy name if any */
140static int proxyPort = 0; /* the proxy port if any */
141static char *proxyUser = NULL; /* user for proxy authentication */
142static char *proxyPasswd = NULL;/* passwd for proxy authentication */
143static int proxyType = 0; /* uses TYPE or a@b ? */
144
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000145#ifdef SUPPORT_IP6
Daniel Veillard2db8c122003-07-08 12:16:59 +0000146static
147int have_ipv6(void) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000148 int s;
149
150 s = socket (AF_INET6, SOCK_STREAM, 0);
151 if (s != -1) {
152 close (s);
153 return (1);
154 }
155 return (0);
156}
157#endif
158
Owen Taylor3473f882001-02-23 17:55:21 +0000159/**
160 * xmlNanoFTPInit:
161 *
162 * Initialize the FTP protocol layer.
163 * Currently it just checks for proxy informations,
164 * and get the hostname
165 */
166
167void
168xmlNanoFTPInit(void) {
169 const char *env;
170#ifdef _WINSOCKAPI_
171 WSADATA wsaData;
172#endif
173
174 if (initialized)
175 return;
176
177#ifdef _WINSOCKAPI_
178 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
179 return;
180#endif
181
Owen Taylor3473f882001-02-23 17:55:21 +0000182 proxyPort = 21;
183 env = getenv("no_proxy");
184 if (env != NULL)
185 return;
186 env = getenv("ftp_proxy");
187 if (env != NULL) {
188 xmlNanoFTPScanProxy(env);
189 } else {
190 env = getenv("FTP_PROXY");
191 if (env != NULL) {
192 xmlNanoFTPScanProxy(env);
193 }
194 }
195 env = getenv("ftp_proxy_user");
196 if (env != NULL) {
197 proxyUser = xmlMemStrdup(env);
198 }
199 env = getenv("ftp_proxy_password");
200 if (env != NULL) {
201 proxyPasswd = xmlMemStrdup(env);
202 }
203 initialized = 1;
204}
205
206/**
Daniel Veillarde356c282001-03-10 12:32:04 +0000207 * xmlNanoFTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000208 *
209 * Cleanup the FTP protocol layer. This cleanup proxy informations.
210 */
211
212void
213xmlNanoFTPCleanup(void) {
214 if (proxy != NULL) {
215 xmlFree(proxy);
216 proxy = NULL;
217 }
218 if (proxyUser != NULL) {
219 xmlFree(proxyUser);
220 proxyUser = NULL;
221 }
222 if (proxyPasswd != NULL) {
223 xmlFree(proxyPasswd);
224 proxyPasswd = NULL;
225 }
Owen Taylor3473f882001-02-23 17:55:21 +0000226#ifdef _WINSOCKAPI_
227 if (initialized)
228 WSACleanup();
229#endif
230 initialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000231}
232
233/**
234 * xmlNanoFTPProxy:
235 * @host: the proxy host name
236 * @port: the proxy port
237 * @user: the proxy user name
238 * @passwd: the proxy password
239 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
240 *
241 * Setup the FTP proxy informations.
242 * This can also be done by using ftp_proxy ftp_proxy_user and
243 * ftp_proxy_password environment variables.
244 */
245
246void
247xmlNanoFTPProxy(const char *host, int port, const char *user,
248 const char *passwd, int type) {
249 if (proxy != NULL)
250 xmlFree(proxy);
251 if (proxyUser != NULL)
252 xmlFree(proxyUser);
253 if (proxyPasswd != NULL)
254 xmlFree(proxyPasswd);
255 if (host)
256 proxy = xmlMemStrdup(host);
257 if (user)
258 proxyUser = xmlMemStrdup(user);
259 if (passwd)
260 proxyPasswd = xmlMemStrdup(passwd);
261 proxyPort = port;
262 proxyType = type;
263}
264
265/**
266 * xmlNanoFTPScanURL:
267 * @ctx: an FTP context
268 * @URL: The URL used to initialize the context
269 *
270 * (Re)Initialize an FTP context by parsing the URL and finding
271 * the protocol host port and path it indicates.
272 */
273
274static void
275xmlNanoFTPScanURL(void *ctx, const char *URL) {
276 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
277 const char *cur = URL;
278 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000279 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000280 int port = 0;
281
282 if (ctxt->protocol != NULL) {
283 xmlFree(ctxt->protocol);
284 ctxt->protocol = NULL;
285 }
286 if (ctxt->hostname != NULL) {
287 xmlFree(ctxt->hostname);
288 ctxt->hostname = NULL;
289 }
290 if (ctxt->path != NULL) {
291 xmlFree(ctxt->path);
292 ctxt->path = NULL;
293 }
294 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000295 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000296 while (*cur != 0) {
297 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000298 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000299 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000300 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000301 cur += 3;
302 break;
303 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000304 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000305 }
306 if (*cur == 0) return;
307
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000308 buf[indx] = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000309 /* allow user@ and user:pass@ forms */
310 {
311 const char *p = strchr(cur, '@');
312 if(p) {
313 while(1) {
314 if(cur[0] == ':' || cur[0] == '@') break;
315 buf[indx++] = *cur++;
316 }
317 buf[indx] = 0;
318 ctxt->user = xmlMemStrdup(buf);
319 indx = 0;
320 if(cur[0] == ':') {
321 cur++;
322 while(1) {
323 if(cur[0] == '@') break;
324 buf[indx++] = *cur++;
325 }
326 buf[indx] = 0;
327 ctxt->passwd = xmlMemStrdup(buf);
328 indx = 0;
329 }
330 cur = p+1;
331 }
332 }
333
Owen Taylor3473f882001-02-23 17:55:21 +0000334 while (1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000335 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
336 (!strchr (cur, '[') && strchr (cur, ']'))) {
337 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
338 "Syntax Error\n");
339 return;
340 }
341
342 if (cur[0] == '[') {
343 cur++;
344 while (cur[0] != ']')
345 buf[indx++] = *cur++;
346
347 if (!strchr (buf, ':')) {
348 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
349 "Use [IPv6]/IPv4 format\n");
350 return;
351 }
352
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000353 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000354 ctxt->hostname = xmlMemStrdup (buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000355 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000356 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000357 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000358 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000359 while (*cur >= '0' && *cur <= '9') {
360 port *= 10;
361 port += *cur - '0';
362 cur++;
363 }
364
365 if (port != 0) ctxt->port = port;
366 while ((cur[0] != '/') && (*cur != 0))
367 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000368 }
Owen Taylor3473f882001-02-23 17:55:21 +0000369 break;
370 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000371 else { /* address is an IPv4 one*/
372 if (cur[0] == ':') {
373 buf[indx] = 0;
374 ctxt->hostname = xmlMemStrdup (buf);
375 indx = 0;
376 cur += 1;
377 while ((*cur >= '0') && (*cur <= '9')) {
378 port *= 10;
379 port += *cur - '0';
380 cur++;
381 }
382 if (port != 0) ctxt->port = port;
383 while ((cur[0] != '/') && (*cur != 0))
384 cur++;
385 break;
386 }
387 if ((*cur == '/') || (*cur == 0)) {
388 buf[indx] = 0;
389 ctxt->hostname = xmlMemStrdup (buf);
390 indx = 0;
391 break;
392 }
Owen Taylor3473f882001-02-23 17:55:21 +0000393 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000394 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000395 }
396 if (*cur == 0)
397 ctxt->path = xmlMemStrdup("/");
398 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000399 indx = 0;
400 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000401 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000402 buf[indx++] = *cur++;
403 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000404 ctxt->path = xmlMemStrdup(buf);
405 }
406}
407
408/**
409 * xmlNanoFTPUpdateURL:
410 * @ctx: an FTP context
411 * @URL: The URL used to update the context
412 *
413 * Update an FTP context by parsing the URL and finding
414 * new path it indicates. If there is an error in the
415 * protocol, hostname, port or other information, the
416 * error is raised. It indicates a new connection has to
417 * be established.
418 *
419 * Returns 0 if Ok, -1 in case of error (other host).
420 */
421
422int
423xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
424 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
425 const char *cur = URL;
426 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000427 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000428 int port = 0;
429
430 if (URL == NULL)
431 return(-1);
432 if (ctxt == NULL)
433 return(-1);
434 if (ctxt->protocol == NULL)
435 return(-1);
436 if (ctxt->hostname == NULL)
437 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000438 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000439 while (*cur != 0) {
440 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000441 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000442 if (strcmp(ctxt->protocol, buf))
443 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000444 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000445 cur += 3;
446 break;
447 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000448 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000449 }
450 if (*cur == 0)
451 return(-1);
452
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000453 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000454 while (1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000455 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
456 (!strchr (cur, '[') && strchr (cur, ']'))) {
457 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
458 "Syntax Error\n");
459 return (-1);
460 }
461
462 if (cur[0] == '[') {
463 cur++;
464 while (cur[0] != ']')
465 buf[indx++] = *cur++;
466
467 if (!strchr (buf, ':')) {
468 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
469 "Use [IPv6]/IPv4 format\n");
470 return (-1);
471 }
472
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000473 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000474 if (strcmp (ctxt->hostname, buf))
475 return (-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000476 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000477 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000478 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000479 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000480 while (*cur >= '0' && *cur <= '9') {
481 port *= 10;
482 port += *cur - '0';
483 cur++;
484 }
485
486 if (port != ctxt->port)
487 return (-1);
488 while ((cur[0] != '/') && (*cur != 0))
489 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000490 }
Owen Taylor3473f882001-02-23 17:55:21 +0000491 break;
492 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000493 else {
494 if (cur[0] == ':') {
495 buf[indx] = 0;
496 if (strcmp (ctxt->hostname, buf))
497 return (-1);
498 indx = 0;
499 cur += 1;
500 while ((*cur >= '0') && (*cur <= '9')) {
501 port *= 10;
502 port += *cur - '0';
503 cur++;
504 }
505 if (port != ctxt->port)
506 return (-1);
507 while ((cur[0] != '/') && (*cur != 0))
508 cur++;
509 break;
510 }
511 if ((*cur == '/') || (*cur == 0)) {
512 buf[indx] = 0;
513 if (strcmp (ctxt->hostname, buf))
514 return (-1);
515 indx = 0;
516 break;
517 }
Owen Taylor3473f882001-02-23 17:55:21 +0000518 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000519 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000520 }
521 if (ctxt->path != NULL) {
522 xmlFree(ctxt->path);
523 ctxt->path = NULL;
524 }
525
526 if (*cur == 0)
527 ctxt->path = xmlMemStrdup("/");
528 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000529 indx = 0;
530 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000531 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000532 buf[indx++] = *cur++;
533 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000534 ctxt->path = xmlMemStrdup(buf);
535 }
536 return(0);
537}
538
539/**
540 * xmlNanoFTPScanProxy:
541 * @URL: The proxy URL used to initialize the proxy context
542 *
543 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
544 * the protocol host port it indicates.
545 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
546 * A NULL URL cleans up proxy informations.
547 */
548
549void
550xmlNanoFTPScanProxy(const char *URL) {
551 const char *cur = URL;
552 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000553 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000554 int port = 0;
555
556 if (proxy != NULL) {
557 xmlFree(proxy);
558 proxy = NULL;
559 }
560 if (proxyPort != 0) {
561 proxyPort = 0;
562 }
563#ifdef DEBUG_FTP
564 if (URL == NULL)
565 xmlGenericError(xmlGenericErrorContext, "Removing FTP proxy info\n");
566 else
567 xmlGenericError(xmlGenericErrorContext, "Using FTP proxy %s\n", URL);
568#endif
569 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000570 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000571 while (*cur != 0) {
572 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000573 buf[indx] = 0;
574 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000575 cur += 3;
576 break;
577 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000578 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000579 }
580 if (*cur == 0) return;
581
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000582 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000583 while (1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000584 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
585 (!strchr (cur, '[') && strchr (cur, ']'))) {
586 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
587 "Syntax error\n");
588 return;
589 }
590
591 if (cur[0] == '[') {
592 cur++;
593 while (cur[0] != ']')
594 buf[indx++] = *cur++;
595 if (!strchr (buf, ':')) {
596 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
597 "Use [IPv6]/IPv4 format\n");
598 return;
599 }
600
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000601 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000602 proxy = xmlMemStrdup (buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000603 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000604 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000605 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000606 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000607 while (*cur >= '0' && *cur <= '9') {
608 port *= 10;
609 port += *cur - '0';
610 cur++;
611 }
612
613 if (port != 0) proxyPort = port;
614 while ((cur[0] != '/') && (*cur != 0))
615 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000616 }
Owen Taylor3473f882001-02-23 17:55:21 +0000617 break;
618 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000619 else {
620 if (cur[0] == ':') {
621 buf[indx] = 0;
622 proxy = xmlMemStrdup (buf);
623 indx = 0;
624 cur += 1;
625 while ((*cur >= '0') && (*cur <= '9')) {
626 port *= 10;
627 port += *cur - '0';
628 cur++;
629 }
630 if (port != 0) proxyPort = port;
631 while ((cur[0] != '/') && (*cur != 0))
632 cur++;
633 break;
634 }
635 if ((*cur == '/') || (*cur == 0)) {
636 buf[indx] = 0;
637 proxy = xmlMemStrdup (buf);
638 indx = 0;
639 break;
640 }
Owen Taylor3473f882001-02-23 17:55:21 +0000641 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000642 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000643 }
644}
645
646/**
647 * xmlNanoFTPNewCtxt:
648 * @URL: The URL used to initialize the context
649 *
650 * Allocate and initialize a new FTP context.
651 *
652 * Returns an FTP context or NULL in case of error.
653 */
654
655void*
656xmlNanoFTPNewCtxt(const char *URL) {
657 xmlNanoFTPCtxtPtr ret;
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000658 char *unescaped;
Owen Taylor3473f882001-02-23 17:55:21 +0000659
660 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
661 if (ret == NULL) return(NULL);
662
663 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
664 ret->port = 21;
665 ret->passive = 1;
666 ret->returnValue = 0;
667 ret->controlBufIndex = 0;
668 ret->controlBufUsed = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000669 ret->controlFd = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000670
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000671 unescaped = xmlURIUnescapeString(URL, 0, NULL);
672 if (unescaped != NULL)
673 xmlNanoFTPScanURL(ret, unescaped);
674 else if (URL != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000675 xmlNanoFTPScanURL(ret, URL);
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000676 xmlFree(unescaped);
Owen Taylor3473f882001-02-23 17:55:21 +0000677
678 return(ret);
679}
680
681/**
682 * xmlNanoFTPFreeCtxt:
683 * @ctx: an FTP context
684 *
685 * Frees the context after closing the connection.
686 */
687
688void
689xmlNanoFTPFreeCtxt(void * ctx) {
690 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
691 if (ctxt == NULL) return;
692 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
693 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
694 if (ctxt->path != NULL) xmlFree(ctxt->path);
695 ctxt->passive = 1;
696 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
697 ctxt->controlFd = -1;
698 ctxt->controlBufIndex = -1;
699 ctxt->controlBufUsed = -1;
700 xmlFree(ctxt);
701}
702
703/**
704 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000705 * @buf: the buffer containing the response
706 * @len: the buffer length
707 *
708 * Parsing of the server answer, we just extract the code.
709 *
710 * returns 0 for errors
711 * +XXX for last line of response
712 * -XXX for response to be continued
713 */
714static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000715xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000716 int val = 0;
717
718 if (len < 3) return(-1);
719 if ((*buf >= '0') && (*buf <= '9'))
720 val = val * 10 + (*buf - '0');
721 else
722 return(0);
723 buf++;
724 if ((*buf >= '0') && (*buf <= '9'))
725 val = val * 10 + (*buf - '0');
726 else
727 return(0);
728 buf++;
729 if ((*buf >= '0') && (*buf <= '9'))
730 val = val * 10 + (*buf - '0');
731 else
732 return(0);
733 buf++;
734 if (*buf == '-')
735 return(-val);
736 return(val);
737}
738
739/**
740 * xmlNanoFTPGetMore:
741 * @ctx: an FTP context
742 *
743 * Read more information from the FTP control connection
744 * Returns the number of bytes read, < 0 indicates an error
745 */
746static int
747xmlNanoFTPGetMore(void *ctx) {
748 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
749 int len;
750 int size;
751
752 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
753#ifdef DEBUG_FTP
754 xmlGenericError(xmlGenericErrorContext,
755 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
756 ctxt->controlBufIndex);
757#endif
758 return(-1);
759 }
760
761 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
762#ifdef DEBUG_FTP
763 xmlGenericError(xmlGenericErrorContext,
764 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
765 ctxt->controlBufUsed);
766#endif
767 return(-1);
768 }
769 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
770#ifdef DEBUG_FTP
771 xmlGenericError(xmlGenericErrorContext,
772 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
773 ctxt->controlBufIndex, ctxt->controlBufUsed);
774#endif
775 return(-1);
776 }
777
778 /*
779 * First pack the control buffer
780 */
781 if (ctxt->controlBufIndex > 0) {
782 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
783 ctxt->controlBufUsed - ctxt->controlBufIndex);
784 ctxt->controlBufUsed -= ctxt->controlBufIndex;
785 ctxt->controlBufIndex = 0;
786 }
787 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
788 if (size == 0) {
789#ifdef DEBUG_FTP
790 xmlGenericError(xmlGenericErrorContext,
791 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
792#endif
793 return(0);
794 }
795
796 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000797 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000798 */
799 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
800 size, 0)) < 0) {
801 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
802 ctxt->controlFd = -1;
803 return(-1);
804 }
805#ifdef DEBUG_FTP
806 xmlGenericError(xmlGenericErrorContext,
807 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
808 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
809#endif
810 ctxt->controlBufUsed += len;
811 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
812
813 return(len);
814}
815
816/**
817 * xmlNanoFTPReadResponse:
818 * @ctx: an FTP context
819 *
820 * Read the response from the FTP server after a command.
821 * Returns the code number
822 */
823static int
824xmlNanoFTPReadResponse(void *ctx) {
825 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
826 char *ptr, *end;
827 int len;
828 int res = -1, cur = -1;
829
830get_more:
831 /*
832 * Assumes everything up to controlBuf[controlBufIndex] has been read
833 * and analyzed.
834 */
835 len = xmlNanoFTPGetMore(ctx);
836 if (len < 0) {
837 return(-1);
838 }
839 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
840 return(-1);
841 }
842 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
843 end = &ctxt->controlBuf[ctxt->controlBufUsed];
844
845#ifdef DEBUG_FTP
846 xmlGenericError(xmlGenericErrorContext,
847 "\n<<<\n%s\n--\n", ptr);
848#endif
849 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000850 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000851 if (cur > 0) {
852 /*
853 * Successfully scanned the control code, scratch
854 * till the end of the line, but keep the index to be
855 * able to analyze the result if needed.
856 */
857 res = cur;
858 ptr += 3;
859 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
860 while ((ptr < end) && (*ptr != '\n')) ptr++;
861 if (*ptr == '\n') ptr++;
862 if (*ptr == '\r') ptr++;
863 break;
864 }
865 while ((ptr < end) && (*ptr != '\n')) ptr++;
866 if (ptr >= end) {
867 ctxt->controlBufIndex = ctxt->controlBufUsed;
868 goto get_more;
869 }
870 if (*ptr != '\r') ptr++;
871 }
872
873 if (res < 0) goto get_more;
874 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
875#ifdef DEBUG_FTP
876 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
877 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
878#endif
879
880#ifdef DEBUG_FTP
881 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
882#endif
883 return(res / 100);
884}
885
886/**
887 * xmlNanoFTPGetResponse:
888 * @ctx: an FTP context
889 *
890 * Get the response from the FTP server after a command.
891 * Returns the code number
892 */
893
894int
895xmlNanoFTPGetResponse(void *ctx) {
896 int res;
897
898 res = xmlNanoFTPReadResponse(ctx);
899
900 return(res);
901}
902
903/**
904 * xmlNanoFTPCheckResponse:
905 * @ctx: an FTP context
906 *
907 * Check if there is a response from the FTP server after a command.
908 * Returns the code number, or 0
909 */
910
911int
912xmlNanoFTPCheckResponse(void *ctx) {
913 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
914 fd_set rfd;
915 struct timeval tv;
916
917 tv.tv_sec = 0;
918 tv.tv_usec = 0;
919 FD_ZERO(&rfd);
920 FD_SET(ctxt->controlFd, &rfd);
921 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
922 case 0:
923 return(0);
924 case -1:
925#ifdef DEBUG_FTP
926 perror("select");
927#endif
928 return(-1);
929
930 }
931
932 return(xmlNanoFTPReadResponse(ctx));
933}
934
935/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000936 * Send the user authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000937 */
938
939static int
940xmlNanoFTPSendUser(void *ctx) {
941 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
942 char buf[200];
943 int len;
944 int res;
945
946 if (ctxt->user == NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000947 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000948 else
Owen Taylor3473f882001-02-23 17:55:21 +0000949 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000950 buf[sizeof(buf) - 1] = 0;
951 len = strlen(buf);
952#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000953 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000954#endif
955 res = send(ctxt->controlFd, buf, len, 0);
956 if (res < 0) return(res);
957 return(0);
958}
959
960/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000961 * Send the password authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000962 */
963
964static int
965xmlNanoFTPSendPasswd(void *ctx) {
966 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
967 char buf[200];
968 int len;
969 int res;
970
971 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +0000972 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000973 else
Owen Taylor3473f882001-02-23 17:55:21 +0000974 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +0000975 buf[sizeof(buf) - 1] = 0;
976 len = strlen(buf);
977#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000978 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000979#endif
980 res = send(ctxt->controlFd, buf, len, 0);
981 if (res < 0) return(res);
982 return(0);
983}
984
985/**
986 * xmlNanoFTPQuit:
987 * @ctx: an FTP context
988 *
989 * Send a QUIT command to the server
990 *
991 * Returns -1 in case of error, 0 otherwise
992 */
993
994
995int
996xmlNanoFTPQuit(void *ctx) {
997 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
998 char buf[200];
999 int len;
Owen Taylor3473f882001-02-23 17:55:21 +00001000
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001001 snprintf(buf, sizeof(buf), "QUIT\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001002 len = strlen(buf);
1003#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001004 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 +00001005#endif
William M. Brack78637da2003-07-31 14:47:38 +00001006 send(ctxt->controlFd, buf, len, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001007 return(0);
1008}
1009
1010/**
1011 * xmlNanoFTPConnect:
1012 * @ctx: an FTP context
1013 *
1014 * Tries to open a control connection
1015 *
1016 * Returns -1 in case of error, 0 otherwise
1017 */
1018
1019int
1020xmlNanoFTPConnect(void *ctx) {
1021 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1022 struct hostent *hp;
1023 int port;
1024 int res;
Daniel Veillard2db8c122003-07-08 12:16:59 +00001025 int addrlen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001026
1027 if (ctxt == NULL)
1028 return(-1);
1029 if (ctxt->hostname == NULL)
1030 return(-1);
1031
1032 /*
1033 * do the blocking DNS query.
1034 */
Owen Taylor3473f882001-02-23 17:55:21 +00001035 if (proxy) {
1036 port = proxyPort;
1037 } else {
1038 port = ctxt->port;
1039 }
1040 if (port == 0)
1041 port = 21;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001042
1043 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
1044
1045#ifdef SUPPORT_IP6
1046 if (have_ipv6 ()) {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001047 struct addrinfo hints, *tmp, *result;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001048
1049 result = NULL;
1050 memset (&hints, 0, sizeof(hints));
1051 hints.ai_socktype = SOCK_STREAM;
1052
1053 if (proxy) {
1054 if (getaddrinfo (proxy, NULL, &hints, &result) != 0)
1055 return (-1);
1056 }
1057 else
1058 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0)
1059 return (-1);
1060
Daniel Veillard2db8c122003-07-08 12:16:59 +00001061 for (tmp = result; tmp; tmp = tmp->ai_next)
1062 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001063 break;
1064
Daniel Veillard3dc93a42003-07-10 14:04:33 +00001065 if (!tmp) {
1066 if (result)
1067 freeaddrinfo (result);
1068 return (-1);
1069 }
1070 else {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001071 if (tmp->ai_family == AF_INET6) {
1072 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001073 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
1074 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
1075 }
1076 else {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001077 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001078 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
1079 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1080 }
Daniel Veillard2db8c122003-07-08 12:16:59 +00001081 addrlen = tmp->ai_addrlen;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001082 freeaddrinfo (result);
1083 }
1084 }
1085 else
1086#endif
1087 {
1088 if (proxy)
1089 hp = gethostbyname (proxy);
1090 else
1091 hp = gethostbyname (ctxt->hostname);
1092 if (hp == NULL)
1093 return (-1);
1094
1095 /*
1096 * Prepare the socket
1097 */
1098 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
1099 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
1100 hp->h_addr_list[0], hp->h_length);
1101 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = htons (port);
1102 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1103 addrlen = sizeof (struct sockaddr_in);
1104 }
1105
Owen Taylor3473f882001-02-23 17:55:21 +00001106 if (ctxt->controlFd < 0)
1107 return(-1);
1108
1109 /*
1110 * Do the connect.
1111 */
1112 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001113 addrlen) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001114 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1115 ctxt->controlFd = -1;
1116 return(-1);
1117 }
1118
1119 /*
1120 * Wait for the HELLO from the server.
1121 */
1122 res = xmlNanoFTPGetResponse(ctxt);
1123 if (res != 2) {
1124 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1125 ctxt->controlFd = -1;
1126 return(-1);
1127 }
1128
1129 /*
1130 * State diagram for the login operation on the FTP server
1131 *
1132 * Reference: RFC 959
1133 *
1134 * 1
1135 * +---+ USER +---+------------->+---+
1136 * | B |---------->| W | 2 ---->| E |
1137 * +---+ +---+------ | -->+---+
1138 * | | | | |
1139 * 3 | | 4,5 | | |
1140 * -------------- ----- | | |
1141 * | | | | |
1142 * | | | | |
1143 * | --------- |
1144 * | 1| | | |
1145 * V | | | |
1146 * +---+ PASS +---+ 2 | ------>+---+
1147 * | |---------->| W |------------->| S |
1148 * +---+ +---+ ---------->+---+
1149 * | | | | |
1150 * 3 | |4,5| | |
1151 * -------------- -------- |
1152 * | | | | |
1153 * | | | | |
1154 * | -----------
1155 * | 1,3| | | |
1156 * V | 2| | |
1157 * +---+ ACCT +---+-- | ----->+---+
1158 * | |---------->| W | 4,5 -------->| F |
1159 * +---+ +---+------------->+---+
1160 *
1161 * Of course in case of using a proxy this get really nasty and is not
1162 * standardized at all :-(
1163 */
1164 if (proxy) {
1165 int len;
1166 char buf[400];
1167
1168 if (proxyUser != NULL) {
1169 /*
1170 * We need proxy auth
1171 */
Owen Taylor3473f882001-02-23 17:55:21 +00001172 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +00001173 buf[sizeof(buf) - 1] = 0;
1174 len = strlen(buf);
1175#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001176 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001177#endif
1178 res = send(ctxt->controlFd, buf, len, 0);
1179 if (res < 0) {
1180 closesocket(ctxt->controlFd);
1181 ctxt->controlFd = -1;
1182 return(res);
1183 }
1184 res = xmlNanoFTPGetResponse(ctxt);
1185 switch (res) {
1186 case 2:
1187 if (proxyPasswd == NULL)
1188 break;
1189 case 3:
1190 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001191 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +00001192 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001193 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001194 buf[sizeof(buf) - 1] = 0;
1195 len = strlen(buf);
1196#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001197 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001198#endif
1199 res = send(ctxt->controlFd, buf, len, 0);
1200 if (res < 0) {
1201 closesocket(ctxt->controlFd);
1202 ctxt->controlFd = -1;
1203 return(res);
1204 }
1205 res = xmlNanoFTPGetResponse(ctxt);
1206 if (res > 3) {
1207 closesocket(ctxt->controlFd);
1208 ctxt->controlFd = -1;
1209 return(-1);
1210 }
1211 break;
1212 case 1:
1213 break;
1214 case 4:
1215 case 5:
1216 case -1:
1217 default:
1218 closesocket(ctxt->controlFd);
1219 ctxt->controlFd = -1;
1220 return(-1);
1221 }
1222 }
1223
1224 /*
1225 * We assume we don't need more authentication to the proxy
1226 * and that it succeeded :-\
1227 */
1228 switch (proxyType) {
1229 case 0:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001230 /* we will try in sequence */
Owen Taylor3473f882001-02-23 17:55:21 +00001231 case 1:
1232 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001233 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001234 buf[sizeof(buf) - 1] = 0;
1235 len = strlen(buf);
1236#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001237 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001238#endif
1239 res = send(ctxt->controlFd, buf, len, 0);
1240 if (res < 0) {
1241 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1242 ctxt->controlFd = -1;
1243 return(res);
1244 }
1245 res = xmlNanoFTPGetResponse(ctxt);
1246 if (res == 2) {
1247 /* we assume it worked :-\ 1 is error for SITE command */
1248 proxyType = 1;
1249 break;
1250 }
1251 if (proxyType == 1) {
1252 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1253 ctxt->controlFd = -1;
1254 return(-1);
1255 }
1256 case 2:
1257 /* USER user@host command */
1258 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001259 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1260 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001261 else
Owen Taylor3473f882001-02-23 17:55:21 +00001262 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1263 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001264 buf[sizeof(buf) - 1] = 0;
1265 len = strlen(buf);
1266#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001267 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001268#endif
1269 res = send(ctxt->controlFd, buf, len, 0);
1270 if (res < 0) {
1271 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1272 ctxt->controlFd = -1;
1273 return(res);
1274 }
1275 res = xmlNanoFTPGetResponse(ctxt);
1276 if ((res == 1) || (res == 2)) {
1277 /* we assume it worked :-\ */
1278 proxyType = 2;
1279 return(0);
1280 }
1281 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001282 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001283 else
Owen Taylor3473f882001-02-23 17:55:21 +00001284 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001285 buf[sizeof(buf) - 1] = 0;
1286 len = strlen(buf);
1287#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001288 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001289#endif
1290 res = send(ctxt->controlFd, buf, len, 0);
1291 if (res < 0) {
1292 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1293 ctxt->controlFd = -1;
1294 return(res);
1295 }
1296 res = xmlNanoFTPGetResponse(ctxt);
1297 if ((res == 1) || (res == 2)) {
1298 /* we assume it worked :-\ */
1299 proxyType = 2;
1300 return(0);
1301 }
1302 if (proxyType == 2) {
1303 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1304 ctxt->controlFd = -1;
1305 return(-1);
1306 }
1307 case 3:
1308 /*
1309 * If you need support for other Proxy authentication scheme
1310 * send the code or at least the sequence in use.
1311 */
1312 default:
1313 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1314 ctxt->controlFd = -1;
1315 return(-1);
1316 }
1317 }
1318 /*
1319 * Non-proxy handling.
1320 */
1321 res = xmlNanoFTPSendUser(ctxt);
1322 if (res < 0) {
1323 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1324 ctxt->controlFd = -1;
1325 return(-1);
1326 }
1327 res = xmlNanoFTPGetResponse(ctxt);
1328 switch (res) {
1329 case 2:
1330 return(0);
1331 case 3:
1332 break;
1333 case 1:
1334 case 4:
1335 case 5:
1336 case -1:
1337 default:
1338 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1339 ctxt->controlFd = -1;
1340 return(-1);
1341 }
1342 res = xmlNanoFTPSendPasswd(ctxt);
1343 if (res < 0) {
1344 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1345 ctxt->controlFd = -1;
1346 return(-1);
1347 }
1348 res = xmlNanoFTPGetResponse(ctxt);
1349 switch (res) {
1350 case 2:
1351 break;
1352 case 3:
1353 xmlGenericError(xmlGenericErrorContext,
1354 "FTP server asking for ACCNT on anonymous\n");
1355 case 1:
1356 case 4:
1357 case 5:
1358 case -1:
1359 default:
1360 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1361 ctxt->controlFd = -1;
1362 return(-1);
1363 }
1364
1365 return(0);
1366}
1367
1368/**
1369 * xmlNanoFTPConnectTo:
1370 * @server: an FTP server name
1371 * @port: the port (use 21 if 0)
1372 *
1373 * Tries to open a control connection to the given server/port
1374 *
1375 * Returns an fTP context or NULL if it failed
1376 */
1377
1378void*
1379xmlNanoFTPConnectTo(const char *server, int port) {
1380 xmlNanoFTPCtxtPtr ctxt;
1381 int res;
1382
1383 xmlNanoFTPInit();
1384 if (server == NULL)
1385 return(NULL);
1386 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1387 ctxt->hostname = xmlMemStrdup(server);
1388 if (port != 0)
1389 ctxt->port = port;
1390 res = xmlNanoFTPConnect(ctxt);
1391 if (res < 0) {
1392 xmlNanoFTPFreeCtxt(ctxt);
1393 return(NULL);
1394 }
1395 return(ctxt);
1396}
1397
1398/**
1399 * xmlNanoFTPCwd:
1400 * @ctx: an FTP context
1401 * @directory: a directory on the server
1402 *
1403 * Tries to change the remote directory
1404 *
1405 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1406 */
1407
1408int
1409xmlNanoFTPCwd(void *ctx, char *directory) {
1410 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1411 char buf[400];
1412 int len;
1413 int res;
1414
1415 /*
1416 * Expected response code for CWD:
1417 *
1418 * CWD
1419 * 250
1420 * 500, 501, 502, 421, 530, 550
1421 */
Owen Taylor3473f882001-02-23 17:55:21 +00001422 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001423 buf[sizeof(buf) - 1] = 0;
1424 len = strlen(buf);
1425#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001426 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001427#endif
1428 res = send(ctxt->controlFd, buf, len, 0);
1429 if (res < 0) return(res);
1430 res = xmlNanoFTPGetResponse(ctxt);
1431 if (res == 4) {
1432 return(-1);
1433 }
1434 if (res == 2) return(1);
1435 if (res == 5) {
1436 return(0);
1437 }
1438 return(0);
1439}
1440
1441/**
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001442 * xmlNanoFTPDele:
1443 * @ctx: an FTP context
1444 * @file: a file or directory on the server
1445 *
1446 * Tries to delete an item (file or directory) from server
1447 *
1448 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1449 */
1450
1451int
1452xmlNanoFTPDele(void *ctx, char *file) {
1453 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1454 char buf[400];
1455 int len;
1456 int res;
1457
1458 /*
1459 * Expected response code for DELE:
1460 *
1461 * DELE
1462 * 250
1463 * 450, 550
1464 * 500, 501, 502, 421, 530
1465 */
1466
1467 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1468 buf[sizeof(buf) - 1] = 0;
1469 len = strlen(buf);
1470#ifdef DEBUG_FTP
1471 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1472#endif
1473 res = send(ctxt->controlFd, buf, len, 0);
1474 if (res < 0) return(res);
1475 res = xmlNanoFTPGetResponse(ctxt);
1476 if (res == 4) {
1477 return(-1);
1478 }
1479 if (res == 2) return(1);
1480 if (res == 5) {
1481 return(0);
1482 }
1483 return(0);
1484}
1485/**
Owen Taylor3473f882001-02-23 17:55:21 +00001486 * xmlNanoFTPGetConnection:
1487 * @ctx: an FTP context
1488 *
1489 * Try to open a data connection to the server. Currently only
1490 * passive mode is supported.
1491 *
1492 * Returns -1 incase of error, 0 otherwise
1493 */
1494
1495int
1496xmlNanoFTPGetConnection(void *ctx) {
1497 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1498 char buf[200], *cur;
1499 int len, i;
1500 int res;
1501 unsigned char ad[6], *adp, *portp;
1502 unsigned int temp[6];
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001503#ifdef SUPPORT_IP6
1504 struct sockaddr_storage dataAddr;
1505#else
Owen Taylor3473f882001-02-23 17:55:21 +00001506 struct sockaddr_in dataAddr;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001507#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001508 SOCKLEN_T dataAddrLen;
1509
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001510 memset (&dataAddr, 0, sizeof(dataAddr));
1511#ifdef SUPPORT_IP6
1512 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1513 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1514 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1515 dataAddrLen = sizeof(struct sockaddr_in6);
1516 } else
1517#endif
1518 {
1519 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1520 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1521 dataAddrLen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001522 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001523
1524 if (ctxt->dataFd < 0) {
1525 xmlGenericError (xmlGenericErrorContext,
1526 "xmlNanoFTPGetConnection: failed to create socket\n");
1527 return (-1);
1528 }
Owen Taylor3473f882001-02-23 17:55:21 +00001529
1530 if (ctxt->passive) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001531#ifdef SUPPORT_IP6
1532 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1533 snprintf (buf, sizeof(buf), "EPSV\r\n");
1534 else
1535#endif
1536 snprintf (buf, sizeof(buf), "PASV\r\n");
1537 len = strlen (buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001538#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001539 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001540#endif
1541 res = send(ctxt->controlFd, buf, len, 0);
1542 if (res < 0) {
1543 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1544 return(res);
1545 }
1546 res = xmlNanoFTPReadResponse(ctx);
1547 if (res != 2) {
1548 if (res == 5) {
1549 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1550 return(-1);
1551 } else {
1552 /*
1553 * retry with an active connection
1554 */
1555 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1556 ctxt->passive = 0;
1557 }
1558 }
1559 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1560 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001561#ifdef SUPPORT_IP6
1562 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1563 if (sscanf (cur, "%u", &temp[0]) != 1) {
1564 xmlGenericError (xmlGenericErrorContext,
1565 "Invalid answer to EPSV\n");
1566 if (ctxt->dataFd != -1) {
1567 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1568 }
1569 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001570 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001571 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1572 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
Owen Taylor3473f882001-02-23 17:55:21 +00001573 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001574 else
1575#endif
1576 {
1577 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1578 &temp[3], &temp[4], &temp[5]) != 6) {
1579 xmlGenericError (xmlGenericErrorContext,
1580 "Invalid answer to PASV\n");
1581 if (ctxt->dataFd != -1) {
1582 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1583 }
1584 return (-1);
1585 }
1586 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1587 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1588 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1589 }
1590
Owen Taylor3473f882001-02-23 17:55:21 +00001591 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1592 xmlGenericError(xmlGenericErrorContext,
1593 "Failed to create a data connection\n");
1594 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1595 return (-1);
1596 }
1597 } else {
1598 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001599#ifdef SUPPORT_IP6
1600 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1601 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1602 else
1603#endif
1604 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1605
Owen Taylor3473f882001-02-23 17:55:21 +00001606 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1607 xmlGenericError(xmlGenericErrorContext,
1608 "Failed to bind a port\n");
1609 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1610 return (-1);
1611 }
1612 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1613
1614 if (listen(ctxt->dataFd, 1) < 0) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001615#ifdef SUPPORT_IP6
1616 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1617 xmlGenericError (xmlGenericErrorContext,
1618 "Could not listen on port %d\n",
1619 ntohs (((struct sockaddr_in6 *)&dataAddr)->sin6_port));
1620 else
1621#endif
1622 xmlGenericError (xmlGenericErrorContext,
1623 "Could not listen on port %d\n",
1624 ntohs (((struct sockaddr_in *)&dataAddr)->sin_port));
Owen Taylor3473f882001-02-23 17:55:21 +00001625 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1626 return (-1);
1627 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001628#ifdef SUPPORT_IP6
1629 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1630 char buf6[INET6_ADDRSTRLEN];
1631 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1632 buf6, INET6_ADDRSTRLEN);
Daniel Veillard2db8c122003-07-08 12:16:59 +00001633 adp = (unsigned char *) buf6;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001634 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1635 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1636 } else
1637#endif
1638 {
1639 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1640 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1641 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1642 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1643 portp[0] & 0xff, portp[1] & 0xff);
1644 }
1645
Owen Taylor3473f882001-02-23 17:55:21 +00001646 buf[sizeof(buf) - 1] = 0;
1647 len = strlen(buf);
1648#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001649 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001650#endif
1651
1652 res = send(ctxt->controlFd, buf, len, 0);
1653 if (res < 0) {
1654 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1655 return(res);
1656 }
1657 res = xmlNanoFTPGetResponse(ctxt);
1658 if (res != 2) {
1659 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1660 return(-1);
1661 }
1662 }
1663 return(ctxt->dataFd);
1664
1665}
1666
1667/**
1668 * xmlNanoFTPCloseConnection:
1669 * @ctx: an FTP context
1670 *
1671 * Close the data connection from the server
1672 *
1673 * Returns -1 incase of error, 0 otherwise
1674 */
1675
1676int
1677xmlNanoFTPCloseConnection(void *ctx) {
1678 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1679 int res;
1680 fd_set rfd, efd;
1681 struct timeval tv;
1682
1683 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1684 tv.tv_sec = 15;
1685 tv.tv_usec = 0;
1686 FD_ZERO(&rfd);
1687 FD_SET(ctxt->controlFd, &rfd);
1688 FD_ZERO(&efd);
1689 FD_SET(ctxt->controlFd, &efd);
1690 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1691 if (res < 0) {
1692#ifdef DEBUG_FTP
1693 perror("select");
1694#endif
1695 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1696 return(-1);
1697 }
1698 if (res == 0) {
1699#ifdef DEBUG_FTP
1700 xmlGenericError(xmlGenericErrorContext,
1701 "xmlNanoFTPCloseConnection: timeout\n");
1702#endif
1703 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1704 } else {
1705 res = xmlNanoFTPGetResponse(ctxt);
1706 if (res != 2) {
1707 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1708 return(-1);
1709 }
1710 }
1711 return(0);
1712}
1713
1714/**
1715 * xmlNanoFTPParseList:
1716 * @list: some data listing received from the server
1717 * @callback: the user callback
1718 * @userData: the user callback data
1719 *
1720 * Parse at most one entry from the listing.
1721 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001722 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001723 */
1724
1725static int
1726xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1727 const char *cur = list;
1728 char filename[151];
1729 char attrib[11];
1730 char owner[11];
1731 char group[11];
1732 char month[4];
1733 int year = 0;
1734 int minute = 0;
1735 int hour = 0;
1736 int day = 0;
1737 unsigned long size = 0;
1738 int links = 0;
1739 int i;
1740
1741 if (!strncmp(cur, "total", 5)) {
1742 cur += 5;
1743 while (*cur == ' ') cur++;
1744 while ((*cur >= '0') && (*cur <= '9'))
1745 links = (links * 10) + (*cur++ - '0');
1746 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1747 cur++;
1748 return(cur - list);
1749 } else if (*list == '+') {
1750 return(0);
1751 } else {
1752 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1753 cur++;
1754 if (*cur == 0) return(0);
1755 i = 0;
1756 while (*cur != ' ') {
1757 if (i < 10)
1758 attrib[i++] = *cur;
1759 cur++;
1760 if (*cur == 0) return(0);
1761 }
1762 attrib[10] = 0;
1763 while (*cur == ' ') cur++;
1764 if (*cur == 0) return(0);
1765 while ((*cur >= '0') && (*cur <= '9'))
1766 links = (links * 10) + (*cur++ - '0');
1767 while (*cur == ' ') cur++;
1768 if (*cur == 0) return(0);
1769 i = 0;
1770 while (*cur != ' ') {
1771 if (i < 10)
1772 owner[i++] = *cur;
1773 cur++;
1774 if (*cur == 0) return(0);
1775 }
1776 owner[i] = 0;
1777 while (*cur == ' ') cur++;
1778 if (*cur == 0) return(0);
1779 i = 0;
1780 while (*cur != ' ') {
1781 if (i < 10)
1782 group[i++] = *cur;
1783 cur++;
1784 if (*cur == 0) return(0);
1785 }
1786 group[i] = 0;
1787 while (*cur == ' ') cur++;
1788 if (*cur == 0) return(0);
1789 while ((*cur >= '0') && (*cur <= '9'))
1790 size = (size * 10) + (*cur++ - '0');
1791 while (*cur == ' ') cur++;
1792 if (*cur == 0) return(0);
1793 i = 0;
1794 while (*cur != ' ') {
1795 if (i < 3)
1796 month[i++] = *cur;
1797 cur++;
1798 if (*cur == 0) return(0);
1799 }
1800 month[i] = 0;
1801 while (*cur == ' ') cur++;
1802 if (*cur == 0) return(0);
1803 while ((*cur >= '0') && (*cur <= '9'))
1804 day = (day * 10) + (*cur++ - '0');
1805 while (*cur == ' ') cur++;
1806 if (*cur == 0) return(0);
1807 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1808 if ((cur[1] == ':') || (cur[2] == ':')) {
1809 while ((*cur >= '0') && (*cur <= '9'))
1810 hour = (hour * 10) + (*cur++ - '0');
1811 if (*cur == ':') cur++;
1812 while ((*cur >= '0') && (*cur <= '9'))
1813 minute = (minute * 10) + (*cur++ - '0');
1814 } else {
1815 while ((*cur >= '0') && (*cur <= '9'))
1816 year = (year * 10) + (*cur++ - '0');
1817 }
1818 while (*cur == ' ') cur++;
1819 if (*cur == 0) return(0);
1820 i = 0;
1821 while ((*cur != '\n') && (*cur != '\r')) {
1822 if (i < 150)
1823 filename[i++] = *cur;
1824 cur++;
1825 if (*cur == 0) return(0);
1826 }
1827 filename[i] = 0;
1828 if ((*cur != '\n') && (*cur != '\r'))
1829 return(0);
1830 while ((*cur == '\n') || (*cur == '\r'))
1831 cur++;
1832 }
1833 if (callback != NULL) {
1834 callback(userData, filename, attrib, owner, group, size, links,
1835 year, month, day, hour, minute);
1836 }
1837 return(cur - list);
1838}
1839
1840/**
1841 * xmlNanoFTPList:
1842 * @ctx: an FTP context
1843 * @callback: the user callback
1844 * @userData: the user callback data
1845 * @filename: optional files to list
1846 *
1847 * Do a listing on the server. All files info are passed back
1848 * in the callbacks.
1849 *
1850 * Returns -1 incase of error, 0 otherwise
1851 */
1852
1853int
1854xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1855 char *filename) {
1856 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1857 char buf[4096 + 1];
1858 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001859 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001860 fd_set rfd, efd;
1861 struct timeval tv;
1862
1863 if (filename == NULL) {
1864 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1865 return(-1);
1866 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1867 if (ctxt->dataFd == -1)
1868 return(-1);
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001869 snprintf(buf, sizeof(buf), "LIST -L\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001870 } else {
1871 if (filename[0] != '/') {
1872 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1873 return(-1);
1874 }
1875 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1876 if (ctxt->dataFd == -1)
1877 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001878 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001879 }
1880 buf[sizeof(buf) - 1] = 0;
1881 len = strlen(buf);
1882#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001883 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001884#endif
1885 res = send(ctxt->controlFd, buf, len, 0);
1886 if (res < 0) {
1887 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1888 return(res);
1889 }
1890 res = xmlNanoFTPReadResponse(ctxt);
1891 if (res != 1) {
1892 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1893 return(-res);
1894 }
1895
1896 do {
1897 tv.tv_sec = 1;
1898 tv.tv_usec = 0;
1899 FD_ZERO(&rfd);
1900 FD_SET(ctxt->dataFd, &rfd);
1901 FD_ZERO(&efd);
1902 FD_SET(ctxt->dataFd, &efd);
1903 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1904 if (res < 0) {
1905#ifdef DEBUG_FTP
1906 perror("select");
1907#endif
1908 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1909 return(-1);
1910 }
1911 if (res == 0) {
1912 res = xmlNanoFTPCheckResponse(ctxt);
1913 if (res < 0) {
1914 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1915 ctxt->dataFd = -1;
1916 return(-1);
1917 }
1918 if (res == 2) {
1919 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1920 return(0);
1921 }
1922
1923 continue;
1924 }
1925
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001926 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00001927#ifdef DEBUG_FTP
1928 perror("recv");
1929#endif
1930 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1931 ctxt->dataFd = -1;
1932 return(-1);
1933 }
1934#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001935 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001936#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001937 indx += len;
1938 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001939 base = 0;
1940 do {
1941 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1942 base += res;
1943 } while (res > 0);
1944
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001945 memmove(&buf[0], &buf[base], indx - base);
1946 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00001947 } while (len != 0);
1948 xmlNanoFTPCloseConnection(ctxt);
1949 return(0);
1950}
1951
1952/**
1953 * xmlNanoFTPGetSocket:
1954 * @ctx: an FTP context
1955 * @filename: the file to retrieve (or NULL if path is in context).
1956 *
1957 * Initiate fetch of the given file from the server.
1958 *
1959 * Returns the socket for the data connection, or <0 in case of error
1960 */
1961
1962
1963int
1964xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1965 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1966 char buf[300];
1967 int res, len;
1968 if ((filename == NULL) && (ctxt->path == NULL))
1969 return(-1);
1970 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1971 if (ctxt->dataFd == -1)
1972 return(-1);
1973
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001974 snprintf(buf, sizeof(buf), "TYPE I\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001975 len = strlen(buf);
1976#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001977 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001978#endif
1979 res = send(ctxt->controlFd, buf, len, 0);
1980 if (res < 0) {
1981 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1982 return(res);
1983 }
1984 res = xmlNanoFTPReadResponse(ctxt);
1985 if (res != 2) {
1986 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1987 return(-res);
1988 }
1989 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001990 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001991 else
Owen Taylor3473f882001-02-23 17:55:21 +00001992 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001993 buf[sizeof(buf) - 1] = 0;
1994 len = strlen(buf);
1995#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001996 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001997#endif
1998 res = send(ctxt->controlFd, buf, len, 0);
1999 if (res < 0) {
2000 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2001 return(res);
2002 }
2003 res = xmlNanoFTPReadResponse(ctxt);
2004 if (res != 1) {
2005 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2006 return(-res);
2007 }
2008 return(ctxt->dataFd);
2009}
2010
2011/**
2012 * xmlNanoFTPGet:
2013 * @ctx: an FTP context
2014 * @callback: the user callback
2015 * @userData: the user callback data
2016 * @filename: the file to retrieve
2017 *
2018 * Fetch the given file from the server. All data are passed back
2019 * in the callbacks. The last callback has a size of 0 block.
2020 *
2021 * Returns -1 incase of error, 0 otherwise
2022 */
2023
2024int
2025xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
2026 const char *filename) {
2027 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2028 char buf[4096];
2029 int len = 0, res;
2030 fd_set rfd;
2031 struct timeval tv;
2032
2033 if ((filename == NULL) && (ctxt->path == NULL))
2034 return(-1);
2035 if (callback == NULL)
2036 return(-1);
2037 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
2038 return(-1);
2039
2040 do {
2041 tv.tv_sec = 1;
2042 tv.tv_usec = 0;
2043 FD_ZERO(&rfd);
2044 FD_SET(ctxt->dataFd, &rfd);
2045 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
2046 if (res < 0) {
2047#ifdef DEBUG_FTP
2048 perror("select");
2049#endif
2050 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2051 return(-1);
2052 }
2053 if (res == 0) {
2054 res = xmlNanoFTPCheckResponse(ctxt);
2055 if (res < 0) {
2056 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2057 ctxt->dataFd = -1;
2058 return(-1);
2059 }
2060 if (res == 2) {
2061 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2062 return(0);
2063 }
2064
2065 continue;
2066 }
2067 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
2068 callback(userData, buf, len);
2069 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2070 return(-1);
2071 }
2072 callback(userData, buf, len);
2073 } while (len != 0);
2074
2075 return(xmlNanoFTPCloseConnection(ctxt));
2076}
2077
2078/**
2079 * xmlNanoFTPRead:
2080 * @ctx: the FTP context
2081 * @dest: a buffer
2082 * @len: the buffer length
2083 *
2084 * This function tries to read @len bytes from the existing FTP connection
2085 * and saves them in @dest. This is a blocking call.
2086 *
2087 * Returns the number of byte read. 0 is an indication of an end of connection.
2088 * -1 indicates a parameter error.
2089 */
2090int
2091xmlNanoFTPRead(void *ctx, void *dest, int len) {
2092 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2093
2094 if (ctx == NULL) return(-1);
2095 if (ctxt->dataFd < 0) return(0);
2096 if (dest == NULL) return(-1);
2097 if (len <= 0) return(0);
2098
2099 len = recv(ctxt->dataFd, dest, len, 0);
2100#ifdef DEBUG_FTP
2101 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
2102#endif
2103 if (len <= 0) {
2104 xmlNanoFTPCloseConnection(ctxt);
2105 }
2106 return(len);
2107}
2108
2109/**
2110 * xmlNanoFTPOpen:
2111 * @URL: the URL to the resource
2112 *
2113 * Start to fetch the given ftp:// resource
2114 *
2115 * Returns an FTP context, or NULL
2116 */
2117
2118void*
2119xmlNanoFTPOpen(const char *URL) {
2120 xmlNanoFTPCtxtPtr ctxt;
2121 int sock;
2122
2123 xmlNanoFTPInit();
2124 if (URL == NULL) return(NULL);
2125 if (strncmp("ftp://", URL, 6)) return(NULL);
2126
2127 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2128 if (ctxt == NULL) return(NULL);
2129 if (xmlNanoFTPConnect(ctxt) < 0) {
2130 xmlNanoFTPFreeCtxt(ctxt);
2131 return(NULL);
2132 }
2133 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2134 if (sock < 0) {
2135 xmlNanoFTPFreeCtxt(ctxt);
2136 return(NULL);
2137 }
2138 return(ctxt);
2139}
2140
2141/**
2142 * xmlNanoFTPClose:
2143 * @ctx: an FTP context
2144 *
2145 * Close the connection and both control and transport
2146 *
2147 * Returns -1 incase of error, 0 otherwise
2148 */
2149
2150int
2151xmlNanoFTPClose(void *ctx) {
2152 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2153
2154 if (ctxt == NULL)
2155 return(-1);
2156
2157 if (ctxt->dataFd >= 0) {
2158 closesocket(ctxt->dataFd);
2159 ctxt->dataFd = -1;
2160 }
2161 if (ctxt->controlFd >= 0) {
2162 xmlNanoFTPQuit(ctxt);
2163 closesocket(ctxt->controlFd);
2164 ctxt->controlFd = -1;
2165 }
2166 xmlNanoFTPFreeCtxt(ctxt);
2167 return(0);
2168}
2169
2170#ifdef STANDALONE
2171/************************************************************************
2172 * *
2173 * Basic test in Standalone mode *
2174 * *
2175 ************************************************************************/
Daniel Veillard01c13b52002-12-10 15:19:08 +00002176static
Owen Taylor3473f882001-02-23 17:55:21 +00002177void ftpList(void *userData, const char *filename, const char* attrib,
2178 const char *owner, const char *group, unsigned long size, int links,
2179 int year, const char *month, int day, int hour, int minute) {
2180 xmlGenericError(xmlGenericErrorContext,
2181 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2182}
Daniel Veillard01c13b52002-12-10 15:19:08 +00002183static
Owen Taylor3473f882001-02-23 17:55:21 +00002184void ftpData(void *userData, const char *data, int len) {
2185 if (userData == NULL) return;
2186 if (len <= 0) {
2187 fclose(userData);
2188 return;
2189 }
2190 fwrite(data, len, 1, userData);
2191}
2192
2193int main(int argc, char **argv) {
2194 void *ctxt;
2195 FILE *output;
2196 char *tstfile = NULL;
2197
2198 xmlNanoFTPInit();
2199 if (argc > 1) {
2200 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2201 if (xmlNanoFTPConnect(ctxt) < 0) {
2202 xmlGenericError(xmlGenericErrorContext,
2203 "Couldn't connect to %s\n", argv[1]);
2204 exit(1);
2205 }
2206 if (argc > 2)
2207 tstfile = argv[2];
2208 } else
2209 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2210 if (ctxt == NULL) {
2211 xmlGenericError(xmlGenericErrorContext,
2212 "Couldn't connect to localhost\n");
2213 exit(1);
2214 }
2215 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2216 output = fopen("/tmp/tstdata", "w");
2217 if (output != NULL) {
2218 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2219 xmlGenericError(xmlGenericErrorContext,
2220 "Failed to get file\n");
2221
2222 }
2223 xmlNanoFTPClose(ctxt);
2224 xmlMemoryDump();
2225 exit(0);
2226}
2227#endif /* STANDALONE */
2228#else /* !LIBXML_FTP_ENABLED */
2229#ifdef STANDALONE
2230#include <stdio.h>
2231int main(int argc, char **argv) {
2232 xmlGenericError(xmlGenericErrorContext,
2233 "%s : FTP support not compiled in\n", argv[0]);
2234 return(0);
2235}
2236#endif /* STANDALONE */
2237#endif /* LIBXML_FTP_ENABLED */