← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3262: Support city-level GeoIP databases - new params such as %[city]

 

------------------------------------------------------------
revno: 3262
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Tue 2013-04-16 18:11:50 +0200
message:
  Support city-level GeoIP databases - new params such as %[city]
modified:
  changelog.txt
  dcpp/DCPlusPlus.cpp
  dcpp/FavoriteManager.cpp
  dcpp/FavoriteManager.h
  dcpp/GeoIP.cpp
  dcpp/GeoIP.h
  dcpp/GeoManager.h
  dcpp/SettingsManager.cpp
  dcpp/SettingsManager.h
  help/settings_advanced.html
  help/settings_appearance.html
  win32/AdvancedPage.cpp
  win32/AppearancePage.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 'changelog.txt'
--- changelog.txt	2013-04-14 22:01:23 +0000
+++ changelog.txt	2013-04-16 16:11:50 +0000
@@ -6,6 +6,7 @@
 * Validate input before trying a TTH search (emtee)
 * Display HTTP downloads in the transfer list (poy)
 * [L#190964] Handle more connection errors (poy)
+* Support city-level GeoIP databases - new params such as %[city] (poy)
 
 -- 0.811 2013-03-04 --
 * Fix status bar parts when the window is too small (poy)

=== modified file 'dcpp/DCPlusPlus.cpp'
--- dcpp/DCPlusPlus.cpp	2013-04-12 18:24:09 +0000
+++ dcpp/DCPlusPlus.cpp	2013-04-16 16:11:50 +0000
@@ -143,6 +143,7 @@
 	MappingManager::getInstance()->close();
 	GeoManager::getInstance()->close();
 	BufferedSocket::waitShutdown();
+	FavoriteManager::getInstance()->shutdown();
 
 	WindowManager::getInstance()->prepareSave();
 	QueueManager::getInstance()->saveQueue(true);

=== modified file 'dcpp/FavoriteManager.cpp'
--- dcpp/FavoriteManager.cpp	2013-04-14 22:01:23 +0000
+++ dcpp/FavoriteManager.cpp	2013-04-16 16:11:50 +0000
@@ -57,11 +57,13 @@
 }
 
 FavoriteManager::~FavoriteManager() {
+	for_each(favoriteHubs.begin(), favoriteHubs.end(), DeleteFunction());
+}
+
+void FavoriteManager::shutdown() {
 	ClientManager::getInstance()->removeListener(this);
 	HttpManager::getInstance()->removeListener(this);
 	SettingsManager::getInstance()->removeListener(this);
-
-	for_each(favoriteHubs.begin(), favoriteHubs.end(), DeleteFunction());
 }
 
 UserCommand FavoriteManager::addUserCommand(int type, int ctx, int flags, const string& name, const string& command, const string& to, const string& hub) {

=== modified file 'dcpp/FavoriteManager.h'
--- dcpp/FavoriteManager.h	2013-04-13 15:08:45 +0000
+++ dcpp/FavoriteManager.h	2013-04-16 16:11:50 +0000
@@ -123,6 +123,8 @@
 	void load();
 	void save();
 
+	void shutdown();
+
 private:
 	FavoriteHubEntryList favoriteHubs;
 	FavHubGroups favHubGroups;

=== modified file 'dcpp/GeoIP.cpp'
--- dcpp/GeoIP.cpp	2013-01-18 21:28:38 +0000
+++ dcpp/GeoIP.cpp	2013-04-16 16:11:50 +0000
@@ -26,6 +26,7 @@
 #include "ZUtils.h"
 
 #include <GeoIP.h>
+#include <GeoIPCity.h>
 
 namespace dcpp {
 
@@ -43,9 +44,10 @@
 const 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 && static_cast<size_t>(id) < cache.size()) {
-			return cache[id];
+		auto i = cache.find((city() ? (v6() ? GeoIP_record_id_by_addr_v6 : GeoIP_record_id_by_addr) :
+			(v6() ? GeoIP_id_by_addr_v6 : GeoIP_id_by_addr))(geo, ip.c_str()));
+		if(i != cache.end()) {
+			return i->second;
 		}
 	}
 	return Util::emptyString;
@@ -61,60 +63,10 @@
 	}
 }
 
