progress in using ByteArray+std::string

This commit is contained in:
csoler 2021-12-01 23:05:16 +01:00
parent 6a4cdcc471
commit d7afbea1dd
15 changed files with 171 additions and 858 deletions

View File

@ -735,7 +735,6 @@ HEADERS += tor/AddOnionCommand.h \
tor/SecureRNG.h \ tor/SecureRNG.h \
tor/TorTypes.h \ tor/TorTypes.h \
tor/SetConfCommand.h \ tor/SetConfCommand.h \
tor/Settings.h \
tor/StrUtil.h \ tor/StrUtil.h \
tor/bytearray.h \ tor/bytearray.h \
tor/TorControl.h \ tor/TorControl.h \
@ -760,7 +759,6 @@ SOURCES += tor/AddOnionCommand.cpp \
tor/CryptoKey.cpp \ tor/CryptoKey.cpp \
tor/PendingOperation.cpp \ tor/PendingOperation.cpp \
tor/SecureRNG.cpp \ tor/SecureRNG.cpp \
tor/Settings.cpp \
tor/StrUtil.cpp tor/StrUtil.cpp
# gxs tunnels # gxs tunnels

View File

@ -173,6 +173,18 @@ void RsFdBinInterface::clean()
in_buffer.clear(); in_buffer.clear();
out_buffer.clear(); out_buffer.clear();
} }
int RsFdBinInterface::readline(void *data, int len)
{
int n=0;
for(auto p:in_buffer)
for(int i=0;i<p.second && n<len;++i,++n)
if(static_cast<unsigned char*>(p.first)[i] == '\n')
return readdata(data,n+1);
return 0;
}
int RsFdBinInterface::readdata(void *data, int len) int RsFdBinInterface::readdata(void *data, int len)
{ {
// read incoming bytes in the buffer // read incoming bytes in the buffer

View File

@ -41,6 +41,10 @@ public:
// //
int readdata(void *data, int len) override; int readdata(void *data, int len) override;
// Read at most len bytes only if \n is encountered within that range. Otherwise, nothing is changed.
//
int readline(void *data, int len) ;
int netstatus() override; int netstatus() override;
int isactive() override; int isactive() override;
bool moretoread(uint32_t usec) override; bool moretoread(uint32_t usec) override;

View File

@ -1,553 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Settings.h"
#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QSaveFile>
#include <QFile>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QDebug>
#include <QPointer>
class SettingsFilePrivate : public QObject
{
Q_OBJECT
public:
SettingsFile *q;
std::string filePath;
std::string errorMessage;
QTimer syncTimer;
QJsonObject jsonRoot;
SettingsObject *rootObject;
SettingsFilePrivate(SettingsFile *qp);
virtual ~SettingsFilePrivate();
void reset();
void setError(const std::string &message);
bool checkDirPermissions(const std::string &path);
bool readFile();
bool writeFile();
static std::list<std::string> splitPath(const std::string& input, bool &ok);
QJsonValue read(const QJsonObject &base, const std::list<std::string> &path);
bool write(const std::list<std::string> &path, const QJsonValue &value);
signals:
void modified(const std::list<std::string> &path, const QJsonValue &value);
private slots:
void sync();
};
SettingsFile::SettingsFile(QObject *parent)
: QObject(parent), d(new SettingsFilePrivate(this))
{
d->rootObject = new SettingsObject(this, QString());
}
SettingsFile::~SettingsFile()
{
}
SettingsFilePrivate::SettingsFilePrivate(SettingsFile *qp)
: QObject(qp)
, q(qp)
, rootObject(0)
{
syncTimer.setInterval(0);
syncTimer.setSingleShot(true);
connect(&syncTimer, &QTimer::timeout, this, &SettingsFilePrivate::sync);
}
SettingsFilePrivate::~SettingsFilePrivate()
{
if (syncTimer.isActive())
sync();
delete rootObject;
}
void SettingsFilePrivate::reset()
{
filePath.clear();
errorMessage.clear();
jsonRoot = QJsonObject();
emit modified(QStringList(), jsonRoot);
}
QString SettingsFile::filePath() const
{
return d->filePath;
}
bool SettingsFile::setFilePath(const std::string& filePath)
{
if (d->filePath == filePath)
return hasError();
d->reset();
d->filePath = filePath;
QFileInfo fileInfo(filePath);
QDir dir(fileInfo.path());
if (!dir.exists() && !dir.mkpath(".")) {
d->setError("Cannot create directory: " + dir.path()));
return false;
}
d->checkDirPermissions(fileInfo.path());
if (!d->readFile())
return false;
return true;
}
std::string SettingsFile::errorMessage() const
{
return d->errorMessage;
}
bool SettingsFile::hasError() const
{
return !d->errorMessage.isEmpty();
}
void SettingsFilePrivate::setError(const QString &message)
{
errorMessage = message;
emit q->error();
}
bool SettingsFilePrivate::checkDirPermissions(const QString &path)
{
static QFile::Permissions desired = QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ExeUser;
static QFile::Permissions ignored = QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner;
QFile file(path);
if ((file.permissions() & ~ignored) != desired) {
qDebug() << "Correcting permissions on configuration directory";
if (!file.setPermissions(desired)) {
qWarning() << "Correcting permissions on configuration directory failed";
return false;
}
}
return true;
}
SettingsObject *SettingsFile::root()
{
return d->rootObject;
}
const SettingsObject *SettingsFile::root() const
{
return d->rootObject;
}
void SettingsFilePrivate::sync()
{
if (filePath.isEmpty())
return;
syncTimer.stop();
writeFile();
}
bool SettingsFilePrivate::readFile()
{
QFile file(filePath);
if (!file.open(QIODevice::ReadWrite)) {
setError(file.errorString());
return false;
}
QByteArray data = file.readAll();
if (data.isEmpty() && (file.error() != QFileDevice::NoError || file.size() > 0)) {
setError(file.errorString());
return false;
}
if (data.isEmpty()) {
jsonRoot = QJsonObject();
return true;
}
QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(data, &parseError);
if (document.isNull()) {
setError(parseError.errorString());
return false;
}
if (!document.isObject()) {
setError(QStringLiteral("Invalid configuration file (expected object)"));
return false;
}
jsonRoot = document.object();
emit modified(QStringList(), jsonRoot);
return true;
}
bool SettingsFilePrivate::writeFile()
{
QSaveFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
setError(file.errorString());
return false;
}
QJsonDocument document(jsonRoot);
QByteArray data = document.toJson();
if (data.isEmpty() && !document.isEmpty()) {
setError(QStringLiteral("Encoding failure"));
return false;
}
if (file.write(data) < data.size() || !file.commit()) {
setError(file.errorString());
return false;
}
return true;
}
QStringList SettingsFilePrivate::splitPath(const QString &input, bool &ok)
{
QStringList components = input.split(QLatin1Char('.'));
// Allow a leading '.' to simplify concatenation
if (!components.isEmpty() && components.first().isEmpty())
components.takeFirst();
// No other empty components, including a trailing .
foreach (const QString &word, components) {
if (word.isEmpty()) {
ok = false;
return QStringList();
}
}
ok = true;
return components;
}
QJsonValue SettingsFilePrivate::read(const QJsonObject &base, const QStringList &path)
{
QJsonValue current = base;
foreach (const QString &key, path) {
QJsonObject object = current.toObject();
if (object.isEmpty() || (current = object.value(key)).isUndefined())
return QJsonValue::Undefined;
}
return current;
}
// Compare two QJsonValue to find keys that have changed,
// recursing into objects and building paths as necessary.
typedef QList<QPair<QStringList, QJsonValue> > ModifiedList;
static void findModifiedRecursive(ModifiedList &modified, const QStringList &path, const QJsonValue &oldValue, const QJsonValue &newValue)
{
if (oldValue.isObject() || newValue.isObject()) {
// If either is a non-object type, this returns an empty object
QJsonObject oldObject = oldValue.toObject();
QJsonObject newObject = newValue.toObject();
// Iterate keys of the original object and compare to new
for (QJsonObject::iterator it = oldObject.begin(); it != oldObject.end(); it++) {
QJsonValue newSubValue = newObject.value(it.key());
if (*it == newSubValue)
continue;
if ((*it).isObject() || newSubValue.isObject())
findModifiedRecursive(modified, QStringList() << path << it.key(), *it, newSubValue);
else
modified.append(qMakePair(QStringList() << path << it.key(), newSubValue));
}
// Iterate keys of the new object that may not be in original
for (QJsonObject::iterator it = newObject.begin(); it != newObject.end(); it++) {
if (oldObject.contains(it.key()))
continue;
if ((*it).isObject())
findModifiedRecursive(modified, QStringList() << path << it.key(), QJsonValue::Undefined, it.value());
else
modified.append(qMakePair(QStringList() << path << it.key(), it.value()));
}
} else
modified.append(qMakePair(path, newValue));
}
bool SettingsFilePrivate::write(const QStringList &path, const QJsonValue &value)
{
typedef QVarLengthArray<QPair<QString,QJsonObject> > ObjectStack;
ObjectStack stack;
QJsonValue current = jsonRoot;
QJsonValue originalValue;
QString currentKey;
foreach (const QString &key, path) {
const QJsonObject &parent = current.toObject();
stack.append(qMakePair(currentKey, parent));
current = parent.value(key);
currentKey = key;
}
// Stack now contains parent objects starting with the root, and current
// is the old value. Write back changes in reverse.
if (current == value)
return false;
originalValue = current;
current = value;
ObjectStack::const_iterator it = stack.end(), begin = stack.begin();
while (it != begin) {
--it;
QJsonObject update = it->second;
update.insert(currentKey, current);
current = update;
currentKey = it->first;
}
// current is now the updated jsonRoot
jsonRoot = current.toObject();
syncTimer.start();
ModifiedList modified;
findModifiedRecursive(modified, path, originalValue, value);
for (ModifiedList::iterator it = modified.begin(); it != modified.end(); it++)
emit this->modified(it->first, it->second);
return true;
}
class SettingsObjectPrivate : public QObject
{
Q_OBJECT
public:
explicit SettingsObjectPrivate(SettingsObject *q);
SettingsObject *q;
SettingsFile *file;
QStringList path;
QJsonObject object;
bool invalid;
void setFile(SettingsFile *file);
public slots:
void modified(const QStringList &absolutePath, const QJsonValue &value);
};
SettingsObject::SettingsObject(QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(defaultFile());
if (d->file)
setPath(QString());
}
SettingsObject::SettingsObject(const QString &path, QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(defaultFile());
setPath(path);
}
SettingsObject::SettingsObject(SettingsFile *file, const QString &path, QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(file);
setPath(path);
}
SettingsObject::SettingsObject(SettingsObject *base, const QString &path, QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(base->d->file);
setPath(base->path() + QLatin1Char('.') + path);
}
SettingsObjectPrivate::SettingsObjectPrivate(SettingsObject *qp)
: QObject(qp)
, q(qp)
, file(0)
, invalid(true)
{
}
void SettingsObjectPrivate::setFile(SettingsFile *value)
{
if (file == value)
return;
if (file)
disconnect(file, 0, this, 0);
file = value;
if (file)
connect(file->d, &SettingsFilePrivate::modified, this, &SettingsObjectPrivate::modified);
}
// Emit SettingsObject::modified with a relative path if path is matched
void SettingsObjectPrivate::modified(const QStringList &key, const QJsonValue &value)
{
if (key.size() < path.size())
return;
for (int i = 0; i < path.size(); i++) {
if (path[i] != key[i])
return;
}
object = file->d->read(file->d->jsonRoot, path).toObject();
emit q->modified(QStringList(key.mid(path.size())).join(QLatin1Char('.')), value);
emit q->dataChanged();
}
static QPointer<SettingsFile> defaultObjectFile;
SettingsFile *SettingsObject::defaultFile()
{
return defaultObjectFile;
}
void SettingsObject::setDefaultFile(SettingsFile *file)
{
defaultObjectFile = file;
}
QString SettingsObject::path() const
{
return d->path.join(QLatin1Char('.'));
}
void SettingsObject::setPath(const QString &input)
{
bool ok = false;
QStringList newPath = SettingsFilePrivate::splitPath(input, ok);
if (!ok) {
d->invalid = true;
d->path.clear();
d->object = QJsonObject();
emit pathChanged();
emit dataChanged();
return;
}
if (!d->invalid && d->path == newPath)
return;
d->path = newPath;
if (d->file) {
d->invalid = false;
d->object = d->file->d->read(d->file->d->jsonRoot, d->path).toObject();
emit dataChanged();
}
emit pathChanged();
}
QJsonObject SettingsObject::data() const
{
return d->object;
}
void SettingsObject::setData(const QJsonObject &input)
{
if (d->invalid || d->object == input)
return;
d->object = input;
d->file->d->write(d->path, d->object);
}
QJsonValue SettingsObject::read(const QString &key, const QJsonValue &defaultValue) const
{
bool ok = false;
QStringList splitKey = SettingsFilePrivate::splitPath(key, ok);
if (d->invalid || !ok || splitKey.isEmpty()) {
qDebug() << "Invalid settings read of path" << key;
return defaultValue;
}
QJsonValue ret = d->file->d->read(d->object, splitKey);
if (ret.isUndefined())
ret = defaultValue;
return ret;
}
void SettingsObject::write(const QString &key, const QJsonValue &value)
{
bool ok = false;
QStringList splitKey = SettingsFilePrivate::splitPath(key, ok);
if (d->invalid || !ok || splitKey.isEmpty()) {
qDebug() << "Invalid settings write of path" << key;
return;
}
splitKey = d->path + splitKey;
d->file->d->write(splitKey, value);
}
void SettingsObject::unset(const QString &key)
{
write(key, QJsonValue());
}
void SettingsObject::undefine()
{
if (d->invalid)
return;
d->object = QJsonObject();
d->file->d->write(d->path, QJsonValue::Undefined);
}
#include "Settings.moc"

