Subversion Repositories tpanel

Rev

Rev 380 | Rev 401 | 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);
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
 
705
    if ((path == NULL) && ((type == ftplib::filewrite)
706
                           || (type == ftplib::fileread)
707
                           || (type == ftplib::filereadappend)
708
                           || (type == ftplib::filewriteappend)))
709
    {
242 andreas 710
        snprintf(nControl->response, sizeof(ftphandle::response), "Missing path argument for file transfer\n");
120 andreas 711
        errorHandler(nControl->response, 0, __LINE__);
117 andreas 712
        return 0;
713
    }
714
 
242 andreas 715
    snprintf(buf, sizeof(buf), "TYPE %c", mode);
117 andreas 716
 
120 andreas 717
    if (!FtpSendCmd(buf, '2', nControl))
718
        return 0;
117 andreas 719
 
720
    switch (type)
721
    {
722
        case ftplib::dir:
723
            strcpy(buf, "NLST");
724
            dir = FTPLIB_READ;
725
            break;
726
 
727
        case ftplib::dirverbose:
179 andreas 728
//            strcpy(buf, "LIST -aL");
729
            strcpy(buf, "LIST");
117 andreas 730
            dir = FTPLIB_READ;
731
            break;
732
 
733
        case ftplib::filereadappend:
734
        case ftplib::fileread:
735
            strcpy(buf, "RETR");
736
            dir = FTPLIB_READ;
737
            break;
738
 
739
        case ftplib::filewriteappend:
740
        case ftplib::filewrite:
741
            strcpy(buf, "STOR");
742
            dir = FTPLIB_WRITE;
743
            break;
744
 
745
        default:
242 andreas 746
            snprintf(nControl->response, sizeof(ftphandle::response), "Invalid open type %d\n", type);
120 andreas 747
            errorHandler(nControl->response, 0, __LINE__);
117 andreas 748
            return 0;
749
    }
750
 
751
    if (path != NULL)
752
    {
753
        int i = strlen(buf);
754
        buf[i++] = ' ';
755
 
756
        if ((strlen(path) + i) >= sizeof(buf))
757
            return 0;
758
 
380 andreas 759
        strcpy(&buf[i], path);
117 andreas 760
    }
761
 
762
    if (nControl->cmode == ftplib::pasv)
763
    {
764
        if (FtpOpenPasv(nControl, nData, mode, dir, buf) == -1)
765
            return 0;
766
    }
767
 
768
    if (nControl->cmode == ftplib::port)
769
    {
770
        if (FtpOpenPort(nControl, nData, mode, dir, buf) == -1)
771
            return 0;
772
 
773
        if (!FtpAcceptConnection(*nData, nControl))
774
        {
775
            FtpClose(*nData);
776
            *nData = NULL;
777
            return 0;
778
        }
779
    }
780
 
781
#ifndef NOSSL
782
    if (nControl->tlsdata)
783
    {
784
        (*nData)->ssl = SSL_new(nControl->ctx);
785
        (*nData)->sbio = BIO_new_socket((*nData)->handle, BIO_NOCLOSE);
786
        SSL_set_bio((*nData)->ssl, (*nData)->sbio, (*nData)->sbio);
787
        int ret = SSL_connect((*nData)->ssl);
788
 
789
        if (ret != 1)
790
            return 0;
791
 
792
        (*nData)->tlsdata = 1;
793
    }
794
 
795
#endif
796
    return 1;
797
}
798
 
799
/*
800
 * FtpOpenPort - Establishes a PORT connection for data transfer
801
 *
802
 * return 1 if successful, -1 otherwise
803
 */
804
int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd)
805
{
806
    int sData;
807
 
808
    union
809
    {
810
        struct sockaddr sa;
811
        struct sockaddr_in in;
812
    } sin;
813
 
814
    struct linger lng = { 0, 0 };
815
    socklen_t l;
816
    int on = 1;
817
    ftphandle *ctrl;
818
    char buf[256];
819
 
820
    if (nControl->dir != FTPLIB_CONTROL)
821
        return -1;
822
 
823
    if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE))
824
    {
242 andreas 825
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid direction %d\n", dir);
117 andreas 826
        return -1;
827
    }
828
 
829
    if ((mode != ftplib::ascii) && (mode != ftplib::image))
830
    {
242 andreas 831
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid mode %c\n", mode);
117 andreas 832
        return -1;
833
    }
834
 
835
    l = sizeof(sin);
836
 
837
    if (getsockname(nControl->handle, &sin.sa, &l) < 0)
838
    {
120 andreas 839
        errorHandler("getsockname", errno, __LINE__);
117 andreas 840
        return -1;
841
    }
842
 
843
    sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
844
 
845
    if (sData == -1)
846
    {
120 andreas 847
        errorHandler("socket", errno, __LINE__);
117 andreas 848
        return -1;
849
    }
850
 
851
    if (setsockopt(sData, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE & on, sizeof(on)) == -1)
852
    {
120 andreas 853
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 854
        close(sData);
117 andreas 855
        return -1;
856
    }
857
 
858
    if (setsockopt(sData, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE & lng, sizeof(lng)) == -1)
859
    {
120 andreas 860
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 861
        close(sData);
117 andreas 862
        return -1;
863
    }
864
 
865
    sin.in.sin_port = 0;
866
 
120 andreas 867
    if (::bind(sData, &sin.sa, sizeof(sin)) == -1)
117 andreas 868
    {
120 andreas 869
        errorHandler("bind", errno, __LINE__);
118 andreas 870
        close(sData);
117 andreas 871
        return -1;
872
    }
873
 
874
    if (listen(sData, 1) < 0)
875
    {
120 andreas 876
        errorHandler("listen", errno, __LINE__);
118 andreas 877
        close(sData);
117 andreas 878
        return -1;
879
    }
880
 
881
    if (getsockname(sData, &sin.sa, &l) < 0)
882
        return 0;
