Coverage Report

Created: 2025-08-04 07:15

/src/libxml2-2.9.7/nanoftp.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * nanoftp.c: basic FTP client support
3
 *
4
 *  Reference: RFC 959
5
 */
6
7
#ifdef TESTING
8
#define STANDALONE
9
#define HAVE_STDLIB_H
10
#define HAVE_UNISTD_H
11
#define HAVE_SYS_SOCKET_H
12
#define HAVE_NETINET_IN_H
13
#define HAVE_NETDB_H
14
#define HAVE_SYS_TIME_H
15
#endif /* TESTING */
16
17
#define IN_LIBXML
18
#include "libxml.h"
19
20
#ifdef LIBXML_FTP_ENABLED
21
#include <string.h>
22
23
#ifdef HAVE_STDLIB_H
24
#include <stdlib.h>
25
#endif
26
#ifdef HAVE_UNISTD_H
27
#include <unistd.h>
28
#endif
29
#ifdef HAVE_SYS_SOCKET_H
30
#include <sys/socket.h>
31
#endif
32
#ifdef HAVE_NETINET_IN_H
33
#include <netinet/in.h>
34
#endif
35
#ifdef HAVE_ARPA_INET_H
36
#include <arpa/inet.h>
37
#endif
38
#ifdef HAVE_NETDB_H
39
#include <netdb.h>
40
#endif
41
#ifdef HAVE_FCNTL_H
42
#include <fcntl.h>
43
#endif
44
#ifdef HAVE_ERRNO_H
45
#include <errno.h>
46
#endif
47
#ifdef HAVE_SYS_TIME_H
48
#include <sys/time.h>
49
#endif
50
#ifdef HAVE_SYS_SELECT_H
51
#include <sys/select.h>
52
#endif
53
#ifdef HAVE_SYS_SOCKET_H
54
#include <sys/socket.h>
55
#endif
56
#ifdef HAVE_SYS_TYPES_H
57
#include <sys/types.h>
58
#endif
59
#ifdef HAVE_STRINGS_H
60
#include <strings.h>
61
#endif
62
63
#include <libxml/xmlmemory.h>
64
#include <libxml/parser.h>
65
#include <libxml/xmlerror.h>
66
#include <libxml/uri.h>
67
#include <libxml/nanoftp.h>
68
#include <libxml/globals.h>
69
70
/* #define DEBUG_FTP 1  */
71
#ifdef STANDALONE
72
#ifndef DEBUG_FTP
73
#define DEBUG_FTP 1
74
#endif
75
#endif
76
77
78
#if defined(_WIN32) && !defined(__CYGWIN__)
79
#include <wsockcompat.h>
80
#endif
81
82
/**
83
 * A couple portability macros
84
 */
85
#ifndef _WINSOCKAPI_
86
#if !defined(__BEOS__) || defined(__HAIKU__)
87
0
#define closesocket(s) close(s)
88
#endif
89
#endif
90
91
#ifdef __BEOS__
92
#ifndef PF_INET
93
#define PF_INET AF_INET
94
#endif
95
#endif
96
97
#ifdef _AIX
98
#ifdef HAVE_BROKEN_SS_FAMILY
99
#define ss_family __ss_family
100
#endif
101
#endif
102
103
#ifndef XML_SOCKLEN_T
104
#define XML_SOCKLEN_T unsigned int
105
#endif
106
107
#define FTP_COMMAND_OK    200
108
#define FTP_SYNTAX_ERROR  500
109
#define FTP_GET_PASSWD    331
110
0
#define FTP_BUF_SIZE    1024
111
112
#define XML_NANO_MAX_URLBUF 4096
113
114
typedef struct xmlNanoFTPCtxt {
115
    char *protocol; /* the protocol name */
116
    char *hostname; /* the host name */
117
    int port;   /* the port */
118
    char *path;   /* the path within the URL */
119
    char *user;   /* user string */
120
    char *passwd; /* passwd string */
121
#ifdef SUPPORT_IP6
122
    struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
123
#else
124
    struct sockaddr_in ftpAddr; /* the socket address struct */
125
#endif
126
    int passive;  /* currently we support only passive !!! */
127
    SOCKET controlFd; /* the file descriptor for the control socket */
128
    SOCKET dataFd;  /* the file descriptor for the data socket */
129
    int state;    /* WRITE / READ / CLOSED */
130
    int returnValue;  /* the protocol return value */
131
    /* buffer for data received from the control connection */
132
    char controlBuf[FTP_BUF_SIZE + 1];
133
    int controlBufIndex;
134
    int controlBufUsed;
135
    int controlBufAnswer;
136
} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
137
138
static int initialized = 0;
139
static char *proxy = NULL;  /* the proxy name if any */
140
static int proxyPort = 0; /* the proxy port if any */
141
static char *proxyUser = NULL;  /* user for proxy authentication */
142
static char *proxyPasswd = NULL;/* passwd for proxy authentication */
143
static int proxyType = 0; /* uses TYPE or a@b ? */
144
145
#ifdef SUPPORT_IP6
146
static
147
int have_ipv6(void) {
148
    int s;
149
150
    s = socket (AF_INET6, SOCK_STREAM, 0);
151
    if (s != -1) {
152
  close (s);
153
  return (1);
154
    }
155
    return (0);
156
}
157
#endif
158
159
/**
160
 * xmlFTPErrMemory:
161
 * @extra:  extra informations
162
 *
163
 * Handle an out of memory condition
164
 */
165
static void
166
xmlFTPErrMemory(const char *extra)
167
0
{
168
0
    __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
169
0
}
170
171
/**
172
 * xmlNanoFTPInit:
173
 *
174
 * Initialize the FTP protocol layer.
175
 * Currently it just checks for proxy informations,
176
 * and get the hostname
177
 */
178
179
void
180
0
xmlNanoFTPInit(void) {
181
0
    const char *env;
182
#ifdef _WINSOCKAPI_
183
    WSADATA wsaData;
184
#endif
185
186
0
    if (initialized)
187
0
  return;
188
189
#ifdef _WINSOCKAPI_
190
    if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
191
  return;
192
#endif
193
194
0
    proxyPort = 21;
195
0
    env = getenv("no_proxy");
196
0
    if (env && ((env[0] == '*' ) && (env[1] == 0)))
197
0
  return;
198
0
    env = getenv("ftp_proxy");
199
0
    if (env != NULL) {
200
0
  xmlNanoFTPScanProxy(env);
201
0
    } else {
202
0
  env = getenv("FTP_PROXY");
203
0
  if (env != NULL) {
204
0
      xmlNanoFTPScanProxy(env);
205
0
  }
206
0
    }
207
0
    env = getenv("ftp_proxy_user");
208
0
    if (env != NULL) {
209
0
  proxyUser = xmlMemStrdup(env);
210
0
    }
211
0
    env = getenv("ftp_proxy_password");
212
0
    if (env != NULL) {
213
0
  proxyPasswd = xmlMemStrdup(env);
214
0
    }
215
0
    initialized = 1;
216
0
}
217
218
/**
219
 * xmlNanoFTPCleanup:
220
 *
221
 * Cleanup the FTP protocol layer. This cleanup proxy informations.
222
 */
223
224
void
225
0
xmlNanoFTPCleanup(void) {
226
0
    if (proxy != NULL) {
227
0
  xmlFree(proxy);
228
0
  proxy = NULL;
229
0
    }
230
0
    if (proxyUser != NULL) {
231
0
  xmlFree(proxyUser);
232
0
  proxyUser = NULL;
233
0
    }
234
0
    if (proxyPasswd != NULL) {
235
0
  xmlFree(proxyPasswd);
236
0
  proxyPasswd = NULL;
237
0
    }
238
#ifdef _WINSOCKAPI_
239
    if (initialized)
240
  WSACleanup();
241
#endif
242
0
    initialized = 0;
243
0
}
244
245
/**
246
 * xmlNanoFTPProxy:
247
 * @host:  the proxy host name
248
 * @port:  the proxy port
249
 * @user:  the proxy user name
250
 * @passwd:  the proxy password
251
 * @type:  the type of proxy 1 for using SITE, 2 for USER a@b
252
 *
253
 * Setup the FTP proxy informations.
254
 * This can also be done by using ftp_proxy ftp_proxy_user and
255
 * ftp_proxy_password environment variables.
256
 */
