[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[vidalia-svn] r4378: Drop remote GeoIP lookups and use either a local city-level (vidalia/trunk/src/vidalia/network)



Author: edmanm
Date: 2010-08-05 16:28:54 -0400 (Thu, 05 Aug 2010)
New Revision: 4378

Added:
   vidalia/trunk/src/vidalia/network/CountryInfo.cpp
   vidalia/trunk/src/vidalia/network/CountryInfo.h
   vidalia/trunk/src/vidalia/network/GeoIpRecord.cpp
   vidalia/trunk/src/vidalia/network/GeoIpRecord.h
Removed:
   vidalia/trunk/src/vidalia/network/GeoIp.cpp
   vidalia/trunk/src/vidalia/network/GeoIp.h
Modified:
   vidalia/trunk/src/vidalia/network/GeoIpResolver.cpp
   vidalia/trunk/src/vidalia/network/GeoIpResolver.h
   vidalia/trunk/src/vidalia/network/NetViewer.cpp
   vidalia/trunk/src/vidalia/network/NetViewer.h
   vidalia/trunk/src/vidalia/network/RouterListItem.cpp
   vidalia/trunk/src/vidalia/network/RouterListItem.h
   vidalia/trunk/src/vidalia/network/RouterListWidget.cpp
   vidalia/trunk/src/vidalia/network/RouterListWidget.h
   vidalia/trunk/src/vidalia/network/TorMapImageView.cpp
   vidalia/trunk/src/vidalia/network/TorMapImageView.h
   vidalia/trunk/src/vidalia/network/TorMapWidget.cpp
   vidalia/trunk/src/vidalia/network/TorMapWidget.h
Log:

Drop remote GeoIP lookups and use either a local city-level database, a
local country-level database, or Tor's built-in database depending on
build and vidalia.conf optinos. This massive commit includes a number of
changes:
  - Move the CountryInfo class over to src/vidalia/network since that is a
    more reasonable location for it, and add the ability to parse the 
    built-in country coordinates CSV file.
  - Rename GeoIp to GeoIpRecord to avoid conflicts with the MaxMind GeoIp
    library.


Copied: vidalia/trunk/src/vidalia/network/CountryInfo.cpp (from rev 4346, vidalia/trunk/src/common/CountryInfo.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/network/CountryInfo.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/CountryInfo.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -0,0 +1,281 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CountryInfo.cpp
+** \version $Id$
+** \brief Provides a method to look up a localized country name given its
+** ISO 3166 2-letter country code.
+*/
+
+#include "CountryInfo.h"
+#include "Vidalia.h"
+
+#include <QMap>
+#include <QHash>
+#include <QFile>
+#include <QStringList>
+
+#define COUNTRY_LOCATION_FILE ":/geoip/country-coordinates.csv"
+
+
+CountryInfo::CountryInfo(QObject *parent)
+  : QObject(parent)
+{
+}
+
+QString
+CountryInfo::countryName(const QString &countryCode)
+{
+  static QMap<QString,QString> db;
+  if (db.isEmpty()) {
+    db.insert("af", QT_TR_NOOP("Afghanistan"));
+    db.insert("al", QT_TR_NOOP("Albania "));
+    db.insert("dz", QT_TR_NOOP("Algeria "));
+    db.insert("ad", QT_TR_NOOP("Andorra"));
+    db.insert("ao", QT_TR_NOOP("Angola"));
+    db.insert("ag", QT_TR_NOOP("Antigua & Barbuda"));
+    db.insert("ar", QT_TR_NOOP("Argentina"));
+    db.insert("am", QT_TR_NOOP("Armenia"));
+    db.insert("au", QT_TR_NOOP("Australia"));
+    db.insert("at", QT_TR_NOOP("Austria "));
+    db.insert("az", QT_TR_NOOP("Azerbaijan"));
+    db.insert("bs", QT_TR_NOOP("Bahamas"));
+    db.insert("bh", QT_TR_NOOP("Bahrain "));
+    db.insert("bd", QT_TR_NOOP("Bangladesh"));
+    db.insert("bb", QT_TR_NOOP("Barbados"));
+    db.insert("by", QT_TR_NOOP("Belarus"));
+    db.insert("be", QT_TR_NOOP("Belgium"));
+    db.insert("bz", QT_TR_NOOP("Belize"));
+    db.insert("bj", QT_TR_NOOP("Benin "));
+    db.insert("bt", QT_TR_NOOP("Bhutan"));
+    db.insert("bo", QT_TR_NOOP("Bolivia"));
+    db.insert("ba", QT_TR_NOOP("Bosnia & Herzegovina"));
+    db.insert("bw", QT_TR_NOOP("Botswana"));
+    db.insert("br", QT_TR_NOOP("Brazil"));
+    db.insert("bn", QT_TR_NOOP("Brunei Darussalam"));
+    db.insert("bg", QT_TR_NOOP("Bulgaria"));
+    db.insert("bf", QT_TR_NOOP("Burkina Faso"));
+    db.insert("bi", QT_TR_NOOP("Burundi"));
+    db.insert("kh", QT_TR_NOOP("Cambodia"));
+    db.insert("cm", QT_TR_NOOP("Cameroon"));
+    db.insert("ca", QT_TR_NOOP("Canada"));
+    db.insert("cv", QT_TR_NOOP("Cape Verde"));
+    db.insert("cf", QT_TR_NOOP("Central African Republic"));
+    db.insert("td", QT_TR_NOOP("Chad"));
+    db.insert("cl", QT_TR_NOOP("Chile"));
+    db.insert("cn", QT_TR_NOOP("China"));
+    db.insert("co", QT_TR_NOOP("Colombia"));
+    db.insert("km", QT_TR_NOOP("Comoros"));
+    db.insert("cd", QT_TR_NOOP("Congo, The Democratic Republic of the"));
+    db.insert("cg", QT_TR_NOOP("Congo"));
+    db.insert("cr", QT_TR_NOOP("Costa Rica"));
+    db.insert("ci", QT_TR_NOOP("Cote dâIvoire"));
+    db.insert("hr", QT_TR_NOOP("Croatia"));
+    db.insert("cu", QT_TR_NOOP("Cuba"));
+    db.insert("cy", QT_TR_NOOP("Cyprus"));
+    db.insert("cz", QT_TR_NOOP("Czech Republic"));
+    db.insert("dk", QT_TR_NOOP("Denmark"));
+    db.insert("dj", QT_TR_NOOP("Djibouti"));
+    db.insert("dm", QT_TR_NOOP("Dominica"));
+    db.insert("do", QT_TR_NOOP("Dominican Republic"));
+    db.insert("ec", QT_TR_NOOP("Ecuador"));
+    db.insert("eg", QT_TR_NOOP("Egypt"));
+    db.insert("sv", QT_TR_NOOP("El Salvador"));
+    db.insert("gq", QT_TR_NOOP("Equatorial Guinea"));
+    db.insert("er", QT_TR_NOOP("Eritrea"));
+    db.insert("ee", QT_TR_NOOP("Estonia"));
+    db.insert("et", QT_TR_NOOP("Ethiopia "));
+    db.insert("fj", QT_TR_NOOP("Fiji "));
+    db.insert("fi", QT_TR_NOOP("Finland "));
+    db.insert("fr", QT_TR_NOOP("France"));
+    db.insert("ga", QT_TR_NOOP("Gabon"));
+    db.insert("gm", QT_TR_NOOP("Gambia"));
+    db.insert("ge", QT_TR_NOOP("Georgia"));
+    db.insert("de", QT_TR_NOOP("Germany"));
+    db.insert("gh", QT_TR_NOOP("Ghana"));
+    db.insert("gr", QT_TR_NOOP("Greece "));
+    db.insert("gd", QT_TR_NOOP("Grenada"));
+    db.insert("gt", QT_TR_NOOP("Guatemala"));
+    db.insert("gu", QT_TR_NOOP("Guam "));
+    db.insert("gn", QT_TR_NOOP("Guinea"));
+    db.insert("gw", QT_TR_NOOP("Guinea-Bissau"));
+    db.insert("gy", QT_TR_NOOP("Guyana"));
+    db.insert("hk", QT_TR_NOOP("Hong Kong"));
+    db.insert("ht", QT_TR_NOOP("Haiti"));
+    db.insert("hn", QT_TR_NOOP("Honduras"));
+    db.insert("hu", QT_TR_NOOP("Hungary "));
+    db.insert("is", QT_TR_NOOP("Iceland "));
+    db.insert("in", QT_TR_NOOP("India "));
+    db.insert("id", QT_TR_NOOP("Indonesia "));
+    db.insert("ir", QT_TR_NOOP("Iran "));
+    db.insert("iq", QT_TR_NOOP("Iraq "));
+    db.insert("ie", QT_TR_NOOP("Ireland "));
+    db.insert("il", QT_TR_NOOP("Israel"));
+    db.insert("it", QT_TR_NOOP("Italy"));
+    db.insert("jm", QT_TR_NOOP("Jamaica"));
+    db.insert("jp", QT_TR_NOOP("Japan"));
+    db.insert("jo", QT_TR_NOOP("Jordan"));
+    db.insert("kz", QT_TR_NOOP("Kazakhstan"));
+    db.insert("ke", QT_TR_NOOP("Kenya"));
+    db.insert("ki", QT_TR_NOOP("Kiribati"));
+    db.insert("kp", QT_TR_NOOP("Korea, North "));
+    db.insert("kr", QT_TR_NOOP("Korea, South "));
+    db.insert("kw", QT_TR_NOOP("Kuwait"));
+    db.insert("kg", QT_TR_NOOP("Kyrgyzstan"));
+    db.insert("la", QT_TR_NOOP("Laos"));
+    db.insert("lv", QT_TR_NOOP("Latvia"));
+    db.insert("lb", QT_TR_NOOP("Lebanon"));
+    db.insert("ls", QT_TR_NOOP("Lesotho"));
+    db.insert("lr", QT_TR_NOOP("Liberia"));
+    db.insert("ly", QT_TR_NOOP("Libya "));
+    db.insert("li", QT_TR_NOOP("Liechtenstein"));
+    db.insert("lt", QT_TR_NOOP("Lithuania"));
+    db.insert("lu", QT_TR_NOOP("Luxembourg"));
+    db.insert("mk", QT_TR_NOOP("Macedonia"));
+    db.insert("mg", QT_TR_NOOP("Madagascar"));
+    db.insert("mw", QT_TR_NOOP("Malawi"));
+    db.insert("my", QT_TR_NOOP("Malaysia"));
+    db.insert("mv", QT_TR_NOOP("Maldives "));
+    db.insert("ml", QT_TR_NOOP("Mali"));
+    db.insert("mt", QT_TR_NOOP("Malta"));
+    db.insert("mh", QT_TR_NOOP("Marshall Islands"));
+    db.insert("mr", QT_TR_NOOP("Mauritania"));
+    db.insert("mu", QT_TR_NOOP("Mauritius"));
+    db.insert("mx", QT_TR_NOOP("Mexico "));
+    db.insert("fm", QT_TR_NOOP("Micronesia"));
+    db.insert("md", QT_TR_NOOP("Moldova"));
+    db.insert("mc", QT_TR_NOOP("Monaco"));
+    db.insert("mn", QT_TR_NOOP("Mongolia"));
+    db.insert("me", QT_TR_NOOP("Montenegro"));
+    db.insert("ma", QT_TR_NOOP("Morocco"));
+    db.insert("mz", QT_TR_NOOP("Mozambique"));
+    db.insert("mm", QT_TR_NOOP("Myanmar "));
+    db.insert("na", QT_TR_NOOP("Namibia"));
+    db.insert("nr", QT_TR_NOOP("Nauru"));
+    db.insert("np", QT_TR_NOOP("Nepal"));
+    db.insert("nl", QT_TR_NOOP("Netherlands"));
+    db.insert("nz", QT_TR_NOOP("New Zealand"));
+    db.insert("ni", QT_TR_NOOP("Nicaragua"));
+    db.insert("ne", QT_TR_NOOP("Niger"));
+    db.insert("ng", QT_TR_NOOP("Nigeria"));
+    db.insert("no", QT_TR_NOOP("Norway"));
+    db.insert("om", QT_TR_NOOP("Oman"));
+    db.insert("pk", QT_TR_NOOP("Pakistan"));
+    db.insert("pw", QT_TR_NOOP("Palau"));
+    db.insert("ps", QT_TR_NOOP("Palestine"));
+    db.insert("pa", QT_TR_NOOP("Panama"));
+    db.insert("pg", QT_TR_NOOP("Papua New Guinea"));
+    db.insert("py", QT_TR_NOOP("Paraguay"));
+    db.insert("pe", QT_TR_NOOP("Peru"));
+    db.insert("ph", QT_TR_NOOP("Philippines"));
+    db.insert("pl", QT_TR_NOOP("Poland"));
+    db.insert("pt", QT_TR_NOOP("Portugal"));
+    db.insert("qa", QT_TR_NOOP("Qatar"));
+    db.insert("ro", QT_TR_NOOP("Romania"));
+    db.insert("ru", QT_TR_NOOP("Russia"));
+    db.insert("rw", QT_TR_NOOP("Rwanda"));
+    db.insert("kn", QT_TR_NOOP("Saint Kitts & Nevis"));
+    db.insert("lc", QT_TR_NOOP("Saint Lucia"));
+    db.insert("vc", QT_TR_NOOP("Saint Vincent & the Grenadines"));
+    db.insert("ws", QT_TR_NOOP("Samoa"));
+    db.insert("sm", QT_TR_NOOP("San Marino"));
+    db.insert("st", QT_TR_NOOP("Sao Tome & Principe"));
+    db.insert("sa", QT_TR_NOOP("Saudi Arabia"));
+    db.insert("sn", QT_TR_NOOP("Senegal"));
+    db.insert("rs", QT_TR_NOOP("Serbia"));
+    db.insert("sc", QT_TR_NOOP("Seychelles"));
+    db.insert("sl", QT_TR_NOOP("Sierra Leone"));
+    db.insert("sg", QT_TR_NOOP("Singapore"));
+    db.insert("sk", QT_TR_NOOP("Slovakia"));
+    db.insert("si", QT_TR_NOOP("Slovenia"));
+    db.insert("sb", QT_TR_NOOP("Solomon Islands"));
+    db.insert("so", QT_TR_NOOP("Somalia"));
+    db.insert("za", QT_TR_NOOP("South Africa"));
+    db.insert("es", QT_TR_NOOP("Spain"));
+    db.insert("lk", QT_TR_NOOP("Sri Lanka"));
+    db.insert("sd", QT_TR_NOOP("Sudan"));
+    db.insert("sr", QT_TR_NOOP("Suriname"));
+    db.insert("sz", QT_TR_NOOP("Swaziland"));
+    db.insert("se", QT_TR_NOOP("Sweden"));
+    db.insert("ch", QT_TR_NOOP("Switzerland"));
+    db.insert("sy", QT_TR_NOOP("Syria"));
+    db.insert("tw", QT_TR_NOOP("Taiwan "));
+    db.insert("tj", QT_TR_NOOP("Tajikistan"));
+    db.insert("tz", QT_TR_NOOP("Tanzania"));
+    db.insert("th", QT_TR_NOOP("Thailand"));
+    db.insert("tl", QT_TR_NOOP("Timor-Leste (East Timor)"));
+    db.insert("tg", QT_TR_NOOP("Togo"));
+    db.insert("to", QT_TR_NOOP("Tonga"));
+    db.insert("tt", QT_TR_NOOP("Trinidad & Tobago"));
+    db.insert("tn", QT_TR_NOOP("Tunisia"));
+    db.insert("tr", QT_TR_NOOP("Turkey"));
+    db.insert("tm", QT_TR_NOOP("Turkmenistan"));
+    db.insert("tv", QT_TR_NOOP("Tuvalu"));
+    db.insert("ug", QT_TR_NOOP("Uganda"));
+    db.insert("ua", QT_TR_NOOP("Ukraine"));
+    db.insert("ae", QT_TR_NOOP("United Arab Emirates"));
+    db.insert("gb", QT_TR_NOOP("United Kingdom"));
+    db.insert("us", QT_TR_NOOP("United States"));
+    db.insert("uy", QT_TR_NOOP("Uruguay"));
+    db.insert("uz", QT_TR_NOOP("Uzbekistan"));
+    db.insert("vu", QT_TR_NOOP("Vanuatu"));
+    db.insert("va", QT_TR_NOOP("Vatican"));
+    db.insert("ve", QT_TR_NOOP("Venezuela"));
+    db.insert("vn", QT_TR_NOOP("Vietnam"));
+    db.insert("eh", QT_TR_NOOP("Western Sahara"));
+    db.insert("ye", QT_TR_NOOP("Yemen"));
+    db.insert("zr", QT_TR_NOOP("Zaire"));
+    db.insert("zm", QT_TR_NOOP("Zambia"));
+    db.insert("zw", QT_TR_NOOP("Zimbabwe"));
+  }
+
+  QString cc = countryCode.toLower();
+  if (db.contains(cc))
+    return  tr(db.value(cc).toLocal8Bit().data());
+  return QString();
+}
+
+QPair<float,float>
+CountryInfo::countryLocation(const QString &countryCode)
+{
+  static QHash<QString,QPair<float,float> > db;
+  if (db.isEmpty()) {
+    /* Load the country coordinates database */
+    QFile infile(COUNTRY_LOCATION_FILE);
+    if (! infile.open(QIODevice::ReadOnly | QIODevice::Text))
+      return QPair<float,float>(-180.0, -180.0);
+
+    while (! infile.atEnd()) {
+      /* Read and parse a single line from the input file */
+      bool ok;
+      QString line = infile.readLine().trimmed();
+      vInfo(line);
+      QStringList parts = line.split(",");
+      if (parts.size() >= 3) {
+        float latitude = parts[1].toFloat(&ok);
+        if (! ok)
+          continue;
+        float longitude = parts[2].toFloat(&ok);
+        if (! ok)
+          continue;
+        db.insert(parts[0], QPair<float,float>(latitude, longitude));
+      }
+    }
+    vInfo("Loaded %1 country location entries from built-in database.").arg(db.size());
+    infile.close();
+  }
+  
+  QString cc = countryCode.toLower();
+  if (db.contains(cc))
+    return db.value(cc);
+  return QPair<float,float>(-180.0, -180.0);
+}
+

Copied: vidalia/trunk/src/vidalia/network/CountryInfo.h (from rev 4346, vidalia/trunk/src/common/CountryInfo.h)
===================================================================
--- vidalia/trunk/src/vidalia/network/CountryInfo.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/CountryInfo.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -0,0 +1,48 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CountryInfo.h
+** \version $Id$
+** \brief Provides a method to look up a localized country name given its
+** ISO 3166-1 2-letter country code.
+*/
+
+#ifndef _COUNTRYINFO_H
+#define _COUNTRYINFO_H
+
+#include <QObject>
+#include <QString>
+#include <QPair>
+
+
+class CountryInfo : public QObject
+{
+  Q_OBJECT
+
+public:
+  /** Default constructor.
+   */
+  CountryInfo(QObject *parent = 0);
+
+  /** Returns the name of the country represented by <b>countryCode</b>, where
+   * <b>countryCode</b> is a 2-letter ISO 3166-1 alpha-2 two-letter country
+   * code. The name will be returned translated to the current locale if an
+   * appropriate QTranslator is currently installed.
+   */
+  static QString countryName(const QString &countryCode);
+
+  /**
+   */
+  static QPair<float,float> countryLocation(const QString &countryCode);
+};
+
+#endif
+

Deleted: vidalia/trunk/src/vidalia/network/GeoIp.cpp

Deleted: vidalia/trunk/src/vidalia/network/GeoIp.h

Copied: vidalia/trunk/src/vidalia/network/GeoIpRecord.cpp (from rev 4346, vidalia/trunk/src/vidalia/network/GeoIp.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/network/GeoIpRecord.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/GeoIpRecord.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -0,0 +1,85 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpRecord.cpp
+** \version $Id$
+** \brief Associates an IP with a geographic location
+*/
+
+#include "GeoIpRecord.h"
+
+#include <QStringList>
+
+/** Verifies a latitude is between -90.0 and 90.0 degrees. */
+#define IS_VALID_LATITUDE(x)    (((x) >= -90.0) && ((x) <= 90.0))
+/** Verifies a longitude is between -180.0 and 180.0 degrees. */
+#define IS_VALID_LONGITUDE(x)   (((x) >= -180.0) && ((x) <= 180.0))
+
+
+GeoIpRecord::GeoIpRecord()
+{
+  _latitude  = 0.0;
+  _longitude = 0.0;
+}
+
+GeoIpRecord::GeoIpRecord(const QHostAddress &ip, float latitude, float longitude,
+                         const QString &country, const QString &countryCode)
+{
+  _ip = ip;
+  _latitude = latitude;
+  _longitude = longitude;
+  _country  = country;
+  _countryCode = countryCode;
+}
+
+GeoIpRecord::GeoIpRecord(const QHostAddress &ip, float latitude, float longitude,
+                         const QString &city, const QString &region,
+                         const QString &country, const QString &countryCode) 
+{
+  _ip = ip;
+  _latitude = latitude;
+  _longitude = longitude;
+  _city = city;
+  _region = region;
+  _country = country;
+  _countryCode = countryCode;
+}
+
+bool
+GeoIpRecord::isValid() const
+{
+  return (! _ip.isNull()
+            && IS_VALID_LATITUDE(_latitude)
+            && IS_VALID_LONGITUDE(_longitude));
+}
+
+QString
+GeoIpRecord::toString() const
+{
+  QStringList location;
+
+  /* Add the city name (if present) */
+  if (!_city.isEmpty())
+    location << _city;
+
+  /* Add the full state or region name (if present) */
+  if (!_region.isEmpty() && _region != _city)
+      location << _region;
+
+  /* Add the country name or the country code (if present) */
+  if (!_country.isEmpty())
+    location << _country;
+  else if (!_countryCode.isEmpty())
+    location << _countryCode;
+
+  return location.join(", ");
+}
+

Copied: vidalia/trunk/src/vidalia/network/GeoIpRecord.h (from rev 4346, vidalia/trunk/src/vidalia/network/GeoIp.h)
===================================================================
--- vidalia/trunk/src/vidalia/network/GeoIpRecord.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/GeoIpRecord.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -0,0 +1,101 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpRecord.h
+** \version $Id$
+** \brief Associates an IP with a geographic location
+*/
+
+#ifndef _GEOIPRECORD_H
+#define _GEOIPRECORD_H
+
+#include <QHash>
+#include <QString>
+#include <QHostAddress>
+
+
+class GeoIpRecord
+{
+public:
+  /** Default constructor. Creates an empty GeoIpRecord object.
+   */
+  GeoIpRecord();
+
+  /**
+   */
+  GeoIpRecord(const QHostAddress &ip, float latitude, float longitude,
+              const QString &country, const QString &countryCode);
+
+  /**
+   */
+  GeoIpRecord(const QHostAddress &ip, float latitude, float longitude,
+              const QString &city, const QString &region,
+              const QString &country, const QString &countryCode);
+
+  /** Returns the IP address associated with this GeoIP object.
+   */
+  QHostAddress ip() const { return _ip; }
+
+  /** Returns the latitude portion of the geographic coordinates associated
+   * with this IP address or range of IP addresses.
+   */
+  float latitude() const { return _latitude; }
+
+  /** Returns the longitude portion of the geographic coordinates associated
+   * with this IP address or range of IP addresses.
+   */
+  float longitude() const { return _longitude; }
+
+  /** Returns the name of the city associated with this IP address, if known.
+   * Otherwise, returns an empty QString.
+   */
+  QString city() const { return _city; }
+
+  /** Returns the full region name (e.g., state) in which this IP address 
+   * resides, if known. Otherwise, returns an empty QString.
+   */
+  QString region() const { return _region; }
+
+  /** Returns the full name of the country associated with this IP address
+   * or range of IP addresses, if known. Otherwise, returns an empty QString.
+   */
+  QString country() const { return _country; }
+
+  /** Returns the ISO 3166-1 alpha-2 two-letter country code of the country
+   * associated with this IP address or range of IP addresses, if known.
+   * Otherwise, returns an empty QString.
+   */
+  QString countryCode() const { return _countryCode; }
+
+  /** Returns a human-readable string of city, region(state), and country.
+   * Some fields may be absent if they are not known. If no fields are known,
+   * this will return an empty QString.
+   */
+  QString toString() const;
+
+  /** Returns true if the GeoIpRecord object is valid. A valid GeoIpRecord object must
+   * have valid IP address, valid latitude and longitude coordinates and a 
+   * two-letter country code.
+   */
+  bool isValid() const;
+
+private:
+  QHostAddress _ip; /**< IP address for this location. */
+  float _latitude;  /**< Latitudinal coordinate for this IP's location. */
+  float _longitude; /**< Longitudinal coordinate for this IP's location. */
+  QString _city;    /**< City in which this IP lives. */
+  QString _region;   /**< State or district in which this IP lives. */
+  QString _country; /**< Country in which this IP lives. */
+  QString _countryCode; /**< ISO-3166-1 alpha-2 country code. */
+};
+
+#endif
+

Modified: vidalia/trunk/src/vidalia/network/GeoIpResolver.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/GeoIpResolver.cpp	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/GeoIpResolver.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -11,273 +11,77 @@
 /*
 ** \file GeoIpResolver.cpp
 ** \version $Id$
-** \brief Requests GeoIP information and caches the result
+** \brief Retrieves GeoIP information either from Tor or from a local
+** database and returns the result.
 */
 
 #include "GeoIpResolver.h"
-#include "GeoIpRequest.h"
-#include "GeoIpResponse.h"
-#include "GeoIp.h"
+#include "GeoIpRecord.h"
 #include "Vidalia.h"
 
-#include "stringutil.h"
-#include "TorSslSocket.h"
 
-/** Host for the GeoIP information. */ 
-#define GEOIP_HOST    "geoips.vidalia-project.net"
-/** The SSL GeoIP service runs on port 443. */
-#define GEOIP_SSL_PORT  443
-/** Page that we request the GeoIP information from. */
-#define GEOIP_PAGE    "/cgi-bin/geoip.py"
-
-
 /** Default constructor. */
 GeoIpResolver::GeoIpResolver(QObject *parent)
-  : QObject(parent)
+  : QObject(parent), _useLocalDatabase(false)
 {
-  _socksAddr = QHostAddress::LocalHost;
-  _socksPort = 9050;
-  _cache = new GeoIpCache(this);
 }
 
-/** Sets the address and port of Tor, through which GeoIP requests will be
- * made. */
-void
-GeoIpResolver::setSocksHost(const QHostAddress &addr, quint16 port)
-{
-  _socksAddr = addr;
-  _socksPort = port;
-}
-
-/** Resolves <b>ip</b> to geographic information if it is cached. A resolved()
- * signal will be emitted and true returned if we have cached geographic
- * information for <b>ip</b>. Otherwise, this returns false. */
 bool
-GeoIpResolver::resolveFromCache(const QHostAddress &ip)
+GeoIpResolver::setLocalDatabase(const QString &databaseFile)
 {
-  if (_cache->contains(ip)) {
-    emit resolved(-1, QList<GeoIp>() << _cache->geoIpForAddress(ip));
-    return true;
-  }
+#if defined(USE_GEOIP)
+  return _database.open(databaseFile);
+#else
   return false;
+#endif
 }
 
-/** Resolves a list of IPs to a geographic location, but only those which are
- * cached. Returns a list of IPs that were not in the cache. */
-QList<QHostAddress>
-GeoIpResolver::resolveFromCache(const QList<QHostAddress> &ips)
-{
-  QList<GeoIp> cached;
-
-  /* Build a list of which IPs have cached GeoIp information */
-  foreach (QHostAddress ip, ips) {
-    if (_cache->contains(ip))
-      cached << _cache->geoIpForAddress(ip);
-  }
-
-  /* If any were cached, emit their results now */
-  if (cached.size() > 0) {
-    vInfo("Resolved %1 GeoIP entries from cache.").arg(cached.size());
-    emit resolved(-1, cached);
-  }
-  return ips;
-}
-
-/** Resolves a single IP to a geographic location. */
-int
-GeoIpResolver::resolve(const QHostAddress &ip)
-{
-  return resolve(QList<QHostAddress>() << ip);
-}
-
-/** Called when the socket has connected to the Geo IP host. */
 void
-GeoIpResolver::connected()
+GeoIpResolver::setUseLocalDatabase(bool useLocalDatabase)
 {
-  /* Find the socket and request for whoever called this slot */ 
-  QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
-  if (!_requestList.contains(socket)) {
-    return;
-  }
-  GeoIpRequest *req = static_cast<GeoIpRequest *>(_requestList.value(socket));
-
-  vInfo("Connected to the GeoIP host. Sending request for %1 uncached "
-        "GeoIP entries. (request id %2)").arg(req->size()).arg(req->id());
-
-  /* Send the request */
-  socket->write(req->request());
+  _useLocalDatabase = useLocalDatabase;
 }
 
-/** Called when the socket has disconnected from the Geo IP host. */
-void
-GeoIpResolver::disconnected()
+GeoIpRecord
+GeoIpResolver::resolveUsingTor(const QHostAddress &ip)
 {
-  /* Find the socket and request for whoever called this slot */ 
-  QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
-  if (!_requestList.contains(socket)) {
-    return;
+  QString countryCode = Vidalia::torControl()->ipToCountry(ip);
+  if (! countryCode.isEmpty()) {
+    QPair<float,float> coords = CountryInfo::countryLocation(countryCode);
+    return GeoIpRecord(ip, coords.first, coords.second,
+                       CountryInfo::countryName(countryCode),
+                       countryCode);
   }
-  GeoIpRequest *req = static_cast<GeoIpRequest *>(_requestList.take(socket));
-
-  /* Read and parse the response header */
-  GeoIpResponse response = GeoIpResponse(socket->readAll());
-
-  /* Check the response code and see what we got */
-  if (response.statusCode() == 200) {
-    /* We got a 200 OK, so get the Geo IP information from the response body
-     * and cache the results. */
-    parseGeoIpResponse(response.content(), req);
-  } else {
-    /* We failed to get the Geo IP information, so emit resolveFailed and
-     * include the HTTP status message. */
-    vWarn("GeoIP resolution failed (request id %1): %2").arg(req->id())
-                                             .arg(response.statusMessage());
-    emit resolveFailed(req->id(), response.statusMessage());
-  }
-  /* Close the socket and clean up */
-  socket->close();
-  delete socket;
-  delete req;
+  return GeoIpRecord();
 }
 
-void
-GeoIpResolver::parseGeoIpResponse(const QByteArray &response,
-                                  GeoIpRequest *request)
+GeoIpRecord
+GeoIpResolver::resolveUsingLocalDatabase(const QHostAddress &ip)
 {
-  QList<GeoIp> geoIpList;
-  QHash<QString,QString> keyvals;
-  QHostAddress ip, from, to;
-  QString city, region, country, cc;
-  float latitude, longitude;
-  GeoIp geoIp;
-  int numCached = 0;
-  bool ok;
-
-  QStringList lines = QString(response).split("\n", QString::SkipEmptyParts);
-  foreach (QString line, lines) {
-    /* Split the key=value formatted GeoIP record into keys and values */
-    QHash<QString,QString> keyvals = string_parse_keyvals(line.trimmed(), &ok);
-    if (! ok)
-      goto err;
-
-    /* Extract each of the required fields from the GeoIP record */
-    ip = QHostAddress(keyvals.value("IP"));
-    if (ip.isNull())
-      goto err;
-    latitude = keyvals.value("LAT").toFloat(&ok);
-    if (! ok)
-      goto err;
-    longitude = keyvals.value("LON").toFloat(&ok);
-    if (! ok)
-      goto err;
-
-    /* Each of these fields is optional */
-    city    = keyvals.value("CITY");
-    region  = keyvals.value("REGION");
-    country = keyvals.value("COUNTRY");
-    cc      = keyvals.value("CC");
-    
-    geoIp = GeoIp(ip, latitude, longitude, city, region, country, cc);
-    if (! geoIp.isValid())
-      goto err;
-
-    if (request->contains(ip)) {
-      if (! _cache->contains(ip)) {
-        from = QHostAddress(keyvals.value("FROM"));
-        to   = QHostAddress(keyvals.value("TO"));
-        if (! from.isNull() && ! to.isNull())
-          _cache->addToCache(from, to, geoIp);
-        else
-          _cache->addToCache(geoIp);
-        numCached++;
-      }
-
-      geoIpList << geoIp;
-      continue;
-
-err:
-      vInfo("Ignored improperly formatted GeoIP record (request id %1): %2")
-                                               .arg(line).arg(request->id());
-    } else {
-      /* This item wasn't requested, so just log it and ignore. */
-      vWarn("Received a GeoIP entry for IP address %1 that was not included "
-            "in the initial request. (request id %2)").arg(ip)
-                                                      .arg(request->id());
+#if defined(USE_GEOIP)
+  if (_database.type() == GeoIpDatabase::CityDatabase) {
+    return _database.recordByAddr(ip);
+  } else {
+    QString countryCode = _database.countryCodeByAddr(ip);
+    if (! countryCode.isEmpty()) {
+      QPair<float,float> coords = CountryInfo::countryLocation(countryCode);
+      return GeoIpRecord(ip, coords.first, coords.second, 
+                         CountryInfo::countryName(countryCode),
+                         countryCode);
     }
   }
-  /* If new results were cached, save them to disk */
-  if (numCached > 0)
-    _cache->saveToDisk();
-  
-  /* Emit the results */
-  vInfo("Parsed %1 entries from the GeoIP response. (request id %2)")
-                                   .arg(geoIpList.size()).arg(request->id());
-  emit resolved(request->id(), geoIpList);  
+#endif
+  return GeoIpRecord();
 }
 
-/** Called when an error has occurred requesting Geo IP information. */
-void
-GeoIpResolver::socketError(const QString &errorString)
+/** Resolves a single IP to a geographic location. */
+GeoIpRecord
+GeoIpResolver::resolve(const QHostAddress &ip)
 {
-  /* Find the socket and request for whoever called this slot */ 
-  QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
-  if (!_requestList.contains(socket)) {
-    return;
-  }
-  
-  /* We expect a remote host to close the socket, because that's how the HTTP
-   * server tells us he's done talking to us. */
-  if (socket->error() != QAbstractSocket::RemoteHostClosedError) {
-    /* Emit the failure and clean up */
-    GeoIpRequest *req = static_cast<GeoIpRequest *>(_requestList.take(socket));
-    emit resolveFailed(req->id(), errorString);
-    socket->abort();
-    vWarn("GeoIP request socket error (request id %1): %2").arg(req->id())
-                                                           .arg(errorString);
-    delete socket;
-    delete req;
-  }
+#if defined(USE_GEOIP)
+  if (_useLocalDatabase)
+    return resolveUsingLocalDatabase(ip);
+#endif
+  return resolveUsingTor(ip); 
 }
 
-/** Creates an HTTP request for Geo IP information. */
-GeoIpRequest*
-GeoIpResolver::createRequest(const QList<QHostAddress> &ips)
-{
-  static int id = -1;
-  GeoIpRequest *request = new GeoIpRequest(++id);
-  request->setHost(GEOIP_HOST);
-  request->setPage(GEOIP_PAGE);
-  request->setRequest(ips);
-  return request;
-}
-
-/** Resolves a list of IPs to a geographic location. */
-int
-GeoIpResolver::resolve(const QList<QHostAddress> &ips)
-{
-  /* Resolve the cached IPs and get a list of IPs that still need to be
-   * resolved to a lat and long. */
-  QList<QHostAddress> uncached = resolveFromCache(ips);
-  if (! uncached.size())
-    return -1;
-
-  /* Create a socket used to request the geo ip information. */
-  TorSslSocket *socket = new TorSslSocket(_socksAddr, _socksPort);
-  connect(socket, SIGNAL(connectedToRemoteHost()), this, SLOT(connected()),
-          Qt::QueuedConnection);
-  connect(socket, SIGNAL(socketError(QString)), 
-          this,   SLOT(socketError(QString)),
-          Qt::QueuedConnection);
-  connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()),
-          Qt::QueuedConnection);
-  GeoIpRequest *request = createRequest(uncached);
-  _requestList.insert(socket, request);
-  
-  /* Connect so we can send our request and return the request ID. */
-  vInfo("Opening an SSL connection to the GeoIP host at %1:%2 (request id %3)")
-                        .arg(GEOIP_HOST).arg(GEOIP_SSL_PORT).arg(request->id());
-  socket->connectToRemoteHost(GEOIP_HOST, GEOIP_SSL_PORT, true);
-
-  return request->id();
-}
-

Modified: vidalia/trunk/src/vidalia/network/GeoIpResolver.h
===================================================================
--- vidalia/trunk/src/vidalia/network/GeoIpResolver.h	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/GeoIpResolver.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -11,24 +11,26 @@
 /*
 G** \file GeoIpResolver.h
 ** \version $Id$
-** \brief Requests GeoIP information and caches the result
+** \brief Retrieves GeoIP information either from Tor or from a local
+** database and returns the result.
 */
 
 #ifndef _GEOIPRESOLVER_H
 #define _GEOIPRESOLVER_H
 
-#include "GeoIpCache.h"
+#include "Vidalia.h"
+#ifdef USE_GEOIP
+#include "GeoIpDatabase.h"
+#endif
+#include "CountryInfo.h"
 
 #include <QObject>
 #include <QList>
 #include <QHash>
 #include <QHostAddress>
 
-class GeoIp;
-class GeoIpRequest;
-class GeoIpResponse;
 class QString;
-class QAbstractSocket;
+class GeoIpRecord;
 
 
 class GeoIpResolver : public QObject
@@ -36,50 +38,51 @@
   Q_OBJECT
 
 public:
-  /** Default constructor. */
+  /** Default constructor.
+   */
   GeoIpResolver(QObject *parent = 0);
-  
-  /** Sets the address and port of Tor, through which GeoIP requests will be
-   * made. */
-  void setSocksHost(const QHostAddress &addr, quint16 port);
-  /** Resolves a single IP to a geographic location. */
-  int resolve(const QHostAddress &ip);
-  /** Resolves a list of IPs to a geographic location. */
-  int resolve(const QList<QHostAddress> &ips);
-  /** Resolves <b>ip</b> to geographic information only if it is cached. */
-  bool resolveFromCache(const QHostAddress &ip);
-  /** Resolves a list of IPs to a geographic location, but only those which
-   * are cached. Returns a list of which IPs were not cached. */
-  QList<QHostAddress> resolveFromCache(const QList<QHostAddress> &ips);
 
-signals:
-  /** Emitted when a list of IPs have been resolved to lat/long. */
-  void resolved(int id, const QList<GeoIp> &geoips);
-  /** Emitted when a resolve has failed. */
-  void resolveFailed(int id, const QString &errorString);
+  /** Sets the local database file to <b>databaseFile</b>. Returns true if
+   * <b>databaseFile</b> could be opened for reading. Otherwise, returns
+   * false.
+   * \sa setUseLocalDatabase()
+   */
+  bool setLocalDatabase(const QString &databaseFile);
 
-private slots:
-  /** Called when the socket has connected to the Geo IP host. */
-  void connected();
-  /** Called when the socket has disconnected from the Geo IP host. */
-  void disconnected();
-  /** Called when an error has occurred getting the Geo IP information. */
-  void socketError(const QString &errorString);
+  /** Enables or disables the use of a local GeoIP database, depending on
+   * the value of <b>useLocalDatabase</b>.
+   * \sa setLocalDatabase()
+   */
+  void setUseLocalDatabase(bool useLocalDatabase);
 
-private:
-  /** Creates an HTTP request for Geo IP information. */
-  GeoIpRequest* createRequest(const QList<QHostAddress> &ips);
+  /** Resolves a single IP to a geographic location and returns the
+   * result on success. On failure, this returns a default-constructed
+   * GeoIpRecord object.
+   */
+  GeoIpRecord resolve(const QHostAddress &ip);
 
-  void parseGeoIpResponse(const QByteArray &response, GeoIpRequest *request);
+protected:
+  /** Maps <b>ip</b> to a country code using Tor, and then maps the
+   * country code to a geographic location using the built-in
+   * country-to-coordinate database.
+   */
+  GeoIpRecord resolveUsingTor(const QHostAddress &ip);
 
-  /**< Cached GeoIp objects. */
-  GeoIpCache*  _cache;
-  /**< List of sockets used for requests. */
-  QHash<QAbstractSocket *,GeoIpRequest*> _requestList;
-  /** Tor's SocksListenAddress. */
-  QHostAddress _socksAddr;
-  /** Tor's SocksPort. */
-  quint16 _socksPort;
+  /** Maps <b>ip</b> to an approximate geographic location using a local
+   * GeoIP database and returns the result on success.
+   * \sa setLocalDatabase()
+   * \sa setUseLocalDatabase()
+   */
+  GeoIpRecord resolveUsingLocalDatabase(const QHostAddress &ip);
+
+private:
+#ifdef USE_GEOIP
+  /** Wrapper around a local database used for looking up GeoIP
+   * information.
+   */
+  GeoIpDatabase _database;
+#endif
+  bool _useLocalDatabase;
 };
 
 #endif

Modified: vidalia/trunk/src/vidalia/network/NetViewer.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/NetViewer.cpp	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/NetViewer.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -22,11 +22,13 @@
 
 #include <QMessageBox>
 #include <QHeaderView>
+#include <QCoreApplication>
 
 #define IMG_MOVE    ":/images/22x22/move-map.png"
 #define IMG_ZOOMIN  ":/images/22x22/zoom-in.png"
 #define IMG_ZOOMOUT ":/images/22x22/zoom-out.png"
 
+#if 0
 /** Number of milliseconds to wait after the arrival of the last descriptor whose
  * IP needs to be resolved to geographic information, in case more descriptors
  * arrive. Then we can simply lump the IPs into a single request. */
@@ -34,8 +36,8 @@
 /** Maximum number of milliseconds to wait after the arrival of the first
  * IP address into the resolve queue, before we flush the entire queue. */
 #define MAX_RESOLVE_QUEUE_DELAY   (30*1000)
+#endif
 
-
 /** Constructor. Loads settings from VidaliaSettings.
  * \param parent The parent widget of this NetViewer object.\
  */
@@ -109,15 +111,7 @@
    * needs to be called to get rid of any descriptors that were removed. */
   _refreshTimer.setInterval(60*60*1000);
   connect(&_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
-  
-  /* Set up the timers used to group together GeoIP requests for new
-   * descriptors arriving within MIN_RESOLVE_QUEUE_DELAY milliseconds, but no
-   * more than MAX_RESOLVE_QUEUE_DELAY milliseconds of each other. */
-  _minResolveQueueTimer.setSingleShot(true);
-  connect(&_minResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
-  _maxResolveQueueTimer.setSingleShot(true);
-  connect(&_maxResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
-
+ 
   /* Connect the necessary slots and signals */
   connect(ui.actionHelp, SIGNAL(triggered()), this, SLOT(help()));
   connect(ui.actionRefresh, SIGNAL(triggered()), this, SLOT(refresh()));
@@ -136,9 +130,7 @@
   connect(ui.treeCircuitList, SIGNAL(closeStream(StreamId)),
           _torControl, SLOT(closeStream(StreamId)));
 
-  /* Connect the slot to find out when geoip information has arrived */
-  connect(&_geoip, SIGNAL(resolved(int, QList<GeoIp>)), 
-             this,   SLOT(resolved(int, QList<GeoIp>)));
+  setupGeoIpResolver();
 }
 
 /** Called when the user changes the UI translation. */
@@ -168,24 +160,31 @@
   }
 }
 
-/** Display the network map window. If there are geoip requests waiting in the
- * queue, start the queue timers now. */
 void
-NetViewer::showWindow()
+NetViewer::setupGeoIpResolver()
 {
-  if (!_resolveQueue.isEmpty()) {
-    _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
-    _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
+  VidaliaSettings settings;
+
+#if defined(USE_GEOIP)
+  if (settings.useLocalGeoIpDatabase()) {
+    QString databaseFile = settings.localGeoIpDatabase();
+    if (! databaseFile.isEmpty()) {
+      _geoip.setLocalDatabase(databaseFile);
+      _geoip.setUseLocalDatabase(true);
+      vInfo("Using local database file for relay mapping: %1")
+                                            .arg(databaseFile);
+      return;
+    }
   }
-  VidaliaWindow::showWindow();
+#endif
+  vInfo("Using Tor's GeoIP database for country-level relay mapping.");
+  _geoip.setUseLocalDatabase(false);
 }
 
 /** Loads data into map, lists and starts timer when we get connected*/
 void
 NetViewer::onAuthenticated()
 {
-  _geoip.setSocksHost(_torControl->getSocksAddress(),
-                      _torControl->getSocksPort());
   refresh();
   _refreshTimer.start();
   ui.actionRefresh->setEnabled(true);
@@ -225,9 +224,6 @@
 void
 NetViewer::clear()
 {
-  /* Clear the resolve queue and map */
-  _resolveMap.clear();
-  _resolveQueue.clear();
   /* Clear the network map */
   _map->clear();
   _map->update();
@@ -324,6 +320,8 @@
     RouterDescriptor rd = _torControl->getRouterDescriptor(rs.id());
     if (!rd.isEmpty())
       addRouter(rd);
+
+    QCoreApplication::processEvents();
   }
 }
 
@@ -333,18 +331,19 @@
 NetViewer::addRouter(const RouterDescriptor &rd)
 {
   /* Add the descriptor to the list of server */
-  ui.treeRouterList->addRouter(rd);
+  RouterListItem *item = ui.treeRouterList->addRouter(rd);
+  if (! item)
+    return;
 
-  /* Add this IP to a list of addresses whose geographic location we'd like to
-   * find, but not for special purpose descriptors (e.g., bridges). This
-   * check is only valid for Tor >= 0.2.0.13-alpha. */
-  if (_torControl->getTorVersion() >= 0x020013) {
-    DescriptorAnnotations annotations =
-      _torControl->getDescriptorAnnotations(rd.id());
-    if (!annotations.contains("purpose"))
-      addToResolveQueue(rd.ip(), rd.id());
-  } else {
-    addToResolveQueue(rd.ip(), rd.id());
+  /* Attempt to map this relay to an approximate geographic location. The
+   * accuracy of the result depends on the database information currently
+   * available to the GeoIP resolver. */
+  if (! item->location().isValid() || rd.ip() != item->location().ip()) {
+    GeoIpRecord location = _geoip.resolve(rd.ip());
+    if (location.isValid()) {
+      item->setLocation(location);
+      _map->addRouter(rd, location);
+    }
   }
 }
 
@@ -361,33 +360,6 @@
   }
 }
 
-/** Adds an IP address to the resolve queue and updates the queue timers. */
-void
-NetViewer::addToResolveQueue(const QHostAddress &ip, const QString &id)
-{
-  QString ipstr = ip.toString();
-  if (!_resolveMap.values(ipstr).contains(id)) {
-    /* Remember which server ids belong to which IP addresses */
-    _resolveMap.insertMulti(ipstr, id);
-  }
- 
-  if (!_resolveQueue.contains(ip) && !_geoip.resolveFromCache(ip)) {
-    /* Add the IP to the queue of IPs waiting for geographic information  */
-    _resolveQueue << ip;
- 
-    /* Wait MIN_RESOLVE_QUEUE_DELAY after the last item inserted into the
-     * queue, before sending the resolve request. */
-    _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
-    
-    /* Do not wait longer than MAX_RESOLVE_QUEUE_DELAY from the time the first
-     * item is inserted into the queue, before flushing and resolving the
-     * queue. */
-    if (_resolveQueue.size() == 1) {
-      _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
-    }
-  }
-}
-
 /** Called when the user selects a circuit from the circuit and streams
  * list. */
 void
@@ -429,65 +401,6 @@
     _map->selectRouter(routers[0].id());
 }
 
-/** If there are any IPs in the resolve queue, do the request now. */
-void
-NetViewer::resolve()
-{
-  if (!_resolveQueue.isEmpty()) {
-    /* Send the request now if either the network map is visible, or the
-     * request is for more than a quarter of the servers in the list. */
-    if (isVisible() || 
-        (_resolveQueue.size() >= ui.treeRouterList->topLevelItemCount()/4)) {
-      vInfo("Sending GeoIP request for %1 IP addresses.")
-                               .arg(_resolveQueue.size());
-      /* Flush the resolve queue and stop the timers */
-      _geoip.resolve(_resolveQueue);
-      _resolveQueue.clear();
-    }
-  }
-  /* Stop the queue timers. Only one should be active since the other is what
-   * called this slot, but calling stop() on a stopped timer does not hurt. */
-  _minResolveQueueTimer.stop();
-  _maxResolveQueueTimer.stop();
-}
-
-/** Called when a list of GeoIp information has been resolved. */
-void
-NetViewer::resolved(int id, const QList<GeoIp> &geoips)
-{
-  Q_UNUSED(id);
-
-  QString ip;
-  RouterListItem *router;
- 
-  foreach (GeoIp geoip, geoips) {
-    /* Find all routers that are at this IP address */
-    ip = geoip.ip().toString();
-    QList<QString> ids = _resolveMap.values(ip);
-    _resolveMap.remove(ip);
-      
-    /* Update their geographic location information with the results of this
-     * GeoIP query. */
-    foreach (QString id, ids) {
-      router = ui.treeRouterList->findRouterById(id);
-      if (router) {
-        /* Save the location information in the descriptor */
-        router->setLocation(geoip);
-        /* Plot the router on the map */
-        _map->addRouter(router->descriptor(), geoip);
-      }
-    }
-  }
-
-  /* Update the circuit lines */
-  foreach (Circuit circuit, ui.treeCircuitList->circuits()) {
-    _map->addCircuit(circuit.id(), circuit.routerIDs());
-  }
-
-  /* Repaint the map */
-  _map->update();
-}
-
 /** Called when the user selects a router on the network map. Displays a 
  * dialog with detailed information for the router specified by
  * <b>id</b>.*/
@@ -514,7 +427,7 @@
   /* Populate the UI with information learned from a previous GeoIP request */
   RouterListItem *item = ui.treeRouterList->findRouterById(id);
   if (item)
-    dlg.setLocation(item->location());
+    dlg.setLocation(item->location().toString());
   else
     dlg.setLocation(tr("Unknown"));
 

Modified: vidalia/trunk/src/vidalia/network/NetViewer.h
===================================================================
--- vidalia/trunk/src/vidalia/network/NetViewer.h	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/NetViewer.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -49,7 +49,7 @@
 
 public slots:
   /** Displays the network map window. */
-  void showWindow();
+//  void showWindow();
   /** Loads a list of current circuits and streams. */
   void loadConnections();
   /** Adds <b>circuit</b> to the list and the map */
@@ -85,16 +85,12 @@
   void refresh();
   /** Called when the user selects a circuit on the circuit list */
   void circuitSelected(const Circuit &circuit);
-  /** Called when an IP has been resolved to geographic information. */
-  void resolved(int id, const QList<GeoIp> &geoips);
   /** Called when the user selects one or more routers in the list. */
   void routerSelected(const QList<RouterDescriptor> &routers);
   /** Handles when we get connected to Tor network */
   void onAuthenticated();
   /** Handles when we get disconnected from Tor network */
   void onDisconnected();
-  /** Resolves IP addresses in the resolve queue to geographic information. */
-  void resolve();
   /** Called when the user selects a router on the network map. Displays a 
    * dialog with detailed information for the router specified by
    * <b>id</b>.*/
@@ -108,8 +104,8 @@
   void toggleFullScreen();
 
 private:
-  /** Adds an IP address to the resolve queue and updates the queue timers. */
-  void addToResolveQueue(const QHostAddress &ip, const QString &id);
+  /** */
+  void setupGeoIpResolver();
   /** Retrieves a list of all running routers from Tor and their descriptors,
    * and adds them to the RouterListWidget. */
   void loadNetworkStatus();
@@ -125,19 +121,8 @@
   QTimer _refreshTimer;
   /** GeoIpResolver used to geolocate routers by IP address. */
   GeoIpResolver _geoip;
-  /** Queue for IPs pending resolution to geographic information. */
-  QList<QHostAddress> _resolveQueue;
-  /** Maps pending GeoIP requests to server IDs. */
-  QHash<QString, QString> _resolveMap;
   /** Stores a list of address mappings from Tor. */
   AddressMap _addressMap;
-  /** Timer used to delay GeoIP requests for MIN_RESOLVE_QUEUE_DELAY
-   * milliseconds after we've inserted the last item into the queue. */
-  QTimer _minResolveQueueTimer;
-  /** Timer used to limit the delay of GeoIP requests to
-   * MAX_RESOLVE_QUEUE_DELAY milliseconds after inserting the first item 
-   * into the queue. */
-  QTimer _maxResolveQueueTimer;
  
   /** Widget that displays the Tor network map. */
 #if defined(USE_MARBLE)

Modified: vidalia/trunk/src/vidalia/network/RouterListItem.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/RouterListItem.cpp	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/RouterListItem.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -38,7 +38,7 @@
 {
   _list = list;
   _rd   = 0;
-  _country = "~"; /* Force items with no country to the bottom */
+  _countryCode = "~"; /* Force items with no country to the bottom */
   setIcon(COUNTRY_COLUMN, QIcon(IMG_FLAG_UNKNOWN));
   update(rd);
 }
@@ -91,17 +91,17 @@
 
 /** Sets the location information for this item's router descriptor. */
 void
-RouterListItem::setLocation(const GeoIp &geoip)
+RouterListItem::setLocation(const GeoIpRecord &geoip)
 {
   QPixmap flag(":/images/flags/" + geoip.countryCode().toLower() + ".png");
   if (!flag.isNull()) {
     setIcon(COUNTRY_COLUMN, QIcon(flag));
   }
   setToolTip(COUNTRY_COLUMN, geoip.toString());
-  
+
   if (_rd)
     _rd->setLocation(geoip.toString());
-  _country = geoip.countryCode();
+  _countryCode = geoip.countryCode();
 }
 
 /** Overload the comparison operator. */
@@ -125,13 +125,13 @@
         return (a->_statusValue < b->_statusValue);
       case RouterListWidget::CountryColumn:
         /* Compare based on country code */
-        if (a->_country == b->_country) {
+        if (a->_countryCode == b->_countryCode) {
           if (order == Qt::AscendingOrder)
             return (a->_statusValue > b->_statusValue);
           else
             return (a->_statusValue < b->_statusValue);
         }
-        return (a->_country < b->_country);
+        return (a->_countryCode < b->_countryCode);
       case RouterListWidget::NameColumn:
         /* Case-insensitive comparison based on router name */
         if (a->name().toLower() == b->name().toLower()) {

Modified: vidalia/trunk/src/vidalia/network/RouterListItem.h
===================================================================
--- vidalia/trunk/src/vidalia/network/RouterListItem.h	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/RouterListItem.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -19,7 +19,7 @@
 
 #include "RouterDescriptor.h"
 #include "RouterListWidget.h"
-#include "GeoIp.h"
+#include "GeoIpRecord.h"
 
 #include <QCoreApplication>
 #include <QTreeWidgetItem>
@@ -47,9 +47,9 @@
   /** Returns the descriptor for this router. */
   RouterDescriptor descriptor() const { return *_rd; }
   /** Sets the location information for this router item. */
-  void setLocation(const GeoIp &geoip);
+  void setLocation(const GeoIpRecord &geoip);
   /** Returns the location information set for this router item. */
-  QString location() const { return _rd->location(); }
+  GeoIpRecord location() const { return _location; }
 
   /** Overload the comparison operator. */
   virtual bool operator<(const QTreeWidgetItem &other) const;
@@ -58,8 +58,8 @@
   RouterDescriptor* _rd;   /**< Descriptor for this router item. */
   RouterListWidget* _list; /**< The list for this list item. */
   qint64 _statusValue;     /**< Value used to sort items by status. */
-  QString _country;        /**< Country in which this router is likely
-                                located. */
+  GeoIpRecord _location;   /**< Location information for this router. */
+  QString _countryCode;
 };
 
 #endif

Modified: vidalia/trunk/src/vidalia/network/RouterListWidget.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/RouterListWidget.cpp	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/RouterListWidget.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -201,12 +201,12 @@
 }
 
 /** Adds a router descriptor to the list. */
-void
-RouterListWidget::addRouter(RouterDescriptor rd)
+RouterListItem*
+RouterListWidget::addRouter(const RouterDescriptor &rd)
 {
   QString id = rd.id();
   if (id.isEmpty())
-    return;
+    return 0;
 
   RouterListItem *item = findRouterById(id);
   if (item) {
@@ -219,6 +219,8 @@
 
   /* Set our status tip to the number of servers in the list */
   setStatusTip(tr("%1 relays online").arg(topLevelItemCount()));
+
+  return item;
 }
 
 /** Called when the selected items have changed. This emits the 

Modified: vidalia/trunk/src/vidalia/network/RouterListWidget.h
===================================================================
--- vidalia/trunk/src/vidalia/network/RouterListWidget.h	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/RouterListWidget.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -47,7 +47,7 @@
   RouterListWidget(QWidget *parent = 0);
 
   /** Adds a new descriptor the list. */
-  void addRouter(RouterDescriptor rd);
+  RouterListItem* addRouter(const RouterDescriptor &rd);
   /** Finds the list item whose key ID matches <b>id</b>. Returns 0 if not 
    * found. */
   RouterListItem* findRouterById(QString id);

Modified: vidalia/trunk/src/vidalia/network/TorMapImageView.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/TorMapImageView.cpp	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/TorMapImageView.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -85,7 +85,7 @@
 
 /** Adds a router to the map. */
 void
-TorMapImageView::addRouter(const RouterDescriptor &desc, const GeoIp &geoip)
+TorMapImageView::addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip)
 {
   QString id = desc.id();
   QPointF routerCoord = toMapSpace(geoip.latitude(), geoip.longitude());

Modified: vidalia/trunk/src/vidalia/network/TorMapImageView.h
===================================================================
--- vidalia/trunk/src/vidalia/network/TorMapImageView.h	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/TorMapImageView.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -18,7 +18,7 @@
 #define _TORMAPIMAGEVIEW_H
 
 #include "ZImageView.h"
-#include "GeoIp.h"
+#include "GeoIpRecord.h"
 
 #include "RouterDescriptor.h"
 #include "Circuit.h"
@@ -40,7 +40,7 @@
   ~TorMapImageView();
 
   /** Plots the given router on the map using the given coordinates. */
-  void addRouter(const RouterDescriptor &desc, const GeoIp &geoip);
+  void addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip);
   /** Plots the given circuit on the map. */
   void addCircuit(const CircuitId &circid, const QStringList &path);
   /** Selects and hightlights a router on the map. */

Modified: vidalia/trunk/src/vidalia/network/TorMapWidget.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/TorMapWidget.cpp	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/TorMapWidget.cpp	2010-08-05 20:28:54 UTC (rev 4378)
@@ -69,7 +69,7 @@
 
 /** Adds a router to the map. */
 void
-TorMapWidget::addRouter(const RouterDescriptor &desc, const GeoIp &geoip)
+TorMapWidget::addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip)
 {
   QString kml;
   qreal lon = geoip.longitude();

Modified: vidalia/trunk/src/vidalia/network/TorMapWidget.h
===================================================================
--- vidalia/trunk/src/vidalia/network/TorMapWidget.h	2010-08-05 20:26:44 UTC (rev 4377)
+++ vidalia/trunk/src/vidalia/network/TorMapWidget.h	2010-08-05 20:28:54 UTC (rev 4378)
@@ -18,7 +18,7 @@
 #define _TORMAPWIDGET_H
 
 #include "RouterDescriptor.h"
-#include "GeoIp.h"
+#include "GeoIpRecord.h"
 
 #include "Circuit.h"
 #include "Stream.h"
@@ -46,7 +46,7 @@
   ~TorMapWidget();
 
   /** Plots the given router on the map using the given coordinates. */
-  void addRouter(const RouterDescriptor &desc, const GeoIp &geoip);
+  void addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip);
   /** Plots the given circuit on the map. */
   void addCircuit(const CircuitId &circid, const QStringList &path);
   /** Selects and hightlights a router on the map. */