883
 
242 andreas 884
    snprintf(buf, sizeof(buf), "PORT %hhu,%hhu,%hhu,%hhu,%hhu,%hhu",
117 andreas 885
            (unsigned char) sin.sa.sa_data[2],
886
            (unsigned char) sin.sa.sa_data[3],
887
            (unsigned char) sin.sa.sa_data[4],
888
            (unsigned char) sin.sa.sa_data[5],
889
            (unsigned char) sin.sa.sa_data[0],
890
            (unsigned char) sin.sa.sa_data[1]);
891
 
892
    if (!FtpSendCmd(buf, '2', nControl))
893
    {
118 andreas 894
        close(sData);
117 andreas 895
        return -1;
896
    }
897
 
898
    if (mp_ftphandle->offset != 0)
899
    {
900
        char buf[256];
242 andreas 901
        sprint_rest(buf, mp_ftphandle->offset, sizeof(buf));
117 andreas 902
 
903
        if (!FtpSendCmd(buf, '3', nControl))
904
        {
118 andreas 905
            close(sData);
117 andreas 906
            return 0;
907
        }
908
    }
909
 
910
    ctrl = static_cast<ftphandle*>(calloc(1, sizeof(ftphandle)));
911
 
912
    if (ctrl == NULL)
913
    {
120 andreas 914
        errorHandler("calloc", errno, __LINE__);
118 andreas 915
        close(sData);
117 andreas 916
        return -1;
917
    }
918
 
919
    if ((mode == 'A') && ((ctrl->buf = static_cast<char*>(malloc(FTPLIB_BUFSIZ))) == NULL))
920
    {
120 andreas 921
        errorHandler("calloc", errno, __LINE__);
118 andreas 922
        close(sData);
117 andreas 923
        free(ctrl);
924
        return -1;
925
    }
926
 
927
    if (!FtpSendCmd(cmd, '1', nControl))
928
    {
929
        FtpClose(*nData);
930
        *nData = NULL;
391 andreas 931
        free(ctrl);
117 andreas 932
        return -1;
933
    }
934
 
935
    ctrl->handle = sData;
936
    ctrl->dir = dir;
937
    ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL;
938
    ctrl->idletime = nControl->idletime;
939
    ctrl->cbarg = nControl->cbarg;
940
    ctrl->xfered = 0;
941
    ctrl->xfered1 = 0;
942
    ctrl->cbbytes = nControl->cbbytes;
943
 
944
    if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec)
945
        ctrl->idlecb = nControl->idlecb;
946
    else
947
        ctrl->idlecb = NULL;
948
 
949
    if (ctrl->cbbytes)
950
        ctrl->xfercb = nControl->xfercb;
951
    else
952
        ctrl->xfercb = NULL;
953
 
954
    *nData = ctrl;
955
 
956
    return 1;
957
}
958
 
959
/*
960
 * FtpOpenPasv - Establishes a PASV connection for data transfer
961
 *
962
 * return 1 if successful, -1 otherwise
963
 */
964
int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd)
965
{
966
    int sData;
967
 
968
    union
969
    {
970
        struct sockaddr sa;
971
        struct sockaddr_in in;
972
    } sin;
973
 
974
    struct linger lng = { 0, 0 };
975
    unsigned int l;
976
    int on = 1;
977
    ftphandle *ctrl;
978
    char *cp;
979
    unsigned char v[6];
980
    int ret;
981
 
982
    if (nControl->dir != FTPLIB_CONTROL)
983
        return -1;
984
 
985
    if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE))
986
    {
242 andreas 987
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid direction %d\n", dir);
117 andreas 988
        return -1;
989
    }
990
 
991
    if ((mode != ftplib::ascii) && (mode != ftplib::image))
992
    {
242 andreas 993
        snprintf(nControl->response, sizeof(ftphandle::response), "Invalid mode %c\n", mode);
117 andreas 994
        return -1;
995
    }
996
 
997
    l = sizeof(sin);
998
 
999
    memset(&sin, 0, l);
1000
    sin.in.sin_family = AF_INET;
1001
 
1002
    if (!FtpSendCmd("PASV", '2', nControl))
1003
        return -1;
1004
 
1005
    cp = strchr(nControl->response, '(');
1006
 
1007
    if (cp == NULL)
1008
        return -1;
1009
 
1010
    cp++;
1011
    sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
1012
 
1013
    if (nControl->correctpasv)
1014
        if (!CorrectPasvResponse(v))
1015
            return -1;
1016
 
1017
    sin.sa.sa_data[2] = v[2];
1018
    sin.sa.sa_data[3] = v[3];
1019
    sin.sa.sa_data[4] = v[4];
1020
    sin.sa.sa_data[5] = v[5];
1021
    sin.sa.sa_data[0] = v[0];
1022
    sin.sa.sa_data[1] = v[1];
1023
 
1024
    if (mp_ftphandle->offset != 0)
1025
    {
1026
        char buf[256];
242 andreas 1027
        sprint_rest(buf, mp_ftphandle->offset, sizeof(buf));
117 andreas 1028
 
1029
        if (!FtpSendCmd(buf, '3', nControl))
1030
            return 0;
1031
    }
1032
 
1033
    sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1034
 
1035
    if (sData == -1)
1036
    {
120 andreas 1037
        errorHandler("socket", errno, __LINE__);
117 andreas 1038
        return -1;
1039
    }
1040
 
1041
    if (setsockopt(sData, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE & on, sizeof(on)) == -1)
1042
    {
120 andreas 1043
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 1044
        close(sData);
117 andreas 1045
        return -1;
1046
    }
1047
 
1048
    if (setsockopt(sData, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE & lng, sizeof(lng)) == -1)
1049
    {
120 andreas 1050
        errorHandler("setsockopt", errno, __LINE__);
118 andreas 1051
        close(sData);
117 andreas 1052
        return -1;
1053
    }
1054
 
