Subversion Repositories tpanel

Rev

Rev 370 | Rev 391 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
380 andreas 1
/***************************************************************************
2
 *                    ftplib.cpp  -  description
3
 *                       -------------------
4
 b e*gin                : Son Jul 27 2003
5
 copyright            : (C) 2013 by magnus kulke
6
 email                : mkulke@gmail.com
7
 ***************************************************************************/
8
 
9
/***************************************************************************
10
 *                                                                         *
11
 *   This program is free software; you can redistribute it and/or modify  *
12
 *   it under the terms of the GNU Lesser General Public License as        *
13
 *   published by the Free Software Foundation; either version 2.1 of the  *
14
 *   License, or (at your option) any later version.                       *
15
 *                                                                         *
16
 ***************************************************************************/
17
 
18
/***************************************************************************
19
 * Note: ftplib, on which ftplibpp was originally based upon used to be    *
20
 * licensed as GPL 2.0 software, as of Jan. 26th 2013 its author Thomas    *
21
 * Pfau allowed the distribution of ftplib via LGPL. Thus the license of   *
22
 * ftplibpp changed aswell.                                                *
23
 ***************************************************************************/
117 andreas 24
#ifndef NOLFS
25
#define _LARGEFILE_SOURCE
26
#define _LARGEFILE64_SOURCE
27
#endif
28
 
120 andreas 29
#include <iostream>
30
 
117 andreas 31
#ifndef NOSSL
32
#include <openssl/ssl.h>
33
#endif
34
 
35
#include "ftplib.h"
36
 
37
#ifndef NOSSL
38
#include <openssl/ssl.h>
39
#endif
40
 
41
#include <sys/socket.h>
42
#include <netinet/in.h>
43
#include <netdb.h>
44
#include <arpa/inet.h>
45
 
46
#include <stdio.h>
47
#include <stdlib.h>
48
#include <string.h>
49
#include <errno.h>
50
#include <ctype.h>
51
#include <sys/types.h>
52
 
53
#define SETSOCKOPT_OPTVAL_TYPE (void *)
54
 
55
using namespace std;
56
 
57
/* socket values */
58
#define FTPLIB_BUFSIZ 1024
59
#define ACCEPT_TIMEOUT 30
60
 
61
/* io types */
62
#define FTPLIB_CONTROL 0
63
#define FTPLIB_READ 1
64
#define FTPLIB_WRITE 2
65
 
66
/*
67
 * Constructor
68
 */
69
 
70
ftplib::ftplib()
71
{
72
#ifndef NOSSL
235 andreas 73
#if OPENSSL_API_COMPAT < 0x010100000
117 andreas 74
    SSL_library_init();
75
#endif
235 andreas 76
#endif
117 andreas 77
 
78
    mp_ftphandle = static_cast<ftphandle *>(calloc(1, sizeof(ftphandle)));
79
 
80
    if (mp_ftphandle == NULL)
120 andreas 81
        errorHandler("calloc", errno, __LINE__);
117 andreas 82
 
83
    mp_ftphandle->buf = static_cast<char *>(malloc(FTPLIB_BUFSIZ));
84
 
85
    if (mp_ftphandle->buf == NULL)
86
    {
120 andreas 87
        errorHandler("calloc", errno, __LINE__);
117 andreas 88
        free(mp_ftphandle);
89
    }
90
 
91
#ifndef NOSSL
92
    mp_ftphandle->ctx = SSL_CTX_new(TLS_client_method());
93
    SSL_CTX_set_verify(mp_ftphandle->ctx, SSL_VERIFY_NONE, NULL);
94
    mp_ftphandle->ssl = SSL_new(mp_ftphandle->ctx);
95
#endif
96
    ClearHandle();
97
}
98
 
99
/*
100
 * Destructor
101
 */
102
 
103
ftplib::~ftplib()
104
{
105
#ifndef NOSSL
106
    SSL_free(mp_ftphandle->ssl);
107
    SSL_CTX_free(mp_ftphandle->ctx);
108
#endif
109
    free(mp_ftphandle->buf);
110
    free(mp_ftphandle);
111
}
112
 
242 andreas 113
void ftplib::sprint_rest(char *buf, off64_t offset, size_t len)
117 andreas 114
{
242 andreas 115
#if defined(__LP64__) && not defined(__MACH__)
116
    snprintf(buf, len, "REST %ld", offset);
117 andreas 117
#else
242 andreas 118
    snprintf(buf, len, "REST %lld", offset);
117 andreas 119
#endif
120
}
121
 
122
/*
123
 * socket_wait - wait for socket to receive or flush data
124
 *
125
 * return 1 if no user callback, otherwise, return value returned by
126
 * user callback
127
 */
128
int ftplib::socket_wait(ftphandle *ctl)
129
{
130
    fd_set fd, *rfd = NULL, *wfd = NULL;
131
    struct timeval tv;
132
    int rv = 0;
133
 
134
    if (ctl->idlecb == NULL)
135
        return 1;
136
 
137
    /*if ((ctl->dir == FTPLIB_CONTROL)
138
     *  || (ctl->idlecb == NULL)
139
     *  || ((ctl->idletime.tv_sec == 0)
140
     *  && //(ctl->idletime.tv_usec 0))
141
     * return 1;*/
142
 
143
    if (ctl->dir == FTPLIB_WRITE)
144
        wfd = &fd;
145
    else
146
        rfd = &fd;
147
 
148
    FD_ZERO(&fd);
149
 
150
    do
151
    {
152
        FD_SET(ctl->handle, &fd);
153
        tv = ctl->idletime;
154
        rv = select(ctl->handle + 1, rfd, wfd, NULL, &tv);
155
 
156
        if (rv == -1)
157
        {
158
            rv = 0;
120 andreas 159
            errorHandler("select", errno, __LINE__);
117 andreas 160
            strncpy(ctl->ctrl->response, strerror(errno), sizeof(ctl->ctrl->response));
161
            break;
162
        }
163
        else if (rv > 0)
164
        {
165
            rv = 1;
166
            break;
167
        }
168
    }
169
    while ((rv = ctl->idlecb(ctl->cbarg)));
170
 
171
    return rv;
172
}
173
 
174
/*
175
 * read a line of text
176
 *
177
 * return -1 on error or bytecount
178
 */
179
int ftplib::readline(char *buf, int max, ftphandle *ctl)
180
{
181
    int x, retval = 0;
182
    char *end, *bp = buf;
183
    int eof = 0;
184
 
185
    if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ))
186
        return -1;
187
 
188
    if (max == 0)
189
        return 0;
190
 
191
    do
192
    {
193
        if (ctl->cavail > 0)
194
        {
195
            x = (max >= ctl->cavail) ? ctl->cavail : max - 1;
196
            end = static_cast<char*>(memccpy(bp, ctl->cget, '\n', x));
197
 
198
            if (end != NULL)
380 andreas 199
                x = end - bp;
117 andreas 200
 
201
            retval += x;
202
            bp += x;
203
            *bp = '\0';
204
            max -= x;
205
            ctl->cget += x;
206
            ctl->cavail -= x;
207
 
208
            if (end != NULL)
209
            {
210
                bp -= 2;
211
 
212
                if (strcmp(bp, "\r\n") == 0)
213
                {
214
                    *bp++ = '\n';
215
                    *bp++ = '\0';
216
                    --retval;
217
                }
218
 
219
                break;
220
            }
221
        }
222
 
223
        if (max == 1)
224
        {
225
            *buf = '\0';
226
            break;
227
        }
228
 
229
        if (ctl->cput == ctl->cget)
230
        {
231
            ctl->cput = ctl->cget = ctl->buf;
232
            ctl->cavail = 0;
233
            ctl->cleft = FTPLIB_BUFSIZ;
234
        }
235
 
236
        if (eof)
237
        {
238
            if (retval == 0)
239
                retval = -1;
240
 
241
            break;
242
        }
243
 
244
        if (!socket_wait(ctl))
245
            return retval;
246
 
247
#ifndef NOSSL
248
        if (ctl->tlsdata)
249
            x = SSL_read(ctl->ssl, ctl->cput, ctl->cleft);
250
        else
251
        {
252
            if (ctl->tlsctrl)
253
                x = SSL_read(ctl->ssl, ctl->cput, ctl->cleft);
254
            else
380 andreas 255
                x = read(ctl->handle, ctl->cput, ctl->cleft);
117 andreas 256
        }
257
 
258
#else
118 andreas 259
        x = read(ctl->handle, ctl->cput, ctl->cleft);
117 andreas 260
#endif
261
 
262
        if (x == -1)
263
        {
120 andreas 264
            errorHandler("read", errno, __LINE__);
117 andreas 265
            retval = -1;
266
            break;
267
        }
268
 
269
        // LOGGING FUNCTIONALITY!!!
270
 
271
        if ((ctl->dir == FTPLIB_CONTROL) && (mp_ftphandle->logcb != NULL))
272
        {
273
            *((ctl->cput) + x) = '\0';
274
            mp_ftphandle->logcb(ctl->cput, mp_ftphandle->cbarg, true);
275
        }
276
 
277
        if (x == 0)
278
            eof = 1;
279
 
280
        ctl->cleft -= x;
281
        ctl->cavail += x;
282
        ctl->cput += x;
283
    }
