← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2628: GeoIP: maintain a cache; better handle enabling/disabling

 

------------------------------------------------------------
revno: 2628
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Sat 2011-10-08 17:21:54 +0200
message:
  GeoIP: maintain a cache; better handle enabling/disabling
added:
  dcpp/GeoManager.cpp
  dcpp/GeoManager.h
modified:
  dcpp/DCPlusPlus.cpp
  dcpp/GeoIP.cpp
  dcpp/GeoIP.h
  dcpp/User.cpp
  dcpp/Util.cpp
  dcpp/Util.h
  win32/MainWindow.cpp
  win32/MainWindow.h
  win32/NotificationsPage.cpp
  win32/PublicHubsFrame.cpp
  win32/SearchFrame.cpp
  win32/TransferView.cpp


--
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/DCPlusPlus.cpp'
--- dcpp/DCPlusPlus.cpp	2011-06-19 15:35:02 +0000
+++ dcpp/DCPlusPlus.cpp	2011-10-08 15:21:54 +0000
@@ -19,25 +19,26 @@
 #include "stdinc.h"
 #include "DCPlusPlus.h"
 
+#include "ADLSearch.h"
+#include "ClientManager.h"
 #include "ConnectionManager.h"
+#include "ConnectivityManager.h"
+#include "CryptoManager.h"
 #include "DownloadManager.h"
-#include "UploadManager.h"
-#include "CryptoManager.h"
-#include "ShareManager.h"
-#include "SearchManager.h"
-#include "QueueManager.h"
-#include "ClientManager.h"
+#include "FavoriteManager.h"
+#include "FinishedManager.h"
+#include "GeoManager.h"
 #include "HashManager.h"
 #include "LogManager.h"
-#include "FavoriteManager.h"
+#include "MappingManager.h"
+#include "QueueManager.h"
+#include "ResourceManager.h"
+#include "SearchManager.h"
 #include "SettingsManager.h"
-#include "FinishedManager.h"
-#include "ResourceManager.h"
-#include "ADLSearch.h"
-#include "MappingManager.h"
+#include "ShareManager.h"
+#include "ThrottleManager.h"
+#include "UploadManager.h"
 #include "WindowManager.h"
-#include "ThrottleManager.h"
-#include "ConnectivityManager.h"
 
 #ifdef _STLP_DEBUG
 void __stl_debug_terminate() {
@@ -84,6 +85,7 @@
 	ADLSearchManager::newInstance();
 	ConnectivityManager::newInstance();
 	MappingManager::newInstance();
+	GeoManager::newInstance();
 	WindowManager::newInstance();
 
 	SettingsManager::getInstance()->load();
@@ -98,24 +100,31 @@
 	}
 #endif
 
-	if(f != NULL)
-		(*f)(p, _("Users"));
+	auto announce = [&f, &p](const string&& str) {
+		if(f)
+			(*f)(p, forward<const string>(str));
+	};
+
+	announce(_("Users"));
 	ClientManager::getInstance()->loadUsers();
 	FavoriteManager::getInstance()->load();
 
+	announce(_("Security certificates"));
 	CryptoManager::getInstance()->loadCertificates();
 
-	if(f != NULL)
-		(*f)(p, _("Hash database"));
+	announce(_("Hash database"));
 	HashManager::getInstance()->startup();
 
-	if(f != NULL)
-		(*f)(p, _("Shared Files"));
+	announce(_("Shared Files"));
 	ShareManager::getInstance()->refresh(true, false, true);
 
-	if(f != NULL)
-		(*f)(p, _("Download Queue"));
+	announce(_("Download Queue"));
 	QueueManager::getInstance()->loadQueue();
