mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-28 08:59:37 -05:00
progress in using ByteArray+std::string
This commit is contained in:
parent
6a4cdcc471
commit
d7afbea1dd
@ -735,7 +735,6 @@ HEADERS += tor/AddOnionCommand.h \
|
||||
tor/SecureRNG.h \
|
||||
tor/TorTypes.h \
|
||||
tor/SetConfCommand.h \
|
||||
tor/Settings.h \
|
||||
tor/StrUtil.h \
|
||||
tor/bytearray.h \
|
||||
tor/TorControl.h \
|
||||
@ -760,7 +759,6 @@ SOURCES += tor/AddOnionCommand.cpp \
|
||||
tor/CryptoKey.cpp \
|
||||
tor/PendingOperation.cpp \
|
||||
tor/SecureRNG.cpp \
|
||||
tor/Settings.cpp \
|
||||
tor/StrUtil.cpp
|
||||
|
||||
# gxs tunnels
|
||||
|
@ -173,6 +173,18 @@ void RsFdBinInterface::clean()
|
||||
in_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)
|
||||
{
|
||||
// read incoming bytes in the buffer
|
||||
|
@ -41,6 +41,10 @@ public:
|
||||
//
|
||||
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 isactive() override;
|
||||
bool moretoread(uint32_t usec) override;
|
||||
|
@ -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"
|
@ -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
|
||||
|
@ -462,6 +462,7 @@ void TorControlPrivate::getTorInfo()
|
||||
|
||||
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. */
|
||||
SettingsObject settings("tor");
|
||||
QHostAddress forceAddress(settings.read("socksAddress").toString());
|
||||
@ -481,6 +482,7 @@ void TorControlPrivate::getTorInfo()
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
keys .push_back("net/listeners/socks");
|
||||
|
||||
socket->sendCommand(command, command->build(keys));
|
||||
@ -558,6 +560,7 @@ void TorControlPrivate::publishServices()
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
#ifdef TODO
|
||||
SettingsObject settings("tor");
|
||||
if (settings.read("neverPublishServices").toBool())
|
||||
{
|
||||
@ -569,6 +572,7 @@ void TorControlPrivate::publishServices()
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (q->torVersionAsNewAs("0.2.7")) {
|
||||
foreach (HiddenService *service, services) {
|
||||
|
@ -37,11 +37,11 @@
|
||||
|
||||
using namespace Tor;
|
||||
|
||||
TorControlSocket::TorControlSocket()
|
||||
: currentCommand(0), inDataReply(false)
|
||||
TorControlSocket::TorControlSocket(const std::string& tcp_address,uint16_t tcp_port)
|
||||
: RsThreadedTcpSocket(tcp_address,tcp_port),currentCommand(0), inDataReply(false)
|
||||
{
|
||||
connect(this, SIGNAL(readyRead()), this, SLOT(process()));
|
||||
connect(this, SIGNAL(disconnected()), this, SLOT(clear()));
|
||||
//connect(this, SIGNAL(readyRead()), this, SLOT(process()));
|
||||
//connect(this, SIGNAL(disconnected()), this, SLOT(clear()));
|
||||
}
|
||||
|
||||
TorControlSocket::~TorControlSocket()
|
||||
@ -49,12 +49,12 @@ TorControlSocket::~TorControlSocket()
|
||||
clear();
|
||||
}
|
||||
|
||||
void TorControlSocket::sendCommand(TorControlCommand *command, const ByteArray &data)
|
||||
void TorControlSocket::sendCommand(TorControlCommand *command, const ByteArray& data)
|
||||
{
|
||||
assert(data.endsWith(ByteArray("\r\n")));
|
||||
|
||||
commandQueue.push_back(command);
|
||||
write(data);
|
||||
senddata((void*)data.data(),data.size());
|
||||
|
||||
std::cerr << "[TOR CTRL] Sent: \"" << data.trimmed().toString() << "\"" << std::endl;
|
||||
}
|
||||
@ -93,13 +93,28 @@ void TorControlSocket::setError(const std::string &message)
|
||||
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()
|
||||
{
|
||||
for (;;) {
|
||||
if (!canReadLine())
|
||||
if (!moretoread(0))
|
||||
return;
|
||||
|
||||
ByteArray line = readLine(5120);
|
||||
ByteArray line = readline(5120);
|
||||
|
||||
if (!line.endsWith(ByteArray("\r\n"))) {
|
||||
setError("Invalid control message syntax");
|
||||
return;
|
||||
@ -142,7 +157,12 @@ void TorControlSocket::process()
|
||||
if (!currentCommand) {
|
||||
int space = line.indexOf(' ');
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
@ -40,23 +40,29 @@ namespace Tor
|
||||
|
||||
class TorControlCommand;
|
||||
|
||||
class TorControlSocket : public RsTcpSocket
|
||||
class TorControlSocket : public RsThreadedTcpSocket
|
||||
{
|
||||
public:
|
||||
explicit TorControlSocket();
|
||||
explicit TorControlSocket(const std::string& tcp_address,uint16_t tcp_port);
|
||||
virtual ~TorControlSocket();
|
||||
|
||||
std::string errorMessage() const { return m_errorMessage; }
|
||||
|
||||
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);
|
||||
|
||||
signals:
|
||||
void error(const QString &message);
|
||||
ByteArray readline(int s);
|
||||
|
||||
private slots:
|
||||
// threaded TcpSocket
|
||||
|
||||
virtual int tick() override;
|
||||
|
||||
//signals:
|
||||
void error(const std::string& message);
|
||||
|
||||
//private slots:
|
||||
void process();
|
||||
void clear();
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <stdio.h>
|
||||
|
||||
// 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.
|
||||
@ -305,6 +306,7 @@ bool TorManager::start()
|
||||
//emit errorChanged(); // not needed because there's no error to handle
|
||||
}
|
||||
|
||||
#ifdef TODO
|
||||
SettingsObject settings("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->connect(address, port);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Launch a bundled Tor instance
|
||||
std::string executable = d->torExecutablePath();
|
||||
|
||||
@ -490,11 +495,14 @@ void TorManagerPrivate::getConfFinished()
|
||||
|
||||
std::string TorManagerPrivate::torExecutablePath() const
|
||||
{
|
||||
std::string path;
|
||||
#ifdef TODO
|
||||
SettingsObject settings("tor");
|
||||
std::string path = settings.read("executablePath").toString();
|
||||
path = settings.read("executablePath").toString();
|
||||
|
||||
if (!path.isEmpty() && QFile::exists(path))
|
||||
return path;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
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.
|
||||
"__ReloadTorrcOnSIGHUP 0\n";
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
return false;
|
||||
if (file.write(defaultTorrcContent) < 0)
|
||||
FILE *f = fopen(path,"w");
|
||||
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
fprintf(f,"%s",defaultTorrcContent);
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -659,13 +670,7 @@ RsTorHiddenServiceStatus RsTor::getHiddenServiceStatus(std::string& service_id)
|
||||
|
||||
std::map<std::string,std::string> RsTor::bootstrapStatus()
|
||||
{
|
||||
QVariantMap m = 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;
|
||||
return instance()->control()->bootstrapStatus();
|
||||
}
|
||||
|
||||
bool RsTor::hasError()
|
||||
|
@ -120,6 +120,63 @@ std::string TorProcess::errorMessage() const
|
||||
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()
|
||||
{
|
||||
if (state() > NotStarted)
|
||||
@ -283,12 +340,14 @@ bool TorProcessPrivate::ensureFilesExist()
|
||||
|
||||
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
|
||||
{
|
||||
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()
|
||||
@ -369,3 +428,7 @@ void TorProcessPrivate::tryReadControlPort()
|
||||
}
|
||||
}
|
||||
|
||||
void TorProcess::run()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <QHostAddress>
|
||||
|
||||
#include "bytearray.h"
|
||||
#include "util/rsthreads.h"
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
@ -55,7 +56,7 @@ public:
|
||||
|
||||
/* Launches and controls a Tor instance with behavior suitable for bundling
|
||||
* an instance with the application. */
|
||||
class TorProcess
|
||||
class TorProcess: public RsTickingThread
|
||||
{
|
||||
//Q_OBJECT
|
||||
//Q_ENUMS(State)
|
||||
@ -102,6 +103,9 @@ public:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
// implements RsThread / RsTickingThread
|
||||
virtual void run() override;
|
||||
|
||||
private:
|
||||
TorProcessPrivate *d;
|
||||
TorProcessClient *m_client;
|
||||
|
@ -34,12 +34,10 @@
|
||||
#define TORPROCESS_P_H
|
||||
|
||||
#include "TorProcess.h"
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
|
||||
namespace Tor {
|
||||
|
||||
class TorProcessPrivate : public QObject
|
||||
class TorProcessPrivate : public RsTickingThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -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 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
|
||||
{
|
||||
for(uint32_t n=0;b[n]!=0;++n)
|
||||
|
@ -84,16 +84,6 @@ bool std::filesystem::create_directories(const std::string& path)
|
||||
# include <filesystem>
|
||||
#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)
|
||||
{
|
||||
size_t n = full_file_path.find_last_of('/');
|
||||
|
@ -70,8 +70,6 @@ std::string getFileName(const std::string& full_file_path);
|
||||
bool renameFile(const std::string& from,const std::string& to) ;
|
||||
//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
|
||||
//
|
||||
uint32_t rs_CRC32(const unsigned char *data,uint32_t len) ;
|
||||
|
Loading…
Reference in New Issue
Block a user