1055
    if (nControl->dir != FTPLIB_CONTROL)
1056
        return -1;
1057
 
1058
    memcpy(cmd + strlen(cmd), "\r\n\0", 3);
1059
#ifndef NOSSL
1060
 
1061
    if (nControl->tlsctrl)
380 andreas 1062
        ret = SSL_write(nControl->ssl, cmd, strlen(cmd));
117 andreas 1063
    else
380 andreas 1064
        ret = write(nControl->handle, cmd, strlen(cmd));
117 andreas 1065
 
1066
#else
118 andreas 1067
    ret = write(nControl->handle, cmd, strlen(cmd));
117 andreas 1068
#endif
1069
 
1070
    if (ret <= 0)
1071
    {
120 andreas 1072
        errorHandler("write", errno, __LINE__);
117 andreas 1073
        return -1;
1074
    }
1075
 
1076
    if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1)
1077
    {
120 andreas 1078
        errorHandler("connect", errno, __LINE__);
118 andreas 1079
        close(sData);
117 andreas 1080
        return -1;
1081
    }
1082
 
1083
    if (!readresp('1', nControl))
1084
    {
118 andreas 1085
        close(sData);
117 andreas 1086
        return -1;
1087
    }
1088
 
1089
    ctrl = static_cast<ftphandle*>(calloc(1, sizeof(ftphandle)));
1090
 
1091
    if (ctrl == NULL)
1092
    {
120 andreas 1093
        errorHandler("calloc", errno, __LINE__);
118 andreas 1094
        close(sData);
117 andreas 1095
        return -1;
1096
    }
1097
 
1098
    if ((mode == 'A') && ((ctrl->buf = static_cast<char*>(malloc(FTPLIB_BUFSIZ))) == NULL))
1099
    {
120 andreas 1100
        errorHandler("calloc", errno, __LINE__);
118 andreas 1101
        close(sData);
117 andreas 1102
        free(ctrl);
1103
        return -1;
1104
    }
1105
 
1106
    ctrl->handle = sData;
1107
    ctrl->dir = dir;
1108
    ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL;
1109
    ctrl->idletime = nControl->idletime;
1110
    ctrl->cbarg = nControl->cbarg;
1111
    ctrl->xfered = 0;
1112
    ctrl->xfered1 = 0;
1113
    ctrl->cbbytes = nControl->cbbytes;
1114
 
1115
    if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec)
1116
        ctrl->idlecb = nControl->idlecb;
1117
    else
1118
        ctrl->idlecb = NULL;
1119
 
1120
    if (ctrl->cbbytes)
1121
        ctrl->xfercb = nControl->xfercb;
1122
    else
1123
        ctrl->xfercb = NULL;
1124
 
1125
    *nData = ctrl;
1126
 
1127
    return 1;
1128
}
1129
 
1130
/*
1131
 * FtpClose - close a data connection
1132
 */
1133
int ftplib::FtpClose(ftphandle *nData)
1134
{
1135
    ftphandle *ctrl;
1136
 
1137
    if (nData->dir == FTPLIB_WRITE)
1138
    {
118 andreas 1139
        if (nData->buf != NULL)
1140
            writeline(NULL, 0, nData);
117 andreas 1141
    }
1142
    else if (nData->dir != FTPLIB_READ)
1143
        return 0;
1144
 
1145
    if (nData->buf)
1146
        free(nData->buf);
1147
 
1148
    shutdown(nData->handle, 2);
118 andreas 1149
    close(nData->handle);
117 andreas 1150
 
1151
    ctrl = nData->ctrl;
1152
#ifndef NOSSL
1153
    SSL_free(nData->ssl);
1154
#endif
1155
    free(nData);
1156
 
1157
    if (ctrl)
1158
        return readresp('2', ctrl);
1159
 
1160
    return 1;
1161
}
1162
 
1163
/*
1164
 * FtpRead - read from a data connection
1165
 */
1166
int ftplib::FtpRead(void *buf, int max, ftphandle *nData)
1167
{
1168
    int i;
1169
 
1170
    if (nData->dir != FTPLIB_READ)
1171
        return 0;
1172
 
1173
    if (nData->buf)
1174
        i = readline(static_cast<char*>(buf), max, nData);
1175
    else
1176
    {
1177
        i = socket_wait(nData);
1178
 
1179
        if (i != 1)
1180
            return 0;
1181
 
1182
#ifndef NOSSL
1183
        if (nData->tlsdata)
1184
            i = SSL_read(nData->ssl, buf, max);
1185
        else
380 andreas 1186
            i = read(nData->handle, buf, max);
117 andreas 1187
 
1188
#else
118 andreas 1189
        i = read(nData->handle, buf, max);
117 andreas 1190
#endif
1191
    }
1192
 
120 andreas 1193
    if (i == -1)
1194
        return 0;
117 andreas 1195
 
1196
    nData->xfered += i;
1197
 
120 andreas 1198
    if (mp_ftphandle->xfercb && nData->cbbytes)
117 andreas 1199
    {
1200
        nData->xfered1 += i;
1201
 
1202
        if (nData->xfered1 > nData->cbbytes)
1203
        {
120 andreas 1204
            if (mp_ftphandle->xfercb(nData->xfered, mp_ftphandle->cbarg) == 0)
117 andreas 1205
                return 0;
1206
 
1207
            nData->xfered1 = 0;
1208
        }
1209
    }
1210
 
1211
    return i;
1212
}
1213
 
1214
/*
1215
 * FtpWrite - write to a data connection
1216
 */