284
    while (1);
285
 
286
    return retval;
287
}
288
 
289
/*
290
 * write lines of text
291
 *
292
 * return -1 on error or bytecount
293
 */
294
int ftplib::writeline(char *buf, int len, ftphandle *nData)
295
{
296
    int x, nb = 0, w;
297
    char *ubp = buf, *nbp;
298
    char lc = 0;
299
 
300
    if (nData->dir != FTPLIB_WRITE)
301
        return -1;
302
 
303
    nbp = nData->buf;
304
 
305
    for (x = 0; x < len; x++)
306
    {
307
        if ((*ubp == '\n') && (lc != '\r'))
308
        {
309
            if (nb == FTPLIB_BUFSIZ)
310
            {
311
                if (!socket_wait(nData))
312
                    return x;
313
 
314
#ifndef NOSSL
315
 
316
                if (nData->tlsctrl)
317
                    w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ);
318
                else
380 andreas 319
                    w = write(nData->handle, nbp, FTPLIB_BUFSIZ);
117 andreas 320
 
321
#else
118 andreas 322
                w = write(nData->handle, nbp, FTPLIB_BUFSIZ);
117 andreas 323
#endif
324
 
325
                if (w != FTPLIB_BUFSIZ)
326
                {
120 andreas 327
                    std::string msg = "write(1) returned " + std::to_string(w) + ", errno = " + std::to_string(errno);
328
                    errorHandler(msg.c_str(), 0, 0);
117 andreas 329
                    return (-1);
330
                }
331
 
332
                nb = 0;
333
            }
334
 
335
            nbp[nb++] = '\r';
336
        }
337
 
338
        if (nb == FTPLIB_BUFSIZ)
339
        {
340
            if (!socket_wait(nData))
341
                return x;
342
 
343
#ifndef NOSSL
344
 
345
            if (nData->tlsctrl)
346
                w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ);
347
            else
380 andreas 348
                w = write(nData->handle, nbp, FTPLIB_BUFSIZ);
117 andreas 349
 
350
#else
118 andreas 351
            w = write(nData->handle, nbp, FTPLIB_BUFSIZ);
117 andreas 352
#endif
353
 
354
            if (w != FTPLIB_BUFSIZ)
355
            {
120 andreas 356
                std::string msg = "write(2) returned " + std::to_string(w) + ", errno = " + std::to_string(errno);
357
                errorHandler(msg.c_str(), 0, 0);
117 andreas 358
                return (-1);
359
            }
360
 
361
            nb = 0;
362
        }
363
 
364
        nbp[nb++] = lc = *ubp++;
365
    }
366
 
367
    if (nb)
368
    {
369
        if (!socket_wait(nData))
370
            return x;
371
 
372
#ifndef NOSSL
373
 
380 andreas 374
        if (nData->tlsctrl) w = SSL_write(nData->ssl, nbp, nb);
375
        else w = write(nData->handle, nbp, nb);
117 andreas 376
 
377
#else
118 andreas 378
        w = write(nData->handle, nbp, nb);
117 andreas 379
#endif
380
 
381
        if (w != nb)
382
        {
120 andreas 383
            std::string msg = "write(2) returned " + std::to_string(w) + ", errno = " + std::to_string(errno);
384
            errorHandler(msg.c_str(), 0, 0);
117 andreas 385
            return (-1);
386
        }
387
    }
388
 
389
    return len;
390
}
391
 
392
/*
393
 * read a response from the server
394
 *
395
 * return 0 if first char doesn't match
396
 * return 1 if first char matches
397
 */
398
int ftplib::readresp(char c, ftphandle *nControl)
399
{
400
    char match[5];
401
 
402
    if (readline(nControl->response, 256, nControl) == -1)
403
    {
120 andreas 404
        errorHandler("Control socket read failed", errno, __LINE__);
117 andreas 405
        return 0;
406
    }
407
 
408
    if (nControl->response[3] == '-')
409
    {
410
        strncpy(match, nControl->response, 3);
411
        match[3] = ' ';
412
        match[4] = '\0';
413
 
414
        do
415
        {
416
            if (readline(nControl->response, 256, nControl) == -1)
417
            {
120 andreas 418
                errorHandler("Control socket read failed", errno, __LINE__);
117 andreas 419
                return 0;
420
            }
421
        }
422
        while (strncmp(nControl->response, match, 4));
423
    }
424
 
425
    if (nControl->response[0] == c)
426
        return 1;
427
 
428
    return 0;
429
}
430
 
431
/*
432
 * FtpLastResponse - return a pointer to the last response received
433
 */
434
char* ftplib::LastResponse()
435
{
436
    if ((mp_ftphandle) && (mp_ftphandle->dir == FTPLIB_CONTROL))
437
        return mp_ftphandle->response;
438
 
439
    return NULL;
440
}
441
 
442
/*
443
 * ftplib::Connect - connect to remote server
444
 *
445
 * return 1 if connected, 0 if not
446
 */
447
int ftplib::Connect(const char *host)
448
{
449
    int sControl;
450
    struct sockaddr_in sin;
451
    struct hostent *phe;
452
    struct servent *pse;
453
    int on = 1;
454
    int ret;
455
    char *lhost;
456
    char *pnum;
457
 
458
    mp_ftphandle->dir = FTPLIB_CONTROL;
459
    mp_ftphandle->ctrl = NULL;
460
    mp_ftphandle->xfered = 0;
461
    mp_ftphandle->xfered1 = 0;
462
#ifndef NOSSL
463
    mp_ftphandle->tlsctrl = 0;
464
    mp_ftphandle->tlsdata = 0;
465
#endif
466
    mp_ftphandle->offset = 0;
467
    mp_ftphandle->handle = 0;
468
 
469
    memset(&sin, 0, sizeof(sin));
470
    sin.sin_family = AF_INET;
471
    lhost = strdup(host);
472
    pnum = strchr(lhost, ':');
473
 
474
    if (pnum == NULL)
475
    {
476
        if ((pse = getservbyname("ftp", "tcp")) == NULL)
477
        {
120 andreas 478
            errorHandler("getservbyname", errno, __LINE__);
117 andreas 479
            free(lhost);
480
            return 0;
481
        }
482
 
483
        sin.sin_port = pse->s_port;
484
    }
485
    else
486
    {
487
        *pnum++ = '\0';
488
 
489
        if (isdigit(*pnum))
490
            sin.sin_port = htons(atoi(pnum));
491
        else
492
        {
493
            pse = getservbyname(pnum, "tcp");
494
            sin.sin_port = pse->s_port;
495
        }
496
    }
497
 
498
    ret = inet_aton(lhost, &sin.sin_addr);
499
 
500
    if (ret == 0)
501
    {
502
        if ((phe = gethostbyname(lhost)) == NULL)
503
        {
120 andreas 504
            errorHandler("gethostbyname", errno, __LINE__);
117 andreas 505
            free(lhost);
506
            return 0;
507
        }
508
 
509
        memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length);
510
    }
511
 
512
    free(lhost);
513
 
514
    sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
515
 
516
    if (sControl == -1)
517
    {
120 andreas 518
        errorHandler("socket", errno, __LINE__);
117 andreas 519
        return 0;
520
    }
521
 
522
    if (setsockopt(sControl, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE & on, sizeof(on)) == -1)
523
    {
120 andreas 524
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 525
        close(sControl);
117 andreas 526
        return 0;
527
    }
528
 
529
    if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1)
530
    {
120 andreas 531
        errorHandler("connect", errno, __LINE__);
118 andreas 532
        close(sControl);
117 andreas 533
        return 0;
534
    }
535
 
536
    mp_ftphandle->handle = sControl;
137 andreas 537
    Log(LOG_DEBUG, string("Successfully connected to ") + host);
117 andreas 538
 
539
    if (readresp('2', mp_ftphandle) == 0)
540
    {
118 andreas 541
        close(sControl);
117 andreas 542
        mp_ftphandle->handle = 0;
543
        return 0;
544
    }