257
258
void
259
xmlNanoFTPProxy(const char *host, int port, const char *user,
260
0
          const char *passwd, int type) {
261
0
    if (proxy != NULL) {
262
0
  xmlFree(proxy);
263
0
  proxy = NULL;
264
0
    }
265
0
    if (proxyUser != NULL) {
266
0
  xmlFree(proxyUser);
267
0
  proxyUser = NULL;
268
0
    }
269
0
    if (proxyPasswd != NULL) {
270
0
  xmlFree(proxyPasswd);
271
0
  proxyPasswd = NULL;
272
0
    }
273
0
    if (host)
274
0
  proxy = xmlMemStrdup(host);
275
0
    if (user)
276
0
  proxyUser = xmlMemStrdup(user);
277
0
    if (passwd)
278
0
  proxyPasswd = xmlMemStrdup(passwd);
279
0
    proxyPort = port;
280
0
    proxyType = type;
281
0
}
282
283
/**
284
 * xmlNanoFTPScanURL:
285
 * @ctx:  an FTP context
286
 * @URL:  The URL used to initialize the context
287
 *
288
 * (Re)Initialize an FTP context by parsing the URL and finding
289
 * the protocol host port and path it indicates.
290
 */
291
292
static void
293
0
xmlNanoFTPScanURL(void *ctx, const char *URL) {
294
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
295
0
    xmlURIPtr uri;
296
297
    /*
298
     * Clear any existing data from the context
299
     */
300
0
    if (ctxt->protocol != NULL) {
301
0
        xmlFree(ctxt->protocol);
302
0
  ctxt->protocol = NULL;
303
0
    }
304
0
    if (ctxt->hostname != NULL) {
305
0
        xmlFree(ctxt->hostname);
306
0
  ctxt->hostname = NULL;
307
0
    }
308
0
    if (ctxt->path != NULL) {
309
0
        xmlFree(ctxt->path);
310
0
  ctxt->path = NULL;
311
0
    }
312
0
    if (URL == NULL) return;
313
314
0
    uri = xmlParseURIRaw(URL, 1);
315
0
    if (uri == NULL)
316
0
  return;
317
318
0
    if ((uri->scheme == NULL) || (uri->server == NULL)) {
319
0
  xmlFreeURI(uri);
320
0
  return;
321
0
    }
322
323
0
    ctxt->protocol = xmlMemStrdup(uri->scheme);
324
0
    ctxt->hostname = xmlMemStrdup(uri->server);
325
0
    if (uri->path != NULL)
326
0
  ctxt->path = xmlMemStrdup(uri->path);
327
0
    else
328
0
  ctxt->path = xmlMemStrdup("/");
329
0
    if (uri->port != 0)
330
0
  ctxt->port = uri->port;
331
332
0
    if (uri->user != NULL) {
333
0
  char *cptr;
334
0
  if ((cptr=strchr(uri->user, ':')) == NULL)
335
0
      ctxt->user = xmlMemStrdup(uri->user);
336
0
  else {
337
0
      ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
338
0
          (cptr - uri->user));
339
0
      ctxt->passwd = xmlMemStrdup(cptr+1);
340
0
  }
341
0
    }
342
343
0
    xmlFreeURI(uri);
344
345
0
}
346
347
/**
348
 * xmlNanoFTPUpdateURL:
349
 * @ctx:  an FTP context
350
 * @URL:  The URL used to update the context
351
 *
352
 * Update an FTP context by parsing the URL and finding
353
 * new path it indicates. If there is an error in the
354
 * protocol, hostname, port or other information, the
355
 * error is raised. It indicates a new connection has to
356
 * be established.
357
 *
358
 * Returns 0 if Ok, -1 in case of error (other host).
359
 */
360
361
int
362
0
xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
363
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
364
0
    xmlURIPtr uri;
365
366
0
    if (URL == NULL)
367
0
  return(-1);
368
0
    if (ctxt == NULL)
369
0
  return(-1);
370
0
    if (ctxt->protocol == NULL)
371
0
  return(-1);
372
0
    if (ctxt->hostname == NULL)
373
0
  return(-1);
374
375
0
    uri = xmlParseURIRaw(URL, 1);
376
0
    if (uri == NULL)
377
0
  return(-1);
378
379
0
    if ((uri->scheme == NULL) || (uri->server == NULL)) {
380
0
  xmlFreeURI(uri);
381
0
  return(-1);
382
0
    }
383
0
    if ((strcmp(ctxt->protocol, uri->scheme)) ||
384
0
  (strcmp(ctxt->hostname, uri->server)) ||
385
0
  ((uri->port != 0) && (ctxt->port != uri->port))) {
386
0
  xmlFreeURI(uri);
387
0
  return(-1);
388
0
    }
389
390
0
    if (uri->port != 0)
391
0
  ctxt->port = uri->port;
392
393
0
    if (ctxt->path != NULL) {
394
0
  xmlFree(ctxt->path);
395
0
  ctxt->path = NULL;
396
0
    }
397
398
0
    if (uri->path == NULL)
399
0
        ctxt->path = xmlMemStrdup("/");
400
0
    else
401
0
  ctxt->path = xmlMemStrdup(uri->path);
402
403
0
    xmlFreeURI(uri);
404
405
0
    return(0);
406
0
}
407
408
/**
409
 * xmlNanoFTPScanProxy:
410
 * @URL:  The proxy URL used to initialize the proxy context
411
 *
412
 * (Re)Initialize the FTP Proxy context by parsing the URL and finding
413
 * the protocol host port it indicates.
414
 * Should be like ftp://myproxy/ or ftp://myproxy:3128/
415
 * A NULL URL cleans up proxy informations.
416
 */
417
418
void
419
0
xmlNanoFTPScanProxy(const char *URL) {
420
0
    xmlURIPtr uri;
421
422
0
    if (proxy != NULL) {
423
0
        xmlFree(proxy);
424
0
  proxy = NULL;
425
0
    }
426
0
    proxyPort = 0;
427
428
#ifdef DEBUG_FTP
429
    if (URL == NULL)
430
  xmlGenericError(xmlGenericErrorContext,
431
    "Removing FTP proxy info\n");
432
    else
433
  xmlGenericError(xmlGenericErrorContext,
434
    "Using FTP proxy %s\n", URL);
435
#endif
436
0
    if (URL == NULL) return;
437
438
0
    uri = xmlParseURIRaw(URL, 1);
439
0
    if ((uri == NULL) || (uri->scheme == NULL) ||
440
0
  (strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
441
0
  __xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
442
0
  if (uri != NULL)
443
0
      xmlFreeURI(uri);
444
0
  return;
445
0
    }
446
447
0
    proxy = xmlMemStrdup(uri->server);
448
0
    if (uri->port != 0)
449
0
  proxyPort = uri->port;
450
451
0
    xmlFreeURI(uri);
452
0
}
453
454
/**
455
 * xmlNanoFTPNewCtxt:
456
 * @URL:  The URL used to initialize the context
457
 *
458
 * Allocate and initialize a new FTP context.
459
 *
460
 * Returns an FTP context or NULL in case of error.
461
 */
462
463
void*
464
0
xmlNanoFTPNewCtxt(const char *URL) {
465
0
    xmlNanoFTPCtxtPtr ret;
466
0
    char *unescaped;
467
468
0
    ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
469
0
    if (ret == NULL) {
470
0
        xmlFTPErrMemory("allocating FTP context");
471
0
        return(NULL);
472
0
    }
473
474
0
    memset(ret, 0, sizeof(xmlNanoFTPCtxt));
475
0
    ret->port = 21;
476
0
    ret->passive = 1;
477
0
    ret->returnValue = 0;
478
0
    ret->controlBufIndex = 0;
479
0
    ret->controlBufUsed = 0;
480
0
    ret->controlFd = INVALID_SOCKET;
481
482
0
    unescaped = xmlURIUnescapeString(URL, 0, NULL);
483
0
    if (unescaped != NULL) {
484
0
  xmlNanoFTPScanURL(ret, unescaped);
485
0
  xmlFree(unescaped);
486
0
    } else if (URL != NULL)
487
0
  xmlNanoFTPScanURL(ret, URL);
488
489
0
    return(ret);
490
0
}
491
492
/**
493
 * xmlNanoFTPFreeCtxt:
494
 * @ctx:  an FTP context
495
 *
496
 * Frees the context after closing the connection.
497
 */
498
499
void
500
0
xmlNanoFTPFreeCtxt(void * ctx) {
501
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
502
0
    if (ctxt == NULL) return;
503
0
    if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
504
0
    if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
505
0
    if (ctxt->path != NULL) xmlFree(ctxt->path);
506
0
    if (ctxt->user != NULL) xmlFree(ctxt->user);
507
0
    if (ctxt->passwd != NULL) xmlFree(ctxt->passwd);
508
0
    ctxt->passive = 1;
509
0
    if (ctxt->controlFd != INVALID_SOCKET) closesocket(ctxt->controlFd);
510
0
    ctxt->controlFd = INVALID_SOCKET;
511
0
    ctxt->controlBufIndex = -1;
512
0
    ctxt->controlBufUsed = -1;
513
0
    xmlFree(ctxt);
514
0
}
515
516
/**
517
 * xmlNanoFTPParseResponse:
518
 * @buf:  the buffer containing the response
519
 * @len:  the buffer length
520
 *
521
 * Parsing of the server answer, we just extract the code.
522
 *
523
 * returns 0 for errors
524
 *     +XXX for last line of response
525
 *     -XXX for response to be continued
526
 */
527
static int
528
0
xmlNanoFTPParseResponse(char *buf, int len) {
529
0
    int val = 0;
530
531
0
    if (len < 3) return(-1);
532
0
    if ((*buf >= '0') && (*buf <= '9'))
533
0
        val = val * 10 + (*buf - '0');
534
0
    else
535
0
        return(0);
536
0
    buf++;
537
0
    if ((*buf >= '0') && (*buf <= '9'))
538
0
        val = val * 10 + (*buf - '0');
539
0
    else
540
0
        return(0);
541
0
    buf++;
542
0
    if ((*buf >= '0') && (*buf <= '9'))
543
0
        val = val * 10 + (*buf - '0');
544
0
    else
545
0
        return(0);
546
0
    buf++;
547
0
    if (*buf == '-')
548
0
        return(-val);
549
0
    return(val);
550
0
}
551
552
/**
553
 * xmlNanoFTPGetMore:
554
 * @ctx:  an FTP context
555
 *
556
 * Read more information from the FTP control connection
557
 * Returns the number of bytes read, < 0 indicates an error
558
 */
559
static int
560
0
xmlNanoFTPGetMore(void *ctx) {
561
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
562
0
    int len;
563
0
    int size;
564
565
0
    if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
566
567
0
    if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
568
#ifdef DEBUG_FTP
569
        xmlGenericError(xmlGenericErrorContext,
570
    "xmlNanoFTPGetMore : controlBufIndex = %d\n",
571
    ctxt->controlBufIndex);
572
#endif
573
0
  return(-1);
574
0
    }
575
576
0
    if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
577
#ifdef DEBUG_FTP
578
        xmlGenericError(xmlGenericErrorContext,
579
    "xmlNanoFTPGetMore : controlBufUsed = %d\n",
580
    ctxt->controlBufUsed);
581
#endif
582
0
  return(-1);
583
0
    }
