Subversion Repositories tpanel

Rev

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