545
 
546
    return 1;
547
}
548
 
549
/*
550
 * FtpSendCmd - send a command and wait for expected response
551
 *
552
 * return 1 if proper response received, 0 otherwise
553
 */
554
int ftplib::FtpSendCmd(const char *cmd, char expresp, ftphandle *nControl)
555
{
556
    char buf[256];
557
    int x;
558
 
120 andreas 559
    if (!nControl->handle)
560
        return 0;
117 andreas 561
 
120 andreas 562
    if (nControl->dir != FTPLIB_CONTROL)
563
        return 0;
117 andreas 564
 
242 andreas 565
    snprintf(buf, sizeof(buf), "%s\r\n", cmd);
117 andreas 566
 
567
#ifndef NOSSL
568
    if (nControl->tlsctrl)
380 andreas 569
        x = SSL_write(nControl->ssl, buf, strlen(buf));
117 andreas 570
    else
380 andreas 571
        x = write(nControl->handle, buf, strlen(buf));
117 andreas 572
 
573
#else
118 andreas 574
    x = write(nControl->handle, buf, strlen(buf));
117 andreas 575
#endif
576
 
577
    if (x <= 0)
578
    {
120 andreas 579
        errorHandler("write", errno, __LINE__);
117 andreas 580
        return 0;
581
    }
582
 
583
    if (mp_ftphandle->logcb != NULL)
584
        mp_ftphandle->logcb(buf, mp_ftphandle->cbarg, false);
585
 
586
    return readresp(expresp, nControl);
587
}
588
 
589
/*
590
 * FtpLogin - log in to remote server
591
 *
592
 * return 1 if logged in, 0 otherwise
593
 */
594
int ftplib::Login(const char *user, const char *pass)
595
{
596
    char tempbuf[64];
597
 
598
    if (((strlen(user) + 7) > sizeof(tempbuf)) || ((strlen(pass) + 7) > sizeof(tempbuf)))
599
        return 0;
600
 
242 andreas 601
    snprintf(tempbuf, sizeof(tempbuf), "USER %s", user);
117 andreas 602
 
603
    if (!FtpSendCmd(tempbuf, '3', mp_ftphandle))
604
    {
605
        if (mp_ftphandle->ctrl != NULL)
606
            return 1;
607
 
608
        if (*LastResponse() == '2')
609
            return 1;
610
 
611
        return 0;
612
    }
613
 
242 andreas 614
    snprintf(tempbuf, sizeof(tempbuf), "PASS %s", pass);
117 andreas 615
    return FtpSendCmd(tempbuf, '2', mp_ftphandle);
616
}
617
 
618
/*
619
 * FtpAcceptConnection - accept connection from server
620
 *
621
 * return 1 if successful, 0 otherwise
622
 */
623
int ftplib::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl)
624
{
625
    int sData;
626
    struct sockaddr addr;
627
    socklen_t l;
628
    int i;
629
    struct timeval tv;
630
    fd_set mask;
631
    int rv = 0;
632
 
633
    FD_ZERO(&mask);
634
    FD_SET(nControl->handle, &mask);
635
    FD_SET(nData->handle, &mask);
636
    tv.tv_usec = 0;
637
    tv.tv_sec = ACCEPT_TIMEOUT;
638
    i = nControl->handle;
639
 
640
    if (i < nData->handle)
641
        i = nData->handle;
642
 
643
    i = select(i + 1, &mask, NULL, NULL, &tv);
644
 
645
    if (i == -1)
646
    {
647
        strncpy(nControl->response, strerror(errno), sizeof(nControl->response));
118 andreas 648
        close(nData->handle);
117 andreas 649
        nData->handle = 0;
650
        rv = 0;
651
    }
652
    else if (i == 0)
653
    {
654
        strcpy(nControl->response, "timed out waiting for connection");
118 andreas 655
        close(nData->handle);
117 andreas 656
        nData->handle = 0;
657
        rv = 0;
658
    }
659
    else
660
    {
661
        if (FD_ISSET(nData->handle, &mask))
662
        {
663
            l = sizeof(addr);
664
            sData = accept(nData->handle, &addr, &l);
665
            i = errno;
118 andreas 666
            close(nData->handle);
117 andreas 667
 
668
            if (sData > 0)
669
            {
670
                rv = 1;
671
                nData->handle = sData;
672
                nData->ctrl = nControl;
673
            }
674
            else
675
            {
676
                strncpy(nControl->response, strerror(i), sizeof(nControl->response));
677
                nData->handle = 0;
678
                rv = 0;
679
            }
680
        }
681
        else if (FD_ISSET(nControl->handle, &mask))
682
        {
118 andreas 683
            close(nData->handle);
117 andreas 684
            nData->handle = 0;
685
            readresp('2', nControl);
686
            rv = 0;
687
        }
688
    }
689
 
690
    return rv;
691
}
692
 
693
/*
694
 * FtpAccess - return a handle for a data stream
695
 *
696
 * return 1 if successful, 0 otherwise
697
 */
698
int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftphandle *nControl, ftphandle **nData)
699
{
700
    char buf[256];
701
    int dir;
702
 
703
    if ((path == NULL) && ((type == ftplib::filewrite)
704
                           || (type == ftplib::fileread)
705
                           || (type == ftplib::filereadappend)
706
                           || (type == ftplib::filewriteappend)))
707
    {
242 andreas 708
        snprintf(nControl->response, sizeof(ftphandle::response), "Missing path argument for file transfer\n");
120 andreas 709
        errorHandler(nControl->response, 0, __LINE__);
117 andreas 710
        return 0;
711
    }
712
 
242 andreas 713
    snprintf(buf, sizeof(buf), "TYPE %c", mode);
117 andreas 714
 
120 andreas 715
    if (!FtpSendCmd(buf, '2', nControl))
716
        return 0;
117 andreas 717
 
718
    switch (type)
719
    {
720
        case ftplib::dir:
721
            strcpy(buf, "NLST");
722
            dir = FTPLIB_READ;
723
            break;
724
 
725
        case ftplib::dirverbose:
179 andreas 726
//            strcpy(buf, "LIST -aL");
727
            strcpy(buf, "LIST");
117 andreas 728
            dir = FTPLIB_READ;
729
            break;
730
 
731
        case ftplib::filereadappend:
732
        case ftplib::fileread:
733
            strcpy(buf, "RETR");
734
            dir = FTPLIB_READ;
735
            break;
736
 
737
        case ftplib::filewriteappend:
738
        case ftplib::filewrite:
739
            strcpy(buf, "STOR");
740
            dir = FTPLIB_WRITE;
741
            break;
742
 
743
        default:
242 andreas 744
            snprintf(nControl->response, sizeof(ftphandle::response), "Invalid open type %d\n", type);
120 andreas 745
            errorHandler(nControl->response, 0, __LINE__);
117 andreas 746
            return 0;
747
    }
748
 
749
    if (path != NULL)
750
    {
751
        int i = strlen(buf);
752
        buf[i++] = ' ';
753
 
754
        if ((strlen(path) + i) >= sizeof(buf))
755
            return 0;
756
 
380 andreas 757
        strcpy(&buf[i], path);
117 andreas 758
    }
759
 
760
    if (nControl->cmode == ftplib::pasv)
761
    {
762
        if (FtpOpenPasv(nControl, nData, mode, dir, buf) == -1)
763
            return 0;
764
    }
765
 
766
    if (nControl->cmode == ftplib::port)
767
    {
768
        if (FtpOpenPort(nControl, nData, mode, dir, buf) == -1)
769
            return 0;
770
 
771
        if (!FtpAcceptConnection(*nData, nControl))
772
        {
773
            FtpClose(*nData);
774
            *nData = NULL;
775
            return 0;
776
        }
777
    }
778
 
779
#ifndef NOSSL
780
 
781
    if (nControl->tlsdata)
782
    {
783
        (*nData)->ssl = SSL_new(nControl->ctx);
784
        (*nData)->sbio = BIO_new_socket((*nData)->handle, BIO_NOCLOSE);
785
        SSL_set_bio((*nData)->ssl, (*nData)->sbio, (*nData)->sbio);
786
        int ret = SSL_connect((*nData)->ssl);
787
 
788
        if (ret != 1)
789
            return 0;
790
 
791
        (*nData)->tlsdata = 1;
792
    }
793
 
794
#endif
795
    return 1;
796
}
797
 
798
/*
799
 * FtpOpenPort - Establishes a PORT connection for data transfer
800
 *
801
 * return 1 if successful, -1 otherwise
802
 */