584
0
    if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
585
#ifdef DEBUG_FTP
586
        xmlGenericError(xmlGenericErrorContext,
587
    "xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
588
         ctxt->controlBufIndex, ctxt->controlBufUsed);
589
#endif
590
0
  return(-1);
591
0
    }
592
593
    /*
594
     * First pack the control buffer
595
     */
596
0
    if (ctxt->controlBufIndex > 0) {
597
0
  memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
598
0
    ctxt->controlBufUsed - ctxt->controlBufIndex);
599
0
  ctxt->controlBufUsed -= ctxt->controlBufIndex;
600
0
  ctxt->controlBufIndex = 0;
601
0
    }
602
0
    size = FTP_BUF_SIZE - ctxt->controlBufUsed;
603
0
    if (size == 0) {
604
#ifdef DEBUG_FTP
605
        xmlGenericError(xmlGenericErrorContext,
606
    "xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
607
#endif
608
0
  return(0);
609
0
    }
610
611
    /*
612
     * Read the amount left on the control connection
613
     */
614
0
    if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
615
0
        size, 0)) < 0) {
616
0
  __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
617
0
  closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
618
0
        ctxt->controlFd = INVALID_SOCKET;
619
0
        return(-1);
620
0
    }
621
#ifdef DEBUG_FTP
622
    xmlGenericError(xmlGenericErrorContext,
623
      "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
624
     ctxt->controlBufUsed, ctxt->controlBufUsed + len);
625
#endif
626
0
    ctxt->controlBufUsed += len;
627
0
    ctxt->controlBuf[ctxt->controlBufUsed] = 0;
628
629
0
    return(len);
630
0
}
631
632
/**
633
 * xmlNanoFTPReadResponse:
634
 * @ctx:  an FTP context
635
 *
636
 * Read the response from the FTP server after a command.
637
 * Returns the code number
638
 */
639
static int
640
0
xmlNanoFTPReadResponse(void *ctx) {
641
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
642
0
    char *ptr, *end;
643
0
    int len;
644
0
    int res = -1, cur = -1;
645
646
0
    if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
647
648
0
get_more:
649
    /*
650
     * Assumes everything up to controlBuf[controlBufIndex] has been read
651
     * and analyzed.
652
     */
653
0
    len = xmlNanoFTPGetMore(ctx);
654
0
    if (len < 0) {
655
0
        return(-1);
656
0
    }
657
0
    if ((ctxt->controlBufUsed == 0) && (len == 0)) {
658
0
        return(-1);
659
0
    }
660
0
    ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
661
0
    end = &ctxt->controlBuf[ctxt->controlBufUsed];
662
663
#ifdef DEBUG_FTP
664
    xmlGenericError(xmlGenericErrorContext,
665
      "\n<<<\n%s\n--\n", ptr);
666
#endif
667
0
    while (ptr < end) {
668
0
        cur = xmlNanoFTPParseResponse(ptr, end - ptr);
669
0
  if (cur > 0) {
670
      /*
671
       * Successfully scanned the control code, scratch
672
       * till the end of the line, but keep the index to be
673
       * able to analyze the result if needed.
674
       */
675
0
      res = cur;
676
0
      ptr += 3;
677
0
      ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
678
0
      while ((ptr < end) && (*ptr != '\n')) ptr++;
679
0
      if (*ptr == '\n') ptr++;
680
0
      if (*ptr == '\r') ptr++;
681
0
      break;
682
0
  }
683
0
  while ((ptr < end) && (*ptr != '\n')) ptr++;
684
0
  if (ptr >= end) {
685
0
      ctxt->controlBufIndex = ctxt->controlBufUsed;
686
0
      goto get_more;
687
0
  }
688
0
  if (*ptr != '\r') ptr++;
689
0
    }
690
691
0
    if (res < 0) goto get_more;
692
0
    ctxt->controlBufIndex = ptr - ctxt->controlBuf;
693
#ifdef DEBUG_FTP
694
    ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
695
    xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
696
#endif
697
698
#ifdef DEBUG_FTP
699
    xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
700
#endif
701
0
    return(res / 100);
702
0
}
703
704
/**
705
 * xmlNanoFTPGetResponse:
706
 * @ctx:  an FTP context
707
 *
708
 * Get the response from the FTP server after a command.
709
 * Returns the code number
710
 */
711
712
int
713
0
xmlNanoFTPGetResponse(void *ctx) {
714
0
    int res;
715
716
0
    res = xmlNanoFTPReadResponse(ctx);
717
718
0
    return(res);
719
0
}
720
721
/**
722
 * xmlNanoFTPCheckResponse:
723
 * @ctx:  an FTP context
724
 *
725
 * Check if there is a response from the FTP server after a command.
726
 * Returns the code number, or 0
727
 */
728
729
int
730
0
xmlNanoFTPCheckResponse(void *ctx) {
731
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
732
0
    fd_set rfd;
733
0
    struct timeval tv;
734
735
0
    if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
736
0
    tv.tv_sec = 0;
737
0
    tv.tv_usec = 0;
738
0
    FD_ZERO(&rfd);
739
0
    FD_SET(ctxt->controlFd, &rfd);
740
0
    switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
741
0
  case 0:
742
0
      return(0);
743
0
  case -1:
744
0
      __xmlIOErr(XML_FROM_FTP, 0, "select");
745
0
      return(-1);
746
747
0
    }
748
749
0
    return(xmlNanoFTPReadResponse(ctx));
750
0
}
751
752
/**
753
 * Send the user authentication
754
 */
755
756
static int
757
0
xmlNanoFTPSendUser(void *ctx) {
758
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
759
0
    char buf[200];
760
0
    int len;
761
0
    int res;
762
763
0
    if (ctxt->user == NULL)
764
0
  snprintf(buf, sizeof(buf), "USER anonymous\r\n");
765
0
    else
766
0
  snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
767
0
    buf[sizeof(buf) - 1] = 0;
768
0
    len = strlen(buf);
769
#ifdef DEBUG_FTP
770
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
771
#endif
772
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
773
0
    if (res < 0) {
774
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
775
0
  return(res);
776
0
    }
777
0
    return(0);
778
0
}
779
780
/**
781
 * Send the password authentication
782
 */
783
784
static int
785
0
xmlNanoFTPSendPasswd(void *ctx) {
786
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
787
0
    char buf[200];
788
0
    int len;
789
0
    int res;
790
791
0
    if (ctxt->passwd == NULL)
792
0
  snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
793
0
    else
794
0
  snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
795
0
    buf[sizeof(buf) - 1] = 0;
796
0
    len = strlen(buf);
797
#ifdef DEBUG_FTP
798
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
799
#endif
800
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
801
0
    if (res < 0) {
802
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
803
0
  return(res);
804
0
    }
805
0
    return(0);
806
0
}
807
808
/**
809
 * xmlNanoFTPQuit:
810
 * @ctx:  an FTP context
811
 *
812
 * Send a QUIT command to the server
813
 *
814
 * Returns -1 in case of error, 0 otherwise
815
 */
