[tor-commits] [vidalia/alpha] Add the torrc parser and the torrc abstraction class

chiiph at torproject.org chiiph at torproject.org
Mon Mar 5 13:01:51 UTC 2012


commit 15cf1838fcaa1456d9e69f2fa8fc2364133547de
Author: Tomás Touceda <chiiph at torproject.org>
Date:   Fri Feb 3 17:30:10 2012 -0300

    Add the torrc parser and the torrc abstraction class
---
 src/vidalia/CMakeLists.txt               |    3 +
 src/vidalia/config/torrc/Torrc.cpp       |  247 +++++++++++++++++
 src/vidalia/config/torrc/Torrc.h         |   75 ++++++
 src/vidalia/config/torrc/TorrcParser.cpp |  428 ++++++++++++++++++++++++++++++
 src/vidalia/config/torrc/TorrcParser.h   |  189 +++++++++++++
 5 files changed, 942 insertions(+), 0 deletions(-)

diff --git a/src/vidalia/CMakeLists.txt b/src/vidalia/CMakeLists.txt
index 3277bf0..5b4d2a2 100644
--- a/src/vidalia/CMakeLists.txt
+++ b/src/vidalia/CMakeLists.txt
@@ -17,6 +17,7 @@ include_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}/about
   ${CMAKE_CURRENT_SOURCE_DIR}/bwgraph
   ${CMAKE_CURRENT_SOURCE_DIR}/config
+  ${CMAKE_CURRENT_SOURCE_DIR}/config/torrc
   ${CMAKE_CURRENT_SOURCE_DIR}/help/browser
   ${CMAKE_CURRENT_SOURCE_DIR}/log
   ${CMAKE_CURRENT_SOURCE_DIR}/network
