← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3294: dl the geoip region db; refresh geoip caches in their socket thread

 

------------------------------------------------------------
revno: 3294
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Wed 2013-05-15 01:30:49 +0200
message:
  dl the geoip region db; refresh geoip caches in their socket thread
modified:
  dcpp/GeoIP.cpp
  dcpp/GeoManager.cpp
  dcpp/GeoManager.h
  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/GeoIP.cpp'
--- dcpp/GeoIP.cpp	2013-05-13 18:38:30 +0000
+++ dcpp/GeoIP.cpp	2013-05-14 23:30:49 +0000
@@ -21,6 +21,7 @@
 
 #include "File.h"
 #include "format.h"
+#include "GeoManager.h"
 #include "SettingsManager.h"
 #include "Util.h"
 #include "ZUtils.h"
@@ -140,6 +141,42 @@
 #endif
 }
 
+inline uint32_t regionCode(char country0, char country1, char region0, char region1) {
+	union { char chars[4]; uint32_t i; } u = { country0, country1, region0, region1 };
+	return u.i;
+}
+
+unordered_map<uint32_t, string> getRegions() {
+	unordered_map<uint32_t, string> ret;
+	if(!SETTING(GEO_REGION))
+		return ret;
+	if(SETTING(COUNTRY_FORMAT).find("%[region]") == string::npos)
+		return ret;
+	try {
+		auto regions = File(GeoManager::getRegionDbPath(), File::READ, File::OPEN).read();
+		size_t begin = 0, end;
+		while((end = regions.find('\n', begin)) != string::npos) {
+			if(begin + 9 > end) { break; } // corrupted file
+			auto country0 = regions[begin++];
+			auto country1 = regions[begin++];
+			++begin; // comma
+			auto region0 = regions[begin++];
+			auto region1 = regions[begin++];
+			++begin; // comma
+			++begin; // begin quote
+			--end; // end quote
+			ret[regionCode(country0, country1, region0, region1)] = regions.substr(begin, end - begin);
+			begin = end + 2; // end quote + new line
+		}
+	} catch (const FileException&) { }
+	return ret;
+}
+
+string regionName(const unordered_map<uint32_t, string>& regions, const char* country, const char* region) {
+	auto i = regions.find(regionCode(country[0], country[1], region[0], region[1]));
+	return i != regions.cend() ? i->second : string();
+}
+
 } // unnamed namespace
 
 void GeoIP::rebuild_cities() {
@@ -147,6 +184,8 @@
 
 	const auto& setting = SETTING(COUNTRY_FORMAT);
 
+	const auto regions = getRegions();
+
 	GeoIPRecord* record = nullptr;
 	auto id = GeoIP_init_record_iter(geo);
 	auto prev_id = id;
@@ -161,11 +200,11 @@
 		params["long"] = [record] { return Util::toString(record->longitude); };
 		params["metrocode"] = [record] { return Util::toString(record->metro_code); };
 		params["postcode"] = [record] { return forwardRet(record->postal_code); };
-		params["region"] = [record]() -> string {
+		params["region"] = [record, &regions]() -> string {
 			auto country = GeoIP_code_by_id(record->country);
 			auto region = record->region;
 			if(country && region) {
-				// todo
+				return regionName(regions, country, region);
 			}
 			return forwardRet(region);
 		};

=== modified file 'dcpp/GeoManager.cpp'
--- dcpp/GeoManager.cpp	2013-01-18 21:28:38 +0000
+++ dcpp/GeoManager.cpp	2013-05-14 23:30:49 +0000
@@ -70,4 +70,8 @@
 	return Util::getPath(Util::PATH_USER_LOCAL) + (v6 ? "GeoIPv6.dat" : "GeoIP.dat");
 }
 
+string GeoManager::getRegionDbPath() {
+	return Util::getPath(Util::PATH_USER_LOCAL) + "GeoIP_Regions.csv";
+}
+
 } // namespace dcpp

=== modified file 'dcpp/GeoManager.h'
--- dcpp/GeoManager.h	2013-04-16 16:11:50 +0000
+++ dcpp/GeoManager.h	2013-05-14 23:30:49 +0000
@@ -49,6 +49,7 @@
 	const string& getCountry(const string& ip, int flags = V6 | V4);
 
 	static string getDbPath(bool v6);
+	static string getRegionDbPath();
 
 private:
 	friend class Singleton<GeoManager>;