816
817
818
int
819
0
xmlNanoFTPQuit(void *ctx) {
820
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
821
0
    char buf[200];
822
0
    int len, res;
823
824
0
    if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
825
826
0
    snprintf(buf, sizeof(buf), "QUIT\r\n");
827
0
    len = strlen(buf);
828
#ifdef DEBUG_FTP
829
    xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
830
#endif
831
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
832
0
    if (res < 0) {
833
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
834
0
  return(res);
835
0
    }
836
0
    return(0);
837
0
}
838
839
/**
840
 * xmlNanoFTPConnect:
841
 * @ctx:  an FTP context
842
 *
843
 * Tries to open a control connection
844
 *
845
 * Returns -1 in case of error, 0 otherwise
846
 */
847
848
int
849
0
xmlNanoFTPConnect(void *ctx) {
850
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
851
0
    struct hostent *hp;
852
0
    int port;
853
0
    int res;
854
0
    int addrlen = sizeof (struct sockaddr_in);
855
856
0
    if (ctxt == NULL)
857
0
  return(-1);
858
0
    if (ctxt->hostname == NULL)
859
0
  return(-1);
860
861
    /*
862
     * do the blocking DNS query.
863
     */
864
0
    if (proxy) {
865
0
        port = proxyPort;
866
0
    } else {
867
0
  port = ctxt->port;
868
0
    }
869
0
    if (port == 0)
870
0
  port = 21;
871
872
0
    memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
873
874
#ifdef SUPPORT_IP6
875
    if (have_ipv6 ()) {
876
  struct addrinfo hints, *tmp, *result;
877
878
  result = NULL;
879
  memset (&hints, 0, sizeof(hints));
880
  hints.ai_socktype = SOCK_STREAM;
881
882
  if (proxy) {
883
      if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
884
    __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
885
    return (-1);
886
      }
887
  }
888
  else
889
      if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
890
    __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
891
    return (-1);
892
      }
893
894
  for (tmp = result; tmp; tmp = tmp->ai_next)
895
      if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
896
    break;
897
898
  if (!tmp) {
899
      if (result)
900
    freeaddrinfo (result);
901
      __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
902
      return (-1);
903
  }
904
  if ((size_t)tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
905
      if (result)
906
    freeaddrinfo (result);
907
      __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
908
      return (-1);
909
  }
910
  if (tmp->ai_family == AF_INET6) {
911
      memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
912
      ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
913
      ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
914
  }
915
  else {
916
      memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
917
      ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
918
      ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
919
  }
920
  addrlen = tmp->ai_addrlen;
921
  freeaddrinfo (result);
922
    }
923
    else
924
#endif
925
0
    {
926
0
  if (proxy)
927
0
      hp = gethostbyname (GETHOSTBYNAME_ARG_CAST proxy);
928
0
  else
929
0
      hp = gethostbyname (GETHOSTBYNAME_ARG_CAST ctxt->hostname);
930
0
  if (hp == NULL) {
931
0
      __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
932
0
      return (-1);
933
0
  }
934
0
  if ((unsigned int) hp->h_length >
935
0
      sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
936
0
      __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
937
0
      return (-1);
938
0
  }
939
940
  /*
941
   * Prepare the socket
942
   */
943
0
  ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
944
0
  memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
945
0
    hp->h_addr_list[0], hp->h_length);
946
0
  ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port =
947
0
             (unsigned short)htons ((unsigned short)port);
948
0
  ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
949
0
  addrlen = sizeof (struct sockaddr_in);
950
0
    }
951
952
0
    if (ctxt->controlFd == INVALID_SOCKET) {
953
0
  __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
954
0
        return(-1);
955
0
    }
956
957
    /*
958
     * Do the connect.
959
     */
960
0
    if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
961
0
      addrlen) < 0) {
962
0
  __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
963
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
964
0
        ctxt->controlFd = INVALID_SOCKET;
965
0
  return(-1);
966
0
    }
967
968
    /*
969
     * Wait for the HELLO from the server.
970
     */
971
0
    res = xmlNanoFTPGetResponse(ctxt);
972
0
    if (res != 2) {
973
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
974
0
        ctxt->controlFd = INVALID_SOCKET;
975
0
  return(-1);
976
0
    }
977
978
    /*
979
     * State diagram for the login operation on the FTP server
980
     *
981
     * Reference: RFC 959
982
     *
983
     *                       1
984
     * +---+   USER    +---+------------->+---+
985
     * | B |---------->| W | 2       ---->| E |
986
     * +---+           +---+------  |  -->+---+
987
     *                  | |       | | |
988
     *                3 | | 4,5   | | |
989
     *    --------------   -----  | | |
990
     *   |                      | | | |
991
     *   |                      | | | |
992
     *   |                 ---------  |
993
     *   |               1|     | |   |
994
     *   V                |     | |   |
995
     * +---+   PASS    +---+ 2  |  ------>+---+
996
     * |   |---------->| W |------------->| S |
997
     * +---+           +---+   ---------->+---+
998
     *                  | |   | |     |
999
     *                3 | |4,5| |     |
1000
     *    --------------   --------   |
1001
     *   |                    | |  |  |
1002
     *   |                    | |  |  |
1003
     *   |                 -----------
1004
     *   |             1,3|   | |  |
1005
     *   V                |  2| |  |
1006
     * +---+   ACCT    +---+--  |   ----->+---+
1007
     * |   |---------->| W | 4,5 -------->| F |
1008
     * +---+           +---+------------->+---+
1009
     *
1010
     * Of course in case of using a proxy this get really nasty and is not
1011
     * standardized at all :-(
1012
     */
1013
0
    if (proxy) {
1014
0
        int len;
1015
0
  char buf[400];
1016
1017
0
        if (proxyUser != NULL) {
1018
      /*
1019
       * We need proxy auth
1020
       */
1021
0
      snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
1022
0
            buf[sizeof(buf) - 1] = 0;
1023
0
            len = strlen(buf);
1024
#ifdef DEBUG_FTP
1025
      xmlGenericError(xmlGenericErrorContext, "%s", buf);
1026
#endif
1027
0
      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1028
0
      if (res < 0) {
1029
0
    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1030
0
    closesocket(ctxt->controlFd);
1031
0
    ctxt->controlFd = INVALID_SOCKET;
1032
0
          return(res);
1033
0
      }
1034
0
      res = xmlNanoFTPGetResponse(ctxt);
1035
0
      switch (res) {
1036
0
    case 2:
1037
0
        if (proxyPasswd == NULL)
1038
0
      break;
1039
                    /* Falls through. */
1040
0
    case 3:
1041
0
        if (proxyPasswd != NULL)
1042
0
      snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
1043
0
        else
1044
0
      snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1045
0
                    buf[sizeof(buf) - 1] = 0;
1046
0
                    len = strlen(buf);
1047
#ifdef DEBUG_FTP
1048
        xmlGenericError(xmlGenericErrorContext, "%s", buf);
1049
#endif
1050
0
        res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1051
0
        if (res < 0) {
1052
0
      __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1053
0
      closesocket(ctxt->controlFd);
1054
0
      ctxt->controlFd = INVALID_SOCKET;
1055
0
      return(res);
1056
0
        }
1057
0
        res = xmlNanoFTPGetResponse(ctxt);
1058
0
        if (res > 3) {
1059
0
      closesocket(ctxt->controlFd);
1060
0
      ctxt->controlFd = INVALID_SOCKET;
1061
0
      return(-1);
1062
0
        }
1063
0
        break;
1064
0
    case 1:
1065
0
        break;
1066
0
    case 4:
1067
0
    case 5:
1068
0
    case -1:
1069
0
    default:
1070
0
        closesocket(ctxt->controlFd);
1071
0
        ctxt->controlFd = INVALID_SOCKET;
1072
0
        return(-1);
1073
0
      }
1074
0
  }
1075
1076
  /*
1077
   * We assume we don't need more authentication to the proxy
1078
   * and that it succeeded :-\
1079
   */
1080
0
  switch (proxyType) {
1081
0
      case 0:
1082
    /* we will try in sequence */
1083
0
      case 1:
1084
    /* Using SITE command */
1085
0
    snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
1086
0
                buf[sizeof(buf) - 1] = 0;
1087
0
                len = strlen(buf);
1088
#ifdef DEBUG_FTP
1089
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1090
#endif
1091
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1092
0
    if (res < 0) {
1093
0
        __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1094
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1095
0
        ctxt->controlFd = INVALID_SOCKET;
1096
0
        return(res);
1097
0
    }
1098
0
    res = xmlNanoFTPGetResponse(ctxt);
1099
0
    if (res == 2) {
1100
        /* we assume it worked :-\ 1 is error for SITE command */
1101
0
        proxyType = 1;
1102
0
        break;
1103
0
    }
1104
0
    if (proxyType == 1) {
1105
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1106
0
        ctxt->controlFd = INVALID_SOCKET;
1107
0
        return(-1);
1108
0
    }
1109
                /* Falls through. */
1110
0
      case 2:
1111
    /* USER user@host command */
1112
0
    if (ctxt->user == NULL)
1113
0
        snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
1114
0
                 ctxt->hostname);
1115
0
    else
1116
0
        snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
1117
0
                 ctxt->user, ctxt->hostname);
