Newer
Older
}
{
int i; /* counter for loop */
for (i=0; ftp->dirs[i]; i++) {
/* RFC 1738 says empty components should be respected too, but
that is plain stupid since CWD can't be used with an empty argument */
if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
return result;
}
}
/* Requested time of file or time-depended transfer? */
if((data->set.get_filetime || data->set.timecondition) &&
ftp->file) {
result = ftp_getfiletime(conn, ftp->file);
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
switch( result )
{
case CURLE_FTP_COULDNT_RETR_FILE:
case CURLE_OK:
if(data->set.timecondition) {
if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
switch(data->set.timecondition) {
case TIMECOND_IFMODSINCE:
default:
if(data->info.filetime < data->set.timevalue) {
infof(data, "The requested document is not new enough\n");
ftp->no_transfer = TRUE; /* mark this to not transfer data */
return CURLE_OK;
}
break;
case TIMECOND_IFUNMODSINCE:
if(data->info.filetime > data->set.timevalue) {
infof(data, "The requested document is not old enough\n");
ftp->no_transfer = TRUE; /* mark this to not transfer data */
return CURLE_OK;
}
break;
} /* switch */
else {
infof(data, "Skipping time comparison\n");
}
break;
default:
return result;
} /* switch */
}
/* If we have selected NOBODY and HEADER, it means that we only want file
information. Which in FTP can't be much more than the file size and
date. */
if(data->set.no_body && data->set.include_header && ftp->file) {
/* The SIZE command is _not_ RFC 959 specified, and therefor many servers
may not support it! It is however the only way we have to get a file's
size! */
ssize_t filesize;
Daniel Stenberg
committed
ssize_t nread;
int ftpcode;
ftp->no_transfer = TRUE; /* this means no actual transfer is made */
/* Some servers return different sizes for different modes, and thus we
must set the proper type before we check the size */
result = ftp_transfertype(conn, data->set.ftp_ascii);
if(result)
return result;
/* failing to get size is not a serious error */
result = ftp_getsize(conn, ftp->file, &filesize);
if(CURLE_OK == result) {
sprintf(buf, "Content-Length: %d\r\n", filesize);
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
}
Daniel Stenberg
committed
/* Determine if server can respond to REST command and therefore
whether it can do a range */
FTPSENDF(conn, "REST 0", NULL);
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if ((CURLE_OK == result) && (ftpcode == 350)) {
result = Curl_client_write(data, CLIENTWRITE_BOTH,
(char *)"Accept-ranges: bytes\r\n", 0);
if(result)
return result;
}
/* If we asked for a time of the file and we actually got one as
well, we "emulate" a HTTP-style header in our output. */
#ifdef HAVE_STRFTIME
if(data->set.get_filetime && (data->info.filetime>=0) ) {
struct tm *tm;
#ifdef HAVE_LOCALTIME_R
struct tm buffer;
Daniel Stenberg
committed
tm = (struct tm *)localtime_r((time_t *)&data->info.filetime, &buffer);
#else
Daniel Stenberg
committed
tm = localtime((time_t *)&data->info.filetime);
#endif
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
tm);
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
}
#endif
return CURLE_OK;
}
if(data->set.no_body)
/* doesn't really transfer any data */
ftp->no_transfer = TRUE;
/* Get us a second connection up and connected */
else if(data->set.ftp_use_port) {
/* We have chosen to use the PORT command */
result = ftp_use_port(conn);
if(CURLE_OK == result) {
/* we have the data connection ready */
infof(data, "Ordered connect of the data stream with PORT!\n");
*connected = TRUE; /* mark us "still connected" */
}
}
else {
/* We have chosen (this is default) to use the PASV command */
result = ftp_use_pasv(conn, connected);
if(!result && *connected)
infof(data, "Connected the data stream with PASV!\n");
}
return result;
}
/***********************************************************************
*
* Curl_ftp()
*
* This function is registered as 'curl_do' function. It decodes the path
* parts etc as a wrapper to the actual DO function (ftp_perform).
*
* The input argument is already checked for validity.
*/
CURLcode Curl_ftp(struct connectdata *conn)
CURLcode retcode=CURLE_OK;
Daniel Stenberg
committed
struct SessionHandle *data = conn->data;
char *slash_pos; /* position of the first '/' char in curpos */
char *cur_pos=conn->ppath; /* current position in ppath. point at the begin
of next path component */
int path_part=0;/* current path component */
/* the ftp struct is already inited in ftp_connect() */
conn->size = -1; /* make sure this is unknown at this point */
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
/* fixed : initialize ftp->dirs[xxx] to NULL !
is done in Curl_ftp_connect() */
Daniel Stenberg
committed
/* parse the URL path into separate path components */
while((slash_pos=strchr(cur_pos, '/'))) {
/* 1 or 0 to indicate absolute directory */
bool absolute_dir = (cur_pos - conn->ppath > 0) && (path_part == 0);
/* seek out the next path component */
Daniel Stenberg
committed
if (slash_pos-cur_pos) {
/* we skip empty path components, like "x//y" since the FTP command CWD
requires a parameter and a non-existant parameter a) doesn't work on
many servers and b) has no effect on the others. */
ftp->dirs[path_part] = curl_unescape(cur_pos - absolute_dir,
slash_pos - cur_pos + absolute_dir);
Daniel Stenberg
committed
if (!ftp->dirs[path_part]) { /* run out of memory ... */
failf(data, "no memory");
freedirs(ftp);
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
}
}
else {
Daniel Stenberg
committed
cur_pos = slash_pos + 1; /* jump to the rest of the string */
continue;
}
if(!retcode) {
cur_pos = slash_pos + 1; /* jump to the rest of the string */
Daniel Stenberg
committed
if(++path_part >= (CURL_MAX_FTP_DIRDEPTH-1)) {
/* too deep, we need the last entry to be kept NULL at all
times to signal end of list */
failf(data, "too deep dir hierarchy");
freedirs(ftp);
return CURLE_URL_MALFORMAT;
}
}
ftp->file = cur_pos; /* the rest is the file name */
if(*ftp->file) {
ftp->file = curl_unescape(ftp->file, 0);
if(NULL == ftp->file) {
freedirs(ftp);
failf(data, "no memory");
return CURLE_OUT_OF_MEMORY;
}
ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
pointer */
retcode = ftp_perform(conn, &connected);
Daniel Stenberg
committed
if(CURLE_OK == retcode) {
if(connected)
retcode = Curl_ftp_nextconnect(conn);
if(retcode && (conn->secondarysocket >= 0)) {
/* Failure detected, close the second socket if it was created already */
sclose(conn->secondarysocket);
conn->secondarysocket = -1;
if(ftp->no_transfer)
/* no data to transfer */
retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
else if(!connected)
/* since we didn't connect now, we want do_more to get called */
conn->bits.do_more = TRUE;
}
else
freedirs(ftp);
Daniel Stenberg
committed
/***********************************************************************
*
* Curl_ftpsendf()
*
* Sends the formated string as a ftp command to a ftp server
*
* NOTE: we build the command in a fixed-length buffer, which sets length
* restrictions on the command!
*/
Daniel Stenberg
committed
CURLcode Curl_ftpsendf(struct connectdata *conn,
const char *fmt, ...)
ssize_t bytes_written;
char s[256];
ssize_t write_len;
char *sptr=s;
CURLcode res = CURLE_OK;
va_list ap;
va_start(ap, fmt);
vsnprintf(s, 250, fmt, ap);
va_end(ap);
strcat(s, "\r\n"); /* append a trailing CRLF */
Daniel Stenberg
committed
write_len = strlen(s);
do {
res = Curl_write(conn, conn->firstsocket, sptr, write_len,
&bytes_written);
if(CURLE_OK != res)
break;
if(conn->data->set.verbose)
Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written);
if(bytes_written != write_len) {
write_len -= bytes_written;
sptr += bytes_written;
}
else
break;
} while(1);
return res;
}
/***********************************************************************
*
* Curl_ftp_disconnect()
*
* Disconnect from an FTP server. Cleanup protocol-specific per-connection
* resources
*/
CURLcode Curl_ftp_disconnect(struct connectdata *conn)
{
struct FTP *ftp= conn->proto.ftp;
Daniel Stenberg
committed
/* The FTP session may or may not have been allocated/setup at this point! */
if(ftp) {
if(ftp->entrypath)
free(ftp->entrypath);
if(ftp->cache) {
free(ftp->cache);
ftp->cache = NULL;
}
if(ftp->file) {
free(ftp->file);
ftp->file = NULL; /* zero */
}
freedirs(ftp);
Daniel Stenberg
committed
}
Sterling Hughes
committed
/***********************************************************************
*
* ftp_mkd()
*
* Makes a directory on the FTP server.
*/
CURLcode ftp_mkd(struct connectdata *conn, char *path)
{
CURLcode result=CURLE_OK;
int ftpcode; /* for ftp status */
ssize_t nread;
/* Create a directory on the remote server */
FTPSENDF(conn, "MKD %s", path);
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
switch(ftpcode) {
case 257:
/* success! */
infof( conn->data , "Created remote directory %s\n" , path );
break;
case 550:
failf(conn->data, "Permission denied to make directory %s", path);
result = CURLE_FTP_ACCESS_DENIED;
break;
default:
failf(conn->data, "unrecognized MKD response: %d", ftpcode );
result = CURLE_FTP_ACCESS_DENIED;
break;
}
return result;
}
/***********************************************************************
*
* ftp_cwd()
*
* Send 'CWD' to the remote server to Change Working Directory. It is the ftp
* version of the unix 'cd' command. This function is only called from the
* ftp_cwd_and_mkd() function these days.
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
*
* This function does NOT call failf().
*/
static
CURLcode ftp_cwd(struct connectdata *conn, char *path)
{
ssize_t nread;
int ftpcode;
CURLcode result;
FTPSENDF(conn, "CWD %s", path);
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (!result) {
/* According to RFC959, CWD is supposed to return 250 on success, but
there seem to be non-compliant FTP servers out there that return 200,
so we accept any '2xy' code here. */
if (ftpcode/100 != 2)
result = CURLE_FTP_ACCESS_DENIED;
}
return result;
}
/***********************************************************************
*
* ftp_cwd_and_mkd()
*
* Change to the given directory. If the directory is not present, and we
* have been told to allow it, then create the directory and cd to it.
*/
static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
{
CURLcode result;
result = ftp_cwd(conn, path);
if (result) {
if(conn->data->set.ftp_create_missing_dirs) {
result = ftp_mkd(conn, path);
if (result)
/* ftp_mkd() calls failf() itself */
return result;
result = ftp_cwd(conn, path);
}
if(result)
failf(conn->data, "Couldn't cd to %s", path);
}
return result;
}
#endif /* CURL_DISABLE_FTP */