=== modified file 'win32/MainWindow.cpp'
--- win32/MainWindow.cpp	2013-05-13 18:22:52 +0000
+++ win32/MainWindow.cpp	2013-05-14 23:30:49 +0000
@@ -104,6 +104,7 @@
 toolbar(0),
 tabs(0),
 tray_pm(false),
+geoRegion(GeoRegion_Idle),
 stopperThread(NULL),
 lastUp(0),
 lastDown(0),
@@ -119,6 +120,7 @@
 	links.geoip4 = _T("http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz";);
 	links.geoip6_city = _T("http://geolite.maxmind.com/download/geoip/database/GeoLiteCityv6-beta/GeoLiteCityv6.dat.gz";);
 	links.geoip4_city = _T("http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz";);
+	links.geoip_regions = _T("http://dev.maxmind.com/static/csv/codes/maxmind/region.csv";);
 	links.faq = links.homepage + _T("faq/");
 	links.help = links.homepage + _T("help/");
 	links.discuss = links.homepage + _T("discussion/");
@@ -678,8 +680,6 @@
 
 	auto i = commands.find(text);
 	if(i == commands.end()) { return; }
-
-	auto index = std::distance(commands.begin(), i);
 	commands.erase(i);
 
 	if(commands.empty()) {
@@ -843,7 +843,7 @@
 void MainWindow::handlePlugins(const dwt::ScreenCoordinate& pt) {
 	auto menu = addChild(WinUtil::Seeds::menu);
 
-	menu->setTitle(T_("Plugins"));
+	menu->setTitle(T_("Plugins"), WinUtil::menuIcon(IDI_PLUGINS));
 	addPluginCommands(menu.get());
 
 	menu->open(pt);
@@ -1227,6 +1227,7 @@
 
 	auto prevGeo = SETTING(GET_USER_COUNTRY);
 	auto prevGeoCity = SETTING(GEO_CITY);
+	auto prevGeoRegion = SETTING(GEO_REGION);
 	auto prevGeoFormat = SETTING(COUNTRY_FORMAT);
 
 	auto prevFont = SETTING(MAIN_FONT);
@@ -1259,10 +1260,10 @@
 		ClientManager::getInstance()->infoUpdated();
 
 		bool rebuildGeo = prevGeo && SETTING(COUNTRY_FORMAT) != prevGeoFormat;
-		if(SETTING(GET_USER_COUNTRY) != prevGeo || SETTING(GEO_CITY) != prevGeoCity) {
+		if(SETTING(GET_USER_COUNTRY) != prevGeo || SETTING(GEO_CITY) != prevGeoCity || SETTING(GEO_REGION) != prevGeoRegion) {
 			if(SETTING(GET_USER_COUNTRY)) {
 				GeoManager::getInstance()->init();
-				if(SETTING(GEO_CITY) != prevGeoCity) {
+				if(SETTING(GEO_CITY) != prevGeoCity || (SETTING(GEO_CITY) && SETTING(GEO_REGION) != prevGeoRegion)) {
 					updateGeo();
 				} else {
 					checkGeoUpdate();
@@ -1478,6 +1479,10 @@
 				links.geoip4_city = Text::toT(xml.getChildData());
 			}
 			xml.resetCurrentChild();
+			if(xml.findChild("GeoIP_Regions")) {
+				links.geoip_regions = Text::toT(xml.getChildData());
+			}
+			xml.resetCurrentChild();
 			if(xml.findChild("Faq")) {
 				links.faq = Text::toT(xml.getChildData());
 			}
@@ -1564,7 +1569,7 @@
 
 void MainWindow::updateGeo(bool v6) {
 	auto& conn = conns[v6 ? CONN_GEO_V6 : CONN_GEO_V4];
-	if(conn)
+	if(static_cast<HttpConnection*>(conn))
 		return;
 
 	auto& file = v6 ? geo6File : geo4File;
@@ -1581,10 +1586,56 @@
 }
 
 void MainWindow::completeGeoUpdate(bool v6, bool success) {
+	/* careful, no GUI call here! this runs in the socket thread so as not to freeze the GUI while
+	regenerating GeoIP caches. */
+
 	if(success) {
+
+		/* this is tricky: the region file covers both v6 & v4 databases so we try our best to
+		download it only once. both databases are refreshed after a succesful region download. */
+		if(SETTING(GEO_CITY) && SETTING(GEO_REGION)) {
+
+			if(geoRegion == (v6 ? GeoRegion_FromV6 : GeoRegion_FromV4)) {
+				geoRegion = GeoRegion_Idle;
+
+				try {
+					File::renameFile(GeoManager::getRegionDbPath() + ".tmp", GeoManager::getRegionDbPath());
+				} catch(const FileException&) { }
+
+				GeoManager::getInstance()->update(true);
+				LogManager::getInstance()->message(str(F_("The %1% GeoIP database has been successfully updated") % geoType(true)));
+				GeoManager::getInstance()->update(false);
+				LogManager::getInstance()->message(str(F_("The %1% GeoIP database has been successfully updated") % geoType(false)));
+
+			} else if(geoRegion == GeoRegion_Idle) {
+
+				/* do nothing if the other download is running - regions will be downloaded and
+				both databases will be refreshed once it completes. */
+				if(static_cast<HttpConnection*>(conns[v6 ? CONN_GEO_V4 : CONN_GEO_V6]))
+					return;
+
+				geoRegion = v6 ? GeoRegion_FromV6 : GeoRegion_FromV4;
+				auto& file = v6 ? geo6File : geo4File;
+				try {
+					file.reset(new File(GeoManager::getRegionDbPath() + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE));
+					conns[v6 ? CONN_GEO_V6 : CONN_GEO_V4] = HttpManager::getInstance()->download(Text::fromT(links.geoip_regions), file.get());
+				} catch(const FileException&) {
+					geoRegion = GeoRegion_Idle;
+				}
+			}
+
+			return;
+		}
+
 		GeoManager::getInstance()->update(v6);
+
 		LogManager::getInstance()->message(str(F_("The %1% GeoIP database has been successfully updated") % geoType(v6)));
+
 	} else {
+		if(geoRegion == (v6 ? GeoRegion_FromV6 : GeoRegion_FromV4)) {
+			geoRegion = GeoRegion_Idle;
+		}
+
 		LogManager::getInstance()->message(str(F_("The %1% GeoIP database could not be updated") % geoType(v6)));
 	}
 }
@@ -1837,13 +1888,13 @@
 		conns[CONN_GEO_V6] = nullptr;
 		geo6File.reset();
 
-		callAsync([this] { completeGeoUpdate(true, false); });
+		completeGeoUpdate(true, false);
 
 	} else if(c == conns[CONN_GEO_V4]) {
 		conns[CONN_GEO_V4] = nullptr;
 		geo4File.reset();
 
-		callAsync([this] { completeGeoUpdate(false, false); });
+		completeGeoUpdate(false, false);
 	}
 }
 
@@ -1858,13 +1909,13 @@
 		conns[CONN_GEO_V6] = nullptr;
 		geo6File.reset();
 
-		callAsync([this] { completeGeoUpdate(true, true); });
+		completeGeoUpdate(true, true);
 
 	} else if(c == conns[CONN_GEO_V4]) {
 		conns[CONN_GEO_V4] = nullptr;
 		geo4File.reset();
 
-		callAsync([this] { completeGeoUpdate(false, true); });
+		completeGeoUpdate(false, true);
 	}
 }
 

=== modified file 'win32/MainWindow.h'
--- win32/MainWindow.h	2013-05-13 18:22:52 +0000
+++ win32/MainWindow.h	2013-05-14 23:30:49 +0000
@@ -20,6 +20,7 @@
 #define DCPLUSPLUS_WIN32_MAIN_WINDOW_H
 
 #include <dcpp/forward.h>
+#include <dcpp/atomic.h>
 #include <dcpp/HttpManagerListener.h>
 #include <dcpp/LogManagerListener.h>
 #include <dcpp/QueueManagerListener.h>
@@ -100,6 +101,7 @@
 		tstring geoip4;
 		tstring geoip6_city;
 		tstring geoip4_city;
+		tstring geoip_regions;
 		tstring faq;
 		tstring help;
 		tstring discuss;
@@ -142,8 +144,9 @@
 	plugin GUID -> { command name -> pair<callback, icon path> } */
 	static unordered_map<string, map<tstring, pair<function<void ()>, tstring>, noCaseStringLess>> pluginCommands;
 
-	HttpConnection* conns[CONN_LAST];
+	atomic<HttpConnection*> conns[CONN_LAST];
 	unique_ptr<File> geo6File, geo4File;
+	enum { GeoRegion_Idle, GeoRegion_FromV4, GeoRegion_FromV6 } geoRegion;
 
 	HANDLE stopperThread;