-namespace {
-
-string forwardRet(const char* ret) {
-	return ret ? ret : Util::emptyString;
-}
-
-#ifdef _WIN32
-string getGeoInfo(int id, GEOTYPE type) {
-	id = GeoIP_Win_GEOID_by_id(id);
-	if(id) {
-		tstring str(GetGeoInfo(id, type, 0, 0, 0), 0);
-		str.resize(GetGeoInfo(id, type, &str[0], str.size(), 0));
-		if(!str.empty()) {
-			return Text::fromT(str);
-		}
-	}
-	return Util::emptyString;
-}
-#endif
-
-} // unnamed namespace
-
 void GeoIP::rebuild() {
 	Lock l(cs);
 	if(geo) {
-		const auto& setting = SETTING(COUNTRY_FORMAT);
-
-		auto size = GeoIP_num_countries();
-		cache.resize(size);
-		for(unsigned id = 1; id < size; ++id) {
-
-			ParamMap params;
-
-			params["2code"] = [id] { return forwardRet(GeoIP_code_by_id(id)); };
-			params["3code"] = [id] { return forwardRet(GeoIP_code3_by_id(id)); };
-			params["continent"] = [id] { return forwardRet(GeoIP_continent_by_id(id)); };
-			params["engname"] = [id] { return forwardRet(GeoIP_name_by_id(id)); };
-#ifdef _WIN32
-			params["name"] = [id]() -> string {
-				auto str = getGeoInfo(id, GEO_FRIENDLYNAME);
-				return str.empty() ? forwardRet(GeoIP_name_by_id(id)) : str;
-			};
-			params["officialname"] = [id]() -> string {
-				auto str = getGeoInfo(id, GEO_OFFICIALNAME);
-				return str.empty() ? forwardRet(GeoIP_name_by_id(id)) : str;
-			};
-#else
-			/// @todo any way to get localized country names on non-Windows?
-			params["name"] = [id] { return forwardRet(GeoIP_name_by_id(id)); };
-			params["officialname"] = [id] { return forwardRet(GeoIP_name_by_id(id)); };
-#endif
-
-			cache[id] = Util::formatParams(setting, params);
-		}
+		city() ? rebuild_cities() : rebuild_countries();
 	}
 }
 
@@ -147,8 +99,107 @@
 	geo = 0;
 }
 
+namespace {
+
+string forwardRet(const char* ret) {
+	return ret ? ret : Util::emptyString;
+}
+
+#ifdef _WIN32
+string getGeoInfo(int id, GEOTYPE type) {
+	id = GeoIP_Win_GEOID_by_id(id);
+	if(id) {
+		tstring str(GetGeoInfo(id, type, 0, 0, 0), 0);
+		str.resize(std::max(GetGeoInfo(id, type, &str[0], str.size(), 0), 1) - 1);
+		if(!str.empty()) {
+			return Text::fromT(str);
+		}
+	}
+	return Util::emptyString;
+}
+#endif
+
+void countryParams(ParamMap& params, int id) {
+	params["2code"] = [id] { return forwardRet(GeoIP_code_by_id(id)); };
+	params["3code"] = [id] { return forwardRet(GeoIP_code3_by_id(id)); };
+	params["continent"] = [id] { return forwardRet(GeoIP_continent_by_id(id)); };
+	params["engname"] = [id] { return forwardRet(GeoIP_name_by_id(id)); };
+#ifdef _WIN32
+	params["name"] = [id]() -> string {
+		auto str = getGeoInfo(id, GEO_FRIENDLYNAME);
+		return str.empty() ? forwardRet(GeoIP_name_by_id(id)) : str;
+	};
+	params["officialname"] = [id]() -> string {
+		auto str = getGeoInfo(id, GEO_OFFICIALNAME);
+		return str.empty() ? forwardRet(GeoIP_name_by_id(id)) : str;
+	};
+#else
+	/// @todo any way to get localized country names on non-Windows?
+	params["name"] = [id] { return forwardRet(GeoIP_name_by_id(id)); };
+	params["officialname"] = [id] { return forwardRet(GeoIP_name_by_id(id)); };
+#endif
+}
+
+} // unnamed namespace
+
+void GeoIP::rebuild_cities() {
+	cache.clear();
+
+	const auto& setting = SETTING(COUNTRY_FORMAT);
+
+	GeoIPRecord* record = nullptr;
+	auto id = GeoIP_init_record_iter(geo);
+	auto prev_id = id;
+	while(!GeoIP_next_record(geo, &record, &id) && record) {
+
+		ParamMap params;
+		countryParams(params, record->country);
+
+		params["areacode"] = [record] { return Util::toString(record->area_code); };
+		params["city"] = [record] { return forwardRet(record->city); };
+		params["lat"] = [record] { return Util::toString(record->latitude); };
+		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 {
+			auto country = GeoIP_code_by_id(record->country);
+			auto region = record->region;
+			if(country && region) {
+				auto str = GeoIP_region_name_by_code(country, region);
+				if(str) { return str; }
+			}
+			return forwardRet(region);
+		};
+
+		cache[prev_id] = Util::formatParams(setting, params);
+		prev_id = id;
+
+		GeoIPRecord_delete(record);
+	}
+}
+
+void GeoIP::rebuild_countries() {
+	cache.clear();
+
+	const auto& setting = SETTING(COUNTRY_FORMAT);
+
+	for(int id = 1, size = GeoIP_num_countries(); id < size; ++id) {
+
+		ParamMap params;
+		countryParams(params, id);
+
+		cache[id] = Util::formatParams(setting, params);
+	}
+}
+
 bool GeoIP::v6() const {
-	return geo->databaseType == GEOIP_COUNTRY_EDITION_V6 || geo->databaseType == GEOIP_LARGE_COUNTRY_EDITION_V6;
+	return geo->databaseType == GEOIP_COUNTRY_EDITION_V6 || geo->databaseType == GEOIP_LARGE_COUNTRY_EDITION_V6 ||
+		geo->databaseType == GEOIP_CITY_EDITION_REV1_V6 || geo->databaseType == GEOIP_CITY_EDITION_REV0_V6;
+}
+
+bool GeoIP::city() const {
+	return geo->databaseType == GEOIP_CITY_EDITION_REV0 || geo->databaseType == GEOIP_CITY_EDITION_REV1 ||
+		geo->databaseType == GEOIP_CITY_EDITION_REV1_V6 || geo->databaseType == GEOIP_CITY_EDITION_REV0_V6;
 }
 
 } // namespace dcpp

