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 */
55 QMap<QString, QString> Vidalia::_args; /**< List of command-line arguments. */
56 QString Vidalia::_style; /**< The current GUI style. */
57 QString Vidalia::_language; /**< The current language. */
58 TorControl* Vidalia::_torControl = 0; /**< Main TorControl object. */
60 QList<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  */
66 void
67 Vidalia::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. */
93 Vidalia::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. */
125  setStyle(_args.value(ARG_GUISTYLE));
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. */
156 int
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. */
166 void
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. */
175 bool
176 Vidalia::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. */
186 bool
188 {
189  return _args.contains(ARG_HELP);
190 }
191 
192 /** Displays usage information for command-line args. */
193 void
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. */
227 bool
228 Vidalia::argNeedsValue(QString argName)
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. */
240 void
241 Vidalia::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. */
271 bool
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. */
317 bool
318 Vidalia::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. */
337 bool
338 Vidalia::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. */
354 QString
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. */
364 QString
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. */
377 QString
379 {
380  if (_args.contains(ARG_PIDFILE)) {
381  return _args.value(ARG_PIDFILE);
382  }
383  return QDir::convertSeparators(dataDirectory() + "/vidalia.pid");
384 }
385 
386 bool
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. */
394 Vidalia::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. */
401 void
402 Vidalia::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. */
411 void
412 Vidalia::createShortcut(const QString &key, QWidget *sender,
413  QObject *receiver, const char *slot)
414 {
415  createShortcut(QKeySequence(key), sender, receiver, slot);
416 }
417 
418 void
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 
429 bool
430 Vidalia::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 
475 err:
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. */
489 void
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 
520 void
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 
static QString settingsFile()
Definition: VSettings.cpp:35
static int run()
Definition: Vidalia.cpp:157
QString trow(QString str)
Definition: html.cpp:46
static void removeAllTranslators()
Definition: Vidalia.cpp:419
static QList< QTranslator * > _translators
Definition: Vidalia.h:160
bool isOpen()
Definition: Log.h:58
#define ARG_LOGLEVEL
Definition: Vidalia.cpp:50
bool err(QString *str, const QString &errmsg)
Definition: stringutil.cpp:37
static QStringList logLevels()
Definition: Log.cpp:42
#define ARG_LANGUAGE
Definition: Vidalia.cpp:43
static QMap< QString, QString > _args
Definition: Vidalia.h:155
static bool retranslateUi(const QString &languageCode)
Definition: Vidalia.cpp:430
static QString defaultDataDirectory()
Definition: Vidalia.cpp:365
static QString _style
Definition: Vidalia.h:156
Vidalia(QStringList args, int &argc, char **argv)
Definition: Vidalia.cpp:93
static QString pidFile()
Definition: Vidalia.cpp:378
#define vInfo(fmt)
Definition: Vidalia.h:40
void onEventLoopStarted()
Definition: Vidalia.cpp:167
#define ARG_PIDFILE
Definition: Vidalia.cpp:48
LogLevel
Definition: Log.h:34
static QStringList languageCodes()
QString tcol(QString str)
Definition: html.cpp:53
void setLogLevel(LogLevel level)
Definition: Log.cpp:52
bool validateArguments(QString &errmsg)
Definition: Vidalia.cpp:272
static QString _language
Definition: Vidalia.h:157
bool argNeedsValue(QString argName)
Definition: Vidalia.cpp:228
static void qt_msg_handler(QtMsgType type, const char *msg)
Definition: Vidalia.cpp:67
#define ARG_DATADIR
Definition: Vidalia.cpp:47
static bool isValidLanguageCode(const QString &languageCode)
static int information(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
#define vDebug(fmt)
Definition: Vidalia.h:39
static bool setStyle(QString styleKey=QString())
Definition: Vidalia.cpp:338
#define vNotice(fmt)
Definition: Vidalia.h:41
#define ARG_HELP
Definition: Vidalia.cpp:46
QString getLanguageCode()
QString i(QString str)
Definition: html.cpp:32
stop errmsg connect(const QHostAddress &address, quint16 port)
void copyDefaultSettingsFile() const
Definition: Vidalia.cpp:490
static QString dataDirectory()
Definition: Vidalia.cpp:355
#define ARG_GUISTYLE
Definition: Vidalia.cpp:44
static bool readPasswordFromStdin()
Definition: Vidalia.cpp:387
bool open(FILE *file)
Definition: Log.cpp:62
#define vWarn(fmt)
Definition: Vidalia.h:42
void loadDefaultCaCertificates() const
Definition: Vidalia.cpp:521
static bool settingsFileExists()
Definition: VSettings.cpp:42
#define ARG_LOGFILE
Definition: Vidalia.cpp:49
QString errorString()
Definition: Log.h:60
#define ARG_RESET
Definition: Vidalia.cpp:45
static Log _log
Definition: Vidalia.h:159
#define ARG_READ_PASSWORD_FROM_STDIN
Definition: Vidalia.cpp:51
#define vApp
Definition: Vidalia.h:37
void running()
void parseArguments(QStringList args)
Definition: Vidalia.cpp:241
QString getInterfaceStyle()
Definition: Log.h:30
static LogLevel stringToLogLevel(QString str)
Definition: Log.cpp:132
#define vError(fmt)
Definition: Vidalia.h:43
Definition: Log.h:40
static Log::LogMessage log(Log::LogLevel level, QString msg)
Definition: Vidalia.cpp:394
~Vidalia()
Definition: Vidalia.cpp:149
static TorControl * _torControl
Definition: Vidalia.h:158
static bool showUsage()
Definition: Vidalia.cpp:187
static bool setLanguage(QString languageCode=QString())
Definition: Vidalia.cpp:318
static void createShortcut(const QKeySequence &key, QWidget *sender, QObject *receiver, const char *slot)
Definition: Vidalia.cpp:402
static void reset()
Definition: VSettings.cpp:88
LogMessage log(LogLevel level, QString message)
Definition: Log.cpp:109
static void showUsageMessageBox()
Definition: Vidalia.cpp:194
QString win32_app_data_folder()
Definition: win32.cpp:86