View File

@ -1,256 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SETTINGS_H
#define SETTINGS_H
#include <QObject>
#include <QJsonValue>
#include <QJsonObject>
#include <QJsonArray>
#include <QDateTime>
class SettingsObject;
class SettingsFilePrivate;
class SettingsObjectPrivate;
/* SettingsFile represents a JSON-encoded configuration file.
*
* SettingsFile is an API for reading, writing, and change notification
* on JSON-encoded settings files.
*
* Data is accessed via SettingsObject, either using the root property
* or by creating a SettingsObject, optionally using a base path.
*/
class SettingsFile : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(SettingsFile)
Q_PROPERTY(SettingsObject *root READ root CONSTANT)
Q_PROPERTY(QString filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY error)
Q_PROPERTY(bool hasError READ hasError NOTIFY error)
public:
explicit SettingsFile(QObject *parent = 0);
virtual ~SettingsFile();
QString filePath() const;
bool setFilePath(const std::string &filePath);
std::string errorMessage() const;
bool hasError() const;
SettingsObject *root();
const SettingsObject *root() const;
signals:
void filePathChanged();
void error();
private:
SettingsFilePrivate *d;
friend class SettingsObject;
friend class SettingsObjectPrivate;
};
/* SettingsObject reads and writes data within a SettingsFile
*
* A SettingsObject is associated with a SettingsFile and represents an object
* tree within that file. It refers to the JSON object tree using a path
* notation with keys separated by '.'. For example:
*
* {
* "one": {
* "two": {
* "three": "value"
* }
* }
* }
*
* With this data, a SettingsObject with an empty path can read with the path
* "one.two.three", and a SettingsObject with a path of "one.two" can simply
* read or write on "three".
*
* Multiple SettingsObjects may be created for the same path, and will be kept
* synchronized with changes. The modified signal is emitted for all changes
* affecting keys within a path, including writes of object trees and from other
* instances.
*/
class SettingsObject : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(SettingsObject)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QJsonObject data READ data WRITE setData NOTIFY dataChanged)
public:
explicit SettingsObject(QObject *parent = 0);
explicit SettingsObject(const QString &path, QObject *parent = 0);
explicit SettingsObject(SettingsFile *file, const QString &path, QObject *parent = 0);
explicit SettingsObject(SettingsObject *base, const QString &path, QObject *parent = 0);
/* Specify a SettingsFile to use by default on SettingsObject instances.
*
* After calling setDefaultFile, a SettingsObject created without any file, e.g.:
*
* SettingsObject settings;
* SettingsObject animals(QStringLiteral("animals"));
*
* Will use the specified SettingsFile instance by default. This is a convenience
* over passing around instances of SettingsFile in application use cases, and is
* particularly useful for QML.
*/
static SettingsFile *defaultFile();
static void setDefaultFile(SettingsFile *file);
std::string path() const;
void setPath(const std::string &path);
QJsonObject data() const;
void setData(const QJsonObject &data);
Q_INVOKABLE QJsonValue read(const std::string &key, const QJsonValue &defaultValue = QJsonValue::Undefined) const;
template<typename T> T read(const std::string &key) const;
Q_INVOKABLE void write(const std::string &key, const QJsonValue &value);
template<typename T> void write(const std::string &key, const T &value);
Q_INVOKABLE void unset(const std::string &key);
// const char* key overloads
QJsonValue read(const char *key, const QJsonValue &defaultValue = QJsonValue::Undefined) const
{
return read(std::string(key), defaultValue);
}
template<typename T> T read(const char *key) const
{
return read<T>(std::string(key));
}
void write(const char *key, const QJsonValue &value)
{
write(std::string(key), value);
}
template<typename T> void write(const char *key, const T &value)
{
write<T>(std::string(key), value);
}
void unset(const char *key)
{
unset(std::string(key));
}
Q_INVOKABLE void undefine();
signals:
void pathChanged();
void dataChanged();
void modified(const std::string &path, const QJsonValue &value);
private:
SettingsObjectPrivate *d;
};
template<typename T> inline void SettingsObject::write(const std::string &key, const T &value)
{
write(key, QJsonValue(value));
}
template<> inline std::string SettingsObject::read<std::string>(const std::string &key) const
{
return read(key).toString();
}
template<> inline QJsonArray SettingsObject::read<QJsonArray>(const std::string &key) const
{
return read(key).toArray();
}
template<> inline QJsonObject SettingsObject::read<QJsonObject>(const std::string &key) const
{
return read(key).toObject();
}
template<> inline double SettingsObject::read<double>(const std::string &key) const
{
return read(key).toDouble();
}
template<> inline int SettingsObject::read<int>(const std::string &key) const
{
return read(key).toInt();
}
template<> inline bool SettingsObject::read<bool>(const std::string &key) const
{
return read(key).toBool();
}
template<> inline QDateTime SettingsObject::read<QDateTime>(const std::string &key) const
{
std::string value = read(key).toString();
if (value.isEmpty())
return QDateTime();
return QDateTime::fromString(value, Qt::ISODate).toLocalTime();
}
template<> inline void SettingsObject::write<QDateTime>(const std::string &key, const QDateTime &value)
{
write(key, QJsonValue(value.toUTC().toString(Qt::ISODate)));
}
// Explicitly store value encoded as base64. Decodes and casts implicitly to QByteArray for reads.
class Base64Encode
{
public:
explicit Base64Encode(const QByteArray &value) : d(value) { }
operator QByteArray() { return d; }
QByteArray encoded() const { return d.toBase64(); }
private:
QByteArray d;
};
template<> inline Base64Encode SettingsObject::read<Base64Encode>(const std::string &key) const
{
return Base64Encode(QByteArray::fromBase64(read(key).toString().toLatin1()));
}
template<> inline void SettingsObject::write<Base64Encode>(const std::string &key, const Base64Encode &value)
{
write(key, QJsonValue(std::string(value.encoded())));
}
#endif