803
int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd)
804
{
805
    int sData;
806
 
807
    union
808
    {
809
        struct sockaddr sa;
810
        struct sockaddr_in in;
811
    } sin;
812
 
813
    struct linger lng = { 0, 0 };
814
    socklen_t l;
815
    int on = 1;
816
    ftphandle *ctrl;
817
    char buf[256];
818
 
819
    if (nControl->dir != FTPLIB_CONTROL)
820
        return -1;
821
 
822
    if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE))
823
    {
242 andreas 824
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid direction %d\n", dir);
117 andreas 825
        return -1;
826
    }
827
 
828
    if ((mode != ftplib::ascii) && (mode != ftplib::image))
829
    {
242 andreas 830
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid mode %c\n", mode);
117 andreas 831
        return -1;
832
    }
833
 
834
    l = sizeof(sin);
835
 
836
    if (getsockname(nControl->handle, &sin.sa, &l) < 0)
837
    {
120 andreas 838
        errorHandler("getsockname", errno, __LINE__);
117 andreas 839
        return -1;
840
    }
841
 
842
    sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
843
 
844
    if (sData == -1)
845
    {
120 andreas 846
        errorHandler("socket", errno, __LINE__);
117 andreas 847
        return -1;
848
    }
849
 
850
    if (setsockopt(sData, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE & on, sizeof(on)) == -1)
851
    {
120 andreas 852
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 853
        close(sData);
117 andreas 854
        return -1;
855
    }
856
 
857
    if (setsockopt(sData, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE & lng, sizeof(lng)) == -1)
858
    {
120 andreas 859
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 860
        close(sData);
117 andreas 861
        return -1;
862
    }
863
 
864
    sin.in.sin_port = 0;
865
 
120 andreas 866
    if (::bind(sData, &sin.sa, sizeof(sin)) == -1)
117 andreas 867
    {
120 andreas 868
        errorHandler("bind", errno, __LINE__);
118 andreas 869
        close(sData);
117 andreas 870
        return -1;
871
    }
872
 
873
    if (listen(sData, 1) < 0)
874
    {
120 andreas 875
        errorHandler("listen", errno, __LINE__);
118 andreas 876
        close(sData);
117 andreas 877
        return -1;
878
    }
879
 
880
    if (getsockname(sData, &sin.sa, &l) < 0)
881
        return 0;
882
 
242 andreas 883
    snprintf(buf, sizeof(buf), "PORT %hhu,%hhu,%hhu,%hhu,%hhu,%hhu",
117 andreas 884
            (unsigned char) sin.sa.sa_data[2],
885
            (unsigned char) sin.sa.sa_data[3],
886
            (unsigned char) sin.sa.sa_data[4],
887
            (unsigned char) sin.sa.sa_data[5],
888
            (unsigned char) sin.sa.sa_data[0],
889
            (unsigned char) sin.sa.sa_data[1]);
890
 
891
    if (!FtpSendCmd(buf, '2', nControl))
892
    {
118 andreas 893
        close(sData);
117 andreas 894
        return -1;
895
    }
896
 
897
    if (mp_ftphandle->offset != 0)
898
    {
899
        char buf[256];
242 andreas 900
        sprint_rest(buf, mp_ftphandle->offset, sizeof(buf));
117 andreas 901
 
902
        if (!FtpSendCmd(buf, '3', nControl))
903
        {
118 andreas 904
            close(sData);
117 andreas 905
            return 0;
906
        }
907
    }
908
 
909
    ctrl = static_cast<ftphandle*>(calloc(1, sizeof(ftphandle)));
910
 
911
    if (ctrl == NULL)
912
    {
120 andreas 913
        errorHandler("calloc", errno, __LINE__);
118 andreas 914
        close(sData);
117 andreas 915
        return -1;
916
    }
917
 
918
    if ((mode == 'A') && ((ctrl->buf = static_cast<char*>(malloc(FTPLIB_BUFSIZ))) == NULL))
919
    {
120 andreas 920
        errorHandler("calloc", errno, __LINE__);
118 andreas 921
        close(sData);
117 andreas 922
        free(ctrl);
923
        return -1;
924
    }
925
 
926
    if (!FtpSendCmd(cmd, '1', nControl))
927
    {
928
        FtpClose(*nData);
929
        *nData = NULL;
930
        return -1;
931
    }
932
 
933
    ctrl->handle = sData;
934
    ctrl->dir = dir;
935
    ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL;
936
    ctrl->idletime = nControl->idletime;
937
    ctrl->cbarg = nControl->cbarg;
938
    ctrl->xfered = 0;
939
    ctrl->xfered1 = 0;
940
    ctrl->cbbytes = nControl->cbbytes;
941
 
942
    if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec)
943
        ctrl->idlecb = nControl->idlecb;
944
    else
945
        ctrl->idlecb = NULL;
946
 
947
    if (ctrl->cbbytes)
948
        ctrl->xfercb = nControl->xfercb;
949
    else
950
        ctrl->xfercb = NULL;
951
 
952
    *nData = ctrl;
953
 
954
    return 1;
955
}
956
 
957
/*
958
 * FtpOpenPasv - Establishes a PASV connection for data transfer
959
 *
960
 * return 1 if successful, -1 otherwise
961
 */
962
int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd)
963
{
964
    int sData;
965
 
966
    union
967
    {
968
        struct sockaddr sa;
969
        struct sockaddr_in in;
970
    } sin;
971
 
972
    struct linger lng = { 0, 0 };
973
    unsigned int l;
974
    int on = 1;
975
    ftphandle *ctrl;
976
    char *cp;
977
    unsigned char v[6];
978
    int ret;
979
 
980
    if (nControl->dir != FTPLIB_CONTROL)
981
        return -1;
982
 
983
    if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE))
984
    {
242 andreas 985
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid direction %d\n", dir);
117 andreas 986
        return -1;
987
    }
988
 
989
    if ((mode != ftplib::ascii) && (mode != ftplib::image))
990
    {
242 andreas 991
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid mode %c\n", mode);
117 andreas 992
        return -1;
993
    }
994
 
995
    l = sizeof(sin);
996
 
997
    memset(&sin, 0, l);
998
    sin.in.sin_family = AF_INET;
999
 
1000
    if (!FtpSendCmd("PASV", '2', nControl))
1001
        return -1;
1002
 
1003
    cp = strchr(nControl->response, '(');
1004
 
1005
    if (cp == NULL)
1006
        return -1;
1007
 
1008
    cp++;
1009
    sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
1010
 
1011
    if (nControl->correctpasv)
1012
        if (!CorrectPasvResponse(v))
1013
            return -1;
1014
 
1015
    sin.sa.sa_data[2] = v[2];
1016
    sin.sa.sa_data[3] = v[3];
1017
    sin.sa.sa_data[4] = v[4];
1018
    sin.sa.sa_data[5] = v[5];
1019
    sin.sa.sa_data[0] = v[0];
1020
    sin.sa.sa_data[1] = v[1];
1021
 
1022
    if (mp_ftphandle->offset != 0)
1023
    {
1024
        char buf[256];
242 andreas 1025
        sprint_rest(buf, mp_ftphandle->offset, sizeof(buf));
117 andreas 1026
 
1027
        if (!FtpSendCmd(buf, '3', nControl))
1028
            return 0;
1029
    }
1030
 
1031
    sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1032
 
1033
    if (sData == -1)
1034
    {
120 andreas 1035
        errorHandler("socket", errno, __LINE__);
117 andreas 1036
        return -1;
1037
    }
1038
 
1039
    if (setsockopt(sData, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE & on, sizeof(on)) == -1)
1040
    {
120 andreas 1041
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 1042
        close(sData);
117 andreas 1043
        return -1;
1044
    }
1045
 
1046
    if (setsockopt(sData, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE & lng, sizeof(lng)) == -1)
1047
    {
120 andreas 1048
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 1049
        close(sData);
117 andreas 1050
        return -1;
1051
    }
1052
 
1053
    if (nControl->dir != FTPLIB_CONTROL)
1054
        return -1;
1055
 
1056
    memcpy(cmd + strlen(cmd), "\r\n\0", 3);
1057
#ifndef NOSSL
1058
 
1059
    if (nControl->tlsctrl)
380 andreas 1060
        ret = SSL_write(nControl->ssl, cmd, strlen(cmd));
117 andreas 1061
    else
380 andreas 1062
        ret = write(nControl->handle, cmd, strlen(cmd));
117 andreas 1063
 
1064
#else
118 andreas 1065
    ret = write(nControl->handle, cmd, strlen(cmd));
117 andreas 1066
#endif
1067
 
1068
    if (ret <= 0)