=== modified file 'dcpp/GeoIP.h'
--- dcpp/GeoIP.h	2013-01-18 21:28:38 +0000
+++ dcpp/GeoIP.h	2013-04-16 16:11:50 +0000
@@ -22,14 +22,14 @@
 #include "CriticalSection.h"
 
 #include <string>
-#include <vector>
+#include <unordered_map>
 
 typedef struct GeoIPTag GeoIP;
 
 namespace dcpp {
 
 using std::string;
-using std::vector;
+using std::unordered_map;
 
 class GeoIP : boost::noncopyable {
 public:
@@ -44,13 +44,16 @@
 	bool decompress() const;
 	void open();
 	void close();
+	void rebuild_cities();
+	void rebuild_countries();
 	bool v6() const;
+	bool city() const;
 
 	mutable CriticalSection cs;
 	::GeoIP* geo;
 
 	const string path;
-	vector<string> cache;
+	unordered_map<int, string> cache;
 };
 
 } // namespace dcpp

=== modified file 'dcpp/GeoManager.h'
--- dcpp/GeoManager.h	2013-01-18 21:28:38 +0000
+++ dcpp/GeoManager.h	2013-04-16 16:11:50 +0000
@@ -19,13 +19,13 @@
 #ifndef DCPLUSPLUS_DCPP_GEO_MANAGER_H
 #define DCPLUSPLUS_DCPP_GEO_MANAGER_H
 
+#include <memory>
+#include <string>
+
 #include "forward.h"
 #include "GeoIP.h"
 #include "Singleton.h"
 
