mainwindow.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 mainwindow.cpp
00024  * \version $Id: mainwindow.cpp 1238 2006-09-25 17:50:57Z edmanm $
00025  * \brief Main (hidden) window. Creates tray menu and child windows
00026  *
00027  * Implements the main window. The main window is a hidden window that serves
00028  * as the parent of the tray icon and popup menu, as well as other application
00029  * dialogs.
00030  */
00031 
00032 #include <QtGui>
00033 #include <vidalia.h>
00034 #include <util/html.h>
00035 
00036 #include "common/vmessagebox.h"
00037 #include "mainwindow.h"
00038 
00039 #define IMG_APP_ICON       ":/images/16x16/tor-logo.png"
00040 #define IMG_START          ":/images/16x16/tor-on.png"
00041 #define IMG_STOP           ":/images/16x16/tor-off.png"
00042 #define IMG_BWGRAPH        ":/images/16x16/utilities-system-monitor.png"
00043 #define IMG_MESSAGELOG     ":/images/16x16/format-justify-fill.png"
00044 #define IMG_CONFIG         ":/images/16x16/preferences-system.png"
00045 #define IMG_IDENTITY       ":/images/16x16/system-users.png"
00046 #define IMG_HELP           ":/images/16x16/help-browser.png"
00047 #define IMG_ABOUT          ":/images/16x16/tor-logo.png"
00048 #define IMG_EXIT           ":/images/16x16/emblem-unreadable.png"
00049 #define IMG_NETWORK        ":/images/16x16/applications-internet.png"
00050 
00051 #if defined(Q_WS_MAC)
00052 /* On Mac, we go straight to Carbon to load our dock images from .icns files */
00053 #define IMG_TOR_STOPPED    "tor-off"
00054 #define IMG_TOR_RUNNING    "tor-on"
00055 #define IMG_TOR_STARTING   "tor-starting"
00056 #define IMG_TOR_STOPPING   "tor-stopping"
00057 #elif defined(Q_WS_X11)
00058 /* On X11, we just use the .png files */
00059 #define IMG_TOR_STOPPED    ":/images/22x22/tor-off.png"
00060 #define IMG_TOR_RUNNING    ":/images/22x22/tor-on.png"
00061 #define IMG_TOR_STARTING   ":/images/22x22/tor-starting.png"
00062 #define IMG_TOR_STOPPING   ":/images/22x22/tor-stopping.png"
00063 #else
00064 /* On Win32, we load .ico files so our icons look correct on all Windowses */
00065 #include "res/vidalia_win.rc.h"
00066 #define IMG_TOR_STOPPED    QString::number(IDI_TOR_OFF)
00067 #define IMG_TOR_RUNNING    QString::number(IDI_TOR_ON)
00068 #define IMG_TOR_STARTING   QString::number(IDI_TOR_STARTING)
00069 #define IMG_TOR_STOPPING   QString::number(IDI_TOR_STOPPING)
00070 #endif
00071 
00072 
00073 /** Default constructor. It installs an icon in the system tray area and
00074  * creates the popup menu associated with that icon. */
00075 MainWindow::MainWindow()
00076 {
00077   VidaliaSettings settings;
00078   
00079   /* Set Vidalia's application icon */
00080   setWindowIcon(QIcon(IMG_APP_ICON));
00081 
00082   /* Create the actions that will go in the tray menu */
00083   createActions();
00084   
00085 #if defined(Q_WS_MAC)
00086   createMenuBar();
00087 #else
00088   /* Create the tray menu itself */
00089   createTrayMenu(); 
00090 #endif 
00091 
00092   /* Create a new TorControl object, used to communicate with and manipulate Tor */
00093   _torControl = Vidalia::torControl(); 
00094   connect(_torControl, SIGNAL(started()), this, SLOT(started()));
00095   connect(_torControl, SIGNAL(startFailed(QString)),
00096                  this,   SLOT(startFailed(QString)));
00097   connect(_torControl, SIGNAL(stopped(int, QProcess::ExitStatus)),
00098                  this,   SLOT(stopped(int, QProcess::ExitStatus)));
00099   connect(_torControl, SIGNAL(connected()), this, SLOT(connected()));
00100   connect(_torControl, SIGNAL(disconnected()), this, SLOT(disconnected()));
00101   connect(_torControl, SIGNAL(connectFailed(QString)), 
00102                  this,   SLOT(connectFailed(QString)));
00103 
00104   /* Create a new MessageLog object so messages can be logged when not shown */
00105   _messageLog = new MessageLog();
00106   
00107   /* Create a new BandwidthGraph object so we can monitor bandwidth usage */
00108   _bandwidthGraph = new BandwidthGraph(this);
00109 
00110   /* Create a new NetViewer object so we can monitor the network */
00111   _netViewer = new NetViewer();
00112 
00113   /* Put an icon in the system tray to indicate the status of Tor */
00114   _trayIcon = new TrayIcon(IMG_TOR_STOPPED,
00115                            tr("Tor is Stopped"), _trayMenu);
00116   _trayIcon->show();
00117   
00118   if (_torControl->isRunning()) {
00119     /* Tor was already running */
00120     this->started();
00121   } else if (settings.runTorAtStart()) {
00122     /* If we're supposed to start Tor when Vidalia starts, then do it now */
00123     start();
00124   }
00125 }
00126 
00127 /** Destructor. */
00128 MainWindow::~MainWindow()
00129 {
00130   _trayIcon->hide();
00131   delete _trayIcon;
00132   delete _messageLog;
00133   delete _netViewer;
00134 }
00135 
00136 /** Called when the application is closing, by selecting "Exit" from the tray
00137  * menu. This function disconnects the control socket and ends the Tor
00138  * process. */
00139 void
00140 MainWindow::close()
00141 {
00142   /* Disconnect all of the TorControl object's signals */
00143   disconnect(_torControl, 0, 0, 0);
00144 
00145   /* Close the control socket */
00146   if (_torControl->isConnected()) {
00147     _torControl->disconnect();
00148   }
00149 
00150   /* Stop the Tor process */
00151   if (_torControl->isVidaliaRunningTor()) {
00152     _torControl->stop();
00153   }
00154 
00155   /* And then quit for real */
00156   QCoreApplication::quit();
00157 }
00158 
00159 /** Create and bind actions to events. Setup for initial
00160  * tray menu configuration. */
00161 void 
00162 MainWindow::createActions()
00163 {
00164   _startAct = new QAction(QIcon(IMG_START), tr("Start"), this);
00165   connect(_startAct, SIGNAL(triggered()), this, SLOT(start()));
00166   _startAct->setEnabled(true);
00167   
00168   _stopAct = new QAction(QIcon(IMG_STOP), tr("Stop"), this);
00169   connect(_stopAct, SIGNAL(triggered()), this, SLOT(stop()));
00170   _stopAct->setEnabled(false);
00171 
00172   _configAct = new QAction(QIcon(IMG_CONFIG), tr("Settings"), this);
00173   connect(_configAct, SIGNAL(triggered()), this, SLOT(showConfig()));
00174   
00175   _aboutAct = new QAction(QIcon(IMG_ABOUT), tr("About"), this);
00176   connect(_aboutAct, SIGNAL(triggered()), this, SLOT(showAbout()));
00177   
00178   _exitAct = new QAction(QIcon(IMG_EXIT), tr("Exit"), this);
00179   connect(_exitAct, SIGNAL(triggered()), this, SLOT(close()));
00180 
00181   _bandwidthAct = new QAction(QIcon(IMG_BWGRAPH), tr("Bandwidth Graph"), this);
00182   connect(_bandwidthAct, SIGNAL(triggered()), this, SLOT(showBandwidthGraph()));
00183 
00184   _messageAct = new QAction(QIcon(IMG_MESSAGELOG), tr("Message Log"), this);
00185   connect(_messageAct, SIGNAL(triggered()), this, SLOT(showMessageLog()));
00186 
00187   _helpAct = new QAction(QIcon(IMG_HELP), tr("Help"), this);
00188   connect(_helpAct, SIGNAL(triggered()), this, SLOT(showHelp()));
00189 
00190   _networkAct = new QAction(QIcon(IMG_NETWORK), tr("Network Map"), this);
00191   connect(_networkAct, SIGNAL(triggered()), this, SLOT(showNetwork()));
00192 
00193   _newIdentityAct = new QAction(QIcon(IMG_IDENTITY), tr("New Identity"), this);
00194   _newIdentityAct->setEnabled(false);
00195   connect(_newIdentityAct, SIGNAL(triggered()), this, SLOT(newIdentity()));
00196 }
00197 
00198 /**
00199  * Creates a QMenu object that contains QActions
00200  *  which compose the system tray menu.
00201  */
00202 void 
00203 MainWindow::createTrayMenu()
00204 {
00205   /* Tray menu */ 
00206   _trayMenu = new QMenu(this);
00207   _trayMenu->addAction(_startAct);
00208   _trayMenu->addAction(_stopAct);
00209   _trayMenu->addSeparator();
00210   _trayMenu->addAction(_bandwidthAct);
00211   _trayMenu->addAction(_messageAct);
00212   _trayMenu->addAction(_networkAct);
00213   _trayMenu->addAction(_newIdentityAct);
00214   _trayMenu->addSeparator();
00215   _trayMenu->addAction(_configAct);
00216   _trayMenu->addAction(_helpAct);
00217   _trayMenu->addAction(_aboutAct);
00218   _trayMenu->addSeparator();
00219   _trayMenu->addAction(_exitAct);
00220 }
00221 
00222 /** Creates a new menubar with no parent, so Qt will use this as the "default
00223  * menubar" on Mac. This adds on to the existing actions from the createMens()
00224  * method. */
00225 void
00226 MainWindow::createMenuBar()
00227 {
00228 #if defined(Q_WS_MAC)
00229   /* Mac users sure like their shortcuts. Actions NOT mentioned below
00230    * don't explicitly need shortcuts, since they are merged to the default
00231    * menubar and get the default shortcuts anyway. */
00232   _startAct->setShortcut(tr("Ctrl+S"));
00233   _stopAct->setShortcut(tr("Ctrl+T"));
00234   _bandwidthAct->setShortcut(tr("Ctrl+B"));
00235   _messageAct->setShortcut(tr("Ctrl+L"));
00236   _networkAct->setShortcut(tr("Ctrl+N"));
00237   _helpAct->setShortcut(tr("Ctrl+?"));
00238   _newIdentityAct->setShortcut(tr("Ctrl+I"));
00239 
00240   /* Force Qt to put merge the Exit, Configure, and About menubar options into
00241    * the default menu, even if Vidalia is currently not speaking English. */
00242   _exitAct->setText("exit");
00243   _configAct->setText("config");
00244   _aboutAct->setText("about");
00245   
00246   /* The File, Help, and Configure menus will get merged into the application
00247    * menu by Qt. */
00248   QMenuBar *menuBar = new QMenuBar();
00249   QMenu *fileMenu = menuBar->addMenu(tr("File"));
00250   fileMenu->addAction(_exitAct);
00251   
00252   QMenu *torMenu = menuBar->addMenu(tr("Tor"));
00253   torMenu->addAction(_startAct);
00254   torMenu->addAction(_stopAct);
00255   torMenu->addSeparator();
00256   torMenu->addAction(_newIdentityAct);
00257 
00258   QMenu *viewMenu = menuBar->addMenu(tr("View"));
00259   viewMenu->addAction(_bandwidthAct);
00260   viewMenu->addAction(_messageAct);
00261   viewMenu->addAction(_networkAct);
00262   viewMenu->addAction(_configAct);
00263   
00264   QMenu *helpMenu = menuBar->addMenu(tr("Help"));
00265   _helpAct->setText(tr("Vidalia Help"));
00266   helpMenu->addAction(_helpAct);
00267   helpMenu->addAction(_aboutAct);
00268 #endif
00269 }
00270 
00271 /** Attempts to start Tor. If Tor fails to start, then startFailed() will be
00272  * called with an error message containing the reason. */
00273 void 
00274 MainWindow::start()
00275 {
00276   /* This doesn't get set to false until Tor is actually up and running, so we
00277    * don't yell at users twice if their Tor doesn't even start, due to the fact
00278    * that QProcess::stopped() is emitted even if the process didn't even
00279    * start. */
00280   _isIntentionalExit = true;
00281   /* Kick off the Tor process */
00282   _torControl->start();
00283   /* Don't let the user start Tor twice. That would be silly. */
00284   _startAct->setEnabled(false);
00285 }
00286 
00287 /** Called when the Tor process fails to start, for example, because the path
00288  * specified to the Tor executable didn't lead to an executable. */
00289 void
00290 MainWindow::startFailed(QString errmsg)
00291 {
00292   /* We don't display the error message for now, because the error message
00293    * that Qt gives us in this instance is almost always "Unknown Error". That
00294    * will make users sad. */
00295   Q_UNUSED(errmsg);
00296   
00297   /* Display an error message and see if the user wants some help */
00298   int response = VMessageBox::warning(this, tr("Error Starting Tor"),
00299                    tr("Vidalia was unable to start Tor. Check your settings "
00300                         "to ensure the correct name and location of your Tor "
00301                         "executable is specified."),
00302                    VMessageBox::Ok, VMessageBox::ShowSettings, VMessageBox::Help);
00303 
00304   if (response == VMessageBox::ShowSettings) {
00305     /* Show the settings dialog so the user can make sure they're pointing to
00306      * the correct Tor. */
00307      ConfigDialog* configDialog = new ConfigDialog(this);
00308      configDialog->show(ConfigDialog::General);
00309   } else if (response == VMessageBox::Help) {
00310     /* Show troubleshooting information about starting Tor */
00311     Vidalia::help("troubleshooting.start");
00312   }
00313   _startAct->setEnabled(true);
00314 }
00315 
00316 /** Slot: Called when the Tor process is started. It will connect the control
00317  * socket and set the icons and tooltips accordingly. */
00318 void 
00319 MainWindow::started()
00320 {
00321   /* Now that Tor is running, we want to know if it dies when we didn't want
00322    * it to. */
00323   _isIntentionalExit = false;
00324   /* Set correct tray icon and tooltip */
00325   _trayIcon->update(IMG_TOR_STARTING, tr("Tor is starting"));
00326   /* Set menu actions appropriately */
00327   _stopAct->setEnabled(true);
00328   _startAct->setEnabled(false);
00329   /* Try to connect to Tor's control port */
00330   _torControl->connect();
00331 }
00332 
00333 /** Called when the connection to the control socket fails. The reason will be
00334  * given in the errmsg parameter. */
00335 void
00336 MainWindow::connectFailed(QString errmsg)
00337 {
00338   /* Ok, ok. It really isn't going to connect. I give up. */
00339   int response = VMessageBox::warning(this, 
00340                    tr("Error Connecting to Tor"), p(errmsg),
00341                    VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, 
00342                    VMessageBox::Retry, VMessageBox::Help);
00343 
00344 
00345   if (response == VMessageBox::Retry) {
00346     /* Let's give it another try. */
00347     _torControl->connect();
00348   } else {
00349     /* Show the help browser (if requested) */
00350     if (response == VMessageBox::Help) {
00351       Vidalia::help("troubleshooting.connect");
00352     }
00353     /* Since Vidalia can't connect, we can't really do much, so stop Tor. */
00354     _torControl->stop();
00355   }
00356 }
00357 
00358 /** Gives users the option of shutting down a server gracefully, giving
00359  * clients time to find a new circuit. Returns true if the timed server
00360  * shutdown was initiated successfully or false if we want to terminate
00361  * forcefully. */
00362 bool
00363 MainWindow::initiateServerShutdown()
00364 {
00365   QString errmsg;
00366   bool rc = false;
00367   
00368   /* Ask the user if they want to shutdown nicely. */
00369   int response = VMessageBox::question(this, tr("Server is Enabled"),
00370                   tr("You are currently running a Tor server. "
00371                        "Terminating your server will interrupt any "
00372                        "open connections from clients.\n\n"
00373                        "Would you like to shutdown gracefully and "
00374                        "give clients time to find a new server?"),
00375                   VMessageBox::Yes, VMessageBox::No);
00376 
00377   if (response == VMessageBox::Yes) {
00378     /* Send a SHUTDOWN signal to Tor */
00379     if (_torControl->signal(TorSignal::Shutdown, &errmsg)) {
00380       rc = true; /* Shutdown successfully initiated */
00381     } else {
00382       /* Let the user know that we couldn't shutdown gracefully and we'll
00383        * kill Tor forcefully now if they want. */
00384       response = VMessageBox::warning(this, tr("Error Shutting Down"),
00385                    p(tr("Vidalia was unable to shutdown Tor gracefully. (") 
00386                      + errmsg + ")") + 
00387                    p(tr("Do you want to close Tor anyway?")),
00388                    VMessageBox::Yes, VMessageBox::No);
00389 
00390       if (response == VMessageBox::No) {
00391         /* Don't try to terminate Tor anymore. Just leave it running */
00392         _trayIcon->update(IMG_TOR_RUNNING, tr("Tor is running"));
00393         rc = true;
00394       }
00395     }
00396   }
00397   return rc;
00398 }
00399 
00400 /** Disconnects the control socket and stops the Tor process. */
00401 void 
00402 MainWindow::stop()
00403 {
00404   ServerSettings server(_torControl);
00405   QString errmsg;
00406 
00407   /* Indicate that Tor is about to shut down */
00408   _trayIcon->update(IMG_TOR_STOPPING, tr("Tor is stopping"));
00409 
00410   /* If we're running a server, give users the option of terminating
00411    * gracefully so clients have time to find new servers. */
00412   if (server.isServerEnabled()) {
00413     if (initiateServerShutdown()) {
00414       /* Server shutdown was started successfully. */
00415       return;
00416     }
00417   }
00418 
00419   /* Terminate the Tor process immediately */
00420   _isIntentionalExit = true;
00421   if (_torControl->stop(&errmsg)) {
00422     _stopAct->setEnabled(false);
00423   } else {
00424     int response = VMessageBox::warning(this, tr("Error Stopping Tor"),
00425                      p(tr("Vidalia was unable to stop Tor.")) + p(errmsg),
00426                      VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, 
00427                      VMessageBox::Help);
00428       
00429     if (response == VMessageBox::Help) {
00430       /* Show some troubleshooting help */
00431       Vidalia::help("troubleshooting.stop");
00432     }
00433     
00434     /* Tor is still running since stopping failed */
00435     _trayIcon->update(IMG_TOR_RUNNING, tr("Tor is running"));
00436     _isIntentionalExit = false;
00437   }
00438 }
00439 
00440 /** Slot: Called when the Tor process has exited. It will adjust the tray
00441  * icons and tooltips accordingly. */
00442 void 
00443 MainWindow::stopped(int exitCode, QProcess::ExitStatus exitStatus)
00444 {
00445   /* Set correct tray icon and tooltip */
00446   _trayIcon->update(IMG_TOR_STOPPED, tr("Tor is stopped"));
00447 
00448   /* Set menu actions appropriately */
00449   _startAct->setEnabled(true);
00450 
00451   /* If we didn't intentionally close Tor, then check to see if it crashed or
00452    * if it closed itself and returned an error code. */
00453   if (!_isIntentionalExit) {
00454     _stopAct->setEnabled(false);
00455     /* A quick overview of Tor's code tells me that if it catches a SIGTERM or
00456      * SIGINT, Tor will exit(0). We might need to change this warning message
00457      * if this turns out to not be the case. */
00458     if (exitStatus == QProcess::CrashExit || exitCode != 0) {
00459       int ret = VMessageBox::warning(this, tr("Tor Exited"),
00460                   tr("Vidalia detected that Tor exited unexpectedly.\n\n"
00461                      "Please check the message log for indicators "
00462                      "about what happened to Tor before it exited."),
00463                   VMessageBox::Ok, VMessageBox::ShowLog, VMessageBox::Help);
00464       if (ret == VMessageBox::ShowLog) {
00465         showMessageLog();  
00466       } else if (ret == VMessageBox::Help) {
00467         Vidalia::help("troubleshooting.torexited");
00468       }
00469     }
00470   }
00471 }
00472 
00473 /** Called when the control socket has successfully connected to Tor. */
00474 void
00475 MainWindow::connected()
00476 {
00477   ServerSettings serverSettings(_torControl);
00478   QString errmsg;
00479 
00480   /* Update our tray status icon */
00481   _trayIcon->update(IMG_TOR_RUNNING, tr("Tor is running"));
00482   _newIdentityAct->setEnabled(true);
00483 
00484   /* If the user changed some of the server's settings while Tor wasn't 
00485    * running, then we better let Tor know about the changes now. */
00486   if (serverSettings.changedSinceLastApply()) {
00487     if (!serverSettings.apply(&errmsg)) {
00488       int ret = VMessageBox::warning(this, 
00489                   tr("Error Applying Server Settings"),
00490                   p(tr("Vidalia was unable to apply your server's settings."))
00491                     + p(errmsg),
00492                   VMessageBox::Ok, VMessageBox::ShowSettings, VMessageBox::ShowLog);
00493 
00494       if (ret == VMessageBox::ShowSettings) {
00495         /* Show the config dialog with the server page already shown. */
00496         ConfigDialog* configDialog = new ConfigDialog(this);
00497         configDialog->show(ConfigDialog::Server);
00498       } else if (ret == VMessageBox::ShowLog) {
00499         /* Show the message log. */
00500         showMessageLog(); 
00501       }
00502     }
00503   }
00504 }
00505 
00506 /** Called when the control socket has been disconnected. */
00507 void
00508 MainWindow::disconnected()
00509 {
00510   _newIdentityAct->setEnabled(false);
00511 }
00512 
00513 /** Creates an instance of AboutDialog and shows it. If the About dialog is
00514  * already displayed, the existing instance will be brought to the foreground. */
00515 void 
00516 MainWindow::showAbout()
00517 {
00518   static AboutDialog* aboutDialog = new AboutDialog(this);
00519   aboutDialog->show();
00520 }
00521 
00522 /** Shows Message Log. If the message log is already displayed, the existing
00523  * instance will be brought to the foreground. */
00524 void
00525 MainWindow::showMessageLog()
00526 {
00527   _messageLog->show();
00528 }
00529 
00530 /** Shows Bandwidth Graph. If the bandwidth graph is already displayed, the
00531  * existing instance will be brought to the foreground. */
00532 void
00533 MainWindow::showBandwidthGraph()
00534 {
00535   _bandwidthGraph->show();
00536 }
00537 
00538 /** Shows Configuration dialog. If the config dialog is already displayed, the
00539  * existing instance will be brought to the foreground. */
00540 void
00541 MainWindow::showConfig()
00542 {
00543   static ConfigDialog* configDialog = new ConfigDialog(this);
00544   configDialog->show();
00545 }
00546 
00547 /** Shows Help Browser. If the browser is already displayed, the existing
00548  * instance will be brought to the foreground. */
00549 void
00550 MainWindow::showHelp()
00551 {
00552   Vidalia::help(); 
00553 }
00554 
00555 /** Shows the View Network dialog. If the View Network dialog is already
00556  *  displayed, the existing instance will be brought to the foreground. */
00557 void
00558 MainWindow::showNetwork()
00559 {
00560   _netViewer->show();  
00561 }
00562 
00563 /** Called when the user selects the "New Identity" action from the menu. */
00564 void
00565 MainWindow::newIdentity()
00566 {
00567   QString errmsg;
00568   if (_torControl->signal(TorSignal::NewNym, &errmsg)) {
00569     VMessageBox::information(this,
00570       tr("New Identity"),
00571       tr("All subsequent connections will appear to be different "
00572          "than your old connections."),
00573       QMessageBox::Ok);
00574   } else {
00575     VMessageBox::warning(this,
00576       tr("Failed to Create New Identity"), errmsg, VMessageBox::Ok);
00577   }
00578 }
00579 

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