torcontrol.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 torcontrol.cpp
00024  * \version $Id: torcontrol.cpp 1261 2006-10-03 06:29:35Z edmanm $
00025  * \brief Object for interacting with the Tor process and control interface
00026  */
00027 
00028 #include <QHostAddress>
00029 #include <config/torsettings.h>
00030 #include <util/net.h>
00031 #include <util/file.h>
00032 #include "torcontrol.h"
00033 
00034 
00035 /** Default constructor */
00036 TorControl::TorControl()
00037 {
00038   /* For reasons currently unknown to me, QProcess saves some state
00039    * information between executions of a process. Consequently, if we started
00040    * Tor, it crashed, and then we tried to restart it, Vidalia would crash in
00041    * the QProcess code. So, we create a new TorProcess object each time we
00042    * start Tor and then destroy it when it stops. */
00043   _torProcess = 0;
00044   _torService = 0;
00045 
00046   /** Create an instance of a connection to Tor's control interface and give
00047    * it an object to use to handle asynchronous events. */
00048   _controlConn = new ControlConnection(&_torEvents);
00049 
00050   /* Plumb the appropriate socket signals */
00051   QObject::connect(_controlConn, SIGNAL(connected()),
00052                    this, SLOT(onConnected()));
00053   QObject::connect(_controlConn, SIGNAL(connectFailed(QString)),
00054                    this, SLOT(onConnectFailed(QString)));
00055   QObject::connect(_controlConn, SIGNAL(disconnected()),
00056                    this, SLOT(onDisconnected()));
00057 }
00058 
00059 /** Default destructor */
00060 TorControl::~TorControl()
00061 {
00062   /* Disconnect the control socket */
00063   if (isConnected()) {
00064     disconnect();
00065   }
00066   /* If we started our own Tor, stop it now */
00067   if (isVidaliaRunningTor()) {
00068     stop();
00069   }
00070   delete _controlConn;
00071 }
00072 
00073 /** Start the Tor process. Returns true if the process was successfully
00074  * started, otherwise returns false. */
00075 void
00076 TorControl::start()
00077 {
00078   if (isRunning()) {
00079     emit started();
00080   } else {
00081     TorSettings settings;
00082     
00083     /* Make sure our torrc and the full path to it exists. If it doesn't,
00084      * then touch it. */
00085     touch_file(settings.getTorrc(), true);
00086     
00087     if (TorService::isSupported() && settings.getUseService()) {
00088       _torService = new TorService(settings.getExecutable(),
00089                                    settings.getTorrc(), this);
00090 
00091       QObject::connect(_torService, SIGNAL(started()),
00092                        this, SLOT(onStarted()), Qt::QueuedConnection);
00093       QObject::connect(_torService, SIGNAL(finished()),
00094                        this, SLOT(onStopped()));
00095       QObject::connect(_torService, SIGNAL(startFailed(QString)),
00096                        this, SLOT(onStartFailed(QString)), 
00097                        Qt::QueuedConnection);
00098 
00099       _torService->start();
00100       
00101     } else {
00102       _torProcess = new TorProcess;
00103   
00104       /* Plumb the process signals */
00105       QObject::connect(_torProcess, SIGNAL(started()),
00106            this, SLOT(onStarted()), Qt::QueuedConnection);
00107       QObject::connect(_torProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
00108            this, SLOT(onStopped(int, QProcess::ExitStatus)));
00109       QObject::connect(_torProcess, SIGNAL(startFailed(QString)),
00110            this, SLOT(onStartFailed(QString)), Qt::QueuedConnection);
00111       QObject::connect(_torProcess, SIGNAL(log(QString, QString)),
00112            this, SLOT(onLogStdout(QString, QString)));
00113   
00114       /* Kick off the Tor process. */
00115       _torProcess->start(settings.getExecutable(), 
00116                          settings.getArguments());
00117     }
00118   }
00119 }
00120 
00121 /** Emits a signal that the Tor process started */
00122 void
00123 TorControl::onStarted()
00124 {
00125   emit started();
00126 }
00127 
00128 /** Emits a signal that the Tor process failed to start and includes an error
00129  * message (hopefully) indicating why. */
00130 void
00131 TorControl::onStartFailed(QString errmsg)
00132 {
00133   /* If an error occurs we need to clean up the tor process */
00134   closeTorProcess();
00135 
00136   emit startFailed(errmsg);
00137 }
00138 
00139 /** Stop the Tor process. */
00140 bool
00141 TorControl::stop(QString *errmsg)
00142 {
00143   /* If there is no Tor running, then our job is done */
00144   if (!isRunning()) {
00145     return true;
00146   }
00147   /* If we didn't start our own Tor, send it a shutdown signal */
00148   if (!_torProcess) {
00149     if (!this->signal(TorSignal::Shutdown)) {
00150       return false;
00151     }
00152     emit stopped(0, QProcess::NormalExit);
00153     return true;
00154   } else {
00155     /* We started our own Tor, so stop the process */
00156     return _torProcess->stop(errmsg);
00157   }
00158 }
00159 
00160 /** Emits a signal that the Tor process stopped */
00161 void
00162 TorControl::onStopped(int exitCode, QProcess::ExitStatus exitStatus)
00163 {
00164   closeTorProcess();
00165   
00166   if (_controlConn->status() == ControlConnection::Connecting) {
00167     _controlConn->cancelConnect();
00168   } else {
00169     disconnect();
00170   }
00171 
00172   emit stopped(exitCode, exitStatus);
00173 }
00174 
00175 /** Disconnect signals from _torProcess and clean up after it. */
00176 void
00177 TorControl::closeTorProcess()
00178 {
00179   if (_torProcess) {
00180     QObject::disconnect(_torProcess, 0, 0, 0);
00181     delete _torProcess;
00182     _torProcess = 0;
00183   }
00184   if (_torService) {
00185     QObject::disconnect(_torService, 0, 0, 0);
00186   }
00187 }
00188 
00189 /** Detects if the Tor process is running under Vidalia. Returns true if
00190  * Vidalia owns the Tor process, or false if it was an independent Tor. */
00191 bool
00192 TorControl::isVidaliaRunningTor()
00193 {
00194   if (_torProcess) {
00195     return (_torProcess->pid() != 0);
00196   }
00197   return false;
00198 }
00199 
00200 /** Detect if the Tor process is running. */
00201 bool
00202 TorControl::isRunning()
00203 {
00204   if (_torProcess) {
00205     return (_torProcess->pid() != 0);
00206   }
00207   
00208   TorSettings settings;
00209   if (net_test_connect(settings.getControlAddress(),
00210                        settings.getControlPort(), 250)) {
00211     return true;
00212   }
00213   return false;
00214 }
00215 
00216 /** Called when Tor has printed a log message to stdout. */
00217 void
00218 TorControl::onLogStdout(QString severity, QString message)
00219 {
00220   LogEvent::Severity s = LogEvent::toSeverity(severity);
00221   _torEvents.dispatch(TorEvents::toTorEvent(s), new LogEvent(s, message));
00222 }
00223 
00224 /** Connect to Tor's control port. The control port to use is determined by
00225  * Vidalia's configuration file. */
00226 void
00227 TorControl::connect()
00228 {
00229   TorSettings settings;
00230   _controlConn->connect(settings.getControlAddress(),
00231                         settings.getControlPort());
00232 }
00233 
00234 /** Emits a signal that the control socket successfully established a
00235  * connection to Tor. */
00236 void
00237 TorControl::onConnected()
00238 {
00239   QString errmsg;
00240  
00241   /* Authenticate and register for any pertinent asynchronous events. */
00242   if (!authenticate(&errmsg) || !setEvents(&errmsg)) {
00243     emit connectFailed(errmsg);
00244     stop();
00245     return;
00246   }
00247   /* The control socket is connected, so we can stop reading from stdout */
00248   if (_torProcess) {
00249     _torProcess->closeStdout();
00250   }
00251   /* The version of Tor isn't going to change while we're connected to it, so
00252    * save it for later. */
00253   getInfo("version", _torVersion);
00254 
00255   /* Let interested parties know that the control socket connected */
00256   emit connected();
00257   emit connected(true);
00258 }
00259 
00260 /** Emits a signal that the control connection to Tor failed. */
00261 void
00262 TorControl::onConnectFailed(QString errmsg)
00263 {
00264   emit connectFailed(errmsg);
00265 }
00266 
00267 /** Disconnect from Tor's control port */
00268 void
00269 TorControl::disconnect()
00270 {
00271   if (isConnected()) {
00272     _controlConn->disconnect();
00273   }
00274 }
00275 
00276 /** Emits a signal that the control socket disconnected from Tor */
00277 void
00278 TorControl::onDisconnected()
00279 {
00280   if (_torProcess) {
00281     /* If we're running a Tor process, then start reading logs from stdout
00282      * again, in case our control connection just died but Tor is still
00283      * running. In this case, there may be relevant information in the logs. */ 
00284     _torProcess->openStdout();
00285   }
00286   /* Tor isn't running, so it has no version */
00287   _torVersion = QString();
00288 
00289   /* Let interested parties know we lost our control connection */
00290   emit disconnected();
00291   emit connected(false);
00292   
00293   if (!isVidaliaRunningTor()) {
00294     /* If we're not running our own Tor, then we interpret the closing of 
00295      * our control connection to mean that Tor stopped. */
00296     emit stopped(0, QProcess::NormalExit);
00297   }
00298 }
00299 
00300 /** Check if the control socket is connected */
00301 bool
00302 TorControl::isConnected()
00303 {
00304   return (_controlConn->status() == ControlConnection::Connected);
00305 }
00306 
00307 /** Send a message to Tor and reads the response. If Vidalia was unable to
00308  * send the command to Tor or read its response, false is returned. If the
00309  * response was read and the status code is not 250 OK, false is also
00310  * returned. */
00311 bool
00312 TorControl::send(ControlCommand cmd, ControlReply &reply, QString *errmsg)
00313 {
00314   if (_controlConn->send(cmd, reply, errmsg)) {
00315     if (reply.getStatus() == "250") {
00316       return true;
00317     }
00318     if (errmsg) {
00319       *errmsg = reply.getMessage();
00320     }
00321   }
00322   return false;
00323 }
00324 
00325 /** Sends a message to Tor and discards the response. */
00326 bool
00327 TorControl::send(ControlCommand cmd, QString *errmsg)
00328 {
00329   ControlReply reply;
00330   return send(cmd, reply, errmsg);
00331 }
00332 
00333 /** Sends an authentication token to Tor. This must be done before sending 
00334  * any control commands to Tor. The syntax is:
00335  * 
00336  *   "AUTHENTICATE" [ SP 1*HEXDIG / QuotedString ] CRLF
00337  */
00338 bool
00339 TorControl::authenticate(QString *errmsg)
00340 {
00341   TorSettings settings;
00342   ControlCommand cmd("AUTHENTICATE", QString(settings.getAuthToken()));
00343   return send(cmd, errmsg);
00344 }
00345 
00346 /** Sends a GETINFO message to Tor based on the given map of keyvals. The
00347  * syntax is:
00348  * 
00349  *    "GETINFO" 1*(SP keyword) CRLF 
00350  */
00351 bool
00352 TorControl::getInfo(QHash<QString,QString> &map, QString *errmsg)
00353 {
00354   ControlCommand cmd("GETINFO");
00355   ControlReply reply;
00356 
00357   /* Add the keys as arguments to the GETINFO message */
00358   foreach (QString key, map.keys()) {
00359     cmd.addArgument(key);
00360   }
00361  
00362   /* Ask Tor for the specified info values */
00363   if (send(cmd, reply, errmsg)) {
00364     /* Parse the response for the returned values */
00365     foreach (ReplyLine line, reply.getLines()) {
00366       /* Split the "key=val" line and map them */
00367       QStringList keyval = line.getMessage().split("=");
00368       if (keyval.size() == 2) {
00369         map.insert(keyval.at(0), keyval.at(1));
00370       }
00371     }
00372     return true;
00373   }
00374   return false;
00375 }
00376 
00377 /** Overloaded method to send a GETINFO command for a single info value */
00378 bool
00379 TorControl::getInfo(QString key, QString &val, QString *errmsg)
00380 {
00381   QHash<QString,QString> map;
00382   map.insert(key, "");
00383 
00384   if (getInfo(map, errmsg)) {
00385     val = map.value(key);
00386     return true;
00387   }
00388   return false;
00389 }
00390 
00391 /** Sends a signal to Tor */
00392 bool
00393 TorControl::signal(TorSignal::Signal sig, QString *errmsg)
00394 {
00395   ControlCommand cmd("SIGNAL");
00396   cmd.addArgument(TorSignal::toString(sig));
00397   if (sig == TorSignal::Shutdown || sig == TorSignal::Halt) {
00398     return _controlConn->send(cmd, errmsg);
00399   }
00400   return send(cmd, errmsg); 
00401 }
00402 
00403 /** Reeturns Tor's version as a string. */
00404 QString
00405 TorControl::getTorVersionString()
00406 {
00407   return _torVersion;
00408 }
00409 
00410 /** Returns Tor's version as a numeric value. Note that this discards any
00411  * version status flag, such as "-alpha" or "-rc". */
00412 quint32
00413 TorControl::getTorVersion()
00414 {
00415   quint8 major, minor, micro, patch;
00416   quint32 version = 0;
00417   
00418   QStringList parts = getTorVersionString().split(".");
00419   if (parts.size() >= 4) {
00420     major = (quint8)parts.at(0).toUInt();
00421     minor = (quint8)parts.at(1).toUInt();
00422     micro = (quint8)parts.at(2).toUInt();
00423     patch = (quint8)parts.at(3).toUInt();
00424     version = ((major << 24) | (minor << 16) | (micro << 8) | patch);
00425   }
00426   return version;
00427 }
00428 
00429 /** Sets an event and its handler. If add is true, then the event is added,
00430  * otherwise it is removed. If set is true, then the given event will be
00431  * registered with Tor. */
00432 bool
00433 TorControl::setEvent(TorEvents::TorEvent e, QObject *obj, 
00434                      bool add, bool set, QString *errmsg)
00435 {
00436   if (add) {
00437     _torEvents.add(e, obj);
00438   } else {
00439     _torEvents.remove(e, obj);
00440   }
00441   if (set && isConnected()) {
00442     return setEvents(errmsg);
00443   }
00444   return true;
00445 }
00446 
00447 /** Registers for a set of logging events according to the given filter. If
00448  * the control socket is currently connected, this method will try to register
00449  * the log events with Tor, otherwise it will simply return true. */
00450 bool
00451 TorControl::setLogEvents(uint filter, QObject *obj, QString *errmsg)
00452 {
00453   setEvent(TorEvents::LogError , obj, filter & LogEvent::Error , false);
00454   setEvent(TorEvents::LogWarn  , obj, filter & LogEvent::Warn  , false);
00455   setEvent(TorEvents::LogNotice, obj, filter & LogEvent::Notice, false);
00456   setEvent(TorEvents::LogInfo  , obj, filter & LogEvent::Info  , false);
00457   setEvent(TorEvents::LogDebug , obj, filter & LogEvent::Debug , false);
00458   return (isConnected() ? setEvents(errmsg) : true);
00459 }
00460 
00461 /** Register for the events currently in the event list */
00462 bool
00463 TorControl::setEvents(QString *errmsg)
00464 {
00465   ControlCommand cmd("SETEVENTS"); 
00466 
00467   /* Add each event to the argument list */
00468   foreach (TorEvents::TorEvent e, _torEvents.eventList()) {
00469     cmd.addArgument(TorEvents::toString(e));
00470   }
00471   return send(cmd, errmsg);
00472 }
00473 
00474 /** Sets each configuration key in \emph map to the value associated with its
00475  * key. */
00476 bool
00477 TorControl::setConf(QHash<QString,QString> map, QString *errmsg)
00478 {
00479   ControlCommand cmd("SETCONF");
00480   QString arg, value;
00481   
00482   /* Add each keyvalue to the argument list */
00483   foreach (QString key, map.keys()) {
00484     arg = key;
00485     value = map.value(key);
00486     if (value.length() > 0) {
00487       arg += "=\"" + value + "\"";
00488     }
00489     cmd.addArgument(arg);
00490   }
00491   return send(cmd, errmsg); 
00492 }
00493 
00494 /** Sets a single configuration key to the given value. */
00495 bool
00496 TorControl::setConf(QString key, QString value, QString *errmsg)
00497 {
00498   QHash<QString,QString> map;
00499   map.insert(key, value);
00500   return setConf(map, errmsg);
00501 }
00502 
00503 /** Gets a set of configuration keyvalues and stores them in \emph map. */
00504 bool
00505 TorControl::getConf(QHash<QString,QString> &map, QString *errmsg)
00506 {
00507   ControlCommand cmd("GETCONF");
00508   ControlReply reply;
00509 
00510   /* Add the keys as arguments to the GETINFO message */
00511   foreach (QString key, map.keys()) {
00512     cmd.addArgument(key);
00513   }
00514 
00515   /* Ask Tor for the specified info values */
00516   if (send(cmd, reply, errmsg)) {
00517     /* Parse the response for the returned values */
00518     foreach (ReplyLine line, reply.getLines()) {
00519       /* Split the "key=val" line and map them */
00520       QStringList keyval = line.getMessage().split("=");
00521       if (keyval.size() == 2) {
00522         map.insert(keyval.at(0), keyval.at(1));
00523       }
00524     }
00525     return true;
00526   }
00527   return false;
00528 }
00529 
00530 /** Gets a single configuration keyvalue. */
00531 bool
00532 TorControl::getConf(QString key, QString &value, QString *errmsg)
00533 {
00534   QHash<QString,QString> map;
00535   map.insert(key, "");
00536 
00537   if (getConf(map, errmsg)) {
00538     value = map.value(key);
00539     return true;
00540   }
00541   return false;
00542 }
00543 
00544 /** Asks Tor to save the current configuration to its torrc. */
00545 bool
00546 TorControl::saveConf(QString *errmsg)
00547 {
00548   ControlCommand cmd("SAVECONF");
00549   return send(cmd, errmsg);
00550 }
00551 
00552 /** Tells Tor to reset the given configuration keys back to defaults. */
00553 bool
00554 TorControl::resetConf(QStringList keys, QString *errmsg)
00555 {
00556   ControlCommand cmd("RESETCONF");
00557 
00558   /* Add each key to the argument list */
00559   foreach (QString key, keys) {
00560     cmd.addArgument(key);
00561   }
00562   return send(cmd, errmsg);
00563 }
00564 
00565 /** Tells Tor to reset a single given configuration key back to its default
00566  * value. */
00567 bool
00568 TorControl::resetConf(QString key, QString *errmsg)
00569 {
00570   return resetConf(QStringList() << key, errmsg);
00571 }
00572 
00573 /** Gets the descriptor for the specified router name. */
00574 RouterDescriptor
00575 TorControl::getDescriptorByName(QString name, QString *errmsg)
00576 {
00577   QList<RouterDescriptor> rdlist;
00578   RouterDescriptor rd;
00579   
00580   rdlist = getDescriptorListByName(QStringList() << name, errmsg);
00581   if (!rdlist.isEmpty()) {
00582     rd = (RouterDescriptor)rdlist.takeFirst();
00583     return rd;
00584   }
00585   return RouterDescriptor();
00586 }
00587 
00588 /** Gets the descriptor for the specified ID. */
00589 RouterDescriptor
00590 TorControl::getDescriptorById(QString id, QString *errmsg)
00591 {
00592   QList<RouterDescriptor> rdlist;
00593   RouterDescriptor rd;
00594   
00595   rdlist = getDescriptorListById(QStringList() << id, errmsg);
00596   if (!rdlist.isEmpty()) {
00597     rd = (RouterDescriptor)rdlist.takeFirst();
00598     return rd;
00599   }
00600   return RouterDescriptor();
00601 }
00602 
00603 /** Gets router descriptors for all names in <b>nameList</b>. */
00604 QList<RouterDescriptor>
00605 TorControl::getDescriptorListByName(QStringList nameList, QString *errmsg)
00606 {
00607   ControlCommand cmd("GETINFO");
00608   ControlReply reply;
00609   QList<RouterDescriptor> rdlist;
00610   
00611   /* If there are no IDs in the list, then return now. */
00612   if (nameList.isEmpty()) {
00613     return QList<RouterDescriptor>();
00614   }
00615   
00616   /* Build up the the getinfo arguments from the list of names */
00617   foreach (QString name, nameList) {
00618     cmd.addArgument("desc/name/"+ name);
00619   }
00620   
00621   /* Request the list of router descriptors */
00622   if (send(cmd, reply, errmsg)) {
00623     foreach (ReplyLine line, reply.getLines()) {
00624       /* Check if we got a "250 OK" and descriptor data. */
00625       if (line.getStatus() == "250" && !line.getData().isEmpty()) {
00626         /* Parse the router descriptor data */
00627         rdlist << RouterDescriptor(line.getData());
00628       }
00629     }
00630   }
00631   return rdlist;
00632 }
00633 
00634 /** Gets router descriptors for all IDs in <b>idlist</b>. */
00635 QList<RouterDescriptor>
00636 TorControl::getDescriptorListById(QStringList idlist, QString *errmsg)
00637 {
00638   ControlCommand cmd("GETINFO");
00639   ControlReply reply;
00640   QHash<QString,bool> offline;
00641   QList<RouterDescriptor> rdlist;
00642   
00643   /* If there are no IDs in the list, then return now. */
00644   if (idlist.isEmpty()) {
00645     return QList<RouterDescriptor>();
00646   }
00647   
00648   /* Build up the the getinfo arguments fromthe list of IDs */
00649   foreach (QString id, idlist) {
00650     if (id.startsWith("!")) {
00651       /* The ! means this router is offline. Save a list of the offline
00652        * routers for easy lookup after we get all descriptors from Tor */
00653       id = id.remove("!");
00654       offline.insert(id, true);
00655     }
00656     cmd.addArgument("desc/id/"+ id);
00657   }
00658   
00659   /* Request the list of router descriptors */
00660   if (send(cmd, reply, errmsg)) {
00661     foreach (ReplyLine line, reply.getLines()) {
00662       /* Check if we got a "250 OK" and descriptor data. */
00663       if (line.getStatus() == "250" && !line.getData().isEmpty()) {
00664         /* Parse the router descriptor data */
00665         RouterDescriptor rd(line.getData());
00666         rd.setOffline(!offline.isEmpty() && offline.contains(rd.id()));
00667         rdlist << rd;
00668       }
00669     }
00670   }
00671   return rdlist;
00672 }
00673 
00674 /** Gets a list of RouterDescriptor objects for all routers that Tor currently
00675  * knows about. */
00676 QList<RouterDescriptor>
00677 TorControl::getRouterList(QString *errmsg)
00678 {
00679   /* Get a list of all router IDs Tor currently know about */
00680   QStringList idList = getRouterIDList(errmsg);
00681   /* Get descriptors for each of those routers */
00682   return getDescriptorListById(idList, errmsg);
00683 }
00684 
00685 /** Gets a list of router IDs for all routers Tor knows about. */
00686 QStringList
00687 TorControl::getRouterIDList(QString *errmsg)
00688 {
00689   ControlCommand cmd("GETINFO", "network-status");
00690   ControlReply reply;
00691   QStringList idList;
00692 
00693   if (send(cmd, reply, errmsg)) {
00694     QString routerIDs = reply.getMessage().remove(0,qstrlen("network-status="));
00695 
00696     /* Split the list of router IDs up */
00697     QStringList routers = routerIDs.split(" ");
00698     foreach (QString router, routers) {
00699       /* A router ID may be of the form <name>=$<ID>, $<ID>, or <ID> */
00700       QString id = router.mid(router.indexOf("=")+1);
00701       id = id.replace("$", "");
00702       /* A "!" before <name> or <ID> means "unresponsive" */
00703       if (router.startsWith("!") && !id.startsWith("!")) {
00704         id.prepend("!");
00705       }
00706       /* Add this router ID to the list. */
00707       idList << id;
00708     }
00709   }
00710   return idList;
00711 }
00712 
00713 /** Gets a list of current circuits. */
00714 QList<Circuit>
00715 TorControl::getCircuits(QString *errmsg)
00716 {
00717   ControlCommand cmd("GETINFO", "circuit-status");
00718   ControlReply reply;
00719   QList<Circuit> circuits;
00720   Circuit c;
00721   
00722   if (send(cmd, reply, errmsg)) {
00723     /* Sometimes there is a circuit on the first message line */
00724     QString msg = reply.getMessage();
00725     c = Circuit::fromString(msg.mid(msg.indexOf("=")+1));
00726     if (!c.isEmpty()) {
00727       circuits << c;
00728     }
00729 
00730     /* The rest of the circuits just come as data, one per line */
00731     foreach(QString line, reply.getData()) {
00732       c = Circuit::fromString(line);
00733       if (!c.isEmpty()) {
00734         circuits << Circuit::fromString(line);
00735       }
00736     }
00737   }
00738   return circuits;
00739 }
00740 
00741 /** Gets a list of current streams. */
00742 QList<Stream>
00743 TorControl::getStreams(QString *errmsg)
00744 {
00745   ControlCommand cmd("GETINFO", "stream-status");
00746   ControlReply reply;
00747   QList<Stream> streams;
00748   Stream s;
00749   
00750   if (send(cmd, reply, errmsg)) {
00751     /* Sometimes there is a stream on the first message line */
00752     QString msg = reply.getMessage();
00753     s = Stream::fromString(msg.mid(msg.indexOf("=")+1));
00754     if (!s.isEmpty()) {
00755       streams << s;
00756     }
00757     
00758     /* The rest of the streams jsut come as data, one per line */
00759     foreach (QString line, reply.getData()) {
00760       s = Stream::fromString(line);
00761       if (!s.isEmpty()) {
00762         streams << s;
00763       }
00764     }
00765   }
00766   return streams;
00767 }
00768 

Generated on Mon Oct 23 20:08:16 2006 for Vidalia by  doxygen 1.5.0