← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3082: fix [L#668548] as well as implement chunked transfer encoding per HTTP/1.1 Specification

 

------------------------------------------------------------
revno: 3082
author: Crise
committer: iceman50 <bdcdevel@xxxxxxxxx>
branch nick: dcplusplus
timestamp: Sat 2012-10-20 11:10:39 -0500
message:
  fix [L#668548] as well as implement chunked transfer encoding per HTTP/1.1 Specification
modified:
  dcpp/BufferedSocket.cpp
  dcpp/FavoriteManager.cpp
  dcpp/FavoriteManager.h
  dcpp/HttpConnection.cpp
  dcpp/HttpConnection.h
  dcpp/HttpConnectionListener.h
  dcpp/HttpDownload.cpp
  dcpp/HttpDownload.h
  win32/AboutDlg.cpp
  win32/MainWindow.cpp
  win32/MainWindow.h


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'dcpp/BufferedSocket.cpp'
--- dcpp/BufferedSocket.cpp	2012-06-22 14:46:44 +0000
+++ dcpp/BufferedSocket.cpp	2012-10-20 16:10:39 +0000
@@ -279,6 +279,7 @@
 						if(dataBytes == 0) {
 							mode = MODE_LINE;
 							fire(BufferedSocketListener::ModeChange());
+							break; // break loop, in case setDataMode is called with less than read buffer size
 						}
 					}
 				}

=== modified file 'dcpp/FavoriteManager.cpp'
--- dcpp/FavoriteManager.cpp	2012-07-13 19:41:18 +0000
+++ dcpp/FavoriteManager.cpp	2012-10-20 16:10:39 +0000
@@ -790,6 +790,8 @@
 	bool parseSuccess = false;
 	c->removeListener(this);
 	if(useHttp) {
+		if(c->getMimeType() == "application/x-bzip2")
+			listType = TYPE_BZIP2;
 		parseSuccess = onHttpFinished(true);
 	}	
 	running = false;
@@ -801,14 +803,6 @@
 	if(useHttp)
 		fire(FavoriteManagerListener::DownloadStarting(), aLine);
 }
