Bug 261449

Summary: Loaded quantities with webseeds do not match
Product: [Applications] ktorrent Reporter: Axel Braun <axel.braun>
Component: generalAssignee: Joris Guisson <joris.guisson>
Status: RESOLVED FIXED    
Severity: normal    
Priority: NOR    
Version: unspecified   
Target Milestone: ---   
Platform: openSUSE   
OS: Linux   
Latest Commit: Version Fixed In:
Attachments: webseed view
File status
1.6 GB loaded on a 1.1 GB file, and still only 9% complete

Description Axel Braun 2010-12-28 14:04:19 UTC
Created attachment 55314 [details]
webseed view

Version:           unspecified (using KDE 4.4.4) 
OS:                Linux

I have currently some downloads running that use webseeds as well as torrents: The webseed claims that it has already loaded some 500 MB, while the main torrent window claims it has only loaded some 100 MB, which is approximately in line with the file status (see screenshots).
where hat the rest of the downloads gone.

Reproducible: Sometimes

Steps to Reproduce:
May this depend on the webseed?



OS: Linux (i686) release 2.6.34.7-0.5-desktop
Compiler: gcc
Comment 1 Axel Braun 2010-12-28 14:05:15 UTC
Created attachment 55315 [details]
File status
Comment 2 Joris Guisson 2010-12-28 14:10:41 UTC
Version ?
Comment 3 Axel Braun 2010-12-28 14:22:56 UTC
(In reply to comment #2)
> Version ?

ktorrent? 3.3.4
Comment 4 Axel Braun 2010-12-28 16:04:15 UTC
Created attachment 55318 [details]
1.6 GB loaded on a 1.1 GB file, and still only 9% complete

...think it does not make sense to continue with webseeds
Comment 5 Joris Guisson 2010-12-28 18:15:41 UTC
Send me the torrent
Comment 6 Joris Guisson 2010-12-29 11:29:50 UTC
Two things:
- There is a bug in the latest development code which results in the hash check always failing in this situation, this bug is not present in the version you are running, so this is not what you are seeing
- The webseed and the torrent do not match, after 99 MB they diverge, none of the data beyond 99 MB matches. The fact that you get stuck at 9 % is thus very logical.

So you need to stop using the webseed for this torrent, and I need to make it clear in the GUI that this is happening, instead of retrying continuously to download a chunk which is never gonna match.
Comment 7 Joris Guisson 2010-12-29 12:19:31 UTC
commit 6f64659c7b05cc0033b4827b37aa87a4e9f4d1da
branch master
Author: Joris <joris.guisson@gmail.com>
Date:   Wed Dec 29 12:17:26 2010 +0100

    Disable webseeds if they send data which does not match
    
    BUG: 261449

diff --git a/ChangeLog b/ChangeLog
index 7192502..78664c3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -26,6 +26,7 @@ Changes in 1.1:
 - Fix crash in PeerConnector cleanup (258878)
 - Queue data checks so that they run one after the other (215711)
 - Revamp UTP code with smart pointers
+- Disable webseeds if they send data which does not match (261449)
 
 Changes in 1.0.5:
 - Update PeerID client identifications
diff --git a/src/download/downloader.cpp b/src/download/downloader.cpp
index 78c89ac..8b55069 100644
--- a/src/download/downloader.cpp
+++ b/src/download/downloader.cpp
@@ -21,6 +21,7 @@
 
 #include <QFile>
 #include <QTextStream>
+#include <KLocale>
 #include <util/file.h>
 #include <util/log.h>
 #include <diskio/chunkmanager.h>
@@ -758,6 +759,7 @@ namespace bt
 	
 	void Downloader::onChunkReady(Chunk* c)
 	{
+		WebSeed* ws = webseeds_chunks.find(c->getIndex());
 		webseeds_chunks.erase(c->getIndex());
 		PieceData::Ptr piece = c->getPiece(0,c->getSize(),true);
 		if (piece && c->checkHash(tor.getHash(c->getIndex())))
@@ -805,6 +807,7 @@ namespace bt
 				cman.resetChunk(c->getIndex());
 			
 			chunk_selector->reinsert(c->getIndex());
+			ws->disable(i18n("Disabled because webseed does not match torrent"));
 		}
 	}
 	
diff --git a/src/download/webseed.cpp b/src/download/webseed.cpp
index 9a18291..b569ff1 100644
--- a/src/download/webseed.cpp
+++ b/src/download/webseed.cpp
@@ -103,6 +103,13 @@ namespace bt
 		reset();
 	}
 