1118
0
                buf[sizeof(buf) - 1] = 0;
1119
0
                len = strlen(buf);
1120
#ifdef DEBUG_FTP
1121
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1122
#endif
1123
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1124
0
    if (res < 0) {
1125
0
        __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1126
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1127
0
        ctxt->controlFd = INVALID_SOCKET;
1128
0
        return(res);
1129
0
    }
1130
0
    res = xmlNanoFTPGetResponse(ctxt);
1131
0
    if ((res == 1) || (res == 2)) {
1132
        /* we assume it worked :-\ */
1133
0
        proxyType = 2;
1134
0
        return(0);
1135
0
    }
1136
0
    if (ctxt->passwd == NULL)
1137
0
        snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
1138
0
    else
1139
0
        snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
1140
0
                buf[sizeof(buf) - 1] = 0;
1141
0
                len = strlen(buf);
1142
#ifdef DEBUG_FTP
1143
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1144
#endif
1145
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1146
0
    if (res < 0) {
1147
0
        __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1148
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1149
0
        ctxt->controlFd = INVALID_SOCKET;
1150
0
        return(res);
1151
0
    }
1152
0
    res = xmlNanoFTPGetResponse(ctxt);
1153
0
    if ((res == 1) || (res == 2)) {
1154
        /* we assume it worked :-\ */
1155
0
        proxyType = 2;
1156
0
        return(0);
1157
0
    }
1158
0
    if (proxyType == 2) {
1159
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1160
0
        ctxt->controlFd = INVALID_SOCKET;
1161
0
        return(-1);
1162
0
    }
1163
                /* Falls through. */
1164
0
      case 3:
1165
    /*
1166
     * If you need support for other Proxy authentication scheme
1167
     * send the code or at least the sequence in use.
1168
     */
1169
0
      default:
1170
0
    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1171
0
    ctxt->controlFd = INVALID_SOCKET;
1172
0
    return(-1);
1173
0
  }
1174
0
    }
1175
    /*
1176
     * Non-proxy handling.
1177
     */
1178
0
    res = xmlNanoFTPSendUser(ctxt);
1179
0
    if (res < 0) {
1180
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1181
0
        ctxt->controlFd = INVALID_SOCKET;
1182
0
  return(-1);
1183
0
    }
1184
0
    res = xmlNanoFTPGetResponse(ctxt);
1185
0
    switch (res) {
1186
0
  case 2:
1187
0
      return(0);
1188
0
  case 3:
1189
0
      break;
1190
0
  case 1:
1191
0
  case 4:
1192
0
  case 5:
1193
0
        case -1:
1194
0
  default:
1195
0
      closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1196
0
      ctxt->controlFd = INVALID_SOCKET;
1197
0
      return(-1);
1198
0
    }
1199
0
    res = xmlNanoFTPSendPasswd(ctxt);
1200
0
    if (res < 0) {
1201
0
        closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1202
0
        ctxt->controlFd = INVALID_SOCKET;
1203
0
  return(-1);
1204
0
    }
1205
0
    res = xmlNanoFTPGetResponse(ctxt);
1206
0
    switch (res) {
1207
0
  case 2:
1208
0
      break;
1209
0
  case 3:
1210
0
      __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
1211
0
           "FTP server asking for ACCNT on anonymous\n");
1212
           /* Falls through. */
1213
0
  case 1:
1214
0
  case 4:
1215
0
  case 5:
1216
0
        case -1:
1217
0
  default:
1218
0
      closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1219
0
      ctxt->controlFd = INVALID_SOCKET;
1220
0
      return(-1);
1221
0
    }
1222
1223
0
    return(0);
1224
0
}
1225
1226
/**
1227
 * xmlNanoFTPConnectTo:
1228
 * @server:  an FTP server name
1229
 * @port:  the port (use 21 if 0)
1230
 *
1231
 * Tries to open a control connection to the given server/port
1232
 *
1233
 * Returns an fTP context or NULL if it failed
1234
 */
1235
1236
void*
1237
0
xmlNanoFTPConnectTo(const char *server, int port) {
1238
0
    xmlNanoFTPCtxtPtr ctxt;
1239
0
    int res;
1240
1241
0
    xmlNanoFTPInit();
1242
0
    if (server == NULL)
1243
0
  return(NULL);
1244
0
    if (port <= 0)
1245
0
  return(NULL);
1246
0
    ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
1247
0
    if (ctxt == NULL)
1248
0
        return(NULL);
1249
0
    ctxt->hostname = xmlMemStrdup(server);
1250
0
    if (ctxt->hostname == NULL) {
1251
0
  xmlNanoFTPFreeCtxt(ctxt);
1252
0
  return(NULL);
1253
0
    }
1254
0
    if (port != 0)
1255
0
  ctxt->port = port;
1256
0
    res = xmlNanoFTPConnect(ctxt);
1257
0
    if (res < 0) {
1258
0
  xmlNanoFTPFreeCtxt(ctxt);
1259
0
  return(NULL);
1260
0
    }
1261
0
    return(ctxt);
1262
0
}
1263
1264
/**
1265
 * xmlNanoFTPCwd:
1266
 * @ctx:  an FTP context
1267
 * @directory:  a directory on the server
1268
 *
1269
 * Tries to change the remote directory
1270
 *
1271
 * Returns -1 incase of error, 1 if CWD worked, 0 if it failed
1272
 */
1273
1274
int
1275
0
xmlNanoFTPCwd(void *ctx, const char *directory) {
1276
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1277
0
    char buf[400];
1278
0
    int len;
1279
0
    int res;
1280
1281
0
    if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
1282
0
    if (directory == NULL) return 0;
1283
1284
    /*
1285
     * Expected response code for CWD:
1286
     *
1287
     * CWD
1288
     *     250
1289
     *     500, 501, 502, 421, 530, 550
1290
     */
1291
0
    snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
1292
0
    buf[sizeof(buf) - 1] = 0;
1293
0
    len = strlen(buf);
1294
#ifdef DEBUG_FTP
1295
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1296
#endif
1297
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1298
0
    if (res < 0) {
1299
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1300
0
  return(res);
1301
0
    }
1302
0
    res = xmlNanoFTPGetResponse(ctxt);
1303
0
    if (res == 4) {
1304
0
  return(-1);
1305
0
    }
1306
0
    if (res == 2) return(1);
1307
0
    if (res == 5) {
1308
0
  return(0);
1309
0
    }
1310
0
    return(0);
1311
0
}
1312
1313
/**
1314
 * xmlNanoFTPDele:
1315
 * @ctx:  an FTP context
1316
 * @file:  a file or directory on the server
1317
 *
1318
 * Tries to delete an item (file or directory) from server
1319
 *
1320
 * Returns -1 incase of error, 1 if DELE worked, 0 if it failed
1321
 */
1322
1323
int
1324
0
xmlNanoFTPDele(void *ctx, const char *file) {
1325
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1326
0
    char buf[400];
1327
0
    int len;
1328
0
    int res;
1329
1330
0
    if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET) ||
1331
0
        (file == NULL)) return(-1);
1332
1333
    /*
1334
     * Expected response code for DELE:
1335
     *
1336
     * DELE
1337
     *       250
1338
     *       450, 550
1339
     *       500, 501, 502, 421, 530
1340
     */
1341
1342
0
    snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1343
0
    buf[sizeof(buf) - 1] = 0;
1344
0
    len = strlen(buf);
1345
#ifdef DEBUG_FTP
1346
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1347
#endif
1348
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1349
0
    if (res < 0) {
1350
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1351
0
  return(res);
1352
0
    }
1353
0
    res = xmlNanoFTPGetResponse(ctxt);
1354
0
    if (res == 4) {
1355
0
  return(-1);
1356
0
    }
1357
0
    if (res == 2) return(1);
1358
0
    if (res == 5) {
1359
0
  return(0);
1360
0
    }
1361
0
    return(0);
