[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[vidalia-svn] r2940: Part 0 of multiple geoip options: move to a separate dir all (in vidalia/branches/exit-country/src/vidalia: . config geoip)
Author: cviecco
Date: 2008-08-05 09:18:56 -0400 (Tue, 05 Aug 2008)
New Revision: 2940
Added:
vidalia/branches/exit-country/src/vidalia/geoip/
vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.cpp
vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.h
vidalia/branches/exit-country/src/vidalia/geoip/geoip.cpp
vidalia/branches/exit-country/src/vidalia/geoip/geoip.h
vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.cpp
vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.h
vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.cpp
vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.h
vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.cpp
vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.h
vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.cpp
vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.h
vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.cpp
vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.h
Removed:
vidalia/branches/exit-country/src/vidalia/config/filegeoipresolver.cpp
vidalia/branches/exit-country/src/vidalia/config/filegeoipresolver.h
vidalia/branches/exit-country/src/vidalia/config/geoip.cpp
vidalia/branches/exit-country/src/vidalia/config/geoip.h
Modified:
vidalia/branches/exit-country/src/vidalia/CMakeLists.txt
vidalia/branches/exit-country/src/vidalia/config/networkoutpage.cpp
vidalia/branches/exit-country/src/vidalia/config/networkoutpage.h
Log:
Part 0 of multiple geoip options: move to a separate dir all possible modules/options. Next step: unified API
Modified: vidalia/branches/exit-country/src/vidalia/CMakeLists.txt
===================================================================
--- vidalia/branches/exit-country/src/vidalia/CMakeLists.txt 2008-08-05 12:00:06 UTC (rev 2939)
+++ vidalia/branches/exit-country/src/vidalia/CMakeLists.txt 2008-08-05 13:18:56 UTC (rev 2940)
@@ -15,6 +15,7 @@
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/config
+ ${CMAKE_CURRENT_SOURCE_DIR}/geoip
${CMAKE_CURRENT_SOURCE_DIR}/help/browser
)
configure_file(
@@ -69,8 +70,8 @@
config/torsettings.cpp
config/vidaliasettings.cpp
config/vsettings.cpp
- config/filegeoipresolver.cpp
- config/geoip.cpp
+ #config/filegeoipresolver.cpp
+ #config/geoip.cpp
)
qt4_wrap_cpp(vidalia_SRCS
config/abstracttorsettings.h
@@ -92,7 +93,7 @@
config/torsettings.h
config/vidaliasettings.h
config/vsettings.h
- config/filegeoipresolver.h
+ #config/filegeoipresolver.h
)
if (USE_MINIUPNPC)
include_directories(${MINIUPNPC_INCLUDE_DIR})
@@ -114,6 +115,28 @@
help/browser/helptextbrowser.h
)
+## Geoip sources
+set(vidalia_SRCS ${vidalia_SRCS}
+ geoip/geoip.cpp
+ geoip/filegeoipresolver.cpp
+ #next are the web enabled ones!
+ geoip/geoipcacheitem.cpp
+ geoip/geoipcache.cpp
+ geoip/geoiprequest.cpp
+ geoip/geoipresponse.cpp
+ geoip/webgeoipresolver.cpp
+)
+qt4_wrap_cpp(vidalia_SRCS
+# geoip/geoip.h
+ geoip/filegeoipresolver.h
+ geoip/geoipcacheitem.h
+ geoip/geoipcache.h
+ geoip/geoiprequest.h
+ geoip/geoipresponse.h
+ geoip/webgeoipresolver.h
+)
+
+
## Message log sources
set(vidalia_SRCS ${vidalia_SRCS}
log/logfile.cpp
Deleted: vidalia/branches/exit-country/src/vidalia/config/filegeoipresolver.cpp
Deleted: vidalia/branches/exit-country/src/vidalia/config/filegeoipresolver.h
Deleted: vidalia/branches/exit-country/src/vidalia/config/geoip.cpp
Deleted: vidalia/branches/exit-country/src/vidalia/config/geoip.h
Modified: vidalia/branches/exit-country/src/vidalia/config/networkoutpage.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/config/networkoutpage.cpp 2008-08-05 12:00:06 UTC (rev 2939)
+++ vidalia/branches/exit-country/src/vidalia/config/networkoutpage.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -44,6 +44,13 @@
}
*/
+ /* Create the timer that will be used to update the internal ans displays once
+ every hour */
+ //_refreshTimer.setInterval(60*60*1000);
+ _refreshTimer.setInterval(60*2*1000);
+ connect(&_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
+
+
/*do connections!*/
connect(_torControl, SIGNAL(authenticated()), this, SLOT(onAuthenticated()));
connect(_torControl, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
@@ -70,6 +77,9 @@
//file based geoip resolver
_geoIpResolver=Vidalia::geoIpResolver();
+
+
+
}
/** Destructor */
@@ -150,9 +160,21 @@
load_longnames();
}
+//void
+//NetworkoutPage::refresh(){
+//}
+
+
void
NetworkoutPage::onAuthenticated()
{
+ refresh();
+ _refreshTimer.start();
+}
+
+void
+NetworkoutPage::refresh()
+{
//_geoip.setSocksHost(_torControl->getSocksAddress(),
// _torControl->getSocksPort());
//refresh();
@@ -171,13 +193,12 @@
//initialize the window.. just in case it has not been initialized yet.
load();
-
+ fprintf(stderr,"refresh!\n");
/*Fill up the internal structure*/
//by_country_exit_nodes.clear();
by_country_exit_nodes2.clear();
foreach( RouterStatus router, networkStatus){
- //fprintf(stderr,"*");
//Get the country!
ip_addr=router.ipAddress().toString();
@@ -219,7 +240,6 @@
by_country_exit_nodes2.insert(short_country_name,QList<RouterStatus>()<<router);
}
}
- return;
//* now fill the combo boxes!*/
@@ -294,7 +314,6 @@
copyExitCountryToText();
applyTorSettings();
}
- fprintf(stderr,"networout on authenticated, done!\n");
}
@@ -369,6 +388,7 @@
//erase data structure
by_country_exit_nodes2.clear();
//remove data from the country combo box?
+ _refreshTimer.stop();
}
@@ -416,7 +436,7 @@
if((ui.chkNodePolicy->isChecked())){
settings.setUseExitNodePolicy(ui.chkExitNodePolicy->isChecked());
settings.setUseExcludeNodePolicy(ui.chkExcludeNodePolicy->isChecked());
- fprintf(stderr,"node pol applyset, checked!\n");
+ vInfo("node pol applyset, checked!\n");
/*Only save if we have enabled saving exit policies!*/
if(ui.chkExitNodePolicy->isChecked()){
@@ -425,7 +445,7 @@
/*iterate over listwidget?*/
QStringList exitList;
QString current=ui.cmboExitNodesbyCountry->currentText();
- fprintf(stderr,"qlen=%d",current.length());
+ //vInfo("qlen=%d",current.length());
QStringList countrylist=current.split("(");
current=countrylist.at(0);
current=getExitCountry();
Modified: vidalia/branches/exit-country/src/vidalia/config/networkoutpage.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/config/networkoutpage.h 2008-08-05 12:00:06 UTC (rev 2939)
+++ vidalia/branches/exit-country/src/vidalia/config/networkoutpage.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -60,6 +60,9 @@
void onAuthenticated();
/** Handles when we get disconnected from Tor network */
void onDisconnected();
+
+ /*refresh the window values*/
+ void refresh();
private:
@@ -91,6 +94,9 @@
TorControl* _torControl;
FileGeoIpResolver* _geoIpResolver;
+
+ /** Timer that fires once an hour to update the QMaps list. */
+ QTimer _refreshTimer;
};
Added: vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.cpp (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,241 @@
+/*
+** 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 geoipresolver.cpp
+** \version $Id: geoipresolver.cpp 2429 2008-03-21 02:27:36Z edmanm $
+** \brief Requests GeoIP information and caches the result
+*/
+
+//#include <torsocket.h>
+#include <vidalia.h>
+#include "filegeoipresolver.h"
+#include "config.h"
+
+#include <time.h>
+
+#define GEOIP_FILENAME (Vidalia::dataDirectory() + "/geoip.data")
+
+inline
+uint32_t
+FileGeoIpResolver::string2intloc(const char *in){
+ //long internal;
+ uint32_t rval;
+ rval=atoi(in);
+ if(0==rval){
+ rval=0xFF000000 | (in[0]<<8) | (in[1]);
+ }
+ return rval;
+}
+
+
+/** Default constructor. */
+FileGeoIpResolver::FileGeoIpResolver()
+{
+ time_t start,end;
+ ready=false;
+ FileGeoIpResolverGeoLocation newlocation;
+ FileGeoIpResolverIpRange newrange;
+
+ start=time(NULL);
+ //start by attempting to read the user geoip file
+ FILE* file2;
+ file2=fopen(GEOIP_FILENAME.toAscii(),"r");
+ char tmpline[256];
+ char tmpline2[256];
+ char tmpline3[256];
+ int block_count=0;
+ char *tok;
+
+
+ if(NULL!=file2){
+ //fprintf(stderr,"fopen, open!\n");
+ while (fgets(tmpline, 255, file2) != NULL) {
+ memcpy(tmpline2,tmpline,256);
+
+ if(3==sscanf(tmpline,"%u,%u,%s",&newrange.low_net,&newrange.high_net,tmpline3)){
+ if(0==block_count){
+ ipRange.reserve((location.size()*location.size())/10);
+ }
+ block_count++;
+ //token = strtok(line, search);
+ //fprintf(stderr,"%d,%d,%s\n",newrange.low_net,newrange.high_net,tmpline2);
+ //newrange.location=tmpline3;
+ QString newline(tmpline3);
+ //QString line2(tmpline2);
+ //QStringList list2(line2.split(","));
+ //newrange.low_net=list2.at(0).toUInt();
+ //newrange.high_net=list2.at(1).toUInt();
+ //newrange.location=list2.at(2);
+ newrange.location2=string2intloc(tmpline3);
+ //newrange.location=newline;
+
+
+ ipRange.append(newrange);
+ }
+ else{
+ QString line2(tmpline2);
+ QStringList list2(line2.split(","));
+ switch(list2.size()){
+ //beware of fallthrough
+ default:
+ newlocation.extra_info=list2.at(4);
+ case 4: //is location;
+ newlocation.country_short=list2.at(1);
+ newlocation.latitude=list2.at(2).toFloat();
+ newlocation.longitude=list2.at(3).toFloat();
+ //fprintf(stderr,"%s",tmpline2);
+ //location.insert(list2.at(0),newlocation);
+ //memcpy(tmpline3,list2.at(0).toAscii(),2);
+ //location.insert(string2intloc(tmpline3),newlocation);
+ location.insert(string2intloc(list2.at(0).toAscii()),newlocation);
+ break;
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ break;
+
+ }
+ }
+ //QString line2(tmpline);
+ //QStringList list2(line2.split(','));
+
+ } //closes while
+ fclose(file2);
+ }
+ else{
+ //This branche when user geoip file not found or cannot be opened
+ //therefore, use the default, built in file!
+ QFile file(":/geoip/geoip.data");
+ QString line;
+ QStringList list;
+ QTextStream in(&file);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){
+ fprintf (stderr,"cannot open geoip data file!\n");
+ return;
+ }
+ //fprintf(stderr,"using default geoip file!\n");
+ while (!in.atEnd()) {
+ line = in.readLine();
+ list=line.split(",");
+ //list=in.readLine().split(',');
+
+ switch (list.size()){
+ case 0:
+ case 1:
+ case 2:
+ //invalid options
+ break;
+
+ case 3: //is range
+ newrange.low_net=list.at(0).toUInt();
+ newrange.high_net=list.at(1).toUInt();
+ //newrange.location=list.at(2);
+ //memcpy(tmpline3,list.at(0).toAscii(),2);
+ newrange.location2=string2intloc(list.at(2).toAscii());
+ ipRange.append(newrange);
+ break;
+
+ //now some magic, beware of fallthrough
+ default:
+ newlocation.extra_info=list.at(4);
+ case 4: //is location;
+ newlocation.country_short=list.at(1);
+ newlocation.latitude=list.at(2).toFloat();
+ newlocation.longitude=list.at(3).toFloat();
+ //location.insert(list.at(0),newlocation);
+ location.insert(string2intloc(list.at(0).toAscii()),newlocation);
+ break;
+
+ }
+
+ }//closes while
+
+
+ }
+
+
+ end=time(NULL);
+ ready=true;
+}
+
+
+//returns an country given an ip.
+QString
+FileGeoIpResolver::get_country(QHostAddress ip){
+ QString unknown("??");
+ //return unknown;
+ GeoIp resolv=search(ip);
+
+ if (resolv.isEmpty() || resolv.isUnknown())
+ return unknown;
+ else
+ return resolv.country();
+}
+
+
+//Search for a geoip address given a host ip
+GeoIp
+FileGeoIpResolver::search(QHostAddress ip){
+ int i;
+ int found=-1;
+ FileGeoIpResolverGeoLocation iploc;
+ int low=0,mid;
+ int high=ipRange.size();
+ GeoIp invalid;
+
+
+ /*
+ //old code.. simple linear search..
+ for(i=0;i<ipRange.size();i++){
+ if(ipRange[i].low_net<=ip.toIPv4Address() && ipRange[i].high_net>=ip.toIPv4Address() ){
+ found=i;
+ break;
+ }
+ }
+ */
+
+ //new code.. combo binary search + linear (linear on near found)
+ low=0;
+ high=ipRange.size();
+ while(low+2<high){
+ //now compare with middle!
+ mid=(low+high)/2;
+ if(ipRange[mid].low_net>ip.toIPv4Address()){
+ high=mid;
+ }
+ else{
+ low=mid;
+ }
+ }
+ for(i=low;i<high+1 && i<ipRange.size();i++){
+ if(ipRange[i].low_net<=ip.toIPv4Address() &&
+ ipRange[i].high_net>=ip.toIPv4Address() ){
+ found=i;
+ break;
+ }
+ }
+
+ if(-1==found){
+ //fprintf(stderr,"not found!\n");
+ return invalid;
+ }
+
+
+ //now get location
+ //iploc=location[ipRange[found].location];
+ iploc=location[ipRange[found].location2];
+ //now convert
+ GeoIp rvalue(ip, iploc.latitude, iploc.longitude,
+ iploc.extra_info, "", iploc.country_short);
+ return rvalue;
+}
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.h (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/filegeoipresolver.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,120 @@
+/*
+** 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 filegeoipresolver.h
+** \version $Id: geoipresolver.h 2429 2008-03-21 02:27:36Z edmanm $
+** \brief Provides and API to resolve geoip queries. Queries are solved syncronously
+** based on data in a local file. All operations must be thread safe.
+*/
+
+#ifndef _FILEGEOIPRESOLVER_H
+#define _FILEGEOIPRESOLVER_H
+
+#include <QObject>
+#include <QList>
+#include <QHash>
+#include <QString>
+#include <QHostAddress>
+#include <QFile>
+#include <QDir>
+#include <QTextStream>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QVector>
+#include <QVarLengthArray>
+#include <geoip.h>
+
+//#include "geoipcache.h"
+//#include "geoiprequest.h"
+//#include "geoipresponse.h"
+
+
+//Two classes for internal storage!
+class FileGeoIpResolverGeoLocation
+{
+ public:
+ QString country_short;
+ float latitude;
+ float longitude;
+ QString extra_info;
+};
+
+class FileGeoIpResolverIpRange
+{
+ public:
+ uint32_t low_net;
+ uint32_t high_net;
+ //QString location;
+ uint32_t location2;
+};
+
+
+//now the actual class
+
+class FileGeoIpResolver : public QObject
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ FileGeoIpResolver();
+
+ /** Sets the address and port of Tor, through which GeoIP requests will be
+ * made. */
+ //void setSocksHost(QHostAddress addr, quint16 port);
+ /** Resolves a single IP to a geographic location. */
+ //int resolve(QHostAddress ip);
+ /** Resolves a list of IPs to a geographic location. */
+ //int resolve(QList<QHostAddress> ips);
+
+ //QList<GeoIp> resolve_sync(QHostAddress ip);
+ //List<GeoIp> resolve_sync(QList<QHostAddress> ips);
+
+ /** Resolves <b>ip</b> to geographic information only if it is cached. */
+ //bool resolveFromCache(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(QList<QHostAddress> ips);
+
+ QString get_country(QHostAddress ip);
+
+//signals:
+ /** Emitted when a list of IPs have been resolved to lat/long. */
+ //void resolved(int id, QList<GeoIp> geoips);
+ /** Emitted when a resolve has failed. */
+ //void resolveFailed(int id, QString errorString);
+
+//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(QString errorString);
+
+public:
+ GeoIp search(QHostAddress ip);
+
+private:
+ //provides locking during db access/updates to guarantee thread safety
+ // not used now since we do not update the DB on realtime
+ QMutex mutex;
+ bool ready;
+
+ //now the internal data structs
+ //QHash<QString,FileGeoIpResolverGeoLocation> location;
+ QHash<uint32_t,FileGeoIpResolverGeoLocation> location;
+ QVector<FileGeoIpResolverIpRange> ipRange;
+ uint32_t string2intloc(const char *);
+};
+
+#endif
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoip.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoip.cpp (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoip.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,139 @@
+/*
+** 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 geoip.cpp
+** \version $Id: geoip.cpp 2362 2008-02-29 04:30:11Z edmanm $
+** \brief Associates an IP with a geographic location
+*/
+
+#include <QStringList>
+
+#include "geoip.h"
+
+/** 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))
+
+
+/** Constructor */
+GeoIp::GeoIp(QHostAddress ip)
+{
+ _ip = ip;
+ _latitude = _longitude = 0xFFFF;
+}
+
+/** Constructor. */
+GeoIp::GeoIp(QHostAddress ip, float latitude, float longitude,
+ QString city, QString state, QString country)
+{
+ _ip = ip;
+ _latitude = latitude;
+ _longitude = longitude;
+ _city = city;
+ _state = state;
+ _country = country;
+}
+
+/** Parses the GeoIp information from a comma-delimited string. The format of
+ * the string is as in the following example:
+ *
+ * 128.213.48.13,Troy,NY,US,42.7495,-73.5951,1138402852
+ */
+GeoIp
+GeoIp::fromString(QString geoip)
+{
+ /* Split comma-delimited data fields */
+ QStringList data = geoip.split(",");
+
+ if (data.size() == 2 && data.at(1).toLower() == "unknown") {
+ return GeoIp(QHostAddress(data.at(0)));
+ } else if (data.size() < 6) {
+ return GeoIp();
+ }
+
+ /* Parse the data from the string */
+ QHostAddress ip(data.at(0));
+ QString city = data.at(1);
+ QString state = data.at(2);
+ QString country = data.at(3);
+ float latitude = data.at(4).toFloat();
+ float longitude = data.at(5).toFloat();
+
+ /* Create a new GeoIp object with the parsed data. */
+ return GeoIp(ip, latitude, longitude, city, state, country);
+}
+
+/** Formats the GeoIp information as a comma-delimited string. */
+QString
+GeoIp::toString() const
+{
+ QString s;
+ /* Assemble and comma-delimit the data fields */
+ s.append(_ip.toString());
+ s.append("," + _city);
+ s.append("," + _state);
+ s.append("," + _country);
+ s.append("," + QString::number(_latitude, 'f', 4));
+ s.append("," + QString::number(_longitude, 'f', 4));
+ return s;
+}
+
+/** Returns true if the GeoIp object is invalid. */
+bool
+GeoIp::isEmpty() const
+{
+ return (_ip.isNull() &&
+ !IS_VALID_LATITUDE(_latitude) &&
+ !IS_VALID_LONGITUDE(_longitude));
+}
+
+/** Returns true if the GeoIp object is valid, but no location information
+ * is known for the associated IP address. */
+bool
+GeoIp::isUnknown() const
+{
+ return (!_ip.isNull() &&
+ !IS_VALID_LATITUDE(_latitude) &&
+ !IS_VALID_LONGITUDE(_longitude));
+}
+
+/** Returns a human-readable string of GeoIp location information. */
+QString
+GeoIp::toLocation() const
+{
+ QStringList location;
+
+ /* Add the city name (if present) */
+ if (!_city.isEmpty()) {
+ location << _city;
+ }
+ /* Add the state or region name (if present) */
+ if (!_state.isEmpty()) {
+ /* Only display non-numeric region codes. */
+ bool valid = true;
+ for (int i = 0; i < _state.length(); i++) {
+ if (_state[i].isDigit()) {
+ valid = false;
+ break;
+ }
+ }
+ if (valid) {
+ location << _state;
+ }
+ }
+ /* Add the country code (if present) */
+ if (!_country.isEmpty()) {
+ location << _country;
+ }
+ return location.join(", ");
+}
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoip.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoip.h (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoip.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,72 @@
+/*
+** 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 geoip.h
+** \version $Id: geoip.h 2362 2008-02-29 04:30:11Z edmanm $
+** \brief Associates an IP with a geographic location
+*/
+
+#ifndef _GEOIP_H
+#define _GEOIP_H
+
+#include <QString>
+#include <QHostAddress>
+
+
+class GeoIp
+{
+public:
+ /** Default constructor */
+ GeoIp() : _latitude(0xFFFF), _longitude(0xFFFF) {}
+ /** Constructor. */
+ GeoIp(QHostAddress ip);
+
+ /** Constructor */
+ GeoIp(QHostAddress ip, float latitude, float longitude,
+ QString city, QString state, QString country);
+
+ /** Creates a GeoIp object from a string. */
+ static GeoIp fromString(QString geoip);
+ /** Builds a comma-delimited string of GeoIp fields. */
+ QString toString() const;
+
+ /** Returns the IP address for this object. */
+ QHostAddress ip() const { return _ip; }
+ /** Returns the latitude coordinate for this IP. */
+ float latitude() const { return _latitude; }
+ /** Returns the longitude coordinate for this IP. */
+ float longitude() const { return _longitude; }
+ /** Returns the city in which this IP lives. */
+ QString city() const { return _city; }
+ /** Returns the state or district in which this IP lives. */
+ QString state() const { return _state; }
+ /** Returns the country in which this IP lives. */
+ QString country() const { return _country; }
+ /** Returns a human-readable string of city, region(state), and country. */
+ QString toLocation() const;
+
+ /** Returns true if the GeoIp object is invalid. */
+ bool isEmpty() const;
+ /** Returns true if the GeoIp object is valid, but no location information
+ * is known for the associated IP address. */
+ bool isUnknown() 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 _state; /**< State or district in which this IP lives. */
+ QString _country; /**< Country in which this IP lives. */
+};
+
+#endif
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.cpp (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,144 @@
+/*
+** 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 geoipcache.cpp
+** \version $Id: geoipcache.cpp 2362 2008-02-29 04:30:11Z edmanm $
+** \brief Caches the results of previous GeoIP requests
+*/
+
+#include <QFile>
+#include <QDir>
+#include <QTextStream>
+#include <stringutil.h>
+#include <file.h>
+#include <vidalia.h>
+
+#include "geoipcache.h"
+
+/* Location of Vidalia's geoip cache file. Qt docs claims that QFile will
+ * translate the "/" correctly on Windows. Hopefully they didn't lie. */
+#define CACHE_FILENAME (Vidalia::dataDirectory() + "/geoip-cache")
+
+
+/** Constructor. */
+GeoIpCache::GeoIpCache()
+{
+ loadFromDisk();
+}
+
+/** Returns the location currently used for the cache file. */
+QString
+GeoIpCache::cacheFilename()
+{
+ return CACHE_FILENAME;
+}
+
+/** Writes the current cache to disk. */
+bool
+GeoIpCache::saveToDisk(QString *errmsg)
+{
+ /* Make sure we have a data directory. */
+ if (!create_path(Vidalia::dataDirectory())) {
+ return false;
+ }
+
+ /* Try to open a temporary cache file for writing */
+ QFile tmpCacheFile(CACHE_FILENAME + ".tmp");
+ if (!tmpCacheFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ return err(errmsg, tmpCacheFile.errorString());
+ }
+
+ /* Write the cache entries to the file. */
+ QTextStream cache(&tmpCacheFile);
+ foreach (GeoIpCacheItem cacheItem, _cache.values()) {
+ /* Save the cache item if it's not too old. */
+ if (!cacheItem.isExpired()) {
+ cache << cacheItem.toString() << endl;
+ }
+ }
+
+ QFile cacheFile(CACHE_FILENAME);
+ /* Check if an previous cache file exists. */
+ if (cacheFile.exists()) {
+ /* A previous cache file exists, so try to remove it */
+ if (!cacheFile.remove()) {
+ return err(errmsg, cacheFile.errorString());
+ }
+ }
+ /* Rename the temporary file into place. */
+ if (tmpCacheFile.rename(cacheFile.fileName())) {
+ return err(errmsg, tmpCacheFile.errorString());
+ }
+ return true;
+}
+
+/** Reads the cache contents in from disk. This function returns true if no
+ * cache file exists, since it's possible nothing has been cached yet. */
+bool
+GeoIpCache::loadFromDisk(QString *errmsg)
+{
+ QFile cacheFile(CACHE_FILENAME);
+
+ if (cacheFile.exists()) {
+ /* Try to open the cache file */
+ if (!cacheFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return err(errmsg, cacheFile.errorString());
+ }
+
+ /* Read the cached items from the cache file */
+ QTextStream cache(&cacheFile);
+ QString line = cache.readLine();
+ while (!line.isNull()) {
+ /* Create a GeoIpCacheItem from the line and save it */
+ GeoIpCacheItem item = GeoIpCacheItem::fromString(line);
+ if (!item.isEmpty() && !item.isExpired()) {
+ /* Only load non-stale cache items. */
+ _cache.insert(item.ip().toIPv4Address(), item);
+ }
+ line = cache.readLine();
+ }
+ }
+ return true;
+}
+
+/** Caches the given IP and geographic information to disk. Call saveToDisk()
+ * when you want to write the cache to disk. */
+void
+GeoIpCache::cache(GeoIp geoip)
+{
+ /* Store the entry in our in-memory cache */
+ _cache.insert(geoip.ip().toIPv4Address(),
+ GeoIpCacheItem(geoip,QDateTime::currentDateTime()));
+}
+
+/** Returns a GeoIp object for the given IP from cache. */
+GeoIp
+GeoIpCache::geoip(QHostAddress ip)
+{
+ if (this->contains(ip)) {
+ return _cache.value(ip.toIPv4Address()).geoip();
+ }
+ return GeoIp();
+}
+
+/** Returns true if the given IP address is cached and the cached information
+ * is not stale. */
+bool
+GeoIpCache::contains(QHostAddress ip)
+{
+ quint32 ipv4 = ip.toIPv4Address();
+ if (_cache.contains(ipv4)) {
+ GeoIpCacheItem cacheItem = _cache.value(ipv4);
+ return !cacheItem.isExpired();
+ }
+ return false;
+}
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.h (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoipcache.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,52 @@
+/*
+** 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 geoipcache.h
+** \version $Id: geoipcache.h 2362 2008-02-29 04:30:11Z edmanm $
+** \brief Caches the results of previous GeoIP requests
+*/
+
+#ifndef _GEOIPCACHE_H
+#define _GEOIPCACHE_H
+
+#include <QString>
+#include <QHash>
+#include <QHostAddress>
+
+#include "geoipcacheitem.h"
+
+
+class GeoIpCache
+{
+public:
+ /** Default constructor. */
+ GeoIpCache();
+
+ /** Writes the current cache to disk. */
+ bool saveToDisk(QString *errmsg = 0);
+ /** Reads the cache in from disk. */
+ bool loadFromDisk(QString *errmsg = 0);
+
+ /** Returns the location currently used for the cache file. */
+ QString cacheFilename();
+ /** Caches the given IP and geographic information to disk. */
+ void cache(GeoIp geoip);
+ /** Returns a GeoIp object for the given IP from cache. */
+ GeoIp geoip(QHostAddress ip);
+ /** Returns true if the given IP address is cached. */
+ bool contains(QHostAddress ip);
+
+private:
+ QHash<quint32, GeoIpCacheItem> _cache; /**< List of cached GeoIp objects. */
+};
+
+#endif
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.cpp (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,84 @@
+/*
+** 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 geoipcacheitem.cpp
+** \version $Id: geoipcacheitem.cpp 2362 2008-02-29 04:30:11Z edmanm $
+** \brief Cached result of a single IP-to-geolocation result
+*/
+
+#include <QStringList>
+
+#include "geoipcacheitem.h"
+
+
+/** Constructor */
+GeoIpCacheItem::GeoIpCacheItem(GeoIp geoip, QDateTime timestamp)
+{
+ _geoip = geoip;
+ _timestamp = timestamp;
+}
+
+/** Returns true if this cache item is empty and invalid. A valid cache item
+ * must have a valid GeoIp object and timestamp. */
+bool
+GeoIpCacheItem::isEmpty() const
+{
+ return (_geoip.isEmpty() || _timestamp.isNull());
+}
+
+/** Returns a string representing the contents of this cache item, suitable
+ * for writing to disk. The format is as in the following example:
+ * <Geo IP Data>:<Timestamp>
+ */
+QString
+GeoIpCacheItem::toString() const
+{
+ return _geoip.toString() + ":" + QString::number(_timestamp.toTime_t());
+}
+
+/** Returns a GeoIpCacheItem from a string as read from the cache that was
+ * written to disk. The format is:
+ * <Geo IP Data>[:<Timestamp>]
+ *
+ * If no value for Timestamp is given, the current date and time will be used.
+ * If the string cannot be parsed for valid cached GeoIP data, then an empty
+ * GeoIpCacheItem object is returned. The calling method should call isEmpty()
+ * on the returned GeoIpCacheItem object to ensure it got a valid object.
+ */
+GeoIpCacheItem
+GeoIpCacheItem::fromString(QString cacheString)
+{
+ QDateTime timestamp;
+ QStringList cacheData = cacheString.split(":");
+
+ if (cacheData.size() >= 1) {
+ GeoIp geoip = GeoIp::fromString(cacheData.at(0));
+ if (cacheData.size() >= 2)
+ timestamp.setTime_t(cacheData.at(1).toUInt());
+ else
+ timestamp = QDateTime::currentDateTime();
+ return GeoIpCacheItem(geoip, timestamp);
+ }
+ return GeoIpCacheItem();
+}
+
+/** Returns true if the cache item is too old to be considered valid. Normal
+ * cached responses are valid for one month. Cached UNKNOWN responses are
+ * considered valid for one week. */
+bool
+GeoIpCacheItem::isExpired() const
+{
+ if (_geoip.isUnknown()) {
+ return (_timestamp.addDays(7) < QDateTime::currentDateTime());
+ }
+ return (_timestamp.addMonths(1) < QDateTime::currentDateTime());
+}
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.h (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoipcacheitem.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,55 @@
+/*
+** 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 geoipcacheitem.h
+** \version $Id: geoipcacheitem.h 2362 2008-02-29 04:30:11Z edmanm $
+** \brief Cached result of a single IP-to-geolocation result
+*/
+
+#ifndef _GEOIPCACHEITEM_H
+#define _GEOIPCACHEITEM_H
+
+#include <QDateTime>
+
+#include <geoip.h>
+
+
+class GeoIpCacheItem
+{
+public:
+ /** Default constructor */
+ GeoIpCacheItem() {};
+ /** Constructor. */
+ GeoIpCacheItem(GeoIp geoip, QDateTime timestamp);
+
+ /** Returns the IP of this cache item. */
+ QHostAddress ip() const { return _geoip.ip(); }
+ /** Returns the cached GeoIp object. */
+ GeoIp geoip() const { return _geoip; }
+ /** Returns true if this cache item is expired. */
+ bool isExpired() const;
+ /** Returns true if this cache item is empty and invalid. */
+ bool isEmpty() const;
+
+ /** Returns a string representing the contents of this cache item, suitable
+ * for writing to disk. */
+ QString toString() const;
+ /** Returns a GeoIpCacheItem from a string as read from the cache that was
+ * written to disk. */
+ static GeoIpCacheItem fromString(QString cacheString);
+
+private:
+ GeoIp _geoip; /**< Cached GeoIp item. */
+ QDateTime _timestamp; /**< Time this item was cached. */
+};
+
+#endif
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.cpp (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,77 @@
+/*
+** 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 geoiprequest.cpp
+** \version $Id: geoiprequest.cpp 2362 2008-02-29 04:30:11Z edmanm $
+** \brief A formatted request for GeoIP information for one or more IPs
+*/
+
+#include <zlibbytearray.h>
+
+#include "geoiprequest.h"
+
+
+/** Creates an HTTP POST header for this request, based on the
+ * Host, Page, and content-length values. */
+QHttpRequestHeader
+GeoIpRequest::createHeader() const
+{
+ QHttpRequestHeader header("POST", _page, 1, 1);
+
+ if (!_host.isEmpty())
+ header.setValue("Host", _host);
+ header.setContentType("application/x-www-form-urlencoded");
+ header.setContentLength(_request.length());
+ header.setValue("Connection", "close");
+
+ if (ZlibByteArray::isZlibAvailable()) {
+ QString acceptEncodings = "deflate, x-deflate";
+ if (ZlibByteArray::isGzipSupported())
+ acceptEncodings += ", gzip, x-gzip";
+ header.setValue("Accept-Encoding", acceptEncodings);
+ }
+
+ return header;
+}
+
+/** Sets the list of IPs whose geo information we want to request. */
+void
+GeoIpRequest::setRequest(const QList<QHostAddress> &ips)
+{
+ _request = "ip=";
+ int ipcount = ips.size();
+
+ /* Add each IP to a comma-delimited list. */
+ for (int i = 0; i < ipcount; i++) {
+ _request.append(ips.at(i).toString());
+ if (i < ipcount-1) {
+ _request.append(",");
+ }
+ }
+ _ips = ips;
+}
+
+/** Formats the request as an HTTP POST request. */
+QByteArray
+GeoIpRequest::request() const
+{
+ /* Create the header and append the request content. */
+ QString request = createHeader().toString() + _request;
+ return request.toAscii();
+}
+
+/** Returns true if this request contains <b>ip</b>. */
+bool
+GeoIpRequest::contains(const QHostAddress &ip) const
+{
+ return _ips.contains(ip);
+}
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.h (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoiprequest.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,61 @@
+/*
+** 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 geoiprequest.h
+** \version $Id: geoiprequest.h 2362 2008-02-29 04:30:11Z edmanm $
+** \brief A formatted request for GeoIP information for one or more IPs
+*/
+
+#ifndef _GEOIPREQUEST_H
+#define _GEOIPREQUEST_H
+
+#include <QList>
+#include <QString>
+#include <QByteArray>
+#include <QHostAddress>
+#include <QHttpRequestHeader>
+
+
+class GeoIpRequest
+{
+public:
+ /** Constructor */
+ GeoIpRequest(int id) : _id(id) {}
+
+ /** Sets the Host: field in this request's header. */
+ void setHost(const QString &host) { _host = host; }
+ /** Sets the page path in this request's header. */
+ void setPage(const QString &page) { _page = page; }
+ /** Sets the list of IPs whose geo information we want to request. */
+ void setRequest(const QList<QHostAddress> &ips);
+ /** Returns true if this request contains <b>ip</b>. */
+ bool contains(const QHostAddress &ip) const;
+
+ /** Returns the request's identifier. */
+ int id() const { return _id; }
+ /** Returns the number of IP addresses contained in this request. */
+ int size() const { return _ips.size(); }
+ /** Formats the request as an HTTP POST request */
+ QByteArray request() const;
+
+private:
+ /** Creates an HTTP header for this request. */
+ QHttpRequestHeader createHeader() const;
+
+ int _id; /**< Request identifier */
+ QString _host; /**< Host: field value. */
+ QString _page; /**< Page giving us the geo ip information. */
+ QString _request; /**< Formatted Geo IP request string. */
+ QList<QHostAddress> _ips; /**< List of IP addresses in this request. */
+};
+
+#endif
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.cpp (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,126 @@
+/*
+** 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 geoipresponse.cpp
+** \version $Id: geoipresponse.cpp 2533 2008-04-25 04:50:43Z edmanm $
+** \brief Parses a response to a previous GeoIP request
+*/
+
+#include <QStringList>
+#include <zlibbytearray.h>
+
+#include "geoipresponse.h"
+
+/** Status code for a successful HTTP request. */
+#define STATUS_HTTP_OK 200
+/** Status code for content encoding errors. */
+#define STATUS_CONTENT_ENCODING_ERR 601
+/** Status code for transfer encoding errors. */
+#define STATUS_TRANSFER_ENCODING_ERR 602
+
+
+/** Constructor. Parses the response data for an HTTP header and Geo IP
+ * information. */
+GeoIpResponse::GeoIpResponse(QByteArray response)
+{
+ QString errmsg;
+
+ /* Parse out the header */
+ int headerPos = response.indexOf("\r\n\r\n");
+ _header = QHttpResponseHeader(QString(response.mid(0, headerPos)));
+
+ /* Parse out the Geo IP information, if any was included. */
+ if (headerPos > 0 && _header.statusCode() == STATUS_HTTP_OK) {
+ QByteArray content = response.mid(headerPos+4);
+
+ if (_header.hasKey("Transfer-Encoding")) {
+ QString encoding = _header.value("Transfer-Encoding");
+ if (encoding == "chunked") {
+ content = decodeChunked(content);
+ if (content.isEmpty()) {
+ _header.setStatusLine(STATUS_TRANSFER_ENCODING_ERR,
+ QString("Failed to decode chunked response"));
+ return;
+ }
+ } else {
+ _header.setStatusLine(STATUS_TRANSFER_ENCODING_ERR,
+ QString("Unknown transfer encoding '%1'").arg(encoding));
+ return;
+ }
+ }
+
+ if (_header.hasKey("Content-Encoding")) {
+ ZlibByteArray::CompressionMethod method;
+ QString encoding = _header.value("Content-Encoding");
+ if (encoding == "gzip" || encoding == "x-gzip") {
+ method = ZlibByteArray::Gzip;
+ } else if (encoding == "deflate" || encoding == "x-deflate") {
+ method = ZlibByteArray::Zlib;
+ } else if (encoding == "text/plain") {
+ method = ZlibByteArray::None;
+ } else {
+ _header.setStatusLine(STATUS_CONTENT_ENCODING_ERR,
+ QString("Unknown content encoding '%1'").arg(encoding));
+ return;
+ }
+
+ content = ZlibByteArray::uncompress(content, method, &errmsg);
+ if (content.isEmpty()) {
+ _header.setStatusLine(STATUS_CONTENT_ENCODING_ERR,
+ QString("Content decoding using method '%1' failed: %2")
+ .arg(encoding).arg(errmsg));
+ return;
+ }
+ }
+
+ /* Parse the Geo IP information in each line */
+ QStringList lines = QString(content).split("\n");
+ foreach (QString line, lines) {
+ GeoIp geoip = GeoIp::fromString(line);
+ if (!geoip.isEmpty())
+ _geoips << geoip;
+ }
+ }
+}
+
+/** Decodes a <b>chunked</b> transfer encoding. Returns the unchunked
+ * result on success, or an empty QByteArray if decoding fails. */
+QByteArray
+GeoIpResponse::decodeChunked(QByteArray chunked)
+{
+ QByteArray unchunked;
+ QString sizeString;
+ int eol, chunkedlen, chunksize, offset = 0;
+ bool ok;
+
+ chunkedlen = chunked.length();
+ while (offset < chunkedlen) {
+ eol = chunked.indexOf("\r\n", offset);
+ if (eol < 0)
+ return QByteArray();
+ sizeString = QString::fromAscii(chunked.mid(offset, eol-offset));
+ offset = eol + 2; /* Skip past the CRLF */
+
+ if (sizeString.indexOf(";") >= 0)
+ sizeString.truncate(sizeString.indexOf(";"));
+ chunksize = sizeString.toInt(&ok, 16);
+ if (!ok || chunksize > chunkedlen - offset)
+ return QByteArray();
+ if (!chunksize)
+ break; /* Last chunk. Ignore the trailer. */
+
+ unchunked.append(chunked.mid(offset, chunksize));
+ offset += chunksize;
+ offset += 2; /* CRLF after each chunk */
+ }
+ return unchunked;
+}
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.h (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/geoipresponse.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,51 @@
+/*
+** 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 geoipresponse.h
+** \version $Id: geoipresponse.h 2362 2008-02-29 04:30:11Z edmanm $
+** \brief Parses a response to a previous GeoIP request
+*/
+
+#ifndef _GEOIPRESPONSE_H
+#define _GEOIPRESPONSE_H
+
+#include <QList>
+#include <QByteArray>
+#include <QHttpResponseHeader>
+
+#include "geoip.h"
+
+
+class GeoIpResponse
+{
+public:
+ /** Constructor. Parses the response data for an HTTP header and Geo IP
+ * information. */
+ GeoIpResponse(QByteArray response);
+
+ /** Returns the HTTP status code for this response. */
+ int statusCode() { return _header.statusCode(); }
+ /** Returns the HTTP status message for this response. */
+ QString statusMessage() { return _header.reasonPhrase(); }
+ /** Returns the Geo IP information contained in this response. */
+ QList<GeoIp> geoIps() { return _geoips; }
+
+private:
+ /** Decodes a <b>chunked</b> transfer encoding. Returns the unchunked
+ * result on success, or an empty QByteArray if decoding fails. */
+ QByteArray decodeChunked(QByteArray chunked);
+
+ QHttpResponseHeader _header; /**< HTTP response header. */
+ QList<GeoIp> _geoips; /**< Geo IP information in this response. */
+};
+
+#endif
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.cpp
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.cpp (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.cpp 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,263 @@
+/*
+** 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 geoipresolver.cpp
+** \version $Id: geoipresolver.cpp 2429 2008-03-21 02:27:36Z edmanm $
+** \brief Requests GeoIP information and caches the result
+*/
+
+#include <torsocket.h>
+#include <vidalia.h>
+#include "webgeoipresolver.h"
+#include "config.h"
+
+#if defined(USE_QSSLSOCKET)
+#include <torsslsocket.h>
+#endif
+
+/** Host for the geo ip information. */
+#define GEOIP_HOST "geoip.vidalia-project.net"
+/** The non-encrypted GeoIP service lives on port 80. */
+#define GEOIP_PORT 80
+/** The SSL GeoIP service runs on port 1443 (443 was taken). */
+#define GEOIP_SSL_PORT 1443
+/** Page that we request the geo ip information from. */
+#define GEOIP_PAGE "/cgi-bin/geoip"
+
+
+/** Default constructor. */
+WebGeoIpResolver::WebGeoIpResolver()
+{
+ _socksAddr = QHostAddress::LocalHost;
+ _socksPort = 9050;
+
+#if defined(USE_QSSLSOCKET)
+ if (! QSslSocket::addDefaultCaCertificates(":/geoip/cacert_root.crt"))
+ vWarn("Failed to add the GeoIP CA certificate to the default CA "
+ "certificate database.");
+#endif
+}
+
+/** Sets the address and port of Tor, through which GeoIP requests will be
+ * made. */
+void
+WebGeoIpResolver::setSocksHost(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
+WebGeoIpResolver::resolveFromCache(QHostAddress ip)
+{
+ if (_cache.contains(ip)) {
+ emit resolved(-1, QList<GeoIp>() << _cache.geoip(ip));
+ return true;
+ }
+ return false;
+}
+
+/** 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>
+WebGeoIpResolver::resolveFromCache(QList<QHostAddress> ips)
+{
+ QList<GeoIp> cached;
+
+ /* Build a list of which IPs have cached GeoIp information */
+ foreach (QHostAddress ip, ips) {
+ if (_cache.contains(ip)) {
+ ips.removeAt(ips.indexOf(ip));
+ cached << _cache.geoip(ip);
+ }
+ }
+
+ /* If any were cached, emit their results now */
+ if (cached.size() > 0) {
+ vInfo("Resolved %1 GeoIP entries from cache.").arg(ips.size());
+ emit resolved(-1, cached);
+ }
+ return ips;
+}
+
+/** Resolves a single IP to a geographic location. */
+int
+WebGeoIpResolver::resolve(QHostAddress ip)
+{
+ return resolve(QList<QHostAddress>() << ip);
+}
+
+/** Called when the socket has connected to the Geo IP host. */
+void
+WebGeoIpResolver::connected()
+{
+ /* Find the socket and request for whoever called this slot */
+ QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
+ if (!_requestList.contains(socket)) {
+ return;
+ }
+ GeoIpRequest *req = (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());
+}
+
+/** Called when the socket has disconnected from the Geo IP host. */
+void
+WebGeoIpResolver::disconnected()
+{
+ /* Find the socket and request for whoever called this slot */
+ QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
+ if (!_requestList.contains(socket)) {
+ return;
+ }
+ GeoIpRequest *request = (GeoIpRequest *)_requestList.take(socket);
+
+ /* Read and parse the response */
+ 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 and cache the results */
+ int numCached = 0, i = 0;
+ QList<GeoIp> geoips = response.geoIps();
+ foreach (GeoIp geoip, geoips) {
+ QHostAddress ip = geoip.ip();
+
+ if (request->contains(ip)) {
+ /* This is a requested geoip item, so if it wasn't cached, then
+ * cache it now. */
+ if (!_cache.contains(ip)) {
+ _cache.cache(geoip);
+ numCached++;
+ }
+ i++;
+ } else {
+ /* This item wasn't requested, so remove it. According to the Qt docs,
+ * this is safe to do inside the foreach() loop because, "Qt
+ * automatically takes a copy of the container when it enters a
+ * foreach loop. If you modify the container as you are iterating,
+ * that won't affect the loop." */
+ 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());
+ geoips.removeAt(i);
+ }
+ }
+ /* 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(geoips.size()).arg(request->id());
+ emit resolved(request->id(), geoips);
+ } 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(request->id())
+ .arg(response.statusMessage());
+ emit resolveFailed(request->id(), response.statusMessage());
+ }
+ /* Close the socket and clean up */
+ socket->close();
+ delete socket;
+ delete request;
+}
+
+/** Called when an error has occurred requesting Geo IP information. */
+void
+WebGeoIpResolver::socketError(QString errorString)
+{
+ /* 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 talkig to us. */
+ if (socket->error() != QAbstractSocket::RemoteHostClosedError) {
+ /* Emit the failure and clean up */
+ GeoIpRequest *request = (GeoIpRequest *)_requestList.take(socket);
+ emit resolveFailed(request->id(), errorString);
+ socket->abort();
+ vWarn("GeoIP request socket error (request id %1): %2").arg(request->id())
+ .arg(errorString);
+ delete socket;
+ delete request;
+ }
+}
+
+/** Creates an HTTP request for Geo IP information. */
+GeoIpRequest*
+WebGeoIpResolver::createRequest(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
+WebGeoIpResolver::resolve(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. */
+#if defined(USE_QSSLSOCKET)
+ TorSslSocket *socket = new TorSslSocket(_socksAddr, _socksPort);
+#else
+ TorSocket *socket = new TorSocket(_socksAddr, _socksPort);
+#endif
+
+ 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(ips);
+ _requestList.insert(socket, request);
+
+ /* Connect so we can send our request and return the request ID. */
+#if defined(USE_QSSLSOCKET)
+ if (TorSslSocket::supportsSsl()) {
+ 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);
+ } else {
+ vInfo("Opening an unencrypted connection to the GeoIP host at %1:%2 "
+ "(request id %3)").arg(GEOIP_HOST).arg(GEOIP_PORT).arg(request->id());
+ socket->connectToRemoteHost(GEOIP_HOST, GEOIP_PORT, false);
+ }
+#else
+ vInfo("Opening an unencrypted connection to the GeoIP host at %1:%2 "
+ "(request id %3)").arg(GEOIP_HOST).arg(GEOIP_PORT).arg(request->id());
+ socket->connectToRemoteHost(GEOIP_HOST, GEOIP_PORT);
+#endif
+ return request->id();
+}
+
Added: vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.h
===================================================================
--- vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.h (rev 0)
+++ vidalia/branches/exit-country/src/vidalia/geoip/webgeoipresolver.h 2008-08-05 13:18:56 UTC (rev 2940)
@@ -0,0 +1,82 @@
+/*
+** 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 geoipresolver.h
+** \version $Id: geoipresolver.h 2429 2008-03-21 02:27:36Z edmanm $
+** \brief Requests GeoIP information and caches the result
+*/
+
+#ifndef _WEBGEOIPRESOLVER_H
+#define _WEBGEOIPRESOLVER_H
+
+#include <QObject>
+#include <QList>
+#include <QHash>
+#include <QString>
+#include <QHostAddress>
+
+#include <geoip.h>
+#include "geoipcache.h"
+#include "geoiprequest.h"
+#include "geoipresponse.h"
+
+
+class WebGeoIpResolver : public QObject
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ WebGeoIpResolver();
+
+ /** Sets the address and port of Tor, through which GeoIP requests will be
+ * made. */
+ void setSocksHost(QHostAddress addr, quint16 port);
+ /** Resolves a single IP to a geographic location. */
+ int resolve(QHostAddress ip);
+ /** Resolves a list of IPs to a geographic location. */
+ int resolve(QList<QHostAddress> ips);
+ /** Resolves <b>ip</b> to geographic information only if it is cached. */
+ bool resolveFromCache(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(QList<QHostAddress> ips);
+
+signals:
+ /** Emitted when a list of IPs have been resolved to lat/long. */
+ void resolved(int id, QList<GeoIp> geoips);
+ /** Emitted when a resolve has failed. */
+ void resolveFailed(int id, QString errorString);
+
+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(QString errorString);
+
+private:
+ /** Creates an HTTP request for Geo IP information. */
+ GeoIpRequest* createRequest(QList<QHostAddress> ips);
+
+ /**< 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;
+};
+
+#endif
+