Subversion Repositories tpanel

Rev

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

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