prozilla/libprozilla/src/download.c

2164 lines
78 KiB
C
Raw Permalink Normal View History

2010-08-31 03:50:41 +02:00
/******************************************************************************
2010-09-01 10:52:02 +02:00
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
2010-08-31 03:50:41 +02:00
******************************************************************************/
/* Download routines. */
2010-09-01 10:52:02 +02:00
/* $Id$ */
2010-08-31 03:50:41 +02:00
#include "common.h"
#include "download.h"
#include "ftp-retr.h"
#include "http-retr.h"
#include "logfile.h"
#include "debug.h"
#include "ftpsearch.h"
/******************************************************************************
2010-09-01 10:52:02 +02:00
Initialize the download.
2010-08-31 03:50:41 +02:00
******************************************************************************/
download_t *proz_download_init(urlinfo * u)
{
/* pthread_mutexattr_t attr; */
download_t *download = kmalloc(sizeof(download_t));
/* attr.__mutexkind = PTHREAD_MUTEX_RECURSIVE_NP; */
memset(download, 0, sizeof(download_t));
/*FIXME pthread_mutex_init(&download->status_change_mutex, &attr); */
pthread_mutex_init(&download->status_change_mutex, NULL);
pthread_mutex_init(&download->access_mutex, NULL);
memcpy(&download->u, u, sizeof(urlinfo));
download->dl_dir = kmalloc(PATH_MAX);
download->output_dir = kmalloc(PATH_MAX);
download->log_dir = kmalloc(PATH_MAX);
strcpy(download->dl_dir, libprozrtinfo.dl_dir);
strcpy(download->output_dir, libprozrtinfo.output_dir);
strcpy(download->log_dir, libprozrtinfo.log_dir);
download->resume_mode = FALSE;
download->max_simul_connections = 0;
download->max_allowed_bps = libprozrtinfo.max_bps_per_dl;
2010-09-01 10:52:02 +02:00
download->file_build_msg = (char *)kmalloc(MAX_MSG_SIZE + 1);
2010-08-31 03:50:41 +02:00
download->using_ftpsearch = FALSE;
return download;
}
/******************************************************************************
2010-09-01 10:52:02 +02:00
This will setup a download based on the connection info, and will attempt
to setup req_connections, but it might not be possible if we dont know the
file size, returns the number of connections allocated.
2010-08-31 03:50:41 +02:00
******************************************************************************/
int proz_download_setup_connections_no_ftpsearch(download_t * download,
2010-09-01 10:52:02 +02:00
connection_t * connection,
int req_connections)
2010-08-31 03:50:41 +02:00
{
int num_connections, i;
off_t bytes_per_connection;
off_t bytes_left;
FILE *fp;
char *out_file;
struct stat stat_buf;
download->main_file_size = connection->main_file_size;
download->resume_support = connection->resume_support;
if (download->main_file_size == -1)
{
num_connections = 1;
bytes_per_connection = -1;
bytes_left = -1;
2010-09-01 10:52:02 +02:00
}
else
{
if (connection->resume_support == FALSE)
num_connections = 1;
else
num_connections = req_connections;
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
bytes_per_connection = connection->main_file_size / num_connections;
bytes_left = connection->main_file_size % num_connections;
}
2010-08-31 03:50:41 +02:00
download->pconnections = kmalloc(sizeof(connection_t *) * num_connections);
download->num_connections = num_connections;
2010-09-01 10:52:02 +02:00
out_file = kmalloc(PATH_MAX);
2010-08-31 03:50:41 +02:00
snprintf(out_file, PATH_MAX, "%s/%s.prozilla",
2010-09-01 10:52:02 +02:00
download->dl_dir, connection->u.file);
proz_debug("out file %s", out_file);
2010-08-31 03:50:41 +02:00
//First see if the file exists then we dont create a new one else we do
if (stat(out_file, &stat_buf) == -1)
{
2010-09-01 10:52:02 +02:00
proz_debug("stat failed");
2010-08-31 03:50:41 +02:00
/* the call failed */
2010-09-01 10:52:02 +02:00
/* if the error is due to the file not been present then there is no
need to do anything..just continue, otherwise return error (-1)
*/
2010-08-31 03:50:41 +02:00
if (errno == ENOENT)
2010-09-01 10:52:02 +02:00
{
proz_debug("file doesnt exist");
//File not exists so create it
if (!
(fp =
fopen(out_file, "w+")))
{
download_show_message(download,
_
("Unable to open file %s: %s!"),
out_file, strerror(errno));
proz_debug("Unable to open file %s: %s!",
out_file, strerror(errno));
return -1;
}
proz_debug("created file");
}
2010-08-31 03:50:41 +02:00
else
2010-09-01 10:52:02 +02:00
{
proz_debug("something else happened %d", errno);
return -1;
}
2010-08-31 03:50:41 +02:00
}
else
{
2010-09-01 10:52:02 +02:00
proz_debug("stat success");
2010-08-31 03:50:41 +02:00
//TODO: File exists : if it doesnt match file size warna boput it...
if (!
2010-09-01 10:52:02 +02:00
(fp =
fopen(out_file, "r+")))
{
download_show_message(download,
_
("Unable to open file %s: %s!"),
out_file, strerror(errno));
proz_debug("Unable to open file %s: %s!",
out_file, strerror(errno));
return -1;
}
2010-08-31 03:50:41 +02:00
}
//TRY setting the offset;
if (download->main_file_size != -1)
{
2010-09-01 10:52:02 +02:00
if (fseeko(fp, download->main_file_size, SEEK_SET) != 0)
{
proz_debug("fseek failed");
return -1;
}
2010-08-31 03:50:41 +02:00
}
/*Make sure all writes go directly to the file */
2010-09-01 10:52:02 +02:00
setvbuf(fp, NULL, _IONBF, 0);
2010-08-31 03:50:41 +02:00
for (i = 0; i < num_connections; i++)
{
2010-09-01 10:52:02 +02:00
download->pconnections[i] = proz_connection_init(&download->u,
&download->status_change_mutex);
2010-08-31 03:50:41 +02:00
/*Copy somethings we need from the original connection */
download->resume_support = download->pconnections[i]->resume_support =
2010-09-01 10:52:02 +02:00
connection->resume_support;
2010-08-31 03:50:41 +02:00
memcpy(&download->pconnections[i]->hs, &connection->hs,
2010-09-01 10:52:02 +02:00
sizeof(http_stat_t));
2010-08-31 03:50:41 +02:00
download->pconnections[i]->localfile = kmalloc(PATH_MAX);
strcpy(out_file, download->pconnections[i]->localfile);
2010-09-01 10:52:02 +02:00
download->pconnections[i]->fp = fp;
2010-08-31 03:50:41 +02:00
download->pconnections[i]->retry = TRUE;
if (connection->main_file_size == -1)
2010-09-01 10:52:02 +02:00
{
download->pconnections[i]->main_file_size = -1;
download->pconnections[i]->remote_startpos = 0;
download->pconnections[i]->orig_remote_startpos = 0;
download->pconnections[i]->remote_endpos = -1;
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
download->pconnections[i]->local_startpos = 0;
}
else
{
download->pconnections[i]->main_file_size = connection->main_file_size;
download->pconnections[i]->orig_remote_startpos = download->pconnections[i]->remote_startpos = i * bytes_per_connection;
download->pconnections[i]->remote_endpos =
i * bytes_per_connection + bytes_per_connection;
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
//Changing things here.....
download->pconnections[i]->local_startpos = download->pconnections[i]->remote_startpos;
}
2010-08-31 03:50:41 +02:00
/*Set the connections message to be download->msg_proc calback */
proz_connection_set_msg_proc(download->pconnections[i],
2010-09-01 10:52:02 +02:00
download->msg_proc, download->cb_data);
2010-08-31 03:50:41 +02:00
}
/* Add the remaining bytes to the last connection */
download->pconnections[--i]->remote_endpos += bytes_left;
download->using_ftpsearch = FALSE;
2010-09-01 10:52:02 +02:00
/*NOTE: Should we check for previously started downloads here and adjust
the local_startpos, accordingly or check for resumes later in another function
which is called after this?
*/
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
proz_debug("return num_connections %d", num_connections);
2010-08-31 03:50:41 +02:00
return num_connections;
}
/* This will for each connection setup the local_startpos and file write mode if any prior download exists returns 1 on success, and -1 on a error.*/
int proz_download_load_resume_info(download_t * download)
{
int i;
int ret = 1;
logfile lf;
2010-09-01 10:52:02 +02:00
if (proz_log_read_logfile(&lf, download, TRUE) == 1)
proz_debug("sucessfully loaded resume info");
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
for (i = 0; i < download->num_connections; i++)
{
if (download->pconnections[i]->remote_endpos - download->pconnections[i]->remote_startpos == download->pconnections[i]->remote_bytes_received)
{
connection_change_status(download->pconnections[i], COMPLETED);
//This should fix the error we received when resuming when the
//average rate was too high.
download->pconnections[i]->remote_startpos += download->pconnections[i]->remote_bytes_received;
continue;
}
download->pconnections[i]->remote_startpos += download->pconnections[i]->remote_bytes_received;
}
2010-08-31 03:50:41 +02:00
download->resume_mode = TRUE;
return ret;
}
2010-09-01 10:52:02 +02:00
/* This will create the threads and start the downloads,
2010-08-31 03:50:41 +02:00
if resume is true it will load the resume info too if the download supports it
2010-09-01 10:52:02 +02:00
*/
2010-08-31 03:50:41 +02:00
void proz_download_start_downloads(download_t * download,
2010-09-01 10:52:02 +02:00
boolean resume_mode)
2010-08-31 03:50:41 +02:00
{
int i;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
if (resume_mode)
2010-09-01 10:52:02 +02:00
{
/*Does this download suport resume? */
if (download->resume_support == TRUE)
proz_download_load_resume_info(download);
}
else
{
/*Create the log file */
if (log_create_logfile
(download->num_connections, download->main_file_size,
download->u.url, download) != 1)
{
download_show_message(download,
_("Warning! Unable to create logfile!"));
}
}
2010-08-31 03:50:41 +02:00
/* Allocate number of threads */
download->threads =
2010-09-01 10:52:02 +02:00
(pthread_t *)kmalloc(sizeof(pthread_t) * download->num_connections);
2010-08-31 03:50:41 +02:00
/*Create them */
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
switch (download->pconnections[i]->u.proto)
{
case URLHTTP:
/* http_loop(&download->connections[i]); */
if (pthread_create(&download->threads[i], NULL,
(void *)&http_loop,
(void *)(download->pconnections[i])) != 0)
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
proz_die(_("Error: Not enough system resources"));
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
break;
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
case URLFTP:
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
/* ftp_loop(&download->connections[i]); */
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
break;
default:
proz_die(_("Error: Unsupported Protocol was specified"));
}
2010-08-31 03:50:41 +02:00
}
download_show_message(download, _("All threads created"));
}
void proz_download_stop_downloads(download_t * download)
{
int i;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
/*Stop the threads */
for (i = 0; i < download->num_connections; i++)
2010-09-01 10:52:02 +02:00
{
pthread_cancel(download->threads[i]);
pthread_join(download->threads[i], NULL);
}
2010-08-31 03:50:41 +02:00
}
/* returns one of DLINPROGRESS, DLERR, DLDONE, DLREMOTEFATAL, DLLOCALFATAL*/
uerr_t proz_download_handle_threads(download_t * download)
{
//Create logfile everytime this is callaed
2010-09-01 10:52:02 +02:00
log_create_logfile(download->num_connections, download->main_file_size, download->u.url, download);
2010-08-31 03:50:41 +02:00
if (download->using_ftpsearch == TRUE)
return download_handle_threads_ftpsearch(download);
else
return download_handle_threads_no_ftpsearch(download);
}
/* returns one of DLINPROGRESS, DLERR, DLDONE, DLREMOTEFATAL, DLLOCALFATAL*/
uerr_t download_handle_threads_no_ftpsearch(download_t * download)
{
int i;
for (i = 0; i < download->num_connections; i++)
{
/*Set the DL start time if it is not done so */
pthread_mutex_lock(download->pconnections[i]->status_change_mutex);
if (download->pconnections[i]->status == DOWNLOADING
2010-09-01 10:52:02 +02:00
&& download->start_time.tv_sec == 0
&& download->start_time.tv_usec == 0)
{
gettimeofday(&download->start_time, NULL);
}
2010-08-31 03:50:41 +02:00
pthread_mutex_unlock(download->pconnections[i]->status_change_mutex);
}
/*If all the connections are completed then end them, and return complete */
if ((proz_download_all_dls_status(download, COMPLETED)) == TRUE)
{
char * out_filename;
char * orig_filename;
download_show_message(download,
2010-09-01 10:52:02 +02:00
"All the conenctions have retreived the file"
"..waiting for them to end");
2010-08-31 03:50:41 +02:00
proz_download_wait_till_all_end(download);
download_show_message(download, "All the threads have being ended.");
/*Close and rename file to original */
// flockfile(download->pconnections[0]->fp);
fclose(download->pconnections[0]->fp);
// funlockfile(download->pconnections[0]->fp);
2010-09-01 10:52:02 +02:00
out_filename = kmalloc(PATH_MAX);
orig_filename = kmalloc(PATH_MAX);
2010-08-31 03:50:41 +02:00
snprintf(orig_filename, PATH_MAX, "%s/%s",
2010-09-01 10:52:02 +02:00
download->dl_dir, download->pconnections[0]->u.file);
2010-08-31 03:50:41 +02:00
snprintf(out_filename, PATH_MAX, "%s/%s.prozilla",
2010-09-01 10:52:02 +02:00
download->dl_dir, download->pconnections[0]->u.file);
if (rename(out_filename, orig_filename) == -1)
{
download_show_message(download, "Error While attempting to rename the file: %s", strerror(errno));
}
2010-08-31 03:50:41 +02:00
download_show_message(download, "Successfully renamed file");
/*Delete the logfile as we dont need it now */
2010-09-01 10:52:02 +02:00
if (proz_log_delete_logfile(download) != 1)
download_show_message(download, "Error: Unable to delete the logfile: %s", strerror(errno));
2010-08-31 03:50:41 +02:00
return DLDONE;
}
/*TODO handle restartable connections */
for (i = 0; i < download->num_connections; i++)
{
dl_status status;
uerr_t connection_err;
pthread_mutex_lock(download->pconnections[i]->status_change_mutex);
status = download->pconnections[i]->status;
pthread_mutex_unlock(download->pconnections[i]->status_change_mutex);
pthread_mutex_lock(&download->pconnections[i]->access_mutex);
connection_err = download->pconnections[i]->err;
pthread_mutex_unlock(&download->pconnections[i]->access_mutex);
switch (status)
2010-09-01 10:52:02 +02:00
{
case MAXTRYS:
break;
case REMOTEFATAL:
/* handle the CANTRESUME err code */
if (connection_err == CANTRESUME)
{
/*Terminate the connections */
proz_download_stop_downloads(download);
/*FIXME Do we delete any downloaded portions here ? */
return CANTRESUME;
}
else /*Handle the file not being found on the server */
if (connection_err == FTPNSFOD || connection_err == HTTPNSFOD)
{
if (proz_download_all_dls_filensfod(download) == TRUE)
{
download_show_message(download,
_
("The file was not found in all the connections!"));
/*Terminate the connections */
proz_download_stop_downloads(download);
return DLREMOTEFATAL;
}
else
{
download_show_message(download, _("Relaunching download"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
}
else /*Handle the file not being found on the server */
if (connection_err == FTPCWDFAIL)
{
if (proz_download_all_dls_ftpcwdfail(download) == TRUE)
{
download_show_message(download,
_
("Failed to change to the working directory on all the connections!"));
/*Terminate the connections */
proz_download_stop_downloads(download);
return DLREMOTEFATAL;
}
else
{
download_show_message(download, _("Relaunching download"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond, &download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
}
break;
case LOCALFATAL:
proz_download_stop_downloads(download);
download_show_message(download,
_
("Connection %d, had a local fatal error: %s .Aborting download. "),
i,
proz_strerror(download->pconnections[i]->err));
return DLLOCALFATAL;
break;
case LOGINFAIL:
/*
* First check if the ftp server did not allow any thread
* to login at all, then retry the curent thread
*/
if (proz_download_all_dls_status(download, LOGINFAIL) == TRUE)
{
download_show_message(download,
_
("All logins rejected! Retrying connection"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
break;
}
else
{
/*
* Ok so at least there is one download whos login has not been rejected,
* so lets see if it has completed, if so we can relaunch this connection,
* as the commonest reason for a ftp login being rejected is because, the
* ftp server has a limit on the number of logins permitted from the same
* IP address.
*/
/*
* Query the number of threads that are downloading
* if it is zero then relaunch this connection
*/
int dling_conns_count =
download_query_conns_status_count(download, DOWNLOADING, NULL);
if (dling_conns_count > download->max_simul_connections)
{
download->max_simul_connections = dling_conns_count;
break;
}
if (dling_conns_count == 0
&&
(download_query_conns_status_count(download, CONNECTING, NULL)
== 0)
&& (download_query_conns_status_count(download, LOGGININ, NULL)
== 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
else
if (dling_conns_count < download->max_simul_connections
&&
(download_query_conns_status_count
(download, CONNECTING, NULL) == 0)
&&
(download_query_conns_status_count
(download, LOGGININ, NULL) == 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
}
break;
case CONREJECT:
/*
* First check if the ftp server did not allow any thread
* to login at all, then retry the curent thread
*/
if (proz_download_all_dls_status(download, CONREJECT) == TRUE)
{
download_show_message(download,
_
("All connections attempts have been rejected! Retrying connection"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
break;
}
else
{
/*
* Ok so at least there is one download whos connections attempt has not been rejected,
* so lets see if it has completed, if so we can relaunch this connection,
* as the commonest reason for a ftp login being rejected is because, the
* ftp server has a limit on the number of logins permitted from the same
* IP address.
*/
/*
* Query the number of threads that are downloading
* if it is zero then relaunch this connection
*/
int dling_conns_count =
download_query_conns_status_count(download, DOWNLOADING, NULL);
if (dling_conns_count > download->max_simul_connections)
{
download->max_simul_connections = dling_conns_count;
break;
}
if (dling_conns_count == 0
&&
(download_query_conns_status_count(download, CONNECTING, NULL)
== 0)
&& (download_query_conns_status_count(download, LOGGININ, NULL)
== 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
else
if (dling_conns_count < download->max_simul_connections
&&
(download_query_conns_status_count
(download, CONNECTING, NULL) == 0)
&&
(download_query_conns_status_count
(download, LOGGININ, NULL) == 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
}
break;
default:
break;
}
2010-08-31 03:50:41 +02:00
}
/*bandwith throttling */
download_calc_throttle_factor(download);
return DLINPROGRESS;
}
pthread_mutex_t download_msg_mutex = PTHREAD_MUTEX_INITIALIZER;
/*calls the msg_proc function if not null */
void download_show_message(download_t * download, const char *format, ...)
{
va_list args;
char message[MAX_MSG_SIZE + 1];
pthread_mutex_lock(&download_msg_mutex);
va_start(args, format);
vsnprintf(message, MAX_MSG_SIZE, format, args);
va_end(args);
if (download->msg_proc)
download->msg_proc(message, download->cb_data);
/*FIXME: Remove this later */
// printf("%s\n", message);
pthread_mutex_unlock(&download_msg_mutex);
}
/*This will return a pointer to the connection requested. */
connection_t *proz_download_get_connection(download_t * download,
2010-09-01 10:52:02 +02:00
int number)
2010-08-31 03:50:41 +02:00
{
assert(number >= 0 && number < download->num_connections);
2010-09-01 10:52:02 +02:00
return(download->pconnections[number]);
2010-08-31 03:50:41 +02:00
}
/*Returns the total number of bytes got.*/
off_t proz_download_get_total_bytes_got(download_t * download)
{
off_t total_bytes_got = 0;
int i;
for (i = 0; i < download->num_connections; i++)
2010-09-01 10:52:02 +02:00
{
total_bytes_got +=
proz_connection_get_total_bytes_got(download->pconnections[i]);
}
2010-08-31 03:50:41 +02:00
return total_bytes_got;
}
/*Returns 1 if a previous download exits, 0 if not, and -1 on error */
int proz_download_prev_download_exists(download_t * download)
{
2010-09-01 10:52:02 +02:00
/* Currently if a logfile exists it assumes that a previous uncompleted
2010-08-31 03:50:41 +02:00
download exists
*/
return proz_log_logfile_exists(download);
}
/*Returns the download speed in bytes per second */
float proz_download_get_average_speed(download_t * download)
{
float speed;
struct timeval cur_time;
struct timeval diff_time;
off_t total_remote_bytes_got =
2010-09-01 10:52:02 +02:00
proz_download_get_total_remote_bytes_got(download);
2010-08-31 03:50:41 +02:00
/*Has the download has been started.... */
if (download->start_time.tv_sec > 0 || download->start_time.tv_usec > 0)
{
2010-09-01 10:52:02 +02:00
gettimeofday(&cur_time, NULL);
/*Get the diff. */
proz_timeval_subtract(&diff_time, &cur_time, &download->start_time);
if (diff_time.tv_sec >= 1) // || diff_time.tv_usec > 0)
{
speed = (float)total_remote_bytes_got / ((float)diff_time.tv_sec +
((float)diff_time.
tv_usec / 10e5));
}
else
speed = 0;
}
else
speed = 0; /*The DL hasnt started yet */
2010-08-31 03:50:41 +02:00
return speed;
}
2010-09-01 10:52:02 +02:00
/* This can be called to erase the portions of the main file
2010-08-31 03:50:41 +02:00
that have been got.
2010-09-01 10:52:02 +02:00
*/
2010-08-31 03:50:41 +02:00
int proz_download_delete_dl_file(download_t * download)
{
char *out_file;
2010-09-01 10:52:02 +02:00
out_file = kmalloc(PATH_MAX);
2010-08-31 03:50:41 +02:00
snprintf(out_file, PATH_MAX, "%s/%s.prozilla",
2010-09-01 10:52:02 +02:00
download->dl_dir, download->u.file);
2010-08-31 03:50:41 +02:00
if (unlink(out_file) == -1)
{
/*
2010-09-01 10:52:02 +02:00
* if the file is not present no need for a error message ;-)
2010-08-31 03:50:41 +02:00
*/
if (errno == ENOENT)
2010-09-01 10:52:02 +02:00
{
return 1;
}
2010-08-31 03:50:41 +02:00
else
2010-09-01 10:52:02 +02:00
{
download_show_message(download,
_
("unable to delete the file %s. Reason-: %s"),
out_file, strerror(errno));
return -1;
}
2010-08-31 03:50:41 +02:00
}
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
return 1;
}
/*This will wait till all the downloaded threads are not running */
void proz_download_wait_till_all_end(download_t * download)
{
int i;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
/*Wait till the end of all the threads */
for (i = 0; i < download->num_connections; i++)
2010-09-01 10:52:02 +02:00
{
pthread_join(download->threads[i], NULL);
}
2010-08-31 03:50:41 +02:00
}
/*Creates the joining thread */
void proz_download_join_downloads(download_t * download)
{
download->building = TRUE;
2010-09-01 10:52:02 +02:00
pthread_create(&download->join_thread, NULL,
(void *(*)(void *))download_join_downloads,
(void *)download);
2010-08-31 03:50:41 +02:00
}
/*This function will call download_join_downloads with a handler to join the downloaded files*/
void download_join_downloads(download_t * download)
{
// pthread_cleanup_push(cleanup_joining_thread, (void *) download);
join_downloads(download);
// pthread_cleanup_pop(0);
}
/*This function will join the downloaded files*/
void join_downloads(download_t * download)
{
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&download->access_mutex);
2010-08-31 03:50:41 +02:00
download->building = 0;
pthread_mutex_unlock(&download->access_mutex);
return;
}
2010-09-01 10:52:02 +02:00
/*****************************************************************************
Returns the total number of bytes that has being got from the server
by the all the connections managed by this download
******************************************************************************/
2010-08-31 03:50:41 +02:00
off_t proz_download_get_total_remote_bytes_got(download_t * download)
{
off_t total_bytes_recv = 0;
int i;
for (i = 0; i < download->num_connections; i++)
2010-09-01 10:52:02 +02:00
{
proz_debug("DOWNLOAD_TOTAL_BYTES_RECV=%lld for connection %d", total_bytes_recv, i);
total_bytes_recv +=
proz_connection_get_total_remote_bytes_got(download->
pconnections[i]);
}
2010-08-31 03:50:41 +02:00
return total_bytes_recv;
}
/*If all the downlaods status is equal to status ,returns TRUE */
int proz_download_all_dls_status(download_t * download, dl_status status)
{
int i;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
pthread_mutex_lock(&download->status_change_mutex);
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
if (download->pconnections[i]->status != status)
{
pthread_mutex_unlock(&download->status_change_mutex);
return FALSE;
}
2010-08-31 03:50:41 +02:00
}
pthread_mutex_unlock(&download->status_change_mutex);
return TRUE;
}
boolean proz_download_all_dls_filensfod(download_t * download)
{
int i;
uerr_t err;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
/*Lock mutex */
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&download->pconnections[i]->access_mutex);
err = download->pconnections[i]->err;
pthread_mutex_unlock(&download->pconnections[i]->access_mutex);
if (err != FTPNSFOD && err != HTTPNSFOD)
{
return FALSE;
}
2010-08-31 03:50:41 +02:00
}
return TRUE;
}
boolean proz_download_all_dls_ftpcwdfail(download_t * download)
{
int i;
uerr_t err;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
/*Lock mutex */
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&download->pconnections[i]->access_mutex);
err = download->pconnections[i]->err;
pthread_mutex_unlock(&download->pconnections[i]->access_mutex);
if (err != FTPNSFOD && err != FTPCWDFAIL)
{
return FALSE;
}
2010-08-31 03:50:41 +02:00
}
return TRUE;
}
/*If all the downlaods conections err status is equal to in_err (ie all encountered the same error) ,returns TRUE */
boolean proz_download_all_dls_err(download_t * download, uerr_t in_err)
{
2010-09-01 10:52:02 +02:00
int i;
2010-08-31 03:50:41 +02:00
uerr_t err;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
/*Lock mutex */
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&download->pconnections[i]->access_mutex);
err = download->pconnections[i]->err;
pthread_mutex_unlock(&download->pconnections[i]->access_mutex);
if (err != in_err)
{
return FALSE;
}
2010-08-31 03:50:41 +02:00
}
return TRUE;
}
/* Returns the number of connections whose status is status ie (connecting to the server specified),
2010-09-01 10:52:02 +02:00
if server is NULL then it returns the total number of connections that are having the status which is equal the to the status specified */
2010-08-31 03:50:41 +02:00
int download_query_conns_status_count(download_t * download,
2010-09-01 10:52:02 +02:00
dl_status status, char *server)
2010-08-31 03:50:41 +02:00
{
int i;
int count = 0;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
pthread_mutex_lock(&download->status_change_mutex);
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
if (download->pconnections[i]->status == status)
{
if (server == NULL
|| (strcasecmp(server, download->pconnections[i]->u.host) == 0))
count++;
}
2010-08-31 03:50:41 +02:00
}
pthread_mutex_unlock(&download->status_change_mutex);
return count;
}
void proz_download_set_msg_proc(download_t * download,
2010-09-01 10:52:02 +02:00
message_proc msg_proc, void *cb_data)
2010-08-31 03:50:41 +02:00
{
assert(download != NULL);
download->msg_proc = msg_proc;
download->cb_data = cb_data;
}
/*Returns the number of seconds left remaining in the download,
2010-09-01 10:52:02 +02:00
if it cannot be calculated say if the file size of not known, it returns -1
*/
2010-08-31 03:50:41 +02:00
off_t proz_download_get_est_time_left(download_t * download)
{
long secs_left;
float average_speed;
off_t total_bytes_got;
if (download->main_file_size == -1)
return -1;
total_bytes_got = proz_download_get_total_bytes_got(download);
average_speed = proz_download_get_average_speed(download);
if (average_speed == 0)
return -1;
return secs_left =
2010-09-01 10:52:02 +02:00
(off_t)((download->main_file_size -
total_bytes_got) / average_speed);
2010-08-31 03:50:41 +02:00
}
void proz_download_free_download(download_t * download, boolean complete)
{
assert(download);
/*TODO free the URL */
if (download->dl_dir)
kfree(download->dl_dir);
if (download->output_dir)
kfree(download->output_dir);
if (download->log_dir)
kfree(download->log_dir);
if (download->file_build_msg)
kfree(download->file_build_msg);
if (download->threads)
kfree(download->threads);
/*Now handle the freeing of the connections */
if (download->num_connections > 0 && download->pconnections)
{
2010-09-01 10:52:02 +02:00
int i;
for (i = 0; i < download->num_connections; i++)
{
proz_connection_free_connection(download->pconnections[i], 0);
}
kfree(download->pconnections);
2010-08-31 03:50:41 +02:00
}
if (complete == TRUE)
kfree(download);
}
void download_calc_throttle_factor(download_t * download)
{
int i;
int num_slow_cons = 0;
long t_slow_rates = 0;
long limit_high_cons_rate;
long avg_rate;
int num_dl_cons =
2010-09-01 10:52:02 +02:00
download_query_conns_status_count(download, DOWNLOADING, NULL);
2010-08-31 03:50:41 +02:00
if (num_dl_cons == 0)
return;
avg_rate = download->max_allowed_bps / num_dl_cons;
if (download->max_allowed_bps == 0)
{
2010-09-01 10:52:02 +02:00
for (i = 0; i < download->num_connections; i++)
{
pthread_mutex_lock(&(download->pconnections[i]->access_mutex));
download->pconnections[i]->max_allowed_bps = 0;
pthread_mutex_unlock(&(download->pconnections[i]->access_mutex));
}
return;
2010-08-31 03:50:41 +02:00
}
/*MAKE IR USE THE NUMBER OF ACTIVE DOWNLOAdING CONENCTIONS: Done */
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&(download->pconnections[i]->access_mutex));
if ((proz_connection_get_status(download->pconnections[i]) ==
DOWNLOADING) && download->pconnections[i]->rate_bps < avg_rate)
{
t_slow_rates += download->pconnections[i]->rate_bps;
num_slow_cons++;
}
pthread_mutex_unlock(&(download->pconnections[i]->access_mutex));
2010-08-31 03:50:41 +02:00
}
/*fixme mutex to preven this conenctions */
if (num_slow_cons > num_dl_cons)
num_dl_cons = num_slow_cons;
/*If all the connections are slower then no need to do anything */
if (num_slow_cons == num_dl_cons)
{
2010-09-01 10:52:02 +02:00
for (i = 0; i < download->num_connections; i++)
{
pthread_mutex_lock(&(download->pconnections[i]->access_mutex));
download->pconnections[i]->max_allowed_bps = 0;
pthread_mutex_unlock(&(download->pconnections[i]->access_mutex));
}
return;
2010-08-31 03:50:41 +02:00
}
limit_high_cons_rate =
2010-09-01 10:52:02 +02:00
(download->max_allowed_bps - t_slow_rates) / (num_dl_cons -
num_slow_cons);
2010-08-31 03:50:41 +02:00
/*
proz_debug("total slow connections = %ld", num_slow_cons);
proz_debug("total slow rates = %ld", t_slow_rates);
proz_debug("limit_high_cons_rate = %ld", limit_high_cons_rate);
*/
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&(download->pconnections[i]->access_mutex));
if ((proz_connection_get_status(download->pconnections[i]) ==
DOWNLOADING) && download->pconnections[i]->rate_bps >= avg_rate)
{
download->pconnections[i]->max_allowed_bps = limit_high_cons_rate;
}
pthread_mutex_unlock(&(download->pconnections[i]->access_mutex));
2010-08-31 03:50:41 +02:00
}
}
2010-09-01 10:52:02 +02:00
/*This function will check if the target output file,
ie: the file that we are going to rebuild to exists.
Returns,
1= file exists
0 = file does not exists
-1 = error, ie cant have permissions to stat the file etc etc
*/
2010-08-31 03:50:41 +02:00
int proz_download_target_exist(download_t * download)
{
char out_file_name[PATH_MAX];
struct stat st_buf;
int ret;
snprintf(out_file_name, PATH_MAX, "%s/%s", download->output_dir,
2010-09-01 10:52:02 +02:00
download->u.file);
2010-08-31 03:50:41 +02:00
ret = stat(out_file_name, &st_buf);
if (ret == -1)
2010-09-01 10:52:02 +02:00
{
if (errno == ENOENT)
return 0;
else
return -1;
}
2010-08-31 03:50:41 +02:00
/*File was statable so it exists */
return 1;
}
2010-09-01 10:52:02 +02:00
/*This function will delete the target output file,
Returns,
1= sucessfully delted the file
0 = file does not exist
-1 = error, ie cant have permissions to delete the file etc etc
*/
2010-08-31 03:50:41 +02:00
int proz_download_delete_target(download_t * download)
{
char out_file_name[PATH_MAX];
int ret;
snprintf(out_file_name, PATH_MAX, "%s/%s", download->output_dir,
2010-09-01 10:52:02 +02:00
download->u.file);
2010-08-31 03:50:41 +02:00
ret = remove(out_file_name);
if (ret == -1)
2010-09-01 10:52:02 +02:00
{
if (errno == ENOENT)
return 0;
else
return -1;
}
2010-08-31 03:50:41 +02:00
/*File was statable so it exists */
return 1;
}
/*Tries to switch to another server which is downloading or has completed, returns 1 on sucesss, or -1 on failure
*/
int download_switch_server_ftpsearch(download_t *download, int bad_connection)
{
2010-09-01 10:52:02 +02:00
int j, usable_server;
usable_server = -1;
/*Search for a server which is downloading or completed and try to switch to it
*/
pthread_mutex_lock(&download->status_change_mutex);
for (j = 0; j < download->num_connections; j++)
{
if (download->pconnections[j]->status == DOWNLOADING ||
download->pconnections[j]->status == COMPLETED)
{
usable_server = j;
}
}
pthread_mutex_unlock(&download->status_change_mutex);
if (usable_server != -1)
{
/*We have a server which is DLING */
/* Make sure this thread has terminated */
pthread_join(download->threads[bad_connection], NULL);
/*copy url and relaunch */
proz_free_url(&download->pconnections[bad_connection]->u, 0);
memcpy(&download->pconnections[bad_connection]->u,
proz_copy_url(&download->
pconnections[usable_server]->u),
sizeof(urlinfo));
proz_debug
("Found server %s which is downloading will relaunch based on it",
download->pconnections[usable_server]->u.host);
pthread_mutex_lock(&download->status_change_mutex);
/*Relaunch thread */
if (pthread_create
(&download->threads[bad_connection], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[bad_connection])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[bad_connection]->connecting_cond, &download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
return 1;
}
else
{
/*Shit! no servers are downloading or completed so what shall we do? we shall do nothing and wait for one connectection at least to start downloading */
return -1;
}
}
2010-08-31 03:50:41 +02:00
/* returns one of DLINPROGRESS, DLERR, DLDONE, DLREMOTEFATAL, DLLOCALFATAL*/
uerr_t download_handle_threads_ftpsearch(download_t * download)
{
int i;
for (i = 0; i < download->num_connections; i++)
{
2010-09-01 10:52:02 +02:00
/*Set the DL start time if it is not done so */
pthread_mutex_lock(download->pconnections[i]->status_change_mutex);
if (download->pconnections[i]->status == DOWNLOADING
&& download->start_time.tv_sec == 0
&& download->start_time.tv_usec == 0)
{
gettimeofday(&download->start_time, NULL);
}
pthread_mutex_unlock(download->pconnections[i]->status_change_mutex);
2010-08-31 03:50:41 +02:00
}
/*If all the connections are completed then end them, and return complete */
if ((proz_download_all_dls_status(download, COMPLETED)) == TRUE)
2010-09-01 10:52:02 +02:00
{
2010-08-31 03:50:41 +02:00
char * out_filename;
char * orig_filename;
2010-09-01 10:52:02 +02:00
download_show_message(download,
"All the conenctions have retreived the file"
"..waiting for them to end");
proz_download_wait_till_all_end(download);
download_show_message(download, "All the threads have being ended.");
2010-08-31 03:50:41 +02:00
/*Close and rename file to original */
flockfile(download->pconnections[0]->fp);
fclose(download->pconnections[0]->fp);
funlockfile(download->pconnections[0]->fp);
2010-09-01 10:52:02 +02:00
out_filename = kmalloc(PATH_MAX);
orig_filename = kmalloc(PATH_MAX);
2010-08-31 03:50:41 +02:00
snprintf(orig_filename, PATH_MAX, "%s/%s",
2010-09-01 10:52:02 +02:00
download->dl_dir, download->pconnections[0]->u.file);
2010-08-31 03:50:41 +02:00
snprintf(out_filename, PATH_MAX, "%s/%s.prozilla",
2010-09-01 10:52:02 +02:00
download->dl_dir, download->pconnections[0]->u.file);
if (rename(out_filename, orig_filename) == -1)
{
download_show_message(download, "Error While attempting to rename the file: %s", strerror(errno));
}
2010-08-31 03:50:41 +02:00
download_show_message(download, "Successfully renamed file");
/*Delete the logfile as we dont need it now */
2010-09-01 10:52:02 +02:00
if (proz_log_delete_logfile(download) != 1)
download_show_message(download, "Error: Unable to delete the logfile: %s", strerror(errno));
return DLDONE;
}
2010-08-31 03:50:41 +02:00
/*TODO handle restartable connections */
for (i = 0; i < download->num_connections; i++)
2010-09-01 10:52:02 +02:00
{
dl_status status;
uerr_t connection_err;
pthread_mutex_lock(download->pconnections[i]->status_change_mutex);
status = download->pconnections[i]->status;
pthread_mutex_unlock(download->pconnections[i]->status_change_mutex);
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&download->pconnections[i]->access_mutex);
connection_err = download->pconnections[i]->err;
pthread_mutex_unlock(&download->pconnections[i]->access_mutex);
2010-08-31 03:50:41 +02:00
2010-09-01 10:52:02 +02:00
switch (status)
{
case MAXTRYS:
break;
case REMOTEFATAL:
/* handle the CANTRESUME err code */
if (connection_err == CANTRESUME)
{
/*Terminate the connections */
proz_download_stop_downloads(download);
/*FIXME Do we delete any downloaded portions here ? */
return CANTRESUME;
}
else /*Handle the file not being found on the server */
if (connection_err == FTPNSFOD || connection_err == HTTPNSFOD)
{
if (proz_download_all_dls_filensfod(download) == TRUE)
{
download_show_message(download,
_
("The file was not found in all the connections!"));
/*Terminate the connections */
proz_download_stop_downloads(download);
return DLREMOTEFATAL;
}
else
{
/*Now we have to be careful */
int server_pos, cur_path_pos;
server_pos =
ftpsearch_get_server_position(download->ftps_info,
download->pconnections[i]->u.
host);
cur_path_pos =
ftpsearch_get_path_position(download->ftps_info,
download->pconnections[i]->u.host,
download->pconnections[i]->u.dir);
assert(cur_path_pos != -1);
proz_debug("Server pos = %d, cur_path_pos=%d", server_pos,
cur_path_pos);
/*mark path as not valid */
download->ftps_info->mirrors[server_pos].paths[cur_path_pos].
valid = FALSE;
/*See if more paths are avail */;
if (cur_path_pos <
(download->ftps_info->mirrors[server_pos].num_paths - 1))
{
char *url_buf;
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
/*additional paths avail to try */
download_show_message(download,
_
("Trying additional paths available on this server"));
proz_debug("Trying path %s",
download->ftps_info->mirrors[server_pos].
paths[cur_path_pos + 1]);
url_buf =
malloc(strlen
(download->ftps_info->mirrors[server_pos].
server_name) +
strlen(download->ftps_info->mirrors[server_pos].
paths[cur_path_pos + 1].path) +
strlen(download->pconnections[i]->u.file) + 11 + 1);
sprintf(url_buf, "ftp://%s/%s/%s",
download->ftps_info->mirrors[server_pos].server_name,
download->ftps_info->mirrors[server_pos].
paths[cur_path_pos + 1].path,
download->pconnections[i]->u.file);
proz_debug("Target url for relaunching is %s", url_buf);
/*FIXME */
proz_parse_url(url_buf, &download->pconnections[i]->u, 0);
free(url_buf);
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
/*Relaunch thread */
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
else
{
/*download_show_message(download,
_
("No additional paths on this server available, so will try to switch to another server")); */
/*Find any server that is downloading or completed and use it */
download_switch_server_ftpsearch(download, i);
}
}
}
else /*Handle the file not being found on the server */
if (connection_err == FTPCWDFAIL)
{
if (proz_download_all_dls_ftpcwdfail(download) == TRUE)
{
download_show_message(download,
_
("Failed to change to the working directory on all the connections!"));
/*Terminate the connections */
proz_download_stop_downloads(download);
return DLREMOTEFATAL;
}
else
{
/*Now we have to be careful */
int server_pos, cur_path_pos;
server_pos =
ftpsearch_get_server_position(download->ftps_info,
download->pconnections[i]->u.
host);
assert(server_pos != -1);
cur_path_pos =
ftpsearch_get_path_position(download->ftps_info,
download->pconnections[i]->u.host,
download->pconnections[i]->u.dir);
assert(cur_path_pos != -1);
proz_debug("Server pos = %d, cur_path_pos=%d", server_pos,
cur_path_pos);
/*mark path as not valid */
download->ftps_info->mirrors[server_pos].paths[cur_path_pos].
valid = FALSE;
/*See if more paths are avail */;
if (cur_path_pos <
(download->ftps_info->mirrors[server_pos].num_paths - 1))
{
char *url_buf;
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
/*additional paths avail to try */
download_show_message(download,
_
("Trying additional paths available on this server"));
proz_debug("Trying path %s",
download->ftps_info->mirrors[server_pos].
paths[cur_path_pos + 1]);
url_buf =
malloc(strlen
(download->ftps_info->mirrors[server_pos].
server_name) +
strlen(download->ftps_info->mirrors[server_pos].
paths[cur_path_pos + 1].path) +
strlen(download->pconnections[i]->u.file) + 11 + 1);
sprintf(url_buf, "ftp://%s/%s/%s",
download->ftps_info->mirrors[server_pos].server_name,
download->ftps_info->mirrors[server_pos].
paths[cur_path_pos + 1].path,
download->pconnections[i]->u.file);
proz_debug("Target url for relaunching is %s", url_buf);
/*FIXME */
proz_parse_url(url_buf, &download->pconnections[i]->u, 0);
free(url_buf);
/*Relaunch thread */
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
}
else
{
/* download_show_message(download, _("No additional paths on this server available, so will try to switch to another server")); */
proz_debug
("No additional paths on this server available, so will try to switch to another server");
/*Find any server that is downloading or completed and use it */
download_switch_server_ftpsearch(download, i);
}
}
}
else if (connection_err == FTPRESTFAIL)
{
/*Handle the server not supporting REST */
if (proz_download_all_dls_err(download, FTPRESTFAIL) == TRUE)
{
download_show_message(download,
_
("The server(s) do not support REST on all the connections!"));
/*Terminate the connections */
proz_download_stop_downloads(download);
return DLREMOTEFATAL;
}
else
{
/*Now we have to be careful */
int server_pos;
server_pos =
ftpsearch_get_server_position(download->ftps_info,
download->pconnections[i]->u.
host);
proz_debug("Server pos = %d", server_pos);
/*mark server as not supporting resume */
download->ftps_info->mirrors[server_pos].resume_supported =
FALSE;
{
download_show_message(download,
_
("This server does not support resuming downloads, so will switch to another server"));
download_switch_server_ftpsearch(download, i);
}
}
}
break;
case LOCALFATAL:
proz_download_stop_downloads(download);
download_show_message(download,
_
("Connection %d, had a local fatal error: %s .Aborting download. "),
i,
proz_strerror(download->pconnections[i]->err));
return DLLOCALFATAL;
break;
case LOGINFAIL:
/*
* First check if the ftp server did not allow any thread
* to login at all, then retry the curent thread
*/
if (proz_download_all_dls_status(download, LOGINFAIL) == TRUE)
{
download_show_message(download,
_
("All logins rejected! Retrying connection"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
break;
}
else
{
int j, usable_server;
/*Find any server that is downloading or completed and use it instead */
usable_server = -1;
pthread_mutex_lock(&download->status_change_mutex);
for (j = 0; j < download->num_connections; j++)
{
if ((download->pconnections[j]->status == DOWNLOADING ||
download->pconnections[j]->status == COMPLETED) && j != i)
{
usable_server = j;
}
}
pthread_mutex_unlock(&download->status_change_mutex);
if (usable_server != -1)
{
/*We have a server which is DLING */
download_show_message(download,
_
("This server has rejected the login attempt, so will switch to another server"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
/*copy url and relaunch */
proz_free_url(&download->pconnections[i]->u, 0);
memcpy(&download->pconnections[i]->u,
proz_copy_url(&download->
pconnections[usable_server]->u),
sizeof(urlinfo));
proz_debug
("Found server %s which is downloading will relaunch based on it",
download->pconnections[usable_server]->u.host);
/*Relaunch thread */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
else
{
/*
* Ok so at least there is one download whos login has not been rejected,
* so lets see if it has completed, if so we can relaunch this connection,
* as the commonest reason for a ftp login being rejected is because, the
* ftp server has a limit on the number of logins permitted from the same
* IP address.
*/
/*
* Query the number of threads that are downloading
* if it is zero then relaunch this connection
*/
int server_pos;
int dling_conns_count =
download_query_conns_status_count(download, DOWNLOADING,
download->
pconnections[i]->u.host);
server_pos =
ftpsearch_get_server_position(download->ftps_info,
download->pconnections[i]->u.host);
if (dling_conns_count >
download->ftps_info->mirrors[server_pos].max_simul_connections)
{
download->ftps_info->mirrors[server_pos].max_simul_connections =
dling_conns_count;
break;
}
if (dling_conns_count == 0
&&
(download_query_conns_status_count
(download, CONNECTING, download->pconnections[i]->u.host) == 0)
&&
(download_query_conns_status_count
(download, LOGGININ, download->pconnections[i]->u.host) == 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
else
if (dling_conns_count <
download->ftps_info->mirrors[server_pos].
max_simul_connections
&&
(download_query_conns_status_count
(download, CONNECTING,
download->pconnections[i]->u.host) == 0)
&&
(download_query_conns_status_count
(download, LOGGININ,
download->pconnections[i]->u.host) == 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
}
}
break;
case CONREJECT:
/*
* First check if the ftp server did not allow any thread
* to login at all, then retry the curent thread
*/
if (proz_download_all_dls_status(download, CONREJECT) == TRUE)
{
download_show_message(download,
_
("All connections attempts have been rejected! Retrying connection"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
break;
}
else
{
int j, usable_server;
/*Find any server that is downloading or completed and use it instead */
usable_server = -1;
pthread_mutex_lock(&download->status_change_mutex);
for (j = 0; j < download->num_connections; j++)
{
if ((download->pconnections[j]->status == DOWNLOADING ||
download->pconnections[j]->status == COMPLETED) && j != i)
{
usable_server = j;
}
}
pthread_mutex_unlock(&download->status_change_mutex);
if (usable_server != -1)
{
/*We have a server which is DLING */
download_show_message(download,
_
("This server has rejected the connection attempt, so will switch to another server"));
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
/*copy url and relaunch */
proz_free_url(&download->pconnections[i]->u, 0);
memcpy(&download->pconnections[i]->u,
proz_copy_url(&download->
pconnections[usable_server]->u),
sizeof(urlinfo));
proz_debug
("Found server %s which is downloading will relaunch based on it",
download->pconnections[usable_server]->u.host);
/*Relaunch thread */
pthread_mutex_lock(&download->status_change_mutex);
if (pthread_create
(&download->threads[i], NULL, (void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
else
{
/*
* Ok so at least there is one download whos connections attempt has not been rejected,
* so lets see if it has completed, if so we can relaunch this connection,
* as the commonest reason for a ftp login being rejected is because, the
* ftp server has a limit on the number of logins permitted from the same
* IP address.
*/
/*
* Query the number of threads that are downloading
* if it is zero then relaunch this connection
*/
int server_pos;
int dling_conns_count =
download_query_conns_status_count(download, DOWNLOADING,
download->
pconnections[i]->u.host);
server_pos =
ftpsearch_get_server_position(download->ftps_info,
download->pconnections[i]->u.host);
if (dling_conns_count >
download->ftps_info->mirrors[server_pos].max_simul_connections)
{
download->ftps_info->mirrors[server_pos].max_simul_connections =
dling_conns_count;
break;
}
if (dling_conns_count == 0
&&
(download_query_conns_status_count
(download, CONNECTING, download->pconnections[i]->u.host) == 0)
&&
(download_query_conns_status_count
(download, LOGGININ, download->pconnections[i]->u.host) == 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
else
if (dling_conns_count <
download->ftps_info->mirrors[server_pos].
max_simul_connections
&&
(download_query_conns_status_count
(download, CONNECTING,
download->pconnections[i]->u.host) == 0)
&&
(download_query_conns_status_count
(download, LOGGININ,
download->pconnections[i]->u.host) == 0))
{
/* Make sure this thread has terminated */
pthread_join(download->threads[i], NULL);
pthread_mutex_lock(&download->status_change_mutex);
download_show_message(download, _("Relaunching download"));
if (pthread_create(&download->threads[i], NULL,
(void *)&ftp_loop,
(void *)(download->pconnections[i])) != 0)
proz_die(_("Error: Not enough system resources"));
pthread_cond_wait(&download->pconnections[i]->connecting_cond,
&download->status_change_mutex);
pthread_mutex_unlock(&download->status_change_mutex);
}
}
}
break;
default:
break;
}
2010-08-31 03:50:41 +02:00
}
/*bandwith throttling */
download_calc_throttle_factor(download);
return DLINPROGRESS;
}
/******************************************************************************
2010-09-01 10:52:02 +02:00
This will setup a download based on the connection info and the ftpsearch results.
2010-08-31 03:50:41 +02:00
******************************************************************************/
int proz_download_setup_connections_ftpsearch(download_t * download,
2010-09-01 10:52:02 +02:00
connection_t * connection,
ftps_request_t * request,
int req_connections)
2010-08-31 03:50:41 +02:00
{
int num_connections, i, num_usable_mirrors = 0;
off_t bytes_per_connection;
off_t bytes_left;
FILE *fp;
char *out_file;
struct stat stat_buf;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
/*TODO Check for log file and use same number of threads */
proz_debug("proz_download_setup_connections_ftpsearch");
download->main_file_size = connection->main_file_size;
download->ftps_info = request;
num_connections = req_connections;
/*Are there any mirrors that are working? */
/**/
if (request->num_mirrors > 0
&& request->mirrors[0].status == RESPONSEOK)
{
2010-09-01 10:52:02 +02:00
proz_debug("got mirrors");
2010-08-31 03:50:41 +02:00
/*We should get the info and allocate here */
download->resume_support = TRUE;
bytes_per_connection = connection->main_file_size / num_connections;
bytes_left = connection->main_file_size % num_connections;
/* Get the number of mirros that are within 200% ping speed of the fastest */
for (i = 0; i < request->num_mirrors; i++)
2010-09-01 10:52:02 +02:00
{
if ((request->mirrors[i].status == RESPONSEOK) &&
(request->mirrors[i].milli_secs <=
(request->mirrors[0].milli_secs +
((request->mirrors[0].milli_secs * 100) / 100))))
num_usable_mirrors++;
}
2010-08-31 03:50:41 +02:00
proz_debug("usable mirrors = %d", num_usable_mirrors);
proz_debug("num_connections = %d", num_connections);
download->pconnections =
2010-09-01 10:52:02 +02:00
kmalloc(sizeof(connection_t*) * num_connections);
2010-08-31 03:50:41 +02:00
download->num_connections = num_connections;
for (i = 0; i < num_connections; i++)
2010-09-01 10:52:02 +02:00
{
urlinfo *url = malloc(sizeof(urlinfo));
char *url_buf;
if (i < num_usable_mirrors)
{
url_buf =
malloc(strlen(request->mirrors[i].server_name) +
strlen(request->mirrors[i].paths[0].path) +
strlen(connection->u.file) + 11 + 1);
sprintf(url_buf, "ftp://%s/%s/%s", request->mirrors[i].server_name,
request->mirrors[i].paths[0].path, connection->u.file);
proz_debug("Target url is %s", url_buf);
/*FIXME */
proz_parse_url(url_buf, url, 0);
free(url_buf);
download->pconnections[i] = proz_connection_init(url,
&download->status_change_mutex);
}
else
{
int extra_mirror = 0;
/*FIXME improve allocation algorithm in this part */
url_buf =
malloc(strlen(request->mirrors[extra_mirror].server_name) +
strlen(request->mirrors[extra_mirror].paths[0].path) +
strlen(connection->u.file) + 11 + 1);
sprintf(url_buf, "ftp://%s/%s/%s",
request->mirrors[extra_mirror].server_name,
request->mirrors[extra_mirror].paths[0].path,
connection->u.file);
proz_debug("Target url is %s", url_buf);
/*FIXME */
proz_parse_url(url_buf, url, 0);
free(url_buf);
download->pconnections[i] = proz_connection_init(url,
&download->status_change_mutex);
}
download->resume_support = download->pconnections[i]->resume_support =
TRUE;
memcpy(&download->pconnections[i]->hs, &connection->hs,
sizeof(http_stat_t));
out_file = kmalloc(PATH_MAX);
snprintf(out_file, PATH_MAX, "%s/%s.prozilla",
download->dl_dir, connection->u.file);
//First see if the file exists then we dont create a new one else we do
if (stat(out_file, &stat_buf) == -1)
{
/* the call failed */
/* if the error is due to the file not been present then there is no
need to do anything..just continue, otherwise return error (-1)
*/
if (errno == ENOENT)
{
//File not exists so create it
if (!
(fp =
fopen(out_file, "w+")))
{
download_show_message(download,
_
("Unable to open file %s: %s!"),
out_file, strerror(errno));
return -1;
}
}
else
return -1;
}
else
{
//TODO: File exists : so stat it and if it doesnt match file size warna boput it...
if (!
(fp =
fopen(out_file, "r+")))
{
download_show_message(download,
_
("Unable to open file %s: %s!"),
out_file, strerror(errno));
return -1;
}
}
//TRY setting the offset;
if (download->main_file_size != -1)
{
if (fseeko(fp, download->main_file_size, SEEK_SET) != 0)
return -1;
}
/*Make sure all writes go directly to the file */
setvbuf(fp, NULL, _IONBF, 0);
download->pconnections[i]->localfile = kmalloc(PATH_MAX);
strcpy(out_file, download->pconnections[i]->localfile);
download->pconnections[i]->fp = fp;
download->pconnections[i]->retry = TRUE;
download->pconnections[i]->main_file_size = connection->main_file_size;
download->pconnections[i]->orig_remote_startpos = download->pconnections[i]->remote_startpos = i * bytes_per_connection;
download->pconnections[i]->remote_endpos =
i * bytes_per_connection + bytes_per_connection;
//Changing things here.....
download->pconnections[i]->local_startpos = download->pconnections[i]->remote_startpos;
/*Set the connections message to be download->msg_proc calback */
proz_connection_set_msg_proc(download->pconnections[i],
download->msg_proc, download->cb_data);
}
2010-08-31 03:50:41 +02:00
/* Add the remaining bytes to the last connection */
download->pconnections[--i]->remote_endpos += bytes_left;
2010-09-01 10:52:02 +02:00
}
else
{
proz_debug("No mirrors, which were up are found");
download->using_ftpsearch = FALSE;
return proz_download_setup_connections_no_ftpsearch(download,
connection,
req_connections);
}
2010-08-31 03:50:41 +02:00
download->using_ftpsearch = TRUE;
return num_connections;
}
uerr_t proz_download_get_join_status(download_t *download)
{
int building_status;
2010-09-01 10:52:02 +02:00
pthread_mutex_lock(&download->access_mutex);
building_status = download->building;
pthread_mutex_unlock(&download->access_mutex);
switch (building_status)
{
case 1:
return JOININPROGRESS;
case 0:
return JOINDONE;
case -1:
return JOINERR;
default:
proz_die("Bad building falg in download structure");
}
return -1;
2010-08-31 03:50:41 +02:00
}
float proz_download_get_file_build_percentage(download_t *download)
{
float percent_done;
2010-09-01 10:52:02 +02:00
2010-08-31 03:50:41 +02:00
pthread_mutex_lock(&download->access_mutex);
2010-09-01 10:52:02 +02:00
percent_done = 100;
2010-08-31 03:50:41 +02:00
pthread_mutex_unlock(&download->access_mutex);
return percent_done;
}
void proz_download_cancel_joining_thread(download_t * download)
{
2010-09-01 10:52:02 +02:00
pthread_cancel(download->join_thread);
pthread_join(download->join_thread, NULL);
2010-08-31 03:50:41 +02:00
}
void proz_download_wait_till_end_joining_thread(download_t * download)
{
2010-09-01 10:52:02 +02:00
pthread_join(download->join_thread, NULL);
2010-08-31 03:50:41 +02:00
}