blob: 58709363819374fda28a3055b57a445625590725 [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
William M. Brack030a7a12004-02-10 12:48:57 +0000114#define XML_NANO_MAX_URLBUF 4096
115
Owen Taylor3473f882001-02-23 17:55:21 +0000116typedef struct xmlNanoFTPCtxt {
117 char *protocol; /* the protocol name */
118 char *hostname; /* the host name */
119 int port; /* the port */
120 char *path; /* the path within the URL */
121 char *user; /* user string */
122 char *passwd; /* passwd string */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000123#ifdef SUPPORT_IP6
124 struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
125#else
Owen Taylor3473f882001-02-23 17:55:21 +0000126 struct sockaddr_in ftpAddr; /* the socket address struct */
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000127#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000128 int passive; /* currently we support only passive !!! */
129 SOCKET controlFd; /* the file descriptor for the control socket */
130 SOCKET dataFd; /* the file descriptor for the data socket */
131 int state; /* WRITE / READ / CLOSED */
132 int returnValue; /* the protocol return value */
133 /* buffer for data received from the control connection */
134 char controlBuf[FTP_BUF_SIZE + 1];
135 int controlBufIndex;
136 int controlBufUsed;
137 int controlBufAnswer;
138} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
139
140static int initialized = 0;
141static char *proxy = NULL; /* the proxy name if any */
142static int proxyPort = 0; /* the proxy port if any */
143static char *proxyUser = NULL; /* user for proxy authentication */
144static char *proxyPasswd = NULL;/* passwd for proxy authentication */
145static int proxyType = 0; /* uses TYPE or a@b ? */
146
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000147#ifdef SUPPORT_IP6
Daniel Veillard2db8c122003-07-08 12:16:59 +0000148static
149int have_ipv6(void) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000150 int s;
151
152 s = socket (AF_INET6, SOCK_STREAM, 0);
153 if (s != -1) {
154 close (s);
155 return (1);
156 }
157 return (0);
158}
159#endif
160
Owen Taylor3473f882001-02-23 17:55:21 +0000161/**
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000162 * xmlFTPErrMemory:
163 * @extra: extra informations
164 *
165 * Handle an out of memory condition
166 */
167static void
168xmlFTPErrMemory(const char *extra)
169{
170 __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
171}
172
173/**
Owen Taylor3473f882001-02-23 17:55:21 +0000174 * xmlNanoFTPInit:
175 *
176 * Initialize the FTP protocol layer.
177 * Currently it just checks for proxy informations,
178 * and get the hostname
179 */
180
181void
182xmlNanoFTPInit(void) {
183 const char *env;
184#ifdef _WINSOCKAPI_
185 WSADATA wsaData;
186#endif
187
188 if (initialized)
189 return;
190
191#ifdef _WINSOCKAPI_
192 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
193 return;
194#endif
195
Owen Taylor3473f882001-02-23 17:55:21 +0000196 proxyPort = 21;
197 env = getenv("no_proxy");
Daniel Veillard29b17482004-08-16 00:39:03 +0000198 if (env && ((env[0] == '*' ) && (env[1] == 0)))
Owen Taylor3473f882001-02-23 17:55:21 +0000199 return;
200 env = getenv("ftp_proxy");
201 if (env != NULL) {
202 xmlNanoFTPScanProxy(env);
203 } else {
204 env = getenv("FTP_PROXY");
205 if (env != NULL) {
206 xmlNanoFTPScanProxy(env);
207 }
208 }
209 env = getenv("ftp_proxy_user");
210 if (env != NULL) {
211 proxyUser = xmlMemStrdup(env);
212 }
213 env = getenv("ftp_proxy_password");
214 if (env != NULL) {
215 proxyPasswd = xmlMemStrdup(env);
216 }
217 initialized = 1;
218}
219
220/**
Daniel Veillarde356c282001-03-10 12:32:04 +0000221 * xmlNanoFTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000222 *
223 * Cleanup the FTP protocol layer. This cleanup proxy informations.
224 */
225
226void
227xmlNanoFTPCleanup(void) {
228 if (proxy != NULL) {
229 xmlFree(proxy);
230 proxy = NULL;
231 }
232 if (proxyUser != NULL) {
233 xmlFree(proxyUser);
234 proxyUser = NULL;
235 }
236 if (proxyPasswd != NULL) {
237 xmlFree(proxyPasswd);
238 proxyPasswd = NULL;
239 }
Owen Taylor3473f882001-02-23 17:55:21 +0000240#ifdef _WINSOCKAPI_
241 if (initialized)
242 WSACleanup();
243#endif
244 initialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000245}
246
247/**
248 * xmlNanoFTPProxy:
249 * @host: the proxy host name
250 * @port: the proxy port
251 * @user: the proxy user name
252 * @passwd: the proxy password
253 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
254 *
255 * Setup the FTP proxy informations.
256 * This can also be done by using ftp_proxy ftp_proxy_user and
257 * ftp_proxy_password environment variables.
258 */
259
260void
261xmlNanoFTPProxy(const char *host, int port, const char *user,
262 const char *passwd, int type) {
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000263 if (proxy != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000264 xmlFree(proxy);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000265 proxy = NULL;
266 }
267 if (proxyUser != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000268 xmlFree(proxyUser);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000269 proxyUser = NULL;
270 }
271 if (proxyPasswd != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000272 xmlFree(proxyPasswd);
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000273 proxyPasswd = NULL;
274 }
Owen Taylor3473f882001-02-23 17:55:21 +0000275 if (host)
276 proxy = xmlMemStrdup(host);
277 if (user)
278 proxyUser = xmlMemStrdup(user);
279 if (passwd)
280 proxyPasswd = xmlMemStrdup(passwd);
281 proxyPort = port;
282 proxyType = type;
283}
284
285/**
286 * xmlNanoFTPScanURL:
287 * @ctx: an FTP context
288 * @URL: The URL used to initialize the context
289 *
290 * (Re)Initialize an FTP context by parsing the URL and finding
291 * the protocol host port and path it indicates.
292 */
293
294static void
295xmlNanoFTPScanURL(void *ctx, const char *URL) {
296 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
297 const char *cur = URL;
William M. Brack030a7a12004-02-10 12:48:57 +0000298 char buf[XML_NANO_MAX_URLBUF];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000299 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000300 int port = 0;
301
302 if (ctxt->protocol != NULL) {
303 xmlFree(ctxt->protocol);
304 ctxt->protocol = NULL;
305 }
306 if (ctxt->hostname != NULL) {
307 xmlFree(ctxt->hostname);
308 ctxt->hostname = NULL;
309 }
310 if (ctxt->path != NULL) {
311 xmlFree(ctxt->path);
312 ctxt->path = NULL;
313 }
314 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000315 buf[indx] = 0;
William M. Brack030a7a12004-02-10 12:48:57 +0000316 while ((*cur != 0) && (indx < XML_NANO_MAX_URLBUF - 1)) {
Owen Taylor3473f882001-02-23 17:55:21 +0000317 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000318 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000319 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000320 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000321 cur += 3;
322 break;
323 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000324 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000325 }
326 if (*cur == 0) return;
327
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000328 buf[indx] = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000329 /* allow user@ and user:pass@ forms */
330 {
331 const char *p = strchr(cur, '@');
332 if(p) {
William M. Brack030a7a12004-02-10 12:48:57 +0000333 while(indx < XML_NANO_MAX_URLBUF-1) {
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000334 if(cur[0] == ':' || cur[0] == '@') break;
335 buf[indx++] = *cur++;
336 }
337 buf[indx] = 0;
338 ctxt->user = xmlMemStrdup(buf);
339 indx = 0;
340 if(cur[0] == ':') {
341 cur++;
William M. Brack030a7a12004-02-10 12:48:57 +0000342 while(indx < XML_NANO_MAX_URLBUF-1) {
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000343 if(cur[0] == '@') break;
344 buf[indx++] = *cur++;
345 }
346 buf[indx] = 0;
347 ctxt->passwd = xmlMemStrdup(buf);
348 indx = 0;
349 }
350 cur = p+1;
351 }
352 }
353
William M. Brack030a7a12004-02-10 12:48:57 +0000354 while (indx < XML_NANO_MAX_URLBUF - 1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000355 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
356 (!strchr (cur, '[') && strchr (cur, ']'))) {
357 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
358 "Syntax Error\n");
359 return;
360 }
361
362 if (cur[0] == '[') {
363 cur++;
Daniel Veillard95ddcd32004-10-26 21:53:55 +0000364 while ((cur[0] != ']') && (indx < XML_NANO_MAX_URLBUF-1))
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000365 buf[indx++] = *cur++;
Daniel Veillard95ddcd32004-10-26 21:53:55 +0000366 if (indx >= XML_NANO_MAX_URLBUF-1) {
367 xmlGenericError(xmlGenericErrorContext,
368 "\nxmlNanoFTPScanURL: %s", "Syntax Error\n");
369 return;
370 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000371
372 if (!strchr (buf, ':')) {
373 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanURL: %s",
374 "Use [IPv6]/IPv4 format\n");
375 return;
376 }
377
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000378 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000379 ctxt->hostname = xmlMemStrdup (buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000380 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000381 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000382 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000383 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000384 while (*cur >= '0' && *cur <= '9') {
385 port *= 10;
386 port += *cur - '0';
387 cur++;
388 }
389
390 if (port != 0) ctxt->port = port;
391 while ((cur[0] != '/') && (*cur != 0))
392 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000393 }
Owen Taylor3473f882001-02-23 17:55:21 +0000394 break;
395 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000396 else { /* address is an IPv4 one*/
397 if (cur[0] == ':') {
398 buf[indx] = 0;
399 ctxt->hostname = xmlMemStrdup (buf);
400 indx = 0;
401 cur += 1;
402 while ((*cur >= '0') && (*cur <= '9')) {
403 port *= 10;
404 port += *cur - '0';
405 cur++;
406 }
407 if (port != 0) ctxt->port = port;
408 while ((cur[0] != '/') && (*cur != 0))
409 cur++;
410 break;
411 }
412 if ((*cur == '/') || (*cur == 0)) {
413 buf[indx] = 0;
414 ctxt->hostname = xmlMemStrdup (buf);
415 indx = 0;
416 break;
417 }
Owen Taylor3473f882001-02-23 17:55:21 +0000418 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000419 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000420 }
421 if (*cur == 0)
422 ctxt->path = xmlMemStrdup("/");
423 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000424 indx = 0;
425 buf[indx] = 0;
William M. Brack030a7a12004-02-10 12:48:57 +0000426 while ((*cur != 0) && (indx < XML_NANO_MAX_URLBUF-1))
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000427 buf[indx++] = *cur++;
428 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000429 ctxt->path = xmlMemStrdup(buf);
430 }
431}
432
433/**
434 * xmlNanoFTPUpdateURL:
435 * @ctx: an FTP context
436 * @URL: The URL used to update the context
437 *
438 * Update an FTP context by parsing the URL and finding
439 * new path it indicates. If there is an error in the
440 * protocol, hostname, port or other information, the
441 * error is raised. It indicates a new connection has to
442 * be established.
443 *
444 * Returns 0 if Ok, -1 in case of error (other host).
445 */
446
447int
448xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
449 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
450 const char *cur = URL;
William M. Brack030a7a12004-02-10 12:48:57 +0000451 char buf[XML_NANO_MAX_URLBUF];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000452 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000453 int port = 0;
454
455 if (URL == NULL)
456 return(-1);
457 if (ctxt == NULL)
458 return(-1);
459 if (ctxt->protocol == NULL)
460 return(-1);
461 if (ctxt->hostname == NULL)
462 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000463 buf[indx] = 0;
William M. Brack030a7a12004-02-10 12:48:57 +0000464 while ((*cur != 0) && (indx < XML_NANO_MAX_URLBUF-1)) {
Owen Taylor3473f882001-02-23 17:55:21 +0000465 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000466 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000467 if (strcmp(ctxt->protocol, buf))
468 return(-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000469 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000470 cur += 3;
471 break;
472 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000473 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000474 }
475 if (*cur == 0)
476 return(-1);
477
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000478 buf[indx] = 0;
William M. Brack030a7a12004-02-10 12:48:57 +0000479 while (indx < XML_NANO_MAX_URLBUF-1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000480 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
481 (!strchr (cur, '[') && strchr (cur, ']'))) {
482 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
483 "Syntax Error\n");
484 return (-1);
485 }
486
487 if (cur[0] == '[') {
488 cur++;
William M. Brack030a7a12004-02-10 12:48:57 +0000489 while ((cur[0] != ']') && (indx < XML_NANO_MAX_URLBUF-1))
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000490 buf[indx++] = *cur++;
491
492 if (!strchr (buf, ':')) {
493 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPUpdateURL: %s",
494 "Use [IPv6]/IPv4 format\n");
495 return (-1);
496 }
497
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000498 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000499 if (strcmp (ctxt->hostname, buf))
500 return (-1);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000501 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000502 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000503 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000504 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000505 while (*cur >= '0' && *cur <= '9') {
506 port *= 10;
507 port += *cur - '0';
508 cur++;
509 }
510
511 if (port != ctxt->port)
512 return (-1);
513 while ((cur[0] != '/') && (*cur != 0))
514 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000515 }
Owen Taylor3473f882001-02-23 17:55:21 +0000516 break;
517 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000518 else {
519 if (cur[0] == ':') {
520 buf[indx] = 0;
521 if (strcmp (ctxt->hostname, buf))
522 return (-1);
523 indx = 0;
524 cur += 1;
525 while ((*cur >= '0') && (*cur <= '9')) {
526 port *= 10;
527 port += *cur - '0';
528 cur++;
529 }
530 if (port != ctxt->port)
531 return (-1);
532 while ((cur[0] != '/') && (*cur != 0))
533 cur++;
534 break;
535 }
536 if ((*cur == '/') || (*cur == 0)) {
537 buf[indx] = 0;
538 if (strcmp (ctxt->hostname, buf))
539 return (-1);
540 indx = 0;
541 break;
542 }
Owen Taylor3473f882001-02-23 17:55:21 +0000543 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000544 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000545 }
546 if (ctxt->path != NULL) {
547 xmlFree(ctxt->path);
548 ctxt->path = NULL;
549 }
550
551 if (*cur == 0)
552 ctxt->path = xmlMemStrdup("/");
553 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000554 indx = 0;
555 buf[indx] = 0;
William M. Brack030a7a12004-02-10 12:48:57 +0000556 while ((*cur != 0) && (indx < XML_NANO_MAX_URLBUF-1))
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000557 buf[indx++] = *cur++;
558 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000559 ctxt->path = xmlMemStrdup(buf);
560 }
561 return(0);
562}
563
564/**
565 * xmlNanoFTPScanProxy:
566 * @URL: The proxy URL used to initialize the proxy context
567 *
568 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
569 * the protocol host port it indicates.
570 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
571 * A NULL URL cleans up proxy informations.
572 */
573
574void
575xmlNanoFTPScanProxy(const char *URL) {
576 const char *cur = URL;
William M. Brack030a7a12004-02-10 12:48:57 +0000577 char buf[XML_NANO_MAX_URLBUF];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000578 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000579 int port = 0;
580
581 if (proxy != NULL) {
582 xmlFree(proxy);
583 proxy = NULL;
584 }
585 if (proxyPort != 0) {
586 proxyPort = 0;
587 }
588#ifdef DEBUG_FTP
589 if (URL == NULL)
590 xmlGenericError(xmlGenericErrorContext, "Removing FTP proxy info\n");
591 else
592 xmlGenericError(xmlGenericErrorContext, "Using FTP proxy %s\n", URL);
593#endif
594 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000595 buf[indx] = 0;
William M. Brack030a7a12004-02-10 12:48:57 +0000596 while ((*cur != 0) && (indx < XML_NANO_MAX_URLBUF-1)) {
Owen Taylor3473f882001-02-23 17:55:21 +0000597 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000598 buf[indx] = 0;
599 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000600 cur += 3;
601 break;
602 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000603 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000604 }
605 if (*cur == 0) return;
606
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000607 buf[indx] = 0;
William M. Brack030a7a12004-02-10 12:48:57 +0000608 while (indx < XML_NANO_MAX_URLBUF-1) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000609 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
610 (!strchr (cur, '[') && strchr (cur, ']'))) {
611 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
612 "Syntax error\n");
613 return;
614 }
615
616 if (cur[0] == '[') {
617 cur++;
Daniel Veillard95ddcd32004-10-26 21:53:55 +0000618 while ((cur[0] != ']') && (indx < XML_NANO_MAX_URLBUF-1))
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000619 buf[indx++] = *cur++;
Daniel Veillard95ddcd32004-10-26 21:53:55 +0000620 if (indx >= XML_NANO_MAX_URLBUF-1) {
621 xmlGenericError (xmlGenericErrorContext,
622 "\nxmlNanoFTPScanProxy: %s", "Syntax error\n");
623 return;
624 }
625
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000626 if (!strchr (buf, ':')) {
627 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoFTPScanProxy: %s",
628 "Use [IPv6]/IPv4 format\n");
629 return;
630 }
631
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000632 buf[indx] = 0;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000633 proxy = xmlMemStrdup (buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000634 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000635 cur += 1;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000636 if (cur[0] == ':') {
Owen Taylor3473f882001-02-23 17:55:21 +0000637 cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000638 while (*cur >= '0' && *cur <= '9') {
639 port *= 10;
640 port += *cur - '0';
641 cur++;
642 }
643
644 if (port != 0) proxyPort = port;
645 while ((cur[0] != '/') && (*cur != 0))
646 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000647 }
Owen Taylor3473f882001-02-23 17:55:21 +0000648 break;
649 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +0000650 else {
651 if (cur[0] == ':') {
652 buf[indx] = 0;
653 proxy = xmlMemStrdup (buf);
654 indx = 0;
655 cur += 1;
656 while ((*cur >= '0') && (*cur <= '9')) {
657 port *= 10;
658 port += *cur - '0';
659 cur++;
660 }
661 if (port != 0) proxyPort = port;
662 while ((cur[0] != '/') && (*cur != 0))
663 cur++;
664 break;
665 }
666 if ((*cur == '/') || (*cur == 0)) {
667 buf[indx] = 0;
668 proxy = xmlMemStrdup (buf);
669 indx = 0;
670 break;
671 }
Owen Taylor3473f882001-02-23 17:55:21 +0000672 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000673 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000674 }
675}
676
677/**
678 * xmlNanoFTPNewCtxt:
679 * @URL: The URL used to initialize the context
680 *
681 * Allocate and initialize a new FTP context.
682 *
683 * Returns an FTP context or NULL in case of error.
684 */
685
686void*
687xmlNanoFTPNewCtxt(const char *URL) {
688 xmlNanoFTPCtxtPtr ret;
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000689 char *unescaped;
Owen Taylor3473f882001-02-23 17:55:21 +0000690
691 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000692 if (ret == NULL) {
693 xmlFTPErrMemory("allocating FTP context");
694 return(NULL);
695 }
Owen Taylor3473f882001-02-23 17:55:21 +0000696
697 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
698 ret->port = 21;
699 ret->passive = 1;
700 ret->returnValue = 0;
701 ret->controlBufIndex = 0;
702 ret->controlBufUsed = 0;
Daniel Veillardc69e0b12001-11-20 08:35:07 +0000703 ret->controlFd = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000704
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000705 unescaped = xmlURIUnescapeString(URL, 0, NULL);
Daniel Veillardb1b3a3e2004-11-03 23:25:47 +0000706 if (unescaped != NULL) {
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000707 xmlNanoFTPScanURL(ret, unescaped);
Daniel Veillardb1b3a3e2004-11-03 23:25:47 +0000708 xmlFree(unescaped);
709 } else if (URL != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +0000710 xmlNanoFTPScanURL(ret, URL);
711
712 return(ret);
713}
714
715/**
716 * xmlNanoFTPFreeCtxt:
717 * @ctx: an FTP context
718 *
719 * Frees the context after closing the connection.
720 */
721
722void
723xmlNanoFTPFreeCtxt(void * ctx) {
724 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
725 if (ctxt == NULL) return;
726 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
727 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
728 if (ctxt->path != NULL) xmlFree(ctxt->path);
729 ctxt->passive = 1;
730 if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
731 ctxt->controlFd = -1;
732 ctxt->controlBufIndex = -1;
733 ctxt->controlBufUsed = -1;
734 xmlFree(ctxt);
735}
736
737/**
738 * xmlNanoFTPParseResponse:
Owen Taylor3473f882001-02-23 17:55:21 +0000739 * @buf: the buffer containing the response
740 * @len: the buffer length
741 *
742 * Parsing of the server answer, we just extract the code.
743 *
744 * returns 0 for errors
745 * +XXX for last line of response
746 * -XXX for response to be continued
747 */
748static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000749xmlNanoFTPParseResponse(char *buf, int len) {
Owen Taylor3473f882001-02-23 17:55:21 +0000750 int val = 0;
751
752 if (len < 3) return(-1);
753 if ((*buf >= '0') && (*buf <= '9'))
754 val = val * 10 + (*buf - '0');
755 else
756 return(0);
757 buf++;
758 if ((*buf >= '0') && (*buf <= '9'))
759 val = val * 10 + (*buf - '0');
760 else
761 return(0);
762 buf++;
763 if ((*buf >= '0') && (*buf <= '9'))
764 val = val * 10 + (*buf - '0');
765 else
766 return(0);
767 buf++;
768 if (*buf == '-')
769 return(-val);
770 return(val);
771}
772
773/**
774 * xmlNanoFTPGetMore:
775 * @ctx: an FTP context
776 *
777 * Read more information from the FTP control connection
778 * Returns the number of bytes read, < 0 indicates an error
779 */
780static int
781xmlNanoFTPGetMore(void *ctx) {
782 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
783 int len;
784 int size;
785
786 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
787#ifdef DEBUG_FTP
788 xmlGenericError(xmlGenericErrorContext,
789 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
790 ctxt->controlBufIndex);
791#endif
792 return(-1);
793 }
794
795 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
796#ifdef DEBUG_FTP
797 xmlGenericError(xmlGenericErrorContext,
798 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
799 ctxt->controlBufUsed);
800#endif
801 return(-1);
802 }
803 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
804#ifdef DEBUG_FTP
805 xmlGenericError(xmlGenericErrorContext,
806 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
807 ctxt->controlBufIndex, ctxt->controlBufUsed);
808#endif
809 return(-1);
810 }
811
812 /*
813 * First pack the control buffer
814 */
815 if (ctxt->controlBufIndex > 0) {
816 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
817 ctxt->controlBufUsed - ctxt->controlBufIndex);
818 ctxt->controlBufUsed -= ctxt->controlBufIndex;
819 ctxt->controlBufIndex = 0;
820 }
821 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
822 if (size == 0) {
823#ifdef DEBUG_FTP
824 xmlGenericError(xmlGenericErrorContext,
825 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
826#endif
827 return(0);
828 }
829
830 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000831 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000832 */
833 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
834 size, 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000835 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000836 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
837 ctxt->controlFd = -1;
838 return(-1);
839 }
840#ifdef DEBUG_FTP
841 xmlGenericError(xmlGenericErrorContext,
842 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
843 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
844#endif
845 ctxt->controlBufUsed += len;
846 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
847
848 return(len);
849}
850
851/**
852 * xmlNanoFTPReadResponse:
853 * @ctx: an FTP context
854 *
855 * Read the response from the FTP server after a command.
856 * Returns the code number
857 */
858static int
859xmlNanoFTPReadResponse(void *ctx) {
860 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
861 char *ptr, *end;
862 int len;
863 int res = -1, cur = -1;
864
865get_more:
866 /*
867 * Assumes everything up to controlBuf[controlBufIndex] has been read
868 * and analyzed.
869 */
870 len = xmlNanoFTPGetMore(ctx);
871 if (len < 0) {
872 return(-1);
873 }
874 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
875 return(-1);
876 }
877 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
878 end = &ctxt->controlBuf[ctxt->controlBufUsed];
879
880#ifdef DEBUG_FTP
881 xmlGenericError(xmlGenericErrorContext,
882 "\n<<<\n%s\n--\n", ptr);
883#endif
884 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000885 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000886 if (cur > 0) {
887 /*
888 * Successfully scanned the control code, scratch
889 * till the end of the line, but keep the index to be
890 * able to analyze the result if needed.
891 */
892 res = cur;
893 ptr += 3;
894 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
895 while ((ptr < end) && (*ptr != '\n')) ptr++;
896 if (*ptr == '\n') ptr++;
897 if (*ptr == '\r') ptr++;
898 break;
899 }
900 while ((ptr < end) && (*ptr != '\n')) ptr++;
901 if (ptr >= end) {
902 ctxt->controlBufIndex = ctxt->controlBufUsed;
903 goto get_more;
904 }
905 if (*ptr != '\r') ptr++;
906 }
907
908 if (res < 0) goto get_more;
909 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
910#ifdef DEBUG_FTP
911 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
912 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
913#endif
914
915#ifdef DEBUG_FTP
916 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
917#endif
918 return(res / 100);
919}
920
921/**
922 * xmlNanoFTPGetResponse:
923 * @ctx: an FTP context
924 *
925 * Get the response from the FTP server after a command.
926 * Returns the code number
927 */
928
929int
930xmlNanoFTPGetResponse(void *ctx) {
931 int res;
932
933 res = xmlNanoFTPReadResponse(ctx);
934
935 return(res);
936}
937
938/**
939 * xmlNanoFTPCheckResponse:
940 * @ctx: an FTP context
941 *
942 * Check if there is a response from the FTP server after a command.
943 * Returns the code number, or 0
944 */
945
946int
947xmlNanoFTPCheckResponse(void *ctx) {
948 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
949 fd_set rfd;
950 struct timeval tv;
951
952 tv.tv_sec = 0;
953 tv.tv_usec = 0;
954 FD_ZERO(&rfd);
955 FD_SET(ctxt->controlFd, &rfd);
956 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
957 case 0:
958 return(0);
959 case -1:
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000960 __xmlIOErr(XML_FROM_FTP, 0, "select");
Owen Taylor3473f882001-02-23 17:55:21 +0000961 return(-1);
962
963 }
964
965 return(xmlNanoFTPReadResponse(ctx));
966}
967
968/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000969 * Send the user authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000970 */
971
972static int
973xmlNanoFTPSendUser(void *ctx) {
974 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
975 char buf[200];
976 int len;
977 int res;
978
979 if (ctxt->user == NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000980 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000981 else
Owen Taylor3473f882001-02-23 17:55:21 +0000982 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000983 buf[sizeof(buf) - 1] = 0;
984 len = strlen(buf);
985#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000986 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000987#endif
988 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000989 if (res < 0) {
990 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
991 return(res);
992 }
Owen Taylor3473f882001-02-23 17:55:21 +0000993 return(0);
994}
995
996/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000997 * Send the password authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000998 */
999
1000static int
1001xmlNanoFTPSendPasswd(void *ctx) {
1002 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1003 char buf[200];
1004 int len;
1005 int res;
1006
1007 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001008 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001009 else
Owen Taylor3473f882001-02-23 17:55:21 +00001010 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001011 buf[sizeof(buf) - 1] = 0;
1012 len = strlen(buf);
1013#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001014 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001015#endif
1016 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001017 if (res < 0) {
1018 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1019 return(res);
1020 }
Owen Taylor3473f882001-02-23 17:55:21 +00001021 return(0);
1022}
1023
1024/**
1025 * xmlNanoFTPQuit:
1026 * @ctx: an FTP context
1027 *
1028 * Send a QUIT command to the server
1029 *
1030 * Returns -1 in case of error, 0 otherwise
1031 */
1032
1033
1034int
1035xmlNanoFTPQuit(void *ctx) {
1036 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1037 char buf[200];
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001038 int len, res;
Owen Taylor3473f882001-02-23 17:55:21 +00001039
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001040 snprintf(buf, sizeof(buf), "QUIT\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001041 len = strlen(buf);
1042#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001043 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 +00001044#endif
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001045 res = send(ctxt->controlFd, buf, len, 0);
1046 if (res < 0) {
1047 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1048 return(res);
1049 }
Owen Taylor3473f882001-02-23 17:55:21 +00001050 return(0);
1051}
1052
1053/**
1054 * xmlNanoFTPConnect:
1055 * @ctx: an FTP context
1056 *
1057 * Tries to open a control connection
1058 *
1059 * Returns -1 in case of error, 0 otherwise
1060 */
1061
1062int
1063xmlNanoFTPConnect(void *ctx) {
1064 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1065 struct hostent *hp;
1066 int port;
1067 int res;
Daniel Veillard2db8c122003-07-08 12:16:59 +00001068 int addrlen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001069
1070 if (ctxt == NULL)
1071 return(-1);
1072 if (ctxt->hostname == NULL)
1073 return(-1);
1074
1075 /*
1076 * do the blocking DNS query.
1077 */
Owen Taylor3473f882001-02-23 17:55:21 +00001078 if (proxy) {
1079 port = proxyPort;
1080 } else {
1081 port = ctxt->port;
1082 }
1083 if (port == 0)
1084 port = 21;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001085
1086 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
1087
1088#ifdef SUPPORT_IP6
1089 if (have_ipv6 ()) {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001090 struct addrinfo hints, *tmp, *result;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001091
1092 result = NULL;
1093 memset (&hints, 0, sizeof(hints));
1094 hints.ai_socktype = SOCK_STREAM;
1095
1096 if (proxy) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001097 if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
1098 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001099 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001100 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001101 }
1102 else
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001103 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
1104 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001105 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001106 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001107
Daniel Veillard2db8c122003-07-08 12:16:59 +00001108 for (tmp = result; tmp; tmp = tmp->ai_next)
1109 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001110 break;
1111
Daniel Veillard3dc93a42003-07-10 14:04:33 +00001112 if (!tmp) {
1113 if (result)
1114 freeaddrinfo (result);
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001115 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillard3dc93a42003-07-10 14:04:33 +00001116 return (-1);
1117 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001118 if (tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
1119 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
1120 return (-1);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001121 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001122 if (tmp->ai_family == AF_INET6) {
1123 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
1124 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
1125 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
1126 }
1127 else {
1128 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
1129 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
1130 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1131 }
1132 addrlen = tmp->ai_addrlen;
1133 freeaddrinfo (result);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001134 }
1135 else
1136#endif
1137 {
1138 if (proxy)
1139 hp = gethostbyname (proxy);
1140 else
1141 hp = gethostbyname (ctxt->hostname);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001142 if (hp == NULL) {
1143 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001144 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001145 }
Daniel Veillard6927b102004-10-27 17:29:04 +00001146 if ((unsigned int) hp->h_length >
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001147 sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
1148 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
1149 return (-1);
1150 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001151
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001152 /*
1153 * Prepare the socket
1154 */
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001155 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
1156 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
1157 hp->h_addr_list[0], hp->h_length);
1158 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = htons (port);
1159 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1160 addrlen = sizeof (struct sockaddr_in);
1161 }
1162
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001163 if (ctxt->controlFd < 0) {
1164 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001165 return(-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001166 }
Owen Taylor3473f882001-02-23 17:55:21 +00001167
1168 /*
1169 * Do the connect.
1170 */
1171 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001172 addrlen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001173 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
Owen Taylor3473f882001-02-23 17:55:21 +00001174 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1175 ctxt->controlFd = -1;
1176 return(-1);
1177 }
1178
1179 /*
1180 * Wait for the HELLO from the server.
1181 */
1182 res = xmlNanoFTPGetResponse(ctxt);
1183 if (res != 2) {
1184 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1185 ctxt->controlFd = -1;
1186 return(-1);
1187 }
1188
1189 /*
1190 * State diagram for the login operation on the FTP server
1191 *
1192 * Reference: RFC 959
1193 *
1194 * 1
1195 * +---+ USER +---+------------->+---+
1196 * | B |---------->| W | 2 ---->| E |
1197 * +---+ +---+------ | -->+---+
1198 * | | | | |
1199 * 3 | | 4,5 | | |
1200 * -------------- ----- | | |
1201 * | | | | |
1202 * | | | | |
1203 * | --------- |
1204 * | 1| | | |
1205 * V | | | |
1206 * +---+ PASS +---+ 2 | ------>+---+
1207 * | |---------->| W |------------->| S |
1208 * +---+ +---+ ---------->+---+
1209 * | | | | |
1210 * 3 | |4,5| | |
1211 * -------------- -------- |
1212 * | | | | |
1213 * | | | | |
1214 * | -----------
1215 * | 1,3| | | |
1216 * V | 2| | |
1217 * +---+ ACCT +---+-- | ----->+---+
1218 * | |---------->| W | 4,5 -------->| F |
1219 * +---+ +---+------------->+---+
1220 *
1221 * Of course in case of using a proxy this get really nasty and is not
1222 * standardized at all :-(
1223 */
1224 if (proxy) {
1225 int len;
1226 char buf[400];
1227
1228 if (proxyUser != NULL) {
1229 /*
1230 * We need proxy auth
1231 */
Owen Taylor3473f882001-02-23 17:55:21 +00001232 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +00001233 buf[sizeof(buf) - 1] = 0;
1234 len = strlen(buf);
1235#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001236 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001237#endif
1238 res = send(ctxt->controlFd, buf, len, 0);
1239 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001240 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001241 closesocket(ctxt->controlFd);
1242 ctxt->controlFd = -1;
1243 return(res);
1244 }
1245 res = xmlNanoFTPGetResponse(ctxt);
1246 switch (res) {
1247 case 2:
1248 if (proxyPasswd == NULL)
1249 break;
1250 case 3:
1251 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001252 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +00001253 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001254 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
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) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001262 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001263 closesocket(ctxt->controlFd);
1264 ctxt->controlFd = -1;
1265 return(res);
1266 }
1267 res = xmlNanoFTPGetResponse(ctxt);
1268 if (res > 3) {
1269 closesocket(ctxt->controlFd);
1270 ctxt->controlFd = -1;
1271 return(-1);
1272 }
1273 break;
1274 case 1:
1275 break;
1276 case 4:
1277 case 5:
1278 case -1:
1279 default:
1280 closesocket(ctxt->controlFd);
1281 ctxt->controlFd = -1;
1282 return(-1);
1283 }
1284 }
1285
1286 /*
1287 * We assume we don't need more authentication to the proxy
1288 * and that it succeeded :-\
1289 */
1290 switch (proxyType) {
1291 case 0:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001292 /* we will try in sequence */
Owen Taylor3473f882001-02-23 17:55:21 +00001293 case 1:
1294 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001295 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001296 buf[sizeof(buf) - 1] = 0;
1297 len = strlen(buf);
1298#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001299 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001300#endif
1301 res = send(ctxt->controlFd, buf, len, 0);
1302 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001303 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001304 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1305 ctxt->controlFd = -1;
1306 return(res);
1307 }
1308 res = xmlNanoFTPGetResponse(ctxt);
1309 if (res == 2) {
1310 /* we assume it worked :-\ 1 is error for SITE command */
1311 proxyType = 1;
1312 break;
1313 }
1314 if (proxyType == 1) {
1315 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1316 ctxt->controlFd = -1;
1317 return(-1);
1318 }
1319 case 2:
1320 /* USER user@host command */
1321 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001322 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1323 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001324 else
Owen Taylor3473f882001-02-23 17:55:21 +00001325 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1326 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001327 buf[sizeof(buf) - 1] = 0;
1328 len = strlen(buf);
1329#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001330 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001331#endif
1332 res = send(ctxt->controlFd, buf, len, 0);
1333 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001334 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001335 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1336 ctxt->controlFd = -1;
1337 return(res);
1338 }
1339 res = xmlNanoFTPGetResponse(ctxt);
1340 if ((res == 1) || (res == 2)) {
1341 /* we assume it worked :-\ */
1342 proxyType = 2;
1343 return(0);
1344 }
1345 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001346 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001347 else
Owen Taylor3473f882001-02-23 17:55:21 +00001348 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001349 buf[sizeof(buf) - 1] = 0;
1350 len = strlen(buf);
1351#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001352 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001353#endif
1354 res = send(ctxt->controlFd, buf, len, 0);
1355 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001356 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001357 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1358 ctxt->controlFd = -1;
1359 return(res);
1360 }
1361 res = xmlNanoFTPGetResponse(ctxt);
1362 if ((res == 1) || (res == 2)) {
1363 /* we assume it worked :-\ */
1364 proxyType = 2;
1365 return(0);
1366 }
1367 if (proxyType == 2) {
1368 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1369 ctxt->controlFd = -1;
1370 return(-1);
1371 }
1372 case 3:
1373 /*
1374 * If you need support for other Proxy authentication scheme
1375 * send the code or at least the sequence in use.
1376 */
1377 default:
1378 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1379 ctxt->controlFd = -1;
1380 return(-1);
1381 }
1382 }
1383 /*
1384 * Non-proxy handling.
1385 */
1386 res = xmlNanoFTPSendUser(ctxt);
1387 if (res < 0) {
1388 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1389 ctxt->controlFd = -1;
1390 return(-1);
1391 }
1392 res = xmlNanoFTPGetResponse(ctxt);
1393 switch (res) {
1394 case 2:
1395 return(0);
1396 case 3:
1397 break;
1398 case 1:
1399 case 4:
1400 case 5:
1401 case -1:
1402 default:
1403 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1404 ctxt->controlFd = -1;
1405 return(-1);
1406 }
1407 res = xmlNanoFTPSendPasswd(ctxt);
1408 if (res < 0) {
1409 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1410 ctxt->controlFd = -1;
1411 return(-1);
1412 }
1413 res = xmlNanoFTPGetResponse(ctxt);
1414 switch (res) {
1415 case 2:
1416 break;
1417 case 3:
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001418 __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
1419 "FTP server asking for ACCNT on anonymous\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001420 case 1:
1421 case 4:
1422 case 5:
1423 case -1:
1424 default:
1425 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1426 ctxt->controlFd = -1;
1427 return(-1);
1428 }
1429
1430 return(0);
1431}
1432
1433/**
1434 * xmlNanoFTPConnectTo:
1435 * @server: an FTP server name
1436 * @port: the port (use 21 if 0)
1437 *
1438 * Tries to open a control connection to the given server/port
1439 *
1440 * Returns an fTP context or NULL if it failed
1441 */
1442
1443void*
1444xmlNanoFTPConnectTo(const char *server, int port) {
1445 xmlNanoFTPCtxtPtr ctxt;
1446 int res;
1447
1448 xmlNanoFTPInit();
1449 if (server == NULL)
1450 return(NULL);
1451 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1452 ctxt->hostname = xmlMemStrdup(server);
1453 if (port != 0)
1454 ctxt->port = port;
1455 res = xmlNanoFTPConnect(ctxt);
1456 if (res < 0) {
1457 xmlNanoFTPFreeCtxt(ctxt);
1458 return(NULL);
1459 }
1460 return(ctxt);
1461}
1462
1463/**
1464 * xmlNanoFTPCwd:
1465 * @ctx: an FTP context
1466 * @directory: a directory on the server
1467 *
1468 * Tries to change the remote directory
1469 *
1470 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1471 */
1472
1473int
Daniel Veillard34099b42004-11-04 17:34:35 +00001474xmlNanoFTPCwd(void *ctx, const char *directory) {
Owen Taylor3473f882001-02-23 17:55:21 +00001475 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1476 char buf[400];
1477 int len;
1478 int res;
1479
1480 /*
1481 * Expected response code for CWD:
1482 *
1483 * CWD
1484 * 250
1485 * 500, 501, 502, 421, 530, 550
1486 */
Owen Taylor3473f882001-02-23 17:55:21 +00001487 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001488 buf[sizeof(buf) - 1] = 0;
1489 len = strlen(buf);
1490#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001491 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001492#endif
1493 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001494 if (res < 0) {
1495 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1496 return(res);
1497 }
Owen Taylor3473f882001-02-23 17:55:21 +00001498 res = xmlNanoFTPGetResponse(ctxt);
1499 if (res == 4) {
1500 return(-1);
1501 }
1502 if (res == 2) return(1);
1503 if (res == 5) {
1504 return(0);
1505 }
1506 return(0);
1507}
1508
1509/**
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001510 * xmlNanoFTPDele:
1511 * @ctx: an FTP context
1512 * @file: a file or directory on the server
1513 *
1514 * Tries to delete an item (file or directory) from server
1515 *
1516 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1517 */
1518
1519int
Daniel Veillard34099b42004-11-04 17:34:35 +00001520xmlNanoFTPDele(void *ctx, const char *file) {
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001521 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1522 char buf[400];
1523 int len;
1524 int res;
1525
1526 /*
1527 * Expected response code for DELE:
1528 *
1529 * DELE
1530 * 250
1531 * 450, 550
1532 * 500, 501, 502, 421, 530
1533 */
1534
1535 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1536 buf[sizeof(buf) - 1] = 0;
1537 len = strlen(buf);
1538#ifdef DEBUG_FTP
1539 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1540#endif
1541 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001542 if (res < 0) {
1543 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1544 return(res);
1545 }
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001546 res = xmlNanoFTPGetResponse(ctxt);
1547 if (res == 4) {
1548 return(-1);
1549 }
1550 if (res == 2) return(1);
1551 if (res == 5) {
1552 return(0);
1553 }
1554 return(0);
1555}
1556/**
Owen Taylor3473f882001-02-23 17:55:21 +00001557 * xmlNanoFTPGetConnection:
1558 * @ctx: an FTP context
1559 *
1560 * Try to open a data connection to the server. Currently only
1561 * passive mode is supported.
1562 *
1563 * Returns -1 incase of error, 0 otherwise
1564 */
1565
1566int
1567xmlNanoFTPGetConnection(void *ctx) {
1568 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1569 char buf[200], *cur;
1570 int len, i;
1571 int res;
1572 unsigned char ad[6], *adp, *portp;
1573 unsigned int temp[6];
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001574#ifdef SUPPORT_IP6
1575 struct sockaddr_storage dataAddr;
1576#else
Owen Taylor3473f882001-02-23 17:55:21 +00001577 struct sockaddr_in dataAddr;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001578#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001579 SOCKLEN_T dataAddrLen;
1580
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001581 memset (&dataAddr, 0, sizeof(dataAddr));
1582#ifdef SUPPORT_IP6
1583 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1584 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1585 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1586 dataAddrLen = sizeof(struct sockaddr_in6);
1587 } else
1588#endif
1589 {
1590 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1591 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1592 dataAddrLen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001593 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001594
1595 if (ctxt->dataFd < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001596 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001597 return (-1);
1598 }
Owen Taylor3473f882001-02-23 17:55:21 +00001599
1600 if (ctxt->passive) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001601#ifdef SUPPORT_IP6
1602 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1603 snprintf (buf, sizeof(buf), "EPSV\r\n");
1604 else
1605#endif
1606 snprintf (buf, sizeof(buf), "PASV\r\n");
1607 len = strlen (buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001608#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001609 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001610#endif
1611 res = send(ctxt->controlFd, buf, len, 0);
1612 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001613 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001614 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1615 return(res);
1616 }
1617 res = xmlNanoFTPReadResponse(ctx);
1618 if (res != 2) {
1619 if (res == 5) {
1620 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1621 return(-1);
1622 } else {
1623 /*
1624 * retry with an active connection
1625 */
1626 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1627 ctxt->passive = 0;
1628 }
1629 }
1630 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1631 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001632#ifdef SUPPORT_IP6
1633 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1634 if (sscanf (cur, "%u", &temp[0]) != 1) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001635 __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001636 "Invalid answer to EPSV\n");
1637 if (ctxt->dataFd != -1) {
1638 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1639 }
1640 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001641 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001642 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1643 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
Owen Taylor3473f882001-02-23 17:55:21 +00001644 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001645 else
1646#endif
1647 {
1648 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1649 &temp[3], &temp[4], &temp[5]) != 6) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001650 __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001651 "Invalid answer to PASV\n");
1652 if (ctxt->dataFd != -1) {
1653 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1654 }
1655 return (-1);
1656 }
1657 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1658 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1659 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1660 }
1661
Owen Taylor3473f882001-02-23 17:55:21 +00001662 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001663 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
Owen Taylor3473f882001-02-23 17:55:21 +00001664 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1665 return (-1);
1666 }
1667 } else {
1668 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001669#ifdef SUPPORT_IP6
1670 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1671 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1672 else
1673#endif
1674 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1675
Owen Taylor3473f882001-02-23 17:55:21 +00001676 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001677 __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001678 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1679 return (-1);
1680 }
1681 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1682
1683 if (listen(ctxt->dataFd, 1) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001684 __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001685 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1686 return (-1);
1687 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001688#ifdef SUPPORT_IP6
1689 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1690 char buf6[INET6_ADDRSTRLEN];
1691 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1692 buf6, INET6_ADDRSTRLEN);
Daniel Veillard2db8c122003-07-08 12:16:59 +00001693 adp = (unsigned char *) buf6;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001694 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1695 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1696 } else
1697#endif
1698 {
1699 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1700 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1701 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1702 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1703 portp[0] & 0xff, portp[1] & 0xff);
1704 }
1705
Owen Taylor3473f882001-02-23 17:55:21 +00001706 buf[sizeof(buf) - 1] = 0;
1707 len = strlen(buf);
1708#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001709 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001710#endif
1711
1712 res = send(ctxt->controlFd, buf, len, 0);
1713 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001714 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001715 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1716 return(res);
1717 }
1718 res = xmlNanoFTPGetResponse(ctxt);
1719 if (res != 2) {
1720 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1721 return(-1);
1722 }
1723 }
1724 return(ctxt->dataFd);
1725
1726}
1727
1728/**
1729 * xmlNanoFTPCloseConnection:
1730 * @ctx: an FTP context
1731 *
1732 * Close the data connection from the server
1733 *
1734 * Returns -1 incase of error, 0 otherwise
1735 */
1736
1737int
1738xmlNanoFTPCloseConnection(void *ctx) {
1739 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1740 int res;
1741 fd_set rfd, efd;
1742 struct timeval tv;
1743
1744 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1745 tv.tv_sec = 15;
1746 tv.tv_usec = 0;
1747 FD_ZERO(&rfd);
1748 FD_SET(ctxt->controlFd, &rfd);
1749 FD_ZERO(&efd);
1750 FD_SET(ctxt->controlFd, &efd);
1751 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1752 if (res < 0) {
1753#ifdef DEBUG_FTP
1754 perror("select");
1755#endif
1756 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1757 return(-1);
1758 }
1759 if (res == 0) {
1760#ifdef DEBUG_FTP
1761 xmlGenericError(xmlGenericErrorContext,
1762 "xmlNanoFTPCloseConnection: timeout\n");
1763#endif
1764 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1765 } else {
1766 res = xmlNanoFTPGetResponse(ctxt);
1767 if (res != 2) {
1768 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1769 return(-1);
1770 }
1771 }
1772 return(0);
1773}
1774
1775/**
1776 * xmlNanoFTPParseList:
1777 * @list: some data listing received from the server
1778 * @callback: the user callback
1779 * @userData: the user callback data
1780 *
1781 * Parse at most one entry from the listing.
1782 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001783 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001784 */
1785
1786static int
1787xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1788 const char *cur = list;
1789 char filename[151];
1790 char attrib[11];
1791 char owner[11];
1792 char group[11];
1793 char month[4];
1794 int year = 0;
1795 int minute = 0;
1796 int hour = 0;
1797 int day = 0;
1798 unsigned long size = 0;
1799 int links = 0;
1800 int i;
1801
1802 if (!strncmp(cur, "total", 5)) {
1803 cur += 5;
1804 while (*cur == ' ') cur++;
1805 while ((*cur >= '0') && (*cur <= '9'))
1806 links = (links * 10) + (*cur++ - '0');
1807 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1808 cur++;
1809 return(cur - list);
1810 } else if (*list == '+') {
1811 return(0);
1812 } else {
1813 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1814 cur++;
1815 if (*cur == 0) return(0);
1816 i = 0;
1817 while (*cur != ' ') {
1818 if (i < 10)
1819 attrib[i++] = *cur;
1820 cur++;
1821 if (*cur == 0) return(0);
1822 }
1823 attrib[10] = 0;
1824 while (*cur == ' ') cur++;
1825 if (*cur == 0) return(0);
1826 while ((*cur >= '0') && (*cur <= '9'))
1827 links = (links * 10) + (*cur++ - '0');
1828 while (*cur == ' ') cur++;
1829 if (*cur == 0) return(0);
1830 i = 0;
1831 while (*cur != ' ') {
1832 if (i < 10)
1833 owner[i++] = *cur;
1834 cur++;
1835 if (*cur == 0) return(0);
1836 }
1837 owner[i] = 0;
1838 while (*cur == ' ') cur++;
1839 if (*cur == 0) return(0);
1840 i = 0;
1841 while (*cur != ' ') {
1842 if (i < 10)
1843 group[i++] = *cur;
1844 cur++;
1845 if (*cur == 0) return(0);
1846 }
1847 group[i] = 0;
1848 while (*cur == ' ') cur++;
1849 if (*cur == 0) return(0);
1850 while ((*cur >= '0') && (*cur <= '9'))
1851 size = (size * 10) + (*cur++ - '0');
1852 while (*cur == ' ') cur++;
1853 if (*cur == 0) return(0);
1854 i = 0;
1855 while (*cur != ' ') {
1856 if (i < 3)
1857 month[i++] = *cur;
1858 cur++;
1859 if (*cur == 0) return(0);
1860 }
1861 month[i] = 0;
1862 while (*cur == ' ') cur++;
1863 if (*cur == 0) return(0);
1864 while ((*cur >= '0') && (*cur <= '9'))
1865 day = (day * 10) + (*cur++ - '0');
1866 while (*cur == ' ') cur++;
1867 if (*cur == 0) return(0);
1868 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1869 if ((cur[1] == ':') || (cur[2] == ':')) {
1870 while ((*cur >= '0') && (*cur <= '9'))
1871 hour = (hour * 10) + (*cur++ - '0');
1872 if (*cur == ':') cur++;
1873 while ((*cur >= '0') && (*cur <= '9'))
1874 minute = (minute * 10) + (*cur++ - '0');
1875 } else {
1876 while ((*cur >= '0') && (*cur <= '9'))
1877 year = (year * 10) + (*cur++ - '0');
1878 }
1879 while (*cur == ' ') cur++;
1880 if (*cur == 0) return(0);
1881 i = 0;
1882 while ((*cur != '\n') && (*cur != '\r')) {
1883 if (i < 150)
1884 filename[i++] = *cur;
1885 cur++;
1886 if (*cur == 0) return(0);
1887 }
1888 filename[i] = 0;
1889 if ((*cur != '\n') && (*cur != '\r'))
1890 return(0);
1891 while ((*cur == '\n') || (*cur == '\r'))
1892 cur++;
1893 }
1894 if (callback != NULL) {
1895 callback(userData, filename, attrib, owner, group, size, links,
1896 year, month, day, hour, minute);
1897 }
1898 return(cur - list);
1899}
1900
1901/**
1902 * xmlNanoFTPList:
1903 * @ctx: an FTP context
1904 * @callback: the user callback
1905 * @userData: the user callback data
1906 * @filename: optional files to list
1907 *
1908 * Do a listing on the server. All files info are passed back
1909 * in the callbacks.
1910 *
1911 * Returns -1 incase of error, 0 otherwise
1912 */
1913
1914int
1915xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
Daniel Veillard34099b42004-11-04 17:34:35 +00001916 const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +00001917 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1918 char buf[4096 + 1];
1919 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001920 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001921 fd_set rfd, efd;
1922 struct timeval tv;
1923
1924 if (filename == NULL) {
1925 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1926 return(-1);
1927 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1928 if (ctxt->dataFd == -1)
1929 return(-1);
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001930 snprintf(buf, sizeof(buf), "LIST -L\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001931 } else {
1932 if (filename[0] != '/') {
1933 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1934 return(-1);
1935 }
1936 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1937 if (ctxt->dataFd == -1)
1938 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001939 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001940 }
1941 buf[sizeof(buf) - 1] = 0;
1942 len = strlen(buf);
1943#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001944 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001945#endif
1946 res = send(ctxt->controlFd, buf, len, 0);
1947 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001948 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001949 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1950 return(res);
1951 }
1952 res = xmlNanoFTPReadResponse(ctxt);
1953 if (res != 1) {
1954 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1955 return(-res);
1956 }
1957
1958 do {
1959 tv.tv_sec = 1;
1960 tv.tv_usec = 0;
1961 FD_ZERO(&rfd);
1962 FD_SET(ctxt->dataFd, &rfd);
1963 FD_ZERO(&efd);
1964 FD_SET(ctxt->dataFd, &efd);
1965 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1966 if (res < 0) {
1967#ifdef DEBUG_FTP
1968 perror("select");
1969#endif
1970 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1971 return(-1);
1972 }
1973 if (res == 0) {
1974 res = xmlNanoFTPCheckResponse(ctxt);
1975 if (res < 0) {
1976 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1977 ctxt->dataFd = -1;
1978 return(-1);
1979 }
1980 if (res == 2) {
1981 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1982 return(0);
1983 }
1984
1985 continue;
1986 }
1987
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001988 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001989 __xmlIOErr(XML_FROM_FTP, 0, "recv");
Owen Taylor3473f882001-02-23 17:55:21 +00001990 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1991 ctxt->dataFd = -1;
1992 return(-1);
1993 }
1994#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001995 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00001996#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001997 indx += len;
1998 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001999 base = 0;
2000 do {
2001 res = xmlNanoFTPParseList(&buf[base], callback, userData);
2002 base += res;
2003 } while (res > 0);
2004
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002005 memmove(&buf[0], &buf[base], indx - base);
2006 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00002007 } while (len != 0);
2008 xmlNanoFTPCloseConnection(ctxt);
2009 return(0);
2010}
2011
2012/**
2013 * xmlNanoFTPGetSocket:
2014 * @ctx: an FTP context
2015 * @filename: the file to retrieve (or NULL if path is in context).
2016 *
2017 * Initiate fetch of the given file from the server.
2018 *
2019 * Returns the socket for the data connection, or <0 in case of error
2020 */
2021
2022
2023int
2024xmlNanoFTPGetSocket(void *ctx, const char *filename) {
2025 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2026 char buf[300];
2027 int res, len;
2028 if ((filename == NULL) && (ctxt->path == NULL))
2029 return(-1);
2030 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
2031 if (ctxt->dataFd == -1)
2032 return(-1);
2033
Aleksey Sanin49cc9752002-06-14 17:07:10 +00002034 snprintf(buf, sizeof(buf), "TYPE I\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002035 len = strlen(buf);
2036#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00002037 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002038#endif
2039 res = send(ctxt->controlFd, buf, len, 0);
2040 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002041 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002042 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2043 return(res);
2044 }
2045 res = xmlNanoFTPReadResponse(ctxt);
2046 if (res != 2) {
2047 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2048 return(-res);
2049 }
2050 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00002051 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00002052 else
Owen Taylor3473f882001-02-23 17:55:21 +00002053 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00002054 buf[sizeof(buf) - 1] = 0;
2055 len = strlen(buf);
2056#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00002057 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002058#endif
2059 res = send(ctxt->controlFd, buf, len, 0);
2060 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002061 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002062 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2063 return(res);
2064 }
2065 res = xmlNanoFTPReadResponse(ctxt);
2066 if (res != 1) {
2067 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2068 return(-res);
2069 }
2070 return(ctxt->dataFd);
2071}
2072
2073/**
2074 * xmlNanoFTPGet:
2075 * @ctx: an FTP context
2076 * @callback: the user callback
2077 * @userData: the user callback data
2078 * @filename: the file to retrieve
2079 *
2080 * Fetch the given file from the server. All data are passed back
2081 * in the callbacks. The last callback has a size of 0 block.
2082 *
2083 * Returns -1 incase of error, 0 otherwise
2084 */
2085
2086int
2087xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
2088 const char *filename) {
2089 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2090 char buf[4096];
2091 int len = 0, res;
2092 fd_set rfd;
2093 struct timeval tv;
2094
2095 if ((filename == NULL) && (ctxt->path == NULL))
2096 return(-1);
2097 if (callback == NULL)
2098 return(-1);
2099 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
2100 return(-1);
2101
2102 do {
2103 tv.tv_sec = 1;
2104 tv.tv_usec = 0;
2105 FD_ZERO(&rfd);
2106 FD_SET(ctxt->dataFd, &rfd);
2107 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
2108 if (res < 0) {
2109#ifdef DEBUG_FTP
2110 perror("select");
2111#endif
2112 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2113 return(-1);
2114 }
2115 if (res == 0) {
2116 res = xmlNanoFTPCheckResponse(ctxt);
2117 if (res < 0) {
2118 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2119 ctxt->dataFd = -1;
2120 return(-1);
2121 }
2122 if (res == 2) {
2123 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2124 return(0);
2125 }
2126
2127 continue;
2128 }
2129 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002130 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002131 callback(userData, buf, len);
2132 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2133 return(-1);
2134 }
2135 callback(userData, buf, len);
2136 } while (len != 0);
2137
2138 return(xmlNanoFTPCloseConnection(ctxt));
2139}
2140
2141/**
2142 * xmlNanoFTPRead:
2143 * @ctx: the FTP context
2144 * @dest: a buffer
2145 * @len: the buffer length
2146 *
2147 * This function tries to read @len bytes from the existing FTP connection
2148 * and saves them in @dest. This is a blocking call.
2149 *
2150 * Returns the number of byte read. 0 is an indication of an end of connection.
2151 * -1 indicates a parameter error.
2152 */
2153int
2154xmlNanoFTPRead(void *ctx, void *dest, int len) {
2155 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2156
2157 if (ctx == NULL) return(-1);
2158 if (ctxt->dataFd < 0) return(0);
2159 if (dest == NULL) return(-1);
2160 if (len <= 0) return(0);
2161
2162 len = recv(ctxt->dataFd, dest, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002163 if (len <= 0) {
2164 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
2165 xmlNanoFTPCloseConnection(ctxt);
2166 }
Owen Taylor3473f882001-02-23 17:55:21 +00002167#ifdef DEBUG_FTP
2168 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
2169#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002170 return(len);
2171}
2172
2173/**
2174 * xmlNanoFTPOpen:
2175 * @URL: the URL to the resource
2176 *
2177 * Start to fetch the given ftp:// resource
2178 *
2179 * Returns an FTP context, or NULL
2180 */
2181
2182void*
2183xmlNanoFTPOpen(const char *URL) {
2184 xmlNanoFTPCtxtPtr ctxt;
2185 int sock;
2186
2187 xmlNanoFTPInit();
2188 if (URL == NULL) return(NULL);
2189 if (strncmp("ftp://", URL, 6)) return(NULL);
2190
2191 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2192 if (ctxt == NULL) return(NULL);
2193 if (xmlNanoFTPConnect(ctxt) < 0) {
2194 xmlNanoFTPFreeCtxt(ctxt);
2195 return(NULL);
2196 }
2197 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2198 if (sock < 0) {
2199 xmlNanoFTPFreeCtxt(ctxt);
2200 return(NULL);
2201 }
2202 return(ctxt);
2203}
2204
2205/**
2206 * xmlNanoFTPClose:
2207 * @ctx: an FTP context
2208 *
2209 * Close the connection and both control and transport
2210 *
2211 * Returns -1 incase of error, 0 otherwise
2212 */
2213
2214int
2215xmlNanoFTPClose(void *ctx) {
2216 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2217
2218 if (ctxt == NULL)
2219 return(-1);
2220
2221 if (ctxt->dataFd >= 0) {
2222 closesocket(ctxt->dataFd);
2223 ctxt->dataFd = -1;
2224 }
2225 if (ctxt->controlFd >= 0) {
2226 xmlNanoFTPQuit(ctxt);
2227 closesocket(ctxt->controlFd);
2228 ctxt->controlFd = -1;
2229 }
2230 xmlNanoFTPFreeCtxt(ctxt);
2231 return(0);
2232}
2233
2234#ifdef STANDALONE
2235/************************************************************************
2236 * *
2237 * Basic test in Standalone mode *
2238 * *
2239 ************************************************************************/
Daniel Veillard01c13b52002-12-10 15:19:08 +00002240static
Owen Taylor3473f882001-02-23 17:55:21 +00002241void ftpList(void *userData, const char *filename, const char* attrib,
2242 const char *owner, const char *group, unsigned long size, int links,
2243 int year, const char *month, int day, int hour, int minute) {
2244 xmlGenericError(xmlGenericErrorContext,
2245 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2246}
Daniel Veillard01c13b52002-12-10 15:19:08 +00002247static
Owen Taylor3473f882001-02-23 17:55:21 +00002248void ftpData(void *userData, const char *data, int len) {
2249 if (userData == NULL) return;
2250 if (len <= 0) {
Daniel Veillard82cb3192003-10-29 13:39:15 +00002251 fclose((FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002252 return;
2253 }
Daniel Veillard82cb3192003-10-29 13:39:15 +00002254 fwrite(data, len, 1, (FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002255}
2256
2257int main(int argc, char **argv) {
2258 void *ctxt;
2259 FILE *output;
2260 char *tstfile = NULL;
2261
2262 xmlNanoFTPInit();
2263 if (argc > 1) {
2264 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2265 if (xmlNanoFTPConnect(ctxt) < 0) {
2266 xmlGenericError(xmlGenericErrorContext,
2267 "Couldn't connect to %s\n", argv[1]);
2268 exit(1);
2269 }
2270 if (argc > 2)
2271 tstfile = argv[2];
2272 } else
2273 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2274 if (ctxt == NULL) {
2275 xmlGenericError(xmlGenericErrorContext,
2276 "Couldn't connect to localhost\n");
2277 exit(1);
2278 }
2279 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2280 output = fopen("/tmp/tstdata", "w");
2281 if (output != NULL) {
2282 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2283 xmlGenericError(xmlGenericErrorContext,
2284 "Failed to get file\n");
2285
2286 }
2287 xmlNanoFTPClose(ctxt);
2288 xmlMemoryDump();
2289 exit(0);
2290}
2291#endif /* STANDALONE */
2292#else /* !LIBXML_FTP_ENABLED */
2293#ifdef STANDALONE
2294#include <stdio.h>
2295int main(int argc, char **argv) {
2296 xmlGenericError(xmlGenericErrorContext,
2297 "%s : FTP support not compiled in\n", argv[0]);
2298 return(0);
2299}
2300#endif /* STANDALONE */
2301#endif /* LIBXML_FTP_ENABLED */