-#include <memory>
-#include <string>
-
 namespace dcpp {
 
 using std::string;

=== modified file 'dcpp/SettingsManager.cpp'
--- dcpp/SettingsManager.cpp	2013-04-12 21:10:13 +0000
+++ dcpp/SettingsManager.cpp	2013-04-16 16:11:50 +0000
@@ -94,10 +94,10 @@
 	"CompressTransfers", "ConfirmADLSRemoval", "ConfirmExit", "ConfirmHubClosing",
 	"ConfirmHubRemoval", "ConfirmItemRemoval", "ConfirmUserRemoval", "Coral",
 	"DontDlAlreadyQueued", "DontDLAlreadyShared", "FavShowJoins", "FilterMessages",
-	"FinishedDLOnlyFull", "FollowLinks", "GetUserCountry", "GetUserInfo", "HubUserCommands",
-	"IgnoreBotPms", "IgnoreHubPms", "OpenNewWindow", "KeepFinishedFiles", "KeepLists",
-	"ListDuplicates", "LogDownloads", "LogFilelistTransfers", "LogFinishedDownloads", "LogMainChat",
-	"LogPrivateChat", "LogStatusMessages", "LogSystem", "LogUploads", "MagnetAsk",
+	"FinishedDLOnlyFull", "FollowLinks", "GeoCity", "GetUserCountry", "GetUserInfo",
+	"HubUserCommands", "IgnoreBotPms", "IgnoreHubPms", "OpenNewWindow", "KeepFinishedFiles",
+	"KeepLists", "ListDuplicates", "LogDownloads", "LogFilelistTransfers", "LogFinishedDownloads",
+	"LogMainChat", "LogPrivateChat", "LogStatusMessages", "LogSystem", "LogUploads", "MagnetAsk",
 	"MagnetRegister", "MinimizeToTray", "NoAwayMsgToBots", "NoIpOverride", "OpenUserCmdHelp",
 	"OwnerDrawnMenus", "PopupBotPms", "PopupHubPms", "PopupPMs", "PopunderFilelist", "PopunderPm",
 	"LowestPrio", "PromptPassword", "QueueFrameShowTree", "RequireTLS", "SearchFilterShared",
@@ -239,6 +239,7 @@
 	setDefault(SEND_UNKNOWN_COMMANDS, true);
 	setDefault(MAX_HASH_SPEED, 0);
 	setDefault(OPEN_USER_CMD_HELP, true);
+	setDefault(GEO_CITY, false);
 	setDefault(GET_USER_COUNTRY, true);
 	setDefault(FAV_SHOW_JOINS, false);
 	setDefault(LOG_STATUS_MESSAGES, false);

=== modified file 'dcpp/SettingsManager.h'
--- dcpp/SettingsManager.h	2013-03-22 15:34:41 +0000
+++ dcpp/SettingsManager.h	2013-04-16 16:11:50 +0000
@@ -131,10 +131,10 @@
 		COMPRESS_TRANSFERS, CONFIRM_ADLS_REMOVAL, CONFIRM_EXIT, CONFIRM_HUB_CLOSING,
 		CONFIRM_HUB_REMOVAL, CONFIRM_ITEM_REMOVAL, CONFIRM_USER_REMOVAL, CORAL,
 		DONT_DL_ALREADY_QUEUED, DONT_DL_ALREADY_SHARED, FAV_SHOW_JOINS, FILTER_MESSAGES,
-		FINISHED_DL_ONLY_FULL, FOLLOW_LINKS, GET_USER_COUNTRY, GET_USER_INFO, HUB_USER_COMMANDS,
-		IGNORE_BOT_PMS, IGNORE_HUB_PMS, JOIN_OPEN_NEW_WINDOW, KEEP_FINISHED_FILES, KEEP_LISTS,
-		LIST_DUPES, LOG_DOWNLOADS, LOG_FILELIST_TRANSFERS, LOG_FINISHED_DOWNLOADS, LOG_MAIN_CHAT,
-		LOG_PRIVATE_CHAT, LOG_STATUS_MESSAGES, LOG_SYSTEM, LOG_UPLOADS, MAGNET_ASK,
+		FINISHED_DL_ONLY_FULL, FOLLOW_LINKS, GEO_CITY, GET_USER_COUNTRY, GET_USER_INFO,
+		HUB_USER_COMMANDS, IGNORE_BOT_PMS, IGNORE_HUB_PMS, JOIN_OPEN_NEW_WINDOW, KEEP_FINISHED_FILES,
+		KEEP_LISTS, LIST_DUPES, LOG_DOWNLOADS, LOG_FILELIST_TRANSFERS, LOG_FINISHED_DOWNLOADS,
+		LOG_MAIN_CHAT, LOG_PRIVATE_CHAT, LOG_STATUS_MESSAGES, LOG_SYSTEM, LOG_UPLOADS, MAGNET_ASK,
 		MAGNET_REGISTER, MINIMIZE_TRAY, NO_AWAYMSG_TO_BOTS, NO_IP_OVERRIDE, OPEN_USER_CMD_HELP,
 		OWNER_DRAWN_MENUS, POPUP_BOT_PMS, POPUP_HUB_PMS, POPUP_PMS, POPUNDER_FILELIST, POPUNDER_PM,
 		PRIO_LOWEST, PROMPT_PASSWORD, QUEUEFRAME_SHOW_TREE, REQUIRE_TLS, SEARCH_FILTER_SHARED,

=== modified file 'help/settings_advanced.html'
--- help/settings_advanced.html	2012-03-11 16:36:44 +0000
+++ help/settings_advanced.html	2013-04-16 16:11:50 +0000
@@ -107,6 +107,11 @@
   <dd cshelp="IDH_SETTINGS_ADVANCED_AUTO_KICK_NO_FAVS">With this option enabled, and <a href="window_settings_advanced.html#disconnect">Automatically disconnect users who leave the hub</a>, users who are in your <a href="favorite_users.html">Favorite users</a> page are not disconnected.</dd>
   <dt>Use extended menus with icons and titles</dt>
   <dd cshelp="IDH_SETTINGS_ADVANCED_OWNER_DRAWN_MENUS">Have DC++ draw its own menus, which allows icons and titles to be added. Disable to revert to classic Windows menus.</dd>
+  <dt>Use system icons when browsing files (slows browsing down a bit)</dt>
+  <dd cshelp="IDH_SETTINGS_ADVANCED_USE_SYSTEM_ICONS">
+  Ask Windows Explorer for per-extension file icons rather than using default ones distributed
+  with DC++. <i>Turning this option on might reduce file browsing performance.</i>
+  </dd>
   <dt>Use Coral network for HTTP downloads (improves reliability)</dt>
   <dd cshelp="IDH_SETTINGS_ADVANCED_CORAL">
 With this option enabled, DC++ uses

=== modified file 'help/settings_appearance.html'
--- help/settings_appearance.html	2013-03-22 15:34:41 +0000
+++ help/settings_appearance.html	2013-04-16 16:11:50 +0000
@@ -38,16 +38,24 @@
 <a href="window_users.html">Favorite Users</a> are shown.</dd>
   <dt>Sort favorite users first</dt>
   <dd cshelp="IDH_SETTINGS_APPEARANCE_SORT_FAVUSERS_FIRST">With this option enabled, those users who are <a href=window_favorite_users.html>favorite users</a> will be sorted first in user lists.</dd>
-  <dt>Use system icons when browsing files (slows browsing down a bit)</dt>
-  <dd cshelp="IDH_SETTINGS_APPEARANCE_USE_SYSTEM_ICONS">If you have this option on, DC++ will use the per-extension icons
-from Windows Explorer. <i>Turning
-on this option slows browsing file lists a bit.</i></dd>
   <dt id="guessip">Guess user country from IP</dt>
   <dd cshelp="IDH_SETTINGS_APPEARANCE_GET_USER_COUNTRY">
   When selected, DC++ displays user country information when available. DC++ uses
   <a href="http://www.maxmind.com/app/geolitecountry"; target="_blank" class="external">MaxMind's GeoIP</a>
   databases in order to do so.
   </dd>
+  <dt>City-level geolocation database (allows parameters such as %[city])</dt>
+  <dd cshelp="IDH_SETTINGS_APPEARANCE_GEO_CITY">
+  Use a more detailed geolocation database that contains information such as the city, the region,
+  the latitude and longitude, etc. Compared to the default country-level geolocation database, the
+  city-level one is bigger and consumes more memory.<br/>
+  See <a href="http://www.maxmind.com/en/geolite_city_accuracy"; target="_blank" class="external">
+	  GeoLite City Accuracy for Selected Countries</a> for more information.<br/>
+  Additional parameters that can be used in conjunction with the city-level country database:
+  <b>%[areacode]</b>, <b>%[city]</b>, <b>%[lat]</b>, <b>%[long]</b>, <b>%[metrocode]</b>,
+  <b>%[postcode]</b>, <b>%[region]</b>.
+  </dd>
+  <dt>
 </dl>
 <h2>Miscellaneous</h2>
 <dl style="margin-left: 40px;">
@@ -72,6 +80,16 @@
 	<li><b>%[officialname]</b> gets replaced by the localized country official name (eg Federal
 	Republic of Germany, French Republic on an English operating system).</li>
 </ul>
+Additional available variables when using the city-level database:
+<ul>
+	<li><b>%[areacode]</b> gets replaced by the area code (United States only).</li>
+	<li><b>%[city]</b> gets replaced by the city's name.</li>
+	<li><b>%[lat]</b> gets replaced by the latitude.</li>
+	<li><b>%[long]</b> gets replaced by the longitude.</li>
+	<li><b>%[metrocode]</b> gets replaced by the DMA / metro code (United States only).</li>
+	<li><b>%[postcode]</b> gets replaced by the city's postal code.</li>
+	<li><b>%[region]</b> gets replaced by the region's name.</li>
+</ul>
 </dd>
 <dt id="messagelines">Height of the message editing box</dt>
 <dd cshelp="IDH_SETTINGS_APPEARANCE_MESSAGE_LINES">

=== modified file 'win32/AdvancedPage.cpp'
--- win32/AdvancedPage.cpp	2013-01-18 21:28:38 +0000
+++ win32/AdvancedPage.cpp	2013-04-16 16:11:50 +0000
@@ -46,6 +46,7 @@
 	{ SettingsManager::USE_CTRL_FOR_LINE_HISTORY, N_("Use CTRL for line history"), IDH_SETTINGS_ADVANCED_USE_CTRL_FOR_LINE_HISTORY },
 	{ SettingsManager::AUTO_KICK_NO_FAVS, N_("Don't automatically disconnect favorite users who leave the hub"), IDH_SETTINGS_ADVANCED_AUTO_KICK_NO_FAVS },
 	{ SettingsManager::OWNER_DRAWN_MENUS, N_("Use extended menus with icons and titles"), IDH_SETTINGS_ADVANCED_OWNER_DRAWN_MENUS },
+	{ SettingsManager::USE_SYSTEM_ICONS, N_("Use system icons when browsing files (slows browsing down a bit)"), IDH_SETTINGS_ADVANCED_USE_SYSTEM_ICONS },
 	{ SettingsManager::CORAL, N_("Use Coral network for HTTP downloads (improves reliability)"), IDH_SETTINGS_ADVANCED_CORAL },
 	{ SettingsManager::SEGMENTED_DL, N_("Enable segmented downloads"), IDH_SETTINGS_ADVANCED_SEGMENTED_DL },
 	{ 0, 0 }

=== modified file 'win32/AppearancePage.cpp'
--- win32/AppearancePage.cpp	2013-03-22 15:34:41 +0000
+++ win32/AppearancePage.cpp	2013-04-16 16:11:50 +0000
@@ -44,8 +44,8 @@
 	{ SettingsManager::SHOW_JOINS, N_("Show joins / parts in chat by default"), IDH_SETTINGS_APPEARANCE_SHOW_JOINS },
 	{ SettingsManager::FAV_SHOW_JOINS, N_("Only show joins / parts for favorite users"), IDH_SETTINGS_APPEARANCE_FAV_SHOW_JOINS },
 	{ SettingsManager::SORT_FAVUSERS_FIRST, N_("Sort favorite users first"), IDH_SETTINGS_APPEARANCE_SORT_FAVUSERS_FIRST },
-	{ SettingsManager::USE_SYSTEM_ICONS, N_("Use system icons when browsing files (slows browsing down a bit)"), IDH_SETTINGS_APPEARANCE_USE_SYSTEM_ICONS },
 	{ SettingsManager::GET_USER_COUNTRY, N_("Guess user country from IP"), IDH_SETTINGS_APPEARANCE_GET_USER_COUNTRY },
+	{ SettingsManager::GEO_CITY, N_("City-level geolocation database (allows parameters such as %[city])"), IDH_SETTINGS_APPEARANCE_GEO_CITY },
 	{ 0, 0 }
 };
 

