geoipresolver.cpp

Go to the documentation of this file.
00001 /****************************************************************
00002  *  Vidalia is distributed under the following license:
00003  *
00004  *  Copyright (C) 2006,  Matt Edman, Justin Hipple
00005  *
00006  *  This program is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU General Public License
00008  *  as published by the Free Software Foundation; either version 2
00009  *  of the License, or (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  *  Boston, MA  02110-1301, USA.
00020  ****************************************************************/
00021 
00022 /**
00023  * \file geoipresolver.cpp
00024  * \version $Id: geoipresolver.cpp 1563 2006-12-26 06:06:04Z edmanm $
00025  * \brief Requests GeoIP information and caches the result
00026  */
00027 
00028 #include "geoipresolver.h"
00029 
00030 /** Host for the geo ip information. This hostname round-robins between
00031  * pasiphae.vidalia-project.net, thebe.vidalia-project.net, and
00032  * cups.cs.cmu.edu. */ 
00033 #define GEOIP_HOST    "geoip.vidalia-project.net"
00034 /** Page that we request the geo ip information from. */
00035 #define GEOIP_PAGE    "/cgi-bin/geoip"
00036 
00037 
00038 /** Sets the address and port of Tor, through which GeoIP requests will be
00039  * made. */
00040 void
00041 GeoIpResolver::setSocksHost(QHostAddress addr, quint16 port)
00042 {
00043   _socksAddr = addr;
00044   _socksPort = port;
00045 }
00046 
00047 /** Resolves <b>ip</b> to geographic information if it is cached. A resolved()
00048  * signal will be emitted and true returned if we have cached geographic
00049  * information for <b>ip</b>. Otherwise, this returns false. */
00050 bool
00051 GeoIpResolver::resolveFromCache(QHostAddress ip)
00052 {
00053   if (_cache.contains(ip)) {
00054     emit resolved(-1, QList<GeoIp>() << _cache.geoip(ip));
00055     return true;
00056   }
00057   return false;
00058 }
00059 
00060 /** Resolves a list of IPs to a geographic location, but only those which are
00061  * cached. Returns a list of IPs that were not in the cache. */
00062 QList<QHostAddress>
00063 GeoIpResolver::resolveFromCache(QList<QHostAddress> ips)
00064 {
00065   QList<GeoIp> cached;
00066 
00067   /* Build a list of which IPs have cached GeoIp information */
00068   foreach (QHostAddress ip, ips) {
00069     if (_cache.contains(ip)) {
00070       ips.removeAt(ips.indexOf(ip));
00071       cached << _cache.geoip(ip);
00072     }
00073   }
00074 
00075   /* If any were cached, emit their results now */
00076   if (cached.size() > 0) {
00077     emit resolved(-1, cached);
00078   }
00079   return ips;
00080 }
00081 
00082 /** Resolves a single IP to a geographic location. */
00083 int
00084 GeoIpResolver::resolve(QHostAddress ip)
00085 {
00086   return resolve(QList<QHostAddress>() << ip);
00087 }
00088 
00089 /** Called when the socket has connected to the Geo IP host. */
00090 void
00091 GeoIpResolver::connected()
00092 {
00093   /* Find the socket and request for whoever called this slot */ 
00094   TorSocket *socket = (TorSocket *)sender();
00095   if (!_requestList.contains(socket)) {
00096     return;
00097   }
00098   GeoIpRequest *req = (GeoIpRequest *)_requestList.value(socket);
00099   
00100   /* Send the request */
00101   socket->write(req->request());
00102 }
00103 
00104 /** Called when the socket has disconnected from the Geo IP host. */
00105 void
00106 GeoIpResolver::disconnected()
00107 {
00108   /* Find the socket and request for whoever called this slot */ 
00109   TorSocket *socket = (TorSocket *)sender();
00110   if (!_requestList.contains(socket)) {
00111     return;
00112   }
00113   GeoIpRequest *request = (GeoIpRequest *)_requestList.take(socket);
00114 
00115   /* Read and parse the response */
00116   GeoIpResponse response = GeoIpResponse(socket->readAll());
00117 
00118   /* Check the response code and see what we got */
00119   if (response.statusCode() == 200) {
00120     /* We got a 200 OK, so get the Geo IP information and cache the results */
00121     int numCached = 0, i = 0;
00122     QList<GeoIp> geoips = response.geoIps();
00123     foreach (GeoIp geoip, geoips) {
00124       QHostAddress ip = geoip.ip();
00125       
00126       if (request->contains(ip)) {
00127         /* This is a requested geoip item, so if it wasn't cached, then 
00128          * cache it now. */
00129         if (!_cache.contains(ip)) {
00130           _cache.cache(geoip);
00131           numCached++;
00132         }
00133         i++;
00134       } else {
00135         /* This item wasn't requested, so remove it. According to the Qt docs,
00136          * this is safe to do inside the foreach() loop because, "Qt
00137          * automatically takes a copy of the container when it enters a
00138          * foreach loop. If you modify the container as you are iterating,
00139          * that won't affect the loop." */
00140         geoips.removeAt(i);
00141       }
00142     }
00143     /* If new results were cached, save them to disk */
00144     if (numCached > 0) {
00145       _cache.saveToDisk();
00146     }
00147     /* Emit the results */
00148     emit resolved(request->id(), geoips);
00149   } else {
00150     /* We failed to get the Geo IP information, so emit resolveFailed and
00151      * include the HTTP status message. */
00152     emit resolveFailed(request->id(), response.statusMessage());
00153   }
00154   /* Close the socket and clean up */
00155   socket->close();
00156   delete socket;
00157   delete request;
00158 }
00159 
00160 /** Called when an error has occurred requesting Geo IP information. */
00161 void
00162 GeoIpResolver::socketError(QString errorString)
00163 {
00164   /* Find the socket and request for whoever called this slot */ 
00165   TorSocket *socket = (TorSocket *)sender();
00166   if (!_requestList.contains(socket)) {
00167     return;
00168   }
00169   
00170   /* We expect a remote host to close the socket, because that's how the HTTP
00171    * server tells us he's done talkig to us. */
00172   if (socket->error() != QAbstractSocket::RemoteHostClosedError) {
00173     /* Emit the failure and clean up */
00174     GeoIpRequest *request = (GeoIpRequest *)_requestList.take(socket);
00175     emit resolveFailed(request->id(), errorString);
00176     socket->abort();
00177     delete socket;
00178     delete request;
00179   }
00180 }
00181 
00182 /** Creates an HTTP request for Geo IP information. */
00183 GeoIpRequest*
00184 GeoIpResolver::createRequest(QList<QHostAddress> ips)
00185 {
00186   static int id = -1;
00187   GeoIpRequest *request = new GeoIpRequest(++id);
00188   request->setHost(GEOIP_HOST);
00189   request->setPage(GEOIP_PAGE);
00190   request->setRequest(ips);
00191   return request;
00192 }
00193 
00194 /** Creates a TorSocket used to connect to the Geo IP host. */
00195 TorSocket*
00196 GeoIpResolver::createRequestSocket()
00197 {
00198   TorSocket *socket = new TorSocket(_socksAddr, _socksPort);
00199   connect(socket, SIGNAL(connectedToHost()), this, SLOT(connected()),
00200           Qt::QueuedConnection);
00201   connect(socket, SIGNAL(socketError(QString)), 
00202             this,   SLOT(socketError(QString)),
00203           Qt::QueuedConnection);
00204   connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()),
00205           Qt::QueuedConnection);
00206   return socket;
00207 }
00208 
00209 /** Resolves a list of IPs to a geographic location. */
00210 int
00211 GeoIpResolver::resolve(QList<QHostAddress> ips)
00212 {
00213   /* Resolve the cached IPs and get a list of IPs that still need to be
00214    * resolved to a lat and long. */
00215   QList<QHostAddress> uncached = resolveFromCache(ips);
00216   if (!uncached.size()) {
00217     return -1;
00218   }
00219 
00220   /* Create a socket used ot request the geo ip information. */
00221   TorSocket *socket = createRequestSocket();
00222   GeoIpRequest *request = createRequest(ips);
00223   _requestList.insert(socket, request);
00224   
00225   /* Connect so we can send our request and return the request ID. */
00226   socket->connectToHost(GEOIP_HOST, 80);
00227   return request->id();
00228 }
00229 

Generated on Wed Sep 5 15:49:27 2007 for Vidalia by  doxygen 1.5.3