blob: 55ccb3a12cbbe53376ad91aee9f5bc5c605f2c04 [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 Veillard39c7d712000-09-10 16:14:55 +0000734 sprintf(buf, "USER anonymous\r\n");
Daniel Veillardda07c342000-01-25 18:31:22 +0000735 else
Daniel Veillard39c7d712000-09-10 16:14:55 +0000736#ifdef HAVE_SNPRINTF
737 snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
738#else
739 sprintf(buf, "USER %s\r\n", ctxt->user);
740#endif
741 buf[sizeof(buf) - 1] = 0;
742 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +0000743#ifdef DEBUG_FTP
744 printf(buf);
745#endif
746 res = send(ctxt->controlFd, buf, len, 0);
747 if (res < 0) return(res);
748 return(0);
749}
750
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000751/**
Daniel Veillardda07c342000-01-25 18:31:22 +0000752 * Send the password authentification
753 */
754
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000755static int
756xmlNanoFTPSendPasswd(void *ctx) {
Daniel Veillardda07c342000-01-25 18:31:22 +0000757 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
758 char buf[200];
759 int len;
760 int res;
761
762 if (ctxt->passwd == NULL)
Daniel Veillard39c7d712000-09-10 16:14:55 +0000763#ifdef HAVE_SNPRINTF
764 snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
765#else
766 sprintf(buf, "PASS libxml@%s\r\n", hostname);
767#endif
Daniel Veillardda07c342000-01-25 18:31:22 +0000768 else
Daniel Veillard39c7d712000-09-10 16:14:55 +0000769#ifdef HAVE_SNPRINTF
770 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
771#else
772 sprintf(buf, "PASS %s\r\n", ctxt->passwd);
773#endif
774 buf[sizeof(buf) - 1] = 0;
775 len = strlen(buf);
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 Veillard39c7d712000-09-10 16:14:55 +0000801 sprintf(buf, "QUIT\r\n");
802 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +0000803#ifdef DEBUG_FTP
804 printf(buf);
805#endif
806 res = send(ctxt->controlFd, buf, len, 0);
807 return(0);
808}
809
Daniel Veillardaeea04f2000-01-25 19:27:27 +0000810/**
811 * xmlNanoFTPConnect:
812 * @ctx: an FTP context
813 *
814 * Tries to open a control connection
815 *
816 * Returns -1 in case of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +0000817 */
818
819int
820xmlNanoFTPConnect(void *ctx) {
821 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
822 struct hostent *hp;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000823 int port;
Daniel Veillardda07c342000-01-25 18:31:22 +0000824 int res;
825
826 if (ctxt == NULL)
827 return(-1);
828 if (ctxt->hostname == NULL)
829 return(-1);
830
831 /*
832 * do the blocking DNS query.
833 */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000834 if (proxy)
835 hp = gethostbyname(proxy);
836 else
837 hp = gethostbyname(ctxt->hostname);
Daniel Veillardda07c342000-01-25 18:31:22 +0000838 if (hp == NULL)
839 return(-1);
840
841 /*
842 * Prepare the socket
843 */
844 memset(&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
845 ctxt->ftpAddr.sin_family = AF_INET;
846 memcpy(&ctxt->ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000847 if (proxy) {
848 port = proxyPort;
849 } else {
850 port = ctxt->port;
851 }
852 if (port == 0)
853 port = 21;
854 ctxt->ftpAddr.sin_port = htons(port);
Daniel Veillardda07c342000-01-25 18:31:22 +0000855 ctxt->controlFd = socket(AF_INET, SOCK_STREAM, 0);
856 if (ctxt->controlFd < 0)
857 return(-1);
858
859 /*
860 * Do the connect.
861 */
862 if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
863 sizeof(struct sockaddr_in)) < 0) {
864 close(ctxt->controlFd); ctxt->controlFd = -1;
865 ctxt->controlFd = -1;
866 return(-1);
867 }
868
869 /*
870 * Wait for the HELLO from the server.
871 */
872 res = xmlNanoFTPGetResponse(ctxt);
873 if (res != 2) {
874 close(ctxt->controlFd); ctxt->controlFd = -1;
875 ctxt->controlFd = -1;
876 return(-1);
877 }
878
879 /*
880 * State diagram for the login operation on the FTP server
881 *
882 * Reference: RFC 959
883 *
884 * 1
885 * +---+ USER +---+------------->+---+
886 * | B |---------->| W | 2 ---->| E |
887 * +---+ +---+------ | -->+---+
888 * | | | | |
889 * 3 | | 4,5 | | |
890 * -------------- ----- | | |
891 * | | | | |
892 * | | | | |
893 * | --------- |
894 * | 1| | | |
895 * V | | | |
896 * +---+ PASS +---+ 2 | ------>+---+
897 * | |---------->| W |------------->| S |
898 * +---+ +---+ ---------->+---+
899 * | | | | |
900 * 3 | |4,5| | |
901 * -------------- -------- |
902 * | | | | |
903 * | | | | |
904 * | -----------
905 * | 1,3| | | |
906 * V | 2| | |
907 * +---+ ACCT +---+-- | ----->+---+
908 * | |---------->| W | 4,5 -------->| F |
909 * +---+ +---+------------->+---+
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000910 *
911 * Of course in case of using a proxy this get really nasty and is not
912 * standardized at all :-(
913 */
914 if (proxy) {
915 int len;
916 char buf[400];
917
918 if (proxyUser != NULL) {
919 /*
920 * We need proxy auth
921 */
Daniel Veillard39c7d712000-09-10 16:14:55 +0000922#ifdef HAVE_SNPRINTF
923 snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
924#else
925 sprintf(buf, "USER %s\r\n", proxyUser);
926#endif
927 buf[sizeof(buf) - 1] = 0;
928 len = strlen(buf);
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000929#ifdef DEBUG_FTP
930 printf(buf);
931#endif
932 res = send(ctxt->controlFd, buf, len, 0);
933 if (res < 0) {
934 close(ctxt->controlFd);
935 ctxt->controlFd = -1;
936 return(res);
937 }
938 res = xmlNanoFTPGetResponse(ctxt);
939 switch (res) {
940 case 2:
941 if (proxyPasswd == NULL)
942 break;
943 case 3:
944 if (proxyPasswd != NULL)
Daniel Veillard39c7d712000-09-10 16:14:55 +0000945#ifdef HAVE_SNPRINTF
946 snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
947#else
948 sprintf(buf, "PASS %s\r\n", proxyPasswd);
949#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000950 else
Daniel Veillard39c7d712000-09-10 16:14:55 +0000951#ifdef HAVE_SNPRINTF
952 snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n",
Daniel Veillardcf461992000-03-14 18:30:20 +0000953 hostname);
Daniel Veillard39c7d712000-09-10 16:14:55 +0000954#else
955 sprintf(buf, "PASS libxml@%s\r\n", hostname);
956#endif
957 buf[sizeof(buf) - 1] = 0;
958 len = strlen(buf);
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 Veillard39c7d712000-09-10 16:14:55 +0000996#ifdef HAVE_SNPRINTF
997 snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
998#else
999 sprintf(buf, "SITE %s\r\n", ctxt->hostname);
1000#endif
1001 buf[sizeof(buf) - 1] = 0;
1002 len = strlen(buf);
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001003#ifdef DEBUG_FTP
1004 printf(buf);
1005#endif
1006 res = send(ctxt->controlFd, buf, len, 0);
1007 if (res < 0) {
1008 close(ctxt->controlFd); ctxt->controlFd = -1;
1009 ctxt->controlFd = -1;
1010 return(res);
1011 }
1012 res = xmlNanoFTPGetResponse(ctxt);
1013 if (res == 2) {
1014 /* we assume it worked :-\ 1 is error for SITE command */
1015 proxyType = 1;
1016 break;
1017 }
1018 if (proxyType == 1) {
1019 close(ctxt->controlFd); ctxt->controlFd = -1;
1020 ctxt->controlFd = -1;
1021 return(-1);
1022 }
1023 case 2:
1024 /* USER user@host command */
1025 if (ctxt->user == NULL)
Daniel Veillard39c7d712000-09-10 16:14:55 +00001026#ifdef HAVE_SNPRINTF
1027 snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001028 ctxt->hostname);
Daniel Veillard39c7d712000-09-10 16:14:55 +00001029#else
1030 sprintf(buf, "USER anonymous@%s\r\n", ctxt->hostname);
1031#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001032 else
Daniel Veillard39c7d712000-09-10 16:14:55 +00001033#ifdef HAVE_SNPRINTF
1034 snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001035 ctxt->user, ctxt->hostname);
Daniel Veillard39c7d712000-09-10 16:14:55 +00001036#else
1037 sprintf(buf, "USER %s@%s\r\n",
1038 ctxt->user, ctxt->hostname);
1039#endif
1040 buf[sizeof(buf) - 1] = 0;
1041 len = strlen(buf);
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001042#ifdef DEBUG_FTP
1043 printf(buf);
1044#endif
1045 res = send(ctxt->controlFd, buf, len, 0);
1046 if (res < 0) {
1047 close(ctxt->controlFd); ctxt->controlFd = -1;
1048 ctxt->controlFd = -1;
1049 return(res);
1050 }
1051 res = xmlNanoFTPGetResponse(ctxt);
1052 if ((res == 1) || (res == 2)) {
1053 /* we assume it worked :-\ */
1054 proxyType = 2;
1055 return(0);
1056 }
1057 if (ctxt->passwd == NULL)
Daniel Veillard39c7d712000-09-10 16:14:55 +00001058#ifdef HAVE_SNPRINTF
1059 snprintf(buf, sizeof(buf), "PASS libxml@%s\r\n", hostname);
1060#else
1061 sprintf(buf, "PASS libxml@%s\r\n", hostname);
1062#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001063 else
Daniel Veillard39c7d712000-09-10 16:14:55 +00001064#ifdef HAVE_SNPRINTF
1065 snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
1066#else
1067 sprintf(buf, "PASS %s\r\n", ctxt->passwd);
1068#endif
1069 buf[sizeof(buf) - 1] = 0;
1070 len = strlen(buf);
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001071#ifdef DEBUG_FTP
1072 printf(buf);
1073#endif
1074 res = send(ctxt->controlFd, buf, len, 0);
1075 if (res < 0) {
1076 close(ctxt->controlFd); ctxt->controlFd = -1;
1077 ctxt->controlFd = -1;
1078 return(res);
1079 }
1080 res = xmlNanoFTPGetResponse(ctxt);
1081 if ((res == 1) || (res == 2)) {
1082 /* we assume it worked :-\ */
1083 proxyType = 2;
1084 return(0);
1085 }
1086 if (proxyType == 2) {
1087 close(ctxt->controlFd); ctxt->controlFd = -1;
1088 ctxt->controlFd = -1;
1089 return(-1);
1090 }
1091 case 3:
1092 /*
1093 * If you need support for other Proxy authentication scheme
1094 * send the code or at least the sequence in use.
1095 */
1096 default:
1097 close(ctxt->controlFd); ctxt->controlFd = -1;
1098 ctxt->controlFd = -1;
1099 return(-1);
1100 }
1101 }
1102 /*
1103 * Non-proxy handling.
Daniel Veillardda07c342000-01-25 18:31:22 +00001104 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001105 res = xmlNanoFTPSendUser(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001106 if (res < 0) {
1107 close(ctxt->controlFd); ctxt->controlFd = -1;
1108 ctxt->controlFd = -1;
1109 return(-1);
1110 }
1111 res = xmlNanoFTPGetResponse(ctxt);
1112 switch (res) {
1113 case 2:
1114 return(0);
1115 case 3:
1116 break;
1117 case 1:
1118 case 4:
1119 case 5:
1120 case -1:
1121 default:
1122 close(ctxt->controlFd); ctxt->controlFd = -1;
1123 ctxt->controlFd = -1;
1124 return(-1);
1125 }
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001126 res = xmlNanoFTPSendPasswd(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001127 if (res < 0) {
1128 close(ctxt->controlFd); ctxt->controlFd = -1;
1129 ctxt->controlFd = -1;
1130 return(-1);
1131 }
1132 res = xmlNanoFTPGetResponse(ctxt);
1133 switch (res) {
1134 case 2:
Daniel Veillard5feb8492000-02-02 17:15:36 +00001135 break;
Daniel Veillardda07c342000-01-25 18:31:22 +00001136 case 3:
1137 fprintf(stderr, "FTP server asking for ACCNT on anonymous\n");
1138 case 1:
1139 case 4:
1140 case 5:
1141 case -1:
1142 default:
1143 close(ctxt->controlFd); ctxt->controlFd = -1;
1144 ctxt->controlFd = -1;
1145 return(-1);
1146 }
1147
1148 return(0);
1149}
1150
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001151/**
1152 * xmlNanoFTPConnectTo:
1153 * @server: an FTP server name
Daniel Veillard06047432000-04-24 11:33:38 +00001154 * @port: the port (use 21 if 0)
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001155 *
1156 * Tries to open a control connection to the given server/port
1157 *
Daniel Veillard06047432000-04-24 11:33:38 +00001158 * Returns an fTP context or NULL if it failed
Daniel Veillardda07c342000-01-25 18:31:22 +00001159 */
1160
Daniel Veillard06047432000-04-24 11:33:38 +00001161void*
Daniel Veillardda07c342000-01-25 18:31:22 +00001162xmlNanoFTPConnectTo(const char *server, int port) {
1163 xmlNanoFTPCtxtPtr ctxt;
1164 int res;
1165
1166 xmlNanoFTPInit();
1167 if (server == NULL)
1168 return(NULL);
Daniel Veillard49703262000-07-10 10:27:46 +00001169 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
Daniel Veillardda07c342000-01-25 18:31:22 +00001170 ctxt->hostname = xmlMemStrdup(server);
1171 if (port != 0)
1172 ctxt->port = port;
1173 res = xmlNanoFTPConnect(ctxt);
1174 if (res < 0) {
1175 xmlNanoFTPFreeCtxt(ctxt);
1176 return(NULL);
1177 }
1178 return(ctxt);
1179}
1180
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001181/**
Daniel Veillard49703262000-07-10 10:27:46 +00001182 * xmlNanoFTPCwd:
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001183 * @ctx: an FTP context
1184 * @directory: a directory on the server
1185 *
1186 * Tries to change the remote directory
1187 *
1188 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
Daniel Veillardda07c342000-01-25 18:31:22 +00001189 */
1190
1191int
1192xmlNanoFTPCwd(void *ctx, char *directory) {
1193 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1194 char buf[400];
1195 int len;
1196 int res;
1197
1198 /*
1199 * Expected response code for CWD:
1200 *
1201 * CWD
1202 * 250
1203 * 500, 501, 502, 421, 530, 550
1204 */
Daniel Veillard39c7d712000-09-10 16:14:55 +00001205#ifdef HAVE_SNPRINTF
1206 snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
1207#else
1208 sprintf(buf, "CWD %s\r\n", directory);
1209#endif
1210 buf[sizeof(buf) - 1] = 0;
1211 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +00001212#ifdef DEBUG_FTP
1213 printf(buf);
1214#endif
1215 res = send(ctxt->controlFd, buf, len, 0);
1216 if (res < 0) return(res);
1217 res = xmlNanoFTPGetResponse(ctxt);
1218 if (res == 4) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001219 return(-1);
1220 }
1221 if (res == 2) return(1);
1222 if (res == 5) {
1223 return(0);
1224 }
1225 return(0);
1226}
1227
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001228/**
1229 * xmlNanoFTPGetConnection:
1230 * @ctx: an FTP context
1231 *
1232 * Try to open a data connection to the server. Currently only
1233 * passive mode is supported.
1234 *
1235 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001236 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001237
Daniel Veillardda07c342000-01-25 18:31:22 +00001238int
1239xmlNanoFTPGetConnection(void *ctx) {
1240 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1241 char buf[200], *cur;
1242 int len, i;
1243 int res;
1244 unsigned char ad[6], *adp, *portp;
1245 unsigned int temp[6];
1246 struct sockaddr_in dataAddr;
1247 size_t dataAddrLen;
1248
1249 ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1250 if (ctxt->dataFd < 0) {
1251 fprintf(stderr, "xmlNanoFTPGetConnection: failed to create socket\n");
1252 }
1253 dataAddrLen = sizeof(dataAddr);
1254 memset(&dataAddr, 0, dataAddrLen);
1255 dataAddr.sin_family = AF_INET;
1256
1257 if (ctxt->passive) {
Daniel Veillard39c7d712000-09-10 16:14:55 +00001258 sprintf(buf, "PASV\r\n");
1259 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +00001260#ifdef DEBUG_FTP
1261 printf(buf);
1262#endif
1263 res = send(ctxt->controlFd, buf, len, 0);
1264 if (res < 0) {
1265 close(ctxt->dataFd); ctxt->dataFd = -1;
1266 return(res);
1267 }
Daniel Veillard49703262000-07-10 10:27:46 +00001268 res = xmlNanoFTPReadResponse(ctx);
Daniel Veillardda07c342000-01-25 18:31:22 +00001269 if (res != 2) {
1270 if (res == 5) {
1271 close(ctxt->dataFd); ctxt->dataFd = -1;
1272 return(-1);
1273 } else {
1274 /*
1275 * retry with an active connection
1276 */
1277 close(ctxt->dataFd); ctxt->dataFd = -1;
1278 ctxt->passive = 0;
1279 }
1280 }
Daniel Veillard49703262000-07-10 10:27:46 +00001281 cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
Daniel Veillardda07c342000-01-25 18:31:22 +00001282 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1283 if (sscanf(cur, "%d,%d,%d,%d,%d,%d", &temp[0], &temp[1], &temp[2],
1284 &temp[3], &temp[4], &temp[5]) != 6) {
1285 fprintf(stderr, "Invalid answer to PASV\n");
Daniel Veillardbe803962000-06-28 23:40:59 +00001286 if (ctxt->dataFd != -1) {
1287 close(ctxt->dataFd); ctxt->dataFd = -1;
1288 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001289 return(-1);
1290 }
1291 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1292 memcpy(&dataAddr.sin_addr, &ad[0], 4);
1293 memcpy(&dataAddr.sin_port, &ad[4], 2);
1294 if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1295 fprintf(stderr, "Failed to create a data connection\n");
1296 close(ctxt->dataFd); ctxt->dataFd = -1;
1297 return (-1);
1298 }
1299 } else {
1300 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1301 dataAddr.sin_port = 0;
1302 if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1303 fprintf(stderr, "Failed to bind a port\n");
1304 close(ctxt->dataFd); ctxt->dataFd = -1;
1305 return (-1);
1306 }
1307 getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1308
1309 if (listen(ctxt->dataFd, 1) < 0) {
1310 fprintf(stderr, "Could not listen on port %d\n",
1311 ntohs(dataAddr.sin_port));
1312 close(ctxt->dataFd); ctxt->dataFd = -1;
1313 return (-1);
1314 }
1315 adp = (unsigned char *) &dataAddr.sin_addr;
1316 portp = (unsigned char *) &dataAddr.sin_port;
Daniel Veillard39c7d712000-09-10 16:14:55 +00001317#ifdef HAVE_SNPRINTF
1318 snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
Daniel Veillardcf461992000-03-14 18:30:20 +00001319 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1320 portp[0] & 0xff, portp[1] & 0xff);
Daniel Veillard39c7d712000-09-10 16:14:55 +00001321#else
1322 sprintf(buf, "PORT %d,%d,%d,%d,%d,%d\r\n",
Daniel Veillardcf461992000-03-14 18:30:20 +00001323 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1324 portp[0] & 0xff, portp[1] & 0xff);
Daniel Veillard39c7d712000-09-10 16:14:55 +00001325#endif
Daniel Veillardda07c342000-01-25 18:31:22 +00001326 buf[sizeof(buf) - 1] = 0;
Daniel Veillard39c7d712000-09-10 16:14:55 +00001327 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +00001328#ifdef DEBUG_FTP
1329 printf(buf);
1330#endif
1331
1332 res = send(ctxt->controlFd, buf, len, 0);
1333 if (res < 0) {
1334 close(ctxt->dataFd); ctxt->dataFd = -1;
1335 return(res);
1336 }
1337 res = xmlNanoFTPGetResponse(ctxt);
1338 if (res != 2) {
1339 close(ctxt->dataFd); ctxt->dataFd = -1;
1340 return(-1);
1341 }
1342 }
1343 return(ctxt->dataFd);
1344
1345}
1346
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001347/**
1348 * xmlNanoFTPCloseConnection:
1349 * @ctx: an FTP context
1350 *
1351 * Close the data connection from the server
1352 *
1353 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001354 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001355
Daniel Veillardda07c342000-01-25 18:31:22 +00001356int
1357xmlNanoFTPCloseConnection(void *ctx) {
1358 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1359 int res;
Daniel Veillardcf461992000-03-14 18:30:20 +00001360 fd_set rfd, efd;
1361 struct timeval tv;
Daniel Veillardda07c342000-01-25 18:31:22 +00001362
1363 close(ctxt->dataFd); ctxt->dataFd = -1;
Daniel Veillardcf461992000-03-14 18:30:20 +00001364 tv.tv_sec = 15;
1365 tv.tv_usec = 0;
1366 FD_ZERO(&rfd);
1367 FD_SET(ctxt->controlFd, &rfd);
1368 FD_ZERO(&efd);
1369 FD_SET(ctxt->controlFd, &efd);
1370 res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1371 if (res < 0) {
1372#ifdef DEBUG_FTP
1373 perror("select");
1374#endif
Daniel Veillardda07c342000-01-25 18:31:22 +00001375 close(ctxt->controlFd); ctxt->controlFd = -1;
1376 return(-1);
1377 }
Daniel Veillardcf461992000-03-14 18:30:20 +00001378 if (res == 0) {
Daniel Veillard2f2bf412000-08-20 15:11:02 +00001379#ifdef DEBUG_FTP
Daniel Veillardcf461992000-03-14 18:30:20 +00001380 fprintf(stderr, "xmlNanoFTPCloseConnection: timeout\n");
Daniel Veillard2f2bf412000-08-20 15:11:02 +00001381#endif
Daniel Veillardcf461992000-03-14 18:30:20 +00001382 close(ctxt->controlFd); ctxt->controlFd = -1;
1383 } else {
1384 res = xmlNanoFTPGetResponse(ctxt);
1385 if (res != 2) {
1386 close(ctxt->controlFd); ctxt->controlFd = -1;
1387 return(-1);
1388 }
1389 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001390 return(0);
1391}
1392
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001393/**
1394 * xmlNanoFTPParseList:
1395 * @list: some data listing received from the server
1396 * @callback: the user callback
1397 * @userData: the user callback data
1398 *
1399 * Parse at most one entry from the listing.
1400 *
1401 * Returns -1 incase of error, the lenght of data parsed otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001402 */
1403
1404static int
1405xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1406 const char *cur = list;
1407 char filename[151];
1408 char attrib[11];
1409 char owner[11];
1410 char group[11];
1411 char month[4];
1412 int year = 0;
1413 int minute = 0;
1414 int hour = 0;
1415 int day = 0;
1416 unsigned long size = 0;
1417 int links = 0;
1418 int i;
1419
1420 if (!strncmp(cur, "total", 5)) {
1421 cur += 5;
1422 while (*cur == ' ') cur++;
1423 while ((*cur >= '0') && (*cur <= '9'))
1424 links = (links * 10) + (*cur++ - '0');
1425 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1426 cur++;
1427 return(cur - list);
1428 } else if (*list == '+') {
1429 return(0);
1430 } else {
1431 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1432 cur++;
1433 if (*cur == 0) return(0);
1434 i = 0;
1435 while (*cur != ' ') {
1436 if (i < 10)
1437 attrib[i++] = *cur;
1438 cur++;
1439 if (*cur == 0) return(0);
1440 }
1441 attrib[10] = 0;
1442 while (*cur == ' ') cur++;
1443 if (*cur == 0) return(0);
1444 while ((*cur >= '0') && (*cur <= '9'))
1445 links = (links * 10) + (*cur++ - '0');
1446 while (*cur == ' ') cur++;
1447 if (*cur == 0) return(0);
1448 i = 0;
1449 while (*cur != ' ') {
1450 if (i < 10)
1451 owner[i++] = *cur;
1452 cur++;
1453 if (*cur == 0) return(0);
1454 }
1455 owner[i] = 0;
1456 while (*cur == ' ') cur++;
1457 if (*cur == 0) return(0);
1458 i = 0;
1459 while (*cur != ' ') {
1460 if (i < 10)
1461 group[i++] = *cur;
1462 cur++;
1463 if (*cur == 0) return(0);
1464 }
1465 group[i] = 0;
1466 while (*cur == ' ') cur++;
1467 if (*cur == 0) return(0);
1468 while ((*cur >= '0') && (*cur <= '9'))
1469 size = (size * 10) + (*cur++ - '0');
1470 while (*cur == ' ') cur++;
1471 if (*cur == 0) return(0);
1472 i = 0;
1473 while (*cur != ' ') {
1474 if (i < 3)
1475 month[i++] = *cur;
1476 cur++;
1477 if (*cur == 0) return(0);
1478 }
1479 month[i] = 0;
1480 while (*cur == ' ') cur++;
1481 if (*cur == 0) return(0);
1482 while ((*cur >= '0') && (*cur <= '9'))
1483 day = (day * 10) + (*cur++ - '0');
1484 while (*cur == ' ') cur++;
1485 if (*cur == 0) return(0);
1486 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1487 if ((cur[1] == ':') || (cur[2] == ':')) {
1488 while ((*cur >= '0') && (*cur <= '9'))
1489 hour = (hour * 10) + (*cur++ - '0');
1490 if (*cur == ':') cur++;
1491 while ((*cur >= '0') && (*cur <= '9'))
1492 minute = (minute * 10) + (*cur++ - '0');
1493 } else {
1494 while ((*cur >= '0') && (*cur <= '9'))
1495 year = (year * 10) + (*cur++ - '0');
1496 }
1497 while (*cur == ' ') cur++;
1498 if (*cur == 0) return(0);
1499 i = 0;
1500 while ((*cur != '\n') && (*cur != '\r')) {
1501 if (i < 150)
1502 filename[i++] = *cur;
1503 cur++;
1504 if (*cur == 0) return(0);
1505 }
1506 filename[i] = 0;
1507 if ((*cur != '\n') && (*cur != '\r'))
1508 return(0);
1509 while ((*cur == '\n') || (*cur == '\r'))
1510 cur++;
1511 }
1512 if (callback != NULL) {
1513 callback(userData, filename, attrib, owner, group, size, links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001514 year, month, day, hour, minute);
Daniel Veillardda07c342000-01-25 18:31:22 +00001515 }
1516 return(cur - list);
1517}
1518
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001519/**
1520 * xmlNanoFTPList:
1521 * @ctx: an FTP context
1522 * @callback: the user callback
1523 * @userData: the user callback data
1524 * @filename: optional files to list
1525 *
1526 * Do a listing on the server. All files info are passed back
1527 * in the callbacks.
1528 *
1529 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001530 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001531
Daniel Veillardda07c342000-01-25 18:31:22 +00001532int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001533xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1534 char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001535 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1536 char buf[4096 + 1];
1537 int len, res;
1538 int index = 0, base;
1539 fd_set rfd, efd;
1540 struct timeval tv;
1541
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001542 if (filename == NULL) {
1543 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1544 return(-1);
1545 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001546 if (ctxt->dataFd == -1)
1547 return(-1);
Daniel Veillard39c7d712000-09-10 16:14:55 +00001548 sprintf(buf, "LIST -L\r\n");
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001549 } else {
1550 if (filename[0] != '/') {
1551 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1552 return(-1);
1553 }
1554 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001555 if (ctxt->dataFd == -1)
1556 return(-1);
Daniel Veillard39c7d712000-09-10 16:14:55 +00001557#ifdef HAVE_SNPRINTF
1558 snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
1559#else
1560 sprintf(buf, "LIST -L %s\r\n", filename);
1561#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001562 }
Daniel Veillard39c7d712000-09-10 16:14:55 +00001563 buf[sizeof(buf) - 1] = 0;
1564 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +00001565#ifdef DEBUG_FTP
1566 printf(buf);
1567#endif
1568 res = send(ctxt->controlFd, buf, len, 0);
1569 if (res < 0) {
1570 close(ctxt->dataFd); ctxt->dataFd = -1;
1571 return(res);
1572 }
Daniel Veillard49703262000-07-10 10:27:46 +00001573 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001574 if (res != 1) {
1575 close(ctxt->dataFd); ctxt->dataFd = -1;
1576 return(-res);
1577 }
1578
1579 do {
1580 tv.tv_sec = 1;
1581 tv.tv_usec = 0;
1582 FD_ZERO(&rfd);
1583 FD_SET(ctxt->dataFd, &rfd);
1584 FD_ZERO(&efd);
1585 FD_SET(ctxt->dataFd, &efd);
1586 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1587 if (res < 0) {
1588#ifdef DEBUG_FTP
1589 perror("select");
1590#endif
1591 close(ctxt->dataFd); ctxt->dataFd = -1;
1592 return(-1);
1593 }
1594 if (res == 0) {
1595 res = xmlNanoFTPCheckResponse(ctxt);
1596 if (res < 0) {
1597 close(ctxt->dataFd); ctxt->dataFd = -1;
1598 ctxt->dataFd = -1;
1599 return(-1);
1600 }
1601 if (res == 2) {
1602 close(ctxt->dataFd); ctxt->dataFd = -1;
1603 return(0);
1604 }
1605
1606 continue;
1607 }
1608
1609 if ((len = read(ctxt->dataFd, &buf[index], sizeof(buf) - (index + 1))) < 0) {
1610#ifdef DEBUG_FTP
1611 perror("read");
1612#endif
1613 close(ctxt->dataFd); ctxt->dataFd = -1;
1614 ctxt->dataFd = -1;
1615 return(-1);
1616 }
1617#ifdef DEBUG_FTP
1618 write(1, &buf[index], len);
1619#endif
1620 index += len;
1621 buf[index] = 0;
1622 base = 0;
1623 do {
1624 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1625 base += res;
1626 } while (res > 0);
1627
1628 memmove(&buf[0], &buf[base], index - base);
1629 index -= base;
1630 } while (len != 0);
1631 xmlNanoFTPCloseConnection(ctxt);
1632 return(0);
1633}
1634
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001635/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001636 * xmlNanoFTPGetSocket:
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001637 * @ctx: an FTP context
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001638 * @filename: the file to retrieve (or NULL if path is in context).
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001639 *
1640 * Initiate fetch of the given file from the server.
1641 *
1642 * Returns the socket for the data connection, or <0 in case of error
Daniel Veillardda07c342000-01-25 18:31:22 +00001643 */
1644
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001645
Daniel Veillardda07c342000-01-25 18:31:22 +00001646int
1647xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1648 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1649 char buf[300];
1650 int res, len;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001651 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001652 return(-1);
1653 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001654 if (ctxt->dataFd == -1)
1655 return(-1);
Daniel Veillardda07c342000-01-25 18:31:22 +00001656
Daniel Veillard39c7d712000-09-10 16:14:55 +00001657 sprintf(buf, "TYPE I\r\n");
1658 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +00001659#ifdef DEBUG_FTP
1660 printf(buf);
1661#endif
1662 res = send(ctxt->controlFd, buf, len, 0);
1663 if (res < 0) {
1664 close(ctxt->dataFd); ctxt->dataFd = -1;
1665 return(res);
1666 }
Daniel Veillard49703262000-07-10 10:27:46 +00001667 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001668 if (res != 2) {
1669 close(ctxt->dataFd); ctxt->dataFd = -1;
1670 return(-res);
1671 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001672 if (filename == NULL)
Daniel Veillard39c7d712000-09-10 16:14:55 +00001673#ifdef HAVE_SNPRINTF
1674 snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
1675#else
1676 sprintf(buf, "RETR %s\r\n", ctxt->path);
1677#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001678 else
Daniel Veillard39c7d712000-09-10 16:14:55 +00001679#ifdef HAVE_SNPRINTF
1680 snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
1681#else
1682 sprintf(buf, "RETR %s\r\n", filename);
1683#endif
1684 buf[sizeof(buf) - 1] = 0;
1685 len = strlen(buf);
Daniel Veillardda07c342000-01-25 18:31:22 +00001686#ifdef DEBUG_FTP
1687 printf(buf);
1688#endif
1689 res = send(ctxt->controlFd, buf, len, 0);
1690 if (res < 0) {
1691 close(ctxt->dataFd); ctxt->dataFd = -1;
1692 return(res);
1693 }
Daniel Veillard49703262000-07-10 10:27:46 +00001694 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001695 if (res != 1) {
1696 close(ctxt->dataFd); ctxt->dataFd = -1;
1697 return(-res);
1698 }
1699 return(ctxt->dataFd);
1700}
1701
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001702/**
1703 * xmlNanoFTPGet:
1704 * @ctx: an FTP context
1705 * @callback: the user callback
1706 * @userData: the user callback data
1707 * @filename: the file to retrieve
1708 *
1709 * Fetch the given file from the server. All data are passed back
1710 * in the callbacks. The last callback has a size of 0 block.
1711 *
1712 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001713 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001714
Daniel Veillardda07c342000-01-25 18:31:22 +00001715int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001716xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1717 const char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001718 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1719 char buf[4096];
1720 int len = 0, res;
1721 fd_set rfd;
1722 struct timeval tv;
1723
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001724 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001725 return(-1);
1726 if (callback == NULL)
1727 return(-1);
1728 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1729 return(-1);
1730
1731 do {
1732 tv.tv_sec = 1;
1733 tv.tv_usec = 0;
1734 FD_ZERO(&rfd);
1735 FD_SET(ctxt->dataFd, &rfd);
1736 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1737 if (res < 0) {
1738#ifdef DEBUG_FTP
1739 perror("select");
1740#endif
1741 close(ctxt->dataFd); ctxt->dataFd = -1;
1742 return(-1);
1743 }
1744 if (res == 0) {
1745 res = xmlNanoFTPCheckResponse(ctxt);
1746 if (res < 0) {
1747 close(ctxt->dataFd); ctxt->dataFd = -1;
1748 ctxt->dataFd = -1;
1749 return(-1);
1750 }
1751 if (res == 2) {
1752 close(ctxt->dataFd); ctxt->dataFd = -1;
1753 return(0);
1754 }
1755
1756 continue;
1757 }
1758 if ((len = read(ctxt->dataFd, &buf, sizeof(buf))) < 0) {
1759 callback(userData, buf, len);
1760 close(ctxt->dataFd); ctxt->dataFd = -1;
1761 return(-1);
1762 }
1763 callback(userData, buf, len);
1764 } while (len != 0);
1765
1766 return(xmlNanoFTPCloseConnection(ctxt));
1767}
1768
1769/**
1770 * xmlNanoFTPRead:
1771 * @ctx: the FTP context
1772 * @dest: a buffer
1773 * @len: the buffer length
1774 *
1775 * This function tries to read @len bytes from the existing FTP connection
1776 * and saves them in @dest. This is a blocking call.
1777 *
1778 * Returns the number of byte read. 0 is an indication of an end of connection.
1779 * -1 indicates a parameter error.
1780 */
1781int
1782xmlNanoFTPRead(void *ctx, void *dest, int len) {
1783 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1784
1785 if (ctx == NULL) return(-1);
1786 if (ctxt->dataFd < 0) return(0);
1787 if (dest == NULL) return(-1);
1788 if (len <= 0) return(0);
1789
1790 len = read(ctxt->dataFd, dest, len);
1791#ifdef DEBUG_FTP
1792 printf("Read %d bytes\n", len);
1793#endif
1794 if (len <= 0) {
1795 xmlNanoFTPCloseConnection(ctxt);
1796 }
1797 return(len);
1798}
1799
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001800/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001801 * xmlNanoFTPOpen:
1802 * @URL: the URL to the resource
1803 *
1804 * Start to fetch the given ftp:// resource
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001805 *
1806 * Returns an FTP context, or NULL
Daniel Veillardda07c342000-01-25 18:31:22 +00001807 */
1808
Daniel Veillard06047432000-04-24 11:33:38 +00001809void*
Daniel Veillardda07c342000-01-25 18:31:22 +00001810xmlNanoFTPOpen(const char *URL) {
1811 xmlNanoFTPCtxtPtr ctxt;
1812 int sock;
1813
1814 xmlNanoFTPInit();
1815 if (URL == NULL) return(NULL);
1816 if (strncmp("ftp://", URL, 6)) return(NULL);
1817
Daniel Veillard49703262000-07-10 10:27:46 +00001818 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
Daniel Veillardda07c342000-01-25 18:31:22 +00001819 if (ctxt == NULL) return(NULL);
1820 if (xmlNanoFTPConnect(ctxt) < 0) {
1821 xmlNanoFTPFreeCtxt(ctxt);
1822 return(NULL);
1823 }
1824 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1825 if (sock < 0) {
1826 xmlNanoFTPFreeCtxt(ctxt);
1827 return(NULL);
1828 }
1829 return(ctxt);
1830}
1831
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001832/**
1833 * xmlNanoFTPClose:
1834 * @ctx: an FTP context
1835 *
1836 * Close the connection and both control and transport
1837 *
1838 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001839 */
1840
1841int
1842xmlNanoFTPClose(void *ctx) {
1843 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1844
1845 if (ctxt == NULL)
1846 return(-1);
1847
1848 if (ctxt->dataFd >= 0) {
1849 close(ctxt->dataFd);
1850 ctxt->dataFd = -1;
1851 }
1852 if (ctxt->controlFd >= 0) {
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001853 xmlNanoFTPQuit(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001854 close(ctxt->controlFd);
1855 ctxt->controlFd = -1;
1856 }
1857 xmlNanoFTPFreeCtxt(ctxt);
1858 return(0);
1859}
1860
1861#ifdef STANDALONE
1862/************************************************************************
1863 * *
1864 * Basic test in Standalone mode *
1865 * *
1866 ************************************************************************/
1867void ftpList(void *userData, const char *filename, const char* attrib,
1868 const char *owner, const char *group, unsigned long size, int links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001869 int year, const char *month, int day, int hour, int minute) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001870 printf("%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1871}
1872void ftpData(void *userData, const char *data, int len) {
1873 if (userData == NULL) return;
1874 if (len <= 0) {
1875 fclose(userData);
1876 return;
1877 }
1878 fwrite(data, len, 1, userData);
1879}
1880
1881int main(int argc, char **argv) {
1882 void *ctxt;
1883 FILE *output;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001884 char *tstfile = NULL;
Daniel Veillardda07c342000-01-25 18:31:22 +00001885
1886 xmlNanoFTPInit();
1887 if (argc > 1) {
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001888 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1889 if (xmlNanoFTPConnect(ctxt) < 0) {
1890 fprintf(stderr, "Couldn't connect to %s\n", argv[1]);
1891 exit(1);
1892 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001893 if (argc > 2)
1894 tstfile = argv[2];
1895 } else
1896 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1897 if (ctxt == NULL) {
1898 fprintf(stderr, "Couldn't connect to localhost\n");
1899 exit(1);
1900 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001901 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
Daniel Veillardda07c342000-01-25 18:31:22 +00001902 output = fopen("/tmp/tstdata", "w");
1903 if (output != NULL) {
1904 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001905 fprintf(stderr, "Failed to get file\n");
Daniel Veillardda07c342000-01-25 18:31:22 +00001906
1907 }
1908 xmlNanoFTPClose(ctxt);
1909 xmlMemoryDump();
1910 exit(0);
1911}
1912#endif /* STANDALONE */
Daniel Veillard361d8452000-04-03 19:48:13 +00001913#else /* !LIBXML_FTP_ENABLED */
1914#ifdef STANDALONE
1915#include <stdio.h>
1916int main(int argc, char **argv) {
1917 printf("%s : FTP support not compiled in\n", argv[0]);
1918 return(0);
1919}
1920#endif /* STANDALONE */
1921#endif /* LIBXML_FTP_ENABLED */