=== modified file 'win32/MainWindow.cpp'
--- win32/MainWindow.cpp	2013-04-15 17:29:39 +0000
+++ win32/MainWindow.cpp	2013-04-16 16:11:50 +0000
@@ -111,10 +111,13 @@
 awayIdle(false),
 fullSlots(false)
 {
+	// Don't forget to update version.xml when changing these links!
 	links.homepage = _T("http://dcplusplus.sourceforge.net/";);
 	links.downloads = links.homepage + _T("download/");
 	links.geoip6 = _T("http://geolite.maxmind.com/download/geoip/database/GeoIPv6.dat.gz";);
 	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.faq = links.homepage + _T("faq/");
 	links.help = links.homepage + _T("help/");
 	links.discuss = links.homepage + _T("discussion/");
@@ -1172,6 +1175,7 @@
 	auto prevProxy = CONNSETTING(OUTGOING_CONNECTIONS);
 
 	auto prevGeo = SETTING(GET_USER_COUNTRY);
+	auto prevGeoCity = SETTING(GEO_CITY);
 	auto prevGeoFormat = SETTING(COUNTRY_FORMAT);
 
 	auto prevFont = SETTING(MAIN_FONT);
@@ -1203,10 +1207,14 @@
 		ClientManager::getInstance()->infoUpdated();
 
 		bool rebuildGeo = prevGeo && SETTING(COUNTRY_FORMAT) != prevGeoFormat;
-		if(SETTING(GET_USER_COUNTRY) != prevGeo) {
+		if(SETTING(GET_USER_COUNTRY) != prevGeo || SETTING(GEO_CITY) != prevGeoCity) {
 			if(SETTING(GET_USER_COUNTRY)) {
 				GeoManager::getInstance()->init();
-				checkGeoUpdate();
+				if(SETTING(GEO_CITY) != prevGeoCity) {
+					updateGeo();
+				} else {
+					checkGeoUpdate();
+				}
 			} else {
 				GeoManager::getInstance()->close();
 				rebuildGeo = false;
@@ -1405,6 +1413,14 @@
 				links.geoip4 = Text::toT(xml.getChildData());
 			}
 			xml.resetCurrentChild();
+			if(xml.findChild("GeoIPv6_City")) {
+				links.geoip6_city = Text::toT(xml.getChildData());
+			}
+			xml.resetCurrentChild();
+			if(xml.findChild("GeoIPv4_City")) {
+				links.geoip4_city = Text::toT(xml.getChildData());
+			}
+			xml.resetCurrentChild();
 			if(xml.findChild("Faq")) {
 				links.faq = Text::toT(xml.getChildData());
 			}
@@ -1485,6 +1501,10 @@
 	}
 }
 
+namespace { string geoType(bool v6) {
+	return str(F_("%1%-level %2%") % (SETTING(GEO_CITY) ? _("city") : _("country")) % (v6 ? "IPv6" : "IPv4"));
+} }
+
 void MainWindow::updateGeo(bool v6) {
 	auto& conn = conns[v6 ? CONN_GEO_V6 : CONN_GEO_V4];
 	if(conn)
@@ -1494,20 +1514,21 @@
 	try {
 		file.reset(new File(GeoManager::getDbPath(v6) + ".gz", File::WRITE, File::CREATE | File::TRUNCATE));
 	} catch(const FileException&) {
-		LogManager::getInstance()->message(str(F_("The %1% GeoIP database could not be updated") % (v6 ? "IPv6" : "IPv4")));
+		LogManager::getInstance()->message(str(F_("The %1% GeoIP database could not be updated") % geoType(v6)));
 		return;
 	}
 
-	LogManager::getInstance()->message(str(F_("Updating the %1% GeoIP database...") % (v6 ? "IPv6" : "IPv4")));
-	conn = HttpManager::getInstance()->download(Text::fromT(v6 ? links.geoip6 : links.geoip4), file.get());
+	LogManager::getInstance()->message(str(F_("Updating the %1% GeoIP database...") % geoType(v6)));
+	conn = HttpManager::getInstance()->download(Text::fromT(SETTING(GEO_CITY) ?
+		(v6 ? links.geoip6_city : links.geoip4_city) : (v6 ? links.geoip6 : links.geoip4)), file.get());
 }
 
 void MainWindow::completeGeoUpdate(bool v6, bool success) {
 	if(success) {
 		GeoManager::getInstance()->update(v6);
-		LogManager::getInstance()->message(str(F_("The %1% GeoIP database has been successfully updated") % (v6 ? "IPv6" : "IPv4")));
+		LogManager::getInstance()->message(str(F_("The %1% GeoIP database has been successfully updated") % geoType(v6)));
 	} else {
-		LogManager::getInstance()->message(str(F_("The %1% GeoIP database could not be updated") % (v6 ? "IPv6" : "IPv4")));
+		LogManager::getInstance()->message(str(F_("The %1% GeoIP database could not be updated") % geoType(v6)));
 	}
 }
 

=== modified file 'win32/MainWindow.h'
--- win32/MainWindow.h	2013-04-15 17:29:39 +0000
+++ win32/MainWindow.h	2013-04-16 16:11:50 +0000
@@ -98,6 +98,8 @@
 		tstring downloads;
 		tstring geoip6;
 		tstring geoip4;
+		tstring geoip6_city;
+		tstring geoip4_city;
 		tstring faq;
 		tstring help;
 		tstring discuss;