1069
    {
120 andreas 1070
        errorHandler("write", errno, __LINE__);
117 andreas 1071
        return -1;
1072
    }
1073
 
1074
    if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1)
1075
    {
120 andreas 1076
        errorHandler("connect", errno, __LINE__);
118 andreas 1077
        close(sData);
117 andreas 1078
        return -1;
1079
    }
1080
 
1081
    if (!readresp('1', nControl))
1082
    {
118 andreas 1083
        close(sData);
117 andreas 1084
        return -1;
1085
    }
1086
 
1087
    ctrl = static_cast<ftphandle*>(calloc(1, sizeof(ftphandle)));
1088
 
1089
    if (ctrl == NULL)
1090
    {
120 andreas 1091
        errorHandler("calloc", errno, __LINE__);
118 andreas 1092
        close(sData);
117 andreas 1093
        return -1;
1094
    }
1095
 
1096
    if ((mode == 'A') && ((ctrl->buf = static_cast<char*>(malloc(FTPLIB_BUFSIZ))) == NULL))
1097
    {
120 andreas 1098
        errorHandler("calloc", errno, __LINE__);
118 andreas 1099
        close(sData);
117 andreas 1100
        free(ctrl);
1101
        return -1;
1102
    }
1103
 
1104
    ctrl->handle = sData;
1105
    ctrl->dir = dir;
1106
    ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL;
1107
    ctrl->idletime = nControl->idletime;
1108
    ctrl->cbarg = nControl->cbarg;
1109
    ctrl->xfered = 0;
1110
    ctrl->xfered1 = 0;
1111
    ctrl->cbbytes = nControl->cbbytes;
1112
 
1113
    if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec)
1114
        ctrl->idlecb = nControl->idlecb;
1115
    else
1116
        ctrl->idlecb = NULL;
1117
 
1118
    if (ctrl->cbbytes)
1119
        ctrl->xfercb = nControl->xfercb;
1120
    else
1121
        ctrl->xfercb = NULL;
1122
 
1123
    *nData = ctrl;
1124
 
1125
    return 1;
1126
}
1127
 
1128
/*
1129
 * FtpClose - close a data connection
1130
 */
1131
int ftplib::FtpClose(ftphandle *nData)
1132
{
1133
    ftphandle *ctrl;
1134
 
1135
    if (nData->dir == FTPLIB_WRITE)
1136
    {
118 andreas 1137
        if (nData->buf != NULL)
1138
            writeline(NULL, 0, nData);
117 andreas 1139
    }
1140
    else if (nData->dir != FTPLIB_READ)
1141
        return 0;
1142
 
1143
    if (nData->buf)
1144
        free(nData->buf);
1145
 
1146
    shutdown(nData->handle, 2);
118 andreas 1147
    close(nData->handle);
117 andreas 1148
 
1149
    ctrl = nData->ctrl;
1150
#ifndef NOSSL
1151
    SSL_free(nData->ssl);
1152
#endif
1153
    free(nData);
1154
 
1155
    if (ctrl)
1156
        return readresp('2', ctrl);
1157
 
1158
    return 1;
1159
}
1160
 
1161
/*
1162
 * FtpRead - read from a data connection
1163
 */
1164
int ftplib::FtpRead(void *buf, int max, ftphandle *nData)
1165
{
1166
    int i;
1167
 
1168
    if (nData->dir != FTPLIB_READ)
1169
        return 0;
1170
 
1171
    if (nData->buf)
1172
        i = readline(static_cast<char*>(buf), max, nData);
1173
    else
1174
    {
1175
        i = socket_wait(nData);
1176
 
1177
        if (i != 1)
1178
            return 0;
1179
 
1180
#ifndef NOSSL
1181
        if (nData->tlsdata)
1182
            i = SSL_read(nData->ssl, buf, max);
1183
        else
380 andreas 1184
            i = read(nData->handle, buf, max);
117 andreas 1185
 
1186
#else
118 andreas 1187
        i = read(nData->handle, buf, max);
117 andreas 1188
#endif
1189
    }
1190
 
120 andreas 1191
    if (i == -1)
1192
        return 0;
117 andreas 1193
 
1194
    nData->xfered += i;
1195
 
120 andreas 1196
    if (mp_ftphandle->xfercb && nData->cbbytes)
117 andreas 1197
    {
1198
        nData->xfered1 += i;
1199
 
1200
        if (nData->xfered1 > nData->cbbytes)
1201
        {
120 andreas 1202
            if (mp_ftphandle->xfercb(nData->xfered, mp_ftphandle->cbarg) == 0)
117 andreas 1203
                return 0;
1204
 
1205
            nData->xfered1 = 0;
1206
        }
1207
    }
1208
 
1209
    return i;
1210
}
1211
 
1212
/*
1213
 * FtpWrite - write to a data connection
1214
 */
1215
int ftplib::FtpWrite(void *buf, int len, ftphandle *nData)
1216
{
1217
    int i;
1218
 
1219
    if (nData->dir != FTPLIB_WRITE) return 0;
1220
 
1221
    if (nData->buf)
1222
        i = writeline(static_cast<char*>(buf), len, nData);
1223
    else
1224
    {
1225
        socket_wait(nData);
1226
#ifndef NOSSL
1227
 
1228
        if (nData->tlsdata)
1229
            i = SSL_write(nData->ssl, buf, len);
1230
        else
380 andreas 1231
            i = write(nData->handle, buf, len);
117 andreas 1232
 
1233
#else
118 andreas 1234
        i = write(nData->handle, buf, len);
117 andreas 1235
#endif
1236
    }
1237
 
1238
    if (i == -1)
1239
        return 0;
1240
 
1241
    nData->xfered += i;
1242
 
120 andreas 1243
    if (mp_ftphandle->xfercb && nData->cbbytes)
117 andreas 1244
    {
1245
        nData->xfered1 += i;
1246
 
1247
        if (nData->xfered1 > nData->cbbytes)
1248
        {
120 andreas 1249
            if (mp_ftphandle->xfercb(nData->xfered, mp_ftphandle->cbarg) == 0)
117 andreas 1250
                return 0;
1251
 
1252
            nData->xfered1 = 0;
1253
        }
1254
    }
1255
 
1256
    return i;
1257
}
1258
 
1259
/*
1260
 * FtpSite - send a SITE command
1261
 *
1262
 * return 1 if command successful, 0 otherwise
1263
 */
1264
int ftplib::Site(const char *cmd)
1265
{
1266
    char buf[256];
1267
 
1268
    if ((strlen(cmd) + 7) > sizeof(buf))
1269
        return 0;
1270
 
242 andreas 1271
    snprintf(buf, sizeof(buf), "SITE %s", cmd);
117 andreas 1272
 
1273
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1274
        return 0;
1275
 
1276
    return 1;
1277
}
1278
 
1279
/*
1280
 * FtpRaw - send a raw string string
1281
 *
1282
 * return 1 if command successful, 0 otherwise
1283
 */
1284
 
1285
int ftplib::Raw(const char *cmd)
1286
{
1287
    char buf[256];
1288
    strncpy(buf, cmd, 256);
1289
 
1290
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1291
        return 0;
1292
 
1293
    return 1;
1294
}
1295
 
1296
/*
1297
 * FtpSysType - send a SYST command
1298
 *
1299
 * Fills in the user buffer with the remote system type.  If more
1300
 * information from the response is required, the user can parse
1301
 * it out of the response buffer returned by FtpLastResponse().
1302
 *
1303
 * return 1 if command successful, 0 otherwise
1304
 */
1305
int ftplib::SysType(char *buf, int max)
1306
{
1307
    int l = max;
1308
    char *b = buf;
1309
    char *s;
1310
 
1311
    if (!FtpSendCmd("SYST", '2', mp_ftphandle))
1312
        return 0;
1313
 
1314
    s = &mp_ftphandle->response[4];
1315
 
1316
    while ((--l) && (*s != ' '))
1317
        *b++ = *s++;
1318
 
1319
    *b++ = '\0';
1320
    return 1;
1321
}
1322
 
1323
/*
1324
 * FtpMkdir - create a directory at server
1325
 *
1326
 * return 1 if successful, 0 otherwise
1327
 */
1328
int ftplib::Mkdir(const char *path)
1329
{
1330
    char buf[256];
1331
 
1332
    if ((strlen(path) + 6) > sizeof(buf))
1333
        return 0;
1334
 
242 andreas 1335
    snprintf(buf, sizeof(buf), "MKD %s", path);
117 andreas 1336
 
1337
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1338
        return 0;
1339
 
1340
    return 1;
1341
}
1342
 