1217
int ftplib::FtpWrite(void *buf, int len, ftphandle *nData)
1218
{
1219
    int i;
1220
 
1221
    if (nData->dir != FTPLIB_WRITE) return 0;
1222
 
1223
    if (nData->buf)
1224
        i = writeline(static_cast<char*>(buf), len, nData);
1225
    else
1226
    {
1227
        socket_wait(nData);
1228
#ifndef NOSSL
1229
 
1230
        if (nData->tlsdata)
1231
            i = SSL_write(nData->ssl, buf, len);
1232
        else
380 andreas 1233
            i = write(nData->handle, buf, len);
117 andreas 1234
 
1235
#else
118 andreas 1236
        i = write(nData->handle, buf, len);
117 andreas 1237
#endif
1238
    }
1239
 
1240
    if (i == -1)
1241
        return 0;
1242
 
1243
    nData->xfered += i;
1244
 
120 andreas 1245
    if (mp_ftphandle->xfercb && nData->cbbytes)
117 andreas 1246
    {
1247
        nData->xfered1 += i;
1248
 
1249
        if (nData->xfered1 > nData->cbbytes)
1250
        {
120 andreas 1251
            if (mp_ftphandle->xfercb(nData->xfered, mp_ftphandle->cbarg) == 0)
117 andreas 1252
                return 0;
1253
 
1254
            nData->xfered1 = 0;
1255
        }
1256
    }
1257
 
1258
    return i;
1259
}
1260
 
1261
/*
1262
 * FtpSite - send a SITE command
1263
 *
1264
 * return 1 if command successful, 0 otherwise
1265
 */
1266
int ftplib::Site(const char *cmd)
1267
{
1268
    char buf[256];
1269
 
1270
    if ((strlen(cmd) + 7) > sizeof(buf))
1271
        return 0;
1272
 
242 andreas 1273
    snprintf(buf, sizeof(buf), "SITE %s", cmd);
117 andreas 1274
 
1275
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1276
        return 0;
1277
 
1278
    return 1;
1279
}
1280
 
1281
/*
1282
 * FtpRaw - send a raw string string
1283
 *
1284
 * return 1 if command successful, 0 otherwise
1285
 */
1286
 
1287
int ftplib::Raw(const char *cmd)
1288
{
1289
    char buf[256];
1290
    strncpy(buf, cmd, 256);
1291
 
1292
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1293
        return 0;
1294
 
1295
    return 1;
1296
}
1297
 
1298
/*
1299
 * FtpSysType - send a SYST command
1300
 *
1301
 * Fills in the user buffer with the remote system type.  If more
1302
 * information from the response is required, the user can parse
1303
 * it out of the response buffer returned by FtpLastResponse().
1304
 *
1305
 * return 1 if command successful, 0 otherwise
1306
 */
1307
int ftplib::SysType(char *buf, int max)
1308
{
1309
    int l = max;
1310
    char *b = buf;
1311
    char *s;
1312
 
1313
    if (!FtpSendCmd("SYST", '2', mp_ftphandle))
1314
        return 0;
1315
 
1316
    s = &mp_ftphandle->response[4];
1317
 
1318
    while ((--l) && (*s != ' '))
1319
        *b++ = *s++;
1320
 
1321
    *b++ = '\0';
1322
    return 1;
1323
}
1324
 
1325
/*
1326
 * FtpMkdir - create a directory at server
1327
 *
1328
 * return 1 if successful, 0 otherwise
1329
 */
1330
int ftplib::Mkdir(const char *path)
1331
{
1332
    char buf[256];
1333
 
1334
    if ((strlen(path) + 6) > sizeof(buf))
1335
        return 0;
1336
 
242 andreas 1337
    snprintf(buf, sizeof(buf), "MKD %s", path);
117 andreas 1338
 
1339
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1340
        return 0;
1341
 
1342
    return 1;
1343
}
1344
 
1345
/*
1346
 * FtpChdir - change path at remote
1347
 *
1348
 * return 1 if successful, 0 otherwise
1349
 */
1350
int ftplib::Chdir(const char *path)
1351
{
1352
    char buf[256];
1353
 
1354
    if ((strlen(path) + 6) > sizeof(buf))
1355
        return 0;
1356
 
242 andreas 1357
    snprintf(buf, sizeof(buf), "CWD %s", path);
117 andreas 1358
 
1359
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1360
        return 0;
1361
 
1362
    return 1;
1363
}
1364
 
1365
/*
1366
 * FtpCDUp - move to parent directory at remote
1367
 *
1368
 * return 1 if successful, 0 otherwise
1369
 */
1370
int ftplib::Cdup()
1371
{
1372
    if (!FtpSendCmd("CDUP", '2', mp_ftphandle))
1373
        return 0;
1374
 
1375
    return 1;
1376
}
1377
 
1378
/*
1379
 * FtpRmdir - remove directory at remote
1380
 *
1381
 * return 1 if successful, 0 otherwise
1382
 */
1383
int ftplib::Rmdir(const char *path)
1384
{
1385
    char buf[256];
1386
 
1387
    if ((strlen(path) + 6) > sizeof(buf))
1388
        return 0;
1389
 
242 andreas 1390
    snprintf(buf, sizeof(buf), "RMD %s", path);
117 andreas 1391
 
1392
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1393
        return 0;
1394
 
1395
    return 1;
1396
}
1397
 
1398
/*
1399
 * FtpPwd - get working directory at remote
1400
 *
1401
 * return 1 if successful, 0 otherwise
1402
 */
1403
int ftplib::Pwd(char *path, int max)
1404
{
1405
    int l = max;
1406
    char *b = path;
1407
    char *s;
1408
 
1409
    if (!FtpSendCmd("PWD", '2', mp_ftphandle))
1410
        return 0;
1411
 
1412
    s = strchr(mp_ftphandle->response, '"');
1413
 
1414
    if (s == NULL)
1415
        return 0;
1416
 
1417
    s++;
1418
 
1419
    while ((--l) && (*s) && (*s != '"'))
1420
        *b++ = *s++;
1421
 
1422
    *b = '\0';
1423
    return 1;
1424
}
1425
 
1426
/*
1427
 * FtpXfer - issue a command and transfer data
1428
 *
1429
 * return 1 if successful, 0 otherwise
1430
 */
