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