140
135
static CURLcode ftp_do(struct connectdata *conn, bool *done);
141
136
static CURLcode ftp_done(struct connectdata *conn,
142
CURLcode, bool premature);
137
CURLcode, bool premature);
143
138
static CURLcode ftp_connect(struct connectdata *conn, bool *done);
144
139
static CURLcode ftp_disconnect(struct connectdata *conn);
145
140
static CURLcode ftp_nextconnect(struct connectdata *conn);
146
141
static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
147
142
static int ftp_getsock(struct connectdata *conn,
148
curl_socket_t *socks,
143
curl_socket_t *socks,
150
145
static CURLcode ftp_doing(struct connectdata *conn,
152
147
static CURLcode ftp_setup_connection(struct connectdata * conn);
149
static CURLcode init_wc_data(struct connectdata *conn);
150
static CURLcode wc_statemach(struct connectdata *conn);
152
static void wc_data_dtor(void *ptr);
154
static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
155
curl_off_t filesize);
154
157
/* easy-to-use macro: */
155
158
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
157
#define NBFTPSENDF(x,y,z) if((result = Curl_nbftpsendf(x,y,z)) != CURLE_OK) \
160
#define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
309
312
struct SessionHandle *data = conn->data;
310
313
curl_socket_t sock = conn->sock[SECONDARYSOCKET];
311
long timeout_ms = Curl_timeleft(conn, NULL, TRUE);
314
/* if a timeout was already reached, bail out */
315
failf(data, "Timed out before server could connect to us");
316
return CURLE_OPERATION_TIMEDOUT;
319
switch (Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)timeout_ms)) {
322
failf(data, "Error while waiting for server connect");
323
return CURLE_FTP_PORT_FAILED;
324
case 0: /* timeout */
326
failf(data, "Timeout while waiting for server connect");
327
return CURLE_FTP_PORT_FAILED;
329
/* we have received data here */
331
curl_socket_t s = CURL_SOCKET_BAD;
316
curl_socket_t s = CURL_SOCKET_BAD;
332
317
#ifdef ENABLE_IPV6
333
struct Curl_sockaddr_storage add;
318
struct Curl_sockaddr_storage add;
335
struct sockaddr_in add;
320
struct sockaddr_in add;
337
curl_socklen_t size = (curl_socklen_t) sizeof(add);
322
curl_socklen_t size = (curl_socklen_t) sizeof(add);
325
timeout_ms = Curl_timeleft(conn, NULL, TRUE);
327
if(timeout_ms <= 0) {
328
/* if a timeout was already reached, bail out */
329
failf(data, "Timeout while waiting for server connect");
330
return CURLE_OPERATION_TIMEDOUT;
333
interval_ms = 1000; /* use 1 second timeout intervals */
334
if(timeout_ms < interval_ms)
335
interval_ms = timeout_ms;
337
switch (Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)interval_ms)) {
340
failf(data, "Error while waiting for server connect");
341
return CURLE_FTP_PORT_FAILED;
342
case 0: /* timeout */
345
/* we have received data here */
339
346
if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
340
347
size = sizeof(add);
353
359
conn->sock[SECONDARYSOCKET] = s;
354
360
curlx_nonblock(s, TRUE); /* enable non-blocking */
362
/* initialize stuff to prepare for reading a fresh new response */
363
static void ftp_respinit(struct connectdata *conn)
365
struct ftp_conn *ftpc = &conn->proto.ftpc;
366
ftpc->nread_resp = 0;
367
ftpc->linestart_resp = conn->data->state.buffer;
368
ftpc->pending_resp = TRUE;
364
/* never reaches this point */
371
367
/* macro to check for a three-digit ftp status code at the start of the
373
#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
369
#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
376
372
/* macro to check for the last line in an FTP server response */
377
373
#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
375
static int ftp_endofresp(struct pingpong *pp,
378
char *line = pp->linestart_resp;
379
size_t len = pp->nread_resp;
381
if((len > 3) && LASTLINE(line)) {
379
388
static CURLcode ftp_readresp(curl_socket_t sockfd,
380
struct connectdata *conn,
381
390
int *ftpcode, /* return the ftp-code if done */
382
391
size_t *size) /* size of the response */
384
ssize_t perline; /* count bytes per line */
393
struct connectdata *conn = pp->conn;
394
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
388
395
struct SessionHandle *data = conn->data;
389
396
char * const buf = data->state.buffer;
390
398
CURLcode result = CURLE_OK;
391
struct ftp_conn *ftpc = &conn->proto.ftpc;
394
*ftpcode = 0; /* 0 for errors or not done */
397
ptr=buf + ftpc->nread_resp;
399
/* number of bytes in the current line, so far */
400
perline = (ssize_t)(ptr-ftpc->linestart_resp);
404
while((ftpc->nread_resp<BUFSIZE) && (keepon && !result)) {
407
/* we had data in the "cache", copy that instead of doing an actual
410
* ftp->cache_size is cast to int here. This should be safe,
411
* because it would have been populated with something of size
412
* int to begin with, even though its datatype may be larger
415
DEBUGASSERT((ptr+ftpc->cache_size) <= (buf+BUFSIZE+1));
416
memcpy(ptr, ftpc->cache, (int)ftpc->cache_size);
417
gotbytes = (int)ftpc->cache_size;
418
free(ftpc->cache); /* free the cache */
419
ftpc->cache = NULL; /* clear the pointer */
420
ftpc->cache_size = 0; /* zero the size just in case */
424
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
425
enum protection_level prot = conn->data_prot;
429
DEBUGASSERT((ptr+BUFSIZE-ftpc->nread_resp) <= (buf+BUFSIZE+1));
430
res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftpc->nread_resp,
432
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
433
conn->data_prot = prot;
437
return CURLE_OK; /* return */
439
#ifdef CURL_DOES_CONVERSIONS
440
if((res == CURLE_OK) && (gotbytes > 0)) {
441
/* convert from the network encoding */
442
res = Curl_convert_from_network(data, ptr, gotbytes);
443
/* Curl_convert_from_network calls failf if unsuccessful */
445
#endif /* CURL_DOES_CONVERSIONS */
447
if(CURLE_OK != res) {
448
result = (CURLcode)res; /* Set outer result variable to this error. */
455
else if(gotbytes <= 0) {
457
result = CURLE_RECV_ERROR;
458
failf(data, "FTP response reading failed");
461
/* we got a whole chunk of data, which can be anything from one
462
* byte to a set of lines and possible just a piece of the last
465
ssize_t clipamount = 0;
466
bool restart = FALSE;
468
data->req.headerbytecount += gotbytes;
470
ftpc->nread_resp += gotbytes;
471
for(i = 0; i < gotbytes; ptr++, i++) {
474
/* a newline is CRLF in ftp-talk, so the CR is ignored as
475
the line isn't really terminated until the LF comes */
477
/* output debug output if that is requested */
478
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
479
if(!conn->sec_complete)
481
if(data->set.verbose)
482
Curl_debug(data, CURLINFO_HEADER_IN,
483
ftpc->linestart_resp, (size_t)perline, conn);
486
* We pass all response-lines to the callback function registered
487
* for "headers". The response lines can be seen as a kind of
490
result = Curl_client_write(conn, CLIENTWRITE_HEADER,
491
ftpc->linestart_resp, perline);
495
if(perline>3 && LASTLINE(ftpc->linestart_resp)) {
496
/* This is the end of the last line, copy the last line to the
497
start of the buffer and zero terminate, for old times sake (and
501
for(meow=ftpc->linestart_resp, n=0; meow<ptr; meow++, n++)
503
*meow=0; /* zero terminate */
505
ftpc->linestart_resp = ptr+1; /* advance pointer */
506
i++; /* skip this before getting out */
508
*size = ftpc->nread_resp; /* size of the response */
509
ftpc->nread_resp = 0; /* restart */
512
perline=0; /* line starts over here */
513
ftpc->linestart_resp = ptr+1;
517
if(!keepon && (i != gotbytes)) {
518
/* We found the end of the response lines, but we didn't parse the
519
full chunk of data we have read from the server. We therefore need
520
to store the rest of the data to be checked on the next invoke as
521
it may actually contain another end of response already! */
522
clipamount = gotbytes - i;
527
if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
528
/* We got an excessive line without newlines and we need to deal
529
with it. First, check if it seems to start with a valid status
530
code and then we keep just that in the line cache. Then throw
532
infof(data, "Excessive FTP response line length received, %zd bytes."
533
" Stripping\n", gotbytes);
535
if(STATUSCODE(ftpc->linestart_resp))
536
/* we copy 4 bytes since after the three-digit number there is a
537
dash or a space and it is significant */
540
else if(ftpc->nread_resp > BUFSIZE/2) {
541
/* We got a large chunk of data and there's potentially still trailing
542
data to take care of, so we put any such part in the "cache", clear
543
the buffer to make space and restart. */
544
clipamount = perline;
548
else if(i == gotbytes)
552
ftpc->cache_size = clipamount;
553
ftpc->cache = malloc((int)ftpc->cache_size);
555
memcpy(ftpc->cache, ftpc->linestart_resp, (int)ftpc->cache_size);
557
return CURLE_OUT_OF_MEMORY;
560
/* now reset a few variables to start over nicely from the start of
562
ftpc->nread_resp = 0; /* start over from scratch in the buffer */
563
ptr = ftpc->linestart_resp = buf;
567
} /* there was data */
569
} /* while there's buffer left and loop is requested */
401
result = Curl_pp_readresp(sockfd, pp, &code, size);
574
403
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
575
404
/* handle the security-oriented responses 6xx ***/
641
469
while(!*ftpcode && !result) {
642
470
/* check and reset timeout value every lap */
643
if(data->set.ftp_response_timeout )
644
/* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
645
remaining time. Also, use "now" as opposed to "conn->now"
646
because ftp_response_timeout is only supposed to govern
647
the response for any given ftp response, not for the time
648
from connect to the given ftp response. */
649
timeout = data->set.ftp_response_timeout - /* timeout time */
650
Curl_tvdiff(Curl_tvnow(), now); /* spent time */
651
else if(data->set.timeout)
652
/* if timeout is requested, find out how much remaining time we have */
653
timeout = data->set.timeout - /* timeout time */
654
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
656
/* Even without a requested timeout, we only wait response_time
657
seconds for the full response to arrive before we bail out */
658
timeout = ftpc->response_time -
659
Curl_tvdiff(Curl_tvnow(), now); /* spent time */
471
timeout = Curl_pp_state_timeout(pp);
661
473
if(timeout <=0 ) {
662
474
failf(data, "FTP response timeout");
663
475
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
666
interval_ms = 1 * 1000; /* use 1 second timeout intervals */
478
interval_ms = 1000; /* use 1 second timeout intervals */
667
479
if(timeout < interval_ms)
668
480
interval_ms = timeout;
2972
/* Returns timeout in ms. 0 or negative number means the timeout has already
2974
static long ftp_state_timeout(struct connectdata *conn)
2976
struct SessionHandle *data=conn->data;
2977
struct ftp_conn *ftpc = &conn->proto.ftpc;
2978
long timeout_ms=360000; /* in milliseconds */
2980
if(data->set.ftp_response_timeout )
2981
/* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
2982
time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
2983
to govern the response for any given ftp response, not for the time
2984
from connect to the given ftp response. */
2985
timeout_ms = data->set.ftp_response_timeout - /* timeout time */
2986
Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
2987
else if(data->set.timeout)
2988
/* if timeout is requested, find out how much remaining time we have */
2989
timeout_ms = data->set.timeout - /* timeout time */
2990
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
2992
/* Without a requested timeout, we only wait 'response_time' seconds for
2993
the full response to arrive before we bail out */
2994
timeout_ms = ftpc->response_time -
2995
Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
3001
2805
/* called repeatedly until done from multi.c */
3002
2806
static CURLcode ftp_multi_statemach(struct connectdata *conn,
3005
curl_socket_t sock = conn->sock[FIRSTSOCKET];
3007
struct SessionHandle *data=conn->data;
3008
2809
struct ftp_conn *ftpc = &conn->proto.ftpc;
3009
CURLcode result = CURLE_OK;
3010
long timeout_ms = ftp_state_timeout(conn);
3012
*done = FALSE; /* default to not done yet */
3014
if(timeout_ms <= 0) {
3015
failf(data, "FTP response timeout");
3016
return CURLE_OPERATION_TIMEDOUT;
3019
rc = Curl_socket_ready(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */
3020
ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */
3024
failf(data, "select/poll error");
3025
return CURLE_OUT_OF_MEMORY;
3028
result = ftp_statemach_act(conn);
3030
/* if rc == 0, then select() timed out */
2810
CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
3032
2812
/* Check for the state outside of the Curl_socket_ready() return code checks
3033
2813
since at times we are in fact already in this state when this function
3473
static void wc_data_dtor(void *ptr)
3475
struct ftp_wc_tmpdata *tmp = ptr;
3477
Curl_ftp_parselist_data_free(&tmp->parser);
3481
static CURLcode init_wc_data(struct connectdata *conn)
3484
char *path = conn->data->state.path;
3485
struct WildcardData *wildcard = &(conn->data->wildcard);
3486
CURLcode ret = CURLE_OK;
3487
struct ftp_wc_tmpdata *ftp_tmp;
3489
last_slash = strrchr(conn->data->state.path, '/');
3492
if(last_slash[0] == '\0') {
3493
wildcard->state = CURLWC_CLEAN;
3494
ret = ftp_parse_url_path(conn);
3498
wildcard->pattern = strdup(last_slash);
3499
if (!wildcard->pattern)
3500
return CURLE_OUT_OF_MEMORY;
3501
last_slash[0] = '\0'; /* cut file from path */
3504
else { /* there is only 'wildcard pattern' or nothing */
3506
wildcard->pattern = strdup(path);
3507
if (!wildcard->pattern)
3508
return CURLE_OUT_OF_MEMORY;
3511
else { /* only list */
3512
conn->data->set.wildcardmatch = FALSE;
3513
ret = ftp_parse_url_path(conn);
3518
/* program continues only if URL is not ending with slash, allocate needed
3519
resources for wildcard transfer */
3521
/* allocate ftp protocol specific temporary wildcard data */
3522
ftp_tmp = malloc(sizeof(struct ftp_wc_tmpdata));
3524
return CURLE_OUT_OF_MEMORY;
3527
/* INITIALIZE parselist structure */
3528
ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
3529
if(!ftp_tmp->parser)
3530
return CURLE_OUT_OF_MEMORY;
3532
wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
3533
wildcard->tmp_dtor = wc_data_dtor;
3535
/* wildcard does not support NOCWD option (assert it?) */
3536
if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3537
conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3539
/* try to parse ftp url */
3540
ret = ftp_parse_url_path(conn);
3545
/* backup old write_function */
3546
ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
3547
/* parsing write function (callback included directly from ftplistparser.c) */
3548
conn->data->set.fwrite_func = Curl_ftp_parselist;
3549
/* backup old file descriptor */
3550
ftp_tmp->backup.file_descriptor = conn->data->set.out;
3551
/* let the writefunc callback know what curl pointer is working with */
3552
conn->data->set.out = conn;
3554
wildcard->path = strdup(conn->data->state.path);
3555
if(!wildcard->path) {
3556
return CURLE_OUT_OF_MEMORY;
3559
infof(conn->data, "Wildcard - Parsing started\n");
3563
static CURLcode wc_statemach(struct connectdata *conn)
3565
struct ftp_conn *ftpc = &conn->proto.ftpc;
3566
struct WildcardData *wildcard = &(conn->data->wildcard);
3567
struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3569
CURLcode ret = CURLE_OK;
3570
long userresponse = 0;
3571
switch (wildcard->state) {
3573
ret = init_wc_data(conn);
3574
if(wildcard->state == CURLWC_CLEAN)
3578
wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
3581
case CURLWC_MATCHING:
3582
/* In this state is LIST response successfully parsed, so lets restore
3583
previous WRITEFUNCTION callback and WRITEDATA pointer */
3584
ftp_tmp = wildcard->tmp;
3585
conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
3586
conn->data->set.out = ftp_tmp->backup.file_descriptor;
3587
wildcard->state = CURLWC_DOWNLOADING;
3589
if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
3590
/* error found in LIST parsing */
3591
wildcard->state = CURLWC_CLEAN;
3592
return wc_statemach(conn);
3594
else if(wildcard->filelist->size == 0) {
3595
/* no corresponding file */
3596
wildcard->state = CURLWC_CLEAN;
3597
return CURLE_REMOTE_FILE_NOT_FOUND;
3599
ret = wc_statemach(conn);
3602
case CURLWC_DOWNLOADING: {
3603
/* filelist has at least one file, lets get first one */
3604
struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
3605
tmp_path = malloc(strlen(conn->data->state.path) +
3606
strlen(finfo->filename) + 1);
3608
return CURLE_OUT_OF_MEMORY;
3612
/* make full path to matched file */
3613
strcat(tmp_path, wildcard->path);
3614
strcat(tmp_path, finfo->filename);
3615
/* switch default "state.pathbuffer" and tmp_path, good to see
3616
ftp_parse_url_path function to understand this trick */
3617
if(conn->data->state.pathbuffer)
3618
free(conn->data->state.pathbuffer);
3619
conn->data->state.pathbuffer = tmp_path;
3620
conn->data->state.path = tmp_path;
3622
infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3623
if(conn->data->set.chunk_bgn) {
3624
userresponse = conn->data->set.chunk_bgn(
3625
finfo, wildcard->customptr, (int)wildcard->filelist->size);
3626
switch(userresponse) {
3627
case CURL_CHUNK_BGN_FUNC_SKIP:
3628
infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3630
wildcard->state = CURLWC_SKIP;
3631
return wc_statemach(conn);
3632
case CURL_CHUNK_BGN_FUNC_FAIL:
3633
return CURLE_CHUNK_FAILED;
3637
if(finfo->filetype != CURLFILETYPE_FILE) {
3638
wildcard->state = CURLWC_SKIP;
3639
return wc_statemach(conn);
3642
if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3643
ftpc->known_filesize = finfo->size;
3645
ret = ftp_parse_url_path(conn);
3650
/* we don't need the Curl_fileinfo of first file anymore */
3651
Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3653
if(wildcard->filelist->size == 0) { /* remains only one file to down. */
3654
wildcard->state = CURLWC_CLEAN;
3655
/* after that will be ftp_do called once again and no transfer
3656
will be done because of CURLWC_CLEAN state */
3662
if(conn->data->set.chunk_end)
3663
conn->data->set.chunk_end(conn->data->wildcard.customptr);
3664
Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3665
wildcard->state = (wildcard->filelist->size == 0) ?
3666
CURLWC_CLEAN : CURLWC_DOWNLOADING;
3667
ret = wc_statemach(conn);
3673
ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
3675
wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
3687
3686
/***********************************************************************
3711
3710
return retcode;
3713
retcode = ftp_parse_url_path(conn);
3712
if(conn->data->set.wildcardmatch) {
3713
retcode = wc_statemach(conn);
3714
if(conn->data->wildcard.state == CURLWC_SKIP ||
3715
conn->data->wildcard.state == CURLWC_DONE) {
3716
/* do not call ftp_regular_transfer */
3719
if(retcode) /* error, loop or skipping the file */
3722
else { /* no wildcard FSM needed */
3723
retcode = ftp_parse_url_path(conn);
3717
3728
retcode = ftp_regular_transfer(conn, done);
3719
3730
return retcode;
3722
/***********************************************************************
3724
* Curl_(nb)ftpsendf()
3726
* Sends the formated string as a ftp command to a ftp server
3728
* NOTE: we build the command in a fixed-length buffer, which sets length
3729
* restrictions on the command!
3731
* The "nb" version is made to Never Block.
3733
CURLcode Curl_nbftpsendf(struct connectdata *conn,
3734
CURLcode Curl_ftpsendf(struct connectdata *conn,
3734
3735
const char *fmt, ...)
3736
3737
ssize_t bytes_written;
3737
/* may still not be big enough for some krb5 tokens */
3738
3738
#define SBUF_SIZE 1024
3739
3739
char s[SBUF_SIZE];
3740
3740
size_t write_len;
3742
3742
CURLcode res = CURLE_OK;
3743
struct SessionHandle *data = conn->data;
3744
struct ftp_conn *ftpc = &conn->proto.ftpc;
3745
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3746
enum protection_level data_sec = conn->data_prot;
3751
vsnprintf(s, SBUF_SIZE-3, fmt, ap);
3754
strcat(s, "\r\n"); /* append a trailing CRLF */
3757
write_len = strlen(s);
3761
#ifdef CURL_DOES_CONVERSIONS
3762
res = Curl_convert_to_network(data, s, write_len);
3763
/* Curl_convert_to_network calls failf if unsuccessful */
3764
if(res != CURLE_OK) {
3767
#endif /* CURL_DOES_CONVERSIONS */
3769
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3770
conn->data_prot = prot_cmd;
3772
res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3774
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3775
conn->data_prot = data_sec;
3781
if(conn->data->set.verbose)
3782
Curl_debug(conn->data, CURLINFO_HEADER_OUT,
3783
sptr, (size_t)bytes_written, conn);
3785
if(bytes_written != (ssize_t)write_len) {
3786
/* the whole chunk was not sent, store the rest of the data */
3787
write_len -= bytes_written;
3788
sptr += bytes_written;
3789
ftpc->sendthis = malloc(write_len);
3790
if(ftpc->sendthis) {
3791
memcpy(ftpc->sendthis, sptr, write_len);
3792
ftpc->sendsize = ftpc->sendleft = write_len;
3795
failf(data, "out of memory");
3796
res = CURLE_OUT_OF_MEMORY;
3800
ftpc->response = Curl_tvnow();
3805
CURLcode Curl_ftpsendf(struct connectdata *conn,
3806
const char *fmt, ...)
3808
ssize_t bytes_written;
3812
CURLcode res = CURLE_OK;
3813
3743
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3814
3744
enum protection_level data_sec = conn->data_prot;