1343
/*
1344
 * FtpChdir - change path at remote
1345
 *
1346
 * return 1 if successful, 0 otherwise
1347
 */
1348
int ftplib::Chdir(const char *path)
1349
{
1350
    char buf[256];
1351
 
1352
    if ((strlen(path) + 6) > sizeof(buf))
1353
        return 0;
1354
 
242 andreas 1355
    snprintf(buf, sizeof(buf), "CWD %s", path);
117 andreas 1356
 
1357
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1358
        return 0;
1359
 
1360
    return 1;
1361
}
1362
 
1363
/*
1364
 * FtpCDUp - move to parent directory at remote
1365
 *
1366
 * return 1 if successful, 0 otherwise
1367
 */
1368
int ftplib::Cdup()
1369
{
1370
    if (!FtpSendCmd("CDUP", '2', mp_ftphandle))
1371
        return 0;
1372
 
1373
    return 1;
1374
}
1375
 
1376
/*
1377
 * FtpRmdir - remove directory at remote
1378
 *
1379
 * return 1 if successful, 0 otherwise
1380
 */
1381
int ftplib::Rmdir(const char *path)
1382
{
1383
    char buf[256];
1384
 
1385
    if ((strlen(path) + 6) > sizeof(buf))
1386
        return 0;
1387
 
242 andreas 1388
    snprintf(buf, sizeof(buf), "RMD %s", path);
117 andreas 1389
 
1390
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1391
        return 0;
1392
 
1393
    return 1;
1394
}
1395
 
1396
/*
1397
 * FtpPwd - get working directory at remote
1398
 *
1399
 * return 1 if successful, 0 otherwise
1400
 */
1401
int ftplib::Pwd(char *path, int max)
1402
{
1403
    int l = max;
1404
    char *b = path;
1405
    char *s;
1406
 
1407
    if (!FtpSendCmd("PWD", '2', mp_ftphandle))
1408
        return 0;
1409
 
1410
    s = strchr(mp_ftphandle->response, '"');
1411
 
1412
    if (s == NULL)
1413
        return 0;
1414
 
1415
    s++;
1416
 
1417
    while ((--l) && (*s) && (*s != '"'))
1418
        *b++ = *s++;
1419
 
1420
    *b = '\0';
1421
    return 1;
1422
}
1423
 
1424
/*
1425
 * FtpXfer - issue a command and transfer data
1426
 *
1427
 * return 1 if successful, 0 otherwise
1428
 */
1429
int ftplib::FtpXfer(const char *localfile, const char *path, ftphandle *nControl, accesstype type, transfermode mode)
1430
{
1431
    int l, c;
1432
    char *dbuf;
1433
    FILE *local = NULL;
1434
    ftphandle *nData;
1435
 
1436
    if (localfile != NULL)
1437
    {
1438
        char ac[3] = "  ";
1439
 
1440
        if ((type == ftplib::dir) || (type == ftplib::dirverbose))
1441
        {
1442
            ac[0] = 'w';
1443
            ac[1] = '\0';
1444
        }
1445
 
1446
        if (type == ftplib::fileread)
1447
        {
1448
            ac[0] = 'w';
1449
            ac[1] = '\0';
1450
        }
1451
 
1452
        if (type == ftplib::filewriteappend)
1453
        {
1454
            ac[0] = 'r';
1455
            ac[1] = '\0';
1456
        }
1457
 
1458
        if (type == ftplib::filereadappend)
1459
        {
1460
            ac[0] = 'a';
1461
            ac[1] = '\0';
1462
        }
1463
 
1464
        if (type == ftplib::filewrite)
1465
        {
1466
            ac[0] = 'r';
1467
            ac[1] = '\0';
1468
        }
1469
 
1470
        if (mode == ftplib::image) ac[1] = 'b';
355 andreas 1471
#ifdef __ANDROID__
1472
        local = fopen(localfile, ac);
1473
#else
117 andreas 1474
        local = fopen64(localfile, ac);
355 andreas 1475
#endif
117 andreas 1476
        if (local == NULL)
1477
        {
120 andreas 1478
            std::string msg = string("Opening local file ") + localfile;
1479
            errorHandler(msg.c_str(), errno, __LINE__);
117 andreas 1480
            strncpy(nControl->response, strerror(errno), sizeof(nControl->response));
1481
            return 0;
1482
        }
1483
 
1484
        if (type == ftplib::filewriteappend)
355 andreas 1485
#ifdef __ANDROID__
1486
            fseek(local, mp_ftphandle->offset, SEEK_SET);
1487
#else
117 andreas 1488
            fseeko64(local, mp_ftphandle->offset, SEEK_SET);
355 andreas 1489
#endif
117 andreas 1490
    }
1491
 
1492
    if (local == NULL) local = ((type == ftplib::filewrite)
1493
                                    || (type == ftplib::filewriteappend)) ? stdin : stdout;
1494
 
1495
    if (!FtpAccess(path, type, mode, nControl, &nData))
1496
    {
1497
        if (localfile != NULL)
1498
            fclose(local);
1499
 
1500
        return 0;
1501
    }
1502
 
1503
    dbuf = static_cast<char*>(malloc(FTPLIB_BUFSIZ));
1504
 
1505
    if ((type == ftplib::filewrite) || (type == ftplib::filewriteappend))
1506
    {
380 andreas 1507
        while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0)
117 andreas 1508
        {
1509
            if ((c = FtpWrite(dbuf, l, nData)) < l)
1510
            {
120 andreas 1511
                std::string msg = string("short write: passed ") + std::to_string(l) + ", wrote " + std::to_string(c);
1512
                errorHandler(msg.c_str(), 0, 0);
117 andreas 1513
                break;
1514
            }
1515
        }
1516
    }
1517
    else
1518
    {
1519
        while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0)
1520
        {
1521
            if (fwrite(dbuf, 1, l, local) <= 0)
1522
            {
120 andreas 1523
                errorHandler("localfile write", errno, __LINE__);
117 andreas 1524
                break;
1525
            }
1526
        }
1527
    }
1528
 
1529
    free(dbuf);
1530
    fflush(local);
1531
 
1532
    if (localfile != NULL)
1533
        fclose(local);
1534
 
1535
    return FtpClose(nData);
1536
}
1537
 
1538
/*
1539
 * FtpNlst - issue an NLST command and write response to output
1540
 *
1541
 * return 1 if successful, 0 otherwise
1542
 */
1543
int ftplib::Nlst(const char *outputfile, const char *path)
1544
{
1545
    mp_ftphandle->offset = 0;
1546
    return FtpXfer(outputfile, path, mp_ftphandle, ftplib::dir, ftplib::ascii);
1547
}
1548
 
1549
/*
1550
 * FtpDir - issue a LIST command and write response to output
1551
 *
1552
 * return 1 if successful, 0 otherwise
1553
 */
1554
int ftplib::Dir(const char *outputfile, const char *path)
1555
{
1556
    mp_ftphandle->offset = 0;
1557
    return FtpXfer(outputfile, path, mp_ftphandle, ftplib::dirverbose, ftplib::ascii);
1558
}
1559
 
1560
/*
1561
 * FtpSize - determine the size of a remote file
1562
 *
1563
 * return 1 if successful, 0 otherwise
1564
 */
1565
int ftplib::Size(const char *path, int *size, transfermode mode)
1566
{
1567
    char cmd[256];
1568
    int resp, sz, rv = 1;
1569
 
1570
    if ((strlen(path) + 7) > sizeof(cmd))
1571
        return 0;
1572
 
242 andreas 1573
    snprintf(cmd, sizeof(cmd), "TYPE %c", mode);
117 andreas 1574
 
1575
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1576
        return 0;
1577
 
242 andreas 1578
    snprintf(cmd, sizeof(cmd), "SIZE %s", path);
117 andreas 1579
 
1580
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1581
        rv = 0;
1582
    else
1583
    {
1584
        if (sscanf(mp_ftphandle->response, "%d %d", &resp, &sz) == 2)
1585
            *size = sz;
1586
        else
1587
            rv = 0;
1588
    }
1589
 
1590
    return rv;
1591
}
1592
 
1593
/*
1594
 * FtpModDate - determine the modification date of a remote file
1595
 *
1596
 * return 1 if successful, 0 otherwise
1597
 */
1598
int ftplib::ModDate(const char *path, char *dt, int max)
1599
{
1600
    char buf[256];
1601
    int rv = 1;
1602
 
1603
    if ((strlen(path) + 7) > sizeof(buf))
1604
        return 0;
1605
 
242 andreas 1606
    snprintf(buf, sizeof(buf), "MDTM %s", path);
117 andreas 1607
 
1608
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1609
        rv = 0;
1610
    else
1611
        strncpy(dt, &mp_ftphandle->response[4], max);
1612
 
1613
    return rv;
1614
}
1615
 
