prozilla/libprozilla/src/ftp.c

1282 lines
35 KiB
C

/******************************************************************************
libprozilla - a download accelerator library
Copyright (C) 2001 Kalum Somaratna
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
******************************************************************************/
/* FTP support. */
/* $Id$ */
#include "common.h"
#include "prozilla.h"
#include "connect.h"
#include "misc.h"
#include "url.h"
#include "netrc.h"
#include "debug.h"
#include "ftpparse.h"
#include "ftp.h"
/* #define UNIMPLEMENTED_CMD(a) ((a == 500) || (a == 502) || (a == 504)) */
#define BUFFER_SIZE 2048
/******************************************************************************
Return the numeric response of the FTP server by reading the first three
characters in the buffer.
******************************************************************************/
static int ftp_get_return(const char *ftp_buffer)
{
char code[4];
strncpy(code, ftp_buffer, 3);
code[3] = '\0';
return atoi(code);
}
/******************************************************************************
...
******************************************************************************/
static uerr_t ftp_get_reply(connection_t * connection)
{
int cont = 0;
int code;
response_line *srl;
/* FIXME Make the line variable dynamically allocated. */
/* Allocate the space in the buffer for the request. */
char szBuffer[FTP_BUFFER_SIZE];
char *strtok_saveptr; // = (char *) alloca(FTP_BUFFER_SIZE);
memset(szBuffer, 0, FTP_BUFFER_SIZE);
if (ftp_get_line(connection, szBuffer) != FTPOK)
return FTPERR;
if (!isdigit(*szBuffer))
return FTPERR;
if (*szBuffer == '\0')
return FTPERR;
code = ftp_get_return(szBuffer);
if (szBuffer[3] == '-')
cont = 1;
else
cont = 0;
(void)strtok_r(szBuffer, "\r\n", &strtok_saveptr);
srl = connection->serv_ret_lines = kmalloc(sizeof(response_line));
srl->line = kstrdup(szBuffer);
srl->next = 0;
/* Add the first line to the struct. */
while (cont)
{
if (ftp_get_line(connection, szBuffer) != FTPOK)
return FTPERR;
/* Server closed the connection. */
if (*szBuffer == '\0')
return FTPERR;
// proz_debug("Code %d",code);
if ((ftp_get_return(szBuffer) == code) && (szBuffer[3] == ' '))
cont = 0;
(void)strtok_r(szBuffer, "\r\n", &strtok_saveptr);
// proz_debug(_("Message = %s"), szBuffer);
srl->next = kmalloc(sizeof(response_line));
srl = srl->next;
srl->line = kstrdup(szBuffer);
srl->next = 0;
}
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
int ftp_check_msg(connection_t * connection, int len)
{
int ret;
if ((ret = krecv(connection->ctrl_sock, connection->szBuffer, len,
MSG_PEEK, &connection->ctrl_timeout)) == -1)
{
proz_debug(_("Error checking for FTP data: %s"), strerror(errno));
return ret;
}
return ret;
}
/******************************************************************************
...
******************************************************************************/
int ftp_read_msg(connection_t * connection, int len)
{
int ret;
if ((ret = krecv(connection->ctrl_sock, connection->szBuffer, len, 0,
&connection->ctrl_timeout)) == -1)
{
proz_debug(_("Error receiving FTP data: %s"), strerror(errno));
return ret;
}
return ret;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_send_msg(connection_t * connection, const char *format, ...)
{
longstring command;
va_list args;
va_start(args, format);
#ifdef HAVE_VSNPRINTF
vsnprintf(command, sizeof(command) - 1, format, args);
command[sizeof(command) - 1] = '\0';
#else
vsprintf(command, format, args);
#endif
va_end(args);
proz_debug(_("Sending: %s"), command);
if ((ksend(connection->ctrl_sock, command, strlen(command), 0,
&connection->ctrl_timeout)) == -1)
{
proz_debug(_("Error sending FTP data: %s"), strerror(errno));
return WRITEERR;
}
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_get_line(connection_t * connection, char *line)
{
int iLen, iBuffLen = 0, ret = 0;
char *szptr = line, ch;
connection->szBuffer = &ch;
while ((iBuffLen < BUFFER_SIZE)
&& ((ret = ftp_check_msg(connection, 1)) > 0))
{
/* Now get the full string. */
iLen = ftp_read_msg(connection, 1);
if (iLen != 1)
return FTPERR;
iBuffLen += iLen;
*szptr = ch;
szptr += iLen;
if (ch == '\n')
break; /* We have a line -> return. */
}
/* Check for error returned in ftp_check_msg(). */
if (ret == -1)
return FTPERR;
/* if zero bytes were found that means the server has closed the connection*/
if (ret == 0)
*(szptr) = '\0';
else
*(szptr + 1) = '\0';
proz_debug(_("Received: %s"), line);
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_ascii(connection_t * connection)
{
uerr_t err;
err = ftp_send_msg(connection, "TYPE A\r\n");
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] != '2')
return FTPUNKNOWNTYPE;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_binary(connection_t * connection)
{
uerr_t err;
err = ftp_send_msg(connection, "TYPE I\r\n");
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] != '2')
return FTPUNKNOWNTYPE;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_port(connection_t * connection, const char *command)
{
uerr_t err;
err = ftp_send_msg(connection, command);
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] != '2')
return FTPPORTERR;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_list(connection_t * connection, const char *file)
{
uerr_t err;
err = ftp_send_msg(connection, "LIST %s\r\n", file);
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (ftp_get_return(connection->serv_ret_lines->line) == 550)
{
return FTPNSFOD;
}
/*TODO Fix this up, any other return code with 5xx is a fatal error
*/
if (connection->serv_ret_lines->line[0] == '5')
return FTPERR;
if (connection->serv_ret_lines->line[0] != '1')
return FTPERR;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_retr(connection_t * connection, const char *file)
{
uerr_t err;
err = ftp_send_msg(connection, "RETR %s\r\n", file);
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] == '5')
return FTPNSFOD;
if (connection->serv_ret_lines->line[0] != '1')
return FTPERR;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_pasv(connection_t * connection, unsigned char *addr)
{
uerr_t err;
unsigned char *p;
int i;
err = ftp_send_msg(connection, "PASV\r\n");
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
proz_debug(_("FTP PASV Header = %s"), connection->serv_ret_lines->line);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] != '2')
return FTPNOPASV;
/* Parse it. */
p = (unsigned char *)connection->serv_ret_lines->line;
for (p += 4; *p && !isdigit(*p); p++)
;
if (!*p)
return FTPINVPASV;
for (i = 0; i < 6; i++)
{
addr[i] = 0;
for (; isdigit(*p); p++)
addr[i] = (*p - '0') + 10 * addr[i];
if (*p == ',')
p++;
else if (i < 5)
{
return FTPINVPASV;
}
}
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_rest(connection_t * connection, off_t bytes)
{
uerr_t err;
err = ftp_send_msg(connection, "REST %lld\r\n", bytes);
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] != '3')
return FTPRESTFAIL;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_cwd(connection_t * connection, const char *dir)
{
uerr_t err;
err = ftp_send_msg(connection, "CWD %s\r\n", dir);
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] == '5')
{
/* Is it due to the file being not found? */
if (strstr(connection->serv_ret_lines->line, "o such file")
|| strstr(connection->serv_ret_lines->line, "o Such File")
|| strstr(connection->serv_ret_lines->line, "ot found")
|| strstr(connection->serv_ret_lines->line, "ot Found"))
return FTPNSFOD;
}
if (connection->serv_ret_lines->line[0] != '2')
return FTPCWDFAIL;
return FTPOK;
}
/******************************************************************************
Returns the current working directory in dir.
******************************************************************************/
uerr_t ftp_pwd(connection_t * connection, char *dir)
{
uerr_t err;
char *r, *l;
char szBuffer[FTP_BUFFER_SIZE];
err = ftp_send_msg(connection, "PWD\r\n");
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] == '5')
return FTPPWDERR;
if (connection->serv_ret_lines->line[0] != '2')
return FTPPWDFAIL;
if ((r = strrchr(connection->serv_ret_lines->line, '"')) != NULL)
{
l = strchr(connection->serv_ret_lines->line, '"');
if ((l != NULL) && (l != r))
{
*r = '\0';
++l;
strcpy(dir, l);
*r = '"';
}
}
else
{
if ((r = strchr(connection->serv_ret_lines->line, ' ')) != NULL)
{
*r = '\0';
strcpy(dir, szBuffer);
*r = ' ';
}
}
return FTPOK;
}
/******************************************************************************
Returns the size of the file in size, on error size will be -1.
******************************************************************************/
uerr_t ftp_size(connection_t * connection, const char *file, off_t *size)
{
uerr_t err;
*size = -1;
err = ftp_send_msg(connection, "SIZE %s\r\n", file);
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
/* Now lets figure out what happened. */
if (connection->serv_ret_lines->line[0] == '2')
{
sscanf(connection->serv_ret_lines->line + 3, "%lld", size);
return FTPOK;
}
else if (connection->serv_ret_lines->line[0] == '5') /* An error occured. */
{
if (ftp_get_return(connection->serv_ret_lines->line) == 550)
{
return FTPNSFOD;
}
/* Is it due to the file being not found? */
if (strstr(connection->serv_ret_lines->line, "o such file")
|| strstr(connection->serv_ret_lines->line, "o Such File")
|| strstr(connection->serv_ret_lines->line, "ot found")
|| strstr(connection->serv_ret_lines->line, "ot Found"))
return FTPNSFOD;
}
return FTPSIZEFAIL;
}
/******************************************************************************
Connect to the given FTP server.
******************************************************************************/
uerr_t ftp_connect_to_server(connection_t * connection, const char *name,
int port)
{
uerr_t err;
err = connect_to_server(&(connection->ctrl_sock), name, port,
&connection->conn_timeout);
if (err != NOCONERROR)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
if (connection->serv_ret_lines->line[0] != '2')
return FTPCONREFUSED;
return FTPOK;
}
/******************************************************************************
This function will call bind() to return a bound socket then the FTP server
will be connected with a port request and asked to connect.
******************************************************************************/
uerr_t ftp_get_listen_socket(connection_t * connection, int *listen_sock)
{
/* Get a fixed value. */
char command[MAX_MSG_SIZE];
int sockfd;
socklen_t len;
struct sockaddr_in TempAddr;
char *port, *ipaddr;
struct sockaddr_in serv_addr;
uerr_t err;
if (bind_socket(&sockfd) != BINDOK)
return LISTENERR;
len = sizeof(serv_addr);
if (getsockname(sockfd, (struct sockaddr *)&serv_addr, &len) < 0)
{
perror("getsockname");
close(sockfd);
return CONPORTERR;
}
/* Get hosts info. */
len = sizeof(TempAddr);
if (getsockname(connection->ctrl_sock, (struct sockaddr *)&TempAddr,
&len) < 0)
{
perror("getsockname");
close(sockfd);
return CONPORTERR;
}
ipaddr = (char *)&TempAddr.sin_addr;
port = (char *)&serv_addr.sin_port;
#define UC(b) (((int)b) & 0xff)
sprintf(command, "PORT %d,%d,%d,%d,%d,%d\r\n", UC(ipaddr[0]),
UC(ipaddr[1]), UC(ipaddr[2]), UC(ipaddr[3]), UC(port[0]),
UC(port[1]));
err = ftp_port(connection, command);
if (err != FTPOK)
return err;
*listen_sock = sockfd;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
uerr_t ftp_login(connection_t * connection, const char *username,
const char *passwd)
{
uerr_t err = FTPERR;
int ret_code = 220;
boolean logged_in = FALSE;
while (1)
{
switch (ret_code)
{
case 220:
/* IDEA: Lets add the proxy support here. */
if (!ftp_use_proxy(connection))
{
/* No proxy just direct connection. */
err = ftp_send_msg(connection, "USER %s\r\n", username);
}
else
{
switch (connection->ftp_proxy->type)
{
case USERatSITE:
err = ftp_send_msg(connection, "USER %s@%s:%d\r\n", username,
connection->u.host, connection->u.port);
break;
case USERatPROXYUSERatSITE:
err = ftp_send_msg(connection, "USER %s@%s@%s:%d\r\n", username,
connection->ftp_proxy->username,
connection->u.host, connection->u.port);
break;
case USERatSITE_PROXYUSER:
err = ftp_send_msg(connection, "USER %s:%d@%s %s\r\n", username,
connection->u.host, connection->u.port,
connection->ftp_proxy->username);
break;
case PROXYUSERatSITE:
err = ftp_send_msg(connection, "USER %s@%s:%d\r\n",
connection->ftp_proxy->username,
connection->u.host, connection->u.port);
break;
default:
/* Something else, just send PROXY USER. */
err = ftp_send_msg(connection, "USER %s\r\n",
connection->ftp_proxy->username);
break;
}
}
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
break;
case 230: /* Fallthrough. */
case 231: /* Fallthrough. */
case 202:
logged_in = TRUE;
if (!ftp_use_proxy(connection))
return FTPOK; /* Logged in succesfully. */
switch (connection->ftp_proxy->type)
{
case LOGINthenUSERatSITE:
err = ftp_send_msg(connection, "USER %s@%s:%d\r\n", username,
connection->u.host, connection->u.port);
break;
case OPENSITE:
err =
ftp_send_msg(connection, "OPEN %s:%d\r\n", connection->u.host,
connection->u.port);
break;
case SITESITE:
err =
ftp_send_msg(connection, "SITE %s:%d\r\n", connection->u.host,
connection->u.port);
break;
case PROXYUSERatSITE:
err = ftp_send_msg(connection, "USER %s\r\n", username);
break;
default:
/* TODO What is the default here? */
return FTPOK;
break;
}
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
break;
/* Handle 421 services not available. */
case 421:
return FTPSERVCLOSEDATLOGIN;
break;
/* User name is all right, need password. */
case 331:
if (!ftp_use_proxy(connection))
{
/* No proxy just direct connection. */
err = ftp_send_msg(connection, "PASS %s\r\n", passwd);
}
else
{
switch (connection->ftp_proxy->type)
{
case USERatSITE:
err = ftp_send_msg(connection, "PASS %s\r\n", passwd);
break;
case USERatPROXYUSERatSITE:
err = ftp_send_msg(connection, "PASS %s@%s\r\n", passwd,
connection->ftp_proxy->passwd);
break;
case USERatSITE_PROXYUSER:
err = ftp_send_msg(connection, "PASS %s\r\n", passwd);
break;
case PROXYUSERatSITE:
err = ftp_send_msg(connection, "PASS %s\r\n",
connection->ftp_proxy->passwd);
break;
default:
/* Something else we dont know about. */
err = ftp_send_msg(connection, "PASS %s\r\n",
connection->ftp_proxy->passwd);
break;
}
}
if (err != FTPOK)
return err;
err = ftp_get_reply(connection);
if (err != FTPOK)
return err;
break;
/* 5xx series of commands indicate error. */
case 530:
return FTPLOGREFUSED;
break;
case 501: /* Fallthrough. */
case 503: /* Fallthrough. */
case 550:
return FTPERR;
break;
default:
/* Unknown error code. */
proz_debug(_("Unknown code %d retuned during FTP login"), ret_code);
return FTPERR;
break;
}
ret_code = ftp_get_return(connection->serv_ret_lines->line);
done_with_response(connection);
}
if (err != FTPOK)
return err;
return FTPOK;
}
/******************************************************************************
...
******************************************************************************/
boolean ftp_use_proxy(connection_t * connection)
{
return (connection->ftp_proxy && connection->ftp_proxy->use_proxy &&
connection->ftp_proxy->proxy_url.url) ? TRUE : FALSE;
}
/******************************************************************************
Gets info about the url (connection->u) from the FTP server, and fills in
info like whether the server supports resume, the file size etc.
******************************************************************************/
uerr_t proz_ftp_get_url_info(connection_t * connection)
{
uerr_t err;
char *user, *passwd, *tmp;
netrc_entry *netrc_ent;
boolean passive_mode;
longstring buffer;
boolean size_ok;
struct ftpparse fp;
/* if we have to use a HTTP proxy call the routine which is defined in http.c
and just return.
*/
if (ftp_use_proxy(connection)
&& connection->ftp_proxy->type == HTTPPROXY)
{
err = ftp_get_url_info_from_http_proxy(connection);
return err;
}
init_response(connection);
if (ftp_use_proxy(connection))
{
connection_show_message(connection, _("Connecting to %s"),
connection->ftp_proxy->proxy_url.host);
/* Connect to the proxy server here. */
err = ftp_connect_to_server(connection,
connection->ftp_proxy->proxy_url.host,
connection->ftp_proxy->proxy_url.port);
if (err != FTPOK)
{
connection_show_message(connection,
_("Error while connecting to %s"),
connection->ftp_proxy->proxy_url.host);
return err;
}
connection_show_message(connection, _("Connected to %s"),
connection->ftp_proxy->proxy_url.host);
}
else
{
connection_show_message(connection, _("Connecting to %s"),
connection->u.host);
err = ftp_connect_to_server(connection, connection->u.host,
connection->u.port);
if (err != FTPOK)
{
connection_show_message(connection,
_("Error while connecting to %s"),
connection->u.host);;
return err;
}
connection_show_message(connection, _("Connected to %s"),
connection->u.host);
}
done_with_response(connection);
user = connection->u.user;
passwd = connection->u.passwd;
/* Use .netrc if asked to do so. */
if (connection->use_netrc == TRUE)
{
netrc_ent = search_netrc(libprozrtinfo.netrc_list, connection->u.host);
if (netrc_ent != NULL)
{
user = netrc_ent->account;
passwd = netrc_ent->password;
}
}
user = user ? user : libprozrtinfo.ftp_default_user;
passwd = passwd ? passwd : libprozrtinfo.ftp_default_passwd;
if (strcmp(user, "anonymous") == 0)
connection_show_message(connection,
_("Logging in as user %s with password %s"),
user, passwd);
else
{
int pwd_len = strlen(passwd);
char *tmp_pwd = (char *)kmalloc(pwd_len + 1);
memset(tmp_pwd, 'x', pwd_len);
tmp_pwd[pwd_len] = 0;
connection_show_message(connection,
_("Logging in as user %s with password %s"),
user, tmp_pwd);
kfree(tmp_pwd);
}
init_response(connection);
err = ftp_login(connection, user, passwd);
if (err != FTPOK)
{
close_sock(&connection->ctrl_sock);
return err;
}
done_with_response(connection);
connection_show_message(connection, _("Logged in successfully"));
init_response(connection);
err = ftp_binary(connection);
if (err != FTPOK)
{
close_sock(&connection->ctrl_sock);
return err;
}
done_with_response(connection);
/* Do we need to CWD? */
if (*connection->u.dir)
{
init_response(connection);
err = ftp_cwd(connection, connection->u.dir);
if (err != FTPOK)
{
connection_show_message(connection,
_("CWD failed to change to directory '%s'"),
connection->u.dir);
close_sock(&connection->ctrl_sock);
return err;
}
else
{
done_with_response(connection);
}
}
else
connection_show_message(connection, _("CWD not needed"));
init_response(connection);
err = ftp_rest(connection, 0);
if (err != FTPOK)
{
connection->resume_support = FALSE;
connection_show_message(connection, _("REST failed"));
/* NOTE: removed return err; */
}
else
{
connection->resume_support = TRUE;
connection_show_message(connection, _("REST ok"));
}
done_with_response(connection);
/* Lets see whether the URL really is a file. */
init_response(connection);
err = ftp_cwd(connection, connection->u.file);
if (err == FTPOK)
{
/* So connection->u.file is a directory and not a file. */
connection->file_type = DIRECTORY;
return FTPOK;
}
else
{
/* FIXME: The statement below is strictly not true, it could be a symlink
but for the moment lets leave this as it is, later we will perform a
LIST command and detect whether it is a symlink. */
connection->file_type = REGULAR_FILE;
}
done_with_response(connection);
init_response(connection);
err =
ftp_size(connection, connection->u.file,
&connection->main_file_size);
/* if ((err == FTPOK) || (err == FTPNSFOD) || (err != FTPSIZEFAIL)) */
/* { */
/* close_sock(&connection->ctrl_sock); */
/* return err; */
/* } */
switch (err)
{
case FTPNSFOD:
{
close_sock(&connection->ctrl_sock);
return err;
}
case FTPOK:
size_ok = TRUE;
break;
case FTPSIZEFAIL:
size_ok = FALSE;
break;
default:
size_ok = FALSE;
}
done_with_response(connection);
/* Now we additionaly will get the server to display info with the
list command, initially we only called the LIST command only if the
SIZE failed
*/
err = ftp_setup_data_sock_1(connection, &passive_mode);
if (err != FTPOK)
{
close_sock(&connection->ctrl_sock);
return err;
}
init_response(connection);
err = ftp_ascii(connection);
if (err != FTPOK)
{
close_sock(&connection->ctrl_sock);
return err;
}
done_with_response(connection);
init_response(connection);
err = ftp_list(connection, connection->u.file);
if (err != FTPOK)
{
if (err == FTPNSFOD)
{
//If the remote server returns ftpnsfod which could be due
//to the fact that the server doesnt permit the directory
//contents to be listed we will print a warning and return
//FTPOK as it is not a fatal error.
connection_show_message(connection,
_("FTP LIST failed: File not found or access not permitted."));
close_sock(&connection->ctrl_sock);
return FTPOK;
}
else
{
connection_show_message(connection,
"FTP LIST failed: Server returned %s", connection->serv_ret_lines->line);
close_sock(&connection->ctrl_sock);
return FTPOK;
}
}
done_with_response(connection);
err = ftp_setup_data_sock_2(connection, &passive_mode);
if (err != FTPOK)
{
close_sock(&connection->ctrl_sock);
return err;
}
/* Now read the data to the buffer. */
/* TODO Create a buffer which dynamically resizes itself as we add data. */
if (krecv(connection->data_sock, buffer, sizeof(buffer), 0,
&connection->xfer_timeout) == -1)
{
connection_show_message(connection,
_("Error receiving FTP transfer data: %s"),
strerror(errno));
return FTPERR;
}
proz_debug(_("String received after the LIST command = %s"), buffer);
while ((tmp = strrchr(buffer, '\n')) || (tmp = strrchr(buffer, '\r')))
{
*tmp = 0;
}
;
close_sock(&connection->data_sock);
close_sock(&connection->ctrl_sock);
// size_rt = size_returner(buffer, strlen(buffer));
err = ftp_parse(&fp, buffer, strlen(buffer));
if (err != FTPPARSEOK)
{
connection_show_message(connection,
_
("Unable to parse the line the FTP server returned:please report URL to prozilla-dev@disconnected-by-peer.at "));
}
if (err == FTPPARSEOK)
{
proz_debug("size returned from LIST %ld", fp.filesize);
//SEC size_rt off_t?
if (size_ok == FALSE)
{
proz_debug("SIZE failed, setting file size based on LIST");
connection->main_file_size = fp.filesize;
}
}
return FTPOK;
}
/******************************************************************************
This will be the first step in setting up a data sock, it will try
PASV or PORT.
******************************************************************************/
uerr_t ftp_setup_data_sock_1(connection_t * connection,
boolean * passive_mode)
{
uerr_t err;
/* If enabled lets try PASV. */
if (connection->ftp_use_pasv == TRUE)
{
init_response(connection);
err = ftp_pasv(connection, connection->pasv_addr);
/* If the error is due to the server not supporting PASV then set the
flag and lets try PORT. */
if ((err == FTPNOPASV) || (err == FTPINVPASV))
{
proz_debug(_("Server doesn't seem to support PASV"));
*passive_mode = FALSE;
}
else if (err == FTPOK) /* Server supports PASV. */
{
char dhost[256];
unsigned short dport;
sprintf(dhost, "%d.%d.%d.%d", connection->pasv_addr[0],
connection->pasv_addr[1], connection->pasv_addr[2],
connection->pasv_addr[3]);
dport = (connection->pasv_addr[4] << 8) + connection->pasv_addr[5];
err = connect_to_server(&connection->data_sock, dhost, dport,
&connection->xfer_timeout);
if (err != NOCONERROR)
return err;
/* Everything seems to be ok. */
*passive_mode = TRUE;
}
else
return err;
done_with_response(connection);
}
else
*passive_mode = FALSE; /* Ok... Since PASV is not to be used. */
if (*passive_mode == FALSE)
{
/* Obtain a listen socket. */
err = ftp_get_listen_socket(connection, &connection->listen_sock);
if (err != FTPOK)
return err;
}
return FTPOK;
}
/******************************************************************************
This will be the second step in setting up a data sock, if passive mode is
FALSE, it will call accept_connection().
******************************************************************************/
uerr_t ftp_setup_data_sock_2(connection_t * connection,
boolean * passive_mode)
{
uerr_t err;
if (*passive_mode == FALSE) /* We have to accept the connection. */
{
err =
accept_connection(connection->listen_sock, &connection->data_sock);
if (err != ACCEPTOK)
return err;
}
return FTPOK;
}
/*Loops for connection->attempts */
uerr_t ftp_get_url_info_loop(connection_t * connection)
{
pthread_mutex_lock(&connection->access_mutex);
connection->running = TRUE;
pthread_mutex_unlock(&connection->access_mutex);
assert(connection->attempts >= 0);
do
{
if (connection->attempts > 0 && connection->err != NEWLOCATION)
{
connection_show_message(connection,
_("Retrying attempt %d in %d seconds"),
connection->attempts,
connection->retry_delay.tv_sec);
delay_ms(connection->retry_delay.tv_sec * 1000);
}
/*Push the handler which will cleanup any sockets that are left open */
pthread_cleanup_push(cleanup_socks, (void *)connection);
connection->err = proz_ftp_get_url_info(connection);
/*pop the handler */
pthread_cleanup_pop(0);
connection->attempts++;
switch (connection->err)
{
case FTPOK:
connection_show_message(connection, _("Successfully got info"));
pthread_mutex_lock(&connection->access_mutex);
connection->running = FALSE;
pthread_mutex_unlock(&connection->access_mutex);
return connection->err;
break;
case FTPNSFOD:
connection_show_message(connection, _("File not found!"));
pthread_mutex_lock(&connection->access_mutex);
connection->running = FALSE;
pthread_mutex_unlock(&connection->access_mutex);
return connection->err;
break;
default:
connection_show_message(connection, proz_strerror(connection->err));
break;
}
} while ((connection->attempts < connection->max_attempts)
|| connection->max_attempts == 0);
connection_show_message(connection,
_
("I have tried %d attempt(s) and have failed, aborting"),
connection->attempts);
pthread_mutex_lock(&connection->access_mutex);
connection->running = FALSE;
pthread_mutex_unlock(&connection->access_mutex);
return connection->err;
}