Vidalia 0.2.12
|
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 ** \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 /* Check if it takes a value and there is one on the command-line */ 00256 if (i < args.size()-1 && argNeedsValue(arg)) { 00257 value = args.at(++i); 00258 } 00259 /* Place this arg/value in the map */ 00260 _args.insert(arg, value); 00261 } 00262 } 00263 00264 /** Verifies that all specified arguments were valid. */ 00265 bool 00266 Vidalia::validateArguments(QString &errmsg) 00267 { 00268 /* Check for a language that Vidalia recognizes. */ 00269 if (_args.contains(ARG_LANGUAGE) && 00270 !LanguageSupport::isValidLanguageCode(_args.value(ARG_LANGUAGE))) { 00271 errmsg = tr("Invalid language code specified: ") + _args.value(ARG_LANGUAGE); 00272 return false; 00273 } 00274 /* Check for a valid GUI style */ 00275 if (_args.contains(ARG_GUISTYLE) && 00276 !QStyleFactory::keys().contains(_args.value(ARG_GUISTYLE), 00277 Qt::CaseInsensitive)) { 00278 errmsg = tr("Invalid GUI style specified: ") + _args.value(ARG_GUISTYLE); 00279 return false; 00280 } 00281 /* Check for a valid log level */ 00282 if (_args.contains(ARG_LOGLEVEL) && 00283 !Log::logLevels().contains(_args.value(ARG_LOGLEVEL))) { 00284 errmsg = tr("Invalid log level specified: ") + _args.value(ARG_LOGLEVEL); 00285 return false; 00286 } 00287 /* Check for a writable log file */ 00288 if (_args.contains(ARG_LOGFILE) && !_log.isOpen()) { 00289 errmsg = tr("Unable to open log file '%1': %2") 00290 .arg(_args.value(ARG_LOGFILE)) 00291 .arg(_log.errorString()); 00292 return false; 00293 } 00294 return true; 00295 } 00296 00297 /** Sets the translation Vidalia will use. If one was specified on the 00298 * command-line, we will use that. Otherwise, we'll check to see if one was 00299 * saved previously. If not, we'll default to one appropriate for the system 00300 * locale. */ 00301 bool 00302 Vidalia::setLanguage(QString languageCode) 00303 { 00304 /* If the language code is empty, use the previously-saved setting */ 00305 if (languageCode.isEmpty()) { 00306 VidaliaSettings settings; 00307 languageCode = settings.getLanguageCode(); 00308 } 00309 /* Translate into the desired langauge */ 00310 if (retranslateUi(languageCode)) { 00311 _language = languageCode; 00312 return true; 00313 } 00314 return false; 00315 } 00316 00317 /** Sets the GUI style Vidalia will use. If one was specified on the 00318 * command-line, we will use that. Otherwise, we'll check to see if one was 00319 * saved previously. If not, we'll default to one appropriate for the 00320 * operating system. */ 00321 bool 00322 Vidalia::setStyle(QString styleKey) 00323 { 00324 /* If no style was specified, use the previously-saved setting */ 00325 if (styleKey.isEmpty()) { 00326 VidaliaSettings settings; 00327 styleKey = settings.getInterfaceStyle(); 00328 } 00329 /* Apply the specified GUI style */ 00330 if (QApplication::setStyle(styleKey)) { 00331 _style = styleKey; 00332 return true; 00333 } 00334 return false; 00335 } 00336 00337 /** Returns the directory Vidalia uses for its data files. */ 00338 QString 00339 Vidalia::dataDirectory() 00340 { 00341 if (_args.contains(ARG_DATADIR)) { 00342 return _args.value(ARG_DATADIR); 00343 } 00344 return defaultDataDirectory(); 00345 } 00346 00347 /** Returns the default location of Vidalia's data directory. */ 00348 QString 00349 Vidalia::defaultDataDirectory() 00350 { 00351 #if defined(Q_OS_WIN32) 00352 return (win32_app_data_folder() + "\\Vidalia"); 00353 #elif defined(Q_OS_MAC) 00354 return (QDir::homePath() + "/Library/Vidalia"); 00355 #else 00356 return (QDir::homePath() + "/.vidalia"); 00357 #endif 00358 } 00359 00360 /** Returns the location of Vidalia's pid file. */ 00361 QString 00362 Vidalia::pidFile() 00363 { 00364 if (_args.contains(ARG_PIDFILE)) { 00365 return _args.value(ARG_PIDFILE); 00366 } 00367 return QDir::convertSeparators(dataDirectory() + "/vidalia.pid"); 00368 } 00369 00370 bool 00371 Vidalia::readPasswordFromStdin() 00372 { 00373 return _args.contains(ARG_READ_PASSWORD_FROM_STDIN); 00374 } 00375 00376 /** Writes <b>msg</b> with severity <b>level</b> to Vidalia's log. */ 00377 Log::LogMessage 00378 Vidalia::log(Log::LogLevel level, QString msg) 00379 { 00380 return _log.log(level, msg); 00381 } 00382 00383 /** Creates and binds a shortcut such that when <b>key</b> is pressed in 00384 * <b>sender</b>'s context, <b>receiver</b>'s <b>slot</b> will be called. */ 00385 void 00386 Vidalia::createShortcut(const QKeySequence &key, QWidget *sender, 00387 QObject *receiver, const char *slot) 00388 { 00389 QShortcut *s = new QShortcut(key, sender); 00390 connect(s, SIGNAL(activated()), receiver, slot); 00391 } 00392 00393 /** Creates and binds a shortcut such that when <b>key</b> is pressed in 00394 * <b>sender</b>'s context, <b>receiver</b>'s <b>slot</b> will be called. */ 00395 void 00396 Vidalia::createShortcut(const QString &key, QWidget *sender, 00397 QObject *receiver, const char *slot) 00398 { 00399 createShortcut(QKeySequence(key), sender, receiver, slot); 00400 } 00401 00402 void 00403 Vidalia::removeAllTranslators() 00404 { 00405 vInfo("Removing all currently installed UI translator objects."); 00406 foreach (QTranslator *translator, _translators) { 00407 QApplication::removeTranslator(translator); 00408 delete translator; 00409 } 00410 _translators.clear(); 00411 } 00412 00413 bool 00414 Vidalia::retranslateUi(const QString &languageCode) 00415 { 00416 QTranslator *systemQtTranslator = 0; 00417 QTranslator *vidaliaQtTranslator = 0; 00418 QTranslator *vidaliaTranslator = 0; 00419 00420 if (! LanguageSupport::isValidLanguageCode(languageCode)) { 00421 vWarn("Invalid language code: %1").arg(languageCode); 00422 return false; 00423 } 00424 if (! languageCode.compare("en", Qt::CaseInsensitive)) { 00425 vNotice("Resetting UI translation to English default."); 00426 _language = languageCode; 00427 removeAllTranslators(); 00428 return true; 00429 } 00430 00431 systemQtTranslator = new QTranslator(vApp); 00432 Q_CHECK_PTR(systemQtTranslator); 00433 QString qtDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath); 00434 systemQtTranslator->load(qtDir + "/qt_" + languageCode + ".qm"); 00435 00436 00437 vidaliaQtTranslator = new QTranslator(vApp); 00438 Q_CHECK_PTR(vidaliaQtTranslator); 00439 vidaliaQtTranslator->load(":/lang/qt_" + languageCode + ".qm"); 00440 00441 vidaliaTranslator = new QTranslator(vApp); 00442 Q_CHECK_PTR(vidaliaTranslator); 00443 if (! vidaliaTranslator->load(":/lang/vidalia_" + languageCode + ".qm")) 00444 goto err; 00445 00446 removeAllTranslators(); 00447 vNotice("Changing UI translation from '%1' to '%2'").arg(_language) 00448 .arg(languageCode); 00449 _language = languageCode; 00450 QApplication::installTranslator(systemQtTranslator); 00451 QApplication::installTranslator(vidaliaQtTranslator); 00452 QApplication::installTranslator(vidaliaTranslator); 00453 _translators << systemQtTranslator 00454 << vidaliaQtTranslator 00455 << vidaliaTranslator; 00456 00457 return true; 00458 00459 err: 00460 vWarn("Unable to set UI translation to '%1'").arg(languageCode); 00461 if (systemQtTranslator) 00462 delete systemQtTranslator; 00463 if (vidaliaQtTranslator) 00464 delete vidaliaQtTranslator; 00465 if (vidaliaTranslator) 00466 delete vidaliaTranslator; 00467 delete vidaliaTranslator; 00468 return false; 00469 } 00470 00471 /** Copies a default settings file (if one exists) to Vidalia's data 00472 * directory. */ 00473 void 00474 Vidalia::copyDefaultSettingsFile() const 00475 { 00476 #ifdef Q_OS_MACX 00477 CFURLRef confUrlRef; 00478 CFStringRef pathRef; 00479 const char *path; 00480 00481 confUrlRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), 00482 CFSTR("vidalia"), CFSTR("conf"), NULL); 00483 if (confUrlRef == NULL) 00484 return; 00485 00486 pathRef = CFURLCopyFileSystemPath(confUrlRef, kCFURLPOSIXPathStyle); 00487 path = CFStringGetCStringPtr(pathRef, CFStringGetSystemEncoding()); 00488 00489 if (path) { 00490 QString defaultConfFile = QString::fromLocal8Bit(path); 00491 QFileInfo fi(defaultConfFile); 00492 if (fi.exists()) { 00493 QFileInfo out(VidaliaSettings::settingsFile()); 00494 if (! out.dir().exists()) 00495 out.dir().mkpath("."); 00496 QFile::copy(defaultConfFile, out.absoluteFilePath()); 00497 } 00498 } 00499 CFRelease(confUrlRef); 00500 CFRelease(pathRef); 00501 #endif 00502 } 00503 00504 void 00505 Vidalia::loadDefaultCaCertificates() const 00506 { 00507 QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>()); 00508 00509 if (! QSslSocket::addDefaultCaCertificates(":/pki/EquifaxSecureCA.crt")) 00510 vWarn("Failed to add the Equifax Secure CA certificate to the default CA " 00511 "certificate database."); 00512 } 00513