@@ -126,6 +127,8 @@ set(vidalia_SRCS ${vidalia_SRCS}
   config/TorSettings.cpp
   config/VidaliaSettings.cpp
   config/VSettings.cpp
+  config/torrc/TorrcParser.cpp
+  config/torrc/Torrc.cpp
 )
 qt4_wrap_cpp(vidalia_SRCS
   config/AbstractTorSettings.h
diff --git a/src/vidalia/config/torrc/Torrc.cpp b/src/vidalia/config/torrc/Torrc.cpp
new file mode 100644
index 0000000..7d2ad6c
--- /dev/null
+++ b/src/vidalia/config/torrc/Torrc.cpp
@@ -0,0 +1,247 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 
+**  including this file, may be copied, modified, propagated, or distributed 
+**  except according to the terms described in the LICENSE file.
+*/
+
+/* 
+** \file Torrc.cpp
+** \brief Handles the interaction with the torrc file
+*/
+
+#include "Torrc.h"
+#include "Vidalia.h"
+
+#include <QtGlobal>
+
+Torrc::Torrc(const QString &torrcPath, const QString &defaultsPath) :
+  _torrcPath(torrcPath), changed(false)
+{
+  load(torrcPath, defaultsPath);
+}
+
+void
+Torrc::load(const QString &torrcPath, const QString &defaultsPath)
+{
+  _lines = _parser.parse(torrcPath, _torrcMap);
+
+  if(not defaultsPath.isEmpty())
+    _parser.parse(defaultsPath, _defaultsMap);
+}
+
+bool
+Torrc::apply(TorControl *tc, QString *errmsg)
+{
+  if(not changed)
+    return true;
+
+  QFile torrc(_torrcPath);
+  QFileInfo torrc_info(torrc);
+
+  if(!torrc.open(QIODevice::WriteOnly)) {
+    *errmsg = "Couldn't open torrc file";
+    return false;
+  }
+  
+  QString torrc_contents = "";
+  QString current_line;
+
+  foreach(TorrcLine *line, _lines) {
+    torrc_contents += QString("%1 %2")
+      .arg(line->content())
+      .arg(line->comment()).trimmed();
+    torrc_contents += "\n";
+  }
+
+  if(torrc_info.isWritable())
+    torrc.write(torrc_contents.trimmed().toLatin1());
+  torrc.close();
+
+  clearAll();
+  load(_torrcPath);
+  
+  changed = false;
+
+  bool ret = true;
+  if(tc && tc->isConnected()) {
+    if(!tc->loadConf(torrc_contents, errmsg)) {
+      vWarn(QObject::tr("There were some settings that Vidalia "
+                        "wasn't able to apply together"));
+      vWarn("Trying to apply them one at a time...");
+      QPair<QString, TorOpt> value;
+      QString ferrmsg;
+      ControlReply reply;
+      bool somefailed = false;
+      foreach(QString key, _torrcMap.keys()) {
+        foreach(value, _torrcMap.values(key)) {
+          if(not tc->setConf(key, value.first, &ferrmsg, &reply)) {
+            if(reply.getStatus() == "553") {
+              vWarn(QObject::tr("Failed to set %1:\nReason: %2")
+                    .arg(key).arg(ferrmsg));
+              somefailed = true;
+            } else {
+              *errmsg = ferrmsg;
+              return false;
+            }
+          }
+        }
+      }
+      if(somefailed) {
+        if(torrc_info.isWritable())
+          vWarn(QObject::tr("The failed options were saved in your torrc "
+                            "and will be applied when you restart."));
+        else
+          vWarn(QObject::tr("The failed options were NOT saved in your torrc "
+                            "and will be applied when you restart."));
+      }
+      if(not torrc_info.isWritable())
+        vWarn(QObject::tr("Vidalia was unable to save the options to the torrc file."));
+    }
+  }
+  return true;
+}
+
+QStringList
+Torrc::value(const QString &key) const
+{
+  QStringList ret;
+  QPair<QString,TorOpt> value;
+  if(_torrcMap.contains(key)) {
+    foreach(value, _torrcMap.values(key))
+      ret << value.first;
+    return ret;
+  }
+
+  if(_defaultsMap.contains(key)) {
+    foreach(value, _defaultsMap.values(key))
+      ret << value.first;
+    return ret;
+  }
+
+  return ret;
+}
+
+void
+Torrc::setValue(const QString &key, const QString &value, const QString &comment)
+{
+  // Prevent possible bugs
+  if(not key.isEmpty() and value.isEmpty())
+    return;
+
+  if(_defaultsMap.contains(key)) {
+    QPair<QString,TorOpt> defvalue;
+    foreach(defvalue, _defaultsMap.values(key)) {
+      if(defvalue.first == value)
+        return;
+    }
+  }
+
+  if(_torrcMap.contains(key)) {
+    QPair<QString,TorOpt> val;
+    foreach(val, _torrcMap.values(key)) {
+      if(not val.second.isMultilined() and val.first != value) {
+        val.second.line()->setContent(QString("%1 %2")
+                                      .arg(key)
+                                      .arg(value));
+        if(not comment.isEmpty() and val.second.line()->comment().isEmpty())
+          val.second.line()->setComment(comment);
+        changed = true;
+        return;
+      } else if(not val.second.isMultilined() and val.first == value) {
+        // if it's the same, leave it like that
+        return;
+      } // else it's multilined, in which case we just add a new entry
+    }
+  }
+
+  changed = true;
+
+  TorOpt opt(_parser.getTorOpt(key));
+  TorrcLine *line = new TorrcLine(QString("%1 %2")
+                                  .arg(key)
+                                  .arg(value).trimmed(),
+                                  comment.trimmed());
+
+  if(key.isEmpty()) {
+    _lines << line;
+    return;
+  }
+
+  if(opt.isMultivalued()) {
+    bool found = false;
+    QPair<QString,TorOpt> val;
+    foreach(val, _torrcMap.values(key)) {
+      if(val.first == value) {
+        found = true;
+        break;
+      }
+    }
+    
+    opt.setLine(line);
+
+    if(!found)
+      _torrcMap.insertMulti(key, QPair<QString,TorOpt>(value, opt));
+    else
+      return;
+  } else
+    _torrcMap.insert(key, QPair<QString,TorOpt>(value, opt));
+
+  _lines << line;
+}
+
+void
+Torrc::clear(QStringList &keys)
+{
+  QPair<QString, TorOpt> value;
+  foreach(const QString key, keys) {
+    foreach(value, _torrcMap.values(key)) {
+      if(value.second.line()) {
+        value.second.line()->setContent("");
+        if(value.second.line()->comment().isEmpty())
+          _lines.removeAll(value.second.line());
+      }
+    }
+    _torrcMap.remove(key);
+  }
+}
+
+bool
+Torrc::isValid(const QString &key, const QString &value)
+{
+  if(not _parser.getTorOpt(key).isNull())
+    return true;
+  // TODO: check value
+  return false;
+}
+
+void
+Torrc::clearAll()
+{
+  _torrcMap.clear();
+  _lines.clear();
+}
+
+bool
+Torrc::setRawContents(const QString &contents, QString *errmsg, TorControl *tc)
+{
+  clearAll();
+
+  QFile torrc(_torrcPath);
+  if(!torrc.open(QIODevice::WriteOnly)) {
+    *errmsg = "Couldn't open torrc file";
+    return false;
+  }
+
+  torrc.write(contents.trimmed().toLatin1());
+  torrc.close();
+
+  load(_torrcPath);
+
+  if(tc && tc->isConnected())
+    return tc->signal(TorSignal::Reload, errmsg);
+  return true;
+}
diff --git a/src/vidalia/config/torrc/Torrc.h b/src/vidalia/config/torrc/Torrc.h
new file mode 100644
index 0000000..6ab7d08
--- /dev/null
+++ b/src/vidalia/config/torrc/Torrc.h
@@ -0,0 +1,75 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 
+**  including this file, may be copied, modified, propagated, or distributed 
+**  except according to the terms described in the LICENSE file.
+*/
+
+/* 
+** \file Torrc.h
+** \brief Handles the interaction with the torrc file
+*/
+
+#ifndef TORRC_H
+#define TORRC_H
+
+#include <QtCore>
+
+#include "TorControl.h"
+#include "TorrcParser.h"
+
+class Torrc {
+ public:
+  /** Constructor */
+  Torrc(const QString &torrcPath, const QString &defaultsPath = "");
+
+  /** Loads the torrc from the path provided and its defaults if provided */
+  void load(const QString &torrcPath, const QString &defaultsPath = "");
+
+  /** Applies the changes in configuration to the torrc file */
+  bool apply(TorControl *tc = 0, QString *errmsg = 0);
+  /** Clears the keys provided in the list */
+  void clear(QStringList &keys);
+  /** Clears the _torrcMap and _lines values */
+  void clearAll();
+
+  /** Returns a list of values associated with the given key */
+  QStringList value(const QString &key) const;
+  /** Sets the value to the given key with a optional
+   *  comment that will appear in the file */
+  void setValue(const QString &key, const QString &value, const QString &comment = "");
+  
+  /** Returns the used torrc path */
+  QString getTorrcPath() const
+  { return _torrcPath; }
+
+  /** Returns true if the key=value is a valid config option */
+  bool isValid(const QString &key, const QString &value);
+
+  /** Resets the config values as if the torrc file contained the
+   *  string in contents, and applies it. */
+  bool setRawContents(const QString &contents, QString *errmsg, TorControl *tc = 0);
+
+ private:
+  /** Map that contains the key=value config options and a
+      representation to the actual line in the torrc file */
+  QMap<QString, QPair<QString,TorOpt> > _torrcMap;
+  /** Map that contains the key=value config options and a
+      representation to the actual line in the defaults torrc file */
+  QMap<QString, QPair<QString,TorOpt> > _defaultsMap;
+  /** List of torrc lines */
+  QList<TorrcLine *> _lines;
+  /** Parser used to analyze the torrc and defaults files */
+  TorrcParser _parser;
+
+  /** Path to the used torrc file */
+  QString _torrcPath;
+
+  /** True if the config options have been changed */
+  bool changed;
+};
+
+#endif
diff --git a/src/vidalia/config/torrc/TorrcParser.cpp b/src/vidalia/config/torrc/TorrcParser.cpp
new file mode 100644
index 0000000..ed21e8e
--- /dev/null
+++ b/src/vidalia/config/torrc/TorrcParser.cpp
@@ -0,0 +1,428 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 
+**  including this file, may be copied, modified, propagated, or distributed 
+**  except according to the terms described in the LICENSE file.
+*/
+
+/*
+** \file TorrcParser.cpp
+** \brief Parser for the torrc file and other data representations for the file
+*/
+
+#include "TorrcParser.h"
+#include "Vidalia.h"
+
+TorrcParser::TorrcParser()
+{
+  _torOpts << TorOpt("AccountingMax", TorOpt::DataSize, "0 bytes");
+  _torOpts << TorOpt("AccountingStart", TorOpt::String, "");
+  _torOpts << TorOpt("Address", TorOpt::String, "");
+  _torOpts << TorOpt("AllowDotExit", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AllowInvalidNodes", TorOpt::CommaList, "middle,rendezvous");
+  _torOpts << TorOpt("AllowNonRFC953Hostnames", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AllowSingleHopCircuits", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AllowSingleHopExits", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AlternateBridgeAuthority", TorOpt::LineList, "");
+  _torOpts << TorOpt("AlternateDirAuthority", TorOpt::LineList, "");
+  _torOpts << TorOpt("AlternateHSAuthority", TorOpt::LineList, "");
+  _torOpts << TorOpt("AssumeReachable", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AuthDirBadDir", TorOpt::LineList, "");
+  _torOpts << TorOpt("AuthDirBadExit", TorOpt::LineList, "");
+  _torOpts << TorOpt("AuthDirInvalid", TorOpt::LineList, "");
+  _torOpts << TorOpt("AuthDirReject", TorOpt::LineList, "");
+  _torOpts << TorOpt("AuthDirRejectUnlisted", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AuthDirListBadDirs", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AuthDirListBadExits", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AuthDirMaxServersPerAddr", TorOpt::Integer, "2");
+  _torOpts << TorOpt("AuthDirMaxServersPerAuthAddr", TorOpt::Integer, "5");
+  _torOpts << TorOpt("AuthoritativeDirectory", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AutomapHostsOnResolve", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("AutomapHostsSuffixes", TorOpt::CommaList, ".onion,.exit");
+  _torOpts << TorOpt("AvoidDiskWrites", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("BandwidthBurst", TorOpt::DataSize, "10 MB");
+  _torOpts << TorOpt("BandwidthRate", TorOpt::DataSize, "5 MB");
+  _torOpts << TorOpt("BridgeAuthoritativeDir", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("Bridge", TorOpt::LineList, "");
+  _torOpts << TorOpt("BridgePassword", TorOpt::String, "");
+  _torOpts << TorOpt("BridgeRecordUsageByCountry", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("BridgeRelay", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("CellStatistics", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("LearnCircuitBuildTimeout", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("CircuitBuildTimeout", TorOpt::TimeInterval, "0");
+  _torOpts << TorOpt("CircuitIdleTimeout", TorOpt::TimeInterval, "1 hour");
+  _torOpts << TorOpt("CircuitStreamTimeout", TorOpt::TimeInterval, "0");
+  _torOpts << TorOpt("CircuitPriorityHalflife", TorOpt::Float, "-100.0");
+  _torOpts << TorOpt("ClientDNSRejectInternalAddresses", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ClientOnly", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ClientRejectInternalAddresses", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ClientTransportPlugin", TorOpt::LineList, "");
+  _torOpts << TorOpt("ConsensusParams", TorOpt::String, "");
+  _torOpts << TorOpt("ConnLimit", TorOpt::Integer, "1000");
+  _torOpts << TorOpt("ConnDirectionStatistics", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ConstrainedSockets", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ConstrainedSockSize", TorOpt::DataSize, "8192");
+  _torOpts << TorOpt("ContactInfo", TorOpt::String, "");
+  _torOpts << TorOpt("ControlListenAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("ControlPort", TorOpt::Port, "0");
+  _torOpts << TorOpt("ControlPortFileGroupReadable", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ControlPortWriteToFile", TorOpt::Filename, "");
+  _torOpts << TorOpt("ControlSocket", TorOpt::LineList, "");
+  _torOpts << TorOpt("ControlSocketsGroupWritable", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("CookieAuthentication", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("CookieAuthFileGroupReadable", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("CookieAuthFile", TorOpt::String, "");
+  _torOpts << TorOpt("CountPrivateBandwidth", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("DataDirectory", TorOpt::Filename, "");
+  _torOpts << TorOpt("DirAllowPrivateAddresses", TorOpt::Boolean, "");
+  _torOpts << TorOpt("TestingAuthDirTimeToLearnReachability", TorOpt::TimeInterval, "30 minutes");
+  _torOpts << TorOpt("DirListenAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("DirPolicy", TorOpt::LineList, "");
+  _torOpts << TorOpt("DirPort", TorOpt::Port, "0");
+  _torOpts << TorOpt("DirPortFrontPage", TorOpt::Filename, "");
+  _torOpts << TorOpt("DirReqStatistics", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("DirServer", TorOpt::LineList, "");
+  _torOpts << TorOpt("DisableAllSwap", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("DisableIOCP", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("DNSPort", TorOpt::LineList, "");
+  _torOpts << TorOpt("DNSListenAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("DownloadExtraInfo", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("EnforceDistinctSubnets", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("EntryNodes", TorOpt::RouterList, "");
+  _torOpts << TorOpt("EntryStatistics", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("TestingEstimatedDescriptorPropagationTime", TorOpt::TimeInterval, "10 minutes");
+  _torOpts << TorOpt("ExcludeNodes", TorOpt::RouterList, "");
+  _torOpts << TorOpt("ExcludeExitNodes", TorOpt::RouterList, "");
+  _torOpts << TorOpt("ExcludeSingleHopRelays", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ExitNodes", TorOpt::RouterList, "");
+  _torOpts << TorOpt("ExitPolicy", TorOpt::LineList, "");
+  _torOpts << TorOpt("ExitPolicyRejectPrivate", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ExitPortStatistics", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ExtraInfoStatistics", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("FallbackNetworkstatusFile", TorOpt::Filename, "/usr/local/share/tor/fallback-consensus");
+  _torOpts << TorOpt("FascistFirewall", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("FirewallPorts", TorOpt::CommaList, "");
+  _torOpts << TorOpt("FastFirstHopPK", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("FetchDirInfoEarly", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("FetchDirInfoExtraEarly", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("FetchServerDescriptors", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("FetchHidServDescriptors", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("FetchUselessDescriptors", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("FetchV2Networkstatus", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("GeoIPFile", TorOpt::Filename, "/usr/local/share/tor/geoip");
+  _torOpts << TorOpt("GiveGuardFlagTo_CVE_2011_2768_VulnerableRelays", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("HardwareAccel", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("HeartbeatPeriod", TorOpt::TimeInterval, "6 hours");
+  _torOpts << TorOpt("AccelName", TorOpt::String, "");
+  _torOpts << TorOpt("AccelDir", TorOpt::Filename, "");
+  // HashedControlPassword will only use one value, the salt prevents
+  // treat it like the rest of the LineLists
+  _torOpts << TorOpt("HashedControlPassword", TorOpt::String, "");
+  _torOpts << TorOpt("HidServDirectoryV2", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("HiddenServiceDir", TorOpt::Dependant, "");
+  _torOpts << TorOpt("HiddenServiceOptions", TorOpt::Virtual, "");
+  _torOpts << TorOpt("HiddenServicePort", TorOpt::Dependant, "");
+  _torOpts << TorOpt("HiddenServiceVersion", TorOpt::Dependant, "");
+  _torOpts << TorOpt("HiddenServiceAuthorizeClient", TorOpt::Dependant, "");
+  _torOpts << TorOpt("HidServAuth", TorOpt::LineList, "");
+  _torOpts << TorOpt("HSAuthoritativeDir", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("HTTPProxy", TorOpt::String, "");
+  _torOpts << TorOpt("HTTPProxyAuthenticator", TorOpt::String, "");
+  _torOpts << TorOpt("HTTPSProxy", TorOpt::String, "");
+  _torOpts << TorOpt("HTTPSProxyAuthenticator", TorOpt::String, "");
+  _torOpts << TorOpt("ServerTransportPlugin", TorOpt::LineList, "");
+  _torOpts << TorOpt("Socks4Proxy", TorOpt::String, "");
+  _torOpts << TorOpt("Socks5Proxy", TorOpt::String, "");
+  _torOpts << TorOpt("Socks5ProxyUsername", TorOpt::String, "");
+  _torOpts << TorOpt("Socks5ProxyPassword", TorOpt::String, "");
+  _torOpts << TorOpt("KeepalivePeriod", TorOpt::TimeInterval, "5 minutes");
+  _torOpts << TorOpt("Log", TorOpt::LineList, "");
+  _torOpts << TorOpt("LogMessageDomains", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("LogTimeGranularity", TorOpt::TimeMsecInterval, "1 second");
+  _torOpts << TorOpt("LongLivedPorts", TorOpt::CommaList, "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300");
+  _torOpts << TorOpt("MapAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("MaxAdvertisedBandwidth", TorOpt::DataSize, "1 GB");
+  _torOpts << TorOpt("MaxCircuitDirtiness", TorOpt::TimeInterval, "10 minutes");
+  _torOpts << TorOpt("MaxClientCircuitsPending", TorOpt::Integer, "32");
+  _torOpts << TorOpt("MaxOnionsPending", TorOpt::Integer, "100");
+  _torOpts << TorOpt("MyFamily", TorOpt::String, "");
+  _torOpts << TorOpt("NewCircuitPeriod", TorOpt::TimeInterval, "30 seconds");
+  _torOpts << TorOpt("NamingAuthoritativeDirectory", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("NATDListenAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("NATDPort", TorOpt::LineList, "");
+  _torOpts << TorOpt("Nickname", TorOpt::String, "");
+  _torOpts << TorOpt("WarnUnsafeSocks", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("NodeFamily", TorOpt::LineList, "");
+  _torOpts << TorOpt("NumCPUs", TorOpt::Integer, "0");
+  _torOpts << TorOpt("NumEntryGuards", TorOpt::Integer, "3");
+  _torOpts << TorOpt("ORListenAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("ORPort", TorOpt::Port, "0");
+  _torOpts << TorOpt("OutboundBindAddress", TorOpt::String, "");
+  _torOpts << TorOpt("PerConnBWBurst", TorOpt::DataSize, "0");
+  _torOpts << TorOpt("PerConnBWRate", TorOpt::DataSize, "0");
+  _torOpts << TorOpt("PidFile", TorOpt::String, "");
+  _torOpts << TorOpt("TestingTorNetwork", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("OptimisticData", TorOpt::BooleanAuto, "auto");
+  _torOpts << TorOpt("PortForwarding", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("PortForwardingHelper", TorOpt::Filename, "tor-fw-helper");
+  _torOpts << TorOpt("PreferTunneledDirConns", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ProtocolWarnings", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("PublishServerDescriptor", TorOpt::CommaList, "1");
+  _torOpts << TorOpt("PublishHidServDescriptors", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ReachableAddresses", TorOpt::LineList, "");
+  _torOpts << TorOpt("ReachableDirAddresses", TorOpt::LineList, "");
+  _torOpts << TorOpt("ReachableORAddresses", TorOpt::LineList, "");
+  _torOpts << TorOpt("RecommendedVersions", TorOpt::LineList, "");
+  _torOpts << TorOpt("RecommendedClientVersions", TorOpt::LineList, "");
+  _torOpts << TorOpt("RecommendedServerVersions", TorOpt::LineList, "");
+  _torOpts << TorOpt("RefuseUnknownExits", TorOpt::BooleanAuto, "auto");
+  _torOpts << TorOpt("RejectPlaintextPorts", TorOpt::CommaList, "");
+  _torOpts << TorOpt("RelayBandwidthBurst", TorOpt::DataSize, "0");
+  _torOpts << TorOpt("RelayBandwidthRate", TorOpt::DataSize, "0");
+  _torOpts << TorOpt("RendPostPeriod", TorOpt::TimeInterval, "1 hour");
+  _torOpts << TorOpt("RephistTrackTime", TorOpt::TimeInterval, "24 hours");
+  _torOpts << TorOpt("RunAsDaemon", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("SafeLogging", TorOpt::String, "1");
+  _torOpts << TorOpt("SafeSocks", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ServerDNSAllowBrokenConfig", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ServerDNSAllowNonRFC953Hostnames", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ServerDNSDetectHijacking", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ServerDNSRandomizeCase", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("ServerDNSResolvConfFile", TorOpt::String, "");
+  _torOpts << TorOpt("ServerDNSSearchDomains", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("ServerDNSTestAddresses", TorOpt::CommaList, "www.google.com,www.mit.edu,www.yahoo.com,www.slashdot.org");
+  _torOpts << TorOpt("ShutdownWaitLength", TorOpt::TimeInterval, "30 seconds");
+  _torOpts << TorOpt("SocksListenAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("SocksPolicy", TorOpt::LineList, "");
+  _torOpts << TorOpt("SocksPort", TorOpt::LineList, "");
+  _torOpts << TorOpt("SocksTimeout", TorOpt::TimeInterval, "2 minutes");
+  _torOpts << TorOpt("StrictNodes", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("TestSocks", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("TokenBucketRefillInterval", TorOpt::TimeMsecInterval, "100 msec");
+  _torOpts << TorOpt("TrackHostExits", TorOpt::CommaList, "");
+  _torOpts << TorOpt("TrackHostExitsExpire", TorOpt::TimeInterval, "30 minutes");
+  _torOpts << TorOpt("TransListenAddress", TorOpt::LineList, "");
+  _torOpts << TorOpt("TransPort", TorOpt::LineList, "");
+  _torOpts << TorOpt("TunnelDirConns", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("UpdateBridgesFromAuthority", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("UseBridges", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("UseEntryGuards", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("UseMicrodescriptors", TorOpt::BooleanAuto, "auto");
+  _torOpts << TorOpt("User", TorOpt::String, "");
+  _torOpts << TorOpt("V1AuthoritativeDirectory", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("V2AuthoritativeDirectory", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("V3AuthoritativeDirectory", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("TestingV3AuthInitialVotingInterval", TorOpt::TimeInterval, "30 minutes");
+  _torOpts << TorOpt("TestingV3AuthInitialVoteDelay", TorOpt::TimeInterval, "5 minutes");
+  _torOpts << TorOpt("TestingV3AuthInitialDistDelay", TorOpt::TimeInterval, "5 minutes");
+  _torOpts << TorOpt("V3AuthVotingInterval", TorOpt::TimeInterval, "1 hour");
+  _torOpts << TorOpt("V3AuthVoteDelay", TorOpt::TimeInterval, "5 minutes");
+  _torOpts << TorOpt("V3AuthDistDelay", TorOpt::TimeInterval, "5 minutes");
+  _torOpts << TorOpt("V3AuthNIntervalsValid", TorOpt::Integer, "3");
+  _torOpts << TorOpt("V3AuthUseLegacyKey", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("V3BandwidthsFile", TorOpt::Filename, "");
+  _torOpts << TorOpt("VersioningAuthoritativeDirectory", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("VirtualAddrNetwork", TorOpt::String, "127.192.0.0/10");
+  _torOpts << TorOpt("WarnPlaintextPorts", TorOpt::CommaList, "23,109,110,143");
+  _torOpts << TorOpt("_UseFilteringSSLBufferevents", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("__ReloadTorrcOnSIGHUP", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("__AllDirActionsPrivate", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("__DisablePredictedCircuits", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("__LeaveStreamsUnattached", TorOpt::Boolean, "0");
+  _torOpts << TorOpt("__HashedControlSessionPassword", TorOpt::LineList, "");
+  _torOpts << TorOpt("__OwningControllerProcess", TorOpt::String, "");
+  _torOpts << TorOpt("MinUptimeHidServDirectoryV2", TorOpt::TimeInterval, "25 hours");
+  _torOpts << TorOpt("VoteOnHidServDirectoriesV2", TorOpt::Boolean, "1");
+  _torOpts << TorOpt("_UsingTestNetworkDefaults", TorOpt::Boolean, "0");
+ 
+  _regexp = createRegExp();
+}
+
+QRegExp
+TorrcParser::createRegExp()
+{
+  QString reg = "";
+  foreach(TorOpt opt, _torOpts) {
+    reg += opt.name();
+    reg += "|";
+  }
+  reg = reg.remove(reg.size() - 1, 1);
+
+  return QRegExp(reg, Qt::CaseInsensitive);
+}
+
+TorOpt
+TorrcParser::getTorOpt(const QString &name)
+{
+  foreach(const TorOpt &opt, _torOpts) {
+    if(opt.name() == name)
+      return opt;
+  }
+
+  return TorOpt::null();
+}
+
+QList<TorrcLine *>
+TorrcParser::parse(const QString &path,
+             QMap<QString, QPair<QString, TorOpt> > &map)
+{
+  QFile torrc(path);
+  QFileInfo torrc_info;
+  QList<TorrcLine *> lines;
+  torrc_info.setFile(torrc);
+
+  if(not torrc_info.isReadable()) {
+    vWarn("Can't read torrc file, aborting parsing.");
+    return lines;
+  }
+  
+  if(not torrc.open(QIODevice::ReadOnly)) {
+    vWarn("Can't open torrc file, aborting parsing.");
+    return lines;
+  }
+
+  QString raw_contents(torrc.readAll());
+  torrc.close();
+
+  lines = toTorrcLines(raw_contents);
+  
+  QString key, value;
+  int sp_pos = -1;
+
+  foreach(TorrcLine *line, lines) {
+    sp_pos = line->content().indexOf(" ");
+    if(sp_pos == -1)
+      continue;
+
+    key = line->content().mid(0, sp_pos);
+    value = "";
+    if(line->isMultilined()) {
+      QList<TorrcLine *> valueLines = findValueLines(line, lines);
+      foreach(TorrcLine *valueLine, valueLines) {
+        if(valueLine->isMultilined()) {
+          value += valueLine->content().remove(valueLine->content().lastIndexOf("\\"), 1);
+        } else
+          // Last line, doesn't have \ at the end
+          value += valueLine->content();
+        // remove the contents, since they will live in the first line only
+        valueLine->setContent("");
+      }
+      line->setContent(QString("%1 %2").arg(key).arg(value));
+    } else {
+      value = line->content().mid(sp_pos+1, line->content().size());
+    }
+
+    TorOpt opt(getTorOpt(key));
+    opt.setLine(line);
+    if(map.contains(key) and opt.isMultivalued())
+      map.insertMulti(key, QPair<QString, TorOpt>(value, opt));
+    else
+      map.insert(key, QPair<QString, TorOpt>(value, opt));
+  }
+  
+  return lines;
+}
+
+QString
+TorrcParser::stripContents(QString contents)
+{
+  QString stripped_contents = "", current_line = "";
+  int comment_pos = -1;
+
+  foreach(QString line, contents.split("\n")) {
+    comment_pos = line.indexOf("#");
+    if(comment_pos == -1) {
+      stripped_contents += line.trimmed();
+      stripped_contents += "\n";
+      continue;
+    }
+    
+    current_line = line.mid(0, comment_pos).trimmed();
+    if(current_line.size() == 0)
+      continue;
+    stripped_contents += current_line;
+    stripped_contents += "\n";
+  }
+
+  return stripped_contents.replace("\t", " ");
+}
+
+QList<TorrcLine *>
+TorrcParser::toTorrcLines(QString contents)
+{
+  QList<TorrcLine *> lines;
+  QString stripped_contents = "", current_line = "";
+  int comment_pos = -1;
+
+  foreach(QString line, contents.split("\n")) {
+    comment_pos = line.indexOf("#");
+    if(comment_pos == -1) {
+      lines << new TorrcLine(line.trimmed());
+      continue;
+    }
+    
+    current_line = line.mid(0, comment_pos).trimmed().replace("\t", " ");
+    if(current_line.size() == 0) {
+      // The line is just a comment
+      lines << new TorrcLine("", line.trimmed());
+      continue;
+    }
+    lines << new TorrcLine(current_line, line.mid(comment_pos, line.size()).trimmed());
+  }
+
+  return lines;
+}
+  
+QList<TorrcLine *>
+TorrcParser::findValueLines(TorrcLine *line, 
+                            const QList<TorrcLine *> &lines) const
+{
+  QList<TorrcLine *> collected;
+
+  int index = -1;
+  index = lines.indexOf(line) + 1;
+  if(index >= lines.size())
+    return collected;
+
+  for(int i = index; i<lines.size(); i++) {
+    if(lines.at(i)->isMultilined())
+      collected << lines.at(i);
+    else {
+      collected << lines.at(i);
+      break;
+    }
+  }
+
+  return collected;
+}
+
+bool
+TorrcParser::isKeyword(const QString &contents, int pos)
+{
+  while(contents.at(pos) != '\n') {
+    pos--;
+    if(pos < 0 or contents.at(pos) == '\n')
+      return true;
+    if(contents.at(pos) != ' ')
+      return false;
+  }
+  return true;
+}
+
+QDebug
+operator<<(QDebug dbg, const TorOpt &opt) {
+  dbg.nospace() << qPrintable(QString("TorOpt(%1)")
+                              .arg(opt.name()));
+  return dbg;
+}
+
+QDebug
+operator<<(QDebug dbg, TorrcLine *line) {
+  dbg.nospace() << qPrintable(QString("TorLine(%1, %2)")
+                              .arg(line->content())
+                              .arg(line->comment()));
+  return dbg;
+}
diff --git a/src/vidalia/config/torrc/TorrcParser.h b/src/vidalia/config/torrc/TorrcParser.h
new file mode 100644
index 0000000..83572bb
--- /dev/null
+++ b/src/vidalia/config/torrc/TorrcParser.h
@@ -0,0 +1,189 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 
+**  including this file, may be copied, modified, propagated, or distributed 
+**  except according to the terms described in the LICENSE file.
+*/
+
+/*
+** \file TorrcParser.h
+** \brief Parser for the torrc file and other data representations for the file
+*/
+
+#ifndef TORRCPARSER_H
+#define TORRCPARSER_H
+
+#include <QtCore>
+
+/** Rpresents a line in the torrc file that respects the formatting
+ *  the user gave it */
+class TorrcLine
+{
+ public:
+  /** Constructor */
+  TorrcLine(const QString &content = "", const QString &comment = "")
+    : _content(content), _comment(comment) {}
+
+  /** Returns the content for this line */
+  QString content() 
+  { return _content; }
+  /** Sets the contents for this line */
+  void setContent(const QString &content)
+  { _content = content; }
+
+  /** Returns the comment for this line */
+  QString comment()
+  { return _comment; }
+  /** Sets the comment for this line */
+  void setComment(const QString &comment)
+  { _comment = comment; }
+
+  /** Returns true if the line's content is divided into other
+   *  subsequent lines */
+  bool isMultilined()
+  {
+    int index = -1;
+    index = _content.lastIndexOf("\\") + 1;
+    if(index == -1)
+      return false;
+    for(int i = index; i<_content.size(); i++) {
+      if(_content.at(i) != ' ')
+        return false;
+    }
+    return true;
+  }
+
+ private:
+  /** Strings that hold the content and comment for the line */
+  QString _content, _comment;
+};
+
+/** Represents an possible option in the torrc file. This is used to
+ *  handle the different type of values that may be associated with
+ *  each option */
+class TorOpt
+{
+ public:
+  /** Possible value types */
+  enum OptType {
+    TimeInterval = 0,
+    TimeMsecInterval,
+    String,
+    RouterList,
+    LineList,
+    Float,
+    Dependant,
+    Virtual,
+    Filename,
+    CommaList,
+    Boolean,
+    BooleanAuto,
+    Integer,
+    DataSize,
+    Port,
+    NIL
+  };
+
+  /** Default constructor */
+  TorOpt()
+    { TorOpt("", NIL); }
+  /** Constructor */
+ TorOpt(const QString &name, OptType type, QString defaultValue = "")
+   : _name(name), _type(type), _default(defaultValue), _line(0) {}
+  /** Copy constructor */
+ TorOpt(const TorOpt &opt)
+   : _name(opt.name()), _type(opt.type()), _default(opt.defaultValue()), _line(opt.line()) {}
+
+  /** Returns true if this option can be defined with multiple lines */
+  bool isMultilined() 
+  { return _type == LineList or _type == Dependant or _type == Virtual; }
+  
+  /** Returns true if this option can be defined multiple times with
+   *  different values */
+  bool isMultivalued()
+  { return _type == LineList; }
+
+  /** Returns the name for this option */
+  QString name() const
+  { return _name; }
+
+  /** Returns the type for this option*/
+  OptType type() const
+  { return _type; }
+
+  /** Returns the default value that tor assumes for this option */
+  QString defaultValue() const
+  { return _default; }
+  
+  /** Returns a pointer to the TorrcLine for this option */
+  TorrcLine *line() const
+  { return _line; }
+
+  /** Sets the line for this option */
+  void setLine(TorrcLine *line)
+  { _line = line; }
+  
+  /** Returns true if it's a null TorOpt */
+  bool isNull() const
+  { return _type == NIL; }
+  
+  /** Creates a null TorOpt*/
+  static TorOpt null()
+  { return TorOpt("Nil", NIL); }
+
+ private:
+  /** Name for this option */
+  QString _name;
+  /** Type for this option */
+  OptType _type;
+  /** Default value for this option */
+  QString _default;
+  /** Pointer to the line that represents this option */
+  TorrcLine *_line;
+};
+
+/** Parser for the torrc file and any other torrc structured file */
+class TorrcParser
+{
+ public:
+  /** Default constructor */
+  TorrcParser();
+
+  /** Parses the torrc file provided in the path and returns the
+   *  TorrcLine list for the file and the map of key=value */
+  QList<TorrcLine *> parse(const QString &path,
+                           QMap<QString, QPair<QString, TorOpt> > &map);
+  /** Returns the TorOpt for a given option name */
+  TorOpt getTorOpt(const QString &name);
+  
+ private:
+  /** List with all the current valid options for the torrc */
+  QList<TorOpt> _torOpts;
+  /** Regular expression used in the parsing based on _torOpts */
+  QRegExp _regexp;
+
+  /** Strips the torrc contents passed from comments an empty lines */
+  QString stripContents(QString contents);
+  /** Given the contents of a torrc file, it returns a list of
+   *  TorrcLines representing it */
+  QList<TorrcLine *> toTorrcLines(QString contents);
+  /** Given a TorrcLine and the line list where it belongs, it returns
+   *  the set of lines that define the multilined value */
+  QList<TorrcLine *> findValueLines(TorrcLine *line, 
+                                    const QList<TorrcLine *> &lines) const;
+  /** Creates the regular expression used in parsing */
+  QRegExp createRegExp();
+  /** Returns true if there's a keyword in the contents at the given
+   *  pos */
+  bool isKeyword(const QString &contents, int pos);
+};
+
+/** Debug output methods */
+QDebug operator<<(QDebug dbg, const TorOpt &opt);
+QDebug operator<<(QDebug dbg, TorrcLine *line);
+
+#endif
+





More information about the tor-commits mailing list