Vidalia 0.2.10
|
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 4409 2010-08-26 17:30:56Z 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" <dir>") + 00207 tcol(tr("Sets the directory Vidalia uses for data files."))); 00208 out << trow(tcol("-"ARG_PIDFILE" <file>") + 00209 tcol(tr("Sets the name and location of Vidalia's pidfile."))); 00210 out << trow(tcol("-"ARG_LOGFILE" <file>") + 00211 tcol(tr("Sets the name and location of Vidalia's logfile."))); 00212 out << trow(tcol("-"ARG_LOGLEVEL" <level>") + 00213 tcol(tr("Sets the verbosity of Vidalia's logging.") + 00214 "<br>[" + Log::logLevels().join("|") +"]")); 00215 out << trow(tcol("-"ARG_GUISTYLE" <style>") + 00216 tcol(tr("Sets Vidalia's interface style.") + 00217 "<br>[" + QStyleFactory::keys().join("|") + "]")); 00218 out << trow(tcol("-"ARG_LANGUAGE" <language>") + 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/EquifaxSecureCA.crt")) 00511 vWarn("Failed to add the Equifax Secure CA certificate to the default CA " 00512 "certificate database."); 00513 } 00514