1431
int ftplib::FtpXfer(const char *localfile, const char *path, ftphandle *nControl, accesstype type, transfermode mode)
1432
{
1433
    int l, c;
1434
    char *dbuf;
1435
    FILE *local = NULL;
1436
    ftphandle *nData;
1437
 
1438
    if (localfile != NULL)
1439
    {
1440
        char ac[3] = "  ";
1441
 
1442
        if ((type == ftplib::dir) || (type == ftplib::dirverbose))
1443
        {
1444
            ac[0] = 'w';
1445
            ac[1] = '\0';
1446
        }
1447
 
1448
        if (type == ftplib::fileread)
1449
        {
1450
            ac[0] = 'w';
1451
            ac[1] = '\0';
1452
        }
1453
 
1454
        if (type == ftplib::filewriteappend)
1455
        {
1456
            ac[0] = 'r';
1457
            ac[1] = '\0';
1458
        }
1459
 
1460
        if (type == ftplib::filereadappend)
1461
        {
1462
            ac[0] = 'a';
1463
            ac[1] = '\0';
1464
        }
1465
 
1466
        if (type == ftplib::filewrite)
1467
        {
1468
            ac[0] = 'r';
1469
            ac[1] = '\0';
1470
        }
1471
 
1472
        if (mode == ftplib::image) ac[1] = 'b';
355 andreas 1473
#ifdef __ANDROID__
1474
        local = fopen(localfile, ac);
1475
#else
117 andreas 1476
        local = fopen64(localfile, ac);
355 andreas 1477
#endif
117 andreas 1478
        if (local == NULL)
1479
        {
120 andreas 1480
            std::string msg = string("Opening local file ") + localfile;
1481
            errorHandler(msg.c_str(), errno, __LINE__);
117 andreas 1482
            strncpy(nControl->response, strerror(errno), sizeof(nControl->response));
1483
            return 0;
1484
        }
1485
 
1486
        if (type == ftplib::filewriteappend)
355 andreas 1487
#ifdef __ANDROID__
1488
            fseek(local, mp_ftphandle->offset, SEEK_SET);
1489
#else
117 andreas 1490
            fseeko64(local, mp_ftphandle->offset, SEEK_SET);
355 andreas 1491
#endif
117 andreas 1492
    }
1493
 
1494
    if (local == NULL) local = ((type == ftplib::filewrite)
1495
                                    || (type == ftplib::filewriteappend)) ? stdin : stdout;
1496
 
1497
    if (!FtpAccess(path, type, mode, nControl, &nData))
1498
    {
1499
        if (localfile != NULL)
1500
            fclose(local);
1501
 
1502
        return 0;
1503
    }
1504
 
1505
    dbuf = static_cast<char*>(malloc(FTPLIB_BUFSIZ));
1506
 
1507
    if ((type == ftplib::filewrite) || (type == ftplib::filewriteappend))
1508
    {
380 andreas 1509
        while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0)
117 andreas 1510
        {
1511
            if ((c = FtpWrite(dbuf, l, nData)) < l)
1512
            {
120 andreas 1513
                std::string msg = string("short write: passed ") + std::to_string(l) + ", wrote " + std::to_string(c);
1514
                errorHandler(msg.c_str(), 0, 0);
117 andreas 1515
                break;
1516
            }
1517
        }
1518
    }
1519
    else
1520
    {
1521
        while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0)
1522
        {
1523
            if (fwrite(dbuf, 1, l, local) <= 0)
1524
            {
120 andreas 1525
                errorHandler("localfile write", errno, __LINE__);
117 andreas 1526
                break;
1527
            }
1528
        }
1529
    }
1530
 
1531
    free(dbuf);
1532
    fflush(local);
1533
 
1534
    if (localfile != NULL)
1535
        fclose(local);
1536
 
1537
    return FtpClose(nData);
1538
}
1539
 
1540
/*
1541
 * FtpNlst - issue an NLST command and write response to output
1542
 *
1543
 * return 1 if successful, 0 otherwise
1544
 */
1545
int ftplib::Nlst(const char *outputfile, const char *path)
1546
{
1547
    mp_ftphandle->offset = 0;
1548
    return FtpXfer(outputfile, path, mp_ftphandle, ftplib::dir, ftplib::ascii);
1549
}
1550
 
1551
/*
1552
 * FtpDir - issue a LIST command and write response to output
1553
 *
1554
 * return 1 if successful, 0 otherwise
1555
 */
1556
int ftplib::Dir(const char *outputfile, const char *path)
1557
{
1558
    mp_ftphandle->offset = 0;
1559
    return FtpXfer(outputfile, path, mp_ftphandle, ftplib::dirverbose, ftplib::ascii);
1560
}
1561
 
1562
/*
1563
 * FtpSize - determine the size of a remote file
1564
 *
1565
 * return 1 if successful, 0 otherwise
1566
 */
1567
int ftplib::Size(const char *path, int *size, transfermode mode)
1568
{
1569
    char cmd[256];
1570
    int resp, sz, rv = 1;
1571
 
1572
    if ((strlen(path) + 7) > sizeof(cmd))
1573
        return 0;
1574
 
242 andreas 1575
    snprintf(cmd, sizeof(cmd), "TYPE %c", mode);
117 andreas 1576
 
1577
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1578
        return 0;
1579
 
242 andreas 1580
    snprintf(cmd, sizeof(cmd), "SIZE %s", path);
117 andreas 1581
 
1582
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1583
        rv = 0;
1584
    else
1585
    {
1586
        if (sscanf(mp_ftphandle->response, "%d %d", &resp, &sz) == 2)
1587
            *size = sz;
1588
        else
1589
            rv = 0;
1590
    }
1591
 
1592
    return rv;
1593
}
1594
 
1595
/*
1596
 * FtpModDate - determine the modification date of a remote file
1597
 *
1598
 * return 1 if successful, 0 otherwise
1599
 */
