blob: e0094025b6c1c50b8929eb23814669dc4d618c0c [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) {
Daniel Veillard2f2bf412000-08-20 15:11:02 +00001372#ifdef DEBUG_FTP
Daniel Veillardcf461992000-03-14 18:30:20 +00001373 fprintf(stderr, "xmlNanoFTPCloseConnection: timeout\n");
Daniel Veillard2f2bf412000-08-20 15:11:02 +00001374#endif
Daniel Veillardcf461992000-03-14 18:30:20 +00001375 close(ctxt->controlFd); ctxt->controlFd = -1;
1376 } else {
1377 res = xmlNanoFTPGetResponse(ctxt);
1378 if (res != 2) {
1379 close(ctxt->controlFd); ctxt->controlFd = -1;
1380 return(-1);
1381 }
1382 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001383 return(0);
1384}
1385
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001386/**
1387 * xmlNanoFTPParseList:
1388 * @list: some data listing received from the server
1389 * @callback: the user callback
1390 * @userData: the user callback data
1391 *
1392 * Parse at most one entry from the listing.
1393 *
1394 * Returns -1 incase of error, the lenght of data parsed otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001395 */
1396
1397static int
1398xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1399 const char *cur = list;
1400 char filename[151];
1401 char attrib[11];
1402 char owner[11];
1403 char group[11];
1404 char month[4];
1405 int year = 0;
1406 int minute = 0;
1407 int hour = 0;
1408 int day = 0;
1409 unsigned long size = 0;
1410 int links = 0;
1411 int i;
1412
1413 if (!strncmp(cur, "total", 5)) {
1414 cur += 5;
1415 while (*cur == ' ') cur++;
1416 while ((*cur >= '0') && (*cur <= '9'))
1417 links = (links * 10) + (*cur++ - '0');
1418 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1419 cur++;
1420 return(cur - list);
1421 } else if (*list == '+') {
1422 return(0);
1423 } else {
1424 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
1425 cur++;
1426 if (*cur == 0) return(0);
1427 i = 0;
1428 while (*cur != ' ') {
1429 if (i < 10)
1430 attrib[i++] = *cur;
1431 cur++;
1432 if (*cur == 0) return(0);
1433 }
1434 attrib[10] = 0;
1435 while (*cur == ' ') cur++;
1436 if (*cur == 0) return(0);
1437 while ((*cur >= '0') && (*cur <= '9'))
1438 links = (links * 10) + (*cur++ - '0');
1439 while (*cur == ' ') cur++;
1440 if (*cur == 0) return(0);
1441 i = 0;
1442 while (*cur != ' ') {
1443 if (i < 10)
1444 owner[i++] = *cur;
1445 cur++;
1446 if (*cur == 0) return(0);
1447 }
1448 owner[i] = 0;
1449 while (*cur == ' ') cur++;
1450 if (*cur == 0) return(0);
1451 i = 0;
1452 while (*cur != ' ') {
1453 if (i < 10)
1454 group[i++] = *cur;
1455 cur++;
1456 if (*cur == 0) return(0);
1457 }
1458 group[i] = 0;
1459 while (*cur == ' ') cur++;
1460 if (*cur == 0) return(0);
1461 while ((*cur >= '0') && (*cur <= '9'))
1462 size = (size * 10) + (*cur++ - '0');
1463 while (*cur == ' ') cur++;
1464 if (*cur == 0) return(0);
1465 i = 0;
1466 while (*cur != ' ') {
1467 if (i < 3)
1468 month[i++] = *cur;
1469 cur++;
1470 if (*cur == 0) return(0);
1471 }
1472 month[i] = 0;
1473 while (*cur == ' ') cur++;
1474 if (*cur == 0) return(0);
1475 while ((*cur >= '0') && (*cur <= '9'))
1476 day = (day * 10) + (*cur++ - '0');
1477 while (*cur == ' ') cur++;
1478 if (*cur == 0) return(0);
1479 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1480 if ((cur[1] == ':') || (cur[2] == ':')) {
1481 while ((*cur >= '0') && (*cur <= '9'))
1482 hour = (hour * 10) + (*cur++ - '0');
1483 if (*cur == ':') cur++;
1484 while ((*cur >= '0') && (*cur <= '9'))
1485 minute = (minute * 10) + (*cur++ - '0');
1486 } else {
1487 while ((*cur >= '0') && (*cur <= '9'))
1488 year = (year * 10) + (*cur++ - '0');
1489 }
1490 while (*cur == ' ') cur++;
1491 if (*cur == 0) return(0);
1492 i = 0;
1493 while ((*cur != '\n') && (*cur != '\r')) {
1494 if (i < 150)
1495 filename[i++] = *cur;
1496 cur++;
1497 if (*cur == 0) return(0);
1498 }
1499 filename[i] = 0;
1500 if ((*cur != '\n') && (*cur != '\r'))
1501 return(0);
1502 while ((*cur == '\n') || (*cur == '\r'))
1503 cur++;
1504 }
1505 if (callback != NULL) {
1506 callback(userData, filename, attrib, owner, group, size, links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001507 year, month, day, hour, minute);
Daniel Veillardda07c342000-01-25 18:31:22 +00001508 }
1509 return(cur - list);
1510}
1511
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001512/**
1513 * xmlNanoFTPList:
1514 * @ctx: an FTP context
1515 * @callback: the user callback
1516 * @userData: the user callback data
1517 * @filename: optional files to list
1518 *
1519 * Do a listing on the server. All files info are passed back
1520 * in the callbacks.
1521 *
1522 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001523 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001524
Daniel Veillardda07c342000-01-25 18:31:22 +00001525int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001526xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1527 char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001528 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1529 char buf[4096 + 1];
1530 int len, res;
1531 int index = 0, base;
1532 fd_set rfd, efd;
1533 struct timeval tv;
1534
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001535 if (filename == NULL) {
1536 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1537 return(-1);
1538 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001539 if (ctxt->dataFd == -1)
1540 return(-1);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001541#ifndef HAVE_SNPRINTF
1542 len = sprintf(buf, "LIST -L\r\n");
1543#else /* HAVE_SNPRINTF */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001544 len = snprintf(buf, sizeof(buf), "LIST -L\r\n");
Daniel Veillard13c757e2000-02-01 23:59:15 +00001545#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001546 } else {
1547 if (filename[0] != '/') {
1548 if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1549 return(-1);
1550 }
1551 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001552 if (ctxt->dataFd == -1)
1553 return(-1);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001554#ifndef HAVE_SNPRINTF
1555 len = sprintf(buf, "LIST -L %s\r\n", filename);
1556#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001557 len = snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001558#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001559 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001560#ifdef DEBUG_FTP
1561 printf(buf);
1562#endif
1563 res = send(ctxt->controlFd, buf, len, 0);
1564 if (res < 0) {
1565 close(ctxt->dataFd); ctxt->dataFd = -1;
1566 return(res);
1567 }
Daniel Veillard49703262000-07-10 10:27:46 +00001568 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001569 if (res != 1) {
1570 close(ctxt->dataFd); ctxt->dataFd = -1;
1571 return(-res);
1572 }
1573
1574 do {
1575 tv.tv_sec = 1;
1576 tv.tv_usec = 0;
1577 FD_ZERO(&rfd);
1578 FD_SET(ctxt->dataFd, &rfd);
1579 FD_ZERO(&efd);
1580 FD_SET(ctxt->dataFd, &efd);
1581 res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1582 if (res < 0) {
1583#ifdef DEBUG_FTP
1584 perror("select");
1585#endif
1586 close(ctxt->dataFd); ctxt->dataFd = -1;
1587 return(-1);
1588 }
1589 if (res == 0) {
1590 res = xmlNanoFTPCheckResponse(ctxt);
1591 if (res < 0) {
1592 close(ctxt->dataFd); ctxt->dataFd = -1;
1593 ctxt->dataFd = -1;
1594 return(-1);
1595 }
1596 if (res == 2) {
1597 close(ctxt->dataFd); ctxt->dataFd = -1;
1598 return(0);
1599 }
1600
1601 continue;
1602 }
1603
1604 if ((len = read(ctxt->dataFd, &buf[index], sizeof(buf) - (index + 1))) < 0) {
1605#ifdef DEBUG_FTP
1606 perror("read");
1607#endif
1608 close(ctxt->dataFd); ctxt->dataFd = -1;
1609 ctxt->dataFd = -1;
1610 return(-1);
1611 }
1612#ifdef DEBUG_FTP
1613 write(1, &buf[index], len);
1614#endif
1615 index += len;
1616 buf[index] = 0;
1617 base = 0;
1618 do {
1619 res = xmlNanoFTPParseList(&buf[base], callback, userData);
1620 base += res;
1621 } while (res > 0);
1622
1623 memmove(&buf[0], &buf[base], index - base);
1624 index -= base;
1625 } while (len != 0);
1626 xmlNanoFTPCloseConnection(ctxt);
1627 return(0);
1628}
1629
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001630/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001631 * xmlNanoFTPGetSocket:
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001632 * @ctx: an FTP context
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001633 * @filename: the file to retrieve (or NULL if path is in context).
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001634 *
1635 * Initiate fetch of the given file from the server.
1636 *
1637 * Returns the socket for the data connection, or <0 in case of error
Daniel Veillardda07c342000-01-25 18:31:22 +00001638 */
1639
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001640
Daniel Veillardda07c342000-01-25 18:31:22 +00001641int
1642xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1643 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1644 char buf[300];
1645 int res, len;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001646 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001647 return(-1);
1648 ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
Daniel Veillardbe803962000-06-28 23:40:59 +00001649 if (ctxt->dataFd == -1)
1650 return(-1);
Daniel Veillardda07c342000-01-25 18:31:22 +00001651
Daniel Veillard13c757e2000-02-01 23:59:15 +00001652#ifndef HAVE_SNPRINTF
1653 len = sprintf(buf, "TYPE I\r\n");
1654#else /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001655 len = snprintf(buf, sizeof(buf), "TYPE I\r\n");
Daniel Veillard13c757e2000-02-01 23:59:15 +00001656#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001657#ifdef DEBUG_FTP
1658 printf(buf);
1659#endif
1660 res = send(ctxt->controlFd, buf, len, 0);
1661 if (res < 0) {
1662 close(ctxt->dataFd); ctxt->dataFd = -1;
1663 return(res);
1664 }
Daniel Veillard49703262000-07-10 10:27:46 +00001665 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001666 if (res != 2) {
1667 close(ctxt->dataFd); ctxt->dataFd = -1;
1668 return(-res);
1669 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001670 if (filename == NULL)
Daniel Veillard13c757e2000-02-01 23:59:15 +00001671#ifndef HAVE_SNPRINTF
1672 len = sprintf(buf, "RETR %s\r\n", ctxt->path);
1673#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001674 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001675#endif /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001676 else
Daniel Veillard13c757e2000-02-01 23:59:15 +00001677#ifndef HAVE_SNPRINTF
1678 len = sprintf(buf, "RETR %s\r\n", filename);
1679#else /* HAVE_SNPRINTF */
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001680 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
Daniel Veillard13c757e2000-02-01 23:59:15 +00001681#endif /* HAVE_SNPRINTF */
Daniel Veillardda07c342000-01-25 18:31:22 +00001682#ifdef DEBUG_FTP
1683 printf(buf);
1684#endif
1685 res = send(ctxt->controlFd, buf, len, 0);
1686 if (res < 0) {
1687 close(ctxt->dataFd); ctxt->dataFd = -1;
1688 return(res);
1689 }
Daniel Veillard49703262000-07-10 10:27:46 +00001690 res = xmlNanoFTPReadResponse(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001691 if (res != 1) {
1692 close(ctxt->dataFd); ctxt->dataFd = -1;
1693 return(-res);
1694 }
1695 return(ctxt->dataFd);
1696}
1697
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001698/**
1699 * xmlNanoFTPGet:
1700 * @ctx: an FTP context
1701 * @callback: the user callback
1702 * @userData: the user callback data
1703 * @filename: the file to retrieve
1704 *
1705 * Fetch the given file from the server. All data are passed back
1706 * in the callbacks. The last callback has a size of 0 block.
1707 *
1708 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001709 */
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001710
Daniel Veillardda07c342000-01-25 18:31:22 +00001711int
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001712xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1713 const char *filename) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001714 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1715 char buf[4096];
1716 int len = 0, res;
1717 fd_set rfd;
1718 struct timeval tv;
1719
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001720 if ((filename == NULL) && (ctxt->path == NULL))
Daniel Veillardda07c342000-01-25 18:31:22 +00001721 return(-1);
1722 if (callback == NULL)
1723 return(-1);
1724 if (xmlNanoFTPGetSocket(ctxt, filename) < 0)
1725 return(-1);
1726
1727 do {
1728 tv.tv_sec = 1;
1729 tv.tv_usec = 0;
1730 FD_ZERO(&rfd);
1731 FD_SET(ctxt->dataFd, &rfd);
1732 res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1733 if (res < 0) {
1734#ifdef DEBUG_FTP
1735 perror("select");
1736#endif
1737 close(ctxt->dataFd); ctxt->dataFd = -1;
1738 return(-1);
1739 }
1740 if (res == 0) {
1741 res = xmlNanoFTPCheckResponse(ctxt);
1742 if (res < 0) {
1743 close(ctxt->dataFd); ctxt->dataFd = -1;
1744 ctxt->dataFd = -1;
1745 return(-1);
1746 }
1747 if (res == 2) {
1748 close(ctxt->dataFd); ctxt->dataFd = -1;
1749 return(0);
1750 }
1751
1752 continue;
1753 }
1754 if ((len = read(ctxt->dataFd, &buf, sizeof(buf))) < 0) {
1755 callback(userData, buf, len);
1756 close(ctxt->dataFd); ctxt->dataFd = -1;
1757 return(-1);
1758 }
1759 callback(userData, buf, len);
1760 } while (len != 0);
1761
1762 return(xmlNanoFTPCloseConnection(ctxt));
1763}
1764
1765/**
1766 * xmlNanoFTPRead:
1767 * @ctx: the FTP context
1768 * @dest: a buffer
1769 * @len: the buffer length
1770 *
1771 * This function tries to read @len bytes from the existing FTP connection
1772 * and saves them in @dest. This is a blocking call.
1773 *
1774 * Returns the number of byte read. 0 is an indication of an end of connection.
1775 * -1 indicates a parameter error.
1776 */
1777int
1778xmlNanoFTPRead(void *ctx, void *dest, int len) {
1779 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1780
1781 if (ctx == NULL) return(-1);
1782 if (ctxt->dataFd < 0) return(0);
1783 if (dest == NULL) return(-1);
1784 if (len <= 0) return(0);
1785
1786 len = read(ctxt->dataFd, dest, len);
1787#ifdef DEBUG_FTP
1788 printf("Read %d bytes\n", len);
1789#endif
1790 if (len <= 0) {
1791 xmlNanoFTPCloseConnection(ctxt);
1792 }
1793 return(len);
1794}
1795
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001796/**
Daniel Veillardda07c342000-01-25 18:31:22 +00001797 * xmlNanoFTPOpen:
1798 * @URL: the URL to the resource
1799 *
1800 * Start to fetch the given ftp:// resource
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001801 *
1802 * Returns an FTP context, or NULL
Daniel Veillardda07c342000-01-25 18:31:22 +00001803 */
1804
Daniel Veillard06047432000-04-24 11:33:38 +00001805void*
Daniel Veillardda07c342000-01-25 18:31:22 +00001806xmlNanoFTPOpen(const char *URL) {
1807 xmlNanoFTPCtxtPtr ctxt;
1808 int sock;
1809
1810 xmlNanoFTPInit();
1811 if (URL == NULL) return(NULL);
1812 if (strncmp("ftp://", URL, 6)) return(NULL);
1813
Daniel Veillard49703262000-07-10 10:27:46 +00001814 ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
Daniel Veillardda07c342000-01-25 18:31:22 +00001815 if (ctxt == NULL) return(NULL);
1816 if (xmlNanoFTPConnect(ctxt) < 0) {
1817 xmlNanoFTPFreeCtxt(ctxt);
1818 return(NULL);
1819 }
1820 sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
1821 if (sock < 0) {
1822 xmlNanoFTPFreeCtxt(ctxt);
1823 return(NULL);
1824 }
1825 return(ctxt);
1826}
1827
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001828/**
1829 * xmlNanoFTPClose:
1830 * @ctx: an FTP context
1831 *
1832 * Close the connection and both control and transport
1833 *
1834 * Returns -1 incase of error, 0 otherwise
Daniel Veillardda07c342000-01-25 18:31:22 +00001835 */
1836
1837int
1838xmlNanoFTPClose(void *ctx) {
1839 xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1840
1841 if (ctxt == NULL)
1842 return(-1);
1843
1844 if (ctxt->dataFd >= 0) {
1845 close(ctxt->dataFd);
1846 ctxt->dataFd = -1;
1847 }
1848 if (ctxt->controlFd >= 0) {
Daniel Veillardaeea04f2000-01-25 19:27:27 +00001849 xmlNanoFTPQuit(ctxt);
Daniel Veillardda07c342000-01-25 18:31:22 +00001850 close(ctxt->controlFd);
1851 ctxt->controlFd = -1;
1852 }
1853 xmlNanoFTPFreeCtxt(ctxt);
1854 return(0);
1855}
1856
1857#ifdef STANDALONE
1858/************************************************************************
1859 * *
1860 * Basic test in Standalone mode *
1861 * *
1862 ************************************************************************/
1863void ftpList(void *userData, const char *filename, const char* attrib,
1864 const char *owner, const char *group, unsigned long size, int links,
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001865 int year, const char *month, int day, int hour, int minute) {
Daniel Veillardda07c342000-01-25 18:31:22 +00001866 printf("%s %s %s %ld %s\n", attrib, owner, group, size, filename);
1867}
1868void ftpData(void *userData, const char *data, int len) {
1869 if (userData == NULL) return;
1870 if (len <= 0) {
1871 fclose(userData);
1872 return;
1873 }
1874 fwrite(data, len, 1, userData);
1875}
1876
1877int main(int argc, char **argv) {
1878 void *ctxt;
1879 FILE *output;
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001880 char *tstfile = NULL;
Daniel Veillardda07c342000-01-25 18:31:22 +00001881
1882 xmlNanoFTPInit();
1883 if (argc > 1) {
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001884 ctxt = xmlNanoFTPNewCtxt(argv[1]);
1885 if (xmlNanoFTPConnect(ctxt) < 0) {
1886 fprintf(stderr, "Couldn't connect to %s\n", argv[1]);
1887 exit(1);
1888 }
Daniel Veillardda07c342000-01-25 18:31:22 +00001889 if (argc > 2)
1890 tstfile = argv[2];
1891 } else
1892 ctxt = xmlNanoFTPConnectTo("localhost", 0);
1893 if (ctxt == NULL) {
1894 fprintf(stderr, "Couldn't connect to localhost\n");
1895 exit(1);
1896 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001897 xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
Daniel Veillardda07c342000-01-25 18:31:22 +00001898 output = fopen("/tmp/tstdata", "w");
1899 if (output != NULL) {
1900 if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001901 fprintf(stderr, "Failed to get file\n");
Daniel Veillardda07c342000-01-25 18:31:22 +00001902
1903 }
1904 xmlNanoFTPClose(ctxt);
1905 xmlMemoryDump();
1906 exit(0);
1907}
1908#endif /* STANDALONE */
Daniel Veillard361d8452000-04-03 19:48:13 +00001909#else /* !LIBXML_FTP_ENABLED */
1910#ifdef STANDALONE
1911#include <stdio.h>
1912int main(int argc, char **argv) {
1913 printf("%s : FTP support not compiled in\n", argv[0]);
1914 return(0);
1915}
1916#endif /* STANDALONE */
1917#endif /* LIBXML_FTP_ENABLED */