1362
0
}
1363
/**
1364
 * xmlNanoFTPGetConnection:
1365
 * @ctx:  an FTP context
1366
 *
1367
 * Try to open a data connection to the server. Currently only
1368
 * passive mode is supported.
1369
 *
1370
 * Returns -1 incase of error, 0 otherwise
1371
 */
1372
1373
SOCKET
1374
0
xmlNanoFTPGetConnection(void *ctx) {
1375
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1376
0
    char buf[200], *cur;
1377
0
    int len, i;
1378
0
    int res;
1379
0
    unsigned char ad[6], *adp, *portp;
1380
0
    unsigned int temp[6];
1381
#ifdef SUPPORT_IP6
1382
    struct sockaddr_storage dataAddr;
1383
#else
1384
0
    struct sockaddr_in dataAddr;
1385
0
#endif
1386
0
    XML_SOCKLEN_T dataAddrLen;
1387
1388
0
    if (ctxt == NULL) return INVALID_SOCKET;
1389
1390
0
    memset (&dataAddr, 0, sizeof(dataAddr));
1391
#ifdef SUPPORT_IP6
1392
    if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1393
  ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1394
  ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1395
  dataAddrLen = sizeof(struct sockaddr_in6);
1396
    } else
1397
#endif
1398
0
    {
1399
0
  ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1400
0
  ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1401
0
  dataAddrLen = sizeof (struct sockaddr_in);
1402
0
    }
1403
1404
0
    if (ctxt->dataFd == INVALID_SOCKET) {
1405
0
  __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
1406
0
  return INVALID_SOCKET;
1407
0
    }
1408
1409
0
    if (ctxt->passive) {
1410
#ifdef SUPPORT_IP6
1411
  if ((ctxt->ftpAddr).ss_family == AF_INET6)
1412
      snprintf (buf, sizeof(buf), "EPSV\r\n");
1413
  else
1414
#endif
1415
0
      snprintf (buf, sizeof(buf), "PASV\r\n");
1416
0
        len = strlen (buf);
1417
#ifdef DEBUG_FTP
1418
  xmlGenericError(xmlGenericErrorContext, "%s", buf);
1419
#endif
1420
0
  res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1421
0
  if (res < 0) {
1422
0
      __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1423
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1424
0
      return INVALID_SOCKET;
1425
0
  }
1426
0
        res = xmlNanoFTPReadResponse(ctx);
1427
0
  if (res != 2) {
1428
0
      if (res == 5) {
1429
0
          closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1430
0
    return INVALID_SOCKET;
1431
0
      } else {
1432
    /*
1433
     * retry with an active connection
1434
     */
1435
0
          closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1436
0
          ctxt->passive = 0;
1437
0
      }
1438
0
  }
1439
0
  cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1440
0
  while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1441
#ifdef SUPPORT_IP6
1442
  if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1443
      if (sscanf (cur, "%u", &temp[0]) != 1) {
1444
    __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
1445
      "Invalid answer to EPSV\n");
1446
    if (ctxt->dataFd != INVALID_SOCKET) {
1447
        closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1448
    }
1449
    return INVALID_SOCKET;
1450
      }
1451
      memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1452
      ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
1453
  }
1454
  else
1455
#endif
1456
0
  {
1457
0
      if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1458
0
    &temp[3], &temp[4], &temp[5]) != 6) {
1459
0
    __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
1460
0
      "Invalid answer to PASV\n");
1461
0
    if (ctxt->dataFd != INVALID_SOCKET) {
1462
0
        closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1463
0
    }
1464
0
    return INVALID_SOCKET;
1465
0
      }
1466
0
      for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1467
0
      memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1468
0
      memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1469
0
  }
1470
1471
0
  if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1472
0
      __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
1473
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1474
0
      return INVALID_SOCKET;
1475
0
  }
1476
0
    } else {
1477
0
        getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1478
#ifdef SUPPORT_IP6
1479
  if ((ctxt->ftpAddr).ss_family == AF_INET6)
1480
      ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1481
  else
1482
#endif
1483
0
      ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1484
1485
0
  if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1486
0
      __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
1487
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1488
0
      return INVALID_SOCKET;
1489
0
  }
1490
0
        getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1491
1492
0
  if (listen(ctxt->dataFd, 1) < 0) {
1493
0
      __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
1494
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1495
0
      return INVALID_SOCKET;
1496
0
  }
1497
#ifdef SUPPORT_IP6
1498
  if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1499
      char buf6[INET6_ADDRSTRLEN];
1500
      inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1501
        buf6, INET6_ADDRSTRLEN);
1502
      adp = (unsigned char *) buf6;
1503
      portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1504
      snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1505
        } else
1506
#endif
1507
0
  {
1508
0
      adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1509
0
      portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1510
0
      snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1511
0
      adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1512
0
      portp[0] & 0xff, portp[1] & 0xff);
1513
0
  }
1514
1515
0
        buf[sizeof(buf) - 1] = 0;
1516
0
        len = strlen(buf);
1517
#ifdef DEBUG_FTP
1518
  xmlGenericError(xmlGenericErrorContext, "%s", buf);
1519
#endif
1520
1521
0
  res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1522
0
  if (res < 0) {
1523
0
      __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1524
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1525
0
      return INVALID_SOCKET;
1526
0
  }
1527
0
        res = xmlNanoFTPGetResponse(ctxt);
1528
0
  if (res != 2) {
1529
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1530
0
      return INVALID_SOCKET;
1531
0
        }
1532
0
    }
1533
0
    return(ctxt->dataFd);
1534
1535
0
}
1536
1537
/**
1538
 * xmlNanoFTPCloseConnection:
1539
 * @ctx:  an FTP context
1540
 *
1541
 * Close the data connection from the server
1542
 *
1543
 * Returns -1 incase of error, 0 otherwise
1544
 */
1545
1546
int
1547
0
xmlNanoFTPCloseConnection(void *ctx) {
1548
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1549
0
    int res;
1550
0
    fd_set rfd, efd;
1551
0
    struct timeval tv;
1552
1553
0
    if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
1554
1555
0
    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1556
0
    tv.tv_sec = 15;
1557
0
    tv.tv_usec = 0;
1558
0
    FD_ZERO(&rfd);
1559
0
    FD_SET(ctxt->controlFd, &rfd);
1560
0
    FD_ZERO(&efd);
1561
0
    FD_SET(ctxt->controlFd, &efd);
1562
0
    res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1563
0
    if (res < 0) {
1564
#ifdef DEBUG_FTP
1565
  perror("select");
1566
#endif
1567
0
  closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1568
0
  return(-1);
1569
0
    }
1570
0
    if (res == 0) {
1571
#ifdef DEBUG_FTP
1572
  xmlGenericError(xmlGenericErrorContext,
1573
    "xmlNanoFTPCloseConnection: timeout\n");
1574
#endif
1575
0
  closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1576
0
    } else {
1577
0
  res = xmlNanoFTPGetResponse(ctxt);
1578
0
  if (res != 2) {
1579
0
      closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1580
0
      return(-1);
1581
0
  }
1582
0
    }
1583
0
    return(0);
1584
0
}
1585
1586
/**
1587
 * xmlNanoFTPParseList:
1588
 * @list:  some data listing received from the server
1589
 * @callback:  the user callback
1590
 * @userData:  the user callback data
1591
 *
1592
 * Parse at most one entry from the listing.
1593
 *
1594
 * Returns -1 incase of error, the length of data parsed otherwise
1595
 */
1596
1597
static int
1598
0
xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1599
0
    const char *cur = list;
1600
0
    char filename[151];
1601
0
    char attrib[11];
1602
0
    char owner[11];
1603
0
    char group[11];
1604
0
    char month[4];
1605
0
    int year = 0;
1606
0
    int minute = 0;
1607
0
    int hour = 0;
1608
0
    int day = 0;
1609
0
    unsigned long size = 0;
1610
0
    int links = 0;
1611
0
    int i;
