diff --git a/CHANGES b/CHANGES index 1bd2c223b..ef5896f20 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,13 @@ Changelog +Patrick Monnerat (24 Aug 2009) +- Introduced a SYST-based test to properly set-up name format when dealing + with the OS/400 FTP server. + +- Fixed an ftp_readresp() bug preventing detection of failing control socket + and causing FTP client to loop forever. + Daniel Stenberg (24 Aug 2009) - Marc de Bruin pointed out that configure --with-gnutls=PATH didn't work properly and provided a fix. http://curl.haxx.se/bug/view.cgi?id=2843008 diff --git a/lib/ftp.c b/lib/ftp.c index b68cc027c..26cf19795 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -439,13 +439,15 @@ static CURLcode ftp_readresp(curl_socket_t sockfd, #ifdef CURL_DOES_CONVERSIONS if((res == CURLE_OK) && (gotbytes > 0)) { /* convert from the network encoding */ - result = res = Curl_convert_from_network(data, ptr, gotbytes); + res = Curl_convert_from_network(data, ptr, gotbytes); /* Curl_convert_from_network calls failf if unsuccessful */ } #endif /* CURL_DOES_CONVERSIONS */ - if(CURLE_OK != res) + if(CURLE_OK != res) { + result = res; /* Set the outer result variable to this error. */ keepon = FALSE; + } } if(!keepon) @@ -742,6 +744,8 @@ static void state(struct connectdata *conn, "PROT", "CCC", "PWD", + "SYST", + "NAMEFMT", "QUOTE", "RETR_PREQUOTE", "STOR_PREQUOTE", @@ -2733,10 +2737,11 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) case FTP_PWD: if(ftpcode == 257) { - char *dir = malloc(nread+1); - char *store=dir; char *ptr=&data->state.buffer[4]; /* start on the first letter */ + char *dir; + char *store; + dir = malloc(nread + 1); if(!dir) return CURLE_OUT_OF_MEMORY; @@ -2751,7 +2756,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if('\"' == *ptr) { /* it started good */ ptr++; - while(ptr && *ptr) { + for (store = dir; *ptr;) { if('\"' == *ptr) { if('\"' == ptr[1]) { /* "quote-doubling" */ @@ -2769,10 +2774,30 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) store++; ptr++; } + if(ftpc->entrypath) + free(ftpc->entrypath); ftpc->entrypath =dir; /* remember this */ infof(data, "Entry path is '%s'\n", ftpc->entrypath); /* also save it where getinfo can access it: */ data->state.most_recent_ftp_entrypath = ftpc->entrypath; + + /* If the path name does not look like an absolute path (i.e.: it + does not start with a '/'), we probably need some server-dependent + adjustments. For example, this is the case when connecting to + an OS400 FTP server: this server supports two name syntaxes, + the default one being incompatible with standard pathes. In + addition, this server switches automatically to the regular path + syntax when one is encountered in a command: this results in + having an entrypath in the wrong syntax when later used in CWD. + The method used here is to check the server OS: we do it only + if the path name looks strange to minimize overhead on other + systems. */ + + if(!ftpc->server_os && ftpc->entrypath[0] != '/') { + NBFTPSENDF(conn, "SYST", NULL); + state(conn, FTP_SYST); + break; + } } else { /* couldn't get the path */ @@ -2784,6 +2809,57 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) DEBUGF(infof(data, "protocol connect phase DONE\n")); break; + case FTP_SYST: + if(ftpcode == 215) { + char *ptr=&data->state.buffer[4]; /* start on the first letter */ + char *os; + char *store; + + os = malloc(nread + 1); + if(!os) + return CURLE_OUT_OF_MEMORY; + + /* Reply format is like + 215 + */ + while (*ptr == ' ') + ptr++; + for (store = os; *ptr && *ptr != ' ';) + *store++ = *ptr++; + *store = '\0'; /* zero terminate */ + ftpc->server_os = os; + + /* Check for special servers here. */ + + if(strequal(ftpc->server_os, "OS/400")) { + /* Force OS400 name format 1. */ + NBFTPSENDF(conn, "SITE NAMEFMT 1", NULL); + state(conn, FTP_NAMEFMT); + break; + } + else { + /* Nothing special for the target server. */ + } + } + else { + /* Cannot identify server OS. Continue anyway and cross fingers. */ + } + + state(conn, FTP_STOP); /* we are done with the CONNECT phase! */ + DEBUGF(infof(data, "protocol connect phase DONE\n")); + break; + + case FTP_NAMEFMT: + if(ftpcode == 250) { + /* Name format change successful: reload initial path. */ + ftp_state_pwd(conn); + break; + } + + state(conn, FTP_STOP); /* we are done with the CONNECT phase! */ + DEBUGF(infof(data, "protocol connect phase DONE\n")); + break; + case FTP_QUOTE: case FTP_POSTQUOTE: case FTP_RETR_PREQUOTE: @@ -3843,6 +3919,10 @@ static CURLcode ftp_disconnect(struct connectdata *conn) free(ftpc->prevpath); ftpc->prevpath = NULL; } + if(ftpc->server_os) { + free(ftpc->server_os); + ftpc->server_os = NULL; + } return CURLE_OK; } diff --git a/lib/urldata.h b/lib/urldata.h index 1ee6637d2..bc2eff960 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -362,6 +362,8 @@ typedef enum { FTP_PROT, FTP_CCC, FTP_PWD, + FTP_SYST, + FTP_NAMEFMT, FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ FTP_RETR_PREQUOTE, FTP_STOR_PREQUOTE, @@ -458,6 +460,7 @@ struct ftp_conn { struct timeval response; /* set to Curl_tvnow() when a command has been sent off, used to time-out response reading */ ftpstate state; /* always use ftp.c:state() to change state! */ + char * server_os; /* The target server operating system. */ }; /**************************************************************************** diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 72afc5128..8e5b4c727 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -62,7 +62,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \ test635 test636 test637 test558 test559 test1086 test1087 test1088 \ test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \ test1097 test560 test561 test1098 test1099 test562 test563 test1100 \ - test564 test1101 + test564 test1101 test1102 test1103 filecheck: @mkdir test-place; \ diff --git a/tests/data/test1102 b/tests/data/test1102 new file mode 100644 index 000000000..addc7b572 --- /dev/null +++ b/tests/data/test1102 @@ -0,0 +1,51 @@ + + + +FTP +SYST +SITE +OS400 + + +# Server-side + + +blabla + + +REPLY PWD 257 "QGPL" is the current library +REPLY SYST 215 OS/400 runs this server +REPLY SITE 250 Name format set to 1 + + + +# Client-side + + +ftp + + +FTP OS/400 server name format check + + +ftp://%HOSTIP:%FTPPORT/1102 + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS ftp@example.com +PWD +SYST +SITE NAMEFMT 1 +PWD +EPSV +TYPE I +SIZE 1102 +RETR 1102 +QUIT + + + diff --git a/tests/data/test1103 b/tests/data/test1103 new file mode 100644 index 000000000..4d45056d5 --- /dev/null +++ b/tests/data/test1103 @@ -0,0 +1,48 @@ + + + +FTP +SYST +SITE +OS400 + + +# Server-side + + +blabla + + +REPLY PWD 257 "C:/somedir" is the current directory +REPLY SYST 215 unknown-OS runs this server + + + +# Client-side + + +ftp + + +FTP non-OS/400 server + + +ftp://%HOSTIP:%FTPPORT/1103 + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS ftp@example.com +PWD +SYST +EPSV +TYPE I +SIZE 1103 +RETR 1103 +QUIT + + +