blob: 83a23b07ca96e320b812b09e16ee3c1707c7627d [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
Daniel Veillard27f20102004-11-05 11:50:11 +0000786 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
787
Owen Taylor3473f882001-02-23 17:55:21 +0000788 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
789#ifdef DEBUG_FTP
790 xmlGenericError(xmlGenericErrorContext,
791 "xmlNanoFTPGetMore : controlBufIndex = %d\n",
792 ctxt->controlBufIndex);
793#endif
794 return(-1);
795 }
796
797 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
798#ifdef DEBUG_FTP
799 xmlGenericError(xmlGenericErrorContext,
800 "xmlNanoFTPGetMore : controlBufUsed = %d\n",
801 ctxt->controlBufUsed);
802#endif
803 return(-1);
804 }
805 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
806#ifdef DEBUG_FTP
807 xmlGenericError(xmlGenericErrorContext,
808 "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
809 ctxt->controlBufIndex, ctxt->controlBufUsed);
810#endif
811 return(-1);
812 }
813
814 /*
815 * First pack the control buffer
816 */
817 if (ctxt->controlBufIndex > 0) {
818 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
819 ctxt->controlBufUsed - ctxt->controlBufIndex);
820 ctxt->controlBufUsed -= ctxt->controlBufIndex;
821 ctxt->controlBufIndex = 0;
822 }
823 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
824 if (size == 0) {
825#ifdef DEBUG_FTP
826 xmlGenericError(xmlGenericErrorContext,
827 "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
828#endif
829 return(0);
830 }
831
832 /*
Daniel Veillard60087f32001-10-10 09:45:09 +0000833 * Read the amount left on the control connection
Owen Taylor3473f882001-02-23 17:55:21 +0000834 */
835 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
836 size, 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000837 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000838 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
839 ctxt->controlFd = -1;
840 return(-1);
841 }
842#ifdef DEBUG_FTP
843 xmlGenericError(xmlGenericErrorContext,
844 "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
845 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
846#endif
847 ctxt->controlBufUsed += len;
848 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
849
850 return(len);
851}
852
853/**
854 * xmlNanoFTPReadResponse:
855 * @ctx: an FTP context
856 *
857 * Read the response from the FTP server after a command.
858 * Returns the code number
859 */
860static int
861xmlNanoFTPReadResponse(void *ctx) {
862 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
863 char *ptr, *end;
864 int len;
865 int res = -1, cur = -1;
866
Daniel Veillard27f20102004-11-05 11:50:11 +0000867 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
868
Owen Taylor3473f882001-02-23 17:55:21 +0000869get_more:
870 /*
871 * Assumes everything up to controlBuf[controlBufIndex] has been read
872 * and analyzed.
873 */
874 len = xmlNanoFTPGetMore(ctx);
875 if (len < 0) {
876 return(-1);
877 }
878 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
879 return(-1);
880 }
881 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
882 end = &ctxt->controlBuf[ctxt->controlBufUsed];
883
884#ifdef DEBUG_FTP
885 xmlGenericError(xmlGenericErrorContext,
886 "\n<<<\n%s\n--\n", ptr);
887#endif
888 while (ptr < end) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000889 cur = xmlNanoFTPParseResponse(ptr, end - ptr);
Owen Taylor3473f882001-02-23 17:55:21 +0000890 if (cur > 0) {
891 /*
892 * Successfully scanned the control code, scratch
893 * till the end of the line, but keep the index to be
894 * able to analyze the result if needed.
895 */
896 res = cur;
897 ptr += 3;
898 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
899 while ((ptr < end) && (*ptr != '\n')) ptr++;
900 if (*ptr == '\n') ptr++;
901 if (*ptr == '\r') ptr++;
902 break;
903 }
904 while ((ptr < end) && (*ptr != '\n')) ptr++;
905 if (ptr >= end) {
906 ctxt->controlBufIndex = ctxt->controlBufUsed;
907 goto get_more;
908 }
909 if (*ptr != '\r') ptr++;
910 }
911
912 if (res < 0) goto get_more;
913 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
914#ifdef DEBUG_FTP
915 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
916 xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
917#endif
918
919#ifdef DEBUG_FTP
920 xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
921#endif
922 return(res / 100);
923}
924
925/**
926 * xmlNanoFTPGetResponse:
927 * @ctx: an FTP context
928 *
929 * Get the response from the FTP server after a command.
930 * Returns the code number
931 */
932
933int
934xmlNanoFTPGetResponse(void *ctx) {
935 int res;
936
937 res = xmlNanoFTPReadResponse(ctx);
938
939 return(res);
940}
941
942/**
943 * xmlNanoFTPCheckResponse:
944 * @ctx: an FTP context
945 *
946 * Check if there is a response from the FTP server after a command.
947 * Returns the code number, or 0
948 */
949
950int
951xmlNanoFTPCheckResponse(void *ctx) {
952 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
953 fd_set rfd;
954 struct timeval tv;
955
Daniel Veillard27f20102004-11-05 11:50:11 +0000956 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000957 tv.tv_sec = 0;
958 tv.tv_usec = 0;
959 FD_ZERO(&rfd);
960 FD_SET(ctxt->controlFd, &rfd);
961 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
962 case 0:
963 return(0);
964 case -1:
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000965 __xmlIOErr(XML_FROM_FTP, 0, "select");
Owen Taylor3473f882001-02-23 17:55:21 +0000966 return(-1);
967
968 }
969
970 return(xmlNanoFTPReadResponse(ctx));
971}
972
973/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000974 * Send the user authentication
Owen Taylor3473f882001-02-23 17:55:21 +0000975 */
976
977static int
978xmlNanoFTPSendUser(void *ctx) {
979 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
980 char buf[200];
981 int len;
982 int res;
983
984 if (ctxt->user == NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000985 snprintf(buf, sizeof(buf), "USER anonymous\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000986 else
Owen Taylor3473f882001-02-23 17:55:21 +0000987 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Owen Taylor3473f882001-02-23 17:55:21 +0000988 buf[sizeof(buf) - 1] = 0;
989 len = strlen(buf);
990#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +0000991 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +0000992#endif
993 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +0000994 if (res < 0) {
995 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
996 return(res);
997 }
Owen Taylor3473f882001-02-23 17:55:21 +0000998 return(0);
999}
1000
1001/**
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001002 * Send the password authentication
Owen Taylor3473f882001-02-23 17:55:21 +00001003 */
1004
1005static int
1006xmlNanoFTPSendPasswd(void *ctx) {
1007 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1008 char buf[200];
1009 int len;
1010 int res;
1011
1012 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001013 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001014 else
Owen Taylor3473f882001-02-23 17:55:21 +00001015 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001016 buf[sizeof(buf) - 1] = 0;
1017 len = strlen(buf);
1018#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001019 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001020#endif
1021 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001022 if (res < 0) {
1023 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1024 return(res);
1025 }
Owen Taylor3473f882001-02-23 17:55:21 +00001026 return(0);
1027}
1028
1029/**
1030 * xmlNanoFTPQuit:
1031 * @ctx: an FTP context
1032 *
1033 * Send a QUIT command to the server
1034 *
1035 * Returns -1 in case of error, 0 otherwise
1036 */
1037
1038
1039int
1040xmlNanoFTPQuit(void *ctx) {
1041 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1042 char buf[200];
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001043 int len, res;
Owen Taylor3473f882001-02-23 17:55:21 +00001044
Daniel Veillard27f20102004-11-05 11:50:11 +00001045 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1046
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001047 snprintf(buf, sizeof(buf), "QUIT\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001048 len = strlen(buf);
1049#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001050 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 +00001051#endif
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001052 res = send(ctxt->controlFd, buf, len, 0);
1053 if (res < 0) {
1054 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1055 return(res);
1056 }
Owen Taylor3473f882001-02-23 17:55:21 +00001057 return(0);
1058}
1059
1060/**
1061 * xmlNanoFTPConnect:
1062 * @ctx: an FTP context
1063 *
1064 * Tries to open a control connection
1065 *
1066 * Returns -1 in case of error, 0 otherwise
1067 */
1068
1069int
1070xmlNanoFTPConnect(void *ctx) {
1071 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1072 struct hostent *hp;
1073 int port;
1074 int res;
Daniel Veillard2db8c122003-07-08 12:16:59 +00001075 int addrlen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001076
1077 if (ctxt == NULL)
1078 return(-1);
1079 if (ctxt->hostname == NULL)
1080 return(-1);
1081
1082 /*
1083 * do the blocking DNS query.
1084 */
Owen Taylor3473f882001-02-23 17:55:21 +00001085 if (proxy) {
1086 port = proxyPort;
1087 } else {
1088 port = ctxt->port;
1089 }
1090 if (port == 0)
1091 port = 21;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001092
1093 memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
1094
1095#ifdef SUPPORT_IP6
1096 if (have_ipv6 ()) {
Daniel Veillard2db8c122003-07-08 12:16:59 +00001097 struct addrinfo hints, *tmp, *result;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001098
1099 result = NULL;
1100 memset (&hints, 0, sizeof(hints));
1101 hints.ai_socktype = SOCK_STREAM;
1102
1103 if (proxy) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001104 if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
1105 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001106 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001107 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001108 }
1109 else
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001110 if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
1111 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001112 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001113 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001114
Daniel Veillard2db8c122003-07-08 12:16:59 +00001115 for (tmp = result; tmp; tmp = tmp->ai_next)
1116 if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001117 break;
1118
Daniel Veillard3dc93a42003-07-10 14:04:33 +00001119 if (!tmp) {
1120 if (result)
1121 freeaddrinfo (result);
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001122 __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
Daniel Veillard3dc93a42003-07-10 14:04:33 +00001123 return (-1);
1124 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001125 if (tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
1126 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
1127 return (-1);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001128 }
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001129 if (tmp->ai_family == AF_INET6) {
1130 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
1131 ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
1132 ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
1133 }
1134 else {
1135 memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
1136 ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
1137 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1138 }
1139 addrlen = tmp->ai_addrlen;
1140 freeaddrinfo (result);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001141 }
1142 else
1143#endif
1144 {
1145 if (proxy)
1146 hp = gethostbyname (proxy);
1147 else
1148 hp = gethostbyname (ctxt->hostname);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001149 if (hp == NULL) {
1150 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001151 return (-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001152 }
Daniel Veillard6927b102004-10-27 17:29:04 +00001153 if ((unsigned int) hp->h_length >
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001154 sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
1155 __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
1156 return (-1);
1157 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001158
Daniel Veillard8e2c9792004-10-27 09:39:50 +00001159 /*
1160 * Prepare the socket
1161 */
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001162 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
1163 memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
1164 hp->h_addr_list[0], hp->h_length);
1165 ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port = htons (port);
1166 ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
1167 addrlen = sizeof (struct sockaddr_in);
1168 }
1169
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001170 if (ctxt->controlFd < 0) {
1171 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001172 return(-1);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001173 }
Owen Taylor3473f882001-02-23 17:55:21 +00001174
1175 /*
1176 * Do the connect.
1177 */
1178 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001179 addrlen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001180 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
Owen Taylor3473f882001-02-23 17:55:21 +00001181 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1182 ctxt->controlFd = -1;
1183 return(-1);
1184 }
1185
1186 /*
1187 * Wait for the HELLO from the server.
1188 */
1189 res = xmlNanoFTPGetResponse(ctxt);
1190 if (res != 2) {
1191 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1192 ctxt->controlFd = -1;
1193 return(-1);
1194 }
1195
1196 /*
1197 * State diagram for the login operation on the FTP server
1198 *
1199 * Reference: RFC 959
1200 *
1201 * 1
1202 * +---+ USER +---+------------->+---+
1203 * | B |---------->| W | 2 ---->| E |
1204 * +---+ +---+------ | -->+---+
1205 * | | | | |
1206 * 3 | | 4,5 | | |
1207 * -------------- ----- | | |
1208 * | | | | |
1209 * | | | | |
1210 * | --------- |
1211 * | 1| | | |
1212 * V | | | |
1213 * +---+ PASS +---+ 2 | ------>+---+
1214 * | |---------->| W |------------->| S |
1215 * +---+ +---+ ---------->+---+
1216 * | | | | |
1217 * 3 | |4,5| | |
1218 * -------------- -------- |
1219 * | | | | |
1220 * | | | | |
1221 * | -----------
1222 * | 1,3| | | |
1223 * V | 2| | |
1224 * +---+ ACCT +---+-- | ----->+---+
1225 * | |---------->| W | 4,5 -------->| F |
1226 * +---+ +---+------------->+---+
1227 *
1228 * Of course in case of using a proxy this get really nasty and is not
1229 * standardized at all :-(
1230 */
1231 if (proxy) {
1232 int len;
1233 char buf[400];
1234
1235 if (proxyUser != NULL) {
1236 /*
1237 * We need proxy auth
1238 */
Owen Taylor3473f882001-02-23 17:55:21 +00001239 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Owen Taylor3473f882001-02-23 17:55:21 +00001240 buf[sizeof(buf) - 1] = 0;
1241 len = strlen(buf);
1242#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001243 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001244#endif
1245 res = send(ctxt->controlFd, buf, len, 0);
1246 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001247 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001248 closesocket(ctxt->controlFd);
1249 ctxt->controlFd = -1;
1250 return(res);
1251 }
1252 res = xmlNanoFTPGetResponse(ctxt);
1253 switch (res) {
1254 case 2:
1255 if (proxyPasswd == NULL)
1256 break;
1257 case 3:
1258 if (proxyPasswd != NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001259 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Owen Taylor3473f882001-02-23 17:55:21 +00001260 else
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001261 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001262 buf[sizeof(buf) - 1] = 0;
1263 len = strlen(buf);
1264#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001265 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001266#endif
1267 res = send(ctxt->controlFd, buf, len, 0);
1268 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001269 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001270 closesocket(ctxt->controlFd);
1271 ctxt->controlFd = -1;
1272 return(res);
1273 }
1274 res = xmlNanoFTPGetResponse(ctxt);
1275 if (res > 3) {
1276 closesocket(ctxt->controlFd);
1277 ctxt->controlFd = -1;
1278 return(-1);
1279 }
1280 break;
1281 case 1:
1282 break;
1283 case 4:
1284 case 5:
1285 case -1:
1286 default:
1287 closesocket(ctxt->controlFd);
1288 ctxt->controlFd = -1;
1289 return(-1);
1290 }
1291 }
1292
1293 /*
1294 * We assume we don't need more authentication to the proxy
1295 * and that it succeeded :-\
1296 */
1297 switch (proxyType) {
1298 case 0:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001299 /* we will try in sequence */
Owen Taylor3473f882001-02-23 17:55:21 +00001300 case 1:
1301 /* Using SITE command */
Owen Taylor3473f882001-02-23 17:55:21 +00001302 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001303 buf[sizeof(buf) - 1] = 0;
1304 len = strlen(buf);
1305#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001306 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001307#endif
1308 res = send(ctxt->controlFd, buf, len, 0);
1309 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001310 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001311 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1312 ctxt->controlFd = -1;
1313 return(res);
1314 }
1315 res = xmlNanoFTPGetResponse(ctxt);
1316 if (res == 2) {
1317 /* we assume it worked :-\ 1 is error for SITE command */
1318 proxyType = 1;
1319 break;
1320 }
1321 if (proxyType == 1) {
1322 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1323 ctxt->controlFd = -1;
1324 return(-1);
1325 }
1326 case 2:
1327 /* USER user@host command */
1328 if (ctxt->user == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001329 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1330 ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001331 else
Owen Taylor3473f882001-02-23 17:55:21 +00001332 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1333 ctxt->user, ctxt->hostname);
Owen Taylor3473f882001-02-23 17:55:21 +00001334 buf[sizeof(buf) - 1] = 0;
1335 len = strlen(buf);
1336#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001337 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001338#endif
1339 res = send(ctxt->controlFd, buf, len, 0);
1340 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001341 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001342 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1343 ctxt->controlFd = -1;
1344 return(res);
1345 }
1346 res = xmlNanoFTPGetResponse(ctxt);
1347 if ((res == 1) || (res == 2)) {
1348 /* we assume it worked :-\ */
1349 proxyType = 2;
1350 return(0);
1351 }
1352 if (ctxt->passwd == NULL)
Daniel Veillard9ae1eba2001-10-19 09:48:35 +00001353 snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001354 else
Owen Taylor3473f882001-02-23 17:55:21 +00001355 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Owen Taylor3473f882001-02-23 17:55:21 +00001356 buf[sizeof(buf) - 1] = 0;
1357 len = strlen(buf);
1358#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001359 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001360#endif
1361 res = send(ctxt->controlFd, buf, len, 0);
1362 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001363 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001364 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1365 ctxt->controlFd = -1;
1366 return(res);
1367 }
1368 res = xmlNanoFTPGetResponse(ctxt);
1369 if ((res == 1) || (res == 2)) {
1370 /* we assume it worked :-\ */
1371 proxyType = 2;
1372 return(0);
1373 }
1374 if (proxyType == 2) {
1375 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1376 ctxt->controlFd = -1;
1377 return(-1);
1378 }
1379 case 3:
1380 /*
1381 * If you need support for other Proxy authentication scheme
1382 * send the code or at least the sequence in use.
1383 */
1384 default:
1385 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1386 ctxt->controlFd = -1;
1387 return(-1);
1388 }
1389 }
1390 /*
1391 * Non-proxy handling.
1392 */
1393 res = xmlNanoFTPSendUser(ctxt);
1394 if (res < 0) {
1395 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1396 ctxt->controlFd = -1;
1397 return(-1);
1398 }
1399 res = xmlNanoFTPGetResponse(ctxt);
1400 switch (res) {
1401 case 2:
1402 return(0);
1403 case 3:
1404 break;
1405 case 1:
1406 case 4:
1407 case 5:
1408 case -1:
1409 default:
1410 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1411 ctxt->controlFd = -1;
1412 return(-1);
1413 }
1414 res = xmlNanoFTPSendPasswd(ctxt);
1415 if (res < 0) {
1416 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1417 ctxt->controlFd = -1;
1418 return(-1);
1419 }
1420 res = xmlNanoFTPGetResponse(ctxt);
1421 switch (res) {
1422 case 2:
1423 break;
1424 case 3:
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001425 __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
1426 "FTP server asking for ACCNT on anonymous\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001427 case 1:
1428 case 4:
1429 case 5:
1430 case -1:
1431 default:
1432 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1433 ctxt->controlFd = -1;
1434 return(-1);
1435 }
1436
1437 return(0);
1438}
1439
1440/**
1441 * xmlNanoFTPConnectTo:
1442 * @server: an FTP server name
1443 * @port: the port (use 21 if 0)
1444 *
1445 * Tries to open a control connection to the given server/port
1446 *
1447 * Returns an fTP context or NULL if it failed
1448 */
1449
1450void*
1451xmlNanoFTPConnectTo(const char *server, int port) {
1452 xmlNanoFTPCtxtPtr ctxt;
1453 int res;
1454
1455 xmlNanoFTPInit();
1456 if (server == NULL)
1457 return(NULL);
Daniel Veillard27f20102004-11-05 11:50:11 +00001458 if (port <= 0)
1459 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001460 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1461 ctxt->hostname = xmlMemStrdup(server);
1462 if (port != 0)
1463 ctxt->port = port;
1464 res = xmlNanoFTPConnect(ctxt);
1465 if (res < 0) {
1466 xmlNanoFTPFreeCtxt(ctxt);
1467 return(NULL);
1468 }
1469 return(ctxt);
1470}
1471
1472/**
1473 * xmlNanoFTPCwd:
1474 * @ctx: an FTP context
1475 * @directory: a directory on the server
1476 *
1477 * Tries to change the remote directory
1478 *
1479 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1480 */
1481
1482int
Daniel Veillard34099b42004-11-04 17:34:35 +00001483xmlNanoFTPCwd(void *ctx, const char *directory) {
Owen Taylor3473f882001-02-23 17:55:21 +00001484 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1485 char buf[400];
1486 int len;
1487 int res;
1488
Daniel Veillard27f20102004-11-05 11:50:11 +00001489 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1490
Owen Taylor3473f882001-02-23 17:55:21 +00001491 /*
1492 * Expected response code for CWD:
1493 *
1494 * CWD
1495 * 250
1496 * 500, 501, 502, 421, 530, 550
1497 */
Owen Taylor3473f882001-02-23 17:55:21 +00001498 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Owen Taylor3473f882001-02-23 17:55:21 +00001499 buf[sizeof(buf) - 1] = 0;
1500 len = strlen(buf);
1501#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001502 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001503#endif
1504 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001505 if (res < 0) {
1506 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1507 return(res);
1508 }
Owen Taylor3473f882001-02-23 17:55:21 +00001509 res = xmlNanoFTPGetResponse(ctxt);
1510 if (res == 4) {
1511 return(-1);
1512 }
1513 if (res == 2) return(1);
1514 if (res == 5) {
1515 return(0);
1516 }
1517 return(0);
1518}
1519
1520/**
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001521 * xmlNanoFTPDele:
1522 * @ctx: an FTP context
1523 * @file: a file or directory on the server
1524 *
1525 * Tries to delete an item (file or directory) from server
1526 *
1527 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1528 */
1529
1530int
Daniel Veillard34099b42004-11-04 17:34:35 +00001531xmlNanoFTPDele(void *ctx, const char *file) {
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001532 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1533 char buf[400];
1534 int len;
1535 int res;
1536
Daniel Veillard27f20102004-11-05 11:50:11 +00001537 if ((ctxt == NULL) || (ctxt->controlFd < 0) || (file == NULL)) return(-1);
1538
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001539 /*
1540 * Expected response code for DELE:
1541 *
1542 * DELE
1543 * 250
1544 * 450, 550
1545 * 500, 501, 502, 421, 530
1546 */
1547
1548 snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1549 buf[sizeof(buf) - 1] = 0;
1550 len = strlen(buf);
1551#ifdef DEBUG_FTP
1552 xmlGenericError(xmlGenericErrorContext, "%s", buf);
1553#endif
1554 res = send(ctxt->controlFd, buf, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001555 if (res < 0) {
1556 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1557 return(res);
1558 }
Daniel Veillard6c73cb82003-03-05 16:45:40 +00001559 res = xmlNanoFTPGetResponse(ctxt);
1560 if (res == 4) {
1561 return(-1);
1562 }
1563 if (res == 2) return(1);
1564 if (res == 5) {
1565 return(0);
1566 }
1567 return(0);
1568}
1569/**
Owen Taylor3473f882001-02-23 17:55:21 +00001570 * xmlNanoFTPGetConnection:
1571 * @ctx: an FTP context
1572 *
1573 * Try to open a data connection to the server. Currently only
1574 * passive mode is supported.
1575 *
1576 * Returns -1 incase of error, 0 otherwise
1577 */
1578
1579int
1580xmlNanoFTPGetConnection(void *ctx) {
1581 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1582 char buf[200], *cur;
1583 int len, i;
1584 int res;
1585 unsigned char ad[6], *adp, *portp;
1586 unsigned int temp[6];
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001587#ifdef SUPPORT_IP6
1588 struct sockaddr_storage dataAddr;
1589#else
Owen Taylor3473f882001-02-23 17:55:21 +00001590 struct sockaddr_in dataAddr;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001591#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001592 SOCKLEN_T dataAddrLen;
1593
Daniel Veillard27f20102004-11-05 11:50:11 +00001594 if (ctxt == NULL) return(-1);
1595
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001596 memset (&dataAddr, 0, sizeof(dataAddr));
1597#ifdef SUPPORT_IP6
1598 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1599 ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1600 ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1601 dataAddrLen = sizeof(struct sockaddr_in6);
1602 } else
1603#endif
1604 {
1605 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1606 ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1607 dataAddrLen = sizeof (struct sockaddr_in);
Owen Taylor3473f882001-02-23 17:55:21 +00001608 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001609
1610 if (ctxt->dataFd < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001611 __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001612 return (-1);
1613 }
Owen Taylor3473f882001-02-23 17:55:21 +00001614
1615 if (ctxt->passive) {
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001616#ifdef SUPPORT_IP6
1617 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1618 snprintf (buf, sizeof(buf), "EPSV\r\n");
1619 else
1620#endif
1621 snprintf (buf, sizeof(buf), "PASV\r\n");
1622 len = strlen (buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001623#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001624 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001625#endif
1626 res = send(ctxt->controlFd, buf, len, 0);
1627 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001628 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001629 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1630 return(res);
1631 }
1632 res = xmlNanoFTPReadResponse(ctx);
1633 if (res != 2) {
1634 if (res == 5) {
1635 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1636 return(-1);
1637 } else {
1638 /*
1639 * retry with an active connection
1640 */
1641 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1642 ctxt->passive = 0;
1643 }
1644 }
1645 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1646 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001647#ifdef SUPPORT_IP6
1648 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1649 if (sscanf (cur, "%u", &temp[0]) != 1) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001650 __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001651 "Invalid answer to EPSV\n");
1652 if (ctxt->dataFd != -1) {
1653 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1654 }
1655 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001656 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001657 memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1658 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
Owen Taylor3473f882001-02-23 17:55:21 +00001659 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001660 else
1661#endif
1662 {
1663 if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1664 &temp[3], &temp[4], &temp[5]) != 6) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001665 __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001666 "Invalid answer to PASV\n");
1667 if (ctxt->dataFd != -1) {
1668 closesocket (ctxt->dataFd); ctxt->dataFd = -1;
1669 }
1670 return (-1);
1671 }
1672 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1673 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1674 memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1675 }
1676
Owen Taylor3473f882001-02-23 17:55:21 +00001677 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001678 __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
Owen Taylor3473f882001-02-23 17:55:21 +00001679 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1680 return (-1);
1681 }
1682 } else {
1683 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001684#ifdef SUPPORT_IP6
1685 if ((ctxt->ftpAddr).ss_family == AF_INET6)
1686 ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1687 else
1688#endif
1689 ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1690
Owen Taylor3473f882001-02-23 17:55:21 +00001691 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001692 __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001693 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1694 return (-1);
1695 }
1696 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1697
1698 if (listen(ctxt->dataFd, 1) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001699 __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001700 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1701 return (-1);
1702 }
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001703#ifdef SUPPORT_IP6
1704 if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1705 char buf6[INET6_ADDRSTRLEN];
1706 inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1707 buf6, INET6_ADDRSTRLEN);
Daniel Veillard2db8c122003-07-08 12:16:59 +00001708 adp = (unsigned char *) buf6;
Daniel Veillardde2a67b2003-06-21 14:20:04 +00001709 portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1710 snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1711 } else
1712#endif
1713 {
1714 adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1715 portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1716 snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1717 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1718 portp[0] & 0xff, portp[1] & 0xff);
1719 }
1720
Owen Taylor3473f882001-02-23 17:55:21 +00001721 buf[sizeof(buf) - 1] = 0;
1722 len = strlen(buf);
1723#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001724 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001725#endif
1726
1727 res = send(ctxt->controlFd, buf, len, 0);
1728 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001729 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001730 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1731 return(res);
1732 }
1733 res = xmlNanoFTPGetResponse(ctxt);
1734 if (res != 2) {
1735 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1736 return(-1);
1737 }
1738 }
1739 return(ctxt->dataFd);
1740
1741}
1742
1743/**
1744 * xmlNanoFTPCloseConnection:
1745 * @ctx: an FTP context
1746 *
1747 * Close the data connection from the server
1748 *
1749 * Returns -1 incase of error, 0 otherwise
1750 */
1751
1752int
1753xmlNanoFTPCloseConnection(void *ctx) {
1754 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1755 int res;
1756 fd_set rfd, efd;
1757 struct timeval tv;
1758
Daniel Veillard27f20102004-11-05 11:50:11 +00001759 if ((ctxt == NULL) || (ctxt->controlFd < 0)) return(-1);
1760
Owen Taylor3473f882001-02-23 17:55:21 +00001761 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1762 tv.tv_sec = 15;
1763 tv.tv_usec = 0;
1764 FD_ZERO(&rfd);
1765 FD_SET(ctxt->controlFd, &rfd);
1766 FD_ZERO(&efd);
1767 FD_SET(ctxt->controlFd, &efd);
1768 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1769 if (res < 0) {
1770#ifdef DEBUG_FTP
1771 perror("select");
1772#endif
1773 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1774 return(-1);
1775 }
1776 if (res == 0) {
1777#ifdef DEBUG_FTP
1778 xmlGenericError(xmlGenericErrorContext,
1779 "xmlNanoFTPCloseConnection: timeout\n");
1780#endif
1781 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1782 } else {
1783 res = xmlNanoFTPGetResponse(ctxt);
1784 if (res != 2) {
1785 closesocket(ctxt->controlFd); ctxt->controlFd = -1;
1786 return(-1);
1787 }
1788 }
1789 return(0);
1790}
1791
1792/**
1793 * xmlNanoFTPParseList:
1794 * @list: some data listing received from the server
1795 * @callback: the user callback
1796 * @userData: the user callback data
1797 *
1798 * Parse at most one entry from the listing.
1799 *
Daniel Veillard60087f32001-10-10 09:45:09 +00001800 * Returns -1 incase of error, the length of data parsed otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00001801 */
1802
1803static int
1804xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1805 const char *cur = list;
1806 char filename[151];
1807 char attrib[11];
1808 char owner[11];
1809 char group[11];
1810 char month[4];
1811 int year = 0;
1812 int minute = 0;
1813 int hour = 0;
1814 int day = 0;
1815 unsigned long size = 0;
1816 int links = 0;
1817 int i;
1818
1819 if (!strncmp(cur, "total", 5)) {
1820 cur += 5;
1821 while (*cur == ' ') cur++;
1822 while ((*cur >= '0') && (*cur <= '9'))
1823 links = (links * 10) + (*cur++ - '0');
1824 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1825 cur++;
1826 return(cur - list);
1827 } else if (*list == '+') {
1828 return(0);
1829 } else {
1830 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1831 cur++;
1832 if (*cur == 0) return(0);
1833 i = 0;
1834 while (*cur != ' ') {
1835 if (i < 10)
1836 attrib[i++] = *cur;
1837 cur++;
1838 if (*cur == 0) return(0);
1839 }
1840 attrib[10] = 0;
1841 while (*cur == ' ') cur++;
1842 if (*cur == 0) return(0);
1843 while ((*cur >= '0') && (*cur <= '9'))
1844 links = (links * 10) + (*cur++ - '0');
1845 while (*cur == ' ') cur++;
1846 if (*cur == 0) return(0);
1847 i = 0;
1848 while (*cur != ' ') {
1849 if (i < 10)
1850 owner[i++] = *cur;
1851 cur++;
1852 if (*cur == 0) return(0);
1853 }
1854 owner[i] = 0;
1855 while (*cur == ' ') cur++;
1856 if (*cur == 0) return(0);
1857 i = 0;
1858 while (*cur != ' ') {
1859 if (i < 10)
1860 group[i++] = *cur;
1861 cur++;
1862 if (*cur == 0) return(0);
1863 }
1864 group[i] = 0;
1865 while (*cur == ' ') cur++;
1866 if (*cur == 0) return(0);
1867 while ((*cur >= '0') && (*cur <= '9'))
1868 size = (size * 10) + (*cur++ - '0');
1869 while (*cur == ' ') cur++;
1870 if (*cur == 0) return(0);
1871 i = 0;
1872 while (*cur != ' ') {
1873 if (i < 3)
1874 month[i++] = *cur;
1875 cur++;
1876 if (*cur == 0) return(0);
1877 }
1878 month[i] = 0;
1879 while (*cur == ' ') cur++;
1880 if (*cur == 0) return(0);
1881 while ((*cur >= '0') && (*cur <= '9'))
1882 day = (day * 10) + (*cur++ - '0');
1883 while (*cur == ' ') cur++;
1884 if (*cur == 0) return(0);
1885 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1886 if ((cur[1] == ':') || (cur[2] == ':')) {
1887 while ((*cur >= '0') && (*cur <= '9'))
1888 hour = (hour * 10) + (*cur++ - '0');
1889 if (*cur == ':') cur++;
1890 while ((*cur >= '0') && (*cur <= '9'))
1891 minute = (minute * 10) + (*cur++ - '0');
1892 } else {
1893 while ((*cur >= '0') && (*cur <= '9'))
1894 year = (year * 10) + (*cur++ - '0');
1895 }
1896 while (*cur == ' ') cur++;
1897 if (*cur == 0) return(0);
1898 i = 0;
1899 while ((*cur != '\n') && (*cur != '\r')) {
1900 if (i < 150)
1901 filename[i++] = *cur;
1902 cur++;
1903 if (*cur == 0) return(0);
1904 }
1905 filename[i] = 0;
1906 if ((*cur != '\n') && (*cur != '\r'))
1907 return(0);
1908 while ((*cur == '\n') || (*cur == '\r'))
1909 cur++;
1910 }
1911 if (callback != NULL) {
1912 callback(userData, filename, attrib, owner, group, size, links,
1913 year, month, day, hour, minute);
1914 }
1915 return(cur - list);
1916}
1917
1918/**
1919 * xmlNanoFTPList:
1920 * @ctx: an FTP context
1921 * @callback: the user callback
1922 * @userData: the user callback data
1923 * @filename: optional files to list
1924 *
1925 * Do a listing on the server. All files info are passed back
1926 * in the callbacks.
1927 *
1928 * Returns -1 incase of error, 0 otherwise
1929 */
1930
1931int
1932xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
Daniel Veillard34099b42004-11-04 17:34:35 +00001933 const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +00001934 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1935 char buf[4096 + 1];
1936 int len, res;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001937 int indx = 0, base;
Owen Taylor3473f882001-02-23 17:55:21 +00001938 fd_set rfd, efd;
1939 struct timeval tv;
1940
1941 if (filename == NULL) {
1942 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1943 return(-1);
1944 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1945 if (ctxt->dataFd == -1)
1946 return(-1);
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001947 snprintf(buf, sizeof(buf), "LIST -L\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001948 } else {
1949 if (filename[0] != '/') {
1950 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1951 return(-1);
1952 }
1953 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1954 if (ctxt->dataFd == -1)
1955 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001956 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00001957 }
1958 buf[sizeof(buf) - 1] = 0;
1959 len = strlen(buf);
1960#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00001961 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001962#endif
1963 res = send(ctxt->controlFd, buf, len, 0);
1964 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00001965 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001966 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1967 return(res);
1968 }
1969 res = xmlNanoFTPReadResponse(ctxt);
1970 if (res != 1) {
1971 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1972 return(-res);
1973 }
1974
1975 do {
1976 tv.tv_sec = 1;
1977 tv.tv_usec = 0;
1978 FD_ZERO(&rfd);
1979 FD_SET(ctxt->dataFd, &rfd);
1980 FD_ZERO(&efd);
1981 FD_SET(ctxt->dataFd, &efd);
1982 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1983 if (res < 0) {
1984#ifdef DEBUG_FTP
1985 perror("select");
1986#endif
1987 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1988 return(-1);
1989 }
1990 if (res == 0) {
1991 res = xmlNanoFTPCheckResponse(ctxt);
1992 if (res < 0) {
1993 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1994 ctxt->dataFd = -1;
1995 return(-1);
1996 }
1997 if (res == 2) {
1998 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
1999 return(0);
2000 }
2001
2002 continue;
2003 }
2004
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002005 if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002006 __xmlIOErr(XML_FROM_FTP, 0, "recv");
Owen Taylor3473f882001-02-23 17:55:21 +00002007 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2008 ctxt->dataFd = -1;
2009 return(-1);
2010 }
2011#ifdef DEBUG_FTP
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002012 write(1, &buf[indx], len);
Owen Taylor3473f882001-02-23 17:55:21 +00002013#endif
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002014 indx += len;
2015 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002016 base = 0;
2017 do {
2018 res = xmlNanoFTPParseList(&buf[base], callback, userData);
2019 base += res;
2020 } while (res > 0);
2021
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002022 memmove(&buf[0], &buf[base], indx - base);
2023 indx -= base;
Owen Taylor3473f882001-02-23 17:55:21 +00002024 } while (len != 0);
2025 xmlNanoFTPCloseConnection(ctxt);
2026 return(0);
2027}
2028
2029/**
2030 * xmlNanoFTPGetSocket:
2031 * @ctx: an FTP context
2032 * @filename: the file to retrieve (or NULL if path is in context).
2033 *
2034 * Initiate fetch of the given file from the server.
2035 *
2036 * Returns the socket for the data connection, or <0 in case of error
2037 */
2038
2039
2040int
2041xmlNanoFTPGetSocket(void *ctx, const char *filename) {
2042 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2043 char buf[300];
2044 int res, len;
Daniel Veillard27f20102004-11-05 11:50:11 +00002045 if (ctx == NULL)
2046 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002047 if ((filename == NULL) && (ctxt->path == NULL))
2048 return(-1);
2049 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
2050 if (ctxt->dataFd == -1)
2051 return(-1);
2052
Aleksey Sanin49cc9752002-06-14 17:07:10 +00002053 snprintf(buf, sizeof(buf), "TYPE I\r\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002054 len = strlen(buf);
2055#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00002056 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002057#endif
2058 res = send(ctxt->controlFd, buf, len, 0);
2059 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002060 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002061 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2062 return(res);
2063 }
2064 res = xmlNanoFTPReadResponse(ctxt);
2065 if (res != 2) {
2066 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2067 return(-res);
2068 }
2069 if (filename == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00002070 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00002071 else
Owen Taylor3473f882001-02-23 17:55:21 +00002072 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Owen Taylor3473f882001-02-23 17:55:21 +00002073 buf[sizeof(buf) - 1] = 0;
2074 len = strlen(buf);
2075#ifdef DEBUG_FTP
Daniel Veillard635ef722001-10-29 11:48:19 +00002076 xmlGenericError(xmlGenericErrorContext, "%s", buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002077#endif
2078 res = send(ctxt->controlFd, buf, len, 0);
2079 if (res < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002080 __xmlIOErr(XML_FROM_FTP, 0, "send failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002081 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2082 return(res);
2083 }
2084 res = xmlNanoFTPReadResponse(ctxt);
2085 if (res != 1) {
2086 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2087 return(-res);
2088 }
2089 return(ctxt->dataFd);
2090}
2091
2092/**
2093 * xmlNanoFTPGet:
2094 * @ctx: an FTP context
2095 * @callback: the user callback
2096 * @userData: the user callback data
2097 * @filename: the file to retrieve
2098 *
2099 * Fetch the given file from the server. All data are passed back
2100 * in the callbacks. The last callback has a size of 0 block.
2101 *
2102 * Returns -1 incase of error, 0 otherwise
2103 */
2104
2105int
2106xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
2107 const char *filename) {
2108 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2109 char buf[4096];
2110 int len = 0, res;
2111 fd_set rfd;
2112 struct timeval tv;
2113
2114 if ((filename == NULL) && (ctxt->path == NULL))
2115 return(-1);
2116 if (callback == NULL)
2117 return(-1);
2118 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
2119 return(-1);
2120
2121 do {
2122 tv.tv_sec = 1;
2123 tv.tv_usec = 0;
2124 FD_ZERO(&rfd);
2125 FD_SET(ctxt->dataFd, &rfd);
2126 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
2127 if (res < 0) {
2128#ifdef DEBUG_FTP
2129 perror("select");
2130#endif
2131 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2132 return(-1);
2133 }
2134 if (res == 0) {
2135 res = xmlNanoFTPCheckResponse(ctxt);
2136 if (res < 0) {
2137 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2138 ctxt->dataFd = -1;
2139 return(-1);
2140 }
2141 if (res == 2) {
2142 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2143 return(0);
2144 }
2145
2146 continue;
2147 }
2148 if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002149 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002150 callback(userData, buf, len);
2151 closesocket(ctxt->dataFd); ctxt->dataFd = -1;
2152 return(-1);
2153 }
2154 callback(userData, buf, len);
2155 } while (len != 0);
2156
2157 return(xmlNanoFTPCloseConnection(ctxt));
2158}
2159
2160/**
2161 * xmlNanoFTPRead:
2162 * @ctx: the FTP context
2163 * @dest: a buffer
2164 * @len: the buffer length
2165 *
2166 * This function tries to read @len bytes from the existing FTP connection
2167 * and saves them in @dest. This is a blocking call.
2168 *
2169 * Returns the number of byte read. 0 is an indication of an end of connection.
2170 * -1 indicates a parameter error.
2171 */
2172int
2173xmlNanoFTPRead(void *ctx, void *dest, int len) {
2174 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2175
2176 if (ctx == NULL) return(-1);
2177 if (ctxt->dataFd < 0) return(0);
2178 if (dest == NULL) return(-1);
2179 if (len <= 0) return(0);
2180
2181 len = recv(ctxt->dataFd, dest, len, 0);
Daniel Veillard2b0f8792003-10-10 19:36:36 +00002182 if (len <= 0) {
2183 __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
2184 xmlNanoFTPCloseConnection(ctxt);
2185 }
Owen Taylor3473f882001-02-23 17:55:21 +00002186#ifdef DEBUG_FTP
2187 xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
2188#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002189 return(len);
2190}
2191
2192/**
2193 * xmlNanoFTPOpen:
2194 * @URL: the URL to the resource
2195 *
2196 * Start to fetch the given ftp:// resource
2197 *
2198 * Returns an FTP context, or NULL
2199 */
2200
2201void*
2202xmlNanoFTPOpen(const char *URL) {
2203 xmlNanoFTPCtxtPtr ctxt;
2204 int sock;
2205
2206 xmlNanoFTPInit();
2207 if (URL == NULL) return(NULL);
2208 if (strncmp("ftp://", URL, 6)) return(NULL);
2209
2210 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2211 if (ctxt == NULL) return(NULL);
2212 if (xmlNanoFTPConnect(ctxt) < 0) {
2213 xmlNanoFTPFreeCtxt(ctxt);
2214 return(NULL);
2215 }
2216 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2217 if (sock < 0) {
2218 xmlNanoFTPFreeCtxt(ctxt);
2219 return(NULL);
2220 }
2221 return(ctxt);
2222}
2223
2224/**
2225 * xmlNanoFTPClose:
2226 * @ctx: an FTP context
2227 *
2228 * Close the connection and both control and transport
2229 *
2230 * Returns -1 incase of error, 0 otherwise
2231 */
2232
2233int
2234xmlNanoFTPClose(void *ctx) {
2235 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2236
2237 if (ctxt == NULL)
2238 return(-1);
2239
2240 if (ctxt->dataFd >= 0) {
2241 closesocket(ctxt->dataFd);
2242 ctxt->dataFd = -1;
2243 }
2244 if (ctxt->controlFd >= 0) {
2245 xmlNanoFTPQuit(ctxt);
2246 closesocket(ctxt->controlFd);
2247 ctxt->controlFd = -1;
2248 }
2249 xmlNanoFTPFreeCtxt(ctxt);
2250 return(0);
2251}
2252
2253#ifdef STANDALONE
2254/************************************************************************
2255 * *
2256 * Basic test in Standalone mode *
2257 * *
2258 ************************************************************************/
Daniel Veillard01c13b52002-12-10 15:19:08 +00002259static
Owen Taylor3473f882001-02-23 17:55:21 +00002260void ftpList(void *userData, const char *filename, const char* attrib,
2261 const char *owner, const char *group, unsigned long size, int links,
2262 int year, const char *month, int day, int hour, int minute) {
2263 xmlGenericError(xmlGenericErrorContext,
2264 "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2265}
Daniel Veillard01c13b52002-12-10 15:19:08 +00002266static
Owen Taylor3473f882001-02-23 17:55:21 +00002267void ftpData(void *userData, const char *data, int len) {
2268 if (userData == NULL) return;
2269 if (len <= 0) {
Daniel Veillard82cb3192003-10-29 13:39:15 +00002270 fclose((FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002271 return;
2272 }
Daniel Veillard82cb3192003-10-29 13:39:15 +00002273 fwrite(data, len, 1, (FILE*)userData);
Owen Taylor3473f882001-02-23 17:55:21 +00002274}
2275
2276int main(int argc, char **argv) {
2277 void *ctxt;
2278 FILE *output;
2279 char *tstfile = NULL;
2280
2281 xmlNanoFTPInit();
2282 if (argc > 1) {
2283 ctxt = xmlNanoFTPNewCtxt(argv[1]);
2284 if (xmlNanoFTPConnect(ctxt) < 0) {
2285 xmlGenericError(xmlGenericErrorContext,
2286 "Couldn't connect to %s\n", argv[1]);
2287 exit(1);
2288 }
2289 if (argc > 2)
2290 tstfile = argv[2];
2291 } else
2292 ctxt = xmlNanoFTPConnectTo("localhost", 0);
2293 if (ctxt == NULL) {
2294 xmlGenericError(xmlGenericErrorContext,
2295 "Couldn't connect to localhost\n");
2296 exit(1);
2297 }
2298 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2299 output = fopen("/tmp/tstdata", "w");
2300 if (output != NULL) {
2301 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2302 xmlGenericError(xmlGenericErrorContext,
2303 "Failed to get file\n");
2304
2305 }
2306 xmlNanoFTPClose(ctxt);
2307 xmlMemoryDump();
2308 exit(0);
2309}
2310#endif /* STANDALONE */
2311#else /* !LIBXML_FTP_ENABLED */
2312#ifdef STANDALONE
2313#include <stdio.h>
2314int main(int argc, char **argv) {
2315 xmlGenericError(xmlGenericErrorContext,
2316 "%s : FTP support not compiled in\n", argv[0]);
2317 return(0);
2318}
2319#endif /* STANDALONE */
2320#endif /* LIBXML_FTP_ENABLED */