1600
int ftplib::ModDate(const char *path, char *dt, int max)
1601
{
1602
    char buf[256];
1603
    int rv = 1;
1604
 
1605
    if ((strlen(path) + 7) > sizeof(buf))
1606
        return 0;
1607
 
242 andreas 1608
    snprintf(buf, sizeof(buf), "MDTM %s", path);
117 andreas 1609
 
1610
    if (!FtpSendCmd(buf, '2', mp_ftphandle))
1611
        rv = 0;
1612
    else
1613
        strncpy(dt, &mp_ftphandle->response[4], max);
1614
 
1615
    return rv;
1616
}
1617
 
1618
/*
1619
 * FtpGet - issue a GET command and write received data to output
1620
 *
1621
 * return 1 if successful, 0 otherwise
1622
 */
1623
 
1624
int ftplib::Get(const char *outputfile, const char *path, transfermode mode, off64_t offset)
1625
{
1626
    mp_ftphandle->offset = offset;
1627
 
1628
    if (offset == 0)
1629
        return FtpXfer(outputfile, path, mp_ftphandle, ftplib::fileread, mode);
1630
    else
1631
        return FtpXfer(outputfile, path, mp_ftphandle, ftplib::filereadappend, mode);
1632
}
1633
 
1634
/*
1635
 * FtpPut - issue a PUT command and send data from input
1636
 *
1637
 * return 1 if successful, 0 otherwise
1638
 */
1639
 
1640
int ftplib::Put(const char *inputfile, const char *path, transfermode mode, off64_t offset)
1641
{
1642
    mp_ftphandle->offset = offset;
1643
 
1644
    if (offset == 0)
1645
        return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewrite, mode);
1646
    else
1647
        return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewriteappend, mode);
1648
}
1649
 
1650
 
1651
int ftplib::Rename(const char *src, const char *dst)
1652
{
1653
    char cmd[256];
1654
 
1655
    if (((strlen(src) + 7) > sizeof(cmd)) || ((strlen(dst) + 7) > sizeof(cmd)))
1656
        return 0;
1657
 
242 andreas 1658
    snprintf(cmd, sizeof(cmd), "RNFR %s", src);
117 andreas 1659
 
1660
    if (!FtpSendCmd(cmd, '3', mp_ftphandle))
1661
        return 0;
1662
 
242 andreas 1663
    snprintf(cmd, sizeof(cmd), "RNTO %s", dst);
117 andreas 1664
 
1665
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1666
        return 0;
1667
 
1668
    return 1;
1669
}
1670
 
1671
int ftplib::Delete(const char *path)
1672
{
1673
    char cmd[256];
1674
 
1675
    if ((strlen(path) + 7) > sizeof(cmd))
1676
        return 0;
1677
 
242 andreas 1678
    snprintf(cmd, sizeof(cmd), "DELE %s", path);
117 andreas 1679
 
1680
    if (!FtpSendCmd(cmd, '2', mp_ftphandle))
1681
        return 0;
1682
 
1683
    return 1;
1684
}
1685
 
1686
/*
1687
 * FtpQuit - disconnect from remote
1688
 *
1689
 * return 1 if successful, 0 otherwise
1690
 */
1691
int ftplib::Quit()
1692
{
1693
    if (mp_ftphandle->dir != FTPLIB_CONTROL)
1694
        return 0;
1695
 
1696
    if (mp_ftphandle->handle == 0)
1697
    {
1698
        strcpy(mp_ftphandle->response, "error: no anwser from server\n");
1699
        return 0;
1700
    }
1701
 
1702
    if (!FtpSendCmd("QUIT", '2', mp_ftphandle))
1703
    {
118 andreas 1704
        close(mp_ftphandle->handle);
117 andreas 1705
        return 0;
1706
    }
1707
    else
1708
    {
118 andreas 1709
        close(mp_ftphandle->handle);
117 andreas 1710
        return 1;
1711
    }
1712
}
1713
 
