prozilla/libprozilla/src/ftp-retr.c

340 lines
9.5 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
******************************************************************************/
/* $Id$ */
#include "common.h"
#include "prozilla.h"
#include "connect.h"
#include "misc.h"
#include "url.h"
#include "netrc.h"
#include "debug.h"
#include "ftp.h"
#include "ftp-retr.h"
/* Will download a portion of/or the full file from the connection->url.
*/
uerr_t proz_ftp_get_file(connection_t * connection)
{
uerr_t err;
char *user, *passwd;
netrc_entry *netrc_ent;
boolean passive_mode;
char buffer[HTTP_BUFFER_SIZE];
/* we want it to terminate immediately */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
assert(connection->localfile != NULL);
assert(connection->file_mode != NULL);
/*clear the socks */
connection->ctrl_sock = 0;
connection->data_sock = 0;
connection->listen_sock = 0;
/* if there is nothing to download then return */
if (connection->status == COMPLETED)
{
gettimeofday(&connection->time_begin, NULL);
return FTPOK;
}
init_response(connection);
pthread_mutex_lock(connection->status_change_mutex);
connection->status = CONNECTING;
/* connection_change_status(connection, CONNECTING); */
pthread_cond_broadcast(&connection->connecting_cond);
pthread_mutex_unlock(connection->status_change_mutex);
/* if we have to use a HTTP proxy call the routine which is defined in http-retr.c and just return.
*/
if (ftp_use_proxy(connection)
&& connection->ftp_proxy->type == HTTPPROXY)
{
err = ftp_get_file_from_http_proxy(connection);
return err;
}
if (ftp_use_proxy(connection))
{
/* Connect to the proxy server here. */
err = ftp_connect_to_server(connection,
connection->ftp_proxy->proxy_url.host,
connection->ftp_proxy->proxy_url.port);
}
else
{
err = ftp_connect_to_server(connection, connection->u.host,
connection->u.port);
}
if (err == FTPCONREFUSED)
{
connection_change_status(connection, CONREJECT);
close_sock(&connection->ctrl_sock);
return err;
}
if (err != FTPOK)
{
connection_change_status(connection, REMOTEFATAL);
return err;
}
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;
proz_debug(_("Logging in as user %s with password %s."), user, passwd);
connection_change_status(connection, LOGGININ);
init_response(connection);
err = ftp_login(connection, user, passwd);
if (err != FTPOK)
{
if (err == FTPLOGREFUSED)
{
connection_change_status(connection, LOGINFAIL);
}
else
{
connection_change_status(connection, REMOTEFATAL);
}
close_sock(&connection->ctrl_sock);
return err;
}
done_with_response(connection);
init_response(connection);
err = ftp_binary(connection);
if (err != FTPOK)
{
connection_change_status(connection, REMOTEFATAL);
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_change_status(connection, REMOTEFATAL);
proz_debug(_("CWD failed to change to directory '%s'."),
connection->u.dir);
close_sock(&connection->ctrl_sock);
return err;
}
else
{
proz_debug(_("CWD ok."));
done_with_response(connection);
}
}
else
proz_debug(_("CWD not needed."));
err = ftp_setup_data_sock_1(connection, &passive_mode);
if (err != FTPOK)
{
connection_change_status(connection, REMOTEFATAL);
close_sock(&connection->ctrl_sock);
return err;
}
/* do we need to REST */
if (connection->remote_startpos > 0
&& connection->resume_support == TRUE)
{
err = ftp_rest(connection, connection->remote_startpos);
}
if (err != FTPOK)
{
if (err == FTPRESTFAIL)
proz_debug
(_
("I have a bug in my code!!, check remote_starpos and resume_support values"));
connection_change_status(connection, REMOTEFATAL);
close_sock(&connection->ctrl_sock);
return err;
}
err = ftp_retr(connection, connection->u.file);
if (err != FTPOK)
{
proz_debug(_("RETR failed"));
close_sock(&connection->ctrl_sock);
connection_change_status(connection, REMOTEFATAL);
return err;
}
err = ftp_setup_data_sock_2(connection, &passive_mode);
if (err != FTPOK)
{
return err;
}
/* which routine to call */
if (connection->main_file_size == -1)
err =
connection_retr_fsize_not_known(connection, buffer,
sizeof(buffer));
else
err = connection_retr_fsize_known(connection, buffer, sizeof(buffer));
close_sock(&connection->data_sock);
close_sock(&connection->ctrl_sock);
if (err == FILEGETOK)
return FTPOK;
else
return err;
}
/* A genuine loop ;) It willed be called by the main thread, and
this will handle all possible errors itself, retrying until the number
of maximum tries for the connection is realised or a error occurs which
needs to be passed upwards for handling, such as LOGINFAIL
*/
uerr_t ftp_loop(connection_t * connection)
{
boolean retrying_from_loop = FALSE;
assert(connection->max_attempts >= 0);
assert(connection->attempts >= 0);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
do
{
if (connection->attempts > 0)
{
if (retrying_from_loop == TRUE)
{
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);
}
if (connection->resume_support == TRUE)
{
if (connection_load_resume_info(connection) == -1)
{
connection_show_message(connection,
_
("Error while attemting to process download file "));
}
}
else
{
/*If we cant resume then reset the connections bytesreceived to 0 */
connection->remote_bytes_received = 0;
}
}
/*Push the handler which will cleanup any sockets that are left open */
pthread_cleanup_push(cleanup_socks, (void *)connection);
connection->err = proz_ftp_get_file(connection);
/*pop the handler */
pthread_cleanup_pop(0);
connection->attempts++;
/*Should the error be handled at this level ? */
if (!ftp_loop_handle_error(connection->err))
{
return connection->err; /*If not return and the main thread will handle it */
}
switch (connection->err)
{
case FTPOK:
connection_show_message(connection, _("Successfully got download"));
return connection->err;
break;
default:
connection_show_message(connection,
_("Error occured in connection..."));
break;
}
retrying_from_loop = TRUE;
} 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);
return connection->err;
}
/*Return true if it is a error which can be handled within the ftp_loop,
or false if it should be passed upwards so that the main download thread can
restart it when necessary after processing other threads status too */
boolean ftp_loop_handle_error(uerr_t err)
{
proz_debug("Error encountered in ftp_loop is %d", err);
if (err == FTPNSFOD || err == FTPLOGREFUSED || err == FTPCONREFUSED
|| err == FWRITEERR || err == FOPENERR || err == FTPCWDFAIL
|| err == FTPRESTFAIL)
return FALSE;
else
return TRUE;
}