1
0
mirror of https://github.com/moparisthebest/curl synced 2025-03-01 09:51:46 -05:00

ftp: add 'prefer_ascii' to the transfer state struct

... and make sure the code never updates 'set.prefer_ascii' as it breaks
handle reuse which should use the setting as the user specified it.

Added test 1569 to verify: it first makes an FTP transfer with ';type=A'
and then another without type on the same handle and the second should
then use binary. Previously, curl failed this.

Closes #6578
This commit is contained in:
Daniel Stenberg 2021-02-08 15:56:10 +01:00
parent 0829909ebd
commit 115c9e27f5
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
9 changed files with 146 additions and 21 deletions

View File

@ -1525,7 +1525,7 @@ static CURLcode ftp_state_type(struct Curl_easy *data)
information. Which in FTP can't be much more than the file size and information. Which in FTP can't be much more than the file size and
date. */ date. */
if(data->set.opt_no_body && ftpc->file && if(data->set.opt_no_body && ftpc->file &&
ftp_need_type(conn, data->set.prefer_ascii)) { ftp_need_type(conn, data->state.prefer_ascii)) {
/* The SIZE command is _not_ RFC 959 specified, and therefore many servers /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
may not support it! It is however the only way we have to get a file's may not support it! It is however the only way we have to get a file's
size! */ size! */
@ -1535,7 +1535,7 @@ static CURLcode ftp_state_type(struct Curl_easy *data)
/* Some servers return different sizes for different modes, and thus we /* Some servers return different sizes for different modes, and thus we
must set the proper type before we check the size */ must set the proper type before we check the size */
result = ftp_nb_type(data, conn, data->set.prefer_ascii, FTP_TYPE); result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
if(result) if(result)
return result; return result;
} }
@ -1747,7 +1747,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
result = ftp_state_retr(data, ftpc->known_filesize); result = ftp_state_retr(data, ftpc->known_filesize);
} }
else { else {
if(data->set.ignorecl || data->set.prefer_ascii) { if(data->set.ignorecl || data->state.prefer_ascii) {
/* 'ignorecl' is used to support download of growing files. It /* 'ignorecl' is used to support download of growing files. It
prevents the state machine from requesting the file size from prevents the state machine from requesting the file size from
the server. With an unknown file size the download continues the server. With an unknown file size the download continues
@ -2454,7 +2454,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
*/ */
if((instate != FTP_LIST) && if((instate != FTP_LIST) &&
!data->set.prefer_ascii && !data->state.prefer_ascii &&
(ftp->downloadsize < 1)) { (ftp->downloadsize < 1)) {
/* /*
* It seems directory listings either don't show the size or very * It seems directory listings either don't show the size or very
@ -2493,7 +2493,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
if(size > data->req.maxdownload && data->req.maxdownload > 0) if(size > data->req.maxdownload && data->req.maxdownload > 0)
size = data->req.size = data->req.maxdownload; size = data->req.size = data->req.maxdownload;
else if((instate != FTP_LIST) && (data->set.prefer_ascii)) else if((instate != FTP_LIST) && (data->state.prefer_ascii))
size = -1; /* kludge for servers that understate ASCII mode file size */ size = -1; /* kludge for servers that understate ASCII mode file size */
infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T "\n", infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T "\n",
@ -3626,7 +3626,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
} }
} }
else if(data->set.upload) { else if(data->set.upload) {
result = ftp_nb_type(data, conn, data->set.prefer_ascii, FTP_STOR_TYPE); result = ftp_nb_type(data, conn, data->state.prefer_ascii,
FTP_STOR_TYPE);
if(result) if(result)
return result; return result;
@ -3661,7 +3662,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
/* otherwise just fall through */ /* otherwise just fall through */
} }
else { else {
result = ftp_nb_type(data, conn, data->set.prefer_ascii, result = ftp_nb_type(data, conn, data->state.prefer_ascii,
FTP_RETR_TYPE); FTP_RETR_TYPE);
if(result) if(result)
return result; return result;
@ -4351,7 +4352,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
switch(command) { switch(command) {
case 'A': /* ASCII mode */ case 'A': /* ASCII mode */
data->set.prefer_ascii = TRUE; data->state.prefer_ascii = TRUE;
break; break;
case 'D': /* directory mode */ case 'D': /* directory mode */
@ -4361,7 +4362,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
case 'I': /* binary mode */ case 'I': /* binary mode */
default: default:
/* switch off ASCII */ /* switch off ASCII */
data->set.prefer_ascii = FALSE; data->state.prefer_ascii = FALSE;
break; break;
} }
} }

View File

@ -1702,7 +1702,7 @@ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
if( if(
#ifdef CURL_DO_LINEEND_CONV #ifdef CURL_DO_LINEEND_CONV
(handle->set.prefer_ascii) || (handle->state.prefer_ascii) ||
#endif #endif
(handle->set.crlf)) { (handle->set.crlf)) {
/* \n will become \r\n later on */ /* \n will become \r\n later on */
@ -2209,7 +2209,7 @@ CURLcode Curl_http_target(struct Curl_easy *data,
} }
if(!type) { if(!type) {
result = Curl_dyn_addf(r, ";type=%c", result = Curl_dyn_addf(r, ";type=%c",
data->set.prefer_ascii ? 'a' : 'i'); data->state.prefer_ascii ? 'a' : 'i');
if(result) if(result)
return result; return result;
} }

View File

@ -460,7 +460,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
/* Set ascii mode if -B flag was used */ /* Set ascii mode if -B flag was used */
if(data->set.prefer_ascii) if(data->state.prefer_ascii)
mode = "netascii"; mode = "netascii";
switch(event) { switch(event) {
@ -1420,14 +1420,14 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data,
switch(command) { switch(command) {
case 'A': /* ASCII mode */ case 'A': /* ASCII mode */
case 'N': /* NETASCII mode */ case 'N': /* NETASCII mode */
data->set.prefer_ascii = TRUE; data->state.prefer_ascii = TRUE;
break; break;
case 'O': /* octet mode */ case 'O': /* octet mode */
case 'I': /* binary mode */ case 'I': /* binary mode */
default: default:
/* switch off ASCII */ /* switch off ASCII */
data->set.prefer_ascii = FALSE; data->state.prefer_ascii = FALSE;
break; break;
} }
} }

View File

@ -286,7 +286,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
* <DATA> CRLF * <DATA> CRLF
*/ */
/* On non-ASCII platforms the <DATA> may or may not be /* On non-ASCII platforms the <DATA> may or may not be
translated based on set.prefer_ascii while the protocol translated based on state.prefer_ascii while the protocol
portion must always be translated to the network encoding. portion must always be translated to the network encoding.
To further complicate matters, line end conversion might be To further complicate matters, line end conversion might be
done later on, so we need to prevent CRLFs from becoming done later on, so we need to prevent CRLFs from becoming
@ -301,7 +301,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
if( if(
#ifdef CURL_DO_LINEEND_CONV #ifdef CURL_DO_LINEEND_CONV
(data->set.prefer_ascii) || (data->state.prefer_ascii) ||
#endif #endif
(data->set.crlf)) { (data->set.crlf)) {
/* \n will become \r\n later on */ /* \n will become \r\n later on */
@ -348,7 +348,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
{ {
CURLcode result; CURLcode result;
size_t length; size_t length;
if(data->set.prefer_ascii) if(data->state.prefer_ascii)
/* translate the protocol and data */ /* translate the protocol and data */
length = nread; length = nread;
else else
@ -389,7 +389,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
nread += strlen(endofline_network); /* for the added end of line */ nread += strlen(endofline_network); /* for the added end of line */
} }
#ifdef CURL_DOES_CONVERSIONS #ifdef CURL_DOES_CONVERSIONS
else if((data->set.prefer_ascii) && (!sending_http_headers)) { else if((data->state.prefer_ascii) && (!sending_http_headers)) {
CURLcode result; CURLcode result;
result = Curl_convert_to_network(data, data->req.upload_fromhere, nread); result = Curl_convert_to_network(data, data->req.upload_fromhere, nread);
/* Curl_convert_to_network calls failf if unsuccessful */ /* Curl_convert_to_network calls failf if unsuccessful */
@ -1028,7 +1028,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
if((!sending_http_headers) && ( if((!sending_http_headers) && (
#ifdef CURL_DO_LINEEND_CONV #ifdef CURL_DO_LINEEND_CONV
/* always convert if we're FTPing in ASCII mode */ /* always convert if we're FTPing in ASCII mode */
(data->set.prefer_ascii) || (data->state.prefer_ascii) ||
#endif #endif
(data->set.crlf))) { (data->set.crlf))) {
/* Do we need to allocate a scratch buffer? */ /* Do we need to allocate a scratch buffer? */
@ -1415,6 +1415,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
} }
} }
data->state.prefer_ascii = data->set.prefer_ascii;
data->state.httpreq = data->set.method; data->state.httpreq = data->set.method;
data->change.url = data->set.str[STRING_SET_URL]; data->change.url = data->set.str[STRING_SET_URL];

View File

@ -1467,6 +1467,7 @@ struct UrlState {
BIT(stream_depends_e); /* set or don't set the Exclusive bit */ BIT(stream_depends_e); /* set or don't set the Exclusive bit */
BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ BIT(previouslypending); /* this transfer WAS in the multi->pending queue */
BIT(cookie_engine); BIT(cookie_engine);
BIT(prefer_ascii); /* ASCII rather than binary */
}; };

View File

@ -187,7 +187,7 @@ test1540 \
\ \
test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \ test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 \ test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 \
test1566 test1567 test1568 \ test1566 test1567 test1568 test1569 \
\ \
test1590 test1591 test1592 test1593 test1594 test1595 test1596 \ test1590 test1591 test1592 test1593 test1594 test1595 test1596 \
\ \

73
tests/data/test1569 Normal file
View File

@ -0,0 +1,73 @@
<testcase>
<info>
<keywords>
FTP
PASV
RETR
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
data
to
see
that FTP
works
so does it?
</data>
<servercmd>
REPLY EPSV 500 no such command
</servercmd>
</reply>
# Client-side
<client>
<server>
ftp
</server>
<name>
FTP first type=A then regular URL
</name>
<tool>
lib1569
</tool>
<command>
"ftp://%HOSTIP:%FTPPORT/1569;type=A" ftp://%HOSTIP:%FTPPORT/1569
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol>
USER anonymous
PASS ftp@example.com
PWD
EPSV
PASV
TYPE A
RETR 1569
PASV
TYPE I
SIZE 1569
RETR 1569
QUIT
</protocol>
</verify>
<stdout>
data
to
see
that FTP
works
so does it?
data
to
see
that FTP
works
so does it?
</stdout>
</testcase>

View File

@ -57,7 +57,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1534 lib1535 lib1536 lib1537 lib1538 lib1539 \ lib1534 lib1535 lib1536 lib1537 lib1538 lib1539 \
lib1540 \ lib1540 \
lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \ lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \
lib1558 lib1559 lib1560 lib1564 lib1565 lib1567 lib1568 \ lib1558 lib1559 lib1560 lib1564 lib1565 lib1567 lib1568 lib1569 \
lib1591 lib1592 lib1593 lib1594 lib1596 \ lib1591 lib1592 lib1593 lib1594 lib1596 \
lib1905 lib1906 lib1907 lib1908 lib1910 lib1911 lib1912 lib1913 \ lib1905 lib1906 lib1907 lib1908 lib1910 lib1911 lib1912 lib1913 \
lib1915 lib1916 lib1917 lib1918 lib1933 lib1934 lib1935 lib1936 \ lib1915 lib1916 lib1917 lib1918 lib1933 lib1934 lib1935 lib1936 \
@ -608,6 +608,9 @@ lib1567_CPPFLAGS = $(AM_CPPFLAGS)
lib1568_SOURCES = lib1568.c $(SUPPORTFILES) lib1568_SOURCES = lib1568.c $(SUPPORTFILES)
lib1568_CPPFLAGS = $(AM_CPPFLAGS) lib1568_CPPFLAGS = $(AM_CPPFLAGS)
lib1569_SOURCES = lib1569.c $(SUPPORTFILES)
lib1569_CPPFLAGS = $(AM_CPPFLAGS)
lib1591_SOURCES = lib1591.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1591_SOURCES = lib1591.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1591_LDADD = $(TESTUTIL_LIBS) lib1591_LDADD = $(TESTUTIL_LIBS)
lib1591_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1591 lib1591_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1591

46
tests/libtest/lib1569.c Normal file
View File

@ -0,0 +1,46 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "test.h"
#include "testtrace.h"
#include "memdebug.h"
int test(char *URL)
{
CURLcode ret;
CURL *hnd;
curl_global_init(CURL_GLOBAL_ALL);
hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_URL, URL);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_HEADER, 1L);
ret = curl_easy_perform(hnd);
curl_easy_setopt(hnd, CURLOPT_URL, libtest_arg2);
ret = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
curl_global_cleanup();
return (int)ret;
}