View File

@ -462,6 +462,7 @@ void TorControlPrivate::getTorInfo()
std::list<std::string> keys{ "status/circuit-established","status/bootstrap-phase" }; std::list<std::string> keys{ "status/circuit-established","status/bootstrap-phase" };
#ifdef TODO
/* If these are set in the config, they override the automatic behavior. */ /* If these are set in the config, they override the automatic behavior. */
SettingsObject settings("tor"); SettingsObject settings("tor");
QHostAddress forceAddress(settings.read("socksAddress").toString()); QHostAddress forceAddress(settings.read("socksAddress").toString());
@ -481,6 +482,7 @@ void TorControlPrivate::getTorInfo()
} }
} }
else else
#endif
keys .push_back("net/listeners/socks"); keys .push_back("net/listeners/socks");
socket->sendCommand(command, command->build(keys)); socket->sendCommand(command, command->build(keys));
@ -558,6 +560,7 @@ void TorControlPrivate::publishServices()
} }
std::cerr << std::endl; std::cerr << std::endl;
#ifdef TODO
SettingsObject settings("tor"); SettingsObject settings("tor");
if (settings.read("neverPublishServices").toBool()) if (settings.read("neverPublishServices").toBool())
{ {
@ -569,6 +572,7 @@ void TorControlPrivate::publishServices()
return; return;
} }
#endif
if (q->torVersionAsNewAs("0.2.7")) { if (q->torVersionAsNewAs("0.2.7")) {
foreach (HiddenService *service, services) { foreach (HiddenService *service, services) {

View File

@ -37,11 +37,11 @@
using namespace Tor; using namespace Tor;
TorControlSocket::TorControlSocket() TorControlSocket::TorControlSocket(const std::string& tcp_address,uint16_t tcp_port)
: currentCommand(0), inDataReply(false) : RsThreadedTcpSocket(tcp_address,tcp_port),currentCommand(0), inDataReply(false)
{ {
connect(this, SIGNAL(readyRead()), this, SLOT(process())); //connect(this, SIGNAL(readyRead()), this, SLOT(process()));
connect(this, SIGNAL(disconnected()), this, SLOT(clear())); //connect(this, SIGNAL(disconnected()), this, SLOT(clear()));
} }
TorControlSocket::~TorControlSocket() TorControlSocket::~TorControlSocket()
@ -49,12 +49,12 @@ TorControlSocket::~TorControlSocket()
clear(); clear();
} }
void TorControlSocket::sendCommand(TorControlCommand *command, const ByteArray &data) void TorControlSocket::sendCommand(TorControlCommand *command, const ByteArray& data)
{ {
assert(data.endsWith(ByteArray("\r\n"))); assert(data.endsWith(ByteArray("\r\n")));
commandQueue.push_back(command); commandQueue.push_back(command);
write(data); senddata((void*)data.data(),data.size());
std::cerr << "[TOR CTRL] Sent: \"" << data.trimmed().toString() << "\"" << std::endl; std::cerr << "[TOR CTRL] Sent: \"" << data.trimmed().toString() << "\"" << std::endl;
} }
@ -93,13 +93,28 @@ void TorControlSocket::setError(const std::string &message)
abort(); abort();
} }
ByteArray TorControlSocket::readline(int s)
{
ByteArray b(s);
int real_size;
if(! (real_size = RsTcpSocket::readline(b.data(),s)))
return ByteArray();
else
{
b.resize(real_size);
return b;
}
}
void TorControlSocket::process() void TorControlSocket::process()
{ {
for (;;) { for (;;) {
if (!canReadLine()) if (!moretoread(0))
return; return;
ByteArray line = readLine(5120); ByteArray line = readline(5120);
if (!line.endsWith(ByteArray("\r\n"))) { if (!line.endsWith(ByteArray("\r\n"))) {
setError("Invalid control message syntax"); setError("Invalid control message syntax");
return; return;
@ -142,7 +157,12 @@ void TorControlSocket::process()
if (!currentCommand) { if (!currentCommand) {
int space = line.indexOf(' '); int space = line.indexOf(' ');
if (space > 0) if (space > 0)
currentCommand = eventCommands.value(line.mid(0, space)); {
auto it = eventCommands.find(line.mid(0, space).toString());
if(it != eventCommands.end())
currentCommand = it->second;
}
if (!currentCommand) { if (!currentCommand) {
RsWarn() << "torctrl: Ignoring unknown event"; RsWarn() << "torctrl: Ignoring unknown event";
@ -178,3 +198,14 @@ void TorControlSocket::process()
} }
} }
} }
int TorControlSocket::tick()
{
bool rw = RsTcpSocket::tick();
if(moretoread(0))
process();
if(!rw)
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // temporisation when nothing happens
}

View File

@ -40,23 +40,29 @@ namespace Tor
class TorControlCommand; class TorControlCommand;
class TorControlSocket : public RsTcpSocket class TorControlSocket : public RsThreadedTcpSocket
{ {
public: public:
explicit TorControlSocket(); explicit TorControlSocket(const std::string& tcp_address,uint16_t tcp_port);
virtual ~TorControlSocket(); virtual ~TorControlSocket();
std::string errorMessage() const { return m_errorMessage; } std::string errorMessage() const { return m_errorMessage; }
void registerEvent(const ByteArray &event, TorControlCommand *handler); void registerEvent(const ByteArray &event, TorControlCommand *handler);
void sendCommand(const std::string& data) { sendCommand(0, data); } void sendCommand(const ByteArray& data) { sendCommand(0, data); }
void sendCommand(TorControlCommand *command, const ByteArray &data); void sendCommand(TorControlCommand *command, const ByteArray &data);
signals: ByteArray readline(int s);
void error(const QString &message);
private slots: // threaded TcpSocket
virtual int tick() override;
//signals:
void error(const std::string& message);
//private slots:
void process(); void process();
void clear(); void clear();

View File

@ -32,6 +32,7 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <stdio.h>
// This works on linux only. I have no clue how to do that on windows. Anyway, this // This works on linux only. I have no clue how to do that on windows. Anyway, this
// is only needed for an assert that should normaly never be triggered. // is only needed for an assert that should normaly never be triggered.
@ -305,6 +306,7 @@ bool TorManager::start()
//emit errorChanged(); // not needed because there's no error to handle //emit errorChanged(); // not needed because there's no error to handle
} }
#ifdef TODO
SettingsObject settings("tor"); SettingsObject settings("tor");
// If a control port is defined by config or environment, skip launching tor // If a control port is defined by config or environment, skip launching tor
@ -338,7 +340,10 @@ bool TorManager::start()
d->control->setAuthPassword(password); d->control->setAuthPassword(password);
d->control->connect(address, port); d->control->connect(address, port);
} else { }
else
#endif
{
// Launch a bundled Tor instance // Launch a bundled Tor instance
std::string executable = d->torExecutablePath(); std::string executable = d->torExecutablePath();
@ -490,11 +495,14 @@ void TorManagerPrivate::getConfFinished()
std::string TorManagerPrivate::torExecutablePath() const std::string TorManagerPrivate::torExecutablePath() const
{ {
std::string path;
#ifdef TODO
SettingsObject settings("tor"); SettingsObject settings("tor");
std::string path = settings.read("executablePath").toString(); path = settings.read("executablePath").toString();
if (!path.isEmpty() && QFile::exists(path)) if (!path.isEmpty() && QFile::exists(path))
return path; return path;
#endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
std::string filename("/tor/tor.exe"); std::string filename("/tor/tor.exe");
@ -540,11 +548,14 @@ bool TorManagerPrivate::createDefaultTorrc(const std::string &path)
// "DisableNetwork 1\n" // (cyril) I removed this because it prevents Tor to bootstrap. // "DisableNetwork 1\n" // (cyril) I removed this because it prevents Tor to bootstrap.
"__ReloadTorrcOnSIGHUP 0\n"; "__ReloadTorrcOnSIGHUP 0\n";
QFile file(path); FILE *f = fopen(path,"w");
if (!file.open(QIODevice::WriteOnly))
return false; if (!f)
if (file.write(defaultTorrcContent) < 0)
return false; return false;
fprintf(f,"%s",defaultTorrcContent);
fclose(f);
return true; return true;
} }
@ -659,13 +670,7 @@ RsTorHiddenServiceStatus RsTor::getHiddenServiceStatus(std::string& service_id)
std::map<std::string,std::string> RsTor::bootstrapStatus() std::map<std::string,std::string> RsTor::bootstrapStatus()
{ {
QVariantMap m = instance()->control()->bootstrapStatus(); return instance()->control()->bootstrapStatus();
std::map<std::string,std::string> res;
for(auto it(m.begin());it!=m.end();++it)
res.insert(std::make_pair(it.key().toStdString(),it.value().toString().toStdString()));
return res;
} }
bool RsTor::hasError() bool RsTor::hasError()

View File

@ -120,6 +120,63 @@ std::string TorProcess::errorMessage() const
return d->errorMessage; return d->errorMessage;
} }
// Does a fopen, but dup all file descriptors (STDIN STDOUT and STDERR) to the
// FDs supplied by the parent process
int popen3(int fd[3],const char **const cmd)
{
int i, e;
int p[3][2];
pid_t pid;
// set all the FDs to invalid
for(i=0; i<3; i++)
p[i][0] = p[i][1] = -1;
// create the pipes
for(int i=0; i<3; i++)
if(pipe(p[i]))
goto error;
// and fork
pid = fork();
if(-1 == pid)
goto error;
// in the parent?
if(pid) {
// parent
fd[STDIN_FILENO] = p[STDIN_FILENO][1];
close(p[STDIN_FILENO][0]);
fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
close(p[STDOUT_FILENO][1]);
fd[STDERR_FILENO] = p[STDERR_FILENO][0];
close(p[STDERR_FILENO][1]);
// success
return 0;
} else {
// child
dup2(p[STDIN_FILENO][0],STDIN_FILENO);
close(p[STDIN_FILENO][1]);
dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
close(p[STDOUT_FILENO][0]);
dup2(p[STDERR_FILENO][1],STDERR_FILENO);
close(p[STDERR_FILENO][0]);
// here we try and run it
execv(*cmd,const_cast<char*const*>(cmd));
// if we are there, then we failed to launch our program
perror("Could not launch");
fprintf(stderr," \"%s\"\n",*cmd);
_exit(EXIT_FAILURE);
}
error:
// preserve original error
e = errno;
for(i=0; i<3; i++) {
close(p[i][0]);
close(p[i][1]);
}
errno = e;
return -1;
}
void TorProcess::start() void TorProcess::start()
{ {
if (state() > NotStarted) if (state() > NotStarted)
@ -283,12 +340,14 @@ bool TorProcessPrivate::ensureFilesExist()
std::string TorProcessPrivate::torrcPath() const std::string TorProcessPrivate::torrcPath() const
{ {
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("torrc"); //return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("torrc");
return dataDir + "/" + "torrc";
} }
std::string TorProcessPrivate::controlPortFilePath() const std::string TorProcessPrivate::controlPortFilePath() const
{ {
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("control-port"); //return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("control-port");
return dataDir + "/" + "control-port";
} }
void TorProcessPrivate::processStarted() void TorProcessPrivate::processStarted()
@ -369,3 +428,7 @@ void TorProcessPrivate::tryReadControlPort()
} }
} }
void TorProcess::run()
{
//
}

