mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-06-28 16:17:28 -04:00
added retrotor option in global .pri, and TorControl code
This commit is contained in:
parent
c0951b301e
commit
5ca2d5e27b
39 changed files with 5617 additions and 3 deletions
105
retroshare-gui/src/TorControl/AddOnionCommand.cpp
Normal file
105
retroshare-gui/src/TorControl/AddOnionCommand.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2016, 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 "AddOnionCommand.h"
|
||||||
|
#include "HiddenService.h"
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
#include "StringUtil.h"
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
AddOnionCommand::AddOnionCommand(HiddenService *service)
|
||||||
|
: m_service(service)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_service);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddOnionCommand::isSuccessful() const
|
||||||
|
{
|
||||||
|
return statusCode() == 250 && m_errorMessage.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AddOnionCommand::build()
|
||||||
|
{
|
||||||
|
QByteArray out("ADD_ONION");
|
||||||
|
|
||||||
|
if (m_service->privateKey().isLoaded()) {
|
||||||
|
out += " RSA1024:";
|
||||||
|
out += m_service->privateKey().encodedPrivateKey(CryptoKey::DER).toBase64();
|
||||||
|
} else {
|
||||||
|
out += " NEW:RSA1024";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const HiddenService::Target &target, m_service->targets()) {
|
||||||
|
out += " Port=";
|
||||||
|
out += QByteArray::number(target.servicePort);
|
||||||
|
out += ",";
|
||||||
|
out += target.targetAddress.toString().toLatin1();
|
||||||
|
out += ":";
|
||||||
|
out += QByteArray::number(target.targetPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.append("\r\n");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddOnionCommand::onReply(int statusCode, const QByteArray &data)
|
||||||
|
{
|
||||||
|
TorControlCommand::onReply(statusCode, data);
|
||||||
|
if (statusCode != 250) {
|
||||||
|
m_errorMessage = QString::fromLatin1(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray keyPrefix("PrivateKey=RSA1024:");
|
||||||
|
if (data.startsWith(keyPrefix)) {
|
||||||
|
QByteArray keyData(QByteArray::fromBase64(data.mid(keyPrefix.size())));
|
||||||
|
CryptoKey key;
|
||||||
|
if (!key.loadFromData(keyData, CryptoKey::PrivateKey, CryptoKey::DER)) {
|
||||||
|
m_errorMessage = QStringLiteral("Key decoding failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_service->setPrivateKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddOnionCommand::onFinished(int statusCode)
|
||||||
|
{
|
||||||
|
TorControlCommand::onFinished(statusCode);
|
||||||
|
if (isSuccessful())
|
||||||
|
emit succeeded();
|
||||||
|
else
|
||||||
|
emit failed(statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
77
retroshare-gui/src/TorControl/AddOnionCommand.h
Normal file
77
retroshare-gui/src/TorControl/AddOnionCommand.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2016, 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 ADDONIONCOMMAND_H
|
||||||
|
#define ADDONIONCOMMAND_H
|
||||||
|
|
||||||
|
#include "TorControlCommand.h"
|
||||||
|
#include <QList>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class HiddenService;
|
||||||
|
|
||||||
|
class AddOnionCommand : public TorControlCommand
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(AddOnionCommand)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString errorMessage READ errorMessage CONSTANT)
|
||||||
|
Q_PROPERTY(bool successful READ isSuccessful CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
AddOnionCommand(HiddenService *service);
|
||||||
|
|
||||||
|
QByteArray build();
|
||||||
|
|
||||||
|
QString errorMessage() const { return m_errorMessage; }
|
||||||
|
bool isSuccessful() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void succeeded();
|
||||||
|
void failed(int code);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HiddenService *m_service;
|
||||||
|
QString m_errorMessage;
|
||||||
|
|
||||||
|
virtual void onReply(int statusCode, const QByteArray &data);
|
||||||
|
virtual void onFinished(int statusCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ADDONIONCOMMAND_H
|
||||||
|
|
64
retroshare-gui/src/TorControl/AuthenticateCommand.cpp
Normal file
64
retroshare-gui/src/TorControl/AuthenticateCommand.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/* 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 "AuthenticateCommand.h"
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
AuthenticateCommand::AuthenticateCommand()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AuthenticateCommand::build(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (data.isNull())
|
||||||
|
return QByteArray("AUTHENTICATE\r\n");
|
||||||
|
|
||||||
|
return QByteArray("AUTHENTICATE ") + data.toHex() + "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticateCommand::onReply(int statusCode, const QByteArray &data)
|
||||||
|
{
|
||||||
|
TorControlCommand::onReply(statusCode, data);
|
||||||
|
m_statusMessage = QString::fromLatin1(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticateCommand::onFinished(int statusCode)
|
||||||
|
{
|
||||||
|
if (statusCode == 515) {
|
||||||
|
m_statusMessage = QStringLiteral("Authentication failed - incorrect password");
|
||||||
|
} else if (statusCode != 250) {
|
||||||
|
if (m_statusMessage.isEmpty())
|
||||||
|
m_statusMessage = QStringLiteral("Authentication failed (error %1").arg(statusCode);
|
||||||
|
}
|
||||||
|
TorControlCommand::onFinished(statusCode);
|
||||||
|
}
|
63
retroshare-gui/src/TorControl/AuthenticateCommand.h
Normal file
63
retroshare-gui/src/TorControl/AuthenticateCommand.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* 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 AUTHENTICATECOMMAND_H
|
||||||
|
#define AUTHENTICATECOMMAND_H
|
||||||
|
|
||||||
|
#include "TorControlCommand.h"
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class AuthenticateCommand : public TorControlCommand
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AuthenticateCommand();
|
||||||
|
|
||||||
|
QByteArray build(const QByteArray &data = QByteArray());
|
||||||
|
|
||||||
|
bool isSuccessful() const { return statusCode() == 250; }
|
||||||
|
QString errorMessage() const { return m_statusMessage; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void onReply(int statusCode, const QByteArray &data);
|
||||||
|
virtual void onFinished(int statusCode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_statusMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AUTHENTICATECOMMAND_H
|
477
retroshare-gui/src/TorControl/CryptoKey.cpp
Normal file
477
retroshare-gui/src/TorControl/CryptoKey.cpp
Normal file
|
@ -0,0 +1,477 @@
|
||||||
|
/* 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 "CryptoKey.h"
|
||||||
|
#include "SecureRNG.h"
|
||||||
|
#include "Useful.h"
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
||||||
|
void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
|
||||||
|
{
|
||||||
|
*p = r->p;
|
||||||
|
*q = r->q;
|
||||||
|
}
|
||||||
|
#define RSA_bits(o) (BN_num_bits((o)->n))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen);
|
||||||
|
bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen);
|
||||||
|
|
||||||
|
CryptoKey::CryptoKey()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoKey::~CryptoKey()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoKey::Data::~Data()
|
||||||
|
{
|
||||||
|
if (key)
|
||||||
|
{
|
||||||
|
RSA_free(key);
|
||||||
|
key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CryptoKey::clear()
|
||||||
|
{
|
||||||
|
d = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CryptoKey::loadFromData(const QByteArray &data, KeyType type, KeyFormat format)
|
||||||
|
{
|
||||||
|
RSA *key = NULL;
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (data.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (format == PEM) {
|
||||||
|
BIO *b = BIO_new_mem_buf((void*)data.constData(), -1);
|
||||||
|
|
||||||
|
if (type == PrivateKey)
|
||||||
|
key = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL);
|
||||||
|
else
|
||||||
|
key = PEM_read_bio_RSAPublicKey(b, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
BIO_free(b);
|
||||||
|
} else if (format == DER) {
|
||||||
|
const uchar *dp = reinterpret_cast<const uchar*>(data.constData());
|
||||||
|
|
||||||
|
if (type == PrivateKey)
|
||||||
|
key = d2i_RSAPrivateKey(NULL, &dp, data.size());
|
||||||
|
else
|
||||||
|
key = d2i_RSAPublicKey(NULL, &dp, data.size());
|
||||||
|
} else {
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
qWarning() << "Failed to parse" << (type == PrivateKey ? "private" : "public") << "key from data";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = new Data(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CryptoKey::loadFromFile(const QString &path, KeyType type, KeyFormat format)
|
||||||
|
{
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
qWarning() << "Failed to open" << (type == PrivateKey ? "private" : "public") << "key from"
|
||||||
|
<< path << "-" << file.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data = file.readAll();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return loadFromData(data, type, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CryptoKey::isPrivate() const
|
||||||
|
{
|
||||||
|
if (!isLoaded()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
const BIGNUM *p, *q;
|
||||||
|
RSA_get0_factors(d->key, &p, &q);
|
||||||
|
return (p != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CryptoKey::bits() const
|
||||||
|
{
|
||||||
|
return isLoaded() ? RSA_bits(d->key) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray CryptoKey::publicKeyDigest() const
|
||||||
|
{
|
||||||
|
if (!isLoaded())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
QByteArray buf = encodedPublicKey(DER);
|
||||||
|
|
||||||
|
QByteArray re(20, 0);
|
||||||
|
bool ok = SHA1(reinterpret_cast<const unsigned char*>(buf.constData()), buf.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(re.data())) != NULL;
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
qWarning() << "Failed to hash public key data for digest";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray CryptoKey::encodedPublicKey(KeyFormat format) const
|
||||||
|
{
|
||||||
|
if (!isLoaded())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
if (format == PEM) {
|
||||||
|
BIO *b = BIO_new(BIO_s_mem());
|
||||||
|
|
||||||
|
if (!PEM_write_bio_RSAPublicKey(b, d->key)) {
|
||||||
|
BUG() << "Failed to encode public key in PEM format";
|
||||||
|
BIO_free(b);
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
BUF_MEM *buf;
|
||||||
|
BIO_get_mem_ptr(b, &buf);
|
||||||
|
|
||||||
|
/* Close BIO, but don't free buf. */
|
||||||
|
(void)BIO_set_close(b, BIO_NOCLOSE);
|
||||||
|
BIO_free(b);
|
||||||
|
|
||||||
|
QByteArray re((const char *)buf->data, (int)buf->length);
|
||||||
|
BUF_MEM_free(buf);
|
||||||
|
return re;
|
||||||
|
} else if (format == DER) {
|
||||||
|
uchar *buf = NULL;
|
||||||
|
int len = i2d_RSAPublicKey(d->key, &buf);
|
||||||
|
if (len <= 0 || !buf) {
|
||||||
|
BUG() << "Failed to encode public key in DER format";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray re((const char*)buf, len);
|
||||||
|
OPENSSL_free(buf);
|
||||||
|
return re;
|
||||||
|
} else {
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray CryptoKey::encodedPrivateKey(KeyFormat format) const
|
||||||
|
{
|
||||||
|
if (!isLoaded() || !isPrivate())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
if (format == PEM) {
|
||||||
|
BIO *b = BIO_new(BIO_s_mem());
|
||||||
|
|
||||||
|
if (!PEM_write_bio_RSAPrivateKey(b, d->key, NULL, NULL, 0, NULL, NULL)) {
|
||||||
|
BUG() << "Failed to encode private key in PEM format";
|
||||||
|
BIO_free(b);
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
BUF_MEM *buf;
|
||||||
|
BIO_get_mem_ptr(b, &buf);
|
||||||
|
|
||||||
|
/* Close BIO, but don't free buf. */
|
||||||
|
(void)BIO_set_close(b, BIO_NOCLOSE);
|
||||||
|
BIO_free(b);
|
||||||
|
|
||||||
|
QByteArray re((const char *)buf->data, (int)buf->length);
|
||||||
|
BUF_MEM_free(buf);
|
||||||
|
return re;
|
||||||
|
} else if (format == DER) {
|
||||||
|
uchar *buf = NULL;
|
||||||
|
int len = i2d_RSAPrivateKey(d->key, &buf);
|
||||||
|
if (len <= 0 || !buf) {
|
||||||
|
BUG() << "Failed to encode private key in DER format";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray re((const char*)buf, len);
|
||||||
|
OPENSSL_free(buf);
|
||||||
|
return re;
|
||||||
|
} else {
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CryptoKey::torServiceID() const
|
||||||
|
{
|
||||||
|
if (!isLoaded())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
QByteArray digest = publicKeyDigest();
|
||||||
|
if (digest.isNull())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
static const int hostnameDigestSize = 10;
|
||||||
|
static const int hostnameEncodedSize = 16;
|
||||||
|
|
||||||
|
QByteArray re(hostnameEncodedSize+1, 0);
|
||||||
|
base32_encode(re.data(), re.size(), digest.constData(), hostnameDigestSize);
|
||||||
|
|
||||||
|
// Chop extra null byte
|
||||||
|
re.chop(1);
|
||||||
|
|
||||||
|
return QString::fromLatin1(re);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray CryptoKey::signData(const QByteArray &data) const
|
||||||
|
{
|
||||||
|
QByteArray digest(32, 0);
|
||||||
|
bool ok = SHA256(reinterpret_cast<const unsigned char*>(data.constData()), data.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(digest.data())) != NULL;
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "Digest for RSA signature failed";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return signSHA256(digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray CryptoKey::signSHA256(const QByteArray &digest) const
|
||||||
|
{
|
||||||
|
if (!isPrivate())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
QByteArray re(RSA_size(d->key), 0);
|
||||||
|
unsigned sigsize = 0;
|
||||||
|
int r = RSA_sign(NID_sha256, reinterpret_cast<const unsigned char*>(digest.constData()), digest.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(re.data()), &sigsize, d->key);
|
||||||
|
|
||||||
|
if (r != 1) {
|
||||||
|
qWarning() << "RSA encryption failed when generating signature";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
re.truncate(sigsize);
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CryptoKey::verifyData(const QByteArray &data, QByteArray signature) const
|
||||||
|
{
|
||||||
|
QByteArray digest(32, 0);
|
||||||
|
bool ok = SHA256(reinterpret_cast<const unsigned char*>(data.constData()), data.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(digest.data())) != NULL;
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "Digest for RSA verify failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return verifySHA256(digest, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CryptoKey::verifySHA256(const QByteArray &digest, QByteArray signature) const
|
||||||
|
{
|
||||||
|
if (!isLoaded())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int r = RSA_verify(NID_sha256, reinterpret_cast<const uchar*>(digest.constData()), digest.size(),
|
||||||
|
reinterpret_cast<uchar*>(signature.data()), signature.size(), d->key);
|
||||||
|
if (r != 1)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */
|
||||||
|
QByteArray torControlHashedPassword(const QByteArray &password)
|
||||||
|
{
|
||||||
|
QByteArray salt = SecureRNG::random(8);
|
||||||
|
if (salt.isNull())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
int count = ((quint32)16 + (96 & 15)) << ((96 >> 4) + 6);
|
||||||
|
|
||||||
|
SHA_CTX hash;
|
||||||
|
SHA1_Init(&hash);
|
||||||
|
|
||||||
|
QByteArray tmp = salt + password;
|
||||||
|
while (count)
|
||||||
|
{
|
||||||
|
int c = qMin(count, tmp.size());
|
||||||
|
SHA1_Update(&hash, reinterpret_cast<const void*>(tmp.constData()), c);
|
||||||
|
count -= c;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char md[20];
|
||||||
|
SHA1_Final(md, &hash);
|
||||||
|
|
||||||
|
/* 60 is the hex-encoded value of 96, which is a constant used by Tor's algorithm. */
|
||||||
|
return QByteArray("16:") + salt.toHex().toUpper() + QByteArray("60") +
|
||||||
|
QByteArray::fromRawData(reinterpret_cast<const char*>(md), 20).toHex().toUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copyright (c) 2001-2004, Roger Dingledine
|
||||||
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
|
||||||
|
* Copyright (c) 2007-2010, The Tor Project, Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
|
||||||
|
|
||||||
|
/* Implements base32 encoding as in rfc3548. Requires that srclen*8 is a multiple of 5. */
|
||||||
|
void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen)
|
||||||
|
{
|
||||||
|
unsigned i, bit, v, u;
|
||||||
|
unsigned nbits = srclen * 8;
|
||||||
|
|
||||||
|
/* We need an even multiple of 5 bits, and enough space */
|
||||||
|
if ((nbits%5) != 0 || destlen > (nbits/5)+1) {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
memset(dest, 0, destlen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, bit = 0; bit < nbits; ++i, bit += 5)
|
||||||
|
{
|
||||||
|
/* set v to the 16-bit value starting at src[bits/8], 0-padded. */
|
||||||
|
v = ((quint8) src[bit / 8]) << 8;
|
||||||
|
if (bit + 5 < nbits)
|
||||||
|
v += (quint8) src[(bit/8)+1];
|
||||||
|
|
||||||
|
/* set u to the 5-bit value at the bit'th bit of src. */
|
||||||
|
u = (v >> (11 - (bit % 8))) & 0x1F;
|
||||||
|
dest[i] = BASE32_CHARS[u];
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implements base32 decoding as in rfc3548. Requires that srclen*5 is a multiple of 8. */
|
||||||
|
bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen)
|
||||||
|
{
|
||||||
|
unsigned int i, j, bit;
|
||||||
|
unsigned nbits = srclen * 5;
|
||||||
|
|
||||||
|
/* We need an even multiple of 8 bits, and enough space */
|
||||||
|
if ((nbits%8) != 0 || (nbits/8)+1 > destlen) {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tmp = new char[srclen];
|
||||||
|
|
||||||
|
/* Convert base32 encoded chars to the 5-bit values that they represent. */
|
||||||
|
for (j = 0; j < srclen; ++j)
|
||||||
|
{
|
||||||
|
if (src[j] > 0x60 && src[j] < 0x7B)
|
||||||
|
tmp[j] = src[j] - 0x61;
|
||||||
|
else if (src[j] > 0x31 && src[j] < 0x38)
|
||||||
|
tmp[j] = src[j] - 0x18;
|
||||||
|
else if (src[j] > 0x40 && src[j] < 0x5B)
|
||||||
|
tmp[j] = src[j] - 0x41;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete[] tmp;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assemble result byte-wise by applying five possible cases. */
|
||||||
|
for (i = 0, bit = 0; bit < nbits; ++i, bit += 8)
|
||||||
|
{
|
||||||
|
switch (bit % 40)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
dest[i] = (((quint8)tmp[(bit/5)]) << 3) + (((quint8)tmp[(bit/5)+1]) >> 2);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
dest[i] = (((quint8)tmp[(bit/5)]) << 6) + (((quint8)tmp[(bit/5)+1]) << 1)
|
||||||
|
+ (((quint8)tmp[(bit/5)+2]) >> 4);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
dest[i] = (((quint8)tmp[(bit/5)]) << 4) + (((quint8)tmp[(bit/5)+1]) >> 1);
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
dest[i] = (((quint8)tmp[(bit/5)]) << 7) + (((quint8)tmp[(bit/5)+1]) << 2)
|
||||||
|
+ (((quint8)tmp[(bit/5)+2]) >> 3);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
dest[i] = (((quint8)tmp[(bit/5)]) << 5) + ((quint8)tmp[(bit/5)+1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] tmp;
|
||||||
|
return true;
|
||||||
|
}
|
95
retroshare-gui/src/TorControl/CryptoKey.h
Normal file
95
retroshare-gui/src/TorControl/CryptoKey.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/* 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 CRYPTOKEY_H
|
||||||
|
#define CRYPTOKEY_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QSharedData>
|
||||||
|
#include <QExplicitlySharedDataPointer>
|
||||||
|
|
||||||
|
class CryptoKey
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum KeyType {
|
||||||
|
PrivateKey,
|
||||||
|
PublicKey
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KeyFormat {
|
||||||
|
PEM,
|
||||||
|
DER
|
||||||
|
};
|
||||||
|
|
||||||
|
CryptoKey();
|
||||||
|
CryptoKey(const CryptoKey &other) : d(other.d) { }
|
||||||
|
~CryptoKey();
|
||||||
|
|
||||||
|
bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM);
|
||||||
|
bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
bool isLoaded() const { return d.data() && d->key != 0; }
|
||||||
|
bool isPrivate() const;
|
||||||
|
|
||||||
|
QByteArray publicKeyDigest() const;
|
||||||
|
QByteArray encodedPublicKey(KeyFormat format) const;
|
||||||
|
QByteArray encodedPrivateKey(KeyFormat format) const;
|
||||||
|
QString torServiceID() const;
|
||||||
|
int bits() const;
|
||||||
|
|
||||||
|
// Calculate and sign SHA-256 digest of data using this key and PKCS #1 v2.0 padding
|
||||||
|
QByteArray signData(const QByteArray &data) const;
|
||||||
|
// Verify a signature as per signData
|
||||||
|
bool verifyData(const QByteArray &data, QByteArray signature) const;
|
||||||
|
|
||||||
|
// Sign the input SHA-256 digest using this key and PKCS #1 v2.0 padding
|
||||||
|
QByteArray signSHA256(const QByteArray &digest) const;
|
||||||
|
// Verify a signature as per signSHA256
|
||||||
|
bool verifySHA256(const QByteArray &digest, QByteArray signature) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Data : public QSharedData
|
||||||
|
{
|
||||||
|
typedef struct rsa_st RSA;
|
||||||
|
RSA *key;
|
||||||
|
|
||||||
|
Data(RSA *k = 0) : key(k) { }
|
||||||
|
~Data();
|
||||||
|
};
|
||||||
|
|
||||||
|
QExplicitlySharedDataPointer<Data> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
QByteArray torControlHashedPassword(const QByteArray &password);
|
||||||
|
|
||||||
|
#endif // CRYPTOKEY_H
|
124
retroshare-gui/src/TorControl/GetConfCommand.cpp
Normal file
124
retroshare-gui/src/TorControl/GetConfCommand.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/* 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 "GetConfCommand.h"
|
||||||
|
#include "StringUtil.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
GetConfCommand::GetConfCommand(Type t)
|
||||||
|
: type(t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray GetConfCommand::build(const QByteArray &key)
|
||||||
|
{
|
||||||
|
return build(QList<QByteArray>() << key);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray GetConfCommand::build(const QList<QByteArray> &keys)
|
||||||
|
{
|
||||||
|
QByteArray out;
|
||||||
|
if (type == GetConf) {
|
||||||
|
out = "GETCONF";
|
||||||
|
} else if (type == GetInfo) {
|
||||||
|
out = "GETINFO";
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const QByteArray &key, keys) {
|
||||||
|
out.append(' ');
|
||||||
|
out.append(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.append("\r\n");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetConfCommand::onReply(int statusCode, const QByteArray &data)
|
||||||
|
{
|
||||||
|
TorControlCommand::onReply(statusCode, data);
|
||||||
|
if (statusCode != 250)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int kep = data.indexOf('=');
|
||||||
|
QString key = QString::fromLatin1(data.mid(0, kep));
|
||||||
|
QVariant value;
|
||||||
|
if (kep >= 0)
|
||||||
|
value = QString::fromLatin1(unquotedString(data.mid(kep + 1)));
|
||||||
|
|
||||||
|
m_lastKey = key;
|
||||||
|
QVariantMap::iterator it = m_results.find(key);
|
||||||
|
if (it != m_results.end()) {
|
||||||
|
// Make a list of values
|
||||||
|
QVariantList results = it->toList();
|
||||||
|
if (results.isEmpty())
|
||||||
|
results.append(*it);
|
||||||
|
results.append(value);
|
||||||
|
*it = QVariant(results);
|
||||||
|
} else {
|
||||||
|
m_results.insert(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetConfCommand::onDataLine(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (m_lastKey.isEmpty()) {
|
||||||
|
qWarning() << "torctrl: Unexpected data line in GetConf command";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap::iterator it = m_results.find(m_lastKey);
|
||||||
|
if (it != m_results.end()) {
|
||||||
|
QVariantList results = it->toList();
|
||||||
|
if (results.isEmpty() && !it->toByteArray().isEmpty())
|
||||||
|
results.append(*it);
|
||||||
|
results.append(data);
|
||||||
|
*it = QVariant(results);
|
||||||
|
} else {
|
||||||
|
m_results.insert(m_lastKey, QVariantList() << data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetConfCommand::onDataFinished()
|
||||||
|
{
|
||||||
|
m_lastKey.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GetConfCommand::get(const QByteArray &key) const
|
||||||
|
{
|
||||||
|
return m_results.value(QString::fromLatin1(key));
|
||||||
|
}
|
||||||
|
|
77
retroshare-gui/src/TorControl/GetConfCommand.h
Normal file
77
retroshare-gui/src/TorControl/GetConfCommand.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/* 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 GETCONFCOMMAND_H
|
||||||
|
#define GETCONFCOMMAND_H
|
||||||
|
|
||||||
|
#include "TorControlCommand.h"
|
||||||
|
#include <QList>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class GetConfCommand : public TorControlCommand
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(GetConfCommand)
|
||||||
|
|
||||||
|
Q_PROPERTY(QVariantMap results READ results CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
GetConf,
|
||||||
|
GetInfo
|
||||||
|
};
|
||||||
|
const Type type;
|
||||||
|
|
||||||
|
GetConfCommand(Type type);
|
||||||
|
|
||||||
|
QByteArray build(const QByteArray &key);
|
||||||
|
QByteArray build(const QList<QByteArray> &keys);
|
||||||
|
|
||||||
|
const QVariantMap &results() const { return m_results; }
|
||||||
|
QVariant get(const QByteArray &key) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void onReply(int statusCode, const QByteArray &data);
|
||||||
|
virtual void onDataLine(const QByteArray &data);
|
||||||
|
virtual void onDataFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariantMap m_results;
|
||||||
|
QString m_lastKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GETCONFCOMMAND_H
|
137
retroshare-gui/src/TorControl/HiddenService.cpp
Normal file
137
retroshare-gui/src/TorControl/HiddenService.cpp
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/* 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 "HiddenService.h"
|
||||||
|
#include "TorControl.h"
|
||||||
|
#include "TorSocket.h"
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
#include "Useful.h"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
HiddenService::HiddenService(QObject *parent)
|
||||||
|
: QObject(parent), m_status(NotCreated)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HiddenService::HiddenService(const QString &path, QObject *parent)
|
||||||
|
: QObject(parent), m_dataPath(path), m_status(NotCreated)
|
||||||
|
{
|
||||||
|
/* Set the initial status and, if possible, load the hostname */
|
||||||
|
if (QDir(m_dataPath).exists(QLatin1String("private_key"))) {
|
||||||
|
loadPrivateKey();
|
||||||
|
if (!m_hostname.isEmpty())
|
||||||
|
m_status = Offline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HiddenService::HiddenService(const CryptoKey &privateKey, const QString &path, QObject *parent)
|
||||||
|
: QObject(parent), m_dataPath(path), m_status(NotCreated)
|
||||||
|
{
|
||||||
|
setPrivateKey(privateKey);
|
||||||
|
m_status = Offline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HiddenService::setStatus(Status newStatus)
|
||||||
|
{
|
||||||
|
if (m_status == newStatus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Status old = m_status;
|
||||||
|
m_status = newStatus;
|
||||||
|
|
||||||
|
emit statusChanged(m_status, old);
|
||||||
|
|
||||||
|
if (m_status == Online)
|
||||||
|
emit serviceOnline();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HiddenService::addTarget(const Target &target)
|
||||||
|
{
|
||||||
|
m_targets.append(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HiddenService::addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort)
|
||||||
|
{
|
||||||
|
Target t = { targetAddress, servicePort, targetPort };
|
||||||
|
m_targets.append(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HiddenService::setPrivateKey(const CryptoKey &key)
|
||||||
|
{
|
||||||
|
if (m_privateKey.isLoaded()) {
|
||||||
|
BUG() << "Cannot change the private key on an existing HiddenService";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key.isPrivate()) {
|
||||||
|
BUG() << "Cannot create a hidden service with a public key";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_privateKey = key;
|
||||||
|
m_hostname = m_privateKey.torServiceID() + QStringLiteral(".onion");
|
||||||
|
emit privateKeyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HiddenService::loadPrivateKey()
|
||||||
|
{
|
||||||
|
if (m_privateKey.isLoaded() || m_dataPath.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"), CryptoKey::PrivateKey);
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "Failed to load hidden service key";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hostname = m_privateKey.torServiceID();
|
||||||
|
emit privateKeyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HiddenService::servicePublished()
|
||||||
|
{
|
||||||
|
loadPrivateKey();
|
||||||
|
|
||||||
|
if (m_hostname.isEmpty()) {
|
||||||
|
qDebug() << "Failed to read hidden service hostname";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Hidden service published successfully";
|
||||||
|
setStatus(Online);
|
||||||
|
}
|
||||||
|
|
104
retroshare-gui/src/TorControl/HiddenService.h
Normal file
104
retroshare-gui/src/TorControl/HiddenService.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/* 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 HIDDENSERVICE_H
|
||||||
|
#define HIDDENSERVICE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QList>
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorSocket;
|
||||||
|
|
||||||
|
class HiddenService : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(HiddenService)
|
||||||
|
|
||||||
|
friend class TorControlPrivate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Target
|
||||||
|
{
|
||||||
|
QHostAddress targetAddress;
|
||||||
|
quint16 servicePort, targetPort;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
NotCreated = -1, /* Service has not been created yet */
|
||||||
|
Offline = 0, /* Data exists, but service is not published */
|
||||||
|
Online /* Published */
|
||||||
|
};
|
||||||
|
|
||||||
|
HiddenService(QObject *parent = 0);
|
||||||
|
HiddenService(const QString &dataPath, QObject *parent = 0);
|
||||||
|
HiddenService(const CryptoKey &privateKey, const QString &dataPath = QString(), QObject *parent = 0);
|
||||||
|
|
||||||
|
Status status() const { return m_status; }
|
||||||
|
|
||||||
|
const QString &hostname() const { return m_hostname; }
|
||||||
|
const QString &dataPath() const { return m_dataPath; }
|
||||||
|
|
||||||
|
CryptoKey privateKey() { return m_privateKey; }
|
||||||
|
void setPrivateKey(const CryptoKey &privateKey);
|
||||||
|
|
||||||
|
const QList<Target> &targets() const { return m_targets; }
|
||||||
|
void addTarget(const Target &target);
|
||||||
|
void addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void statusChanged(int newStatus, int oldStatus);
|
||||||
|
void serviceOnline();
|
||||||
|
void privateKeyChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void servicePublished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_dataPath;
|
||||||
|
QList<Target> m_targets;
|
||||||
|
QString m_hostname;
|
||||||
|
Status m_status;
|
||||||
|
CryptoKey m_privateKey;
|
||||||
|
|
||||||
|
void loadPrivateKey();
|
||||||
|
void setStatus(Status newStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HIDDENSERVICE_H
|
84
retroshare-gui/src/TorControl/PendingOperation.cpp
Normal file
84
retroshare-gui/src/TorControl/PendingOperation.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/* 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 "PendingOperation.h"
|
||||||
|
|
||||||
|
PendingOperation::PendingOperation(QObject *parent)
|
||||||
|
: QObject(parent), m_finished(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PendingOperation::isFinished() const
|
||||||
|
{
|
||||||
|
return m_finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PendingOperation::isSuccess() const
|
||||||
|
{
|
||||||
|
return m_finished && m_errorMessage.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PendingOperation::isError() const
|
||||||
|
{
|
||||||
|
return m_finished && !m_errorMessage.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PendingOperation::errorMessage() const
|
||||||
|
{
|
||||||
|
return m_errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingOperation::finishWithError(const QString &message)
|
||||||
|
{
|
||||||
|
if (message.isEmpty())
|
||||||
|
m_errorMessage = QStringLiteral("Unknown Error");
|
||||||
|
m_errorMessage = message;
|
||||||
|
|
||||||
|
if (!m_finished) {
|
||||||
|
m_finished = true;
|
||||||
|
emit finished();
|
||||||
|
emit error(m_errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingOperation::finishWithSuccess()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_errorMessage.isNull());
|
||||||
|
|
||||||
|
if (!m_finished) {
|
||||||
|
m_finished = true;
|
||||||
|
emit finished();
|
||||||
|
if (isSuccess())
|
||||||
|
emit success();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
87
retroshare-gui/src/TorControl/PendingOperation.h
Normal file
87
retroshare-gui/src/TorControl/PendingOperation.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/* 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 PENDINGOPERATION_H
|
||||||
|
#define PENDINGOPERATION_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
/* Represents an asynchronous operation for reporting status
|
||||||
|
*
|
||||||
|
* This class is used for asynchronous operations that report a
|
||||||
|
* status and errors when finished, particularly for exposing them
|
||||||
|
* to QML.
|
||||||
|
*
|
||||||
|
* Subclass PendingOperation to implement your operation's logic.
|
||||||
|
* You also need to handle the object's lifetime, for example by
|
||||||
|
* calling deleteLater() when finished() is emitted.
|
||||||
|
*
|
||||||
|
* PendingOperation will emit finished() and one of success() or
|
||||||
|
* error() when completed.
|
||||||
|
*/
|
||||||
|
class PendingOperation : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool isFinished READ isFinished NOTIFY finished FINAL)
|
||||||
|
Q_PROPERTY(bool isSuccess READ isSuccess NOTIFY success FINAL)
|
||||||
|
Q_PROPERTY(bool isError READ isError NOTIFY error FINAL)
|
||||||
|
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY finished FINAL)
|
||||||
|
|
||||||
|
public:
|
||||||
|
PendingOperation(QObject *parent = 0);
|
||||||
|
|
||||||
|
bool isFinished() const;
|
||||||
|
bool isSuccess() const;
|
||||||
|
bool isError() const;
|
||||||
|
QString errorMessage() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Always emitted once when finished, regardless of status
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
// One of error() or success() is emitted once
|
||||||
|
void error(const QString &errorMessage);
|
||||||
|
void success();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void finishWithError(const QString &errorMessage);
|
||||||
|
void finishWithSuccess();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_finished;
|
||||||
|
QString m_errorMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(PendingOperation*)
|
||||||
|
|
||||||
|
#endif
|
85
retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp
Normal file
85
retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* 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 "ProtocolInfoCommand.h"
|
||||||
|
#include "TorControl.h"
|
||||||
|
#include "StringUtil.h"
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
ProtocolInfoCommand::ProtocolInfoCommand(TorControl *m)
|
||||||
|
: manager(m)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ProtocolInfoCommand::build()
|
||||||
|
{
|
||||||
|
return QByteArray("PROTOCOLINFO 1\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data)
|
||||||
|
{
|
||||||
|
TorControlCommand::onReply(statusCode, data);
|
||||||
|
if (statusCode != 250)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (data.startsWith("AUTH "))
|
||||||
|
{
|
||||||
|
QList<QByteArray> tokens = splitQuotedStrings(data.mid(5), ' ');
|
||||||
|
|
||||||
|
foreach (QByteArray token, tokens)
|
||||||
|
{
|
||||||
|
if (token.startsWith("METHODS="))
|
||||||
|
{
|
||||||
|
QList<QByteArray> textMethods = unquotedString(token.mid(8)).split(',');
|
||||||
|
for (QList<QByteArray>::Iterator it = textMethods.begin(); it != textMethods.end(); ++it)
|
||||||
|
{
|
||||||
|
if (*it == "NULL")
|
||||||
|
m_authMethods |= AuthNull;
|
||||||
|
else if (*it == "HASHEDPASSWORD")
|
||||||
|
m_authMethods |= AuthHashedPassword;
|
||||||
|
else if (*it == "COOKIE")
|
||||||
|
m_authMethods |= AuthCookie;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (token.startsWith("COOKIEFILE="))
|
||||||
|
{
|
||||||
|
m_cookieFile = QString::fromLatin1(unquotedString(token.mid(11)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (data.startsWith("VERSION Tor="))
|
||||||
|
{
|
||||||
|
m_torVersion = QString::fromLatin1(unquotedString(data.mid(12, data.indexOf(' ', 12))));
|
||||||
|
}
|
||||||
|
}
|
78
retroshare-gui/src/TorControl/ProtocolInfoCommand.h
Normal file
78
retroshare-gui/src/TorControl/ProtocolInfoCommand.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/* 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 PROTOCOLINFOCOMMAND_H
|
||||||
|
#define PROTOCOLINFOCOMMAND_H
|
||||||
|
|
||||||
|
#include "TorControlCommand.h"
|
||||||
|
#include <QFlags>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorControl;
|
||||||
|
|
||||||
|
class ProtocolInfoCommand : public TorControlCommand
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(ProtocolInfoCommand)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum AuthMethod
|
||||||
|
{
|
||||||
|
AuthUnknown = 0,
|
||||||
|
AuthNull = 0x1,
|
||||||
|
AuthHashedPassword = 0x2,
|
||||||
|
AuthCookie = 0x4
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(AuthMethods, AuthMethod)
|
||||||
|
|
||||||
|
ProtocolInfoCommand(TorControl *manager);
|
||||||
|
QByteArray build();
|
||||||
|
|
||||||
|
AuthMethods authMethods() const { return m_authMethods; }
|
||||||
|
QString torVersion() const { return m_torVersion; }
|
||||||
|
QString cookieFile() const { return m_cookieFile; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void onReply(int statusCode, const QByteArray &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TorControl *manager;
|
||||||
|
AuthMethods m_authMethods;
|
||||||
|
QString m_torVersion;
|
||||||
|
QString m_cookieFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PROTOCOLINFOCOMMAND_H
|
146
retroshare-gui/src/TorControl/SecureRNG.cpp
Normal file
146
retroshare-gui/src/TorControl/SecureRNG.cpp
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/* 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 "SecureRNG.h"
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <wincrypt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if QT_VERSION >= 0x040700
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool SecureRNG::seed()
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= 0x040700
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
/* RAND_poll is very unreliable on windows; with older versions of OpenSSL,
|
||||||
|
* it can take up to several minutes to run and has been known to crash.
|
||||||
|
* Even newer versions seem to take around 400ms, which is far too long for
|
||||||
|
* interactive startup. Random data from the windows CSP is used as a seed
|
||||||
|
* instead, as it should be very high quality random and fast. */
|
||||||
|
HCRYPTPROV provider = 0;
|
||||||
|
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||||
|
{
|
||||||
|
qWarning() << "Failed to acquire CSP context for RNG seed:" << hex << GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same amount of entropy OpenSSL uses, apparently. */
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
if (!CryptGenRandom(provider, sizeof(buf), reinterpret_cast<BYTE*>(buf)))
|
||||||
|
{
|
||||||
|
qWarning() << "Failed to get entropy from CSP for RNG seed: " << hex << GetLastError();
|
||||||
|
CryptReleaseContext(provider, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptReleaseContext(provider, 0);
|
||||||
|
|
||||||
|
RAND_seed(buf, sizeof(buf));
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
#else
|
||||||
|
if (!RAND_poll())
|
||||||
|
{
|
||||||
|
qWarning() << "OpenSSL RNG seed failed:" << ERR_get_error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if QT_VERSION >= 0x040700
|
||||||
|
qDebug() << "RNG seed took" << timer.elapsed() << "ms";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SecureRNG::random(char *buf, int size)
|
||||||
|
{
|
||||||
|
int r = RAND_bytes(reinterpret_cast<unsigned char*>(buf), size);
|
||||||
|
if (r <= 0)
|
||||||
|
qFatal("RNG failed: %lu", ERR_get_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SecureRNG::random(int size)
|
||||||
|
{
|
||||||
|
QByteArray re(size, 0);
|
||||||
|
random(re.data(), size);
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SecureRNG::randomPrintable(int length)
|
||||||
|
{
|
||||||
|
QByteArray re(length, 0);
|
||||||
|
for (int i = 0; i < re.size(); i++)
|
||||||
|
re[i] = randomInt(95) + 32;
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned SecureRNG::randomInt(unsigned max)
|
||||||
|
{
|
||||||
|
unsigned cutoff = UINT_MAX - (UINT_MAX % max);
|
||||||
|
unsigned value = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
random(reinterpret_cast<char*>(&value), sizeof(value));
|
||||||
|
if (value < cutoff)
|
||||||
|
return value % max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef UINT64_MAX
|
||||||
|
#define UINT64_MAX ((quint64)-1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
quint64 SecureRNG::randomInt64(quint64 max)
|
||||||
|
{
|
||||||
|
quint64 cutoff = UINT64_MAX - (UINT64_MAX % max);
|
||||||
|
quint64 value = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
random(reinterpret_cast<char*>(value), sizeof(value));
|
||||||
|
if (value < cutoff)
|
||||||
|
return value % max;
|
||||||
|
}
|
||||||
|
}
|
51
retroshare-gui/src/TorControl/SecureRNG.h
Normal file
51
retroshare-gui/src/TorControl/SecureRNG.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/* 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 SECURERNG_H
|
||||||
|
#define SECURERNG_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
class SecureRNG
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool seed();
|
||||||
|
|
||||||
|
static void random(char *buf, int size);
|
||||||
|
static QByteArray random(int size);
|
||||||
|
|
||||||
|
static QByteArray randomPrintable(int length);
|
||||||
|
static unsigned randomInt(unsigned max);
|
||||||
|
static quint64 randomInt64(quint64 max);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SECURERNG_H
|
106
retroshare-gui/src/TorControl/SetConfCommand.cpp
Normal file
106
retroshare-gui/src/TorControl/SetConfCommand.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/* 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 "SetConfCommand.h"
|
||||||
|
#include "StringUtil.h"
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
SetConfCommand::SetConfCommand()
|
||||||
|
: m_resetMode(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetConfCommand::setResetMode(bool enabled)
|
||||||
|
{
|
||||||
|
m_resetMode = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetConfCommand::isSuccessful() const
|
||||||
|
{
|
||||||
|
return statusCode() == 250;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SetConfCommand::build(const QByteArray &key, const QByteArray &value)
|
||||||
|
{
|
||||||
|
return build(QList<QPair<QByteArray, QByteArray> >() << qMakePair(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SetConfCommand::build(const QVariantMap &data)
|
||||||
|
{
|
||||||
|
QList<QPair<QByteArray, QByteArray> > out;
|
||||||
|
|
||||||
|
for (QVariantMap::ConstIterator it = data.begin(); it != data.end(); it++) {
|
||||||
|
QByteArray key = it.key().toLatin1();
|
||||||
|
|
||||||
|
if (static_cast<QMetaType::Type>(it.value().type()) == QMetaType::QVariantList) {
|
||||||
|
QVariantList values = it.value().value<QVariantList>();
|
||||||
|
foreach (const QVariant &value, values)
|
||||||
|
out.append(qMakePair(key, value.toString().toLatin1()));
|
||||||
|
} else {
|
||||||
|
out.append(qMakePair(key, it.value().toString().toLatin1()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return build(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SetConfCommand::build(const QList<QPair<QByteArray, QByteArray> > &data)
|
||||||
|
{
|
||||||
|
QByteArray out(m_resetMode ? "RESETCONF" : "SETCONF");
|
||||||
|
|
||||||
|
for (int i = 0; i < data.size(); i++) {
|
||||||
|
out += " " + data[i].first;
|
||||||
|
if (!data[i].second.isEmpty())
|
||||||
|
out += "=" + quotedString(data[i].second);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.append("\r\n");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetConfCommand::onReply(int statusCode, const QByteArray &data)
|
||||||
|
{
|
||||||
|
TorControlCommand::onReply(statusCode, data);
|
||||||
|
if (statusCode != 250)
|
||||||
|
m_errorMessage = QString::fromLatin1(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetConfCommand::onFinished(int statusCode)
|
||||||
|
{
|
||||||
|
TorControlCommand::onFinished(statusCode);
|
||||||
|
if (isSuccessful())
|
||||||
|
emit setConfSucceeded();
|
||||||
|
else
|
||||||
|
emit setConfFailed(statusCode);
|
||||||
|
}
|
||||||
|
|
78
retroshare-gui/src/TorControl/SetConfCommand.h
Normal file
78
retroshare-gui/src/TorControl/SetConfCommand.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/* 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 SETCONFCOMMAND_H
|
||||||
|
#define SETCONFCOMMAND_H
|
||||||
|
|
||||||
|
#include "TorControlCommand.h"
|
||||||
|
#include <QList>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class SetConfCommand : public TorControlCommand
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(SetConfCommand)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString errorMessage READ errorMessage CONSTANT)
|
||||||
|
Q_PROPERTY(bool successful READ isSuccessful CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
SetConfCommand();
|
||||||
|
|
||||||
|
void setResetMode(bool resetMode);
|
||||||
|
|
||||||
|
QByteArray build(const QByteArray &key, const QByteArray &value);
|
||||||
|
QByteArray build(const QVariantMap &data);
|
||||||
|
QByteArray build(const QList<QPair<QByteArray, QByteArray> > &data);
|
||||||
|
|
||||||
|
QString errorMessage() const { return m_errorMessage; }
|
||||||
|
bool isSuccessful() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void setConfSucceeded();
|
||||||
|
void setConfFailed(int code);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_errorMessage;
|
||||||
|
bool m_resetMode;
|
||||||
|
|
||||||
|
virtual void onReply(int statusCode, const QByteArray &data);
|
||||||
|
virtual void onFinished(int statusCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SETCONFCOMMAND_H
|
553
retroshare-gui/src/TorControl/Settings.cpp
Normal file
553
retroshare-gui/src/TorControl/Settings.cpp
Normal file
|
@ -0,0 +1,553 @@
|
||||||
|
/* 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;
|
||||||
|
QString filePath;
|
||||||
|
QString errorMessage;
|
||||||
|
QTimer syncTimer;
|
||||||
|
QJsonObject jsonRoot;
|
||||||
|
SettingsObject *rootObject;
|
||||||
|
|
||||||
|
SettingsFilePrivate(SettingsFile *qp);
|
||||||
|
virtual ~SettingsFilePrivate();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void setError(const QString &message);
|
||||||
|
bool checkDirPermissions(const QString &path);
|
||||||
|
bool readFile();
|
||||||
|
bool writeFile();
|
||||||
|
|
||||||
|
static QStringList splitPath(const QString &input, bool &ok);
|
||||||
|
QJsonValue read(const QJsonObject &base, const QStringList &path);
|
||||||
|
bool write(const QStringList &path, const QJsonValue &value);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void modified(const QStringList &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 QString &filePath)
|
||||||
|
{
|
||||||
|
if (d->filePath == filePath)
|
||||||
|
return hasError();
|
||||||
|
|
||||||
|
d->reset();
|
||||||
|
d->filePath = filePath;
|
||||||
|
|
||||||
|
QFileInfo fileInfo(filePath);
|
||||||
|
QDir dir(fileInfo.path());
|
||||||
|
if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
|
||||||
|
d->setError(QStringLiteral("Cannot create directory: %1").arg(dir.path()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
d->checkDirPermissions(fileInfo.path());
|
||||||
|
|
||||||
|
if (!d->readFile())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString 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"
|
257
retroshare-gui/src/TorControl/Settings.h
Normal file
257
retroshare-gui/src/TorControl/Settings.h
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
/* 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 <QStringList>
|
||||||
|
#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 QString &filePath);
|
||||||
|
|
||||||
|
QString 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);
|
||||||
|
|
||||||
|
QString path() const;
|
||||||
|
void setPath(const QString &path);
|
||||||
|
|
||||||
|
QJsonObject data() const;
|
||||||
|
void setData(const QJsonObject &data);
|
||||||
|
|
||||||
|
Q_INVOKABLE QJsonValue read(const QString &key, const QJsonValue &defaultValue = QJsonValue::Undefined) const;
|
||||||
|
template<typename T> T read(const QString &key) const;
|
||||||
|
Q_INVOKABLE void write(const QString &key, const QJsonValue &value);
|
||||||
|
template<typename T> void write(const QString &key, const T &value);
|
||||||
|
Q_INVOKABLE void unset(const QString &key);
|
||||||
|
|
||||||
|
// const char* key overloads
|
||||||
|
QJsonValue read(const char *key, const QJsonValue &defaultValue = QJsonValue::Undefined) const
|
||||||
|
{
|
||||||
|
return read(QString::fromLatin1(key), defaultValue);
|
||||||
|
}
|
||||||
|
template<typename T> T read(const char *key) const
|
||||||
|
{
|
||||||
|
return read<T>(QString::fromLatin1(key));
|
||||||
|
}
|
||||||
|
void write(const char *key, const QJsonValue &value)
|
||||||
|
{
|
||||||
|
write(QString::fromLatin1(key), value);
|
||||||
|
}
|
||||||
|
template<typename T> void write(const char *key, const T &value)
|
||||||
|
{
|
||||||
|
write<T>(QString::fromLatin1(key), value);
|
||||||
|
}
|
||||||
|
void unset(const char *key)
|
||||||
|
{
|
||||||
|
unset(QString::fromLatin1(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void undefine();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pathChanged();
|
||||||
|
void dataChanged();
|
||||||
|
|
||||||
|
void modified(const QString &path, const QJsonValue &value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SettingsObjectPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> inline void SettingsObject::write(const QString &key, const T &value)
|
||||||
|
{
|
||||||
|
write(key, QJsonValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline QString SettingsObject::read<QString>(const QString &key) const
|
||||||
|
{
|
||||||
|
return read(key).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline QJsonArray SettingsObject::read<QJsonArray>(const QString &key) const
|
||||||
|
{
|
||||||
|
return read(key).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline QJsonObject SettingsObject::read<QJsonObject>(const QString &key) const
|
||||||
|
{
|
||||||
|
return read(key).toObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline double SettingsObject::read<double>(const QString &key) const
|
||||||
|
{
|
||||||
|
return read(key).toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline int SettingsObject::read<int>(const QString &key) const
|
||||||
|
{
|
||||||
|
return read(key).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline bool SettingsObject::read<bool>(const QString &key) const
|
||||||
|
{
|
||||||
|
return read(key).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline QDateTime SettingsObject::read<QDateTime>(const QString &key) const
|
||||||
|
{
|
||||||
|
QString value = read(key).toString();
|
||||||
|
if (value.isEmpty())
|
||||||
|
return QDateTime();
|
||||||
|
return QDateTime::fromString(value, Qt::ISODate).toLocalTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline void SettingsObject::write<QDateTime>(const QString &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 QString &key) const
|
||||||
|
{
|
||||||
|
return Base64Encode(QByteArray::fromBase64(read(key).toString().toLatin1()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline void SettingsObject::write<Base64Encode>(const QString &key, const Base64Encode &value)
|
||||||
|
{
|
||||||
|
write(key, QJsonValue(QString::fromLatin1(value.encoded())));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
118
retroshare-gui/src/TorControl/StringUtil.cpp
Normal file
118
retroshare-gui/src/TorControl/StringUtil.cpp
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/* 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 "StringUtil.h"
|
||||||
|
|
||||||
|
QByteArray quotedString(const QByteArray &string)
|
||||||
|
{
|
||||||
|
QByteArray out;
|
||||||
|
out.reserve(string.size() * 2);
|
||||||
|
|
||||||
|
out.append('"');
|
||||||
|
|
||||||
|
for (int i = 0; i < string.size(); ++i)
|
||||||
|
{
|
||||||
|
switch (string[i])
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
out.append("\\\"");
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
out.append("\\\\");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out.append(string[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.append('"');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray unquotedString(const QByteArray &string)
|
||||||
|
{
|
||||||
|
if (string.size() < 2 || string[0] != '"')
|
||||||
|
return string;
|
||||||
|
|
||||||
|
QByteArray out;
|
||||||
|
out.reserve(string.size() - 2);
|
||||||
|
|
||||||
|
for (int i = 1; i < string.size(); ++i)
|
||||||
|
{
|
||||||
|
switch (string[i])
|
||||||
|
{
|
||||||
|
case '\\':
|
||||||
|
if (++i < string.size())
|
||||||
|
out.append(string[i]);
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
return out;
|
||||||
|
default:
|
||||||
|
out.append(string[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QByteArray> splitQuotedStrings(const QByteArray &input, char separator)
|
||||||
|
{
|
||||||
|
QList<QByteArray> out;
|
||||||
|
bool inquote = false;
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < input.size(); ++i)
|
||||||
|
{
|
||||||
|
switch (input[i])
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
inquote = !inquote;
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
if (inquote)
|
||||||
|
++i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inquote && input[i] == separator)
|
||||||
|
{
|
||||||
|
out.append(input.mid(start, i - start));
|
||||||
|
start = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < input.size())
|
||||||
|
out.append(input.mid(start));
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
46
retroshare-gui/src/TorControl/StringUtil.h
Normal file
46
retroshare-gui/src/TorControl/StringUtil.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/* 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 STRINGUTIL_H
|
||||||
|
#define STRINGUTIL_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
QByteArray quotedString(const QByteArray &string);
|
||||||
|
|
||||||
|
/* Return the unquoted contents of a string, either until an end quote or an unescaped separator character. */
|
||||||
|
QByteArray unquotedString(const QByteArray &string);
|
||||||
|
|
||||||
|
QList<QByteArray> splitQuotedStrings(const QByteArray &input, char separator);
|
||||||
|
|
||||||
|
#endif // STRINGUTIL_H
|
763
retroshare-gui/src/TorControl/TorControl.cpp
Normal file
763
retroshare-gui/src/TorControl/TorControl.cpp
Normal file
|
@ -0,0 +1,763 @@
|
||||||
|
/* 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 "TorControl.h"
|
||||||
|
#include "TorControlSocket.h"
|
||||||
|
#include "HiddenService.h"
|
||||||
|
#include "ProtocolInfoCommand.h"
|
||||||
|
#include "AuthenticateCommand.h"
|
||||||
|
#include "SetConfCommand.h"
|
||||||
|
#include "GetConfCommand.h"
|
||||||
|
#include "AddOnionCommand.h"
|
||||||
|
#include "StringUtil.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "PendingOperation.h"
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
//#include <QQmlEngine>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QSaveFile>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
Tor::TorControl *torControl = 0;
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
namespace Tor {
|
||||||
|
|
||||||
|
class TorControlPrivate : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TorControl *q;
|
||||||
|
|
||||||
|
TorControlSocket *socket;
|
||||||
|
QHostAddress torAddress;
|
||||||
|
QString errorMessage;
|
||||||
|
QString torVersion;
|
||||||
|
QByteArray authPassword;
|
||||||
|
QHostAddress socksAddress;
|
||||||
|
QList<HiddenService*> services;
|
||||||
|
quint16 controlPort, socksPort;
|
||||||
|
TorControl::Status status;
|
||||||
|
TorControl::TorStatus torStatus;
|
||||||
|
QVariantMap bootstrapStatus;
|
||||||
|
bool hasOwnership;
|
||||||
|
|
||||||
|
TorControlPrivate(TorControl *parent);
|
||||||
|
|
||||||
|
void setStatus(TorControl::Status status);
|
||||||
|
void setTorStatus(TorControl::TorStatus status);
|
||||||
|
|
||||||
|
void getTorInfo();
|
||||||
|
void publishServices();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void socketConnected();
|
||||||
|
void socketDisconnected();
|
||||||
|
void socketError();
|
||||||
|
|
||||||
|
void authenticateReply();
|
||||||
|
void protocolInfoReply();
|
||||||
|
void getTorInfoReply();
|
||||||
|
void setError(const QString &message);
|
||||||
|
|
||||||
|
void statusEvent(int code, const QByteArray &data);
|
||||||
|
void updateBootstrap(const QList<QByteArray> &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl::TorControl(QObject *parent)
|
||||||
|
: QObject(parent), d(new TorControlPrivate(this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlPrivate::TorControlPrivate(TorControl *parent)
|
||||||
|
: QObject(parent), q(parent), controlPort(0), socksPort(0),
|
||||||
|
status(TorControl::NotConnected), torStatus(TorControl::TorUnknown),
|
||||||
|
hasOwnership(false)
|
||||||
|
{
|
||||||
|
socket = new TorControlSocket(this);
|
||||||
|
QObject::connect(socket, SIGNAL(connected()), this, SLOT(socketConnected()));
|
||||||
|
QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
|
||||||
|
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError()));
|
||||||
|
QObject::connect(socket, SIGNAL(error(QString)), this, SLOT(setError(QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkProxy TorControl::connectionProxy()
|
||||||
|
{
|
||||||
|
return QNetworkProxy(QNetworkProxy::Socks5Proxy, d->socksAddress.toString(), d->socksPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::setStatus(TorControl::Status n)
|
||||||
|
{
|
||||||
|
if (n == status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TorControl::Status old = status;
|
||||||
|
status = n;
|
||||||
|
|
||||||
|
if (old == TorControl::Error)
|
||||||
|
errorMessage.clear();
|
||||||
|
|
||||||
|
emit q->statusChanged(status, old);
|
||||||
|
|
||||||
|
if (status == TorControl::Connected && old < TorControl::Connected)
|
||||||
|
emit q->connected();
|
||||||
|
else if (status < TorControl::Connected && old >= TorControl::Connected)
|
||||||
|
emit q->disconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::setTorStatus(TorControl::TorStatus n)
|
||||||
|
{
|
||||||
|
if (n == torStatus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TorControl::TorStatus old = torStatus;
|
||||||
|
torStatus = n;
|
||||||
|
emit q->torStatusChanged(torStatus, old);
|
||||||
|
emit q->connectivityChanged();
|
||||||
|
|
||||||
|
if (torStatus == TorControl::TorReady && socksAddress.isNull()) {
|
||||||
|
// Request info again to read the SOCKS port
|
||||||
|
getTorInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::setError(const QString &message)
|
||||||
|
{
|
||||||
|
errorMessage = message;
|
||||||
|
setStatus(TorControl::Error);
|
||||||
|
|
||||||
|
qWarning() << "torctrl: Error:" << errorMessage;
|
||||||
|
|
||||||
|
socket->abort();
|
||||||
|
|
||||||
|
QTimer::singleShot(15000, q, SLOT(reconnect()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl::Status TorControl::status() const
|
||||||
|
{
|
||||||
|
return d->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl::TorStatus TorControl::torStatus() const
|
||||||
|
{
|
||||||
|
return d->torStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorControl::torVersion() const
|
||||||
|
{
|
||||||
|
return d->torVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorControl::errorMessage() const
|
||||||
|
{
|
||||||
|
return d->errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorControl::hasConnectivity() const
|
||||||
|
{
|
||||||
|
return torStatus() == TorReady && !d->socksAddress.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHostAddress TorControl::socksAddress() const
|
||||||
|
{
|
||||||
|
return d->socksAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 TorControl::socksPort() const
|
||||||
|
{
|
||||||
|
return d->socksPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<HiddenService*> TorControl::hiddenServices() const
|
||||||
|
{
|
||||||
|
return d->services;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap TorControl::bootstrapStatus() const
|
||||||
|
{
|
||||||
|
return d->bootstrapStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::setAuthPassword(const QByteArray &password)
|
||||||
|
{
|
||||||
|
d->authPassword = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::connect(const QHostAddress &address, quint16 port)
|
||||||
|
{
|
||||||
|
if (status() > Connecting)
|
||||||
|
{
|
||||||
|
qDebug() << "Ignoring TorControl::connect due to existing connection";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->torAddress = address;
|
||||||
|
d->controlPort = port;
|
||||||
|
d->setTorStatus(TorUnknown);
|
||||||
|
|
||||||
|
bool b = d->socket->blockSignals(true);
|
||||||
|
d->socket->abort();
|
||||||
|
d->socket->blockSignals(b);
|
||||||
|
|
||||||
|
d->setStatus(Connecting);
|
||||||
|
d->socket->connectToHost(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::reconnect()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!d->torAddress.isNull() && d->controlPort);
|
||||||
|
if (d->torAddress.isNull() || !d->controlPort || status() >= Connecting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->setStatus(Connecting);
|
||||||
|
d->socket->connectToHost(d->torAddress, d->controlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::authenticateReply()
|
||||||
|
{
|
||||||
|
AuthenticateCommand *command = qobject_cast<AuthenticateCommand*>(sender());
|
||||||
|
Q_ASSERT(command);
|
||||||
|
Q_ASSERT(status == TorControl::Authenticating);
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!command->isSuccessful()) {
|
||||||
|
setError(command->errorMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "torctrl: Authentication successful";
|
||||||
|
setStatus(TorControl::Connected);
|
||||||
|
|
||||||
|
setTorStatus(TorControl::TorUnknown);
|
||||||
|
|
||||||
|
TorControlCommand *clientEvents = new TorControlCommand;
|
||||||
|
connect(clientEvents, &TorControlCommand::replyLine, this, &TorControlPrivate::statusEvent);
|
||||||
|
socket->registerEvent("STATUS_CLIENT", clientEvents);
|
||||||
|
|
||||||
|
getTorInfo();
|
||||||
|
publishServices();
|
||||||
|
|
||||||
|
// XXX Fix old configurations that would store unwanted options in torrc.
|
||||||
|
// This can be removed some suitable amount of time after 1.0.4.
|
||||||
|
if (hasOwnership)
|
||||||
|
q->saveConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::socketConnected()
|
||||||
|
{
|
||||||
|
Q_ASSERT(status == TorControl::Connecting);
|
||||||
|
|
||||||
|
qDebug() << "torctrl: Connected socket; querying information";
|
||||||
|
setStatus(TorControl::Authenticating);
|
||||||
|
|
||||||
|
ProtocolInfoCommand *command = new ProtocolInfoCommand(q);
|
||||||
|
connect(command, &TorControlCommand::finished, this, &TorControlPrivate::protocolInfoReply);
|
||||||
|
socket->sendCommand(command, command->build());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::socketDisconnected()
|
||||||
|
{
|
||||||
|
/* Clear some internal state */
|
||||||
|
torVersion.clear();
|
||||||
|
socksAddress.clear();
|
||||||
|
socksPort = 0;
|
||||||
|
setTorStatus(TorControl::TorUnknown);
|
||||||
|
|
||||||
|
/* This emits the disconnected() signal as well */
|
||||||
|
setStatus(TorControl::NotConnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::socketError()
|
||||||
|
{
|
||||||
|
setError(QStringLiteral("Connection failed: %1").arg(socket->errorString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::protocolInfoReply()
|
||||||
|
{
|
||||||
|
ProtocolInfoCommand *info = qobject_cast<ProtocolInfoCommand*>(sender());
|
||||||
|
if (!info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
torVersion = info->torVersion();
|
||||||
|
|
||||||
|
if (status == TorControl::Authenticating)
|
||||||
|
{
|
||||||
|
AuthenticateCommand *auth = new AuthenticateCommand;
|
||||||
|
connect(auth, &TorControlCommand::finished, this, &TorControlPrivate::authenticateReply);
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
ProtocolInfoCommand::AuthMethods methods = info->authMethods();
|
||||||
|
|
||||||
|
if (methods.testFlag(ProtocolInfoCommand::AuthNull))
|
||||||
|
{
|
||||||
|
qDebug() << "torctrl: Using null authentication";
|
||||||
|
data = auth->build();
|
||||||
|
}
|
||||||
|
else if (methods.testFlag(ProtocolInfoCommand::AuthCookie) && !info->cookieFile().isEmpty())
|
||||||
|
{
|
||||||
|
QString cookieFile = info->cookieFile();
|
||||||
|
QString cookieError;
|
||||||
|
qDebug() << "torctrl: Using cookie authentication with file" << cookieFile;
|
||||||
|
|
||||||
|
QFile file(cookieFile);
|
||||||
|
if (file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
QByteArray cookie = file.readAll();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
/* Simple test to avoid a vulnerability where any process listening on what we think is
|
||||||
|
* the control port could trick us into sending the contents of an arbitrary file */
|
||||||
|
if (cookie.size() == 32)
|
||||||
|
data = auth->build(cookie);
|
||||||
|
else
|
||||||
|
cookieError = QStringLiteral("Unexpected file size");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cookieError = file.errorString();
|
||||||
|
|
||||||
|
if (!cookieError.isNull() || data.isNull())
|
||||||
|
{
|
||||||
|
/* If we know a password and password authentication is allowed, try using that instead.
|
||||||
|
* This is a strange corner case that will likely never happen in a normal configuration,
|
||||||
|
* but it has happened. */
|
||||||
|
if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword) && !authPassword.isEmpty())
|
||||||
|
{
|
||||||
|
qDebug() << "torctrl: Unable to read authentication cookie file:" << cookieError;
|
||||||
|
goto usePasswordAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
setError(QStringLiteral("Unable to read authentication cookie file: %1").arg(cookieError));
|
||||||
|
delete auth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword) && !authPassword.isEmpty())
|
||||||
|
{
|
||||||
|
usePasswordAuth:
|
||||||
|
qDebug() << "torctrl: Using hashed password authentication";
|
||||||
|
data = auth->build(authPassword);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword))
|
||||||
|
setError(QStringLiteral("Tor requires a control password to connect, but no password is configured."));
|
||||||
|
else
|
||||||
|
setError(QStringLiteral("Tor is not configured to accept any supported authentication methods."));
|
||||||
|
delete auth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket->sendCommand(auth, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::getTorInfo()
|
||||||
|
{
|
||||||
|
Q_ASSERT(q->isConnected());
|
||||||
|
|
||||||
|
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetInfo);
|
||||||
|
connect(command, &TorControlCommand::finished, this, &TorControlPrivate::getTorInfoReply);
|
||||||
|
|
||||||
|
QList<QByteArray> keys;
|
||||||
|
keys << QByteArray("status/circuit-established") << QByteArray("status/bootstrap-phase");
|
||||||
|
|
||||||
|
/* If these are set in the config, they override the automatic behavior. */
|
||||||
|
SettingsObject settings(QStringLiteral("tor"));
|
||||||
|
QHostAddress forceAddress(settings.read("socksAddress").toString());
|
||||||
|
quint16 port = (quint16)settings.read("socksPort").toInt();
|
||||||
|
|
||||||
|
if (!forceAddress.isNull() && port) {
|
||||||
|
qDebug() << "torctrl: Using manually specified SOCKS connection settings";
|
||||||
|
socksAddress = forceAddress;
|
||||||
|
socksPort = port;
|
||||||
|
emit q->connectivityChanged();
|
||||||
|
} else
|
||||||
|
keys << QByteArray("net/listeners/socks");
|
||||||
|
|
||||||
|
socket->sendCommand(command, command->build(keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::getTorInfoReply()
|
||||||
|
{
|
||||||
|
GetConfCommand *command = qobject_cast<GetConfCommand*>(sender());
|
||||||
|
if (!command || !q->isConnected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QList<QByteArray> listenAddresses = splitQuotedStrings(command->get(QByteArray("net/listeners/socks")).toString().toLatin1(), ' ');
|
||||||
|
for (QList<QByteArray>::Iterator it = listenAddresses.begin(); it != listenAddresses.end(); ++it) {
|
||||||
|
QByteArray value = unquotedString(*it);
|
||||||
|
int sepp = value.indexOf(':');
|
||||||
|
QHostAddress address(QString::fromLatin1(value.mid(0, sepp)));
|
||||||
|
quint16 port = (quint16)value.mid(sepp+1).toUInt();
|
||||||
|
|
||||||
|
/* Use the first address that matches the one used for this control connection. If none do,
|
||||||
|
* just use the first address and rely on the user to reconfigure if necessary (not a problem;
|
||||||
|
* their setup is already very customized) */
|
||||||
|
if (socksAddress.isNull() || address == socket->peerAddress()) {
|
||||||
|
socksAddress = address;
|
||||||
|
socksPort = port;
|
||||||
|
if (address == socket->peerAddress())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It is not immediately an error to have no SOCKS address; when DisableNetwork is set there won't be a
|
||||||
|
* listener yet. To handle that situation, we'll try to read the socks address again when TorReady state
|
||||||
|
* is reached. */
|
||||||
|
if (!socksAddress.isNull()) {
|
||||||
|
qDebug().nospace() << "torctrl: SOCKS address is " << socksAddress.toString() << ":" << socksPort;
|
||||||
|
emit q->connectivityChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command->get(QByteArray("status/circuit-established")).toInt() == 1) {
|
||||||
|
qDebug() << "torctrl: Tor indicates that circuits have been established; state is TorReady";
|
||||||
|
setTorStatus(TorControl::TorReady);
|
||||||
|
} else {
|
||||||
|
setTorStatus(TorControl::TorOffline);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray bootstrap = command->get(QByteArray("status/bootstrap-phase")).toString().toLatin1();
|
||||||
|
if (!bootstrap.isEmpty())
|
||||||
|
updateBootstrap(splitQuotedStrings(bootstrap, ' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::addHiddenService(HiddenService *service)
|
||||||
|
{
|
||||||
|
if (d->services.contains(service))
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->services.append(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::publishServices()
|
||||||
|
{
|
||||||
|
Q_ASSERT(q->isConnected());
|
||||||
|
if (services.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SettingsObject settings(QStringLiteral("tor"));
|
||||||
|
if (settings.read("neverPublishServices").toBool())
|
||||||
|
{
|
||||||
|
qDebug() << "torctrl: Skipping service publication because neverPublishService is enabled";
|
||||||
|
|
||||||
|
/* Call servicePublished under the assumption that they're published externally. */
|
||||||
|
for (QList<HiddenService*>::Iterator it = services.begin(); it != services.end(); ++it)
|
||||||
|
(*it)->servicePublished();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->torVersionAsNewAs(QStringLiteral("0.2.7"))) {
|
||||||
|
foreach (HiddenService *service, services) {
|
||||||
|
if (service->hostname().isEmpty())
|
||||||
|
qDebug() << "torctrl: Creating a new hidden service";
|
||||||
|
else
|
||||||
|
qDebug() << "torctrl: Publishing hidden service" << service->hostname();
|
||||||
|
AddOnionCommand *onionCommand = new AddOnionCommand(service);
|
||||||
|
QObject::connect(onionCommand, &AddOnionCommand::succeeded, service, &HiddenService::servicePublished);
|
||||||
|
socket->sendCommand(onionCommand, onionCommand->build());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "torctrl: Using legacy SETCONF hidden service configuration for tor" << torVersion;
|
||||||
|
SetConfCommand *command = new SetConfCommand;
|
||||||
|
QList<QPair<QByteArray,QByteArray> > torConfig;
|
||||||
|
|
||||||
|
foreach (HiddenService *service, services)
|
||||||
|
{
|
||||||
|
if (service->dataPath().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (service->privateKey().isLoaded() && !QFile::exists(service->dataPath() + QStringLiteral("/private_key"))) {
|
||||||
|
// This case can happen if tor is downgraded after the profile is created
|
||||||
|
qWarning() << "Cannot publish ephemeral hidden services with this version of tor; skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "torctrl: Configuring hidden service at" << service->dataPath();
|
||||||
|
|
||||||
|
QDir dir(service->dataPath());
|
||||||
|
torConfig.append(qMakePair(QByteArray("HiddenServiceDir"), dir.absolutePath().toLocal8Bit()));
|
||||||
|
|
||||||
|
const QList<HiddenService::Target> &targets = service->targets();
|
||||||
|
for (QList<HiddenService::Target>::ConstIterator tit = targets.begin(); tit != targets.end(); ++tit)
|
||||||
|
{
|
||||||
|
QString target = QString::fromLatin1("%1 %2:%3").arg(tit->servicePort)
|
||||||
|
.arg(tit->targetAddress.toString())
|
||||||
|
.arg(tit->targetPort);
|
||||||
|
torConfig.append(qMakePair(QByteArray("HiddenServicePort"), target.toLatin1()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(command, &SetConfCommand::setConfSucceeded, service, &HiddenService::servicePublished);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!torConfig.isEmpty())
|
||||||
|
socket->sendCommand(command, command->build(torConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::shutdown()
|
||||||
|
{
|
||||||
|
if (!hasOwnership()) {
|
||||||
|
qWarning() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->socket->sendCommand("SIGNAL SHUTDOWN\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::shutdownSync()
|
||||||
|
{
|
||||||
|
if (!hasOwnership()) {
|
||||||
|
qWarning() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown();
|
||||||
|
while (d->socket->bytesToWrite())
|
||||||
|
{
|
||||||
|
if (!d->socket->waitForBytesWritten(5000))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::statusEvent(int code, const QByteArray &data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(code);
|
||||||
|
|
||||||
|
QList<QByteArray> tokens = splitQuotedStrings(data.trimmed(), ' ');
|
||||||
|
if (tokens.size() < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug() << "torctrl: status event:" << data.trimmed();
|
||||||
|
|
||||||
|
if (tokens[2] == "CIRCUIT_ESTABLISHED") {
|
||||||
|
setTorStatus(TorControl::TorReady);
|
||||||
|
} else if (tokens[2] == "CIRCUIT_NOT_ESTABLISHED") {
|
||||||
|
setTorStatus(TorControl::TorOffline);
|
||||||
|
} else if (tokens[2] == "BOOTSTRAP") {
|
||||||
|
tokens.takeFirst();
|
||||||
|
updateBootstrap(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlPrivate::updateBootstrap(const QList<QByteArray> &data)
|
||||||
|
{
|
||||||
|
bootstrapStatus.clear();
|
||||||
|
// WARN or NOTICE
|
||||||
|
bootstrapStatus[QStringLiteral("severity")] = data.value(0);
|
||||||
|
for (int i = 1; i < data.size(); i++) {
|
||||||
|
int equals = data[i].indexOf('=');
|
||||||
|
QString key = QString::fromLatin1(data[i].mid(0, equals));
|
||||||
|
QString value;
|
||||||
|
if (equals >= 0)
|
||||||
|
value = QString::fromLatin1(unquotedString(data[i].mid(equals + 1)));
|
||||||
|
bootstrapStatus[key.toLower()] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << bootstrapStatus;
|
||||||
|
emit q->bootstrapStatusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject *TorControl::getConfiguration(const QString &options)
|
||||||
|
{
|
||||||
|
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetConf);
|
||||||
|
d->socket->sendCommand(command, command->build(options.toLatin1()));
|
||||||
|
|
||||||
|
//QQmlEngine::setObjectOwnership(command, QQmlEngine::CppOwnership);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject *TorControl::setConfiguration(const QVariantMap &options)
|
||||||
|
{
|
||||||
|
SetConfCommand *command = new SetConfCommand;
|
||||||
|
command->setResetMode(true);
|
||||||
|
d->socket->sendCommand(command, command->build(options));
|
||||||
|
|
||||||
|
//QQmlEngine::setObjectOwnership(command, QQmlEngine::CppOwnership);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Tor {
|
||||||
|
|
||||||
|
class SaveConfigOperation : public PendingOperation
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SaveConfigOperation(QObject *parent)
|
||||||
|
: PendingOperation(parent), command(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void start(TorControlSocket *socket)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!command);
|
||||||
|
command = new GetConfCommand(GetConfCommand::GetInfo);
|
||||||
|
QObject::connect(command, &TorControlCommand::finished, this, &SaveConfigOperation::configTextReply);
|
||||||
|
socket->sendCommand(command, command->build(QList<QByteArray>() << "config-text" << "config-file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void configTextReply()
|
||||||
|
{
|
||||||
|
Q_ASSERT(command);
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString path = QFile::decodeName(command->get("config-file").toByteArray());
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
finishWithError(QStringLiteral("Cannot write torrc without knowing its path"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out of paranoia, refuse to write any file not named 'torrc', or if the
|
||||||
|
// file doesn't exist
|
||||||
|
QFileInfo fileInfo(path);
|
||||||
|
if (fileInfo.fileName() != QStringLiteral("torrc") || !fileInfo.exists()) {
|
||||||
|
finishWithError(QStringLiteral("Refusing to write torrc to unacceptable path %1").arg(path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSaveFile file(path);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
finishWithError(QStringLiteral("Failed opening torrc file for writing: %1").arg(file.errorString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove these keys when writing torrc; they are set at runtime and contain
|
||||||
|
// absolute paths or port numbers
|
||||||
|
static const char *bannedKeys[] = {
|
||||||
|
"ControlPortWriteToFile",
|
||||||
|
"DataDirectory",
|
||||||
|
"HiddenServiceDir",
|
||||||
|
"HiddenServicePort",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
QVariantList configText = command->get("config-text").toList();
|
||||||
|
foreach (const QVariant &value, configText) {
|
||||||
|
QByteArray line = value.toByteArray();
|
||||||
|
|
||||||
|
bool skip = false;
|
||||||
|
for (const char **key = bannedKeys; *key; key++) {
|
||||||
|
if (line.startsWith(*key)) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
file.write(line);
|
||||||
|
file.write("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.commit()) {
|
||||||
|
finishWithError(QStringLiteral("Failed writing torrc: %1").arg(file.errorString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "torctrl: Wrote torrc file";
|
||||||
|
finishWithSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GetConfCommand *command;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingOperation *TorControl::saveConfiguration()
|
||||||
|
{
|
||||||
|
if (!hasOwnership()) {
|
||||||
|
qWarning() << "torctrl: Ignoring save configuration command for a tor instance I don't own";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveConfigOperation *operation = new SaveConfigOperation(this);
|
||||||
|
QObject::connect(operation, &PendingOperation::finished, operation, &QObject::deleteLater);
|
||||||
|
operation->start(d->socket);
|
||||||
|
|
||||||
|
//QQmlEngine::setObjectOwnership(operation, QQmlEngine::CppOwnership);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorControl::hasOwnership() const
|
||||||
|
{
|
||||||
|
return d->hasOwnership;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::takeOwnership()
|
||||||
|
{
|
||||||
|
d->hasOwnership = true;
|
||||||
|
d->socket->sendCommand("TAKEOWNERSHIP\r\n");
|
||||||
|
|
||||||
|
// Reset PID-based polling
|
||||||
|
QVariantMap options;
|
||||||
|
options[QStringLiteral("__OwningControllerProcess")] = QVariant();
|
||||||
|
setConfiguration(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorControl::torVersionAsNewAs(const QString &match) const
|
||||||
|
{
|
||||||
|
QRegularExpression r(QStringLiteral("[.-]"));
|
||||||
|
QStringList split = torVersion().split(r);
|
||||||
|
QStringList matchSplit = match.split(r);
|
||||||
|
|
||||||
|
for (int i = 0; i < matchSplit.size(); i++) {
|
||||||
|
if (i >= split.size())
|
||||||
|
return false;
|
||||||
|
bool ok1 = false, ok2 = false;
|
||||||
|
int currentVal = split[i].toInt(&ok1);
|
||||||
|
int matchVal = matchSplit[i].toInt(&ok2);
|
||||||
|
if (!ok1 || !ok2)
|
||||||
|
return false;
|
||||||
|
if (currentVal > matchVal)
|
||||||
|
return true;
|
||||||
|
if (currentVal < matchVal)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Versions are equal, up to the length of match
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "TorControl.moc"
|
||||||
|
|
141
retroshare-gui/src/TorControl/TorControl.h
Normal file
141
retroshare-gui/src/TorControl/TorControl.h
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/* 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 TORCONTROL_H
|
||||||
|
#define TORCONTROL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include "PendingOperation.h"
|
||||||
|
|
||||||
|
class QNetworkProxy;
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class HiddenService;
|
||||||
|
class TorControlPrivate;
|
||||||
|
|
||||||
|
class TorControl : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_ENUMS(Status TorStatus)
|
||||||
|
|
||||||
|
// Status of the control connection
|
||||||
|
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
||||||
|
// Status of Tor (and whether it believes it can connect)
|
||||||
|
Q_PROPERTY(TorStatus torStatus READ torStatus NOTIFY torStatusChanged)
|
||||||
|
// Whether it's possible to make a SOCKS connection and connect
|
||||||
|
Q_PROPERTY(bool hasConnectivity READ hasConnectivity NOTIFY connectivityChanged)
|
||||||
|
Q_PROPERTY(QString torVersion READ torVersion NOTIFY connected)
|
||||||
|
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY statusChanged)
|
||||||
|
Q_PROPERTY(QVariantMap bootstrapStatus READ bootstrapStatus NOTIFY bootstrapStatusChanged)
|
||||||
|
Q_PROPERTY(bool hasOwnership READ hasOwnership NOTIFY hasOwnershipChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
Error = -1,
|
||||||
|
NotConnected,
|
||||||
|
Connecting,
|
||||||
|
Authenticating,
|
||||||
|
Connected
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TorStatus
|
||||||
|
{
|
||||||
|
TorUnknown,
|
||||||
|
TorOffline,
|
||||||
|
TorReady
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit TorControl(QObject *parent = 0);
|
||||||
|
|
||||||
|
/* Information */
|
||||||
|
Status status() const;
|
||||||
|
TorStatus torStatus() const;
|
||||||
|
QString torVersion() const;
|
||||||
|
bool torVersionAsNewAs(const QString &version) const;
|
||||||
|
QString errorMessage() const;
|
||||||
|
|
||||||
|
bool hasConnectivity() const;
|
||||||
|
QHostAddress socksAddress() const;
|
||||||
|
quint16 socksPort() const;
|
||||||
|
QNetworkProxy connectionProxy();
|
||||||
|
|
||||||
|
/* Authentication */
|
||||||
|
void setAuthPassword(const QByteArray &password);
|
||||||
|
|
||||||
|
/* Connection */
|
||||||
|
bool isConnected() const { return status() == Connected; }
|
||||||
|
void connect(const QHostAddress &address, quint16 port);
|
||||||
|
|
||||||
|
/* Ownership means that tor is managed by this socket, and we
|
||||||
|
* can shut it down, own its configuration, etc. */
|
||||||
|
bool hasOwnership() const;
|
||||||
|
void takeOwnership();
|
||||||
|
|
||||||
|
/* Hidden Services */
|
||||||
|
QList<HiddenService*> hiddenServices() const;
|
||||||
|
void addHiddenService(HiddenService *service);
|
||||||
|
|
||||||
|
QVariantMap bootstrapStatus() const;
|
||||||
|
Q_INVOKABLE QObject *getConfiguration(const QString &options);
|
||||||
|
Q_INVOKABLE QObject *setConfiguration(const QVariantMap &options);
|
||||||
|
Q_INVOKABLE PendingOperation *saveConfiguration();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void statusChanged(int newStatus, int oldStatus);
|
||||||
|
void torStatusChanged(int newStatus, int oldStatus);
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
void connectivityChanged();
|
||||||
|
void bootstrapStatusChanged();
|
||||||
|
void hasOwnershipChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/* Instruct Tor to shutdown */
|
||||||
|
void shutdown();
|
||||||
|
/* Call shutdown(), and wait synchronously for the command to be written */
|
||||||
|
void shutdownSync();
|
||||||
|
|
||||||
|
void reconnect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TorControlPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern Tor::TorControl *torControl;
|
||||||
|
|
||||||
|
#endif // TORCONTROLMANAGER_H
|
63
retroshare-gui/src/TorControl/TorControlCommand.cpp
Normal file
63
retroshare-gui/src/TorControl/TorControlCommand.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* 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 "TorControlCommand.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
TorControlCommand::TorControlCommand()
|
||||||
|
: m_finalStatus(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlCommand::onReply(int statusCode, const QByteArray &data)
|
||||||
|
{
|
||||||
|
emit replyLine(statusCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlCommand::onFinished(int statusCode)
|
||||||
|
{
|
||||||
|
m_finalStatus = statusCode;
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlCommand::onDataLine(const QByteArray &data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlCommand::onDataFinished()
|
||||||
|
{
|
||||||
|
qWarning() << "torctrl: Unexpected data response for command";
|
||||||
|
}
|
||||||
|
|
70
retroshare-gui/src/TorControl/TorControlCommand.h
Normal file
70
retroshare-gui/src/TorControl/TorControlCommand.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/* 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 TORCONTROLCOMMAND_H
|
||||||
|
#define TORCONTROLCOMMAND_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorControlCommand : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(TorControlCommand)
|
||||||
|
|
||||||
|
friend class TorControlSocket;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TorControlCommand();
|
||||||
|
|
||||||
|
int statusCode() const { return m_finalStatus; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void replyLine(int statusCode, const QByteArray &data);
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void onReply(int statusCode, const QByteArray &data);
|
||||||
|
virtual void onFinished(int statusCode);
|
||||||
|
virtual void onDataLine(const QByteArray &data);
|
||||||
|
virtual void onDataFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_finalStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TORCONTROLCOMMAND_H
|
176
retroshare-gui/src/TorControl/TorControlSocket.cpp
Normal file
176
retroshare-gui/src/TorControl/TorControlSocket.cpp
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/* 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 "TorControlSocket.h"
|
||||||
|
#include "TorControlCommand.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
TorControlSocket::TorControlSocket(QObject *parent)
|
||||||
|
: QTcpSocket(parent), currentCommand(0), inDataReply(false)
|
||||||
|
{
|
||||||
|
connect(this, SIGNAL(readyRead()), this, SLOT(process()));
|
||||||
|
connect(this, SIGNAL(disconnected()), this, SLOT(clear()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlSocket::~TorControlSocket()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlSocket::sendCommand(TorControlCommand *command, const QByteArray &data)
|
||||||
|
{
|
||||||
|
Q_ASSERT(data.endsWith("\r\n"));
|
||||||
|
|
||||||
|
commandQueue.append(command);
|
||||||
|
write(data);
|
||||||
|
|
||||||
|
qDebug() << "torctrl: Sent" << data.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlSocket::registerEvent(const QByteArray &event, TorControlCommand *command)
|
||||||
|
{
|
||||||
|
eventCommands.insert(event, command);
|
||||||
|
|
||||||
|
QByteArray data("SETEVENTS");
|
||||||
|
foreach (const QByteArray &key, eventCommands.keys()) {
|
||||||
|
data += ' ';
|
||||||
|
data += key;
|
||||||
|
}
|
||||||
|
data += "\r\n";
|
||||||
|
|
||||||
|
sendCommand(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlSocket::clear()
|
||||||
|
{
|
||||||
|
qDeleteAll(commandQueue);
|
||||||
|
commandQueue.clear();
|
||||||
|
qDeleteAll(eventCommands);
|
||||||
|
eventCommands.clear();
|
||||||
|
inDataReply = false;
|
||||||
|
currentCommand = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlSocket::setError(const QString &message)
|
||||||
|
{
|
||||||
|
m_errorMessage = message;
|
||||||
|
emit error(message);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlSocket::process()
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
if (!canReadLine())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QByteArray line = readLine(5120);
|
||||||
|
if (!line.endsWith("\r\n")) {
|
||||||
|
setError(QStringLiteral("Invalid control message syntax"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
line.chop(2);
|
||||||
|
|
||||||
|
if (inDataReply) {
|
||||||
|
if (line == ".") {
|
||||||
|
inDataReply = false;
|
||||||
|
if (currentCommand)
|
||||||
|
currentCommand->onDataFinished();
|
||||||
|
currentCommand = 0;
|
||||||
|
} else {
|
||||||
|
if (currentCommand)
|
||||||
|
currentCommand->onDataLine(line);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.size() < 4) {
|
||||||
|
setError(QStringLiteral("Invalid control message syntax"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int statusCode = line.left(3).toInt();
|
||||||
|
char type = line[3];
|
||||||
|
bool isFinalReply = (type == ' ');
|
||||||
|
inDataReply = (type == '+');
|
||||||
|
|
||||||
|
// Trim down to just data
|
||||||
|
line = line.mid(4);
|
||||||
|
|
||||||
|
if (!isFinalReply && !inDataReply && type != '-') {
|
||||||
|
setError(QStringLiteral("Invalid control message syntax"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6xx replies are asynchronous responses
|
||||||
|
if (statusCode >= 600 && statusCode < 700) {
|
||||||
|
if (!currentCommand) {
|
||||||
|
int space = line.indexOf(' ');
|
||||||
|
if (space > 0)
|
||||||
|
currentCommand = eventCommands.value(line.mid(0, space));
|
||||||
|
|
||||||
|
if (!currentCommand) {
|
||||||
|
qWarning() << "torctrl: Ignoring unknown event";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCommand->onReply(statusCode, line);
|
||||||
|
if (isFinalReply) {
|
||||||
|
currentCommand->onFinished(statusCode);
|
||||||
|
currentCommand = 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandQueue.isEmpty()) {
|
||||||
|
qWarning() << "torctrl: Received unexpected data";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlCommand *command = commandQueue.first();
|
||||||
|
if (command)
|
||||||
|
command->onReply(statusCode, line);
|
||||||
|
|
||||||
|
if (inDataReply) {
|
||||||
|
currentCommand = command;
|
||||||
|
} else if (isFinalReply) {
|
||||||
|
commandQueue.takeFirst();
|
||||||
|
if (command) {
|
||||||
|
command->onFinished(statusCode);
|
||||||
|
command->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
retroshare-gui/src/TorControl/TorControlSocket.h
Normal file
77
retroshare-gui/src/TorControl/TorControlSocket.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/* 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 TORCONTROLSOCKET_H
|
||||||
|
#define TORCONTROLSOCKET_H
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorControlCommand;
|
||||||
|
|
||||||
|
class TorControlSocket : public QTcpSocket
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TorControlSocket(QObject *parent = 0);
|
||||||
|
virtual ~TorControlSocket();
|
||||||
|
|
||||||
|
QString errorMessage() const { return m_errorMessage; }
|
||||||
|
|
||||||
|
void registerEvent(const QByteArray &event, TorControlCommand *handler);
|
||||||
|
|
||||||
|
void sendCommand(const QByteArray &data) { sendCommand(0, data); }
|
||||||
|
void sendCommand(TorControlCommand *command, const QByteArray &data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void error(const QString &message);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void process();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QQueue<TorControlCommand*> commandQueue;
|
||||||
|
QHash<QByteArray,TorControlCommand*> eventCommands;
|
||||||
|
QString m_errorMessage;
|
||||||
|
TorControlCommand *currentCommand;
|
||||||
|
bool inDataReply;
|
||||||
|
|
||||||
|
void setError(const QString &message);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TORCONTROLSOCKET_H
|
335
retroshare-gui/src/TorControl/TorManager.cpp
Normal file
335
retroshare-gui/src/TorControl/TorManager.cpp
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
/* 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 <iostream>
|
||||||
|
|
||||||
|
#include "TorManager.h"
|
||||||
|
#include "TorProcess.h"
|
||||||
|
#include "TorControl.h"
|
||||||
|
#include "GetConfCommand.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorManagerPrivate : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TorManager *q;
|
||||||
|
TorProcess *process;
|
||||||
|
TorControl *control;
|
||||||
|
QString dataDir;
|
||||||
|
QStringList logMessages;
|
||||||
|
QString errorMessage;
|
||||||
|
bool configNeeded;
|
||||||
|
|
||||||
|
explicit TorManagerPrivate(TorManager *parent = 0);
|
||||||
|
|
||||||
|
QString torExecutablePath() const;
|
||||||
|
bool createDataDir(const QString &path);
|
||||||
|
bool createDefaultTorrc(const QString &path);
|
||||||
|
|
||||||
|
void setError(const QString &errorMessage);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void processStateChanged(int state);
|
||||||
|
void processErrorChanged(const QString &errorMessage);
|
||||||
|
void processLogMessage(const QString &message);
|
||||||
|
void controlStatusChanged(int status);
|
||||||
|
void getConfFinished();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManager::TorManager(QObject *parent)
|
||||||
|
: QObject(parent), d(new TorManagerPrivate(this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManagerPrivate::TorManagerPrivate(TorManager *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, q(parent)
|
||||||
|
, process(0)
|
||||||
|
, control(new TorControl(this))
|
||||||
|
, configNeeded(false)
|
||||||
|
{
|
||||||
|
connect(control, SIGNAL(statusChanged(int,int)), SLOT(controlStatusChanged(int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManager *TorManager::instance()
|
||||||
|
{
|
||||||
|
static TorManager *p = 0;
|
||||||
|
if (!p)
|
||||||
|
p = new TorManager(qApp);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl *TorManager::control()
|
||||||
|
{
|
||||||
|
return d->control;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorProcess *TorManager::process()
|
||||||
|
{
|
||||||
|
return d->process;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorManager::dataDirectory() const
|
||||||
|
{
|
||||||
|
return d->dataDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::setDataDirectory(const QString &path)
|
||||||
|
{
|
||||||
|
d->dataDir = QDir::fromNativeSeparators(path);
|
||||||
|
if (!d->dataDir.isEmpty() && !d->dataDir.endsWith(QLatin1Char('/')))
|
||||||
|
d->dataDir.append(QLatin1Char('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManager::configurationNeeded() const
|
||||||
|
{
|
||||||
|
return d->configNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TorManager::logMessages() const
|
||||||
|
{
|
||||||
|
return d->logMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManager::hasError() const
|
||||||
|
{
|
||||||
|
return !d->errorMessage.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorManager::errorMessage() const
|
||||||
|
{
|
||||||
|
return d->errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::start()
|
||||||
|
{
|
||||||
|
if (!d->errorMessage.isEmpty()) {
|
||||||
|
d->errorMessage.clear();
|
||||||
|
emit errorChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsObject settings(QStringLiteral("tor"));
|
||||||
|
|
||||||
|
// If a control port is defined by config or environment, skip launching tor
|
||||||
|
if (!settings.read("controlPort").isUndefined() ||
|
||||||
|
!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT"))
|
||||||
|
{
|
||||||
|
QHostAddress address(settings.read("controlAddress").toString());
|
||||||
|
quint16 port = (quint16)settings.read("controlPort").toInt();
|
||||||
|
QByteArray password = settings.read("controlPassword").toString().toLatin1();
|
||||||
|
|
||||||
|
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_HOST"))
|
||||||
|
address = QHostAddress(QString::fromLatin1(qgetenv("TOR_CONTROL_HOST")));
|
||||||
|
|
||||||
|
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT")) {
|
||||||
|
bool ok = false;
|
||||||
|
port = qgetenv("TOR_CONTROL_PORT").toUShort(&ok);
|
||||||
|
if (!ok)
|
||||||
|
port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PASSWD"))
|
||||||
|
password = qgetenv("TOR_CONTROL_PASSWD");
|
||||||
|
|
||||||
|
if (!port) {
|
||||||
|
d->setError(QStringLiteral("Invalid control port settings from environment or configuration"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.isNull())
|
||||||
|
address = QHostAddress::LocalHost;
|
||||||
|
|
||||||
|
d->control->setAuthPassword(password);
|
||||||
|
d->control->connect(address, port);
|
||||||
|
} else {
|
||||||
|
// Launch a bundled Tor instance
|
||||||
|
QString executable = d->torExecutablePath();
|
||||||
|
|
||||||
|
std::cerr << "Executable path: " << executable.toStdString() << std::endl;
|
||||||
|
|
||||||
|
if (executable.isEmpty()) {
|
||||||
|
d->setError(QStringLiteral("Cannot find tor executable"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!d->process) {
|
||||||
|
d->process = new TorProcess(this);
|
||||||
|
connect(d->process, SIGNAL(stateChanged(int)), d, SLOT(processStateChanged(int)));
|
||||||
|
connect(d->process, SIGNAL(errorMessageChanged(QString)), d,
|
||||||
|
SLOT(processErrorChanged(QString)));
|
||||||
|
connect(d->process, SIGNAL(logMessage(QString)), d, SLOT(processLogMessage(QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QFile::exists(d->dataDir) && !d->createDataDir(d->dataDir)) {
|
||||||
|
d->setError(QStringLiteral("Cannot write data location: %1").arg(d->dataDir));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString defaultTorrc = d->dataDir + QStringLiteral("default_torrc");
|
||||||
|
if (!QFile::exists(defaultTorrc) && !d->createDefaultTorrc(defaultTorrc)) {
|
||||||
|
d->setError(QStringLiteral("Cannot write data files: %1").arg(defaultTorrc));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile torrc(d->dataDir + QStringLiteral("torrc"));
|
||||||
|
if (!torrc.exists() || torrc.size() == 0) {
|
||||||
|
d->configNeeded = true;
|
||||||
|
emit configurationNeededChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
d->process->setExecutable(executable);
|
||||||
|
d->process->setDataDir(d->dataDir);
|
||||||
|
d->process->setDefaultTorrc(defaultTorrc);
|
||||||
|
d->process->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::processStateChanged(int state)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << state << TorProcess::Ready << process->controlPassword() << process->controlHost() << process->controlPort();
|
||||||
|
if (state == TorProcess::Ready) {
|
||||||
|
control->setAuthPassword(process->controlPassword());
|
||||||
|
control->connect(process->controlHost(), process->controlPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::processErrorChanged(const QString &errorMessage)
|
||||||
|
{
|
||||||
|
qDebug() << "tor error:" << errorMessage;
|
||||||
|
setError(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::processLogMessage(const QString &message)
|
||||||
|
{
|
||||||
|
qDebug() << "tor:" << message;
|
||||||
|
if (logMessages.size() >= 50)
|
||||||
|
logMessages.takeFirst();
|
||||||
|
logMessages.append(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::controlStatusChanged(int status)
|
||||||
|
{
|
||||||
|
if (status == TorControl::Connected) {
|
||||||
|
if (!configNeeded) {
|
||||||
|
// If DisableNetwork is 1, trigger configurationNeeded
|
||||||
|
connect(control->getConfiguration(QStringLiteral("DisableNetwork")),
|
||||||
|
SIGNAL(finished()), SLOT(getConfFinished()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process) {
|
||||||
|
// Take ownership via this control socket
|
||||||
|
control->takeOwnership();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::getConfFinished()
|
||||||
|
{
|
||||||
|
GetConfCommand *command = qobject_cast<GetConfCommand*>(sender());
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (command->get("DisableNetwork").toInt() == 1 && !configNeeded) {
|
||||||
|
configNeeded = true;
|
||||||
|
emit q->configurationNeededChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorManagerPrivate::torExecutablePath() const
|
||||||
|
{
|
||||||
|
SettingsObject settings(QStringLiteral("tor"));
|
||||||
|
QString path = settings.read("executablePath").toString();
|
||||||
|
if (!path.isEmpty())
|
||||||
|
return path;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QString filename(QStringLiteral("/tor.exe"));
|
||||||
|
#else
|
||||||
|
QString filename(QStringLiteral("/tor"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
path = qApp->applicationDirPath();
|
||||||
|
if (QFile::exists(path + filename))
|
||||||
|
return path + filename;
|
||||||
|
|
||||||
|
#ifdef BUNDLED_TOR_PATH
|
||||||
|
path = QStringLiteral(BUNDLED_TOR_PATH);
|
||||||
|
if (QFile::exists(path + filename))
|
||||||
|
return path + filename;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Try $PATH
|
||||||
|
return filename.mid(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManagerPrivate::createDataDir(const QString &path)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
return dir.mkpath(QStringLiteral("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManagerPrivate::createDefaultTorrc(const QString &path)
|
||||||
|
{
|
||||||
|
static const char defaultTorrcContent[] =
|
||||||
|
"SocksPort auto\n"
|
||||||
|
"AvoidDiskWrites 1\n"
|
||||||
|
"DisableNetwork 1\n"
|
||||||
|
"__ReloadTorrcOnSIGHUP 0\n";
|
||||||
|
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::WriteOnly))
|
||||||
|
return false;
|
||||||
|
if (file.write(defaultTorrcContent) < 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::setError(const QString &message)
|
||||||
|
{
|
||||||
|
errorMessage = message;
|
||||||
|
emit q->errorChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "TorManager.moc"
|
||||||
|
|
94
retroshare-gui/src/TorControl/TorManager.h
Normal file
94
retroshare-gui/src/TorControl/TorManager.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This code has been further modified to fit Retroshare context.
|
||||||
|
|
||||||
|
#ifndef TORMANAGER_H
|
||||||
|
#define TORMANAGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorProcess;
|
||||||
|
class TorControl;
|
||||||
|
class TorManagerPrivate;
|
||||||
|
|
||||||
|
/* Run/connect to an instance of Tor according to configuration, and manage
|
||||||
|
* UI interaction, first time configuration, etc. */
|
||||||
|
class TorManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool configurationNeeded READ configurationNeeded NOTIFY configurationNeededChanged)
|
||||||
|
Q_PROPERTY(QStringList logMessages READ logMessages CONSTANT)
|
||||||
|
Q_PROPERTY(Tor::TorProcess* process READ process CONSTANT)
|
||||||
|
Q_PROPERTY(Tor::TorControl* control READ control CONSTANT)
|
||||||
|
Q_PROPERTY(bool hasError READ hasError NOTIFY errorChanged)
|
||||||
|
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorChanged)
|
||||||
|
Q_PROPERTY(QString dataDirectory READ dataDirectory WRITE setDataDirectory)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TorManager(QObject *parent = 0);
|
||||||
|
static TorManager *instance();
|
||||||
|
|
||||||
|
TorProcess *process();
|
||||||
|
TorControl *control();
|
||||||
|
|
||||||
|
QString dataDirectory() const;
|
||||||
|
void setDataDirectory(const QString &path);
|
||||||
|
|
||||||
|
// True on first run or when the Tor configuration wizard needs to be shown
|
||||||
|
bool configurationNeeded() const;
|
||||||
|
|
||||||
|
QStringList logMessages() const;
|
||||||
|
|
||||||
|
bool hasError() const;
|
||||||
|
QString errorMessage() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void start();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void configurationNeededChanged();
|
||||||
|
void errorChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TorManagerPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#
|
311
retroshare-gui/src/TorControl/TorProcess.cpp
Normal file
311
retroshare-gui/src/TorControl/TorProcess.cpp
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
/* 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 "TorProcess_p.h"
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
#include "SecureRNG.h"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
TorProcess::TorProcess(QObject *parent)
|
||||||
|
: QObject(parent), d(new TorProcessPrivate(this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorProcess::~TorProcess()
|
||||||
|
{
|
||||||
|
if (state() > NotStarted)
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
TorProcessPrivate::TorProcessPrivate(TorProcess *q)
|
||||||
|
: QObject(q), q(q), state(TorProcess::NotStarted), controlPort(0), controlPortAttempts(0)
|
||||||
|
{
|
||||||
|
connect(&process, &QProcess::started, this, &TorProcessPrivate::processStarted);
|
||||||
|
connect(&process, (void (QProcess::*)(int, QProcess::ExitStatus))&QProcess::finished,
|
||||||
|
this, &TorProcessPrivate::processFinished);
|
||||||
|
connect(&process, (void (QProcess::*)(QProcess::ProcessError))&QProcess::error,
|
||||||
|
this, &TorProcessPrivate::processError);
|
||||||
|
connect(&process, &QProcess::readyRead, this, &TorProcessPrivate::processReadable);
|
||||||
|
|
||||||
|
controlPortTimer.setInterval(500);
|
||||||
|
connect(&controlPortTimer, &QTimer::timeout, this, &TorProcessPrivate::tryReadControlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorProcess::executable() const
|
||||||
|
{
|
||||||
|
return d->executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setExecutable(const QString &path)
|
||||||
|
{
|
||||||
|
d->executable = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorProcess::dataDir() const
|
||||||
|
{
|
||||||
|
return d->dataDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setDataDir(const QString &path)
|
||||||
|
{
|
||||||
|
d->dataDir = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorProcess::defaultTorrc() const
|
||||||
|
{
|
||||||
|
return d->defaultTorrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setDefaultTorrc(const QString &path)
|
||||||
|
{
|
||||||
|
d->defaultTorrc = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TorProcess::extraSettings() const
|
||||||
|
{
|
||||||
|
return d->extraSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setExtraSettings(const QStringList &settings)
|
||||||
|
{
|
||||||
|
d->extraSettings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorProcess::State TorProcess::state() const
|
||||||
|
{
|
||||||
|
return d->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorProcess::errorMessage() const
|
||||||
|
{
|
||||||
|
return d->errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::start()
|
||||||
|
{
|
||||||
|
if (state() > NotStarted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->errorMessage.clear();
|
||||||
|
|
||||||
|
if (d->executable.isEmpty() || d->dataDir.isEmpty()) {
|
||||||
|
d->errorMessage = QStringLiteral("Tor executable and data directory not specified");
|
||||||
|
d->state = Failed;
|
||||||
|
emit errorMessageChanged(d->errorMessage);
|
||||||
|
emit stateChanged(d->state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!d->ensureFilesExist()) {
|
||||||
|
d->state = Failed;
|
||||||
|
emit errorMessageChanged(d->errorMessage);
|
||||||
|
emit stateChanged(d->state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray password = controlPassword();
|
||||||
|
QByteArray hashedPassword = torControlHashedPassword(password);
|
||||||
|
if (password.isEmpty() || hashedPassword.isEmpty()) {
|
||||||
|
d->errorMessage = QStringLiteral("Random password generation failed");
|
||||||
|
d->state = Failed;
|
||||||
|
emit errorMessageChanged(d->errorMessage);
|
||||||
|
emit stateChanged(d->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList args;
|
||||||
|
if (!d->defaultTorrc.isEmpty())
|
||||||
|
args << QStringLiteral("--defaults-torrc") << d->defaultTorrc;
|
||||||
|
args << QStringLiteral("-f") << d->torrcPath();
|
||||||
|
args << QStringLiteral("DataDirectory") << d->dataDir;
|
||||||
|
args << QStringLiteral("HashedControlPassword") << QString::fromLatin1(hashedPassword);
|
||||||
|
args << QStringLiteral("ControlPort") << QStringLiteral("auto");
|
||||||
|
args << QStringLiteral("ControlPortWriteToFile") << d->controlPortFilePath();
|
||||||
|
args << QStringLiteral("__OwningControllerProcess") << QString::number(qApp->applicationPid());
|
||||||
|
args << d->extraSettings;
|
||||||
|
|
||||||
|
d->state = Starting;
|
||||||
|
emit stateChanged(d->state);
|
||||||
|
|
||||||
|
if (QFile::exists(d->controlPortFilePath()))
|
||||||
|
QFile::remove(d->controlPortFilePath());
|
||||||
|
d->controlPort = 0;
|
||||||
|
d->controlHost.clear();
|
||||||
|
|
||||||
|
d->process.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
d->process.start(d->executable, args, QIODevice::ReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::stop()
|
||||||
|
{
|
||||||
|
if (state() < Starting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->controlPortTimer.stop();
|
||||||
|
|
||||||
|
if (d->process.state() == QProcess::Starting)
|
||||||
|
d->process.waitForStarted(2000);
|
||||||
|
|
||||||
|
d->state = NotStarted;
|
||||||
|
|
||||||
|
// Windows can't terminate the process well, but Tor will clean itself up
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
if (d->process.state() == QProcess::Running) {
|
||||||
|
d->process.terminate();
|
||||||
|
if (!d->process.waitForFinished(5000)) {
|
||||||
|
qWarning() << "Tor process" << d->process.pid() << "did not respond to terminate, killing...";
|
||||||
|
d->process.kill();
|
||||||
|
if (!d->process.waitForFinished(2000)) {
|
||||||
|
qCritical() << "Tor process" << d->process.pid() << "did not respond to kill!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
emit stateChanged(d->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray TorProcess::controlPassword()
|
||||||
|
{
|
||||||
|
if (d->controlPassword.isEmpty())
|
||||||
|
d->controlPassword = SecureRNG::randomPrintable(16);
|
||||||
|
return d->controlPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHostAddress TorProcess::controlHost()
|
||||||
|
{
|
||||||
|
return d->controlHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 TorProcess::controlPort()
|
||||||
|
{
|
||||||
|
return d->controlPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorProcessPrivate::ensureFilesExist()
|
||||||
|
{
|
||||||
|
QFile torrc(torrcPath());
|
||||||
|
if (!torrc.exists()) {
|
||||||
|
QDir dir(dataDir);
|
||||||
|
if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
|
||||||
|
errorMessage = QStringLiteral("Cannot create Tor data directory: %1").arg(dataDir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!torrc.open(QIODevice::ReadWrite)) {
|
||||||
|
errorMessage = QStringLiteral("Cannot create Tor configuration file: %1").arg(torrcPath());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorProcessPrivate::torrcPath() const
|
||||||
|
{
|
||||||
|
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("torrc");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorProcessPrivate::controlPortFilePath() const
|
||||||
|
{
|
||||||
|
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("control-port");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcessPrivate::processStarted()
|
||||||
|
{
|
||||||
|
state = TorProcess::Connecting;
|
||||||
|
emit q->stateChanged(state);
|
||||||
|
|
||||||
|
controlPortAttempts = 0;
|
||||||
|
controlPortTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcessPrivate::processFinished()
|
||||||
|
{
|
||||||
|
if (state < TorProcess::Starting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
controlPortTimer.stop();
|
||||||
|
errorMessage = process.errorString();
|
||||||
|
if (errorMessage.isEmpty())
|
||||||
|
errorMessage = QStringLiteral("Process exited unexpectedly (code %1)").arg(process.exitCode());
|
||||||
|
state = TorProcess::Failed;
|
||||||
|
emit q->errorMessageChanged(errorMessage);
|
||||||
|
emit q->stateChanged(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcessPrivate::processError(QProcess::ProcessError error)
|
||||||
|
{
|
||||||
|
if (error == QProcess::FailedToStart || error == QProcess::Crashed)
|
||||||
|
processFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcessPrivate::processReadable()
|
||||||
|
{
|
||||||
|
while (process.bytesAvailable() > 0) {
|
||||||
|
QByteArray line = process.readLine(2048).trimmed();
|
||||||
|
if (!line.isEmpty())
|
||||||
|
emit q->logMessage(QString::fromLatin1(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcessPrivate::tryReadControlPort()
|
||||||
|
{
|
||||||
|
QFile file(controlPortFilePath());
|
||||||
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
|
QByteArray data = file.readLine().trimmed();
|
||||||
|
|
||||||
|
int p;
|
||||||
|
if (data.startsWith("PORT=") && (p = data.lastIndexOf(':')) > 0) {
|
||||||
|
controlHost = QHostAddress(QString::fromLatin1(data.mid(5, p - 5)));
|
||||||
|
controlPort = data.mid(p+1).toUShort();
|
||||||
|
|
||||||
|
if (!controlHost.isNull() && controlPort > 0) {
|
||||||
|
controlPortTimer.stop();
|
||||||
|
state = TorProcess::Ready;
|
||||||
|
emit q->stateChanged(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++controlPortAttempts * controlPortTimer.interval() > 10000) {
|
||||||
|
errorMessage = QStringLiteral("No control port available after launching process");
|
||||||
|
state = TorProcess::Failed;
|
||||||
|
emit q->errorMessageChanged(errorMessage);
|
||||||
|
emit q->stateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
100
retroshare-gui/src/TorControl/TorProcess.h
Normal file
100
retroshare-gui/src/TorControl/TorProcess.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/* 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 TORPROCESS_H
|
||||||
|
#define TORPROCESS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorProcessPrivate;
|
||||||
|
|
||||||
|
/* Launches and controls a Tor instance with behavior suitable for bundling
|
||||||
|
* an instance with the application. */
|
||||||
|
class TorProcess : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_ENUMS(State)
|
||||||
|
|
||||||
|
Q_PROPERTY(State state READ state NOTIFY stateChanged)
|
||||||
|
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum State {
|
||||||
|
Failed = -1,
|
||||||
|
NotStarted,
|
||||||
|
Starting,
|
||||||
|
Connecting,
|
||||||
|
Ready
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit TorProcess(QObject *parent = 0);
|
||||||
|
virtual ~TorProcess();
|
||||||
|
|
||||||
|
QString executable() const;
|
||||||
|
void setExecutable(const QString &path);
|
||||||
|
|
||||||
|
QString dataDir() const;
|
||||||
|
void setDataDir(const QString &path);
|
||||||
|
|
||||||
|
QString defaultTorrc() const;
|
||||||
|
void setDefaultTorrc(const QString &path);
|
||||||
|
|
||||||
|
QStringList extraSettings() const;
|
||||||
|
void setExtraSettings(const QStringList &settings);
|
||||||
|
|
||||||
|
State state() const;
|
||||||
|
QString errorMessage() const;
|
||||||
|
QHostAddress controlHost();
|
||||||
|
quint16 controlPort();
|
||||||
|
QByteArray controlPassword();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void stateChanged(int newState);
|
||||||
|
void errorMessageChanged(const QString &errorMessage);
|
||||||
|
void logMessage(const QString &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TorProcessPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
79
retroshare-gui/src/TorControl/TorProcess_p.h
Normal file
79
retroshare-gui/src/TorControl/TorProcess_p.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/* 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 TORPROCESS_P_H
|
||||||
|
#define TORPROCESS_P_H
|
||||||
|
|
||||||
|
#include "TorProcess.h"
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace Tor {
|
||||||
|
|
||||||
|
class TorProcessPrivate : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TorProcess *q;
|
||||||
|
QProcess process;
|
||||||
|
QString executable;
|
||||||
|
QString dataDir;
|
||||||
|
QString defaultTorrc;
|
||||||
|
QStringList extraSettings;
|
||||||
|
TorProcess::State state;
|
||||||
|
QString errorMessage;
|
||||||
|
QHostAddress controlHost;
|
||||||
|
quint16 controlPort;
|
||||||
|
QByteArray controlPassword;
|
||||||
|
|
||||||
|
QTimer controlPortTimer;
|
||||||
|
int controlPortAttempts;
|
||||||
|
|
||||||
|
TorProcessPrivate(TorProcess *q);
|
||||||
|
|
||||||
|
QString torrcPath() const;
|
||||||
|
QString controlPortFilePath() const;
|
||||||
|
bool ensureFilesExist();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void processStarted();
|
||||||
|
void processFinished();
|
||||||
|
void processError(QProcess::ProcessError error);
|
||||||
|
void processReadable();
|
||||||
|
void tryReadControlPort();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
155
retroshare-gui/src/TorControl/TorSocket.cpp
Normal file
155
retroshare-gui/src/TorControl/TorSocket.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/* 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 "TorSocket.h"
|
||||||
|
#include "TorControl.h"
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
TorSocket::TorSocket(QObject *parent)
|
||||||
|
: QTcpSocket(parent)
|
||||||
|
, m_port(0)
|
||||||
|
, m_reconnectEnabled(true)
|
||||||
|
, m_maxInterval(900)
|
||||||
|
, m_connectAttempts(0)
|
||||||
|
{
|
||||||
|
connect(torControl, SIGNAL(connectivityChanged()), SLOT(connectivityChanged()));
|
||||||
|
connect(&m_connectTimer, SIGNAL(timeout()), SLOT(reconnect()));
|
||||||
|
connect(this, SIGNAL(disconnected()), SLOT(onFailed()));
|
||||||
|
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onFailed()));
|
||||||
|
|
||||||
|
m_connectTimer.setSingleShot(true);
|
||||||
|
connectivityChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
TorSocket::~TorSocket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::setReconnectEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled == m_reconnectEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_reconnectEnabled = enabled;
|
||||||
|
if (m_reconnectEnabled) {
|
||||||
|
m_connectAttempts = 0;
|
||||||
|
reconnect();
|
||||||
|
} else {
|
||||||
|
m_connectTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::setMaxAttemptInterval(int interval)
|
||||||
|
{
|
||||||
|
m_maxInterval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::resetAttempts()
|
||||||
|
{
|
||||||
|
m_connectAttempts = 0;
|
||||||
|
if (m_connectTimer.isActive()) {
|
||||||
|
m_connectTimer.stop();
|
||||||
|
m_connectTimer.start(reconnectInterval() * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int TorSocket::reconnectInterval()
|
||||||
|
{
|
||||||
|
int delay = 0;
|
||||||
|
if (m_connectAttempts <= 4)
|
||||||
|
delay = 30;
|
||||||
|
else if (m_connectAttempts <= 6)
|
||||||
|
delay = 120;
|
||||||
|
else
|
||||||
|
delay = m_maxInterval;
|
||||||
|
|
||||||
|
return qMin(delay, m_maxInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::reconnect()
|
||||||
|
{
|
||||||
|
if (!torControl->hasConnectivity() || !reconnectEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_connectTimer.stop();
|
||||||
|
if (!m_host.isEmpty() && m_port) {
|
||||||
|
qDebug() << "Attempting reconnection of socket to" << m_host << m_port;
|
||||||
|
connectToHost(m_host, m_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::connectivityChanged()
|
||||||
|
{
|
||||||
|
if (torControl->hasConnectivity()) {
|
||||||
|
setProxy(torControl->connectionProxy());
|
||||||
|
if (state() == QAbstractSocket::UnconnectedState)
|
||||||
|
reconnect();
|
||||||
|
} else {
|
||||||
|
m_connectTimer.stop();
|
||||||
|
m_connectAttempts = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode,
|
||||||
|
NetworkLayerProtocol protocol)
|
||||||
|
{
|
||||||
|
m_host = hostName;
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if (!torControl->hasConnectivity())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (proxy() != torControl->connectionProxy())
|
||||||
|
setProxy(torControl->connectionProxy());
|
||||||
|
|
||||||
|
QAbstractSocket::connectToHost(hostName, port, openMode, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode)
|
||||||
|
{
|
||||||
|
TorSocket::connectToHost(address.toString(), port, openMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorSocket::onFailed()
|
||||||
|
{
|
||||||
|
// Make sure the internal connection to the SOCKS proxy is closed
|
||||||
|
// Otherwise reconnect attempts will fail (#295)
|
||||||
|
close();
|
||||||
|
|
||||||
|
if (reconnectEnabled() && !m_connectTimer.isActive()) {
|
||||||
|
m_connectAttempts++;
|
||||||
|
m_connectTimer.start(reconnectInterval() * 1000);
|
||||||
|
qDebug() << "Reconnecting socket to" << m_host << m_port << "in" << m_connectTimer.interval() / 1000 << "seconds";
|
||||||
|
}
|
||||||
|
}
|
97
retroshare-gui/src/TorControl/TorSocket.h
Normal file
97
retroshare-gui/src/TorControl/TorSocket.h
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/* 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 TORSOCKET_H
|
||||||
|
#define TORSOCKET_H
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace Tor {
|
||||||
|
|
||||||
|
/* Specialized QTcpSocket which makes connections over the SOCKS proxy
|
||||||
|
* from a TorControl instance, automatically attempts reconnections, and
|
||||||
|
* reacts to Tor's connectivity state.
|
||||||
|
*
|
||||||
|
* Use normal QTcpSocket/QAbstractSocket API. When a connection fails, it
|
||||||
|
* will be retried automatically after the correct interval and when
|
||||||
|
* connectivity is available.
|
||||||
|
*
|
||||||
|
* To fully disconnect, destroy the object, or call
|
||||||
|
* setReconnectEnabled(false) and disconnect the socket with
|
||||||
|
* disconnectFromHost or abort.
|
||||||
|
*
|
||||||
|
* The caller is responsible for resetting the attempt counter if a
|
||||||
|
* connection was successful and reconnection will be used again.
|
||||||
|
*/
|
||||||
|
class TorSocket : public QTcpSocket
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TorSocket(QObject *parent = 0);
|
||||||
|
virtual ~TorSocket();
|
||||||
|
|
||||||
|
bool reconnectEnabled() const { return m_reconnectEnabled; }
|
||||||
|
void setReconnectEnabled(bool enabled);
|
||||||
|
int maxAttemptInterval() { return m_maxInterval; }
|
||||||
|
void setMaxAttemptInterval(int interval);
|
||||||
|
void resetAttempts();
|
||||||
|
|
||||||
|
virtual void connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
|
||||||
|
virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);
|
||||||
|
|
||||||
|
QString hostName() const { return m_host; }
|
||||||
|
quint16 port() const { return m_port; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int reconnectInterval();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void reconnect();
|
||||||
|
void connectivityChanged();
|
||||||
|
void onFailed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_host;
|
||||||
|
quint16 m_port;
|
||||||
|
QTimer m_connectTimer;
|
||||||
|
bool m_reconnectEnabled;
|
||||||
|
int m_maxInterval;
|
||||||
|
int m_connectAttempts;
|
||||||
|
|
||||||
|
using QAbstractSocket::connectToHost;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
71
retroshare-gui/src/TorControl/Useful.h
Normal file
71
retroshare-gui/src/TorControl/Useful.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/* 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 UTILS_USEFUL_H
|
||||||
|
#define UTILS_USEFUL_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
/* Print a warning for bug conditions, and assert on a debug build.
|
||||||
|
*
|
||||||
|
* This should be used in place of Q_ASSERT for bug conditions, along
|
||||||
|
* with a proper error case for release-mode builds. For example:
|
||||||
|
*
|
||||||
|
* if (!connection || !user) {
|
||||||
|
* BUG() << "Request" << request << "should have a connection and user";
|
||||||
|
* return false;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Do not confuse bugs with actual error cases; BUG() should never be
|
||||||
|
* triggered unless the code or logic is wrong.
|
||||||
|
*/
|
||||||
|
#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
|
||||||
|
# define BUG() Explode(__FILE__,__LINE__), qWarning() << "BUG:"
|
||||||
|
namespace {
|
||||||
|
class Explode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char *file;
|
||||||
|
int line;
|
||||||
|
Explode(const char *file, int line) : file(file), line(line) { }
|
||||||
|
~Explode() {
|
||||||
|
qt_assert("something broke!", file, line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define BUG() qWarning() << "BUG:"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -46,6 +46,10 @@
|
||||||
#include "lang/languagesupport.h"
|
#include "lang/languagesupport.h"
|
||||||
#include "util/RsGxsUpdateBroadcast.h"
|
#include "util/RsGxsUpdateBroadcast.h"
|
||||||
|
|
||||||
|
#ifdef RETROTOR
|
||||||
|
#include "TorControl/TorManager.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "retroshare/rsidentity.h"
|
#include "retroshare/rsidentity.h"
|
||||||
|
|
||||||
#ifdef SIGFPE_DEBUG
|
#ifdef SIGFPE_DEBUG
|
||||||
|
@ -274,8 +278,26 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
||||||
RshareSettings::Create ();
|
RshareSettings::Create ();
|
||||||
|
|
||||||
/* Setup The GUI Stuff */
|
/* Setup The GUI Stuff */
|
||||||
Rshare rshare(args, argc, argv,
|
Rshare rshare(args, argc, argv, QString::fromUtf8(RsAccounts::ConfigDirectory().c_str()));
|
||||||
QString::fromUtf8(RsAccounts::ConfigDirectory().c_str()));
|
|
||||||
|
#ifdef RETROTOR
|
||||||
|
// Start the Tor engine, and make sure it provides a viable hidden service
|
||||||
|
|
||||||
|
/* Tor control manager */
|
||||||
|
Tor::TorManager *torManager = Tor::TorManager::instance();
|
||||||
|
torManager->setDataDirectory(Rshare::dataDirectory() + QString("/tor/"));
|
||||||
|
//torManager->setDataDirectory(QString("./tor"));//settings->filePath()).path() + QString("/tor/"));
|
||||||
|
|
||||||
|
Tor::TorControl *torControl = torManager->control();
|
||||||
|
torManager->start();
|
||||||
|
|
||||||
|
while(torManager->configurationNeeded())
|
||||||
|
{
|
||||||
|
usleep(1000*1000) ;
|
||||||
|
|
||||||
|
// we should display some configuration window here!
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Start RetroShare */
|
/* Start RetroShare */
|
||||||
QSplashScreen splashScreen(QPixmap(":/images/logo/logo_splash.png")/* , Qt::WindowStaysOnTopHint*/);
|
QSplashScreen splashScreen(QPixmap(":/images/logo/logo_splash.png")/* , Qt::WindowStaysOnTopHint*/);
|
||||||
|
|
|
@ -20,6 +20,9 @@ profiling {
|
||||||
QMAKE_LFLAGS *= -pg
|
QMAKE_LFLAGS *= -pg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retrotor {
|
||||||
|
DEFINES *= RETROTOR
|
||||||
|
}
|
||||||
|
|
||||||
#QMAKE_CFLAGS += -fmudflap
|
#QMAKE_CFLAGS += -fmudflap
|
||||||
#LIBS *= /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflap.a /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflapth.a
|
#LIBS *= /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflap.a /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflapth.a
|
||||||
|
@ -325,6 +328,47 @@ INCLUDEPATH += $$PWD/../../libresapi/src
|
||||||
PRE_TARGETDEPS *= $$OUT_PWD/../../libresapi/src/lib/libresapi.a
|
PRE_TARGETDEPS *= $$OUT_PWD/../../libresapi/src/lib/libresapi.a
|
||||||
LIBS += $$OUT_PWD/../../libresapi/src/lib/libresapi.a
|
LIBS += $$OUT_PWD/../../libresapi/src/lib/libresapi.a
|
||||||
|
|
||||||
|
retrotor {
|
||||||
|
HEADERS += TorControl/AddOnionCommand.h \
|
||||||
|
TorControl/AuthenticateCommand.h \
|
||||||
|
TorControl/GetConfCommand.h \
|
||||||
|
TorControl/HiddenService.h \
|
||||||
|
TorControl/ProtocolInfoCommand.h \
|
||||||
|
TorControl/SetConfCommand.h \
|
||||||
|
TorControl/TorControlCommand.h \
|
||||||
|
TorControl/TorControl.h \
|
||||||
|
TorControl/TorControlSocket.h \
|
||||||
|
TorControl/TorManager.h \
|
||||||
|
TorControl/TorProcess.h \
|
||||||
|
TorControl/TorProcess_p.h \
|
||||||
|
TorControl/TorSocket.h \
|
||||||
|
TorControl/Useful.h \
|
||||||
|
TorControl/CryptoKey.h \
|
||||||
|
TorControl/PendingOperation.h \
|
||||||
|
TorControl/SecureRNG.h \
|
||||||
|
TorControl/Settings.h \
|
||||||
|
TorControl/StringUtil.h \
|
||||||
|
TorControl/TorProcess_p.h
|
||||||
|
|
||||||
|
SOURCES += TorControl/AddOnionCommand.cpp \
|
||||||
|
TorControl/AuthenticateCommand.cpp \
|
||||||
|
TorControl/GetConfCommand.cpp \
|
||||||
|
TorControl/HiddenService.cpp \
|
||||||
|
TorControl/ProtocolInfoCommand.cpp \
|
||||||
|
TorControl/SetConfCommand.cpp \
|
||||||
|
TorControl/TorControlCommand.cpp \
|
||||||
|
TorControl/TorControl.cpp \
|
||||||
|
TorControl/TorControlSocket.cpp \
|
||||||
|
TorControl/TorManager.cpp \
|
||||||
|
TorControl/TorProcess.cpp \
|
||||||
|
TorControl/TorSocket.cpp \
|
||||||
|
TorControl/CryptoKey.cpp \
|
||||||
|
TorControl/PendingOperation.cpp \
|
||||||
|
TorControl/SecureRNG.cpp \
|
||||||
|
TorControl/Settings.cpp \
|
||||||
|
TorControl/StringUtil.cpp
|
||||||
|
}
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
HEADERS += rshare.h \
|
HEADERS += rshare.h \
|
||||||
retroshare-gui/configpage.h \
|
retroshare-gui/configpage.h \
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
CONFIG *= retroshare_gui
|
CONFIG *= retroshare_gui
|
||||||
no_retroshare_gui:CONFIG -= retroshare_gui
|
no_retroshare_gui:CONFIG -= retroshare_gui
|
||||||
|
|
||||||
|
# To build the RetroTor executable, just include the following option
|
||||||
|
|
||||||
|
CONFIG *= retrotor
|
||||||
|
|
||||||
# To disable RetroShare-nogui append the following
|
# To disable RetroShare-nogui append the following
|
||||||
# assignation to qmake command line "CONFIG+=no_retroshare_nogui"
|
# assignation to qmake command line "CONFIG+=no_retroshare_nogui"
|
||||||
CONFIG *= retroshare_nogui
|
CONFIG *= retroshare_nogui
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue