@ -74,6 +74,18 @@ struct download_list_call {
Download * m_download ;
} ;
DownloadList : : ~ DownloadList ( ) {
std : : for_each ( begin ( ) , end ( ) , std : : bind1st ( std : : mem_fun ( & DownloadList : : close ) , this ) ) ;
std : : for_each ( begin ( ) , end ( ) , rak : : call_delete < Download > ( ) ) ;
base_type : : clear ( ) ;
}
void
DownloadList : : session_save ( ) {
std : : for_each ( begin ( ) , end ( ) , std : : bind1st ( std : : mem_fun ( & DownloadStore : : save ) , control - > core ( ) - > download_store ( ) ) ) ;
}
Download *
DownloadList : : create ( std : : istream * str , bool printLog ) {
torrent : : Object * object = new torrent : : Object ;
@ -113,7 +125,7 @@ DownloadList::insert(Download* download) {
} catch ( torrent : : local_error & e ) {
// Should perhaps relax this, just print an error and remove the
// downloads?
throw torrent : : internal_error ( " Caught during DownloadList::insert part 2 : " + std : : string ( e . what ( ) ) ) ;
throw torrent : : internal_error ( " Caught during DownloadList::insert(...) : " + std : : string ( e . what ( ) ) ) ;
}
return itr ;
@ -131,9 +143,9 @@ DownloadList::erase(iterator itr) {
if ( itr = = end ( ) )
throw torrent : : internal_error ( " DownloadList::erase(...) could not find download. " ) ;
// Make safe to erase active downloads.
if ( ( * itr ) - > download ( ) - > is_active ( ) )
throw torrent : : internal_error ( " DownloadList::erase(...) called on an active download. " ) ;
close ( * itr ) ;
control - > core ( ) - > download_store ( ) - > remove ( * itr ) ;
std : : for_each ( m_slotMapErase . begin ( ) , m_slotMapErase . end ( ) , download_list_call ( * itr ) ) ;
@ -187,7 +199,14 @@ DownloadList::close_throw(Download* download) {
if ( download - > download ( ) - > is_active ( ) )
pause ( download ) ;
// Save the torrent on close, this covers shutdown and if a torrent
// is manually closed which would clear the progress data. For
// better crash protection, save regulary in addition to this.
//
// Used to be in pause, but this was wrong for rehashing etc.
control - > core ( ) - > download_store ( ) - > save ( download ) ;
control - > core ( ) - > hash_queue ( ) - > remove ( download ) ;
download - > download ( ) - > close ( ) ;
std : : for_each ( m_slotMapClose . begin ( ) , m_slotMapClose . end ( ) , download_list_call ( download ) ) ;
@ -220,28 +239,43 @@ DownloadList::resume(Download* download) {
if ( download - > download ( ) - > is_active ( ) )
return ;
open_throw ( download ) ;
// Properly escape when resume get's called during hashing. The
// 'state' is changed by the call to DownloadList::start so it
// will automagically start afterwards.
if ( control - > core ( ) - > hash_queue ( ) - > find ( download ) ! = control - > core ( ) - > hash_queue ( ) - > end ( ) )
return ;
if ( download - > download ( ) - > is_hash_checked ( ) ) {
download - > variable ( ) - > set ( " state_changed " , cachedTime . seconds ( ) ) ;
if ( download - > is_done ( ) )
download - > set_connection_type ( download - > variable ( ) - > get_string ( " connection_seed " ) ) ;
else
download - > set_connection_type ( download - > variable ( ) - > get_string ( " connection_leech " ) ) ;
open_throw ( download ) ;
// Update the priority to ensure it has the correct
// seeding/unfinished modifiers.
download - > set_priority ( download - > priority ( ) ) ;
download - > download ( ) - > start ( ) ;
// Manual or end-of-download rehashing clears the resume data so
// we can just start the hashing again without clearing it again.
//
// It is also assumed the is_hash_checked flag gets cleared when
// 'hashing' was set.
if ( ! download - > download ( ) - > is_hash_checked ( ) ) {
std : : for_each ( m_slotMapStart . begin ( ) , m_slotMapStart . end ( ) , download_list_call ( download ) ) ;
// Set 'hashing' to started if hashing wasn't started, else keep
// the old value.
if ( download - > variable ( ) - > get_value ( " hashing " ) = = Download : : variable_hashing_stopped )
download - > variable ( ) - > set ( " hashing " , Download : : variable_hashing_started ) ;
} else {
// TODO: This can cause infinit looping?
control - > core ( ) - > hash_queue ( ) - > insert ( download ) ;
return ;
}
download - > variable ( ) - > set ( " state_changed " , cachedTime . seconds ( ) ) ;
if ( download - > is_done ( ) )
download - > set_connection_type ( download - > variable ( ) - > get_string ( " connection_seed " ) ) ;
else
download - > set_connection_type ( download - > variable ( ) - > get_string ( " connection_leech " ) ) ;
// Update the priority to ensure it has the correct
// seeding/unfinished modifiers.
download - > set_priority ( download - > priority ( ) ) ;
download - > download ( ) - > start ( ) ;
std : : for_each ( m_slotMapStart . begin ( ) , m_slotMapStart . end ( ) , download_list_call ( download ) ) ;
} catch ( torrent : : local_error & e ) {
control - > core ( ) - > push_log ( e . what ( ) ) ;
@ -254,6 +288,10 @@ DownloadList::pause(Download* download) {
try {
// Make sure we don't start hash checking a download that we won't
// start.
control - > core ( ) - > hash_queue ( ) - > remove ( download ) ;
if ( ! download - > download ( ) - > is_active ( ) )
return ;
@ -266,7 +304,7 @@ DownloadList::pause(Download* download) {
// Save the state after all the slots, etc have been called so we
// include the modifications they may make.
control - > core ( ) - > download_store ( ) . save ( download ) ;
//control->core()->download_store()->save(download);
} catch ( torrent : : local_error & e ) {
control - > core ( ) - > push_log ( e . what ( ) ) ;
@ -274,10 +312,8 @@ DownloadList::pause(Download* download) {
}
void
DownloadList : : clear ( ) {
std : : for_each ( begin ( ) , end ( ) , rak : : call_delete < Download > ( ) ) ;
base_type : : clear ( ) ;
DownloadList : : save ( Download * d ) {
}
void
@ -286,17 +322,28 @@ DownloadList::check_hash(Download* download) {
try {
close_throw ( download ) ;
download - > download ( ) - > hash_resume_clear ( ) ;
open_throw ( download ) ;
control - > core ( ) - > hash_queue ( ) - > insert ( download ) ;
download - > variable ( ) - > set ( " hashing " , Download : : variable_hashing_started ) ;
check_hash_throw ( download ) ;
} catch ( torrent : : local_error & e ) {
control - > core ( ) - > push_log ( e . what ( ) ) ;
}
}
// Throw in addition to not setting 'hashing'.
void
DownloadList : : check_hash_throw ( Download * download ) {
check_contains ( download ) ;
close_throw ( download ) ;
download - > download ( ) - > hash_resume_clear ( ) ;
open_throw ( download ) ;
// If any more stuff is added here, make sure resume etc are still
// correct.
control - > core ( ) - > hash_queue ( ) - > insert ( download ) ;
}
void
DownloadList : : hash_done ( Download * download ) {
check_contains ( download ) ;
@ -313,18 +360,42 @@ DownloadList::hash_done(Download* download) {
// confirm all the data, avoiding large BW usage on f.ex. the
// ReiserFS bug with >4GB files.
// Use just is_done(), have another if statement inside.
if ( download - > is_don e( ) & & download - > variable ( ) - > get_value ( " complete " ) = = 0 ) {
int64_t hashing = download - > variable ( ) - > get_value ( " hashing " ) ;
download - > variabl e( ) - > set ( " hashing " , Download : : variable_hashing_stopped ) ;
if ( control - > variable ( ) - > get_value ( " session_on_completion " ) )
control - > core ( ) - > download_store ( ) . save ( download ) ;
switch ( hashing ) {
case Download : : variable_hashing_started :
// Normal re/hashing.
// Send a "truly finished message from here.
confirm_finished ( download ) ;
}
if ( download - > is_done ( ) )
download - > variable ( ) - > set ( " complete " , ( int64_t ) 1 ) ;
if ( download - > variable ( ) - > get_value ( " state " ) = = 1 )
resume ( download ) ;
return ;
case Download : : variable_hashing_last :
if ( download - > variable ( ) - > get_value ( " state " ) = = 1 )
resume ( download ) ;
if ( download - > is_done ( ) ) {
confirm_finished ( download ) ;
if ( download - > variable ( ) - > get_value ( " state " ) = = 1 )
resume ( download ) ;
} else {
download - > set_message ( " Hash check on download completion found bad chunks. " ) ;
}
return ;
case Download : : variable_hashing_stopped :
default :
// Either an error or someone wrote to the hashing variable...
download - > set_message ( " Hash check completed but the \" hashing \" variable is in an invalid state. " ) ;
return ;
}
}
void
@ -335,13 +406,15 @@ DownloadList::received_finished(Download* download) {
// Set some 'checking_finished_thingie' variable to make hash_done
// trigger correctly, also so it can bork on missing data.
check_hash ( download ) ;
download - > variable ( ) - > set ( " hashing " , Download : : variable_hashing_last ) ;
check_hash_throw ( download ) ;
} else {
confirm_finished ( download ) ;
}
}
// The download must be open when we call this function.
void
DownloadList : : confirm_finished ( Download * download ) {
check_contains ( download ) ;
@ -354,6 +427,13 @@ DownloadList::confirm_finished(Download* download) {
download - > download ( ) - > tracker_list ( ) . send_completed ( ) ;
// Do this before the slots are called in case one of them closes
// the download.
if ( ! download - > is_active ( ) & & control - > variable ( ) - > get_value ( " session_on_completion " ) = = 1 ) {
download - > download ( ) - > hash_resume_save ( ) ;
control - > core ( ) - > download_store ( ) - > save ( download ) ;
}
std : : for_each ( m_slotMapFinished . begin ( ) , m_slotMapFinished . end ( ) , download_list_call ( download ) ) ;
}