blob: 96fa272d46b5a020493e9333427e0b517150a59e [file] [log] [blame]
Daniel Veillard06047432000-04-24 11:33:38 +00001/*
Daniel Veillard49703262000-07-10 10:27:46 +00002 * nanoftp.c: basic FTP client support
Daniel Veillardda07c342000-01-25 18:31:22 +00003 *
4 * Reference: RFC 959
5 */
6
Daniel Veillard32bc74e2000-07-14 14:49:25 +00007#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
15#else /* STANDALONE */
Daniel Veillardda07c342000-01-25 18:31:22 +000016#ifdef WIN32
Daniel Veillardf341f932000-02-02 14:52:08 +000017#define INCLUDE_WINSOCK
Daniel Veillardda07c342000-01-25 18:31:22 +000018#include "win32config.h"
19#else
20#include "config.h"
21#endif
Daniel Veillard32bc74e2000-07-14 14:49:25 +000022#endif /* STANDALONE */
Daniel Veillard06047432000-04-24 11:33:38 +000023
Daniel Veillard361d8452000-04-03 19:48:13 +000024#include "xmlversion.h"
Daniel Veillardda07c342000-01-25 18:31:22 +000025
Daniel Veillard361d8452000-04-03 19:48:13 +000026#ifdef LIBXML_FTP_ENABLED
Daniel Veillardda07c342000-01-25 18:31:22 +000027#include <stdio.h>
28#include <string.h>
29
Daniel Veillard06047432000-04-24 11:33:38 +000030#ifdef HAVE_STDLIB_H
31#include <stdlib.h>
Daniel Veillardda07c342000-01-25 18:31:22 +000032#endif
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000036#ifdef HAVE_SYS_SOCKET_H
37#include <sys/socket.h>
38#endif
James Henstridgef3be9312000-01-28 13:59:21 +000039#ifdef HAVE_NETINET_IN_H
40#include <netinet/in.h>
41#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +000042#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000045#ifdef HAVE_NETDB_H
46#include <netdb.h>
47#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +000048#ifdef HAVE_FCNTL_H
49#include <fcntl.h>
50#endif
51#ifdef HAVE_ERRNO_H
52#include <errno.h>
53#endif
54#ifdef HAVE_SYS_TIME_H
55#include <sys/time.h>
56#endif
57#ifdef HAVE_SYS_SELECT_H
58#include <sys/select.h>
59#endif
Daniel Veillard5feb8492000-02-02 17:15:36 +000060#ifdef HAVE_STRINGS_H
61#include <strings.h>
62#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000063
Daniel Veillard361d8452000-04-03 19:48:13 +000064#include <libxml/xmlmemory.h>
65#include <libxml/nanoftp.h>
Daniel Veillardda07c342000-01-25 18:31:22 +000066
67/* #define DEBUG_FTP 1 */
68#ifdef STANDALONE
Daniel Veillarde41f2b72000-01-30 20:00:07 +000069#ifndef DEBUG_FTP
Daniel Veillardda07c342000-01-25 18:31:22 +000070#define DEBUG_FTP 1
71#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +000072#endif
Daniel Veillardda07c342000-01-25 18:31:22 +000073
74static char hostname[100];
75
76#define FTP_COMMAND_OK 200
77#define FTP_SYNTAX_ERROR 500
78#define FTP_GET_PASSWD 331
Daniel Veillard49703262000-07-10 10:27:46 +000079#define FTP_BUF_SIZE 512
Daniel Veillardda07c342000-01-25 18:31:22 +000080
81typedef struct xmlNanoFTPCtxt {
82 char *protocol; /* the protocol name */
83 char *hostname; /* the host name */
84 int port; /* the port */
85 char *path; /* the path within the URL */
86 char *user; /* user string */
87 char *passwd; /* passwd string */
88 struct sockaddr_in ftpAddr; /* the socket address struct */
89 int passive; /* currently we support only passive !!! */
90 int controlFd; /* the file descriptor for the control socket */
91 int dataFd; /* the file descriptor for the data socket */
92 int state; /* WRITE / READ / CLOSED */
93 int returnValue; /* the protocol return value */
Daniel Veillard49703262000-07-10 10:27:46 +000094 /* buffer for data received from the control connection */
95 char controlBuf[FTP_BUF_SIZE + 1];
96 int controlBufIndex;
97 int controlBufUsed;
98 int controlBufAnswer;
Daniel Veillardda07c342000-01-25 18:31:22 +000099} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
100
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000101static int initialized = 0;
102static char *proxy = NULL; /* the proxy name if any */
103static int proxyPort = 0; /* the proxy port if any */
104static char *proxyUser = NULL; /* user for proxy authentication */
105static char *proxyPasswd = NULL;/* passwd for proxy authentication */
106static int proxyType = 0; /* uses TYPE or a@b ? */
107
108/**
109 * xmlNanoFTPInit:
110 *
111 * Initialize the FTP protocol layer.
112 * Currently it just checks for proxy informations,
113 * and get the hostname
114 */
115
116void
117xmlNanoFTPInit(void) {
118 const char *env;
119
120 if (initialized)
121 return;
122
123 gethostname(hostname, sizeof(hostname));
124
125 proxyPort = 21;
126 env = getenv("no_proxy");
127 if (env != NULL)
128 return;
129 env = getenv("ftp_proxy");
130 if (env != NULL) {
131 xmlNanoFTPScanProxy(env);
132 } else {
133 env = getenv("FTP_PROXY");
134 if (env != NULL) {
135 xmlNanoFTPScanProxy(env);
136 }
137 }
138 env = getenv("ftp_proxy_user");
139 if (env != NULL) {
140 proxyUser = xmlMemStrdup(env);
141 }
142 env = getenv("ftp_proxy_password");
143 if (env != NULL) {
144 proxyPasswd = xmlMemStrdup(env);
145 }
146 initialized = 1;
147}
148
149/**
150 * xmlNanoFTPClenup:
151 *
152 * Cleanup the FTP protocol layer. This cleanup proxy informations.
153 */
154
155void
156xmlNanoFTPCleanup(void) {
157 if (proxy != NULL) {
158 xmlFree(proxy);
159 proxy = NULL;
160 }
161 if (proxyUser != NULL) {
162 xmlFree(proxyUser);
163 proxyUser = NULL;
164 }
165 if (proxyPasswd != NULL) {
166 xmlFree(proxyPasswd);
167 proxyPasswd = NULL;
168 }
169 hostname[0] = 0;
170 initialized = 0;
171 return;
172}
173
174/**
175 * xmlNanoFTPProxy:
176 * @host: the proxy host name
177 * @port: the proxy port
178 * @user: the proxy user name
179 * @passwd: the proxy password
180 * @type: the type of proxy 1 for using SITE, 2 for USER a@b
181 *
182 * Setup the FTP proxy informations.
183 * This can also be done by using ftp_proxy ftp_proxy_user and
184 * ftp_proxy_password environment variables.
185 */
186
187void
188xmlNanoFTPProxy(const char *host, int port, const char *user,
189 const char *passwd, int type) {
190 if (proxy != NULL)
191 xmlFree(proxy);
192 if (proxyUser != NULL)
193 xmlFree(proxyUser);
194 if (proxyPasswd != NULL)
195 xmlFree(proxyPasswd);
196 if (host)
197 proxy = xmlMemStrdup(host);
198 if (user)
199 proxyUser = xmlMemStrdup(user);
200 if (passwd)
201 proxyPasswd = xmlMemStrdup(passwd);
202 proxyPort = port;
203 proxyType = type;
204}
205
Daniel Veillardda07c342000-01-25 18:31:22 +0000206/**
207 * xmlNanoFTPScanURL:
208 * @ctx: an FTP context
209 * @URL: The URL used to initialize the context
210 *
211 * (Re)Initialize an FTP context by parsing the URL and finding
212 * the protocol host port and path it indicates.
213 */
214
215static void
216xmlNanoFTPScanURL(void *ctx, const char *URL) {
217 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
218 const char *cur = URL;
219 char buf[4096];
220 int index = 0;
221 int port = 0;
222
223 if (ctxt->protocol != NULL) {
224 xmlFree(ctxt->protocol);
225 ctxt->protocol = NULL;
226 }
227 if (ctxt->hostname != NULL) {
228 xmlFree(ctxt->hostname);
229 ctxt->hostname = NULL;
230 }
231 if (ctxt->path != NULL) {
232 xmlFree(ctxt->path);
233 ctxt->path = NULL;
234 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000235 if (URL == NULL) return;
Daniel Veillardda07c342000-01-25 18:31:22 +0000236 buf[index] = 0;
237 while (*cur != 0) {
238 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
239 buf[index] = 0;
240 ctxt->protocol = xmlMemStrdup(buf);
241 index = 0;
242 cur += 3;
243 break;
244 }
245 buf[index++] = *cur++;
246 }
247 if (*cur == 0) return;
248
249 buf[index] = 0;
250 while (1) {
251 if (cur[0] == ':') {
252 buf[index] = 0;
253 ctxt->hostname = xmlMemStrdup(buf);
254 index = 0;
255 cur += 1;
256 while ((*cur >= '0') && (*cur <= '9')) {
257 port *= 10;
258 port += *cur - '0';
259 cur++;
260 }
261 if (port != 0) ctxt->port = port;
262 while ((cur[0] != '/') && (*cur != 0))
263 cur++;
264 break;
265 }
266 if ((*cur == '/') || (*cur == 0)) {
267 buf[index] = 0;
268 ctxt->hostname = xmlMemStrdup(buf);
269 index = 0;
270 break;
271 }
272 buf[index++] = *cur++;
273 }
274 if (*cur == 0)
275 ctxt->path = xmlMemStrdup("/");
276 else {
Daniel Veillard726e8792000-01-30 20:04:29 +0000277 index = 0;
Daniel Veillardda07c342000-01-25 18:31:22 +0000278 buf[index] = 0;
Daniel Veillard726e8792000-01-30 20:04:29 +0000279 while (*cur != 0)
Daniel Veillardda07c342000-01-25 18:31:22 +0000280 buf[index++] = *cur++;
Daniel Veillardda07c342000-01-25 18:31:22 +0000281 buf[index] = 0;
282 ctxt->path = xmlMemStrdup(buf);
283 }
284}
285
286/**
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000287 * xmlNanoFTPUpdateURL:
288 * @ctx: an FTP context
289 * @URL: The URL used to update the context
290 *
291 * Update an FTP context by parsing the URL and finding
292 * new path it indicates. If there is an error in the
293 * protocol, hostname, port or other information, the
294 * error is raised. It indicates a new connection has to
295 * be established.
296 *
297 * Returns 0 if Ok, -1 in case of error (other host).
298 */
299
300int
301xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
302 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
303 const char *cur = URL;
304 char buf[4096];
305 int index = 0;
306 int port = 0;
307
308 if (URL == NULL)
309 return(-1);
310 if (ctxt == NULL)
311 return(-1);
312 if (ctxt->protocol == NULL)
313 return(-1);
314 if (ctxt->hostname == NULL)
315 return(-1);
316 buf[index] = 0;
317 while (*cur != 0) {
318 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
319 buf[index] = 0;
320 if (strcmp(ctxt->protocol, buf))
321 return(-1);
322 index = 0;
323 cur += 3;
324 break;
325 }
326 buf[index++] = *cur++;
327 }
328 if (*cur == 0)
329 return(-1);
330
331 buf[index] = 0;
332 while (1) {
333 if (cur[0] == ':') {
334 buf[index] = 0;
335 if (strcmp(ctxt->hostname, buf))
336 return(-1);
337 index = 0;
338 cur += 1;
339 while ((*cur >= '0') && (*cur <= '9')) {
340 port *= 10;
341 port += *cur - '0';
342 cur++;
343 }
344 if (port != ctxt->port)
345 return(-1);
346 while ((cur[0] != '/') && (*cur != 0))
347 cur++;
348 break;
349 }
350 if ((*cur == '/') || (*cur == 0)) {
351 buf[index] = 0;
352 if (strcmp(ctxt->hostname, buf))
353 return(-1);
354 index = 0;
355 break;
356 }
357 buf[index++] = *cur++;
358 }
359 if (ctxt->path != NULL) {
360 xmlFree(ctxt->path);
361 ctxt->path = NULL;
362 }
363
364 if (*cur == 0)
365 ctxt->path = xmlMemStrdup("/");
366 else {
Daniel Veillard726e8792000-01-30 20:04:29 +0000367 index = 0;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000368 buf[index] = 0;
Daniel Veillard726e8792000-01-30 20:04:29 +0000369 while (*cur != 0)
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000370 buf[index++] = *cur++;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000371 buf[index] = 0;
372 ctxt->path = xmlMemStrdup(buf);
373 }
374 return(0);
375}
376
377/**
378 * xmlNanoFTPScanProxy:
379 * @URL: The proxy URL used to initialize the proxy context
380 *
381 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
382 * the protocol host port it indicates.
383 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
384 * A NULL URL cleans up proxy informations.
385 */
386
387void
388xmlNanoFTPScanProxy(const char *URL) {
389 const char *cur = URL;
390 char buf[4096];
391 int index = 0;
392 int port = 0;
393
394 if (proxy != NULL) {
395 xmlFree(proxy);
396 proxy = NULL;
397 }
398 if (proxyPort != 0) {
399 proxyPort = 0;
400 }
401#ifdef DEBUG_FTP
402 if (URL == NULL)
403 printf("Removing FTP proxy info\n");
404 else
405 printf("Using FTP proxy %s\n", URL);
406#endif
407 if (URL == NULL) return;
408 buf[index] = 0;
409 while (*cur != 0) {
410 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
411 buf[index] = 0;
412 index = 0;
413 cur += 3;
414 break;
415 }
416 buf[index++] = *cur++;
417 }
418 if (*cur == 0) return;
419
420 buf[index] = 0;
421 while (1) {
422 if (cur[0] == ':') {
423 buf[index] = 0;
424 proxy = xmlMemStrdup(buf);
425 index = 0;
426 cur += 1;
427 while ((*cur >= '0') && (*cur <= '9')) {
428 port *= 10;
429 port += *cur - '0';
430 cur++;
431 }
432 if (port != 0) proxyPort = port;
433 while ((cur[0] != '/') && (*cur != 0))
434 cur++;
435 break;
436 }
437 if ((*cur == '/') || (*cur == 0)) {
438 buf[index] = 0;
439 proxy = xmlMemStrdup(buf);
440 index = 0;
441 break;
442 }
443 buf[index++] = *cur++;
444 }
445}
446
447/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000448 * xmlNanoFTPNewCtxt:
449 * @URL: The URL used to initialize the context
450 *
451 * Allocate and initialize a new FTP context.
452 *
453 * Returns an FTP context or NULL in case of error.
454 */
455
Daniel Veillard06047432000-04-24 11:33:38 +0000456void*
Daniel Veillardda07c342000-01-25 18:31:22 +0000457xmlNanoFTPNewCtxt(const char *URL) {
458 xmlNanoFTPCtxtPtr ret;
459
460 ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
461 if (ret == NULL) return(NULL);
462
463 memset(ret, 0, sizeof(xmlNanoFTPCtxt));
464 ret->port = 21;
465 ret->passive = 1;
466 ret->returnValue = 0;
Daniel Veillard49703262000-07-10 10:27:46 +0000467 ret->controlBufIndex = 0;
468 ret->controlBufUsed = 0;
Daniel Veillardda07c342000-01-25 18:31:22 +0000469
470 if (URL != NULL)
471 xmlNanoFTPScanURL(ret, URL);
472
473 return(ret);
474}
475
476/**
477 * xmlNanoFTPFreeCtxt:
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000478 * @ctx: an FTP context
Daniel Veillardda07c342000-01-25 18:31:22 +0000479 *
480 * Frees the context after closing the connection.
481 */
482
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000483void
484xmlNanoFTPFreeCtxt(void * ctx) {
485 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
Daniel Veillardda07c342000-01-25 18:31:22 +0000486 if (ctxt == NULL) return;
487 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
488 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
489 if (ctxt->path != NULL) xmlFree(ctxt->path);
490 ctxt->passive = 1;
491 if (ctxt->controlFd >= 0) close(ctxt->controlFd);
492 ctxt->controlFd = -1;
Daniel Veillard49703262000-07-10 10:27:46 +0000493 ctxt->controlBufIndex = -1;
494 ctxt->controlBufUsed = -1;
Daniel Veillardda07c342000-01-25 18:31:22 +0000495 xmlFree(ctxt);
496}
497
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000498/**
Daniel Veillard06047432000-04-24 11:33:38 +0000499 * xmlNanoFTPParseResponse:
500 * @ctx: the FTP connection context
501 * @buf: the buffer containing the response
502 * @len: the buffer length
503 *
Daniel Veillardda07c342000-01-25 18:31:22 +0000504 * Parsing of the server answer, we just extract the code.
Daniel Veillard06047432000-04-24 11:33:38 +0000505 *
506 * returns 0 for errors
Daniel Veillardda07c342000-01-25 18:31:22 +0000507 * +XXX for last line of response
508 * -XXX for response to be continued
509 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000510static int
Daniel Veillardda07c342000-01-25 18:31:22 +0000511xmlNanoFTPParseResponse(void *ctx, char *buf, int len) {
512 int val = 0;
513
514 if (len < 3) return(-1);
515 if ((*buf >= '0') && (*buf <= '9'))
516 val = val * 10 + (*buf - '0');
517 else
518 return(0);
519 buf++;
520 if ((*buf >= '0') && (*buf <= '9'))
521 val = val * 10 + (*buf - '0');
522 else
523 return(0);
524 buf++;
525 if ((*buf >= '0') && (*buf <= '9'))
526 val = val * 10 + (*buf - '0');
527 else
528 return(0);
529 buf++;
530 if (*buf == '-')
531 return(-val);
532 return(val);
533}
534
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000535/**
Daniel Veillard49703262000-07-10 10:27:46 +0000536 * xmlNanoFTPGetMore:
537 * @ctx: an FTP context
538 *
539 * Read more information from the FTP control connection
540 * Returns the number of bytes read, < 0 indicates an error
541 */
542static int
543xmlNanoFTPGetMore(void *ctx) {
544 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
545 int len;
546 int size;
547
548 if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
549#ifdef DEBUG_FTP
550 printf("xmlNanoFTPGetMore : controlBufIndex = %d\n",
551 ctxt->controlBufIndex);
552#endif
553 return(-1);
554 }
555
556 if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
557#ifdef DEBUG_FTP
558 printf("xmlNanoFTPGetMore : controlBufUsed = %d\n",
559 ctxt->controlBufUsed);
560#endif
561 return(-1);
562 }
563 if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
564#ifdef DEBUG_FTP
565 printf("xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
566 ctxt->controlBufIndex, ctxt->controlBufUsed);
567#endif
568 return(-1);
569 }
570
571 /*
572 * First pack the control buffer
573 */
574 if (ctxt->controlBufIndex > 0) {
575 memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
576 ctxt->controlBufUsed - ctxt->controlBufIndex);
577 ctxt->controlBufUsed -= ctxt->controlBufIndex;
578 ctxt->controlBufIndex = 0;
579 }
580 size = FTP_BUF_SIZE - ctxt->controlBufUsed;
581 if (size == 0) {
582#ifdef DEBUG_FTP
583 printf("xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
584#endif
585 return(0);
586 }
587
588 /*
589 * Read the amount left on teh control connection
590 */
591 if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
592 size, 0)) < 0) {
593 close(ctxt->controlFd); ctxt->controlFd = -1;
594 ctxt->controlFd = -1;
595 return(-1);
596 }
597#ifdef DEBUG_FTP
598 printf("xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
599 ctxt->controlBufUsed, ctxt->controlBufUsed + len);
600#endif
601 ctxt->controlBufUsed += len;
602 ctxt->controlBuf[ctxt->controlBufUsed] = 0;
603
604 return(len);
605}
606
607/**
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000608 * xmlNanoFTPReadResponse:
609 * @ctx: an FTP context
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000610 *
Daniel Veillardda07c342000-01-25 18:31:22 +0000611 * Read the response from the FTP server after a command.
612 * Returns the code number
Daniel Veillardda07c342000-01-25 18:31:22 +0000613 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000614static int
Daniel Veillard49703262000-07-10 10:27:46 +0000615xmlNanoFTPReadResponse(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000616 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
617 char *ptr, *end;
618 int len;
Daniel Veillard49703262000-07-10 10:27:46 +0000619 int res = -1, cur = -1;
Daniel Veillardda07c342000-01-25 18:31:22 +0000620
621get_more:
Daniel Veillard49703262000-07-10 10:27:46 +0000622 /*
623 * Assumes everything up to controlBuf[controlBufIndex] has been read
624 * and analyzed.
625 */
626 len = xmlNanoFTPGetMore(ctx);
627 if ((ctxt->controlBufUsed == 0) && (len == 0)) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000628 return(-1);
629 }
Daniel Veillard49703262000-07-10 10:27:46 +0000630 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
631 end = &ctxt->controlBuf[ctxt->controlBufUsed];
Daniel Veillardda07c342000-01-25 18:31:22 +0000632
Daniel Veillardda07c342000-01-25 18:31:22 +0000633#ifdef DEBUG_FTP
Daniel Veillard49703262000-07-10 10:27:46 +0000634 printf("\n<<<\n%s\n--\n", ptr);
Daniel Veillardda07c342000-01-25 18:31:22 +0000635#endif
Daniel Veillardda07c342000-01-25 18:31:22 +0000636 while (ptr < end) {
Daniel Veillard49703262000-07-10 10:27:46 +0000637 cur = xmlNanoFTPParseResponse(ctxt, ptr, end - ptr);
638 if (cur > 0) {
639 /*
640 * Successfully scanned the control code, scratch
641 * till the end of the line, but keep the index to be
642 * able to analyze the result if needed.
643 */
644 res = cur;
645 ptr += 3;
646 ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
647 while ((ptr < end) && (*ptr != '\n')) ptr++;
648 if (*ptr == '\n') ptr++;
649 if (*ptr == '\r') ptr++;
650 break;
Daniel Veillardda07c342000-01-25 18:31:22 +0000651 }
652 while ((ptr < end) && (*ptr != '\n')) ptr++;
653 if (ptr >= end) {
Daniel Veillard49703262000-07-10 10:27:46 +0000654 ctxt->controlBufIndex = ctxt->controlBufUsed;
655 goto get_more;
Daniel Veillardda07c342000-01-25 18:31:22 +0000656 }
657 if (*ptr != '\r') ptr++;
658 }
659
660 if (res < 0) goto get_more;
Daniel Veillard49703262000-07-10 10:27:46 +0000661 ctxt->controlBufIndex = ptr - ctxt->controlBuf;
662#ifdef DEBUG_FTP
663 ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
664 printf("\n---\n%s\n--\n", ptr);
665#endif
Daniel Veillardda07c342000-01-25 18:31:22 +0000666
667#ifdef DEBUG_FTP
668 printf("Got %d\n", res);
669#endif
670 return(res / 100);
671}
672
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000673/**
674 * xmlNanoFTPGetResponse:
675 * @ctx: an FTP context
676 *
Daniel Veillardda07c342000-01-25 18:31:22 +0000677 * Get the response from the FTP server after a command.
678 * Returns the code number
Daniel Veillardda07c342000-01-25 18:31:22 +0000679 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000680
Daniel Veillardda07c342000-01-25 18:31:22 +0000681int
682xmlNanoFTPGetResponse(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000683 int res;
684
Daniel Veillard49703262000-07-10 10:27:46 +0000685 res = xmlNanoFTPReadResponse(ctx);
Daniel Veillardda07c342000-01-25 18:31:22 +0000686
Daniel Veillard49703262000-07-10 10:27:46 +0000687 return(res);
Daniel Veillardda07c342000-01-25 18:31:22 +0000688}
689
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000690/**
691 * xmlNanoFTPCheckResponse:
692 * @ctx: an FTP context
693 *
Daniel Veillardda07c342000-01-25 18:31:22 +0000694 * Check if there is a response from the FTP server after a command.
695 * Returns the code number, or 0
696 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000697
Daniel Veillardda07c342000-01-25 18:31:22 +0000698int
699xmlNanoFTPCheckResponse(void *ctx) {
700 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
Daniel Veillardda07c342000-01-25 18:31:22 +0000701 fd_set rfd;
702 struct timeval tv;
703
704 tv.tv_sec = 0;
705 tv.tv_usec = 0;
706 FD_ZERO(&rfd);
707 FD_SET(ctxt->controlFd, &rfd);
708 switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
709 case 0:
710 return(0);
711 case -1:
712#ifdef DEBUG_FTP
713 perror("select");
714#endif
715 return(-1);
716
717 }
718
Daniel Veillard49703262000-07-10 10:27:46 +0000719 return(xmlNanoFTPReadResponse(ctx));
Daniel Veillardda07c342000-01-25 18:31:22 +0000720}
721
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000722/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000723 * Send the user authentification
724 */
725
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000726static int
727xmlNanoFTPSendUser(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000728 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
729 char buf[200];
730 int len;
731 int res;
732
733 if (ctxt->user == NULL)
Daniel Veillard13c757e2000-02-01 23:59:15 +0000734#ifndef HAVE_SNPRINTF
735 len = sprintf(buf, "USER anonymous\r\n");
736#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000737 len = snprintf(buf, sizeof(buf), "USER anonymous\r\n");
Daniel Veillard13c757e2000-02-01 23:59:15 +0000738#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000739 else
Daniel Veillard13c757e2000-02-01 23:59:15 +0000740#ifndef HAVE_SNPRINTF
741 len = sprintf(buf, "USER %s\r\n", ctxt->user);
742#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000743 len = snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
Daniel Veillard13c757e2000-02-01 23:59:15 +0000744#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000745#ifdef DEBUG_FTP
746 printf(buf);
747#endif
748 res = send(ctxt->controlFd, buf, len, 0);
749 if (res < 0) return(res);
750 return(0);
751}
752
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000753/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000754 * Send the password authentification
755 */
756
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000757static int
758xmlNanoFTPSendPasswd(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000759 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
760 char buf[200];
761 int len;
762 int res;
763
764 if (ctxt->passwd == NULL)
Daniel Veillard13c757e2000-02-01 23:59:15 +0000765#ifndef HAVE_SNPRINTF
766 len = sprintf(buf, "PASS libxml@%s\r\n", hostname);
767#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000768 len = snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
Daniel Veillard13c757e2000-02-01 23:59:15 +0000769#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000770 else
Daniel Veillard13c757e2000-02-01 23:59:15 +0000771#ifndef HAVE_SNPRINTF
772 len = sprintf(buf, "PASS %s\r\n", ctxt->passwd);
773#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000774 len = snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Daniel Veillard13c757e2000-02-01 23:59:15 +0000775#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000776#ifdef DEBUG_FTP
777 printf(buf);
778#endif
779 res = send(ctxt->controlFd, buf, len, 0);
780 if (res < 0) return(res);
781 return(0);
782}
783
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000784/**
785 * xmlNanoFTPQuit:
786 * @ctx: an FTP context
787 *
788 * Send a QUIT command to the server
789 *
790 * Returns -1 in case of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +0000791 */
792
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000793
Daniel Veillardda07c342000-01-25 18:31:22 +0000794int
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000795xmlNanoFTPQuit(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000796 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
797 char buf[200];
798 int len;
799 int res;
800
Daniel Veillard13c757e2000-02-01 23:59:15 +0000801#ifndef HAVE_SNPRINTF
802 len = sprintf(buf, "QUIT\r\n");
803#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000804 len = snprintf(buf, sizeof(buf), "QUIT\r\n");
Daniel Veillard13c757e2000-02-01 23:59:15 +0000805#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +0000806#ifdef DEBUG_FTP
807 printf(buf);
808#endif
809 res = send(ctxt->controlFd, buf, len, 0);
810 return(0);
811}
812
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000813/**
814 * xmlNanoFTPConnect:
815 * @ctx: an FTP context
816 *
817 * Tries to open a control connection
818 *
819 * Returns -1 in case of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +0000820 */
821
822int
823xmlNanoFTPConnect(void *ctx) {
824 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
825 struct hostent *hp;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000826 int port;
Daniel Veillardda07c342000-01-25 18:31:22 +0000827 int res;
828
829 if (ctxt == NULL)
830 return(-1);
831 if (ctxt->hostname == NULL)
832 return(-1);
833
834 /*
835 * do the blocking DNS query.
836 */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000837 if (proxy)
838 hp = gethostbyname(proxy);
839 else
840 hp = gethostbyname(ctxt->hostname);
Daniel Veillardda07c342000-01-25 18:31:22 +0000841 if (hp == NULL)
842 return(-1);
843
844 /*
845 * Prepare the socket
846 */
847 memset(&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
848 ctxt->ftpAddr.sin_family = AF_INET;
849 memcpy(&ctxt->ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000850 if (proxy) {
851 port = proxyPort;
852 } else {
853 port = ctxt->port;
854 }
855 if (port == 0)
856 port = 21;
857 ctxt->ftpAddr.sin_port = htons(port);
Daniel Veillardda07c342000-01-25 18:31:22 +0000858 ctxt->controlFd = socket(AF_INET, SOCK_STREAM, 0);
859 if (ctxt->controlFd < 0)
860 return(-1);
861
862 /*
863 * Do the connect.
864 */
865 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
866 sizeof(struct sockaddr_in)) < 0) {
867 close(ctxt->controlFd); ctxt->controlFd = -1;
868 ctxt->controlFd = -1;
869 return(-1);
870 }
871
872 /*
873 * Wait for the HELLO from the server.
874 */
875 res = xmlNanoFTPGetResponse(ctxt);
876 if (res != 2) {
877 close(ctxt->controlFd); ctxt->controlFd = -1;
878 ctxt->controlFd = -1;
879 return(-1);
880 }
881
882 /*
883 * State diagram for the login operation on the FTP server
884 *
885 * Reference: RFC 959
886 *
887 * 1
888 * +---+ USER +---+------------->+---+
889 * | B |---------->| W | 2 ---->| E |
890 * +---+ +---+------ | -->+---+
891 * | | | | |
892 * 3 | | 4,5 | | |
893 * -------------- ----- | | |
894 * | | | | |
895 * | | | | |
896 * | --------- |
897 * | 1| | | |
898 * V | | | |
899 * +---+ PASS +---+ 2 | ------>+---+
900 * | |---------->| W |------------->| S |
901 * +---+ +---+ ---------->+---+
902 * | | | | |
903 * 3 | |4,5| | |
904 * -------------- -------- |
905 * | | | | |
906 * | | | | |
907 * | -----------
908 * | 1,3| | | |
909 * V | 2| | |
910 * +---+ ACCT +---+-- | ----->+---+
911 * | |---------->| W | 4,5 -------->| F |
912 * +---+ +---+------------->+---+
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000913 *
914 * Of course in case of using a proxy this get really nasty and is not
915 * standardized at all :-(
916 */
917 if (proxy) {
918 int len;
919 char buf[400];
920
921 if (proxyUser != NULL) {
922 /*
923 * We need proxy auth
924 */
Daniel Veillard13c757e2000-02-01 23:59:15 +0000925#ifndef HAVE_SNPRINTF
926 len = sprintf(buf, "USER %s\r\n", proxyUser);
927#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000928 len = snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
Daniel Veillard13c757e2000-02-01 23:59:15 +0000929#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000930#ifdef DEBUG_FTP
931 printf(buf);
932#endif
933 res = send(ctxt->controlFd, buf, len, 0);
934 if (res < 0) {
935 close(ctxt->controlFd);
936 ctxt->controlFd = -1;
937 return(res);
938 }
939 res = xmlNanoFTPGetResponse(ctxt);
940 switch (res) {
941 case 2:
942 if (proxyPasswd == NULL)
943 break;
944 case 3:
945 if (proxyPasswd != NULL)
Daniel Veillard13c757e2000-02-01 23:59:15 +0000946#ifndef HAVE_SNPRINTF
947 len = sprintf(buf, "PASS %s\r\n", proxyPasswd);
948#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000949 len = snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
Daniel Veillard13c757e2000-02-01 23:59:15 +0000950#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000951 else
Daniel Veillard13c757e2000-02-01 23:59:15 +0000952#ifndef HAVE_SNPRINTF
953 len = sprintf(buf, "PASS libxml@%s\r\n",
Daniel Veillardcf461992000-03-14 18:30:20 +0000954 hostname);
Daniel Veillard13c757e2000-02-01 23:59:15 +0000955#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000956 len = snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n",
957 hostname);
Daniel Veillardcf461992000-03-14 18:30:20 +0000958#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000959#ifdef DEBUG_FTP
960 printf(buf);
961#endif
962 res = send(ctxt->controlFd, buf, len, 0);
963 if (res < 0) {
964 close(ctxt->controlFd);
965 ctxt->controlFd = -1;
966 return(res);
967 }
968 res = xmlNanoFTPGetResponse(ctxt);
969 if (res > 3) {
970 close(ctxt->controlFd);
971 ctxt->controlFd = -1;
972 return(-1);
973 }
974 break;
975 case 1:
976 break;
977 case 4:
978 case 5:
979 case -1:
980 default:
981 close(ctxt->controlFd);
982 ctxt->controlFd = -1;
983 return(-1);
984 }
985 }
986
987 /*
988 * We assume we don't need more authentication to the proxy
989 * and that it succeeded :-\
990 */
991 switch (proxyType) {
992 case 0:
993 /* we will try in seqence */
994 case 1:
995 /* Using SITE command */
Daniel Veillard13c757e2000-02-01 23:59:15 +0000996#ifndef HAVE_SNPRINTF
997 len = sprintf(buf, "SITE %s\r\n", ctxt->hostname);
998#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000999 len = snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001000#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001001#ifdef DEBUG_FTP
1002 printf(buf);
1003#endif
1004 res = send(ctxt->controlFd, buf, len, 0);
1005 if (res < 0) {
1006 close(ctxt->controlFd); ctxt->controlFd = -1;
1007 ctxt->controlFd = -1;
1008 return(res);
1009 }
1010 res = xmlNanoFTPGetResponse(ctxt);
1011 if (res == 2) {
1012 /* we assume it worked :-\ 1 is error for SITE command */
1013 proxyType = 1;
1014 break;
1015 }
1016 if (proxyType == 1) {
1017 close(ctxt->controlFd); ctxt->controlFd = -1;
1018 ctxt->controlFd = -1;
1019 return(-1);
1020 }
1021 case 2:
1022 /* USER user@host command */
1023 if (ctxt->user == NULL)
Daniel Veillard13c757e2000-02-01 23:59:15 +00001024#ifndef HAVE_SNPRINTF
1025 len = sprintf(buf, "USER anonymous@%s\r\n",
1026#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001027 len = snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
Daniel Veillard13c757e2000-02-01 23:59:15 +00001028#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001029 ctxt->hostname);
1030 else
Daniel Veillard13c757e2000-02-01 23:59:15 +00001031#ifndef HAVE_SNPRINTF
1032 len = sprintf(buf, "USER %s@%s\r\n",
1033#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001034 len = snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
Daniel Veillard13c757e2000-02-01 23:59:15 +00001035#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001036 ctxt->user, ctxt->hostname);
1037#ifdef DEBUG_FTP
1038 printf(buf);
1039#endif
1040 res = send(ctxt->controlFd, buf, len, 0);
1041 if (res < 0) {
1042 close(ctxt->controlFd); ctxt->controlFd = -1;
1043 ctxt->controlFd = -1;
1044 return(res);
1045 }
1046 res = xmlNanoFTPGetResponse(ctxt);
1047 if ((res == 1) || (res == 2)) {
1048 /* we assume it worked :-\ */
1049 proxyType = 2;
1050 return(0);
1051 }
1052 if (ctxt->passwd == NULL)
Daniel Veillard13c757e2000-02-01 23:59:15 +00001053#ifndef HAVE_SNPRINTF
1054 len = sprintf(buf, "PASS libxml@%s\r\n", hostname);
1055#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001056 len = snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001057#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001058 else
Daniel Veillard13c757e2000-02-01 23:59:15 +00001059#ifndef HAVE_SNPRINTF
1060 len = sprintf(buf, "PASS %s\r\n", ctxt->passwd);
1061#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001062 len = snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001063#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001064#ifdef DEBUG_FTP
1065 printf(buf);
1066#endif
1067 res = send(ctxt->controlFd, buf, len, 0);
1068 if (res < 0) {
1069 close(ctxt->controlFd); ctxt->controlFd = -1;
1070 ctxt->controlFd = -1;
1071 return(res);
1072 }
1073 res = xmlNanoFTPGetResponse(ctxt);
1074 if ((res == 1) || (res == 2)) {
1075 /* we assume it worked :-\ */
1076 proxyType = 2;
1077 return(0);
1078 }
1079 if (proxyType == 2) {
1080 close(ctxt->controlFd); ctxt->controlFd = -1;
1081 ctxt->controlFd = -1;
1082 return(-1);
1083 }
1084 case 3:
1085 /*
1086 * If you need support for other Proxy authentication scheme
1087 * send the code or at least the sequence in use.
1088 */
1089 default:
1090 close(ctxt->controlFd); ctxt->controlFd = -1;
1091 ctxt->controlFd = -1;
1092 return(-1);
1093 }
1094 }
1095 /*
1096 * Non-proxy handling.
Daniel Veillardda07c342000-01-25 18:31:22 +00001097 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001098 res = xmlNanoFTPSendUser(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001099 if (res < 0) {
1100 close(ctxt->controlFd); ctxt->controlFd = -1;
1101 ctxt->controlFd = -1;
1102 return(-1);
1103 }
1104 res = xmlNanoFTPGetResponse(ctxt);
1105 switch (res) {
1106 case 2:
1107 return(0);
1108 case 3:
1109 break;
1110 case 1:
1111 case 4:
1112 case 5:
1113 case -1:
1114 default:
1115 close(ctxt->controlFd); ctxt->controlFd = -1;
1116 ctxt->controlFd = -1;
1117 return(-1);
1118 }
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001119 res = xmlNanoFTPSendPasswd(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001120 if (res < 0) {
1121 close(ctxt->controlFd); ctxt->controlFd = -1;
1122 ctxt->controlFd = -1;
1123 return(-1);
1124 }
1125 res = xmlNanoFTPGetResponse(ctxt);
1126 switch (res) {
1127 case 2:
Daniel Veillard5feb8492000-02-02 17:15:36 +00001128 break;
Daniel Veillardda07c342000-01-25 18:31:22 +00001129 case 3:
1130 fprintf(stderr, "FTP server asking for ACCNT on anonymous\n");
1131 case 1:
1132 case 4:
1133 case 5:
1134 case -1:
1135 default:
1136 close(ctxt->controlFd); ctxt->controlFd = -1;
1137 ctxt->controlFd = -1;
1138 return(-1);
1139 }
1140
1141 return(0);
1142}
1143
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001144/**
1145 * xmlNanoFTPConnectTo:
1146 * @server: an FTP server name
Daniel Veillard06047432000-04-24 11:33:38 +00001147 * @port: the port (use 21 if 0)
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001148 *
1149 * Tries to open a control connection to the given server/port
1150 *
Daniel Veillard06047432000-04-24 11:33:38 +00001151 * Returns an fTP context or NULL if it failed
Daniel Veillardda07c342000-01-25 18:31:22 +00001152 */
1153
Daniel Veillard06047432000-04-24 11:33:38 +00001154void*
Daniel Veillardda07c342000-01-25 18:31:22 +00001155xmlNanoFTPConnectTo(const char *server, int port) {
1156 xmlNanoFTPCtxtPtr ctxt;
1157 int res;
1158
1159 xmlNanoFTPInit();
1160 if (server == NULL)
1161 return(NULL);
Daniel Veillard49703262000-07-10 10:27:46 +00001162 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
Daniel Veillardda07c342000-01-25 18:31:22 +00001163 ctxt->hostname = xmlMemStrdup(server);
1164 if (port != 0)
1165 ctxt->port = port;
1166 res = xmlNanoFTPConnect(ctxt);
1167 if (res < 0) {
1168 xmlNanoFTPFreeCtxt(ctxt);
1169 return(NULL);
1170 }
1171 return(ctxt);
1172}
1173
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001174/**
Daniel Veillard49703262000-07-10 10:27:46 +00001175 * xmlNanoFTPCwd:
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001176 * @ctx: an FTP context
1177 * @directory: a directory on the server
1178 *
1179 * Tries to change the remote directory
1180 *
1181 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
Daniel Veillardda07c342000-01-25 18:31:22 +00001182 */
1183
1184int
1185xmlNanoFTPCwd(void *ctx, char *directory) {
1186 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1187 char buf[400];
1188 int len;
1189 int res;
1190
1191 /*
1192 * Expected response code for CWD:
1193 *
1194 * CWD
1195 * 250
1196 * 500, 501, 502, 421, 530, 550
1197 */
Daniel Veillard13c757e2000-02-01 23:59:15 +00001198#ifndef HAVE_SNPRINTF
1199 len = sprintf(buf, "CWD %s\r\n", directory);
1200#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001201 len = snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001202#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001203#ifdef DEBUG_FTP
1204 printf(buf);
1205#endif
1206 res = send(ctxt->controlFd, buf, len, 0);
1207 if (res < 0) return(res);
1208 res = xmlNanoFTPGetResponse(ctxt);
1209 if (res == 4) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001210 return(-1);
1211 }
1212 if (res == 2) return(1);
1213 if (res == 5) {
1214 return(0);
1215 }
1216 return(0);
1217}
1218
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001219/**
1220 * xmlNanoFTPGetConnection:
1221 * @ctx: an FTP context
1222 *
1223 * Try to open a data connection to the server. Currently only
1224 * passive mode is supported.
1225 *
1226 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001227 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001228
Daniel Veillardda07c342000-01-25 18:31:22 +00001229int
1230xmlNanoFTPGetConnection(void *ctx) {
1231 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1232 char buf[200], *cur;
1233 int len, i;
1234 int res;
1235 unsigned char ad[6], *adp, *portp;
1236 unsigned int temp[6];
1237 struct sockaddr_in dataAddr;
1238 size_t dataAddrLen;
1239
1240 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1241 if (ctxt->dataFd < 0) {
1242 fprintf(stderr, "xmlNanoFTPGetConnection: failed to create socket\n");
1243 }
1244 dataAddrLen = sizeof(dataAddr);
1245 memset(&dataAddr, 0, dataAddrLen);
1246 dataAddr.sin_family = AF_INET;
1247
1248 if (ctxt->passive) {
Daniel Veillard13c757e2000-02-01 23:59:15 +00001249#ifndef HAVE_SNPRINTF
1250 len = sprintf(buf, "PASV\r\n");
1251#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001252 len = snprintf(buf, sizeof(buf), "PASV\r\n");
Daniel Veillard13c757e2000-02-01 23:59:15 +00001253#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001254#ifdef DEBUG_FTP
1255 printf(buf);
1256#endif
1257 res = send(ctxt->controlFd, buf, len, 0);
1258 if (res < 0) {
1259 close(ctxt->dataFd); ctxt->dataFd = -1;
1260 return(res);
1261 }
Daniel Veillard49703262000-07-10 10:27:46 +00001262 res = xmlNanoFTPReadResponse(ctx);
Daniel Veillardda07c342000-01-25 18:31:22 +00001263 if (res != 2) {
1264 if (res == 5) {
1265 close(ctxt->dataFd); ctxt->dataFd = -1;
1266 return(-1);
1267 } else {
1268 /*
1269 * retry with an active connection
1270 */
1271 close(ctxt->dataFd); ctxt->dataFd = -1;
1272 ctxt->passive = 0;
1273 }
1274 }
Daniel Veillard49703262000-07-10 10:27:46 +00001275 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
Daniel Veillardda07c342000-01-25 18:31:22 +00001276 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1277 if (sscanf(cur, "%d,%d,%d,%d,%d,%d", &temp[0], &temp[1], &temp[2],
1278 &temp[3], &temp[4], &temp[5]) != 6) {
1279 fprintf(stderr, "Invalid answer to PASV\n");
Daniel Veillardbe803962000-06-28 23:40:59 +00001280 if (ctxt->dataFd != -1) {
1281 close(ctxt->dataFd); ctxt->dataFd = -1;
1282 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001283 return(-1);
1284 }
1285 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1286 memcpy(&dataAddr.sin_addr, &ad[0], 4);
1287 memcpy(&dataAddr.sin_port, &ad[4], 2);
1288 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1289 fprintf(stderr, "Failed to create a data connection\n");
1290 close(ctxt->dataFd); ctxt->dataFd = -1;
1291 return (-1);
1292 }
1293 } else {
1294 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1295 dataAddr.sin_port = 0;
1296 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1297 fprintf(stderr, "Failed to bind a port\n");
1298 close(ctxt->dataFd); ctxt->dataFd = -1;
1299 return (-1);
1300 }
1301 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1302
1303 if (listen(ctxt->dataFd, 1) < 0) {
1304 fprintf(stderr, "Could not listen on port %d\n",
1305 ntohs(dataAddr.sin_port));
1306 close(ctxt->dataFd); ctxt->dataFd = -1;
1307 return (-1);
1308 }
1309 adp = (unsigned char *) &dataAddr.sin_addr;
1310 portp = (unsigned char *) &dataAddr.sin_port;
Daniel Veillard13c757e2000-02-01 23:59:15 +00001311#ifndef HAVE_SNPRINTF
1312 len = sprintf(buf, "PORT %d,%d,%d,%d,%d,%d\r\n",
Daniel Veillardcf461992000-03-14 18:30:20 +00001313 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1314 portp[0] & 0xff, portp[1] & 0xff);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001315#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001316 len = snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
Daniel Veillardcf461992000-03-14 18:30:20 +00001317 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1318 portp[0] & 0xff, portp[1] & 0xff);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001319#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001320 buf[sizeof(buf) - 1] = 0;
1321#ifdef DEBUG_FTP
1322 printf(buf);
1323#endif
1324
1325 res = send(ctxt->controlFd, buf, len, 0);
1326 if (res < 0) {
1327 close(ctxt->dataFd); ctxt->dataFd = -1;
1328 return(res);
1329 }
1330 res = xmlNanoFTPGetResponse(ctxt);
1331 if (res != 2) {
1332 close(ctxt->dataFd); ctxt->dataFd = -1;
1333 return(-1);
1334 }
1335 }
1336 return(ctxt->dataFd);
1337
1338}
1339
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001340/**
1341 * xmlNanoFTPCloseConnection:
1342 * @ctx: an FTP context
1343 *
1344 * Close the data connection from the server
1345 *
1346 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001347 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001348
Daniel Veillardda07c342000-01-25 18:31:22 +00001349int
1350xmlNanoFTPCloseConnection(void *ctx) {
1351 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1352 int res;
Daniel Veillardcf461992000-03-14 18:30:20 +00001353 fd_set rfd, efd;
1354 struct timeval tv;
Daniel Veillardda07c342000-01-25 18:31:22 +00001355
1356 close(ctxt->dataFd); ctxt->dataFd = -1;
Daniel Veillardcf461992000-03-14 18:30:20 +00001357 tv.tv_sec = 15;
1358 tv.tv_usec = 0;
1359 FD_ZERO(&rfd);
1360 FD_SET(ctxt->controlFd, &rfd);
1361 FD_ZERO(&efd);
1362 FD_SET(ctxt->controlFd, &efd);
1363 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1364 if (res < 0) {
1365#ifdef DEBUG_FTP
1366 perror("select");
1367#endif
Daniel Veillardda07c342000-01-25 18:31:22 +00001368 close(ctxt->controlFd); ctxt->controlFd = -1;
1369 return(-1);
1370 }
Daniel Veillardcf461992000-03-14 18:30:20 +00001371 if (res == 0) {
1372 fprintf(stderr, "xmlNanoFTPCloseConnection: timeout\n");
1373 close(ctxt->controlFd); ctxt->controlFd = -1;
1374 } else {
1375 res = xmlNanoFTPGetResponse(ctxt);
1376 if (res != 2) {
1377 close(ctxt->controlFd); ctxt->controlFd = -1;
1378 return(-1);
1379 }
1380 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001381 return(0);
1382}
1383
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001384/**
1385 * xmlNanoFTPParseList:
1386 * @list: some data listing received from the server
1387 * @callback: the user callback
1388 * @userData: the user callback data
1389 *
1390 * Parse at most one entry from the listing.
1391 *
1392 * Returns -1 incase of error, the lenght of data parsed otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001393 */
1394
1395static int
1396xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1397 const char *cur = list;
1398 char filename[151];
1399 char attrib[11];
1400 char owner[11];
1401 char group[11];
1402 char month[4];
1403 int year = 0;
1404 int minute = 0;
1405 int hour = 0;
1406 int day = 0;
1407 unsigned long size = 0;
1408 int links = 0;
1409 int i;
1410
1411 if (!strncmp(cur, "total", 5)) {
1412 cur += 5;
1413 while (*cur == ' ') cur++;
1414 while ((*cur >= '0') && (*cur <= '9'))
1415 links = (links * 10) + (*cur++ - '0');
1416 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1417 cur++;
1418 return(cur - list);
1419 } else if (*list == '+') {
1420 return(0);
1421 } else {
1422 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1423 cur++;
1424 if (*cur == 0) return(0);
1425 i = 0;
1426 while (*cur != ' ') {
1427 if (i < 10)
1428 attrib[i++] = *cur;
1429 cur++;
1430 if (*cur == 0) return(0);
1431 }
1432 attrib[10] = 0;
1433 while (*cur == ' ') cur++;
1434 if (*cur == 0) return(0);
1435 while ((*cur >= '0') && (*cur <= '9'))
1436 links = (links * 10) + (*cur++ - '0');
1437 while (*cur == ' ') cur++;
1438 if (*cur == 0) return(0);
1439 i = 0;
1440 while (*cur != ' ') {
1441 if (i < 10)
1442 owner[i++] = *cur;
1443 cur++;
1444 if (*cur == 0) return(0);
1445 }
1446 owner[i] = 0;
1447 while (*cur == ' ') cur++;
1448 if (*cur == 0) return(0);
1449 i = 0;
1450 while (*cur != ' ') {
1451 if (i < 10)
1452 group[i++] = *cur;
1453 cur++;
1454 if (*cur == 0) return(0);
1455 }
1456 group[i] = 0;
1457 while (*cur == ' ') cur++;
1458 if (*cur == 0) return(0);
1459 while ((*cur >= '0') && (*cur <= '9'))
1460 size = (size * 10) + (*cur++ - '0');
1461 while (*cur == ' ') cur++;
1462 if (*cur == 0) return(0);
1463 i = 0;
1464 while (*cur != ' ') {
1465 if (i < 3)
1466 month[i++] = *cur;
1467 cur++;
1468 if (*cur == 0) return(0);
1469 }
1470 month[i] = 0;
1471 while (*cur == ' ') cur++;
1472 if (*cur == 0) return(0);
1473 while ((*cur >= '0') && (*cur <= '9'))
1474 day = (day * 10) + (*cur++ - '0');
1475 while (*cur == ' ') cur++;
1476 if (*cur == 0) return(0);
1477 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1478 if ((cur[1] == ':') || (cur[2] == ':')) {
1479 while ((*cur >= '0') && (*cur <= '9'))
1480 hour = (hour * 10) + (*cur++ - '0');
1481 if (*cur == ':') cur++;
1482 while ((*cur >= '0') && (*cur <= '9'))
1483 minute = (minute * 10) + (*cur++ - '0');
1484 } else {
1485 while ((*cur >= '0') && (*cur <= '9'))
1486 year = (year * 10) + (*cur++ - '0');
1487 }
1488 while (*cur == ' ') cur++;
1489 if (*cur == 0) return(0);
1490 i = 0;
1491 while ((*cur != '\n') && (*cur != '\r')) {
1492 if (i < 150)
1493 filename[i++] = *cur;
1494 cur++;
1495 if (*cur == 0) return(0);
1496 }
1497 filename[i] = 0;
1498 if ((*cur != '\n') && (*cur != '\r'))
1499 return(0);
1500 while ((*cur == '\n') || (*cur == '\r'))
1501 cur++;
1502 }
1503 if (callback != NULL) {
1504 callback(userData, filename, attrib, owner, group, size, links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001505 year, month, day, hour, minute);
Daniel Veillardda07c342000-01-25 18:31:22 +00001506 }
1507 return(cur - list);
1508}
1509
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001510/**
1511 * xmlNanoFTPList:
1512 * @ctx: an FTP context
1513 * @callback: the user callback
1514 * @userData: the user callback data
1515 * @filename: optional files to list
1516 *
1517 * Do a listing on the server. All files info are passed back
1518 * in the callbacks.
1519 *
1520 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001521 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001522
Daniel Veillardda07c342000-01-25 18:31:22 +00001523int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001524xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1525 char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001526 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1527 char buf[4096 + 1];
1528 int len, res;
1529 int index = 0, base;
1530 fd_set rfd, efd;
1531 struct timeval tv;
1532
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001533 if (filename == NULL) {
1534 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1535 return(-1);
1536 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001537 if (ctxt->dataFd == -1)
1538 return(-1);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001539#ifndef HAVE_SNPRINTF
1540 len = sprintf(buf, "LIST -L\r\n");
1541#else /* HAVE_SNPRINTF */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001542 len = snprintf(buf, sizeof(buf), "LIST -L\r\n");
Daniel Veillard13c757e2000-02-01 23:59:15 +00001543#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001544 } else {
1545 if (filename[0] != '/') {
1546 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1547 return(-1);
1548 }
1549 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001550 if (ctxt->dataFd == -1)
1551 return(-1);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001552#ifndef HAVE_SNPRINTF
1553 len = sprintf(buf, "LIST -L %s\r\n", filename);
1554#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001555 len = snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001556#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001557 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001558#ifdef DEBUG_FTP
1559 printf(buf);
1560#endif
1561 res = send(ctxt->controlFd, buf, len, 0);
1562 if (res < 0) {
1563 close(ctxt->dataFd); ctxt->dataFd = -1;
1564 return(res);
1565 }
Daniel Veillard49703262000-07-10 10:27:46 +00001566 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001567 if (res != 1) {
1568 close(ctxt->dataFd); ctxt->dataFd = -1;
1569 return(-res);
1570 }
1571
1572 do {
1573 tv.tv_sec = 1;
1574 tv.tv_usec = 0;
1575 FD_ZERO(&rfd);
1576 FD_SET(ctxt->dataFd, &rfd);
1577 FD_ZERO(&efd);
1578 FD_SET(ctxt->dataFd, &efd);
1579 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1580 if (res < 0) {
1581#ifdef DEBUG_FTP
1582 perror("select");
1583#endif
1584 close(ctxt->dataFd); ctxt->dataFd = -1;
1585 return(-1);
1586 }
1587 if (res == 0) {
1588 res = xmlNanoFTPCheckResponse(ctxt);
1589 if (res < 0) {
1590 close(ctxt->dataFd); ctxt->dataFd = -1;
1591 ctxt->dataFd = -1;
1592 return(-1);
1593 }
1594 if (res == 2) {
1595 close(ctxt->dataFd); ctxt->dataFd = -1;
1596 return(0);
1597 }
1598
1599 continue;
1600 }
1601
1602 if ((len = read(ctxt->dataFd, &buf[index], sizeof(buf) - (index + 1))) < 0) {
1603#ifdef DEBUG_FTP
1604 perror("read");
1605#endif
1606 close(ctxt->dataFd); ctxt->dataFd = -1;
1607 ctxt->dataFd = -1;
1608 return(-1);
1609 }
1610#ifdef DEBUG_FTP
1611 write(1, &buf[index], len);
1612#endif
1613 index += len;
1614 buf[index] = 0;
1615 base = 0;
1616 do {
1617 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1618 base += res;
1619 } while (res > 0);
1620
1621 memmove(&buf[0], &buf[base], index - base);
1622 index -= base;
1623 } while (len != 0);
1624 xmlNanoFTPCloseConnection(ctxt);
1625 return(0);
1626}
1627
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001628/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001629 * xmlNanoFTPGetSocket:
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001630 * @ctx: an FTP context
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001631 * @filename: the file to retrieve (or NULL if path is in context).
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001632 *
1633 * Initiate fetch of the given file from the server.
1634 *
1635 * Returns the socket for the data connection, or <0 in case of error
Daniel Veillardda07c342000-01-25 18:31:22 +00001636 */
1637
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001638
Daniel Veillardda07c342000-01-25 18:31:22 +00001639int
1640xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1641 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1642 char buf[300];
1643 int res, len;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001644 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001645 return(-1);
1646 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001647 if (ctxt->dataFd == -1)
1648 return(-1);
Daniel Veillardda07c342000-01-25 18:31:22 +00001649
Daniel Veillard13c757e2000-02-01 23:59:15 +00001650#ifndef HAVE_SNPRINTF
1651 len = sprintf(buf, "TYPE I\r\n");
1652#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001653 len = snprintf(buf, sizeof(buf), "TYPE I\r\n");
Daniel Veillard13c757e2000-02-01 23:59:15 +00001654#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001655#ifdef DEBUG_FTP
1656 printf(buf);
1657#endif
1658 res = send(ctxt->controlFd, buf, len, 0);
1659 if (res < 0) {
1660 close(ctxt->dataFd); ctxt->dataFd = -1;
1661 return(res);
1662 }
Daniel Veillard49703262000-07-10 10:27:46 +00001663 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001664 if (res != 2) {
1665 close(ctxt->dataFd); ctxt->dataFd = -1;
1666 return(-res);
1667 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001668 if (filename == NULL)
Daniel Veillard13c757e2000-02-01 23:59:15 +00001669#ifndef HAVE_SNPRINTF
1670 len = sprintf(buf, "RETR %s\r\n", ctxt->path);
1671#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001672 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001673#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001674 else
Daniel Veillard13c757e2000-02-01 23:59:15 +00001675#ifndef HAVE_SNPRINTF
1676 len = sprintf(buf, "RETR %s\r\n", filename);
1677#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001678 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001679#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001680#ifdef DEBUG_FTP
1681 printf(buf);
1682#endif
1683 res = send(ctxt->controlFd, buf, len, 0);
1684 if (res < 0) {
1685 close(ctxt->dataFd); ctxt->dataFd = -1;
1686 return(res);
1687 }
Daniel Veillard49703262000-07-10 10:27:46 +00001688 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001689 if (res != 1) {
1690 close(ctxt->dataFd); ctxt->dataFd = -1;
1691 return(-res);
1692 }
1693 return(ctxt->dataFd);
1694}
1695
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001696/**
1697 * xmlNanoFTPGet:
1698 * @ctx: an FTP context
1699 * @callback: the user callback
1700 * @userData: the user callback data
1701 * @filename: the file to retrieve
1702 *
1703 * Fetch the given file from the server. All data are passed back
1704 * in the callbacks. The last callback has a size of 0 block.
1705 *
1706 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001707 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001708
Daniel Veillardda07c342000-01-25 18:31:22 +00001709int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001710xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1711 const char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001712 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1713 char buf[4096];
1714 int len = 0, res;
1715 fd_set rfd;
1716 struct timeval tv;
1717
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001718 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001719 return(-1);
1720 if (callback == NULL)
1721 return(-1);
1722 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1723 return(-1);
1724
1725 do {
1726 tv.tv_sec = 1;
1727 tv.tv_usec = 0;
1728 FD_ZERO(&rfd);
1729 FD_SET(ctxt->dataFd, &rfd);
1730 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1731 if (res < 0) {
1732#ifdef DEBUG_FTP
1733 perror("select");
1734#endif
1735 close(ctxt->dataFd); ctxt->dataFd = -1;
1736 return(-1);
1737 }
1738 if (res == 0) {
1739 res = xmlNanoFTPCheckResponse(ctxt);
1740 if (res < 0) {
1741 close(ctxt->dataFd); ctxt->dataFd = -1;
1742 ctxt->dataFd = -1;
1743 return(-1);
1744 }
1745 if (res == 2) {
1746 close(ctxt->dataFd); ctxt->dataFd = -1;
1747 return(0);
1748 }
1749
1750 continue;
1751 }
1752 if ((len = read(ctxt->dataFd, &buf, sizeof(buf))) < 0) {
1753 callback(userData, buf, len);
1754 close(ctxt->dataFd); ctxt->dataFd = -1;
1755 return(-1);
1756 }
1757 callback(userData, buf, len);
1758 } while (len != 0);
1759
1760 return(xmlNanoFTPCloseConnection(ctxt));
1761}
1762
1763/**
1764 * xmlNanoFTPRead:
1765 * @ctx: the FTP context
1766 * @dest: a buffer
1767 * @len: the buffer length
1768 *
1769 * This function tries to read @len bytes from the existing FTP connection
1770 * and saves them in @dest. This is a blocking call.
1771 *
1772 * Returns the number of byte read. 0 is an indication of an end of connection.
1773 * -1 indicates a parameter error.
1774 */
1775int
1776xmlNanoFTPRead(void *ctx, void *dest, int len) {
1777 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1778
1779 if (ctx == NULL) return(-1);
1780 if (ctxt->dataFd < 0) return(0);
1781 if (dest == NULL) return(-1);
1782 if (len <= 0) return(0);
1783
1784 len = read(ctxt->dataFd, dest, len);
1785#ifdef DEBUG_FTP
1786 printf("Read %d bytes\n", len);
1787#endif
1788 if (len <= 0) {
1789 xmlNanoFTPCloseConnection(ctxt);
1790 }
1791 return(len);
1792}
1793
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001794/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001795 * xmlNanoFTPOpen:
1796 * @URL: the URL to the resource
1797 *
1798 * Start to fetch the given ftp:// resource
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001799 *
1800 * Returns an FTP context, or NULL
Daniel Veillardda07c342000-01-25 18:31:22 +00001801 */
1802
Daniel Veillard06047432000-04-24 11:33:38 +00001803void*
Daniel Veillardda07c342000-01-25 18:31:22 +00001804xmlNanoFTPOpen(const char *URL) {
1805 xmlNanoFTPCtxtPtr ctxt;
1806 int sock;
1807
1808 xmlNanoFTPInit();
1809 if (URL == NULL) return(NULL);
1810 if (strncmp("ftp://", URL, 6)) return(NULL);
1811
Daniel Veillard49703262000-07-10 10:27:46 +00001812 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
Daniel Veillardda07c342000-01-25 18:31:22 +00001813 if (ctxt == NULL) return(NULL);
1814 if (xmlNanoFTPConnect(ctxt) < 0) {
1815 xmlNanoFTPFreeCtxt(ctxt);
1816 return(NULL);
1817 }
1818 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1819 if (sock < 0) {
1820 xmlNanoFTPFreeCtxt(ctxt);
1821 return(NULL);
1822 }
1823 return(ctxt);
1824}
1825
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001826/**
1827 * xmlNanoFTPClose:
1828 * @ctx: an FTP context
1829 *
1830 * Close the connection and both control and transport
1831 *
1832 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001833 */
1834
1835int
1836xmlNanoFTPClose(void *ctx) {
1837 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1838
1839 if (ctxt == NULL)
1840 return(-1);
1841
1842 if (ctxt->dataFd >= 0) {
1843 close(ctxt->dataFd);
1844 ctxt->dataFd = -1;
1845 }
1846 if (ctxt->controlFd >= 0) {
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001847 xmlNanoFTPQuit(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001848 close(ctxt->controlFd);
1849 ctxt->controlFd = -1;
1850 }
1851 xmlNanoFTPFreeCtxt(ctxt);
1852 return(0);
1853}
1854
1855#ifdef STANDALONE
1856/************************************************************************
1857 * *
1858 * Basic test in Standalone mode *
1859 * *
1860 ************************************************************************/
1861void ftpList(void *userData, const char *filename, const char* attrib,
1862 const char *owner, const char *group, unsigned long size, int links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001863 int year, const char *month, int day, int hour, int minute) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001864 printf("%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1865}
1866void ftpData(void *userData, const char *data, int len) {
1867 if (userData == NULL) return;
1868 if (len <= 0) {
1869 fclose(userData);
1870 return;
1871 }
1872 fwrite(data, len, 1, userData);
1873}
1874
1875int main(int argc, char **argv) {
1876 void *ctxt;
1877 FILE *output;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001878 char *tstfile = NULL;
Daniel Veillardda07c342000-01-25 18:31:22 +00001879
1880 xmlNanoFTPInit();
1881 if (argc > 1) {
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001882 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1883 if (xmlNanoFTPConnect(ctxt) < 0) {
1884 fprintf(stderr, "Couldn't connect to %s\n", argv[1]);
1885 exit(1);
1886 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001887 if (argc > 2)
1888 tstfile = argv[2];
1889 } else
1890 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1891 if (ctxt == NULL) {
1892 fprintf(stderr, "Couldn't connect to localhost\n");
1893 exit(1);
1894 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001895 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
Daniel Veillardda07c342000-01-25 18:31:22 +00001896 output = fopen("/tmp/tstdata", "w");
1897 if (output != NULL) {
1898 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001899 fprintf(stderr, "Failed to get file\n");
Daniel Veillardda07c342000-01-25 18:31:22 +00001900
1901 }
1902 xmlNanoFTPClose(ctxt);
1903 xmlMemoryDump();
1904 exit(0);
1905}
1906#endif /* STANDALONE */
Daniel Veillard361d8452000-04-03 19:48:13 +00001907#else /* !LIBXML_FTP_ENABLED */
1908#ifdef STANDALONE
1909#include <stdio.h>
1910int main(int argc, char **argv) {
1911 printf("%s : FTP support not compiled in\n", argv[0]);
1912 return(0);
1913}
1914#endif /* STANDALONE */
1915#endif /* LIBXML_FTP_ENABLED */