1616
/*
1617
 * FtpGet - issue a GET command and write received data to output
1618
 *
1619
 * return 1 if successful, 0 otherwise
1620
 */
1621
 
1622
int ftplib::Get(const char *outputfile, const char *path, transfermode mode, off64_t offset)
1623
{
1624
    mp_ftphandle->offset = offset;
1625
 
1626
    if (offset == 0)
1627
        return FtpXfer(outputfile, path, mp_ftphandle, ftplib::fileread, mode);
1628
    else
1629
        return FtpXfer(outputfile, path, mp_ftphandle, ftplib::filereadappend, mode);
1630
}
1631
 
1632
/*
1633
 * FtpPut - issue a PUT command and send data from input
1634
 *
1635
 * return 1 if successful, 0 otherwise
1636
 */
1637
 
1638
int ftplib::Put(const char *inputfile, const char *path, transfermode mode, off64_t offset)
1639
{
1640
    mp_ftphandle->offset = offset;
1641
 
1642
    if (offset == 0)
1643
        return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewrite, mode);
1644
    else
1645
        return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewriteappend, mode);
1646
}
1647
 
1648
 
1649
int ftplib::Rename(const char *src, const char *dst)
1650
{
1651
    char cmd[256];
1652
 
1653
    if (((strlen(src) + 7) > sizeof(cmd)) || ((strlen(dst) + 7) > sizeof(cmd)))
1654
        return 0;
1655
 
242 andreas 1656
    snprintf(cmd, sizeof(cmd), "RNFR %s", src);
117 andreas 1657
 
1658
    if (!FtpSendCmd(cmd, '3', mp_ftphandle))
1659
        return 0;
1660
 
242 andreas 1661
    snprintf(cmd, sizeof(cmd), "RNTO %s", dst);
117 andreas 1662
 
1663
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1664
        return 0;
1665
 
1666
    return 1;
1667
}
1668
 
1669
int ftplib::Delete(const char *path)
1670
{
1671
    char cmd[256];
1672
 
1673
    if ((strlen(path) + 7) > sizeof(cmd))
1674
        return 0;
1675
 
242 andreas 1676
    snprintf(cmd, sizeof(cmd), "DELE %s", path);
117 andreas 1677
 
1678
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1679
        return 0;
1680
 
1681
    return 1;
1682
}
1683
 
1684
/*
1685
 * FtpQuit - disconnect from remote
1686
 *
1687
 * return 1 if successful, 0 otherwise
1688
 */
1689
int ftplib::Quit()
1690
{
1691
    if (mp_ftphandle->dir != FTPLIB_CONTROL)
1692
        return 0;
1693
 
1694
    if (mp_ftphandle->handle == 0)
1695
    {
1696
        strcpy(mp_ftphandle->response, "error: no anwser from server\n");
1697
        return 0;
1698
    }
1699
 
1700
    if (!FtpSendCmd("QUIT", '2', mp_ftphandle))
1701
    {
118 andreas 1702
        close(mp_ftphandle->handle);
117 andreas 1703
        return 0;
1704
    }
1705
    else
1706
    {
118 andreas 1707
        close(mp_ftphandle->handle);
117 andreas 1708
        return 1;
1709
    }
1710
}
1711
 
1712
int ftplib::Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathDst, transfermode mode, fxpmethod method)
1713
{
1714
    char *cp;
1715
    unsigned char v[6];
1716
    char buf[256];
1717
    int retval = 0;
1718
 
242 andreas 1719
    snprintf(buf, sizeof(buf), "TYPE %c", mode);
117 andreas 1720
 
1721
    if (!dst->FtpSendCmd(buf, '2', dst->mp_ftphandle))
1722
        return -1;
1723
 
1724
    if (!src->FtpSendCmd(buf, '2', src->mp_ftphandle))
1725
        return -1;
1726
 
1727
    if (method == ftplib::defaultfxp)
1728
    {
1729
        // PASV dst
1730
 
1731
        if (!dst->FtpSendCmd("PASV", '2', dst->mp_ftphandle))
1732
            return -1;
1733
 
1734
        cp = strchr(dst->mp_ftphandle->response, '(');
1735
 
1736
        if (cp == NULL) return -1;
1737
 
1738
        cp++;
1739
        sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
1740
 
1741
        if (dst->mp_ftphandle->correctpasv)
1742
            if (!dst->CorrectPasvResponse(v))
1743
                return -1;
1744
 
1745
        // PORT src
1746
 
242 andreas 1747
        snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d", v[2], v[3], v[4], v[5], v[0], v[1]);
117 andreas 1748
 
1749
        if (!src->FtpSendCmd(buf, '2', src->mp_ftphandle))
1750
            return -1;
1751
 
1752
        // RETR src
1753
 
1754
        strcpy(buf, "RETR");
1755
 
1756
        if (pathSrc != NULL)
1757
        {
380 andreas 1758
            int i = strlen(buf);
117 andreas 1759
            buf[i++] = ' ';
1760
 
1761
            if ((strlen(pathSrc) + i) >= sizeof(buf))
1762
                return 0;
1763
 
1764
            strcpy(&buf[i], pathSrc);
1765
        }
1766
 
1767
        if (!src->FtpSendCmd(buf, '1', src->mp_ftphandle))
1768
            return 0;
1769
 
1770
        // STOR dst
1771
 
1772
        strcpy(buf, "STOR");
1773
 
1774
        if (pathDst != NULL)
1775
        {
1776
            int i = strlen(buf);
1777
            buf[i++] = ' ';
1778
 
1779
            if ((strlen(pathDst) + i) >= sizeof(buf))
1780
                return 0;
1781
 
1782
            strcpy(&buf[i], pathDst);
1783
        }
1784
 
1785
        if (!dst->FtpSendCmd(buf, '1', dst->mp_ftphandle))
1786
        {
1787
            /* this closes the data connection, to abort the RETR on
1788
             *      the source ftp. all hail pftp, it took me several
1789
             *      hours and i was absolutely clueless, playing around with
1790
             *      ABOR and whatever, when i desperately checked the pftp
1791
             *      source which gave me this final hint. thanks dude(s). */
1792
 
1793
            dst->FtpSendCmd("PASV", '2', dst->mp_ftphandle);
1794
            src->readresp('4', src->mp_ftphandle);
1795
            return 0;
1796
        }
1797
 
1798
        retval = (src->readresp('2', src->mp_ftphandle)) & (dst->readresp('2', dst->mp_ftphandle));
1799
 
1800
    }
1801
    else
1802
    {
1803
        // PASV src
1804
        if (!src->FtpSendCmd("PASV", '2', src->mp_ftphandle))
1805
            return -1;
1806
 
1807
        cp = strchr(src->mp_ftphandle->response, '(');
1808
 
1809
        if (cp == NULL)
1810
            return -1;
1811
 
1812
        cp++;
1813
        sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
1814
 
1815
        if (src->mp_ftphandle->correctpasv)
1816
            if (!src->CorrectPasvResponse(v))
1817
                return -1;
1818
 
1819
        // PORT dst
1820
 
242 andreas 1821
        snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d", v[2], v[3], v[4], v[5], v[0], v[1]);
117 andreas 1822
 
1823
        if (!dst->FtpSendCmd(buf, '2', dst->mp_ftphandle))
1824
            return -1;
1825
 
1826
        // STOR dest
1827
 
1828
        strcpy(buf, "STOR");
1829
 
1830
        if (pathDst != NULL)
1831
        {
1832
            int i = strlen(buf);
1833
            buf[i++] = ' ';
1834
 
1835
            if ((strlen(pathDst) + i) >= sizeof(buf))
1836
                return 0;
1837
 
1838
            strcpy(&buf[i], pathDst);
1839
        }
1840
 
1841
        if (!dst->FtpSendCmd(buf, '1', dst->mp_ftphandle))
1842
            return 0;
1843
 
1844
        // RETR src
1845
 
1846
        strcpy(buf, "RETR");
1847
 
1848
        if (pathSrc != NULL)
1849
        {
1850
            int i = strlen(buf);
1851
            buf[i++] = ' ';
1852
 
1853
            if ((strlen(pathSrc) + i) >= sizeof(buf))
1854
                return 0;
1855
 
1856
            strcpy(&buf[i], pathSrc);
1857
        }
1858
 
1859
        if (!src->FtpSendCmd(buf, '1', src->mp_ftphandle))
1860
        {
1861
            src->FtpSendCmd("PASV", '2', src->mp_ftphandle);
1862
            dst->readresp('4', dst->mp_ftphandle);
1863
            return 0;
1864
        }
1865
 
1866
        // wait til its finished!
1867
 
1868
        retval = (src->readresp('2', src->mp_ftphandle)) & (dst->readresp('2', dst->mp_ftphandle));
1869
 
1870
    }
1871
 
1872
    return retval;
1873
}
1874
 