1612
1613
0
    if (!strncmp(cur, "total", 5)) {
1614
0
        cur += 5;
1615
0
  while (*cur == ' ') cur++;
1616
0
  while ((*cur >= '0') && (*cur <= '9'))
1617
0
      links = (links * 10) + (*cur++ - '0');
1618
0
  while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
1619
0
      cur++;
1620
0
  return(cur - list);
1621
0
    } else if (*list == '+') {
1622
0
  return(0);
1623
0
    } else {
1624
0
  while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
1625
0
      cur++;
1626
0
  if (*cur == 0) return(0);
1627
0
  i = 0;
1628
0
  while (*cur != ' ') {
1629
0
      if (i < 10)
1630
0
    attrib[i++] = *cur;
1631
0
      cur++;
1632
0
      if (*cur == 0) return(0);
1633
0
  }
1634
0
  attrib[10] = 0;
1635
0
  while (*cur == ' ') cur++;
1636
0
  if (*cur == 0) return(0);
1637
0
  while ((*cur >= '0') && (*cur <= '9'))
1638
0
      links = (links * 10) + (*cur++ - '0');
1639
0
  while (*cur == ' ') cur++;
1640
0
  if (*cur == 0) return(0);
1641
0
  i = 0;
1642
0
  while (*cur != ' ') {
1643
0
      if (i < 10)
1644
0
    owner[i++] = *cur;
1645
0
      cur++;
1646
0
      if (*cur == 0) return(0);
1647
0
  }
1648
0
  owner[i] = 0;
1649
0
  while (*cur == ' ') cur++;
1650
0
  if (*cur == 0) return(0);
1651
0
  i = 0;
1652
0
  while (*cur != ' ') {
1653
0
      if (i < 10)
1654
0
    group[i++] = *cur;
1655
0
      cur++;
1656
0
      if (*cur == 0) return(0);
1657
0
  }
1658
0
  group[i] = 0;
1659
0
  while (*cur == ' ') cur++;
1660
0
  if (*cur == 0) return(0);
1661
0
  while ((*cur >= '0') && (*cur <= '9'))
1662
0
      size = (size * 10) + (*cur++ - '0');
1663
0
  while (*cur == ' ') cur++;
1664
0
  if (*cur == 0) return(0);
1665
0
  i = 0;
1666
0
  while (*cur != ' ') {
1667
0
      if (i < 3)
1668
0
    month[i++] = *cur;
1669
0
      cur++;
1670
0
      if (*cur == 0) return(0);
1671
0
  }
1672
0
  month[i] = 0;
1673
0
  while (*cur == ' ') cur++;
1674
0
  if (*cur == 0) return(0);
1675
0
        while ((*cur >= '0') && (*cur <= '9'))
1676
0
      day = (day * 10) + (*cur++ - '0');
1677
0
  while (*cur == ' ') cur++;
1678
0
  if (*cur == 0) return(0);
1679
0
  if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1680
0
  if ((cur[1] == ':') || (cur[2] == ':')) {
1681
0
      while ((*cur >= '0') && (*cur <= '9'))
1682
0
    hour = (hour * 10) + (*cur++ - '0');
1683
0
      if (*cur == ':') cur++;
1684
0
      while ((*cur >= '0') && (*cur <= '9'))
1685
0
    minute = (minute * 10) + (*cur++ - '0');
1686
0
  } else {
1687
0
      while ((*cur >= '0') && (*cur <= '9'))
1688
0
    year = (year * 10) + (*cur++ - '0');
1689
0
  }
1690
0
  while (*cur == ' ') cur++;
1691
0
  if (*cur == 0) return(0);
1692
0
  i = 0;
1693
0
  while ((*cur != '\n')  && (*cur != '\r')) {
1694
0
      if (i < 150)
1695
0
    filename[i++] = *cur;
1696
0
      cur++;
1697
0
      if (*cur == 0) return(0);
1698
0
  }
1699
0
  filename[i] = 0;
1700
0
  if ((*cur != '\n') && (*cur != '\r'))
1701
0
      return(0);
1702
0
  while ((*cur == '\n')  || (*cur == '\r'))
1703
0
      cur++;
1704
0
    }
1705
0
    if (callback != NULL) {
1706
0
        callback(userData, filename, attrib, owner, group, size, links,
1707
0
     year, month, day, hour, minute);
1708
0
    }
1709
0
    return(cur - list);
1710
0
}
1711
1712
/**
1713
 * xmlNanoFTPList:
1714
 * @ctx:  an FTP context
1715
 * @callback:  the user callback
1716
 * @userData:  the user callback data
1717
 * @filename:  optional files to list
1718
 *
1719
 * Do a listing on the server. All files info are passed back
1720
 * in the callbacks.
1721
 *
1722
 * Returns -1 incase of error, 0 otherwise
1723
 */
1724
1725
int
1726
xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1727
0
         const char *filename) {
1728
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1729
0
    char buf[4096 + 1];
1730
0
    int len, res;
1731
0
    int indx = 0, base;
1732
0
    fd_set rfd, efd;
1733
0
    struct timeval tv;
1734
1735
0
    if (ctxt == NULL) return (-1);
1736
0
    if (filename == NULL) {
1737
0
        if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1738
0
      return(-1);
1739
0
  ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1740
0
  if (ctxt->dataFd == INVALID_SOCKET)
1741
0
      return(-1);
1742
0
  snprintf(buf, sizeof(buf), "LIST -L\r\n");
1743
0
    } else {
1744
0
  if (filename[0] != '/') {
1745
0
      if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1746
0
    return(-1);
1747
0
  }
1748
0
  ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1749
0
  if (ctxt->dataFd == INVALID_SOCKET)
1750
0
      return(-1);
1751
0
  snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
1752
0
    }
1753
0
    buf[sizeof(buf) - 1] = 0;
1754
0
    len = strlen(buf);
1755
#ifdef DEBUG_FTP
1756
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1757
#endif
1758
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1759
0
    if (res < 0) {
1760
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1761
0
  closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1762
0
  return(res);
1763
0
    }
1764
0
    res = xmlNanoFTPReadResponse(ctxt);
1765
0
    if (res != 1) {
1766
0
  closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1767
0
  return(-res);
1768
0
    }
1769
1770
0
    do {
1771
0
  tv.tv_sec = 1;
1772
0
  tv.tv_usec = 0;
1773
0
  FD_ZERO(&rfd);
1774
0
  FD_SET(ctxt->dataFd, &rfd);
1775
0
  FD_ZERO(&efd);
1776
0
  FD_SET(ctxt->dataFd, &efd);
1777
0
  res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1778
0
  if (res < 0) {
1779
#ifdef DEBUG_FTP
1780
      perror("select");
1781
#endif
1782
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1783
0
      return(-1);
1784
0
  }
1785
0
  if (res == 0) {
1786
0
      res = xmlNanoFTPCheckResponse(ctxt);
1787
0
      if (res < 0) {
1788
0
    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1789
0
    ctxt->dataFd = INVALID_SOCKET;
1790
0
    return(-1);
1791
0
      }
1792
0
      if (res == 2) {
1793
0
    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1794
0
    return(0);
1795
0
      }
1796
1797
0
      continue;
1798
0
  }
1799
1800
0
  if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
1801
0
      __xmlIOErr(XML_FROM_FTP, 0, "recv");
1802
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1803
0
      ctxt->dataFd = INVALID_SOCKET;
1804
0
      return(-1);
1805
0
  }
1806
#ifdef DEBUG_FTP
1807
        write(1, &buf[indx], len);
1808
#endif
1809
0
  indx += len;
1810
0
  buf[indx] = 0;
1811
0
  base = 0;
1812
0
  do {
1813
0
      res = xmlNanoFTPParseList(&buf[base], callback, userData);
1814
0
      base += res;
1815
0
  } while (res > 0);
1816
1817
0
  memmove(&buf[0], &buf[base], indx - base);
1818
0
  indx -= base;
1819
0
    } while (len != 0);
1820
0
    xmlNanoFTPCloseConnection(ctxt);
1821
0
    return(0);
1822
0
}
1823
1824
/**
1825
 * xmlNanoFTPGetSocket:
1826
 * @ctx:  an FTP context
1827
 * @filename:  the file to retrieve (or NULL if path is in context).
1828
 *
1829
 * Initiate fetch of the given file from the server.
1830
 *
1831
 * Returns the socket for the data connection, or <0 in case of error
1832
 */
1833
1834
1835
SOCKET
1836
0
xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1837
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1838
0
    char buf[300];
1839
0
    int res, len;
1840
0
    if (ctx == NULL)
1841
0
  return INVALID_SOCKET;
1842
0
    if ((filename == NULL) && (ctxt->path == NULL))
1843
0
  return INVALID_SOCKET;
1844
0
    ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1845
0
    if (ctxt->dataFd == INVALID_SOCKET)
1846
0
  return INVALID_SOCKET;
1847
1848
0
    snprintf(buf, sizeof(buf), "TYPE I\r\n");
1849
0
    len = strlen(buf);
1850
#ifdef DEBUG_FTP
1851
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1852
#endif
1853
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1854
0
    if (res < 0) {
1855
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1856
0
  closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1857
0
  return INVALID_SOCKET;
1858
0
    }
1859
0
    res = xmlNanoFTPReadResponse(ctxt);
1860
0
    if (res != 2) {
1861
0
  closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1862
0
  return INVALID_SOCKET;
1863
0
    }
1864
0
    if (filename == NULL)
1865
0
  snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
1866
0
    else
1867
0
  snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
1868
0
    buf[sizeof(buf) - 1] = 0;
1869
0
    len = strlen(buf);
1870
#ifdef DEBUG_FTP
1871
    xmlGenericError(xmlGenericErrorContext, "%s", buf);