1714
int ftplib::Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathDst, transfermode mode, fxpmethod method)
1715
{
1716
    char *cp;
1717
    unsigned char v[6];
1718
    char buf[256];
1719
    int retval = 0;
1720
 
242 andreas 1721
    snprintf(buf, sizeof(buf), "TYPE %c", mode);
117 andreas 1722
 
1723
    if (!dst->FtpSendCmd(buf, '2', dst->mp_ftphandle))
1724
        return -1;
1725
 
1726
    if (!src->FtpSendCmd(buf, '2', src->mp_ftphandle))
1727
        return -1;
1728
 
1729
    if (method == ftplib::defaultfxp)
1730
    {
1731
        // PASV dst
1732
 
1733
        if (!dst->FtpSendCmd("PASV", '2', dst->mp_ftphandle))
1734
            return -1;
1735
 
1736
        cp = strchr(dst->mp_ftphandle->response, '(');
1737
 
1738
        if (cp == NULL) return -1;
1739
 
1740
        cp++;
1741
        sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
1742
 
1743
        if (dst->mp_ftphandle->correctpasv)
1744
            if (!dst->CorrectPasvResponse(v))
1745
                return -1;
1746
 
1747
        // PORT src
1748
 
242 andreas 1749
        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 1750
 
1751
        if (!src->FtpSendCmd(buf, '2', src->mp_ftphandle))
1752
            return -1;
1753
 
1754
        // RETR src
1755
 
1756
        strcpy(buf, "RETR");
1757
 
1758
        if (pathSrc != NULL)
1759
        {
380 andreas 1760
            int i = strlen(buf);
117 andreas 1761
            buf[i++] = ' ';
1762
 
1763
            if ((strlen(pathSrc) + i) >= sizeof(buf))
1764
                return 0;
1765
 
1766
            strcpy(&buf[i], pathSrc);
1767
        }
1768
 
1769
        if (!src->FtpSendCmd(buf, '1', src->mp_ftphandle))
1770
            return 0;
1771
 
1772
        // STOR dst
1773
 
1774
        strcpy(buf, "STOR");
1775
 
1776
        if (pathDst != NULL)
1777
        {
1778
            int i = strlen(buf);
1779
            buf[i++] = ' ';
1780
 
1781
            if ((strlen(pathDst) + i) >= sizeof(buf))
1782
                return 0;
1783
 
1784
            strcpy(&buf[i], pathDst);
1785
        }
1786
 
1787
        if (!dst->FtpSendCmd(buf, '1', dst->mp_ftphandle))
1788
        {
1789
            /* this closes the data connection, to abort the RETR on
1790
             *      the source ftp. all hail pftp, it took me several
1791
             *      hours and i was absolutely clueless, playing around with
1792
             *      ABOR and whatever, when i desperately checked the pftp
1793
             *      source which gave me this final hint. thanks dude(s). */
1794
 
1795
            dst->FtpSendCmd("PASV", '2', dst->mp_ftphandle);
1796
            src->readresp('4', src->mp_ftphandle);
1797
            return 0;
1798
        }
1799
 
1800
        retval = (src->readresp('2', src->mp_ftphandle)) & (dst->readresp('2', dst->mp_ftphandle));
1801
 
1802
    }
1803
    else
1804
    {
1805
        // PASV src
1806
        if (!src->FtpSendCmd("PASV", '2', src->mp_ftphandle))
1807
            return -1;
1808
 
1809
        cp = strchr(src->mp_ftphandle->response, '(');
1810
 
1811
        if (cp == NULL)
1812
            return -1;
1813
 
1814
        cp++;
1815
        sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
1816
 
1817
        if (src->mp_ftphandle->correctpasv)
1818
            if (!src->CorrectPasvResponse(v))
1819
                return -1;
1820
 
1821
        // PORT dst
1822
 
242 andreas 1823
        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 1824
 
1825
        if (!dst->FtpSendCmd(buf, '2', dst->mp_ftphandle))
1826
            return -1;
1827
 
1828
        // STOR dest
1829
 
1830
        strcpy(buf, "STOR");
1831
 
1832
        if (pathDst != NULL)
1833
        {
1834
            int i = strlen(buf);
1835
            buf[i++] = ' ';
1836
 
1837
            if ((strlen(pathDst) + i) >= sizeof(buf))
1838
                return 0;
1839
 
1840
            strcpy(&buf[i], pathDst);
1841
        }
1842
 
1843
        if (!dst->FtpSendCmd(buf, '1', dst->mp_ftphandle))
1844
            return 0;
1845
 
1846
        // RETR src
1847
 
1848
        strcpy(buf, "RETR");
1849
 
1850
        if (pathSrc != NULL)
1851
        {
1852
            int i = strlen(buf);
1853
            buf[i++] = ' ';
1854
 
1855
            if ((strlen(pathSrc) + i) >= sizeof(buf))
1856
                return 0;
1857
 
1858
            strcpy(&buf[i], pathSrc);
1859
        }
1860
 
1861
        if (!src->FtpSendCmd(buf, '1', src->mp_ftphandle))
1862
        {
1863
            src->FtpSendCmd("PASV", '2', src->mp_ftphandle);
1864
            dst->readresp('4', dst->mp_ftphandle);
1865
            return 0;
1866
        }
1867
 
1868
        // wait til its finished!
1869
 
1870
        retval = (src->readresp('2', src->mp_ftphandle)) & (dst->readresp('2', dst->mp_ftphandle));
1871
 
1872
    }
1873
 
1874
    return retval;
1875
}
1876
 
1877
 
1878
int ftplib::SetDataEncryption(dataencryption enc)
1879
{
1880
#ifdef NOSSL
1881
    (void)enc;
1882
    return 0;
1883
#else
1884
 
1885
    if (!mp_ftphandle->tlsctrl) return 0;
1886
 
1887
    if (!FtpSendCmd("PBSZ 0", '2', mp_ftphandle)) return 0;
1888
 
1889
    switch (enc)
1890
    {
1891
        case ftplib::unencrypted:
1892
            mp_ftphandle->tlsdata = 0;
1893
 
1894
            if (!FtpSendCmd("PROT C", '2', mp_ftphandle)) return 0;
1895
 
1896
            break;
1897
 
1898
        case ftplib::secure:
1899
            mp_ftphandle->tlsdata = 1;
1900
 
1901
            if (!FtpSendCmd("PROT P", '2', mp_ftphandle)) return 0;
1902
 
1903
            break;
1904
 
1905
        default:
1906
            return 0;
1907
    }
1908
 
1909
    return 1;
1910
#endif
1911
}
1912
 
1913
int ftplib::NegotiateEncryption()
1914
{
1915
#ifdef NOSSL
1916
    return 0;
1917
#else
1918
    int ret;
1919
 
1920
    if (!FtpSendCmd("AUTH TLS", '2', mp_ftphandle))
1921
        return 0;
1922
 
1923
    mp_ftphandle->sbio = BIO_new_socket(mp_ftphandle->handle, BIO_NOCLOSE);
1924
    SSL_set_bio(mp_ftphandle->ssl, mp_ftphandle->sbio, mp_ftphandle->sbio);
1925
 
1926
    ret = SSL_connect(mp_ftphandle->ssl);
1927
 
1928
    if (ret == 1)
1929
        mp_ftphandle->tlsctrl = 1;
1930
 
1931
    if (mp_ftphandle->certcb != NULL)
1932
    {
1933
        X509 *cert = SSL_get_peer_certificate(mp_ftphandle->ssl);
1934
 
1935
        if (!mp_ftphandle->certcb(mp_ftphandle->cbarg, cert))
1936
            return 0;
1937
    }
1938
 
1939
    if (ret < 1)
1940
        return 0;
1941
 
1942
    return 1;
1943
#endif
1944
}
1945
 
