Vidalia 0.3.1
Vidalia.cpp
Go to the documentation of this file.
1/*
2** This file is part of Vidalia, and is subject to the license terms in the
3** LICENSE file, found in the top level directory of this distribution. If you
4** did not receive the LICENSE file with this file, you may obtain it from the
5** Vidalia source package distributed by the Vidalia Project at
6** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7** including this file, may be copied, modified, propagated, or distributed
8** except according to the terms described in the LICENSE file.
9*/
10
11/*
12** \file Vidalia.cpp
13** \brief Main Vidalia QApplication object
14*/
15
16#include "config.h"
17#include "Vidalia.h"
18#include "LanguageSupport.h"
19#include "VMessageBox.h"
20
21#include "stringutil.h"
22#include "html.h"
23
24#ifdef USE_MARBLE
25#include <MarbleDirs.h>
26#endif
27
28#include <QDir>
29#include <QTimer>
30#include <QTextStream>
31#include <QStyleFactory>
32#include <QShortcut>
33#include <QTranslator>
34#include <QLibraryInfo>
35#include <QSslSocket>
36
37#ifdef Q_OS_MACX
38#include <Carbon/Carbon.h>
39#endif
40#include <stdlib.h>
41
42/* Available command-line arguments. */
43#define ARG_LANGUAGE "lang" /**< Argument specifying language. */
44#define ARG_GUISTYLE "style" /**< Argument specfying GUI style. */
45#define ARG_RESET "reset" /**< Reset Vidalia's saved settings. */
46#define ARG_HELP "help" /**< Display usage informatino. */
47#define ARG_DATADIR "datadir" /**< Directory to use for data files. */
48#define ARG_PIDFILE "pidfile" /**< Location and name of our pidfile.*/
49#define ARG_LOGFILE "logfile" /**< Location of our logfile. */
50#define ARG_LOGLEVEL "loglevel" /**< Log verbosity. */
51#define ARG_READ_PASSWORD_FROM_STDIN \
52 "read-password-from-stdin" /**< Read password from stdin. */
53
54/* Static member variables */
55QMap<QString, QString> Vidalia::_args; /**< List of command-line arguments. */
56QString Vidalia::_style; /**< The current GUI style. */
57QString Vidalia::_language; /**< The current language. */
58TorControl* Vidalia::_torControl = 0; /**< Main TorControl object. */
60QList<QTranslator *> Vidalia::_translators;
61
62
63/** Catches debugging messages from Qt and sends them to Vidalia's logs. If Qt
64 * emits a QtFatalMsg, we will write the message to the log and then abort().
65 */
66void
67Vidalia::qt_msg_handler(QtMsgType type, const char *s)
68{
69 QString msg(s);
70 switch (type) {
71 case QtDebugMsg:
72 vDebug("QtDebugMsg: %1").arg(msg);
73 break;
74 case QtWarningMsg:
75 vNotice("QtWarningMsg: %1").arg(msg);
76 break;
77 case QtCriticalMsg:
78 vWarn("QtCriticalMsg: %1").arg(msg);
79 break;
80 case QtFatalMsg:
81 vError("QtFatalMsg: %1").arg(msg);
82 break;
83 }
84 if (type == QtFatalMsg) {
85 vError("Fatal Qt error. Aborting.");
86 abort();
87 }
88}
89
90/** Constructor. Parses the command-line arguments, resets Vidalia's
91 * configuration (if requested), and sets up the GUI style and language
92 * translation. */
93Vidalia::Vidalia(QStringList args, int &argc, char **argv)
94: QApplication(argc, argv)
95{
96 qInstallMsgHandler(qt_msg_handler);
97
98 /* Read in all our command-line arguments. */
99 parseArguments(args);
100
101 /* Check if we're supposed to reset our config before proceeding. */
102 if (_args.contains(ARG_RESET))
104
105 /* See if we should load a default configuration file. */
108
109 /* Handle the -loglevel and -logfile options. */
110 if (_args.contains(ARG_LOGFILE))
111 _log.open(_args.value(ARG_LOGFILE));
112 if (_args.contains(ARG_LOGLEVEL)) {
114 _args.value(ARG_LOGLEVEL)));
115 if (!_args.contains(ARG_LOGFILE))
116 _log.open(stdout);
117 }
118 if (!_args.contains(ARG_LOGLEVEL) &&
119 !_args.contains(ARG_LOGFILE))
121
122 /* Translate the GUI to the appropriate language. */
124 /* Set the GUI style appropriately. */
126
127 /* Creates a TorControl object, used to talk to Tor. */
128 _torControl = new TorControl(TorSettings().getControlMethod());
129
130 /* If we were built with QSslSocket support, then populate the default
131 * CA certificate store. */
133
134#ifdef USE_MARBLE
135 /* Tell Marble where to stash its generated data */
136 Marble::MarbleDirs::setMarbleDataPath(dataDirectory());
137
138#ifdef Q_OS_WIN32
139 Marble::MarbleDirs::setMarblePluginPath(vApp->applicationDirPath()
140 + "/plugins/marble");
141#endif
142#endif
143#ifdef Q_WS_MAC
144 setStyleSheet("QTreeWidget { font-size: 12pt }");
145#endif
146}
147
148/** Destructor */
150{
151 delete _torControl;
152}
153
154/** Enters the main event loop and waits until exit() is called. The signal
155 * running() will be emitted when the event loop has started. */
156int
158{
159 QTimer::singleShot(0, vApp, SLOT(onEventLoopStarted()));
160 return vApp->exec();
161}
162
163/** Called when the application's main event loop has started. This method
164 * will emit the running() signal to indicate that the application's event
165 * loop is running. */
166void
168{
169 emit running();
170}
171
172#if defined(Q_OS_WIN)
173/** On Windows, we need to catch the WM_QUERYENDSESSION message
174 * so we know that it is time to shutdown. */
175bool
176Vidalia::winEventFilter(MSG *msg, long *result)
177{
178 if (msg->message == WM_QUERYENDSESSION) {
179 quit();
180 }
181 return QApplication::winEventFilter(msg, result);
182}
183#endif
184
185/** Returns true if the user wants to see usage information. */
186bool
188{
189 return _args.contains(ARG_HELP);
190}
191
192/** Displays usage information for command-line args. */
193void
195{
196 QString usage;
197 QTextStream out(&usage);
198
199 out << "Available Options:" << endl;
200 out << "<table>";
201 out << trow(tcol("-"ARG_HELP) +
202 tcol(tr("Displays this usage message and exits.")));
203 out << trow(tcol("-"ARG_RESET) +
204 tcol(tr("Resets ALL stored Vidalia settings.")));
205 out << trow(tcol("-"ARG_DATADIR" &lt;dir&gt;") +
206 tcol(tr("Sets the directory Vidalia uses for data files.")));
207 out << trow(tcol("-"ARG_PIDFILE" &lt;file&gt;") +
208 tcol(tr("Sets the name and location of Vidalia's pidfile.")));
209 out << trow(tcol("-"ARG_LOGFILE" &lt;file&gt;") +
210 tcol(tr("Sets the name and location of Vidalia's logfile.")));
211 out << trow(tcol("-"ARG_LOGLEVEL" &lt;level&gt;") +
212 tcol(tr("Sets the verbosity of Vidalia's logging.") +
213 "<br>[" + Log::logLevels().join("|") +"]"));
214 out << trow(tcol("-"ARG_GUISTYLE" &lt;style&gt;") +
215 tcol(tr("Sets Vidalia's interface style.") +
216 "<br>[" + QStyleFactory::keys().join("|") + "]"));
217 out << trow(tcol("-"ARG_LANGUAGE" &lt;language&gt;") +
218 tcol(tr("Sets Vidalia's language.") +
219 "<br>[" + LanguageSupport::languageCodes().join("|") + "]"));
220 out << "</table>";
221
223 tr("Vidalia Usage Information"), usage, VMessageBox::Ok);
224}
225
226/** Returns true if the specified argument expects a value. */
227bool
229{
230 return (argName == ARG_GUISTYLE ||
231 argName == ARG_LANGUAGE ||
232 argName == ARG_DATADIR ||
233 argName == ARG_PIDFILE ||
234 argName == ARG_LOGFILE ||
235 argName == ARG_LOGLEVEL);
236}
237
238/** Parses the list of command-line arguments for their argument names and
239 * values. */
240void
241Vidalia::parseArguments(QStringList args)
242{
243 QString arg, value;
244
245 /* Loop through all command-line args/values and put them in a map */
246 for (int i = 0; i < args.size(); i++) {
247 /* Get the argument name and set a blank value */
248 arg = args.at(i).toLower();
249 value = "";
250
251 /* Check if it starts with a - or -- */
252 if (arg.startsWith("-")) {
253 arg = arg.mid((arg.startsWith("--") ? 2 : 1));
254 }
255 /* Argument names do not include equal sign. Assume value follows. */
256 if (arg.indexOf("=") > -1) {
257 value = arg.right(arg.length() - (arg.indexOf("=")+1));
258 arg = arg.left(arg.indexOf("="));
259 }
260 else
261 /* Check if it takes a value and there is one on the command-line */
262 if (i < args.size()-1 && argNeedsValue(arg)) {
263 value = args.at(++i);
264 }
265 /* Place this arg/value in the map */
266 _args.insert(arg, value);
267 }
268}
269
270/** Verifies that all specified arguments were valid. */
271bool
273{
274 /* Check for missing parameter values */
275 QMapIterator<QString, QString> _i(_args);
276 QString tmp;
277 while(_i.hasNext()) {
278 _i.next();
279 if(argNeedsValue(_i.key()) && (_i.value() == "")) {
280 errmsg = tr("Value required for parameter :") + _i.key();
281 return false;
282 }
283 }
284 /* Check for a language that Vidalia recognizes. */
285 if (_args.contains(ARG_LANGUAGE) &&
287 errmsg = tr("Invalid language code specified: ") + _args.value(ARG_LANGUAGE);
288 return false;
289 }
290 /* Check for a valid GUI style */
291 if (_args.contains(ARG_GUISTYLE) &&
292 !QStyleFactory::keys().contains(_args.value(ARG_GUISTYLE),
293 Qt::CaseInsensitive)) {
294 errmsg = tr("Invalid GUI style specified: ") + _args.value(ARG_GUISTYLE);
295 return false;
296 }
297 /* Check for a valid log level */
298 if (_args.contains(ARG_LOGLEVEL) &&
299 !Log::logLevels().contains(_args.value(ARG_LOGLEVEL))) {
300 errmsg = tr("Invalid log level specified: ") + _args.value(ARG_LOGLEVEL);
301 return false;
302 }
303 /* Check for a writable log file */
304 if (_args.contains(ARG_LOGFILE) && !_log.isOpen()) {
305 errmsg = tr("Unable to open log file '%1': %2")
306 .arg(_args.value(ARG_LOGFILE))
307 .arg(_log.errorString());
308 return false;
309 }
310 return true;
311}
312
313/** Sets the translation Vidalia will use. If one was specified on the
314 * command-line, we will use that. Otherwise, we'll check to see if one was
315 * saved previously. If not, we'll default to one appropriate for the system
316 * locale. */
317bool
318Vidalia::setLanguage(QString languageCode)
319{
320 /* If the language code is empty, use the previously-saved setting */
321 if (languageCode.isEmpty()) {
322 VidaliaSettings settings;
323 languageCode = settings.getLanguageCode();
324 }
325 /* Translate into the desired langauge */
326 if (retranslateUi(languageCode)) {
327 _language = languageCode;
328 return true;
329 }
330 return false;
331}
332
333/** Sets the GUI style Vidalia will use. If one was specified on the
334 * command-line, we will use that. Otherwise, we'll check to see if one was
335 * saved previously. If not, we'll default to one appropriate for the
336 * operating system. */
337bool
338Vidalia::setStyle(QString styleKey)
339{
340 /* If no style was specified, use the previously-saved setting */
341 if (styleKey.isEmpty()) {
342 VidaliaSettings settings;
343 styleKey = settings.getInterfaceStyle();
344 }
345 /* Apply the specified GUI style */
346 if (QApplication::setStyle(styleKey)) {
347 _style = styleKey;
348 return true;
349 }
350 return false;
351}
352
353/** Returns the directory Vidalia uses for its data files. */
354QString
356{
357 if (_args.contains(ARG_DATADIR)) {
358 return _args.value(ARG_DATADIR);
359 }
360 return defaultDataDirectory();
361}
362
363/** Returns the default location of Vidalia's data directory. */
364QString
366{
367#if defined(Q_OS_WIN32)
368 return (win32_app_data_folder() + "\\Vidalia");
369#elif defined(Q_OS_MAC)
370 return (QDir::homePath() + "/Library/Vidalia");
371#else
372 return (QDir::homePath() + "/.vidalia");
373#endif
374}
375
376/** Returns the location of Vidalia's pid file. */
377QString
379{
380 if (_args.contains(ARG_PIDFILE)) {
381 return _args.value(ARG_PIDFILE);
382 }
383 return QDir::convertSeparators(dataDirectory() + "/vidalia.pid");
384}
385
386bool
388{
389 return _args.contains(ARG_READ_PASSWORD_FROM_STDIN);
390}
391
392/** Writes <b>msg</b> with severity <b>level</b> to Vidalia's log. */
394Vidalia::log(Log::LogLevel level, QString msg)
395{
396 return _log.log(level, msg);
397}
398
399/** Creates and binds a shortcut such that when <b>key</b> is pressed in
400 * <b>sender</b>'s context, <b>receiver</b>'s <b>slot</b> will be called. */
401void
402Vidalia::createShortcut(const QKeySequence &key, QWidget *sender,
403 QObject *receiver, const char *slot)
404{
405 QShortcut *s = new QShortcut(key, sender);
406 connect(s, SIGNAL(activated()), receiver, slot);
407}
408
409/** Creates and binds a shortcut such that when <b>key</b> is pressed in
410 * <b>sender</b>'s context, <b>receiver</b>'s <b>slot</b> will be called. */
411void
412Vidalia::createShortcut(const QString &key, QWidget *sender,
413 QObject *receiver, const char *slot)
414{
415 createShortcut(QKeySequence(key), sender, receiver, slot);
416}
417
418void
420{
421 vInfo("Removing all currently installed UI translator objects.");
422 foreach (QTranslator *translator, _translators) {
423 QApplication::removeTranslator(translator);
424 delete translator;
425 }
426 _translators.clear();
427}
428
429bool
430Vidalia::retranslateUi(const QString &languageCode)
431{
432 QTranslator *systemQtTranslator = 0;
433 QTranslator *vidaliaQtTranslator = 0;
434 QTranslator *vidaliaTranslator = 0;
435
436 if (! LanguageSupport::isValidLanguageCode(languageCode)) {
437 vWarn("Invalid language code: %1").arg(languageCode);
438 return false;
439 }
440 if (! languageCode.compare("en", Qt::CaseInsensitive)) {
441 vNotice("Resetting UI translation to English default.");
442 _language = languageCode;
444 return true;
445 }
446
447 systemQtTranslator = new QTranslator(vApp);
448 Q_CHECK_PTR(systemQtTranslator);
449 QString qtDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
450 systemQtTranslator->load(qtDir + "/qt_" + languageCode + ".qm");
451
452
453 vidaliaQtTranslator = new QTranslator(vApp);
454 Q_CHECK_PTR(vidaliaQtTranslator);
455 vidaliaQtTranslator->load(":/lang/qt_" + languageCode + ".qm");
456
457 vidaliaTranslator = new QTranslator(vApp);
458 Q_CHECK_PTR(vidaliaTranslator);
459 if (! vidaliaTranslator->load(":/lang/vidalia_" + languageCode + ".qm"))
460 goto err;
461
463 vNotice("Changing UI translation from '%1' to '%2'").arg(_language)
464 .arg(languageCode);
465 _language = languageCode;
466 QApplication::installTranslator(systemQtTranslator);
467 QApplication::installTranslator(vidaliaQtTranslator);
468 QApplication::installTranslator(vidaliaTranslator);
469 _translators << systemQtTranslator
470 << vidaliaQtTranslator
471 << vidaliaTranslator;
472
473 return true;
474
475err:
476 vWarn("Unable to set UI translation to '%1'").arg(languageCode);
477 if (systemQtTranslator)
478 delete systemQtTranslator;
479 if (vidaliaQtTranslator)
480 delete vidaliaQtTranslator;
481 if (vidaliaTranslator)
482 delete vidaliaTranslator;
483 delete vidaliaTranslator;
484 return false;
485}
486
487/** Copies a default settings file (if one exists) to Vidalia's data
488 * directory. */
489void
491{
492#ifdef Q_OS_MACX
493 CFURLRef confUrlRef;
494 CFStringRef pathRef;
495 const char *path;
496
497 confUrlRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
498 CFSTR("vidalia"), CFSTR("conf"), NULL);
499 if (confUrlRef == NULL)
500 return;
501
502 pathRef = CFURLCopyFileSystemPath(confUrlRef, kCFURLPOSIXPathStyle);
503 path = CFStringGetCStringPtr(pathRef, CFStringGetSystemEncoding());
504
505 if (path) {
506 QString defaultConfFile = QString::fromLocal8Bit(path);
507 QFileInfo fi(defaultConfFile);
508 if (fi.exists()) {
509 QFileInfo out(VidaliaSettings::settingsFile());
510 if (! out.dir().exists())
511 out.dir().mkpath(".");
512 QFile::copy(defaultConfFile, out.absoluteFilePath());
513 }
514 }
515 CFRelease(confUrlRef);
516 CFRelease(pathRef);
517#endif
518}
519
520void
522{
523 QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
524
525 if (! QSslSocket::addDefaultCaCertificates(":/pki/EquifaxSecureCA.crt"))
526 vWarn("Failed to add the Equifax Secure CA certificate to the default CA "
527 "certificate database.");
528 if (! QSslSocket::addDefaultCaCertificates(":/pki/DigiCertCA.crt"))
529 vWarn("Failed to add the DigiCert Global CA certificate to the default CA "
530 "certificate database.");
531 if (! QSslSocket::addDefaultCaCertificates(":/pki/DigiCertAssuredCA.crt"))
532 vWarn("Failed to add the DigiCert Assured CA certificate to the default CA "
533 "certificate database.");
534 if (! QSslSocket::addDefaultCaCertificates(":/pki/DigiCertHighAssuranceCA.crt"))
535 vWarn("Failed to add the DigiCert High Assurance CA certificate to the default CA "
536 "certificate database.");
537}
538
stop errmsg connect(const QHostAddress &address, quint16 port)
#define ARG_HELP
Definition: Vidalia.cpp:46
#define ARG_LOGLEVEL
Definition: Vidalia.cpp:50
#define ARG_DATADIR
Definition: Vidalia.cpp:47
#define ARG_READ_PASSWORD_FROM_STDIN
Definition: Vidalia.cpp:51
#define ARG_RESET
Definition: Vidalia.cpp:45
#define ARG_GUISTYLE
Definition: Vidalia.cpp:44
#define ARG_LANGUAGE
Definition: Vidalia.cpp:43
#define ARG_PIDFILE
Definition: Vidalia.cpp:48
#define ARG_LOGFILE
Definition: Vidalia.cpp:49
#define vNotice(fmt)
Definition: Vidalia.h:41
#define vError(fmt)
Definition: Vidalia.h:43
#define vWarn(fmt)
Definition: Vidalia.h:42
#define vInfo(fmt)
Definition: Vidalia.h:40
#define vDebug(fmt)
Definition: Vidalia.h:39
#define vApp
Definition: Vidalia.h:37
static bool isValidLanguageCode(const QString &languageCode)
static QStringList languageCodes()
Definition: Log.h:31
QString errorString()
Definition: Log.h:60
static QStringList logLevels()
Definition: Log.cpp:42
static LogLevel stringToLogLevel(QString str)
Definition: Log.cpp:132
bool isOpen()
Definition: Log.h:58
LogLevel
Definition: Log.h:34
@ Off
Definition: Log.h:40
bool open(FILE *file)
Definition: Log.cpp:62
void setLogLevel(LogLevel level)
Definition: Log.cpp:52
LogMessage log(LogLevel level, QString message)
Definition: Log.cpp:109
static int information(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
static QString settingsFile()
Definition: VSettings.cpp:35
static bool settingsFileExists()
Definition: VSettings.cpp:42
static void reset()
Definition: VSettings.cpp:88
void onEventLoopStarted()
Definition: Vidalia.cpp:167
static Log::LogMessage log(Log::LogLevel level, QString msg)
Definition: Vidalia.cpp:394
static bool retranslateUi(const QString &languageCode)
Definition: Vidalia.cpp:430
static QString _style
Definition: Vidalia.h:156
static QString _language
Definition: Vidalia.h:157
static bool setLanguage(QString languageCode=QString())
Definition: Vidalia.cpp:318
static Log _log
Definition: Vidalia.h:159
void copyDefaultSettingsFile() const
Definition: Vidalia.cpp:490
static int run()
Definition: Vidalia.cpp:157
static TorControl * _torControl
Definition: Vidalia.h:158
static void createShortcut(const QKeySequence &key, QWidget *sender, QObject *receiver, const char *slot)
Definition: Vidalia.cpp:402
static QString dataDirectory()
Definition: Vidalia.cpp:355
static void removeAllTranslators()
Definition: Vidalia.cpp:419
static bool readPasswordFromStdin()
Definition: Vidalia.cpp:387
static QString pidFile()
Definition: Vidalia.cpp:378
static bool setStyle(QString styleKey=QString())
Definition: Vidalia.cpp:338
static void showUsageMessageBox()
Definition: Vidalia.cpp:194
void loadDefaultCaCertificates() const
Definition: Vidalia.cpp:521
static QList< QTranslator * > _translators
Definition: Vidalia.h:160
static QString defaultDataDirectory()
Definition: Vidalia.cpp:365
static bool showUsage()
Definition: Vidalia.cpp:187
void running()
Vidalia(QStringList args, int &argc, char **argv)
Definition: Vidalia.cpp:93
static void qt_msg_handler(QtMsgType type, const char *msg)
Definition: Vidalia.cpp:67
static QMap< QString, QString > _args
Definition: Vidalia.h:155
~Vidalia()
Definition: Vidalia.cpp:149
bool argNeedsValue(QString argName)
Definition: Vidalia.cpp:228
bool validateArguments(QString &errmsg)
Definition: Vidalia.cpp:272
void parseArguments(QStringList args)
Definition: Vidalia.cpp:241
QString getInterfaceStyle()
QString getLanguageCode()
QString i(QString str)
Definition: html.cpp:32
QString trow(QString str)
Definition: html.cpp:46
QString tcol(QString str)
Definition: html.cpp:53
bool err(QString *str, const QString &errmsg)
Definition: stringutil.cpp:37
QString win32_app_data_folder()
Definition: win32.cpp:86