1872
#endif
1873
0
    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1874
0
    if (res < 0) {
1875
0
  __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1876
0
  closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1877
0
  return INVALID_SOCKET;
1878
0
    }
1879
0
    res = xmlNanoFTPReadResponse(ctxt);
1880
0
    if (res != 1) {
1881
0
  closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1882
0
  return INVALID_SOCKET;
1883
0
    }
1884
0
    return(ctxt->dataFd);
1885
0
}
1886
1887
/**
1888
 * xmlNanoFTPGet:
1889
 * @ctx:  an FTP context
1890
 * @callback:  the user callback
1891
 * @userData:  the user callback data
1892
 * @filename:  the file to retrieve
1893
 *
1894
 * Fetch the given file from the server. All data are passed back
1895
 * in the callbacks. The last callback has a size of 0 block.
1896
 *
1897
 * Returns -1 incase of error, 0 otherwise
1898
 */
1899
1900
int
1901
xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1902
0
        const char *filename) {
1903
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1904
0
    char buf[4096];
1905
0
    int len = 0, res;
1906
0
    fd_set rfd;
1907
0
    struct timeval tv;
1908
1909
0
    if (ctxt == NULL) return(-1);
1910
0
    if ((filename == NULL) && (ctxt->path == NULL))
1911
0
  return(-1);
1912
0
    if (callback == NULL)
1913
0
  return(-1);
1914
0
    if (xmlNanoFTPGetSocket(ctxt, filename) == INVALID_SOCKET)
1915
0
  return(-1);
1916
1917
0
    do {
1918
0
  tv.tv_sec = 1;
1919
0
  tv.tv_usec = 0;
1920
0
  FD_ZERO(&rfd);
1921
0
  FD_SET(ctxt->dataFd, &rfd);
1922
0
  res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1923
0
  if (res < 0) {
1924
#ifdef DEBUG_FTP
1925
      perror("select");
1926
#endif
1927
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1928
0
      return(-1);
1929
0
  }
1930
0
  if (res == 0) {
1931
0
      res = xmlNanoFTPCheckResponse(ctxt);
1932
0
      if (res < 0) {
1933
0
    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1934
0
    ctxt->dataFd = INVALID_SOCKET;
1935
0
    return(-1);
1936
0
      }
1937
0
      if (res == 2) {
1938
0
    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1939
0
    return(0);
1940
0
      }
1941
1942
0
      continue;
1943
0
  }
1944
0
  if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1945
0
      __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1946
0
      callback(userData, buf, len);
1947
0
      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1948
0
      return(-1);
1949
0
  }
1950
0
  callback(userData, buf, len);
1951
0
    } while (len != 0);
1952
1953
0
    return(xmlNanoFTPCloseConnection(ctxt));
1954
0
}
1955
1956
/**
1957
 * xmlNanoFTPRead:
1958
 * @ctx:  the FTP context
1959
 * @dest:  a buffer
1960
 * @len:  the buffer length
1961
 *
1962
 * This function tries to read @len bytes from the existing FTP connection
1963
 * and saves them in @dest. This is a blocking call.
1964
 *
1965
 * Returns the number of byte read. 0 is an indication of an end of connection.
1966
 *         -1 indicates a parameter error.
1967
 */
1968
int
1969
0
xmlNanoFTPRead(void *ctx, void *dest, int len) {
1970
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1971
1972
0
    if (ctx == NULL) return(-1);
1973
0
    if (ctxt->dataFd == INVALID_SOCKET) return(0);
1974
0
    if (dest == NULL) return(-1);
1975
0
    if (len <= 0) return(0);
1976
1977
0
    len = recv(ctxt->dataFd, dest, len, 0);
1978
0
    if (len <= 0) {
1979
0
  if (len < 0)
1980
0
      __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1981
0
  xmlNanoFTPCloseConnection(ctxt);
1982
0
    }
1983
#ifdef DEBUG_FTP
1984
    xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1985
#endif
1986
0
    return(len);
1987
0
}
1988
1989
/**
1990
 * xmlNanoFTPOpen:
1991
 * @URL: the URL to the resource
1992
 *
1993
 * Start to fetch the given ftp:// resource
1994
 *
1995
 * Returns an FTP context, or NULL
1996
 */
1997
1998
void*
1999
0
xmlNanoFTPOpen(const char *URL) {
2000
0
    xmlNanoFTPCtxtPtr ctxt;
2001
0
    SOCKET sock;
2002
2003
0
    xmlNanoFTPInit();
2004
0
    if (URL == NULL) return(NULL);
2005
0
    if (strncmp("ftp://", URL, 6)) return(NULL);
2006
2007
0
    ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2008
0
    if (ctxt == NULL) return(NULL);
2009
0
    if (xmlNanoFTPConnect(ctxt) < 0) {
2010
0
  xmlNanoFTPFreeCtxt(ctxt);
2011
0
  return(NULL);
2012
0
    }
2013
0
    sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2014
0
    if (sock == INVALID_SOCKET) {
2015
0
  xmlNanoFTPFreeCtxt(ctxt);
2016
0
  return(NULL);
2017
0
    }
2018
0
    return(ctxt);
2019
0
}
2020
2021
/**
2022
 * xmlNanoFTPClose:
2023
 * @ctx: an FTP context
2024
 *
2025
 * Close the connection and both control and transport
2026
 *
2027
 * Returns -1 incase of error, 0 otherwise
2028
 */
2029
2030
int
2031
0
xmlNanoFTPClose(void *ctx) {
2032
0
    xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2033
2034
0
    if (ctxt == NULL)
2035
0
  return(-1);
2036
2037
0
    if (ctxt->dataFd != INVALID_SOCKET) {
2038
0
  closesocket(ctxt->dataFd);
2039
0
  ctxt->dataFd = INVALID_SOCKET;
2040
0
    }
2041
0
    if (ctxt->controlFd != INVALID_SOCKET) {
2042
0
  xmlNanoFTPQuit(ctxt);
2043
0
  closesocket(ctxt->controlFd);
2044
0
  ctxt->controlFd = INVALID_SOCKET;
2045
0
    }
2046
0
    xmlNanoFTPFreeCtxt(ctxt);
2047
0
    return(0);
2048
0
}
2049
2050
#ifdef STANDALONE
2051
/************************************************************************
2052
 *                  *
2053
 *      Basic test in Standalone mode     *
2054
 *                  *
2055
 ************************************************************************/
2056
static
2057
void ftpList(void *userData, const char *filename, const char* attrib,
2058
       const char *owner, const char *group, unsigned long size, int links,
2059
       int year, const char *month, int day, int hour, int minute) {
2060
    xmlGenericError(xmlGenericErrorContext,
2061
      "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2062
}
2063
static
2064
void ftpData(void *userData, const char *data, int len) {
2065
    if (userData == NULL) return;
2066
    if (len <= 0) {
2067
  fclose((FILE*)userData);
2068
  return;
2069
    }
2070
    fwrite(data, len, 1, (FILE*)userData);
2071
}
2072
2073
int main(int argc, char **argv) {
2074
    void *ctxt;
2075
    FILE *output;
2076
    char *tstfile = NULL;
2077
2078
    xmlNanoFTPInit();
2079
    if (argc > 1) {
2080
  ctxt = xmlNanoFTPNewCtxt(argv[1]);
2081
  if (xmlNanoFTPConnect(ctxt) < 0) {
2082
      xmlGenericError(xmlGenericErrorContext,
2083
        "Couldn't connect to %s\n", argv[1]);
2084
      exit(1);
2085
  }
2086
  if (argc > 2)
2087
      tstfile = argv[2];
2088
    } else
2089
  ctxt = xmlNanoFTPConnectTo("localhost", 0);
2090
    if (ctxt == NULL) {
2091
        xmlGenericError(xmlGenericErrorContext,
2092
    "Couldn't connect to localhost\n");
2093
        exit(1);
2094
    }
2095
    xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2096
    output = fopen("/tmp/tstdata", "w");
2097
    if (output != NULL) {
2098
  if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2099
      xmlGenericError(xmlGenericErrorContext,
2100
        "Failed to get file\n");
2101
2102
    }
2103
    xmlNanoFTPClose(ctxt);
2104
    xmlMemoryDump();
2105
    exit(0);
2106
}
2107
#endif /* STANDALONE */
2108
#else /* !LIBXML_FTP_ENABLED */
2109
#ifdef STANDALONE
2110
#include <stdio.h>
2111
int main(int argc, char **argv) {
2112
    xmlGenericError(xmlGenericErrorContext,
2113
      "%s : FTP support not compiled in\n", argv[0]);
2114
    return(0);
2115
}
2116
#endif /* STANDALONE */
2117
#endif /* LIBXML_FTP_ENABLED */
2118
#define bottom_nanoftp
2119
#include "elfgcchack.h"