+
+	if(BOOLSETTING(GET_USER_COUNTRY)) {
+		announce(_("Country information"));
+		GeoManager::getInstance()->init();
+	}
 }
 
 void shutdown() {
@@ -124,6 +133,7 @@
 	ThrottleManager::getInstance()->shutdown();
 	ConnectionManager::getInstance()->shutdown();
 	MappingManager::getInstance()->close();
+	GeoManager::getInstance()->close();
 	BufferedSocket::waitShutdown();
 
 	WindowManager::getInstance()->prepareSave();
@@ -132,6 +142,7 @@
 	SettingsManager::getInstance()->save();
 
 	WindowManager::deleteInstance();
+	GeoManager::deleteInstance();
 	MappingManager::deleteInstance();
 	ConnectivityManager::deleteInstance();
 	ADLSearchManager::deleteInstance();

=== modified file 'dcpp/GeoIP.cpp'
--- dcpp/GeoIP.cpp	2011-10-06 18:58:07 +0000
+++ dcpp/GeoIP.cpp	2011-10-08 15:21:54 +0000
@@ -29,7 +29,12 @@
 
 namespace dcpp {
 
-GeoIP::GeoIP() : geo(0) {
+GeoIP::GeoIP(const string&& path) : geo(0), path(path) {
+	if(File::getSize(path) <= 0 && !decompress()) {
+		return;
+	}
+
+	open();
 }
 
 GeoIP::~GeoIP() {
@@ -37,9 +42,25 @@
 	close();
 }
 
-void GeoIP::init(const string& path_) {
-	path = move(path_);
-	init();
+string GeoIP::getCountry(const string& ip) const {
+	Lock l(cs);
+	if(geo) {
+		auto id = (v6() ? GeoIP_id_by_addr_v6 : GeoIP_id_by_addr)(geo, ip.c_str());
+		if(id > 0 && id < cache.size()) {
+			return cache[id];
+		}
+	}
+	return Util::emptyString;
+}
+
+void GeoIP::update() {
+	Lock l(cs);
+
+	close();
+
+	if(decompress()) {
+		open();
+	}
 }
 
 namespace {
@@ -64,13 +85,14 @@
 
 } // unnamed namespace
 
-string GeoIP::getCountry(const string& ip) const {
+void GeoIP::rebuild() {
 	Lock l(cs);
-
 	if(geo) {
+		const auto& setting = SETTING(COUNTRY_FORMAT);
 
-		auto id = (v6() ? GeoIP_id_by_addr_v6 : GeoIP_id_by_addr)(geo, ip.c_str());
-		if(id > 0) {
+		auto size = GeoIP_num_countries();
+		cache.resize(size);
+		for(int id = 1; id < size; ++id) {
 
 			ParamMap params;
 
@@ -93,29 +115,9 @@
 			params["officialname"] = [id] { return forwardRet(GeoIP_name_by_id(id)); };
 #endif
 
-			return Util::formatParams(SETTING(COUNTRY_FORMAT), params);
+			cache[id] = Util::formatParams(setting, params);
 		}
 	}
-
-	return Util::emptyString;
-}
-
-void GeoIP::update() {
-	Lock l(cs);
-
-	close();
-
-	if(decompress()) {
-		open();
-	}
-}
-
-void GeoIP::init() {
-	if(File::getSize(path) <= 0 && !decompress()) {
-		return;
-	}
-
-	open();
 }
 
 bool GeoIP::decompress() const {
@@ -137,6 +139,8 @@
 }
 
 void GeoIP::close() {
+	cache.clear();
+
 	GeoIP_delete(geo);
 	geo = 0;
 }

=== modified file 'dcpp/GeoIP.h'
--- dcpp/GeoIP.h	2011-10-06 18:58:07 +0000
+++ dcpp/GeoIP.h	2011-10-08 15:21:54 +0000
@@ -22,24 +22,25 @@
 #include "CriticalSection.h"
 
 #include <string>
+#include <vector>
 
 typedef struct GeoIPTag GeoIP;
 
 namespace dcpp {
 
 using std::string;
+using std::vector;
 
-class GeoIP {
+class GeoIP : boost::noncopyable {
 public:
-	GeoIP();
+	explicit GeoIP(const string&& path);
 	~GeoIP();
 
-	void init(const string& path_);
 	string getCountry(const string& ip) const;
 	void update();
+	void rebuild();
 
 private:
-	void init();
 	bool decompress() const;
 	void open();
 	void close();
@@ -48,7 +49,8 @@
 	mutable CriticalSection cs;
 	::GeoIP* geo;
 
-	string path;
+	const string path;
+	vector<string> cache;
 };
 
 } // namespace dcpp

=== added file 'dcpp/GeoManager.cpp'
--- dcpp/GeoManager.cpp	1970-01-01 00:00:00 +0000
+++ dcpp/GeoManager.cpp	2011-10-08 15:21:54 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2001-2011 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * 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.
+ */
+
+#include "stdinc.h"
+#include "GeoManager.h"
+
+#include "GeoIP.h"
+#include "Util.h"
+
+namespace dcpp {
+
+void GeoManager::init() {
+	geo6.reset(new GeoIP(getDbPath(true)));
+	geo4.reset(new GeoIP(getDbPath(false)));
+
+	rebuild();
+}
+
+void GeoManager::update(bool v6) {
+	auto geo = (v6 ? geo6 : geo4).get();
+	if(geo) {
+		geo->update();
+		geo->rebuild();
+	}
+}
+
+void GeoManager::rebuild() {
+	geo6->rebuild();
+	geo4->rebuild();
+}
+
+void GeoManager::close() {
+	geo6.reset();
+	geo4.reset();
+}
+
+string GeoManager::getCountry(const string& ip, int flags) {
+	if(!ip.empty()) {
+
+		if((flags & V6) && geo6.get()) {
+			auto ret = geo6->getCountry(ip);
+			if(!ret.empty())
+				return ret;
+		}
+
+		if((flags & V4) && geo4.get()) {
+			return geo4->getCountry(ip);
+		}
+	}
+
+	return Util::emptyString;
+}
+
+string GeoManager::getDbPath(bool v6) {
+	return Util::getPath(Util::PATH_USER_LOCAL) + (v6 ? "GeoIPv6.dat" : "GeoIP.dat");
+}
+
+} // namespace dcpp

=== added file 'dcpp/GeoManager.h'
--- dcpp/GeoManager.h	1970-01-01 00:00:00 +0000
+++ dcpp/GeoManager.h	2011-10-08 15:21:54 +0000
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2001-2011 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * 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.
+ */
+
+#ifndef DCPLUSPLUS_DCPP_GEO_MANAGER_H
+#define DCPLUSPLUS_DCPP_GEO_MANAGER_H
+
+#include "forward.h"
+#include "GeoIP.h"
+#include "Singleton.h"
+
+#include <memory>
+#include <string>
+
+namespace dcpp {
+
+using std::string;
+using std::unique_ptr;
+
+/** Manages IP->country mappings. */
+class GeoManager : public Singleton<GeoManager>
+{
+public:
+	/** Prepare the databases and fill internal caches. */
+	void init();
+	/** Update a database and its internal caches. Call after a new one have been downloaded. */
+	void update(bool v6);
+	/** Rebuild the internal caches. Call after a change of country settings. */
+	void rebuild();
+	/** Unload databases and clear internal caches. */
+	void close();
+
+	enum { V6 = 1 << 1, V4 = 1 << 2 };
+	/** Map an IP address to a country. The flags specify which database(s) to look into. */
+	string getCountry(const string& ip, int flags = V6 | V4);
+
+	static string getDbPath(bool v6);
+
+private:
+	friend class Singleton<GeoManager>;
+
+	// only these 2 for now. in the future, more databases could be added (region / city info...).
+	unique_ptr<GeoIP> geo6, geo4;
+
+	GeoManager() { }
+	virtual ~GeoManager() { }
+};
+
+} // namespace dcpp
+
+#endif

=== modified file 'dcpp/User.cpp'
--- dcpp/User.cpp	2011-09-30 11:33:12 +0000
+++ dcpp/User.cpp	2011-10-08 15:21:54 +0000
@@ -21,8 +21,9 @@
 
 #include "AdcHub.h"
 #include "FavoriteUser.h"
+#include "format.h"
+#include "GeoManager.h"
 #include "StringTokenizer.h"
-#include "format.h"
 
 namespace dcpp {
 
@@ -82,24 +83,24 @@
 		}
 	}
 	if(user) {
-		params[prefix + "SID"] = getSIDString();
-		params[prefix + "CID"] = user->getCID().toBase32();
-		params[prefix + "TAG"] = getTag();
-		params[prefix + "SSshort"] = Util::formatBytes(get("SS"));
+		params[prefix + "SID"] = [this] { return getSIDString(); };
+		params[prefix + "CID"] = [this] { return user->getCID().toBase32(); };
+		params[prefix + "TAG"] = [this] { return getTag(); };
+		params[prefix + "SSshort"] = [this] { return Util::formatBytes(get("SS")); };
 
 		if(compatibility) {
 			if(prefix == "my") {
-				params["mynick"] = getNick();
-				params["mycid"] = user->getCID().toBase32();
+				params["mynick"] = [this] { return getNick(); };
+				params["mycid"] = [this] { return user->getCID().toBase32(); };
 			} else {
-				params["nick"] = getNick();
-				params["cid"] = user->getCID().toBase32();
-				params["ip"] = get("I4");
-				params["tag"] = getTag();
-				params["description"] = get("DE");
-				params["email"] = get("EM");
-				params["share"] = get("SS");
-				params["shareshort"] = Util::formatBytes(get("SS"));
+				params["nick"] = [this] { return getNick(); };
+				params["cid"] = [this] { return user->getCID().toBase32(); };
+				params["ip"] = [this] { return get("I4"); };
+				params["tag"] = [this] { return getTag(); };
+				params["description"] = [this] { return get("DE"); };
+				params["email"] = [this] { return get("EM"); };
+				params["share"] = [this] { return get("SS"); };
+				params["shareshort"] = [this] { return Util::formatBytes(get("SS")); };
 			}
 		}
 	}
@@ -144,7 +145,7 @@
 
 string Identity::getCountry() const {
 	bool v6 = !getIp6().empty();
-	return Util::getCountry(v6 ? getIp6() : getIp4(), v6 ? Util::V6 : Util::V4);
+	return GeoManager::getInstance()->getCountry(v6 ? getIp6() : getIp4(), v6 ? GeoManager::V6 : GeoManager::V4);
 }
 
 string Identity::get(const char* name) const {

=== modified file 'dcpp/Util.cpp'
--- dcpp/Util.cpp	2011-10-06 18:58:07 +0000
+++ dcpp/Util.cpp	2011-10-08 15:21:54 +0000
@@ -30,7 +30,6 @@
 #include "ClientManager.h"
 #include "FastAlloc.h"
 #include "File.h"
-#include "GeoIP.h"
 #include "LogManager.h"
 #include "SettingsManager.h"
 #include "SimpleXML.h"
@@ -55,8 +54,6 @@
 FastCriticalSection FastAllocBase::cs;
 #endif
 
-GeoIP geo6, geo4;
-
 string Util::emptyString;
 wstring Util::emptyStringW;
 tstring Util::emptyStringT;
@@ -196,9 +193,6 @@
 
 	File::ensureDirectory(paths[PATH_USER_CONFIG]);
 	File::ensureDirectory(paths[PATH_USER_LOCAL]);
-
-	geo6.init(getGeoPath(true));
-	geo4.init(getGeoPath(false));
 }
 
 void Util::migrate(const string& file) {
@@ -250,10 +244,6 @@
 	}
 }
 
-string Util::getGeoPath(bool v6) {
-	return getPath(PATH_USER_LOCAL) + (v6 ? "GeoIPv6.dat" : "GeoIP.dat");
-}
-
 #ifdef _WIN32
 static const char badChars[] = {
 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
@@ -951,27 +941,6 @@
 	return y;
 }
 
-string Util::getCountry(const string& ip, int flags) {
-	if(BOOLSETTING(GET_USER_COUNTRY) && !ip.empty()) {
-
-		if(flags & V6) {
-			auto ret = geo6.getCountry(ip);
-			if(!ret.empty())
-				return ret;
-		}
-
-		if(flags & V4) {
-			return geo4.getCountry(ip);
-		}
-	}
-
-	return emptyString;
-}
-
-void Util::updateCountryDb(bool v6) {
-	(v6 ? geo6 : geo4).update();
-}
-
 string Util::getTimeString() {
 	char buf[64];
 	time_t _tt;

=== modified file 'dcpp/Util.h'
--- dcpp/Util.h	2011-10-06 18:58:07 +0000
+++ dcpp/Util.h	2011-10-08 15:21:54 +0000
@@ -157,8 +157,6 @@
 	static string getHubListsPath() { return getPath(PATH_HUB_LISTS); }
 	/** Notepad filename */
 	static string getNotepadFile() { return getPath(PATH_NOTEPAD); }
-	/** GeoIP database path */
-	static string getGeoPath(bool v6);
 
 	static string translateError(int aError);
 
@@ -432,10 +430,6 @@
 	static int stricmp(const wstring& a, const wstring& b) { return stricmp(a.c_str(), b.c_str()); }
 	static int strnicmp(const wstring& a, const wstring& b, size_t n) { return strnicmp(a.c_str(), b.c_str(), n); }
 
-	enum { V6 = 1 << 1, V4 = 1 << 2 };
-	static string getCountry(const string& ip, int flags = V6 | V4);
-	static void updateCountryDb(bool v6);
-
 	static bool getAway();
 	static void setAway(bool aAway);
 	static void switchAway();

=== modified file 'win32/MainWindow.cpp'
--- win32/MainWindow.cpp	2011-10-06 18:58:07 +0000
+++ win32/MainWindow.cpp	2011-10-08 15:21:54 +0000
@@ -27,6 +27,7 @@
 #include <dcpp/Download.h>
 #include <dcpp/DownloadManager.h>
 #include <dcpp/FavoriteManager.h>
+#include <dcpp/GeoManager.h>
 #include <dcpp/HttpDownload.h>
 #include <dcpp/LogManager.h>
 #include <dcpp/QueueManager.h>
@@ -172,11 +173,6 @@
 	conns[CONN_VERSION].reset(new HttpDownload("http://dcplusplus.sourceforge.net/version.xml";,
 		[this] { callAsync([=] { completeVersionUpdate(); }); }));
 
-	if(BOOLSETTING(GET_USER_COUNTRY)) {
-		checkGeoUpdate(true);
-		checkGeoUpdate(false);
-	}
-
 	try {
 		ConnectivityManager::getInstance()->setup(true);
 	} catch (const Exception& e) {
@@ -285,7 +281,7 @@
 
 		file->appendItem(T_("Settings\tCtrl+F3"), [this] { handleSettings(); }, WinUtil::menuIcon(IDI_SETTINGS));
 		file->appendSeparator();
-		file->appendItem(T_("GeoIP database update"), [this] { updateGeo(true); updateGeo(false); });
+		file->appendItem(T_("GeoIP database update"), [this] { updateGeo(); });
 		file->appendSeparator();
 		file->appendItem(T_("E&xit\tAlt+F4"), [this] { GCC_WTF->close(true); }, WinUtil::menuIcon(IDI_EXIT));
 	}
@@ -922,7 +918,9 @@
 
 bool MainWindow::handleClosing() {
 	if(!closing()) {
-		if ( !BOOLSETTING(CONFIRM_EXIT) || (dwt::MessageBox(this).show(T_("Really exit?"), _T(APPNAME) _T(" ") _T(VERSIONSTRING), dwt::MessageBox::BOX_YESNO, dwt::MessageBox::BOX_ICONQUESTION) == IDYES)) {
+		if(!BOOLSETTING(CONFIRM_EXIT) || (dwt::MessageBox(this).show(T_("Really exit?"), _T(APPNAME) _T(" ") _T(VERSIONSTRING),
+			dwt::MessageBox::BOX_YESNO, dwt::MessageBox::BOX_ICONQUESTION) == IDYES))
+		{
 
 			for(uint8_t i = 0; i < CONN_LAST; ++i)
 				conns[i].reset();
@@ -1057,53 +1055,71 @@
 	if(isIconic())
 		handleRestore();
 
-	SettingsDialog dlg(this);
-
-	unsigned short lastTCP = static_cast<unsigned short>(SETTING(TCP_PORT));
-	unsigned short lastUDP = static_cast<unsigned short>(SETTING(UDP_PORT));
-	unsigned short lastTLS = static_cast<unsigned short>(SETTING(TLS_PORT));
-
-	int lastConn = SETTING(INCOMING_CONNECTIONS);
-	string lastMapper = SETTING(MAPPER);
-	string lastBind = SETTING(BIND_ADDRESS);
-
-	bool lastTray = BOOLSETTING(ALWAYS_TRAY);
-	bool lastSortFavUsersFirst = BOOLSETTING(SORT_FAVUSERS_FIRST);
-	bool lastURLReg = BOOLSETTING(URL_HANDLER);
-	bool lastMagnetReg = BOOLSETTING(MAGNET_REGISTER);
-	int lastSettingsSave = SETTING(SETTINGS_SAVE_INTERVAL);
-
-	if(dlg.run() == IDOK) {
+	auto prevTCP = SETTING(TCP_PORT);
+	auto prevUDP = SETTING(UDP_PORT);
+	auto prevTLS = SETTING(TLS_PORT);
+
+	auto prevConn = SETTING(INCOMING_CONNECTIONS);
+	auto prevMapper = SETTING(MAPPER);
+	auto prevBind = SETTING(BIND_ADDRESS);
+
+	auto prevGeo = BOOLSETTING(GET_USER_COUNTRY);
+	auto prevGeoFormat = SETTING(COUNTRY_FORMAT);
+
+	auto prevTray = BOOLSETTING(ALWAYS_TRAY);
+	auto prevSortFavUsersFirst = BOOLSETTING(SORT_FAVUSERS_FIRST);
+	auto prevURLReg = BOOLSETTING(URL_HANDLER);
+	auto prevMagnetReg = BOOLSETTING(MAGNET_REGISTER);
+	auto prevSettingsSave = SETTING(SETTINGS_SAVE_INTERVAL);
+
+	if(SettingsDialog(this).run() == IDOK) {
 		SettingsManager::getInstance()->save();
 
 		try {
-			ConnectivityManager::getInstance()->setup(SETTING(INCOMING_CONNECTIONS) != lastConn ||
-				SETTING(TCP_PORT) != lastTCP || SETTING(UDP_PORT) != lastUDP || SETTING(TLS_PORT) != lastTLS ||
-				SETTING(MAPPER) != lastMapper || SETTING(BIND_ADDRESS) != lastBind);
+			ConnectivityManager::getInstance()->setup(SETTING(INCOMING_CONNECTIONS) != prevConn ||
+				SETTING(TCP_PORT) != prevTCP || SETTING(UDP_PORT) != prevUDP || SETTING(TLS_PORT) != prevTLS ||
+				SETTING(MAPPER) != prevMapper || SETTING(BIND_ADDRESS) != prevBind);
 		} catch (const Exception& e) {
 			showPortsError(e.getError());
 		}
 
-		if(BOOLSETTING(ALWAYS_TRAY) != lastTray)
+		ClientManager::getInstance()->infoUpdated();
+
+		bool rebuildGeo = prevGeo && SETTING(COUNTRY_FORMAT) != prevGeoFormat;
+		if(BOOLSETTING(GET_USER_COUNTRY) != prevGeo) {
+			if(BOOLSETTING(GET_USER_COUNTRY)) {
+				GeoManager::getInstance()->init();
+				checkGeoUpdate();
+			} else {
+				GeoManager::getInstance()->close();
+				rebuildGeo = false;
+			}
+		}
+		if(rebuildGeo) {
+			GeoManager::getInstance()->rebuild();
+		}
+
+		if(BOOLSETTING(ALWAYS_TRAY) != prevTray)
 			notifier->setVisible(BOOLSETTING(ALWAYS_TRAY));
 
-		if(BOOLSETTING(SORT_FAVUSERS_FIRST) != lastSortFavUsersFirst)
+		if(BOOLSETTING(SORT_FAVUSERS_FIRST) != prevSortFavUsersFirst)
 			HubFrame::resortUsers();
 
-		if(BOOLSETTING(URL_HANDLER) != lastURLReg)
+		if(BOOLSETTING(URL_HANDLER) != prevURLReg)
 			WinUtil::registerHubHandlers();
-		if(BOOLSETTING(MAGNET_REGISTER) != lastMagnetReg)
+		if(BOOLSETTING(MAGNET_REGISTER) != prevMagnetReg)
 			WinUtil::registerMagnetHandler();
 
-		ClientManager::getInstance()->infoUpdated();
-
-		if(SETTING(SETTINGS_SAVE_INTERVAL) != lastSettingsSave)
+		if(SETTING(SETTINGS_SAVE_INTERVAL) != prevSettingsSave)
 			setSaveTimer();
 	}
 }
 
 void MainWindow::showPortsError(const string& port) {
-	dwt::MessageBox(this).show(Text::toT(str(F_("Unable to open %1% port. Searching or file transfers will not work correctly until you change settings or turn off any application that might be using that port.") % port)), _T(APPNAME) _T(" ") _T(VERSIONSTRING), dwt::MessageBox::BOX_OK, dwt::MessageBox::BOX_ICONSTOP);
+	dwt::MessageBox(this).show(Text::toT(str(F_(
+		"Unable to open %1% port. Searching or file transfers will not work correctly until you "
+		"change settings or turn off any application that might be using that port."
+		) % port)), _T(APPNAME) _T(" ") _T(VERSIONSTRING), dwt::MessageBox::BOX_OK, dwt::MessageBox::BOX_ICONSTOP);
 }
 
 void MainWindow::handleOpenFileList() {
@@ -1113,7 +1129,8 @@
 		if (u) {
 			DirectoryListingFrame::openWindow(getTabView(), file, Util::emptyStringT, HintedUser(u, Util::emptyString), 0);
 		} else {
-			dwt::MessageBox(this).show(T_("Invalid file list name"), _T(APPNAME) _T(" ") _T(VERSIONSTRING));
+			dwt::MessageBox(this).show(T_("Invalid file list name"), _T(APPNAME) _T(" ") _T(VERSIONSTRING),
+				dwt::MessageBox::BOX_OK, dwt::MessageBox::BOX_ICONEXCLAMATION);
 		}
 	}
 }
@@ -1286,12 +1303,22 @@
 	} catch (const Exception&) { }
 
 	conns[CONN_VERSION].reset();
+
+	// check after the version.xml download in case it contains updated GeoIP links.
+	if(BOOLSETTING(GET_USER_COUNTRY)) {
+		checkGeoUpdate();
+	}
+}
+
+void MainWindow::checkGeoUpdate() {
+	checkGeoUpdate(true);
+	checkGeoUpdate(false);
 }
 
 void MainWindow::checkGeoUpdate(bool v6) {
 	// update when the database is non-existent or older than 16 days (GeoIP updates every month).
 	try {
-		File f(Util::getGeoPath(v6) + ".gz", File::READ, File::OPEN);
+		File f(GeoManager::getDbPath(v6) + ".gz", File::READ, File::OPEN);
 		if(f.getSize() > 0 && f.getLastModified() > GET_TIME() - 3600 * 24 * 16) {
 			return;
 		}
@@ -1299,6 +1326,16 @@
 	updateGeo(v6);
 }
 
+void MainWindow::updateGeo() {
+	if(BOOLSETTING(GET_USER_COUNTRY)) {
+		updateGeo(true);
+		updateGeo(false);
+	} else {
+		dwt::MessageBox(this).show(T_("IP -> country mappings are disabled. Turn them back on via Settings > Appearance."),
+			_T(APPNAME) _T(" ") _T(VERSIONSTRING), dwt::MessageBox::BOX_OK, dwt::MessageBox::BOX_ICONEXCLAMATION);
+	}
+}
+
 void MainWindow::updateGeo(bool v6) {
 	auto& conn = conns[v6 ? CONN_GEO_V6 : CONN_GEO_V4];
 	if(conn.get())
@@ -1315,8 +1352,8 @@
 
 	if(!conn->buf.empty()) {
 		try {
-			File(Util::getGeoPath(v6) + ".gz", File::WRITE, File::CREATE | File::TRUNCATE).write(conn->buf);
-			Util::updateCountryDb(v6);
+			File(GeoManager::getDbPath(v6) + ".gz", File::WRITE, File::CREATE | File::TRUNCATE).write(conn->buf);
+			GeoManager::getInstance()->update(v6);
 			LogManager::getInstance()->message(str(F_("The %1% GeoIP database has been successfully updated") % (v6 ? "IPv6" : "IPv4")));
 			return;
 		} catch(const FileException&) { }

=== modified file 'win32/MainWindow.h'
--- win32/MainWindow.h	2011-10-02 12:14:41 +0000
+++ win32/MainWindow.h	2011-10-08 15:21:54 +0000
@@ -208,7 +208,9 @@
 	void statusMessage(time_t t, const string& m);
 
 	void completeVersionUpdate();
+	void checkGeoUpdate();
 	void checkGeoUpdate(bool v6);
+	void updateGeo();
 	void updateGeo(bool v6);
 	void completeGeoUpdate(bool v6);
 

=== modified file 'win32/NotificationsPage.cpp'
--- win32/NotificationsPage.cpp	2011-05-27 11:03:29 +0000
+++ win32/NotificationsPage.cpp	2011-10-08 15:21:54 +0000
@@ -147,7 +147,7 @@
 	table->onDblClicked([this] { handleDblClicked(); });
 
 	table->onHelp([this](Widget*, unsigned id) { handleTableHelp(id); });
-	table->setHelpId([this](unsigned id) { handleTableHelpId(id); });
+	table->setHelpId([this](unsigned& id) { handleTableHelpId(id); });
 }
 
 NotificationsPage::~NotificationsPage() {

=== modified file 'win32/PublicHubsFrame.cpp'
--- win32/PublicHubsFrame.cpp	2011-05-04 19:32:00 +0000
+++ win32/PublicHubsFrame.cpp	2011-10-08 15:21:54 +0000
@@ -20,6 +20,7 @@
 #include "PublicHubsFrame.h"
 
 #include <dcpp/FavoriteManager.h>
+#include <dcpp/GeoManager.h>
 #include <dcpp/version.h>
 
 #include <dwt/widgets/Grid.h>

=== modified file 'win32/SearchFrame.cpp'
--- win32/SearchFrame.cpp	2011-09-30 11:33:12 +0000
+++ win32/SearchFrame.cpp	2011-10-08 15:21:54 +0000
@@ -19,15 +19,16 @@
 #include "stdafx.h"
 #include "SearchFrame.h"
 
+#include <dcpp/ClientManager.h>
 #include <dcpp/FavoriteManager.h>
+#include <dcpp/GeoManager.h>
 #include <dcpp/QueueManager.h>
-#include <dcpp/ClientManager.h>
+#include <dcpp/SearchResult.h>
 #include <dcpp/ShareManager.h>
-#include <dcpp/SearchResult.h>
 
 #include <dwt/widgets/FolderDialog.h>
+#include <dwt/widgets/Grid.h>
 #include <dwt/widgets/SplitterContainer.h>
-#include <dwt/widgets/Grid.h>
 
 #include "resource.h"
 #include "TypedTable.h"
@@ -486,13 +487,9 @@
 		columns[COLUMN_NICK] = WinUtil::getNicks(sr->getUser(), sr->getHubURL());
 		columns[COLUMN_CONNECTION] = Text::toT(ClientManager::getInstance()->getConnection(sr->getUser()->getCID()));
 		columns[COLUMN_SLOTS] = Text::toT(sr->getSlotString());
-		columns[COLUMN_IP] = Text::toT(sr->getIP());
-		if(!columns[COLUMN_IP].empty()) {
-			// Only attempt to grab a country mapping if we actually have an IP address
-			auto country = Util::getCountry(sr->getIP());
-			if(!country.empty())
-				columns[COLUMN_IP] = Text::toT(country) + _T(" (") + columns[COLUMN_IP] + _T(")");
-		}
+		const auto& ip = sr->getIP();
+		auto country = GeoManager::getInstance()->getCountry(ip);
+		columns[COLUMN_IP] = Text::toT(country.empty() ? ip : str(F_("%1% (%2%)") % country % ip));
 		columns[COLUMN_HUB] = Text::toT(sr->getHubName());
 		columns[COLUMN_CID] = Text::toT(sr->getUser()->getCID().toBase32());
 	}

=== modified file 'win32/TransferView.cpp'
--- win32/TransferView.cpp	2011-09-30 11:33:12 +0000
+++ win32/TransferView.cpp	2011-10-08 15:21:54 +0000
@@ -19,23 +19,24 @@
 #include "stdafx.h"
 #include "TransferView.h"
 
-#include <dcpp/SettingsManager.h>
+#include <dcpp/ClientManager.h>
 #include <dcpp/ConnectionManager.h>
+#include <dcpp/Download.h>
 #include <dcpp/DownloadManager.h>
-#include <dcpp/UploadManager.h>
+#include <dcpp/GeoManager.h>
 #include <dcpp/QueueManager.h>
-#include <dcpp/ClientManager.h>
-#include <dcpp/Download.h>
+#include <dcpp/SettingsManager.h>
 #include <dcpp/Upload.h>
+#include <dcpp/UploadManager.h>
 #include <dcpp/UserConnection.h>
 
 #include <dwt/resources/Pen.h>
 #include <dwt/util/StringUtils.h>
 
 #include "resource.h"
+#include "HoldRedraw.h"
 #include "TypedTable.h"
 #include "WinUtil.h"
-#include "HoldRedraw.h"
 
 using dwt::util::escapeMenu;
 
@@ -734,7 +735,7 @@
 	ui->setChunk(t->getPos(), t->getSize());
 	const UserConnection& uc = t->getUserConnection();
 	ui->setCipher(Text::toT(uc.getCipherName()));
-	auto country = Util::getCountry(uc.getRemoteIp());
+	auto country = GeoManager::getInstance()->getCountry(uc.getRemoteIp());
 	if(!country.empty())
 		ui->setCountry(Text::toT(country));
 	ui->setIP(Text::toT(uc.getRemoteIp()));