Vidalia.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file Vidalia.cpp
00013 ** \version $Id: Vidalia.cpp 4299 2010-05-20 22:04:40Z edmanm $
00014 ** \brief Main Vidalia QApplication object
00015 */
00016 
00017 #include "config.h"
00018 #include "Vidalia.h"
00019 #include "LanguageSupport.h"
00020 #include "VMessageBox.h"
00021 
00022 #include "stringutil.h"
00023 #include "html.h"
00024 
00025 #ifdef USE_MARBLE
00026 #include <MarbleDirs.h>
00027 #endif
00028 
00029 #include <QDir>
00030 #include <QTimer>
00031 #include <QTextStream>
00032 #include <QStyleFactory>
00033 #include <QShortcut>
00034 #include <QTranslator>
00035 #include <QLibraryInfo>
00036 #include <QSslSocket>
00037 
00038 #ifdef Q_OS_MACX
00039 #include <Carbon/Carbon.h>
00040 #endif
00041 #include <stdlib.h>
00042 
00043 /* Available command-line arguments. */
00044 #define ARG_LANGUAGE   "lang"     /**< Argument specifying language.    */
00045 #define ARG_GUISTYLE   "style"    /**< Argument specfying GUI style.    */
00046 #define ARG_RESET      "reset"    /**< Reset Vidalia's saved settings.  */
00047 #define ARG_HELP       "help"     /**< Display usage informatino.       */
00048 #define ARG_DATADIR    "datadir"  /**< Directory to use for data files. */
00049 #define ARG_PIDFILE    "pidfile"  /**< Location and name of our pidfile.*/
00050 #define ARG_LOGFILE    "logfile"  /**< Location of our logfile.         */
00051 #define ARG_LOGLEVEL   "loglevel" /**< Log verbosity.                   */
00052 #define ARG_READ_PASSWORD_FROM_STDIN  \
00053   "read-password-from-stdin" /**< Read password from stdin. */
00054 
00055 /* Static member variables */
00056 QMap<QString, QString> Vidalia::_args; /**< List of command-line arguments.  */
00057 QString Vidalia::_style;               /**< The current GUI style.           */
00058 QString Vidalia::_language;            /**< The current language.            */
00059 TorControl* Vidalia::_torControl = 0;  /**< Main TorControl object.          */
00060 Log Vidalia::_log;
00061 QList<QTranslator *> Vidalia::_translators;
00062 
00063 
00064 /** Catches debugging messages from Qt and sends them to Vidalia's logs. If Qt
00065  * emits a QtFatalMsg, we will write the message to the log and then abort().
00066  */
00067 void
00068 Vidalia::qt_msg_handler(QtMsgType type, const char *s)
00069 {
00070   QString msg(s);
00071   switch (type) {
00072     case QtDebugMsg:
00073       vDebug("QtDebugMsg: %1").arg(msg);
00074       break;
00075     case QtWarningMsg:
00076       vNotice("QtWarningMsg: %1").arg(msg);
00077       break;
00078     case QtCriticalMsg:
00079       vWarn("QtCriticalMsg: %1").arg(msg);
00080       break;
00081     case QtFatalMsg:
00082       vError("QtFatalMsg: %1").arg(msg);
00083       break;
00084   }
00085   if (type == QtFatalMsg) {
00086     vError("Fatal Qt error. Aborting.");
00087     abort();
00088   }
00089 }
00090 
00091 /** Constructor. Parses the command-line arguments, resets Vidalia's
00092  * configuration (if requested), and sets up the GUI style and language
00093  * translation. */
00094 Vidalia::Vidalia(QStringList args, int &argc, char **argv)
00095 : QApplication(argc, argv)
00096 {
00097   qInstallMsgHandler(qt_msg_handler);
00098 
00099   /* Read in all our command-line arguments. */
00100   parseArguments(args);
00101 
00102   /* Check if we're supposed to reset our config before proceeding. */
00103   if (_args.contains(ARG_RESET))
00104     VidaliaSettings::reset();
00105 
00106   /* See if we should load a default configuration file. */
00107   if (! VidaliaSettings::settingsFileExists())
00108     copyDefaultSettingsFile();
00109 
00110   /* Handle the -loglevel and -logfile options. */
00111   if (_args.contains(ARG_LOGFILE))
00112     _log.open(_args.value(ARG_LOGFILE));
00113   if (_args.contains(ARG_LOGLEVEL)) {
00114     _log.setLogLevel(Log::stringToLogLevel(
00115                       _args.value(ARG_LOGLEVEL)));
00116     if (!_args.contains(ARG_LOGFILE))
00117       _log.open(stdout);
00118   }
00119   if (!_args.contains(ARG_LOGLEVEL) && 
00120       !_args.contains(ARG_LOGFILE))
00121     _log.setLogLevel(Log::Off);
00122 
00123   /* Translate the GUI to the appropriate language. */
00124   setLanguage(_args.value(ARG_LANGUAGE));
00125   /* Set the GUI style appropriately. */
00126   setStyle(_args.value(ARG_GUISTYLE));
00127 
00128   /* Creates a TorControl object, used to talk to Tor. */
00129   _torControl = new TorControl();
00130 
00131   /* If we were built with QSslSocket support, then populate the default
00132    * CA certificate store. */
00133   loadDefaultCaCertificates();
00134 
00135 #ifdef USE_MARBLE
00136   /* Tell Marble where to stash its generated data */
00137   Marble::MarbleDirs::setMarbleDataPath(dataDirectory());
00138 
00139 #ifdef Q_OS_WIN32
00140   Marble::MarbleDirs::setMarblePluginPath(vApp->applicationDirPath() 
00141                                             + "/plugins/marble");
00142 #endif
00143 #endif
00144 #ifdef Q_WS_MAC
00145   setStyleSheet("QTreeWidget { font-size: 12pt }");
00146 #endif
00147 }
00148 
00149 /** Destructor */
00150 Vidalia::~Vidalia()
00151 {
00152   delete _torControl;
00153 }
00154 
00155 /** Enters the main event loop and waits until exit() is called. The signal
00156  * running() will be emitted when the event loop has started. */
00157 int
00158 Vidalia::run()
00159 {
00160   QTimer::singleShot(0, vApp, SLOT(onEventLoopStarted()));
00161   return vApp->exec();
00162 }
00163 
00164 /** Called when the application's main event loop has started. This method
00165  * will emit the running() signal to indicate that the application's event
00166  * loop is running. */
00167 void
00168 Vidalia::onEventLoopStarted()
00169 {
00170   emit running();
00171 }
00172 
00173 #if defined(Q_OS_WIN)
00174 /** On Windows, we need to catch the WM_QUERYENDSESSION message
00175  * so we know that it is time to shutdown. */
00176 bool
00177 Vidalia::winEventFilter(MSG *msg, long *result)
00178 {
00179   if (msg->message == WM_QUERYENDSESSION) {
00180     quit();
00181   }
00182   return QApplication::winEventFilter(msg, result);
00183 }
00184 #endif
00185 
00186 /** Returns true if the user wants to see usage information. */
00187 bool
00188 Vidalia::showUsage()
00189 {
00190   return _args.contains(ARG_HELP);
00191 }
00192 
00193 /** Displays usage information for command-line args. */
00194 void
00195 Vidalia::showUsageMessageBox()
00196 {
00197   QString usage;
00198   QTextStream out(&usage);
00199 
00200   out << "Available Options:" << endl;
00201   out << "<table>";
00202   out << trow(tcol("-"ARG_HELP) + 
00203               tcol(tr("Displays this usage message and exits.")));
00204   out << trow(tcol("-"ARG_RESET) +
00205               tcol(tr("Resets ALL stored Vidalia settings.")));
00206   out << trow(tcol("-"ARG_DATADIR" &lt;dir&gt;") +
00207               tcol(tr("Sets the directory Vidalia uses for data files.")));
00208   out << trow(tcol("-"ARG_PIDFILE" &lt;file&gt;") +
00209               tcol(tr("Sets the name and location of Vidalia's pidfile.")));
00210   out << trow(tcol("-"ARG_LOGFILE" &lt;file&gt;") +
00211               tcol(tr("Sets the name and location of Vidalia's logfile.")));
00212   out << trow(tcol("-"ARG_LOGLEVEL" &lt;level&gt;") +
00213               tcol(tr("Sets the verbosity of Vidalia's logging.") +
00214                    "<br>[" + Log::logLevels().join("|") +"]"));
00215   out << trow(tcol("-"ARG_GUISTYLE" &lt;style&gt;") +
00216               tcol(tr("Sets Vidalia's interface style.") +
00217                    "<br>[" + QStyleFactory::keys().join("|") + "]"));
00218   out << trow(tcol("-"ARG_LANGUAGE" &lt;language&gt;") + 
00219               tcol(tr("Sets Vidalia's language.") +
00220                    "<br>[" + LanguageSupport::languageCodes().join("|") + "]"));
00221   out << "</table>";
00222 
00223   VMessageBox::information(0, 
00224     tr("Vidalia Usage Information"), usage, VMessageBox::Ok);
00225 }
00226 
00227 /** Returns true if the specified argument expects a value. */
00228 bool
00229 Vidalia::argNeedsValue(QString argName)
00230 {
00231   return (argName == ARG_GUISTYLE ||
00232           argName == ARG_LANGUAGE ||
00233           argName == ARG_DATADIR  ||
00234           argName == ARG_PIDFILE  ||
00235           argName == ARG_LOGFILE  ||
00236           argName == ARG_LOGLEVEL);
00237 }
00238 
00239 /** Parses the list of command-line arguments for their argument names and
00240  * values. */
00241 void
00242 Vidalia::parseArguments(QStringList args)
00243 {
00244   QString arg, value;
00245 
00246   /* Loop through all command-line args/values and put them in a map */
00247   for (int i = 0; i < args.size(); i++) {
00248     /* Get the argument name and set a blank value */
00249     arg   = args.at(i).toLower();
00250     value = "";
00251 
00252     /* Check if it starts with a - or -- */
00253     if (arg.startsWith("-")) {
00254       arg = arg.mid((arg.startsWith("--") ? 2 : 1));
00255     }
00256     /* Check if it takes a value and there is one on the command-line */
00257     if (i < args.size()-1 && argNeedsValue(arg)) {
00258       value = args.at(++i);
00259     }
00260     /* Place this arg/value in the map */
00261     _args.insert(arg, value);
00262   }
00263 }
00264 
00265 /** Verifies that all specified arguments were valid. */
00266 bool
00267 Vidalia::validateArguments(QString &errmsg)
00268 {
00269   /* Check for a language that Vidalia recognizes. */
00270   if (_args.contains(ARG_LANGUAGE) &&
00271       !LanguageSupport::isValidLanguageCode(_args.value(ARG_LANGUAGE))) {
00272     errmsg = tr("Invalid language code specified: ") + _args.value(ARG_LANGUAGE);
00273     return false;
00274   }
00275   /* Check for a valid GUI style */
00276   if (_args.contains(ARG_GUISTYLE) &&
00277       !QStyleFactory::keys().contains(_args.value(ARG_GUISTYLE),
00278                                       Qt::CaseInsensitive)) {
00279     errmsg = tr("Invalid GUI style specified: ") + _args.value(ARG_GUISTYLE);
00280     return false;
00281   }
00282   /* Check for a valid log level */
00283   if (_args.contains(ARG_LOGLEVEL) &&
00284       !Log::logLevels().contains(_args.value(ARG_LOGLEVEL))) {
00285     errmsg = tr("Invalid log level specified: ") + _args.value(ARG_LOGLEVEL);
00286     return false;
00287   }
00288   /* Check for a writable log file */
00289   if (_args.contains(ARG_LOGFILE) && !_log.isOpen()) {
00290     errmsg = tr("Unable to open log file '%1': %2")
00291                            .arg(_args.value(ARG_LOGFILE))
00292                            .arg(_log.errorString());
00293     return false;
00294   }
00295   return true;
00296 }
00297 
00298 /** Sets the translation Vidalia will use. If one was specified on the
00299  * command-line, we will use that. Otherwise, we'll check to see if one was
00300  * saved previously. If not, we'll default to one appropriate for the system
00301  * locale. */
00302 bool
00303 Vidalia::setLanguage(QString languageCode)
00304 {
00305   /* If the language code is empty, use the previously-saved setting */
00306   if (languageCode.isEmpty()) {
00307     VidaliaSettings settings;
00308     languageCode = settings.getLanguageCode();
00309   }
00310   /* Translate into the desired langauge */
00311   if (retranslateUi(languageCode)) {
00312     _language = languageCode;
00313     return true;
00314   }
00315   return false;
00316 }
00317 
00318 /** Sets the GUI style Vidalia will use. If one was specified on the
00319  * command-line, we will use that. Otherwise, we'll check to see if one was
00320  * saved previously. If not, we'll default to one appropriate for the
00321  * operating system. */
00322 bool
00323 Vidalia::setStyle(QString styleKey)
00324 {
00325   /* If no style was specified, use the previously-saved setting */
00326   if (styleKey.isEmpty()) {
00327     VidaliaSettings settings;
00328     styleKey = settings.getInterfaceStyle();
00329   }
00330   /* Apply the specified GUI style */
00331   if (QApplication::setStyle(styleKey)) {
00332     _style = styleKey;
00333     return true;
00334   }
00335   return false;
00336 }
00337 
00338 /** Returns the directory Vidalia uses for its data files. */
00339 QString
00340 Vidalia::dataDirectory()
00341 {
00342   if (_args.contains(ARG_DATADIR)) {
00343     return _args.value(ARG_DATADIR);
00344   }
00345   return defaultDataDirectory();
00346 }
00347 
00348 /** Returns the default location of Vidalia's data directory. */
00349 QString
00350 Vidalia::defaultDataDirectory()
00351 {
00352 #if defined(Q_OS_WIN32)
00353   return (win32_app_data_folder() + "\\Vidalia");
00354 #elif defined(Q_OS_MAC)
00355   return (QDir::homePath() + "/Library/Vidalia");
00356 #else
00357   return (QDir::homePath() + "/.vidalia");
00358 #endif
00359 }
00360 
00361 /** Returns the location of Vidalia's pid file. */
00362 QString
00363 Vidalia::pidFile()
00364 {
00365   if (_args.contains(ARG_PIDFILE)) {
00366     return _args.value(ARG_PIDFILE);
00367   }
00368   return QDir::convertSeparators("/var/run/tor/tor.pid");
00369 }
00370 
00371 bool
00372 Vidalia::readPasswordFromStdin()
00373 {
00374   return _args.contains(ARG_READ_PASSWORD_FROM_STDIN);
00375 }
00376 
00377 /** Writes <b>msg</b> with severity <b>level</b> to Vidalia's log. */
00378 Log::LogMessage
00379 Vidalia::log(Log::LogLevel level, QString msg)
00380 {
00381   return _log.log(level, msg);
00382 }
00383 
00384 /** Creates and binds a shortcut such that when <b>key</b> is pressed in
00385  * <b>sender</b>'s context, <b>receiver</b>'s <b>slot</b> will be called. */
00386 void
00387 Vidalia::createShortcut(const QKeySequence &key, QWidget *sender,
00388                         QObject *receiver, const char *slot)
00389 {
00390   QShortcut *s = new QShortcut(key, sender);
00391   connect(s, SIGNAL(activated()), receiver, slot);
00392 }
00393 
00394 /** Creates and binds a shortcut such that when <b>key</b> is pressed in
00395  * <b>sender</b>'s context, <b>receiver</b>'s <b>slot</b> will be called. */
00396 void
00397 Vidalia::createShortcut(const QString &key, QWidget *sender,
00398                         QObject *receiver, const char *slot)
00399 {
00400   createShortcut(QKeySequence(key), sender, receiver, slot);
00401 }
00402 
00403 void
00404 Vidalia::removeAllTranslators()
00405 {
00406   vInfo("Removing all currently installed UI translator objects.");
00407   foreach (QTranslator *translator, _translators) {
00408     QApplication::removeTranslator(translator);
00409     delete translator;
00410   }
00411   _translators.clear();
00412 }
00413 
00414 bool
00415 Vidalia::retranslateUi(const QString &languageCode)
00416 {
00417   QTranslator *systemQtTranslator = 0;
00418   QTranslator *vidaliaQtTranslator = 0;
00419   QTranslator *vidaliaTranslator = 0;
00420 
00421   if (! LanguageSupport::isValidLanguageCode(languageCode)) {
00422     vWarn("Invalid language code: %1").arg(languageCode);
00423     return false;
00424   }
00425   if (! languageCode.compare("en", Qt::CaseInsensitive)) {
00426     vNotice("Resetting UI translation to English default.");
00427     _language = languageCode;
00428     removeAllTranslators();
00429     return true;
00430   }
00431 
00432   systemQtTranslator = new QTranslator(vApp);
00433   Q_CHECK_PTR(systemQtTranslator);
00434   QString qtDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
00435   systemQtTranslator->load(qtDir + "/qt_" + languageCode + ".qm");
00436 
00437 
00438   vidaliaQtTranslator = new QTranslator(vApp);
00439   Q_CHECK_PTR(vidaliaQtTranslator);
00440   vidaliaQtTranslator->load(":/lang/qt_" + languageCode + ".qm");
00441 
00442   vidaliaTranslator = new QTranslator(vApp);
00443   Q_CHECK_PTR(vidaliaTranslator);
00444   if (! vidaliaTranslator->load(":/lang/vidalia_" + languageCode + ".qm"))
00445     goto err;
00446 
00447   removeAllTranslators();
00448   vNotice("Changing UI translation from '%1' to '%2'").arg(_language)
00449                                                       .arg(languageCode);
00450   _language = languageCode;
00451   QApplication::installTranslator(systemQtTranslator);
00452   QApplication::installTranslator(vidaliaQtTranslator);
00453   QApplication::installTranslator(vidaliaTranslator);
00454   _translators << systemQtTranslator
00455                << vidaliaQtTranslator
00456                << vidaliaTranslator;
00457 
00458   return true;
00459 
00460 err:
00461   vWarn("Unable to set UI translation to '%1'").arg(languageCode);
00462   if (systemQtTranslator)
00463     delete systemQtTranslator;
00464   if (vidaliaQtTranslator)
00465     delete vidaliaQtTranslator;
00466   if (vidaliaTranslator)
00467     delete vidaliaTranslator;
00468   delete vidaliaTranslator;
00469   return false;
00470 }
00471 
00472 /** Copies a default settings file (if one exists) to Vidalia's data
00473  * directory. */
00474 void
00475 Vidalia::copyDefaultSettingsFile() const
00476 {
00477 #ifdef Q_OS_MACX
00478   CFURLRef confUrlRef;
00479   CFStringRef pathRef;
00480   const char *path;
00481 
00482   confUrlRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), 
00483                                        CFSTR("vidalia"), CFSTR("conf"), NULL);
00484   if (confUrlRef == NULL)
00485     return;
00486 
00487   pathRef = CFURLCopyFileSystemPath(confUrlRef, kCFURLPOSIXPathStyle);
00488   path    = CFStringGetCStringPtr(pathRef, CFStringGetSystemEncoding());
00489 
00490   if (path) {
00491     QString defaultConfFile = QString::fromLocal8Bit(path);
00492     QFileInfo fi(defaultConfFile);
00493     if (fi.exists()) {
00494       QFileInfo out(VidaliaSettings::settingsFile());
00495       if (! out.dir().exists())
00496         out.dir().mkpath(".");
00497       QFile::copy(defaultConfFile, out.absoluteFilePath());
00498     }
00499   }
00500   CFRelease(confUrlRef);
00501   CFRelease(pathRef);
00502 #endif
00503 }
00504 
00505 void
00506 Vidalia::loadDefaultCaCertificates() const
00507 {
00508   QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
00509 
00510   if (! QSslSocket::addDefaultCaCertificates(":/pki/cacert_root.crt"))
00511     vWarn("Failed to add the CACert CA certificate to the default CA "
00512           "certificate database.");
00513 
00514   if (! QSslSocket::addDefaultCaCertificates(":/pki/EquifaxSecureCA.crt"))
00515     vWarn("Failed to add the Equifax Secure CA certificate to the default CA "
00516           "certificate database.");
00517 }
00518 
Generated on Mon Aug 30 23:09:51 2010 for Vidalia by  doxygen 1.6.3