+	void WebSeed::disable(const QString& reason)
+	{
+		setEnabled(false);
+		status = reason;
+		Out(SYS_CON|LOG_IMPORTANT) << "Auto disabled webseed " << url.prettyUrl() << endl;
+	}
+
 
 	bool WebSeed::busy() const
 	{
@@ -218,7 +225,6 @@ namespace bt
 		if (path.endsWith('/') && !isUserCreated())
 			path += tor.getNameSuggestion();
 		
-		//Out(SYS_GEN|LOG_DEBUG) << "WebSeed: continuing current chunk " << cur_chunk << " " << bytes_of_cur_chunk << endl;
 		first_chunk = cur_chunk;
 		if (tor.isMultiFile())
 		{
@@ -292,82 +298,88 @@ namespace bt
 		
 	Uint32 WebSeed::update()
 	{
-		if (!conn || !busy())
-			return 0;
-		
-		if (!conn->ok())
-		{
-			readData();
-			Out(SYS_CON|LOG_DEBUG) << "WebSeed: connection not OK" << endl;
-			// shit happened delete connection
-			status = conn->getStatusString();
-			if (conn->responseCode() == 404)
-			{
-				// if not found then retire this webseed for now
-				retryLater();
-			}
-			delete conn;
-			conn = 0;
-			chunkStopped();
-			first_chunk = last_chunk = cur_chunk = tor.getNumChunks() + 1;
-			num_failures++;
-			if (num_failures == 3)
-				retryLater();
-			return 0;
-		}
-		else if (conn->closed())
+		try
 		{
-			// Make sure we handle all data
-			readData();
+			if (!conn || !busy())
+				return 0;
 			
-			Out(SYS_CON|LOG_DEBUG) << "WebSeed: connection closed" << endl;
-			delete conn;
-			conn = 0;
-			
-			status = i18n("Connection closed");
-			chunkStopped();
-			if (last_chunk < tor.getNumChunks())
+			if (!conn->ok())
 			{
-				// lets try this again if we have not yet got the full range
-				download(cur_chunk,last_chunk);
+				readData();
+				
+				Out(SYS_CON|LOG_DEBUG) << "WebSeed: connection not OK" << endl;
+				// shit happened delete connection
 				status = conn->getStatusString();
-			}
-		}
-		else if (conn->isRedirected())
-		{
-			// Make sure we handle all data
-			readData();
-			redirected(conn->redirectedUrl());
-		}
-		else 
-		{
-			readData();
-			if (range_queue.count() > 0 && conn->ready())
-			{
-				if (conn->closed())
+				if (conn->responseCode() == 404)
 				{
-					// after a redirect it is possible that the connection is closed
-					// so we need to reconnect to the old url
-					conn->deleteLater();
-					conn = new HttpConnection();
-					conn->setGroupIDs(up_gid,down_gid);
-					connectToServer();
+					// if not found then retire this webseed for now
+					retryLater();
 				}
+				delete conn;
+				conn = 0;
+				chunkStopped();
+				first_chunk = last_chunk = cur_chunk = tor.getNumChunks() + 1;
+				num_failures++;
+				if (num_failures == 3)
+					retryLater();
+				return 0;
+			}
+			else if (conn->closed())
+			{
+				// Make sure we handle all data
+				readData();
 				
-				QString path = url.path();
-				if (path.endsWith('/'))
-					path += tor.getNameSuggestion();
+				Out(SYS_CON|LOG_DEBUG) << "WebSeed: connection closed" << endl;
+				delete conn;
+				conn = 0;
 				
-				// ask for the next range
-				Range r = range_queue[0];
-				range_queue.pop_front();
-				const TorrentFile & tf = tor.getFile(r.file);
-				QString host = redirected_url.isValid() ? redirected_url.host() : url.host();
-				conn->get(host,path + '/' + tf.getPath(),r.off,r.len);
+				status = i18n("Connection closed");
+				chunkStopped();
+				if (last_chunk < tor.getNumChunks())
+				{
+					// lets try this again if we have not yet got the full range
+					download(cur_chunk,last_chunk);
+					status = conn->getStatusString();
+				}
+			}
+			else if (conn->isRedirected())
+			{
+				// Make sure we handle all data
+				readData();
+				redirected(conn->redirectedUrl());
+			}
+			else 
+			{
+				readData();
+				if (range_queue.count() > 0 && conn->ready())
+				{
+					if (conn->closed())
+					{
+						// after a redirect it is possible that the connection is closed
+						// so we need to reconnect to the old url
+						conn->deleteLater();
+						conn = new HttpConnection();
+						conn->setGroupIDs(up_gid,down_gid);
+						connectToServer();
+					}
+					
+					QString path = url.path();
+					if (path.endsWith('/'))
+						path += tor.getNameSuggestion();
+					
+					// ask for the next range
+					Range r = range_queue[0];
+					range_queue.pop_front();
+					const TorrentFile & tf = tor.getFile(r.file);
+					QString host = redirected_url.isValid() ? redirected_url.host() : url.host();
+					conn->get(host,path + '/' + tf.getPath(),r.off,r.len);
+				}
+				status = conn->getStatusString();
 			}
-			status = conn->getStatusString();
 		}
-		
+		catch (AutoDisabled &)
+		{
+		}
 		Uint32 ret = downloaded;
 		downloaded = 0;
 		total_downloaded += ret;
@@ -397,7 +409,6 @@ namespace bt
 	
 	void WebSeed::handleData(const QByteArray & tmp)
 	{
-//		Out(SYS_GEN|LOG_DEBUG) << "Handling data: " << tmp.length() << " bytes" << endl;
 		Uint32 off = 0;
 		while (off < (Uint32)tmp.size() && cur_chunk <= last_chunk)
 		{
@@ -427,6 +438,9 @@ namespace bt
 				if (c->getStatus() != Chunk::ON_DISK)
 				{
 					chunkReady(c);
+					// It is possible that the webseed has been disabled due receiving a bad chunk
+					if (!isEnabled())
+						throw AutoDisabled();
 				}
 				
 				chunkStopped();
diff --git a/src/download/webseed.h b/src/download/webseed.h
index e0e9cc9..b4ae35a 100644
--- a/src/download/webseed.h
+++ b/src/download/webseed.h
@@ -119,6 +119,9 @@ namespace bt
 		
 		virtual void setEnabled(bool on);
 		
+		/// Disable the webseed 
+		void disable(const QString & reason);
+		
 		/// Get the number of failed attempts
 		Uint32 failedAttempts() const {return num_failures;}
 		
@@ -165,6 +168,8 @@ namespace bt
 			Uint64 len;
 		};
 		
+		class AutoDisabled {}; // Exception
+		
 		void fillRangeList(Uint32 chunk);
 		void handleData(const QByteArray & data);
 		void chunkStarted(Uint32 chunk);