1875
 
1876
int ftplib::SetDataEncryption(dataencryption enc)
1877
{
1878
#ifdef NOSSL
1879
    (void)enc;
1880
    return 0;
1881
#else
1882
 
1883
    if (!mp_ftphandle->tlsctrl) return 0;
1884
 
1885
    if (!FtpSendCmd("PBSZ 0", '2', mp_ftphandle)) return 0;
1886
 
1887
    switch (enc)
1888
    {
1889
        case ftplib::unencrypted:
1890
            mp_ftphandle->tlsdata = 0;
1891
 
1892
            if (!FtpSendCmd("PROT C", '2', mp_ftphandle)) return 0;
1893
 
1894
            break;
1895
 
1896
        case ftplib::secure:
1897
            mp_ftphandle->tlsdata = 1;
1898
 
1899
            if (!FtpSendCmd("PROT P", '2', mp_ftphandle)) return 0;
1900
 
1901
            break;
1902
 
1903
        default:
1904
            return 0;
1905
    }
1906
 
1907
    return 1;
1908
#endif
1909
}
1910
 
1911
int ftplib::NegotiateEncryption()
1912
{
1913
#ifdef NOSSL
1914
    return 0;
1915
#else
1916
    int ret;
1917
 
1918
    if (!FtpSendCmd("AUTH TLS", '2', mp_ftphandle))
1919
        return 0;
1920
 
1921
    mp_ftphandle->sbio = BIO_new_socket(mp_ftphandle->handle, BIO_NOCLOSE);
1922
    SSL_set_bio(mp_ftphandle->ssl, mp_ftphandle->sbio, mp_ftphandle->sbio);
1923
 
1924
    ret = SSL_connect(mp_ftphandle->ssl);
1925
 
1926
    if (ret == 1)
1927
        mp_ftphandle->tlsctrl = 1;
1928
 
1929
    if (mp_ftphandle->certcb != NULL)
1930
    {
1931
        X509 *cert = SSL_get_peer_certificate(mp_ftphandle->ssl);
1932
 
1933
        if (!mp_ftphandle->certcb(mp_ftphandle->cbarg, cert))
1934
            return 0;
1935
    }
1936
 
1937
    if (ret < 1)
1938
        return 0;
1939
 
1940
    return 1;
1941
#endif
1942
}
1943
 
1944
void ftplib::SetCallbackCertFunction(FtpCallbackCert pointer)
1945
{
1946
#ifdef NOSSL
1947
    (void)pointer;
1948
#else
1949
    mp_ftphandle->certcb = pointer;
1950
#endif
1951
}
1952
 
1953
void ftplib::SetCallbackIdleFunction(FtpCallbackIdle pointer)
1954
{
1955
    mp_ftphandle->idlecb = pointer;
1956
}
1957
 
1958
void ftplib::SetCallbackXferFunction(FtpCallbackXfer pointer)
1959
{
1960
    mp_ftphandle->xfercb = pointer;
1961
}
1962
 
1963
void ftplib::SetCallbackLogFunction(FtpCallbackLog pointer)
1964
{
1965
    mp_ftphandle->logcb = pointer;
1966
}
1967
 
120 andreas 1968
void ftplib::SetCallbackErrorFunction(FtpCallbackError pointer)
1969
{
1970
    mp_ftphandle->errorcb = pointer;
1971
}
1972
 
117 andreas 1973
void ftplib::SetCallbackArg(void *arg)
1974
{
1975
    mp_ftphandle->cbarg = arg;
1976
}
1977
 
1978
void ftplib::SetCallbackBytes(off64_t bytes)
1979
{
1980
    mp_ftphandle->cbbytes = bytes;
1981
}
1982
 
1983
void ftplib::SetCallbackIdletime(int time)
1984
{
1985
    mp_ftphandle->idletime.tv_sec = time / 1000;
1986
    mp_ftphandle->idletime.tv_usec = (time % 1000) * 1000;
1987
}
1988
 
1989
void ftplib::SetConnmode(connmode mode)
1990
{
1991
    mp_ftphandle->cmode = mode;
137 andreas 1992
    Log(LOG_DEBUG, string("Mode was set to ") + (mode == pasv ? "PASSIVE" : "PORT"));
117 andreas 1993
}
1994
 
1995
void ftplib::ClearHandle()
1996
{
1997
    mp_ftphandle->dir = FTPLIB_CONTROL;
1998
    mp_ftphandle->ctrl = NULL;
1999
    mp_ftphandle->cmode = ftplib::pasv;
2000
    mp_ftphandle->idlecb = NULL;
2001
    mp_ftphandle->idletime.tv_sec = mp_ftphandle->idletime.tv_usec = 0;
2002
    mp_ftphandle->cbarg = NULL;
2003
    mp_ftphandle->xfered = 0;
2004
    mp_ftphandle->xfered1 = 0;
2005
    mp_ftphandle->cbbytes = 0;
2006
#ifndef NOSSL
2007
    mp_ftphandle->tlsctrl = 0;
2008
    mp_ftphandle->tlsdata = 0;
2009
    mp_ftphandle->certcb = NULL;
2010
#endif
2011
    mp_ftphandle->offset = 0;
2012
    mp_ftphandle->handle = 0;
2013
    mp_ftphandle->logcb = NULL;
2014
    mp_ftphandle->xfercb = NULL;
2015
    mp_ftphandle->correctpasv = false;
2016
}
2017
 
2018
int ftplib::CorrectPasvResponse(unsigned char *v)
2019
{
2020
    struct sockaddr ipholder;
2021
    socklen_t ipholder_size = sizeof(ipholder);
2022
 
2023
    if (getpeername(mp_ftphandle->handle, &ipholder, &ipholder_size) == -1)
2024
    {
120 andreas 2025
        errorHandler("getpeername", errno, __LINE__);
118 andreas 2026
        close(mp_ftphandle->handle);
117 andreas 2027
        return 0;
2028
    }
2029
 
2030
    for (int i = 2; i < 6; i++)
2031
        v[i] = ipholder.sa_data[i];
2032
 
2033
    return 1;
2034
}
2035
 
2036
ftphandle* ftplib::RawOpen(const char *path, accesstype type, transfermode mode)
2037
{
2038
    int ret;
137 andreas 2039
    ftphandle* datahandle{nullptr};
117 andreas 2040
    ret = FtpAccess(path, type, mode, mp_ftphandle, &datahandle);
2041
 
2042
    if (ret)
2043
        return datahandle;
2044
    else
137 andreas 2045
        return nullptr;
117 andreas 2046
}
2047
 
2048
int ftplib::RawClose(ftphandle* handle)
2049
{
2050
    return FtpClose(handle);
2051
}
2052
 
2053
int ftplib::RawWrite(void* buf, int len, ftphandle* handle)
2054
{
2055
    return FtpWrite(buf, len, handle);
2056
}
2057
 
2058
int ftplib::RawRead(void* buf, int max, ftphandle* handle)
2059
{
2060
    return FtpRead(buf, max, handle);
2061
}
120 andreas 2062
 
2063
void ftplib::errorHandler(const char* stub, int err, int line)
2064
{
2065
    char emsg[BUFSIZ];
2066
 
2067
    memset(emsg, 0, BUFSIZ);
2068
 
2069
    if (err != 0)
2070
        snprintf(emsg, sizeof(emsg), "%d: %s: %s", line, stub, strerror(err));
2071
    else if (line > 0)
2072
        snprintf(emsg, sizeof(emsg), "%d: %s", line, stub);
2073
    else
2074
        strncpy(emsg, stub, BUFSIZ-1);
2075
 
2076
    if (mp_ftphandle && mp_ftphandle->errorcb)
2077
        mp_ftphandle->errorcb(emsg, mp_ftphandle->cbarg, err);
137 andreas 2078
    else if (_Logging)
2079
        Log(LOG_ERROR, emsg);
120 andreas 2080
    else
2081
        std::cerr << emsg << std::endl;
2082
}
137 andreas 2083
 
2084
void ftplib::Log(int level, const std::string& msg)
2085
{
2086
    if (!_Logging)
2087
        return;
2088
 
2089
    _Logging(level, msg);
2090
}