View File

@ -37,6 +37,7 @@
#include <QHostAddress> #include <QHostAddress>
#include "bytearray.h" #include "bytearray.h"
#include "util/rsthreads.h"
namespace Tor namespace Tor
{ {
@ -55,7 +56,7 @@ public:
/* Launches and controls a Tor instance with behavior suitable for bundling /* Launches and controls a Tor instance with behavior suitable for bundling
* an instance with the application. */ * an instance with the application. */
class TorProcess class TorProcess: public RsTickingThread
{ {
//Q_OBJECT //Q_OBJECT
//Q_ENUMS(State) //Q_ENUMS(State)
@ -102,6 +103,9 @@ public:
void start(); void start();
void stop(); void stop();
// implements RsThread / RsTickingThread
virtual void run() override;
private: private:
TorProcessPrivate *d; TorProcessPrivate *d;
TorProcessClient *m_client; TorProcessClient *m_client;

View File

@ -34,12 +34,10 @@
#define TORPROCESS_P_H #define TORPROCESS_P_H
#include "TorProcess.h" #include "TorProcess.h"
#include <QProcess>
#include <QTimer>
namespace Tor { namespace Tor {
class TorProcessPrivate : public QObject class TorProcessPrivate : public RsTickingThread
{ {
Q_OBJECT Q_OBJECT

View File

@ -38,7 +38,16 @@ public:
ByteArray toUpper() const { auto res = *this; for(uint32_t i=0;i<size();++i) if( res[i]<='z' && res[i]>='a') res[i] += int('A')-int('a'); return res; } ByteArray toUpper() const { auto res = *this; for(uint32_t i=0;i<size();++i) if( res[i]<='z' && res[i]>='a') res[i] += int('A')-int('a'); return res; }
ByteArray toLower() const { auto res = *this; for(uint32_t i=0;i<size();++i) if( res[i]<='Z' && res[i]>='A') res[i] += int('a')-int('A'); return res; } ByteArray toLower() const { auto res = *this; for(uint32_t i=0;i<size();++i) if( res[i]<='Z' && res[i]>='A') res[i] += int('a')-int('A'); return res; }
bool endsWidth(const ByteArray& b) const { return size() >= b.size() && !memcmp(&data()[size()-b.size()],b.data(),b.size()); } int toInt() const
{
std::istringstream is(toString().c_str());
int res = 0;
is >> res ;
return res;
}
bool endsWith(const ByteArray& b) const { return size() >= b.size() && !memcmp(&data()[size()-b.size()],b.data(),b.size()); }
bool startsWith(const char *b) const bool startsWith(const char *b) const
{ {
for(uint32_t n=0;b[n]!=0;++n) for(uint32_t n=0;b[n]!=0;++n)

View File

@ -84,16 +84,6 @@ bool std::filesystem::create_directories(const std::string& path)
# include <filesystem> # include <filesystem>
#endif // __cplusplus < 201703L #endif // __cplusplus < 201703L
bool RsDirUtil::fileExists(const std::string& file_path)
{
FILE *f = fopen(file_path.c_str(),"r");
if(!f)
return false;
fclose(f);
return true;
}
std::string RsDirUtil::getFileName(const std::string& full_file_path) std::string RsDirUtil::getFileName(const std::string& full_file_path)
{ {
size_t n = full_file_path.find_last_of('/'); size_t n = full_file_path.find_last_of('/');

View File

@ -70,8 +70,6 @@ std::string getFileName(const std::string& full_file_path);
bool renameFile(const std::string& from,const std::string& to) ; bool renameFile(const std::string& from,const std::string& to) ;
//bool createBackup (const std::string& sFilename, unsigned int nCount = 5); //bool createBackup (const std::string& sFilename, unsigned int nCount = 5);
bool fileExists(const std::string& file_path);
// returns the CRC32 of the data of length len // returns the CRC32 of the data of length len
// //
uint32_t rs_CRC32(const unsigned char *data,uint32_t len) ; uint32_t rs_CRC32(const unsigned char *data,uint32_t len) ;