commit 15cf1838fcaa1456d9e69f2fa8fc2364133547de Author: Tomás Touceda chiiph@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 +
tor-commits@lists.torproject.org