1946
void ftplib::SetCallbackCertFunction(FtpCallbackCert pointer)
1947
{
1948
#ifdef NOSSL
1949
    (void)pointer;
1950
#else
1951
    mp_ftphandle->certcb = pointer;
1952
#endif
1953
}
1954
 
1955
void ftplib::SetCallbackIdleFunction(FtpCallbackIdle pointer)
1956
{
1957
    mp_ftphandle->idlecb = pointer;
1958
}
1959
 
1960
void ftplib::SetCallbackXferFunction(FtpCallbackXfer pointer)
1961
{
1962
    mp_ftphandle->xfercb = pointer;
1963
}
1964
 
1965
void ftplib::SetCallbackLogFunction(FtpCallbackLog pointer)
1966
{
1967
    mp_ftphandle->logcb = pointer;
1968
}
1969
 
120 andreas 1970
void ftplib::SetCallbackErrorFunction(FtpCallbackError pointer)
1971
{
1972
    mp_ftphandle->errorcb = pointer;
1973
}
1974
 
117 andreas 1975
void ftplib::SetCallbackArg(void *arg)
1976
{
1977
    mp_ftphandle->cbarg = arg;
1978
}
1979
 
1980
void ftplib::SetCallbackBytes(off64_t bytes)
1981
{
1982
    mp_ftphandle->cbbytes = bytes;
1983
}
1984
 
1985
void ftplib::SetCallbackIdletime(int time)
1986
{
1987
    mp_ftphandle->idletime.tv_sec = time / 1000;
1988
    mp_ftphandle->idletime.tv_usec = (time % 1000) * 1000;
1989
}
1990
 
1991
void ftplib::SetConnmode(connmode mode)
1992
{
1993
    mp_ftphandle->cmode = mode;
137 andreas 1994
    Log(LOG_DEBUG, string("Mode was set to ") + (mode == pasv ? "PASSIVE" : "PORT"));
117 andreas 1995
}
1996
 
1997
void ftplib::ClearHandle()
1998
{
1999
    mp_ftphandle->dir = FTPLIB_CONTROL;
2000
    mp_ftphandle->ctrl = NULL;
2001
    mp_ftphandle->cmode = ftplib::pasv;
2002
    mp_ftphandle->idlecb = NULL;
2003
    mp_ftphandle->idletime.tv_sec = mp_ftphandle->idletime.tv_usec = 0;
2004
    mp_ftphandle->cbarg = NULL;
2005
    mp_ftphandle->xfered = 0;
2006
    mp_ftphandle->xfered1 = 0;
2007
    mp_ftphandle->cbbytes = 0;
2008
#ifndef NOSSL
2009
    mp_ftphandle->tlsctrl = 0;
2010
    mp_ftphandle->tlsdata = 0;
2011
    mp_ftphandle->certcb = NULL;
2012
#endif
2013
    mp_ftphandle->offset = 0;
2014
    mp_ftphandle->handle = 0;
2015
    mp_ftphandle->logcb = NULL;
2016
    mp_ftphandle->xfercb = NULL;
2017
    mp_ftphandle->correctpasv = false;
2018
}
2019
 
2020
int ftplib::CorrectPasvResponse(unsigned char *v)
2021
{
2022
    struct sockaddr ipholder;
2023
    socklen_t ipholder_size = sizeof(ipholder);
2024
 
2025
    if (getpeername(mp_ftphandle->handle, &ipholder, &ipholder_size) == -1)
2026
    {
120 andreas 2027
        errorHandler("getpeername", errno, __LINE__);
118 andreas 2028
        close(mp_ftphandle->handle);
117 andreas 2029
        return 0;
2030
    }
2031
 
2032
    for (int i = 2; i < 6; i++)
2033
        v[i] = ipholder.sa_data[i];
2034
 
2035
    return 1;
2036
}
2037
 
2038
ftphandle* ftplib::RawOpen(const char *path, accesstype type, transfermode mode)
2039
{
2040
    int ret;
137 andreas 2041
    ftphandle* datahandle{nullptr};
117 andreas 2042
    ret = FtpAccess(path, type, mode, mp_ftphandle, &datahandle);
2043
 
2044
    if (ret)
2045
        return datahandle;
2046
    else
137 andreas 2047
        return nullptr;
117 andreas 2048
}
2049
 
2050
int ftplib::RawClose(ftphandle* handle)
2051
{
2052
    return FtpClose(handle);
2053
}
2054
 
2055
int ftplib::RawWrite(void* buf, int len, ftphandle* handle)
2056
{
2057
    return FtpWrite(buf, len, handle);
2058
}
2059
 
2060
int ftplib::RawRead(void* buf, int max, ftphandle* handle)
2061
{
2062
    return FtpRead(buf, max, handle);
2063
}
120 andreas 2064
 
2065
void ftplib::errorHandler(const char* stub, int err, int line)
2066
{
2067
    char emsg[BUFSIZ];
2068
 
2069
    memset(emsg, 0, BUFSIZ);
2070
 
2071
    if (err != 0)
2072
        snprintf(emsg, sizeof(emsg), "%d: %s: %s", line, stub, strerror(err));
2073
    else if (line > 0)
2074
        snprintf(emsg, sizeof(emsg), "%d: %s", line, stub);
2075
    else
2076
        strncpy(emsg, stub, BUFSIZ-1);
2077
 
2078
    if (mp_ftphandle && mp_ftphandle->errorcb)
2079
        mp_ftphandle->errorcb(emsg, mp_ftphandle->cbarg, err);
137 andreas 2080
    else if (_Logging)
2081
        Log(LOG_ERROR, emsg);
120 andreas 2082
    else
2083
        std::cerr << emsg << std::endl;
2084
}
137 andreas 2085
 
2086
void ftplib::Log(int level, const std::string& msg)
2087
{
2088
    if (!_Logging)
2089
        return;
2090
 
2091
    _Logging(level, msg);
2092
}