-void FavoriteManager::on(TypeNormal, HttpConnection*) noexcept {
-	if(useHttp)
-		listType = TYPE_NORMAL;
-}
-void FavoriteManager::on(TypeBZ2, HttpConnection*) noexcept {
-	if(useHttp)
-		listType = TYPE_BZIP2;
-}
 void FavoriteManager::on(Retried, HttpConnection*, bool connected) noexcept {
 	if(connected)
 		downloadBuf.clear();

=== modified file 'dcpp/FavoriteManager.h'
--- dcpp/FavoriteManager.h	2012-05-15 23:26:22 +0000
+++ dcpp/FavoriteManager.h	2012-10-20 16:10:39 +0000
@@ -165,8 +165,6 @@
 	void on(Failed, HttpConnection*, const string&) noexcept;
 	void on(Complete, HttpConnection*, const string&, bool) noexcept;
 	void on(Redirected, HttpConnection*, const string&) noexcept;
-	void on(TypeNormal, HttpConnection*) noexcept;
-	void on(TypeBZ2, HttpConnection*) noexcept;
 	void on(Retried, HttpConnection*, bool) noexcept; 
 
 	bool onHttpFinished(bool fromHttp) noexcept;

=== modified file 'dcpp/HttpConnection.cpp'
--- dcpp/HttpConnection.cpp	2012-07-13 18:50:28 +0000
+++ dcpp/HttpConnection.cpp	2012-10-20 16:10:39 +0000
@@ -28,11 +28,12 @@
 
 static const std::string CORAL_SUFFIX = ".nyud.net";
 
-HttpConnection::HttpConnection(bool coralize) :
-ok(false),
+HttpConnection::HttpConnection(bool coralize, const string& aUserAgent) :
+userAgent(aUserAgent),
 port("80"),
 size(-1),
-moved302(false),
+done(0),
+connState(CONN_UNKNOWN),
 coralizeState(coralize ? CST_DEFAULT : CST_NOCORALIZE),
 socket(0)
 {
@@ -40,8 +41,7 @@
 
 HttpConnection::~HttpConnection() {
 	if(socket) {
-		socket->removeListener(this);
-		BufferedSocket::putSocket(socket);
+		abortRequest(true);
 	}
 }
 
@@ -52,21 +52,48 @@
  * @param aUrl Full URL of file
  * @return A string with the content, or empty if download failed
  */
-void HttpConnection::downloadFile(const string& aUrl) {
-	dcassert(Util::findSubString(aUrl, "http://";) == 0);
+void HttpConnection::downloadFile(const string& aFile) {
+	currentUrl = aFile;
+	prepareRequest(TYPE_GET);
+}
+
+/**
+ * Initiates a basic urlencoded form submission
+ * @param aFile Fully qualified file URL
+ * @param aData StringMap with the args and values
+ */
+void HttpConnection::postData(const string& aUrl, const StringMap& aData) {
 	currentUrl = aUrl;
+	coralizeState = CST_NOCORALIZE;
+	requestBody.clear();
+
+	for(StringMap::const_iterator i = aData.begin(); i != aData.end(); ++i)
+		requestBody += "&" + Util::encodeURI(i->first) + "=" + Util::encodeURI(i->second);
+
+	if (!requestBody.empty()) requestBody = requestBody.substr(1);
+	prepareRequest(TYPE_POST);
+}
+
+void HttpConnection::prepareRequest(RequestType type) {
+	dcassert(Util::findSubString(currentUrl, "http://";) == 0 || Util::findSubString(currentUrl, "https://";) == 0);
 	Util::sanitizeUrl(currentUrl);
 
-	// reset all settings (as in constructor), moved here from onLine(302) because ok was not reset properly
-	moved302 = false;
-	ok = false;
+	// Reset the connection states
+	if(connState == CONN_OK || connState == CONN_FAILED) 
+		userAgent.clear();
+
 	size = -1;
+	done = 0;
+	connState = CONN_UNKNOWN;
+	connType = type;
+
+	// method selection
+	method = (connType == TYPE_POST) ? "POST" : "GET";
+
 	// set download type
-	if(Util::stricmp(currentUrl.substr(currentUrl.size() - 4), ".bz2") == 0) {
-		fire(HttpConnectionListener::TypeBZ2(), this);
-	} else {
-		fire(HttpConnectionListener::TypeNormal(), this);
-	}
+	if(stricmp(currentUrl.substr(currentUrl.size() - 4).c_str(), ".bz2") == 0) {
+		mimeType = "application/x-bzip2";
+	} else mimeType.clear();
 
 	string proto, query, fragment;
 	if(SETTING(HTTP_PROXY).empty()) {
@@ -87,23 +114,37 @@
 		} else {
 			coralizeState = CST_NOCORALIZE;
 		}
-
 	}
 
 	if(port.empty())
 		port = "80";
 
-	if(!socket) {
+	if(userAgent.empty())
+		userAgent = dcpp::fullVersionString;
+
+	if(!socket)
 		socket = BufferedSocket::getSocket(0x0a);
-	}
+
+
 	socket->addListener(this);
 	try {
-		socket->connect(server, port, false, false, false);
+		socket->connect(server, port, (proto == "https"), true, false);
 	} catch(const Exception& e) {
 		fire(HttpConnectionListener::Failed(), this, e.getError() + " (" + currentUrl + ")");
+		connState = CONN_FAILED;
 	}
 }
 
+void HttpConnection::abortRequest(bool disconnect) {
+	dcassert(socket);
+
+	socket->removeListener(this);
+	if(disconnect) socket->disconnect();
+
+	BufferedSocket::putSocket(socket);
+	socket = NULL;
+}
+
 void HttpConnection::on(BufferedSocketListener::Connected) noexcept {
 	dcassert(socket);
 	socket->write("GET " + file + " HTTP/1.1\r\n");
@@ -115,86 +156,108 @@
 		string tfile, tport, proto, query, fragment;
 		Util::decodeUrl(file, proto, sRemoteServer, tport, tfile, query, fragment);
 	}
+
 	socket->write("Host: " + sRemoteServer + "\r\n");
 	socket->write("Connection: close\r\n");	// we'll only be doing one request
 	socket->write("Cache-Control: no-cache\r\n\r\n");
+	if (connType == TYPE_POST) socket->write(requestBody);
 	if (coralizeState == CST_DEFAULT) coralizeState = CST_CONNECTED;
 }
 
 void HttpConnection::on(BufferedSocketListener::Line, const string& aLine) noexcept {
-	if(!ok) {
-		dcdebug("%s\n",aLine.c_str());
-		if(aLine.find("200") == string::npos) {
-			if(aLine.find("301") != string::npos || aLine.find("302") != string::npos){
-				moved302 = true;
+	if(connState == CONN_CHUNKED && aLine.size() > 1) {
+		string::size_type i;
+		string chunkSizeStr;
+		if((i = aLine.find(";")) == string::npos) {
+			chunkSizeStr = aLine.substr(0, aLine.length() - 1);
+		} else chunkSizeStr = aLine.substr(0, i);
+
+		unsigned long chunkSize = strtoul(chunkSizeStr.c_str(), NULL, 16);
+		if(chunkSize == 0 || chunkSize == ULONG_MAX) {
+			abortRequest(true);
+
+			if(chunkSize == 0) {
+				fire(HttpConnectionListener::Complete(), this, currentUrl, SETTING(CORAL) && coralizeState != CST_NOCORALIZE);
+				connState = CONN_OK;
 			} else {
-				socket->disconnect();
-				socket->removeListener(this);
-				BufferedSocket::putSocket(socket);
-				socket = NULL;
-				if(SETTING(CORAL) && coralizeState != CST_NOCORALIZE) {
-					fire(HttpConnectionListener::Retried(), this, coralizeState == CST_CONNECTED);		
-					coralizeState = CST_NOCORALIZE;
-					dcdebug("HTTP error with Coral, retrying : %s\n",currentUrl.c_str());
-					downloadFile(currentUrl);
-					return;
-				}
-				fire(HttpConnectionListener::Failed(), this, str(F_("%1% (%2%)") % aLine % currentUrl));
-				coralizeState = CST_DEFAULT;
+				fire(HttpConnectionListener::Failed(), this, "Transfer-encoding error (" + currentUrl + ")");
+				connState = CONN_FAILED;
+			}
+
+			coralizeState = CST_DEFAULT;
+		} else socket->setDataMode(chunkSize);
+	} else if(connState == CONN_UNKNOWN) {
+		if(aLine.find("200") != string::npos) {
+			connState = CONN_OK;
+		} else if(aLine.find("301") != string::npos || aLine.find("302") != string::npos) {
+			connState = CONN_MOVED; 
+		} else {
+			abortRequest(true);
+
+			if(SETTING(CORAL) && coralizeState != CST_NOCORALIZE) {
+				fire(HttpConnectionListener::Retried(), this, coralizeState == CST_CONNECTED);		
+				coralizeState = CST_NOCORALIZE;
+				dcdebug("HTTP error with Coral, retrying : %s\n",currentUrl.c_str());
+				downloadFile(currentUrl);
 				return;
 			}
+		
+			fire(HttpConnectionListener::Failed(), this, str(F_("%1% (%2%)") % aLine % currentUrl));
+			connState = CONN_FAILED;
+			coralizeState = CST_DEFAULT;
 		}
-		ok = true;
-	} else if(moved302 && Util::findSubString(aLine, "Location") != string::npos){
-		dcassert(socket);
-		socket->removeListener(this);
-		socket->disconnect();
-		BufferedSocket::putSocket(socket);
-		socket = NULL;
+	} else if(connState == CONN_MOVED && Util::findSubString(aLine, "Location") != string::npos) {
+		abortRequest(true);
 
-		string location302 = aLine.substr(10, aLine.length() - 10);
-		Util::sanitizeUrl(location302);
+		string location = aLine.substr(10, aLine.length() - 10);
+		Util::sanitizeUrl(location);
 
 		// make sure we can also handle redirects with relative paths
-		if(Util::strnicmp(location302.c_str(), "http://";, 7) != 0) {
-			if(location302[0] == '/') {
+		if(location.find("://") == string::npos) {
+			if(location[0] == '/') {
 				string proto, query, fragment;
 				Util::decodeUrl(currentUrl, proto, server, port, file, query, fragment);
-				string tmp = "http://"; + server;
-				if(port != "80")
+				string tmp = proto + "://" + server;
+				if(port != "80" || port != "443")
 					tmp += ':' + port;
-				location302 = tmp + location302;
+				location = tmp + location;
 			} else {
 				string::size_type i = currentUrl.rfind('/');
 				dcassert(i != string::npos);
-				location302 = currentUrl.substr(0, i + 1) + location302;
+				location = currentUrl.substr(0, i + 1) + location;
 			}
 		}
 
-		if(location302 == currentUrl) {
+		if(location == currentUrl) {
+			connState = CONN_FAILED;
 			fire(HttpConnectionListener::Failed(), this, str(F_("Endless redirection loop (%1%)") % currentUrl));
 			return;
 		}
 
-		fire(HttpConnectionListener::Redirected(), this, location302);
-
-		coralizeState = CST_DEFAULT;
-		downloadFile(location302);
-
-	} else if(aLine == "\x0d") {
-		socket->setDataMode(size);
+		fire(HttpConnectionListener::Redirected(), this, location);
+
+		if (coralizeState != CST_NOCORALIZE)
+			coralizeState = CST_DEFAULT;
+
+		downloadFile(location);
+	} else if(aLine[0] == 0x0d) {
+		if(size != -1) {
+			socket->setDataMode(size);
+		} else connState = CONN_CHUNKED;
 	} else if(Util::findSubString(aLine, "Content-Length") != string::npos) {
 		size = Util::toInt(aLine.substr(16, aLine.length() - 17));
-	} else if(Util::findSubString(aLine, "Content-Encoding") != string::npos) {
-		if(aLine.substr(18, aLine.length() - 19) == "x-bzip2")
-			fire(HttpConnectionListener::TypeBZ2(), this);
+	} else if(mimeType.empty()) {
+		if(Util::findSubString(aLine, "Content-Encoding") != string::npos) {
+			if(aLine.substr(18, aLine.length() - 19) == "x-bzip2")
+				mimeType = "application/x-bzip2";
+		} else if(Util::findSubString(aLine, "Content-Type") != string::npos) {
+			mimeType = aLine.substr(14, aLine.length() - 15);
+		}
 	}
 }
 
 void HttpConnection::on(BufferedSocketListener::Failed, const string& aLine) noexcept {
-	socket->removeListener(this);
-	BufferedSocket::putSocket(socket);
-	socket = NULL;
+	abortRequest(false);
 	if(SETTING(CORAL) && coralizeState != CST_NOCORALIZE) {
 		fire(HttpConnectionListener::Retried(), this, coralizeState == CST_CONNECTED);
 		coralizeState = CST_NOCORALIZE;
@@ -202,20 +265,32 @@
 		downloadFile(currentUrl);
 		return;
 	}
+
+	connState = CONN_FAILED;
 	coralizeState = CST_DEFAULT;
 	fire(HttpConnectionListener::Failed(), this, str(F_("%1% (%2%)") % aLine % currentUrl));
 }
 
 void HttpConnection::on(BufferedSocketListener::ModeChange) noexcept {
-	socket->removeListener(this);
-	socket->disconnect();
-	BufferedSocket::putSocket(socket);
-	socket = NULL;
-	fire(HttpConnectionListener::Complete(), this, currentUrl, SETTING(CORAL) && coralizeState != CST_NOCORALIZE);
-	coralizeState = CST_DEFAULT;
+	if(connState != CONN_CHUNKED) {
+		abortRequest(true);
+
+		fire(HttpConnectionListener::Complete(), this, currentUrl, SETTING(CORAL) && coralizeState != CST_NOCORALIZE);
+		coralizeState = CST_DEFAULT;
+	}
 }
 void HttpConnection::on(BufferedSocketListener::Data, uint8_t* aBuf, size_t aLen) noexcept {
+	if(size != -1 && static_cast<size_t>(size - done)  < aLen) {
+		abortRequest(true);
+
+		connState = CONN_FAILED;
+		coralizeState = CST_DEFAULT;
+		fire(HttpConnectionListener::Failed(), this, "Too much data in response body (" + currentUrl + ")");
+		return;
+	}
+
 	fire(HttpConnectionListener::Data(), this, aBuf, aLen);
+	done += aLen;
 }
 
 } // namespace dcpp

=== modified file 'dcpp/HttpConnection.h'
--- dcpp/HttpConnection.h	2012-01-13 20:55:20 +0000
+++ dcpp/HttpConnection.h	2012-10-20 16:10:39 +0000
@@ -22,6 +22,7 @@
 #include "BufferedSocketListener.h"
 #include "HttpConnectionListener.h"
 #include "Speaker.h"
+#include "Util.h"
 
 namespace dcpp {
 
@@ -30,25 +31,43 @@
 class HttpConnection : BufferedSocketListener, public Speaker<HttpConnectionListener>, boost::noncopyable
 {
 public:
-	HttpConnection(bool coralize = true);
+	HttpConnection(bool coralize = true, const string& aUserAgent = Util::emptyString);
 	virtual ~HttpConnection();
 
 	void downloadFile(const string& aUrl);
+	void postData(const string& aUrl, const StringMap& aData);
+
+	const string& getMimeType() const { return mimeType; }
+
+	int64_t getSize() const { return size; }
+	int64_t getDone() const { return done; }
 
 private:
-	enum CoralizeState { CST_DEFAULT, CST_CONNECTED, CST_NOCORALIZE };
-
-	string currentUrl;
-	string file;
-	string server;
-	bool ok;
-	string port;
-	int64_t size;
-	bool moved302;
-
-	CoralizeState coralizeState;
-
-	BufferedSocket* socket;
+	enum RequestType { TYPE_GET, TYPE_POST };
+	enum ConnectionStates { CONN_UNKNOWN, CONN_OK, CONN_FAILED, CONN_MOVED, CONN_CHUNKED };
+	enum CoralizeStates { CST_DEFAULT, CST_CONNECTED, CST_NOCORALIZE };
+
+	string currentUrl;
+	string userAgent;
+	string method;
+	string file;
+	string server;
+	string port;
+
+	string requestBody;
+
+	string mimeType;
+	int64_t size;
+	int64_t done;
+
+	ConnectionStates connState;
+	CoralizeStates coralizeState;
+	RequestType connType;
+
+	BufferedSocket* socket;
+
+	void prepareRequest(RequestType type);
+	void abortRequest(bool disconnect);
 
 	// BufferedSocketListener
 	void on(Connected) noexcept;
@@ -56,9 +75,6 @@
 	void on(Data, uint8_t*, size_t) noexcept;
 	void on(ModeChange) noexcept;
 	void on(Failed, const string&) noexcept;
-
-	void onConnected();
-	void onLine(const string& aLine);
 };
 
 } // namespace dcpp

=== modified file 'dcpp/HttpConnectionListener.h'
--- dcpp/HttpConnectionListener.h	2012-01-13 20:55:20 +0000
+++ dcpp/HttpConnectionListener.h	2012-10-20 16:10:39 +0000
@@ -36,16 +36,12 @@
 	typedef X<1> Failed;
 	typedef X<2> Complete;
 	typedef X<3> Redirected;
-	typedef X<4> TypeNormal;
-	typedef X<5> TypeBZ2;
-	typedef X<6> Retried;
+	typedef X<4> Retried;
 
 	virtual void on(Data, HttpConnection*, const uint8_t*, size_t) noexcept = 0;
 	virtual void on(Failed, HttpConnection*, const string&) noexcept = 0;
 	virtual void on(Complete, HttpConnection*, const string&, bool) noexcept = 0;
 	virtual void on(Redirected, HttpConnection*, const string&) noexcept { }
-	virtual void on(TypeNormal, HttpConnection*) noexcept { }
-	virtual void on(TypeBZ2, HttpConnection*) noexcept { }
 	virtual void on(Retried, HttpConnection*, bool) noexcept { }
 };
 

=== modified file 'dcpp/HttpDownload.cpp'
--- dcpp/HttpDownload.cpp	2012-01-13 20:55:20 +0000
+++ dcpp/HttpDownload.cpp	2012-10-20 16:10:39 +0000
@@ -21,31 +21,41 @@
 
 namespace dcpp {
 
-HttpDownload::HttpDownload(const string& address, CompletionF f, bool coralize) :
+HttpDownload::HttpDownload(const string& address, CompletionFunc f, bool coralize) :
 c(coralize),
-f(f)
+f(f),
+buf("")
 {
 	c.addListener(this);
 	c.downloadFile(address);
 }
 
+HttpDownload::HttpDownload(const string& address, const StringMap& data, CompletionFunc f, bool coralize) :
+c(coralize),
+f(f),
+buf("")
+{
+	c.addListener(this);
+	c.postData(address, data);
+}
+
 HttpDownload::~HttpDownload() {
 	c.removeListener(this);
 }
 
-void HttpDownload::on(HttpConnectionListener::Data, HttpConnection*, const uint8_t* buf_, size_t len) noexcept {
+void HttpDownload::on(HttpConnectionListener::Data, HttpConnection* c, const uint8_t* buf_, size_t len) noexcept {
+	if(buf.empty() && c->getSize() != -1)
+		buf.reserve(c->getSize());
 	buf.append(reinterpret_cast<const char*>(buf_), len);
 }
 
-void HttpDownload::on(HttpConnectionListener::Failed, HttpConnection*, const string& status_) noexcept {
+void HttpDownload::on(HttpConnectionListener::Failed, HttpConnection*, const string& line) noexcept {
 	buf.clear();
-	status = status_;
-	f();
+	f(false, line);
 }
 
-void HttpDownload::on(HttpConnectionListener::Complete, HttpConnection*, const string& status_, bool) noexcept {
-	status = status_;
-	f();
+void HttpDownload::on(HttpConnectionListener::Complete, HttpConnection*, const string& line, bool) noexcept {
+	f(true, buf);
 }
 
 void HttpDownload::on(HttpConnectionListener::Retried, HttpConnection*, bool connected) noexcept {

=== modified file 'dcpp/HttpDownload.h'
--- dcpp/HttpDownload.h	2012-01-13 20:55:20 +0000
+++ dcpp/HttpDownload.h	2012-10-20 16:10:39 +0000
@@ -27,19 +27,21 @@
 
 /** Helper struct to manage a single HTTP download. Calls a completion function when finished. */
 struct HttpDownload : private HttpConnectionListener, private boost::noncopyable {
+	typedef std::function<void (bool success, const string& result)> CompletionFunc;
+	
+	explicit HttpDownload(const string& address, CompletionFunc f, bool coralize = true);
+	explicit HttpDownload(const string& address, const StringMap& data, CompletionFunc f, bool coralize = true);
+	~HttpDownload();
+
+private:
 	HttpConnection c;
 	string buf;
-	string status;
-	typedef std::function<void ()> CompletionF;
-	CompletionF f;
-
-	explicit HttpDownload(const string& address, CompletionF f, bool coralize = true);
-	~HttpDownload();
+	CompletionFunc f;
 
 	// HttpConnectionListener
-	void on(HttpConnectionListener::Data, HttpConnection*, const uint8_t* buf_, size_t len) noexcept;
-	void on(HttpConnectionListener::Failed, HttpConnection*, const string& status_) noexcept;
-	void on(HttpConnectionListener::Complete, HttpConnection*, const string& status_, bool) noexcept;
+	void on(HttpConnectionListener::Data, HttpConnection* c, const uint8_t* buf_, size_t len) noexcept;
+	void on(HttpConnectionListener::Failed, HttpConnection*, const string& line) noexcept;
+	void on(HttpConnectionListener::Complete, HttpConnection*, const string& line, bool) noexcept;
 	void on(HttpConnectionListener::Retried, HttpConnection*, bool connected) noexcept;
 };
 

=== modified file 'win32/AboutDlg.cpp'
--- win32/AboutDlg.cpp	2012-10-11 16:37:31 +0000
+++ win32/AboutDlg.cpp	2012-10-20 16:10:39 +0000
@@ -152,7 +152,7 @@
 	centerWindow();
 
 	c.reset(new HttpDownload("http://dcplusplus.sourceforge.net/version.xml";,
-		[this] { callAsync([=] { completeDownload(); }); }));
+		[this](bool success, const string& result) { callAsync([=] { completeDownload(success, result); }); }));
 
 	return false;
 }
@@ -162,13 +162,13 @@
 	grid->resize(dwt::Rectangle(3, 3, sz.x - 6, sz.y - 6));
 }
 
-void AboutDlg::completeDownload() {
+void AboutDlg::completeDownload(bool success, const string& result) {
 	tstring str;
 
-	if(!c->buf.empty()) {
+	if(success && !result.empty()) {
 		try {
 			SimpleXML xml;
-			xml.fromXML(c->buf);
+			xml.fromXML(result);
 			if(xml.findChild("DCUpdate")) {
 				xml.stepIn();
 				if(xml.findChild("Version")) {
@@ -183,7 +183,7 @@
 		}
 	}
 
-	version->setText(str.empty() ? Text::toT(c->status) : str);
+	version->setText(str.empty() ? Text::toT(result) : str);
 
 	c.reset();
 }

=== modified file 'win32/MainWindow.cpp'
--- win32/MainWindow.cpp	2012-09-13 19:56:10 +0000
+++ win32/MainWindow.cpp	2012-10-20 16:10:39 +0000
@@ -174,7 +174,7 @@
 	TimerManager::getInstance()->start();
 
 	conns[CONN_VERSION].reset(new HttpDownload("http://dcplusplus.sourceforge.net/version.xml";,
-		[this] { callAsync([=] { completeVersionUpdate(); }); }));
+		[this](bool success, const string& result) { callAsync([=] { completeVersionUpdate(success, result); }); }));
 
 	try {
 		ConnectivityManager::getInstance()->setup(true);
@@ -1308,12 +1308,12 @@
 	}
 }
 
-void MainWindow::completeVersionUpdate() {
+void MainWindow::completeVersionUpdate(bool success, const string& result) {
 	if(!conns[CONN_VERSION]) { return; }
 
 	try {
 		SimpleXML xml;
-		xml.fromXML(conns[CONN_VERSION]->buf);
+		xml.fromXML(result);
 		xml.stepIn();
 
 		string url = Text::fromT(links.homepage);
@@ -1456,17 +1456,17 @@
 
 	LogManager::getInstance()->message(str(F_("Updating the %1% GeoIP database...") % (v6 ? "IPv6" : "IPv4")));
 	conn.reset(new HttpDownload(Text::fromT(v6 ? links.geoip6 : links.geoip4),
-		[this, v6] { callAsync([=] { completeGeoUpdate(v6); }); }, false));
+		[this, v6](bool success, const string& result) { callAsync([=] { completeGeoUpdate(v6, success, result); }); }, false));
 }
 
-void MainWindow::completeGeoUpdate(bool v6) {
+void MainWindow::completeGeoUpdate(bool v6, bool success, const string& result) {
 	auto& conn = conns[v6 ? CONN_GEO_V6 : CONN_GEO_V4];
 	if(!conn) { return; }
 	ScopedFunctor([&conn] { conn.reset(); });
 
-	if(!conn->buf.empty()) {
+	if(success && !result.empty()) {
 		try {
-			File(GeoManager::getDbPath(v6) + ".gz", File::WRITE, File::CREATE | File::TRUNCATE).write(conn->buf);
+			File(GeoManager::getDbPath(v6) + ".gz", File::WRITE, File::CREATE | File::TRUNCATE).write(result);
 			GeoManager::getInstance()->update(v6);
 			LogManager::getInstance()->message(str(F_("The %1% GeoIP database has been successfully updated") % (v6 ? "IPv6" : "IPv4")));
 			return;

=== modified file 'win32/MainWindow.h'
--- win32/MainWindow.h	2012-06-18 16:32:14 +0000
+++ win32/MainWindow.h	2012-10-20 16:10:39 +0000
@@ -206,12 +206,12 @@
 	void fillLimiterMenu(Menu* menu, bool upload);
 	void statusMessage(time_t t, const string& m);
 
-	void completeVersionUpdate();
+	void completeVersionUpdate(bool success, const string& result);
 	void checkGeoUpdate();
 	void checkGeoUpdate(bool v6);
 	void updateGeo();
 	void updateGeo(bool v6);
-	void completeGeoUpdate(bool v6);
+	void completeGeoUpdate(bool v6, bool success, const string& result);
 
 	bool filter(MSG& msg);