mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-07-29 09:18:45 -04:00
fixed merge with upstream/master
This commit is contained in:
commit
7367fb3e46
284 changed files with 12153 additions and 10835 deletions
|
@ -1,116 +0,0 @@
|
|||
/* 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 "StrUtil.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 += " ";
|
||||
out += m_service->privateKey().bytes();
|
||||
} else {
|
||||
//out += " NEW:RSA1024"; // this is v2. For v3, use NEW:BEST, or NEW:ED25519-V3
|
||||
//out += " NEW:ED25519-V3"; // this is v3.
|
||||
out += " NEW:BEST"; // this is v3, but without control of key type. Generates a RSA1024 key on older Tor versions.
|
||||
}
|
||||
|
||||
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=");
|
||||
const QByteArray sidPrefix("ServiceID=");
|
||||
|
||||
if(data.startsWith("ServiceID=")){
|
||||
QByteArray service_id = data.mid(sidPrefix.size());
|
||||
m_service->setServiceId(service_id);
|
||||
}
|
||||
|
||||
if (data.startsWith(keyPrefix)) {
|
||||
|
||||
QByteArray keyData(data.mid(keyPrefix.size()));
|
||||
CryptoKey key;
|
||||
|
||||
if (!key.loadFromTorMessage(keyData)) {
|
||||
m_errorMessage = QStringLiteral("Key structure check failed");
|
||||
return;
|
||||
}
|
||||
|
||||
m_service->setPrivateKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
void AddOnionCommand::onFinished(int statusCode)
|
||||
{
|
||||
TorControlCommand::onFinished(statusCode);
|
||||
if (isSuccessful())
|
||||
emit succeeded();
|
||||
else
|
||||
emit failed(statusCode);
|
||||
}
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/* 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
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "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);
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,527 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "CryptoKey.h"
|
||||
#include "SecureRNG.h"
|
||||
#include "Useful.h"
|
||||
#include <QtDebug>
|
||||
#include <QFile>
|
||||
#include <QByteArray>
|
||||
#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
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen);
|
||||
bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen);
|
||||
#endif
|
||||
|
||||
CryptoKey::CryptoKey()
|
||||
{
|
||||
}
|
||||
|
||||
CryptoKey::~CryptoKey()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
CryptoKey::Data::~Data()
|
||||
{
|
||||
if (key)
|
||||
{
|
||||
RSA_free(key);
|
||||
key = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CryptoKey::clear()
|
||||
{
|
||||
key_data.clear();
|
||||
}
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CryptoKey::loadFromFile(const QString& path)
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qWarning() << "Failed to open Tor key file " << path << ": " << file.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
|
||||
if(data.contains("-----BEGIN RSA PRIVATE KEY-----"))
|
||||
{
|
||||
std::cerr << "Note: Reading/converting Tor v2 key format." << std::endl;
|
||||
|
||||
// This to be compliant with old format. New format is oblivious to the type of key so we dont need a header
|
||||
data = data.replace("-----BEGIN RSA PRIVATE KEY-----",nullptr);
|
||||
data = data.replace("-----END RSA PRIVATE KEY-----",nullptr);
|
||||
data = data.replace("\n",nullptr);
|
||||
data = data.replace("\t",nullptr);
|
||||
|
||||
data = "RSA1024:"+data;
|
||||
}
|
||||
|
||||
std::cerr << "Have read the following key: " << std::endl;
|
||||
std::cerr << QString(data).toStdString() << std::endl;
|
||||
|
||||
key_data = data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoKey::loadFromTorMessage(const QByteArray& b)
|
||||
{
|
||||
// note: We should probably check the structure a bit more, for security.
|
||||
|
||||
std::cerr << "Loading new key:" << std::endl;
|
||||
|
||||
if(b.startsWith("RSA1024"))
|
||||
std::cerr << " type: RSA-1024 (Tor v2)" << std::endl;
|
||||
else if(b.startsWith("ED25519-V3"))
|
||||
std::cerr << " type: ED25519-V3 (Tor v3)" << std::endl;
|
||||
else if(b.indexOf(':'))
|
||||
{
|
||||
std::cerr << " unknown type, or bad syntax in key: \"" << b.left(b.indexOf(':')).toStdString() << "\". Not accepted." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
key_data = b;
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,106 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef CRYPTOKEY_H
|
||||
#define CRYPTOKEY_H
|
||||
|
||||
#include <QString>
|
||||
#include <QSharedData>
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
|
||||
class CryptoKey
|
||||
{
|
||||
public:
|
||||
enum KeyType {
|
||||
PrivateKey,
|
||||
PublicKey
|
||||
};
|
||||
|
||||
enum KeyFormat {
|
||||
PEM,
|
||||
DER
|
||||
};
|
||||
|
||||
CryptoKey();
|
||||
~CryptoKey();
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM);
|
||||
bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM);
|
||||
#endif
|
||||
bool loadFromFile(const QString &path);
|
||||
void clear();
|
||||
|
||||
const QByteArray bytes() const { return key_data; }
|
||||
bool loadFromTorMessage(const QByteArray& b);
|
||||
bool isLoaded() const { return !key_data.isNull(); }
|
||||
#ifdef TO_REMOVE
|
||||
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;
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifdef TO_REMOVE
|
||||
struct Data : public QSharedData
|
||||
{
|
||||
typedef struct rsa_st RSA;
|
||||
RSA *key;
|
||||
|
||||
Data(RSA *k = 0) : key(k) { }
|
||||
~Data();
|
||||
};
|
||||
#endif
|
||||
|
||||
QByteArray key_data;
|
||||
#ifdef TO_REMOVE
|
||||
QExplicitlySharedDataPointer<Data> d;
|
||||
#endif
|
||||
};
|
||||
|
||||
QByteArray torControlHashedPassword(const QByteArray &password);
|
||||
|
||||
#endif // CRYPTOKEY_H
|
|
@ -1,124 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "GetConfCommand.h"
|
||||
#include "StrUtil.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));
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,146 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "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::setServiceId(const QByteArray& sid)
|
||||
{
|
||||
m_service_id = sid;
|
||||
m_hostname = sid + ".onion";
|
||||
|
||||
emit hostnameChanged();
|
||||
}
|
||||
void HiddenService::setPrivateKey(const CryptoKey &key)
|
||||
{
|
||||
if (m_privateKey.isLoaded()) {
|
||||
BUG() << "Cannot change the private key on an existing HiddenService";
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
if (!key.isPrivate()) {
|
||||
BUG() << "Cannot create a hidden service with a public key";
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_privateKey = key;
|
||||
|
||||
emit privateKeyChanged();
|
||||
}
|
||||
|
||||
void HiddenService::loadPrivateKey()
|
||||
{
|
||||
if (m_privateKey.isLoaded() || m_dataPath.isEmpty())
|
||||
return;
|
||||
|
||||
bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"));
|
||||
|
||||
if (!ok) {
|
||||
qWarning() << "Failed to load hidden service key";
|
||||
return;
|
||||
}
|
||||
|
||||
emit privateKeyChanged();
|
||||
}
|
||||
|
||||
void HiddenService::servicePublished()
|
||||
{
|
||||
loadPrivateKey();
|
||||
|
||||
if (m_hostname.isEmpty()) {
|
||||
std::cerr << "Failed to read hidden service hostname" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr << "Hidden service published successfully" << std::endl;
|
||||
setStatus(Online);
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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 serviceId() const { return QString(m_service_id); }
|
||||
const QString& dataPath() const { return m_dataPath; }
|
||||
|
||||
CryptoKey privateKey() { return m_privateKey; }
|
||||
void setPrivateKey(const CryptoKey &privateKey);
|
||||
void setServiceId(const QByteArray& sid);
|
||||
|
||||
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();
|
||||
void hostnameChanged();
|
||||
|
||||
private slots:
|
||||
void servicePublished();
|
||||
|
||||
private:
|
||||
QString m_dataPath;
|
||||
QList<Target> m_targets;
|
||||
QString m_hostname;
|
||||
Status m_status;
|
||||
CryptoKey m_privateKey;
|
||||
QByteArray m_service_id;
|
||||
|
||||
void loadPrivateKey();
|
||||
void setStatus(Status newStatus);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HIDDENSERVICE_H
|
|
@ -1,84 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,85 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "ProtocolInfoCommand.h"
|
||||
#include "TorControl.h"
|
||||
#include "StrUtil.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))));
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,147 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "SecureRNG.h"
|
||||
#include <QtDebug>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/err.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <wtypes.h>
|
||||
#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;
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,106 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "SetConfCommand.h"
|
||||
#include "StrUtil.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);
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,553 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "Settings.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QSaveFile>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QPointer>
|
||||
|
||||
class SettingsFilePrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SettingsFile *q;
|
||||
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"
|
|
@ -1,257 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <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
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "StrUtil.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;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,788 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#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 "StrUtil.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;
|
||||
|
||||
class nullstream: public std::ostream {};
|
||||
|
||||
static std::ostream& torctrldebug()
|
||||
{
|
||||
static nullstream null ;
|
||||
|
||||
if(true)
|
||||
return std::cerr << time(NULL) << ":TOR CONTROL: " ;
|
||||
else
|
||||
return null ;
|
||||
}
|
||||
|
||||
#define torCtrlDebug torctrldebug
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
torCtrlDebug() << "Ignoring TorControl::connect due to existing connection" << std::endl;
|
||||
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;
|
||||
}
|
||||
|
||||
torCtrlDebug() << "torctrl: Authentication successful" << std::endl;
|
||||
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);
|
||||
|
||||
torCtrlDebug() << "torctrl: Connected socket; querying information" << std::endl;
|
||||
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))
|
||||
{
|
||||
torCtrlDebug() << "torctrl: Using null authentication" << std::endl;
|
||||
data = auth->build();
|
||||
}
|
||||
else if (methods.testFlag(ProtocolInfoCommand::AuthCookie) && !info->cookieFile().isEmpty())
|
||||
{
|
||||
QString cookieFile = info->cookieFile();
|
||||
QString cookieError;
|
||||
torCtrlDebug() << "torctrl: Using cookie authentication with file" << cookieFile.toStdString() << std::endl;
|
||||
|
||||
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())
|
||||
{
|
||||
torCtrlDebug() << "torctrl: Unable to read authentication cookie file:" << cookieError.toStdString() << std::endl;
|
||||
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:
|
||||
torCtrlDebug() << "torctrl: Using hashed password authentication" << std::endl;
|
||||
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) {
|
||||
torCtrlDebug() << "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()) {
|
||||
torCtrlDebug() << "torctrl: SOCKS address is " << socksAddress.toString().toStdString() << ":" << socksPort << std::endl;
|
||||
emit q->connectivityChanged();
|
||||
}
|
||||
|
||||
if (command->get(QByteArray("status/circuit-established")).toInt() == 1) {
|
||||
torCtrlDebug() << "torctrl: Tor indicates that circuits have been established; state is TorReady" << std::endl;
|
||||
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()
|
||||
{
|
||||
torCtrlDebug() << "Publish Services... " ;
|
||||
|
||||
Q_ASSERT(q->isConnected());
|
||||
if (services.isEmpty())
|
||||
{
|
||||
std::cerr << "No service regstered!" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
SettingsObject settings(QStringLiteral("tor"));
|
||||
if (settings.read("neverPublishServices").toBool())
|
||||
{
|
||||
torCtrlDebug() << "torctrl: Skipping service publication because neverPublishService is enabled" << std::endl;
|
||||
|
||||
/* 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())
|
||||
torCtrlDebug() << "torctrl: Creating a new hidden service" << std::endl;
|
||||
else
|
||||
torCtrlDebug() << "torctrl: Publishing hidden service: " << service->hostname().toStdString() << std::endl;
|
||||
AddOnionCommand *onionCommand = new AddOnionCommand(service);
|
||||
QObject::connect(onionCommand, &AddOnionCommand::succeeded, service, &HiddenService::servicePublished);
|
||||
socket->sendCommand(onionCommand, onionCommand->build());
|
||||
}
|
||||
} else {
|
||||
torCtrlDebug() << "torctrl: Using legacy SETCONF hidden service configuration for tor" << torVersion.toStdString() << std::endl;
|
||||
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;
|
||||
}
|
||||
|
||||
torCtrlDebug() << "torctrl: Configuring hidden service at" << service->dataPath().toStdString() << std::endl;
|
||||
|
||||
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;
|
||||
|
||||
torCtrlDebug() << "torctrl: status event:" << QString(data.trimmed()).toStdString() << std::endl;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//torCtrlDebug() << bootstrapStatus << std::endl;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
torCtrlDebug() << "torctrl: Wrote torrc file" << std::endl;
|
||||
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"
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef TORCONTROL_H
|
||||
#define TORCONTROL_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#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 = 0x00,
|
||||
Connecting = 0x01,
|
||||
Authenticating = 0x02,
|
||||
Connected = 0x03
|
||||
};
|
||||
|
||||
enum TorStatus
|
||||
{
|
||||
TorUnknown = 0x00,
|
||||
TorOffline = 0x01,
|
||||
TorReady = 0x02
|
||||
};
|
||||
|
||||
|
||||
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
|
|
@ -1,63 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "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";
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,178 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#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);
|
||||
|
||||
std::cerr << "[TOR CTRL] Sent: \"" << QString(data.trimmed()).toStdString() << "\"" << std::endl;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -9,42 +9,59 @@
|
|||
#include <iostream>
|
||||
#include "util/rstime.h"
|
||||
|
||||
#include "retroshare/rstor.h"
|
||||
#include "retroshare/rsevents.h"
|
||||
|
||||
#include "TorControlWindow.h"
|
||||
#include "TorManager.h"
|
||||
#include "TorControl.h"
|
||||
#include "HiddenService.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
|
||||
TorControlDialog::TorControlDialog(Tor::TorManager *tm,QWidget *parent)
|
||||
: mTorManager(tm)
|
||||
TorControlDialog::TorControlDialog(QWidget *)
|
||||
{
|
||||
setupUi(this) ;
|
||||
setupUi(this) ;
|
||||
|
||||
QObject::connect(tm->control(),SIGNAL(statusChanged(int,int)),this,SLOT(statusChanged())) ;
|
||||
QObject::connect(tm->control(),SIGNAL(connected()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm->control(),SIGNAL(disconnected()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm->control(),SIGNAL(bootstrapStatusChanged()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm->control(),SIGNAL(connectivityChanged()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm ,SIGNAL(errorChanged()),this,SLOT(statusChanged()));
|
||||
mEventHandlerId = 0; // very important!
|
||||
|
||||
//QTimer::singleShot(2000,this,SLOT(checkForHiddenService())) ;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
|
||||
}, mEventHandlerId, RsEventType::TOR_MANAGER );
|
||||
|
||||
mIncomingServer = new QTcpServer(this) ;
|
||||
mHiddenService = NULL ;
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_UNKNOWN;
|
||||
//mBootstrapPhaseFinished = false ;
|
||||
mIncomingServer = new QTcpServer(this) ;
|
||||
|
||||
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
|
||||
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
|
||||
|
||||
QTimer *timer = new QTimer ;
|
||||
QTimer *timer = new QTimer ;
|
||||
|
||||
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
|
||||
timer->start(500) ;
|
||||
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
|
||||
timer->start(300) ;
|
||||
|
||||
// Hide some debug output for the released version
|
||||
// Hide some debug output for the released version
|
||||
|
||||
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
|
||||
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
|
||||
|
||||
adjustSize();
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
TorControlDialog::~TorControlDialog()
|
||||
{
|
||||
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||
}
|
||||
|
||||
void TorControlDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
if(event->mType != RsEventType::TOR_MANAGER) return;
|
||||
|
||||
const RsTorManagerEvent *fe = dynamic_cast<const RsTorManagerEvent*>(event.get());
|
||||
if(!fe)
|
||||
return;
|
||||
|
||||
switch(fe->mTorManagerEventType)
|
||||
{
|
||||
case RsTorManagerEventCode::TOR_STATUS_CHANGED:
|
||||
case RsTorManagerEventCode::TOR_CONNECTIVITY_CHANGED: statusChanged(fe->mTorStatus,fe->mTorConnectivityStatus);
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
void TorControlDialog::onIncomingConnection()
|
||||
|
@ -52,67 +69,66 @@ void TorControlDialog::onIncomingConnection()
|
|||
std::cerr << "Incoming connection !!" << std::endl;
|
||||
}
|
||||
|
||||
void TorControlDialog::statusChanged()
|
||||
void TorControlDialog::statusChanged(RsTorStatus torstatus, RsTorConnectivityStatus tor_control_status)
|
||||
{
|
||||
int tor_control_status = mTorManager->control()->status();
|
||||
int torstatus = mTorManager->control()->torStatus();
|
||||
|
||||
QString tor_control_status_str,torstatus_str ;
|
||||
|
||||
if(mTorManager->hasError())
|
||||
mErrorMsg = mTorManager->errorMessage() ;
|
||||
if(RsTor::hasError())
|
||||
mErrorMsg = QString::fromStdString(RsTor::errorMessage()) ;
|
||||
|
||||
switch(tor_control_status)
|
||||
{
|
||||
default:
|
||||
case Tor::TorControl::Error : tor_control_status_str = "Error" ; break ;
|
||||
case Tor::TorControl::NotConnected: tor_control_status_str = "Not connected" ; break ;
|
||||
case Tor::TorControl::Connecting: tor_control_status_str = "Connecting" ; break ;
|
||||
case Tor::TorControl::Authenticating: tor_control_status_str = "Authenticating" ; break ;
|
||||
case Tor::TorControl::Connected: tor_control_status_str = "Connected" ; break ;
|
||||
}
|
||||
case RsTorConnectivityStatus::ERROR: tor_control_status_str = tr("Error") ; break ;
|
||||
case RsTorConnectivityStatus::NOT_CONNECTED: tor_control_status_str = tr("Not connected") ; break ;
|
||||
case RsTorConnectivityStatus::CONNECTING: tor_control_status_str = tr("Connecting") ; break ;
|
||||
case RsTorConnectivityStatus::SOCKET_CONNECTED: tor_control_status_str = tr("Socket connected") ; break ;
|
||||
case RsTorConnectivityStatus::AUTHENTICATING: tor_control_status_str = tr("Authenticating") ; break ;
|
||||
case RsTorConnectivityStatus::AUTHENTICATED: tor_control_status_str = tr("Authenticated") ; break ;
|
||||
case RsTorConnectivityStatus::HIDDEN_SERVICE_READY: tor_control_status_str = tr("Hidden service ready") ; break ;
|
||||
case RsTorConnectivityStatus::UNKNOWN: tor_control_status_str = tr("Unknown") ; break ;
|
||||
}
|
||||
|
||||
switch(torstatus)
|
||||
{
|
||||
default:
|
||||
case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ;
|
||||
case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ;
|
||||
case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ;
|
||||
case RsTorStatus::UNKNOWN: torstatus_str = tr("Unknown") ; break ;
|
||||
case RsTorStatus::OFFLINE: torstatus_str = tr("Tor offline") ; break ;
|
||||
case RsTorStatus::READY: torstatus_str = tr("Tor ready") ; break ;
|
||||
}
|
||||
|
||||
torStatus_LB->setText(torstatus_str) ;
|
||||
|
||||
if(torstatus == Tor::TorControl::TorUnknown)
|
||||
if(torstatus == RsTorStatus::UNKNOWN)
|
||||
torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ;
|
||||
else
|
||||
torStatus_LB->setToolTip("") ;
|
||||
|
||||
QVariantMap qvm = mTorManager->control()->bootstrapStatus();
|
||||
std::map<std::string,std::string> qvm = RsTor::bootstrapStatus();
|
||||
QString bootstrapstatus_str ;
|
||||
|
||||
std::cerr << "Tor control status: " << tor_control_status_str.toStdString() << std::endl;
|
||||
std::cerr << "Tor status: " << torstatus_str.toStdString() << std::endl;
|
||||
|
||||
std::cerr << "Bootstrap status map: " << std::endl;
|
||||
|
||||
for(auto it(qvm.begin());it!=qvm.end();++it)
|
||||
std::cerr << " " << it.key().toStdString() << " : " << it.value().toString().toStdString() << std::endl;
|
||||
std::cerr << " " << it->first << " : " << it->second << std::endl;
|
||||
|
||||
if(!qvm["progress"].toString().isNull())
|
||||
torBootstrapStatus_LB->setText(qvm["progress"].toString() + " % (" + qvm["summary"].toString() + ")") ;
|
||||
if(!qvm["progress"].empty())
|
||||
torBootstrapStatus_LB->setText(QString::fromStdString(qvm["progress"]) + " % (" + QString::fromStdString(qvm["summary"]) + ")") ;
|
||||
else
|
||||
torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ;
|
||||
|
||||
QString service_id ;
|
||||
QString onion_address ;
|
||||
QHostAddress service_target_address ;
|
||||
std::string service_id ;
|
||||
std::string onion_address ;
|
||||
std::string service_target_address ;
|
||||
uint16_t service_port ;
|
||||
uint16_t target_port ;
|
||||
|
||||
if(mTorManager->getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
|
||||
if(RsTor::getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
|
||||
{
|
||||
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + service_target_address.toString() + ":" + QString::number(target_port));
|
||||
onionAddress_LB->setText(onion_address);
|
||||
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + QString::fromStdString(service_target_address) + ":" + QString::number(target_port));
|
||||
onionAddress_LB->setText(QString::fromStdString(onion_address));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -123,34 +139,39 @@ void TorControlDialog::statusChanged()
|
|||
showLog();
|
||||
adjustSize();
|
||||
|
||||
QCoreApplication::processEvents(); // forces update
|
||||
QCoreApplication::processEvents(); // forces update
|
||||
}
|
||||
|
||||
void TorControlDialog::showLog()
|
||||
{
|
||||
static std::set<QString> already_seen ;
|
||||
static std::set<std::string> already_seen ;
|
||||
|
||||
QString s ;
|
||||
QStringList logmsgs = mTorManager->logMessages() ;
|
||||
bool can_print = false ;
|
||||
std::string s ;
|
||||
std::list<std::string> logmsgs = RsTor::logMessages() ;
|
||||
bool can_print = false ;
|
||||
|
||||
for(QStringList::const_iterator it(logmsgs.begin());it!=logmsgs.end();++it)
|
||||
{
|
||||
for(auto it(logmsgs.begin());it!=logmsgs.end();++it)
|
||||
{
|
||||
s += *it + "\n" ;
|
||||
|
||||
if(already_seen.find(*it) == already_seen.end())
|
||||
{
|
||||
can_print = true ;
|
||||
already_seen.insert(*it);
|
||||
}
|
||||
if(already_seen.find(*it) == already_seen.end())
|
||||
{
|
||||
can_print = true ;
|
||||
already_seen.insert(*it);
|
||||
}
|
||||
|
||||
if(can_print)
|
||||
std::cerr << "[TOR DEBUG LOG] " << (*it).toStdString() << std::endl;
|
||||
}
|
||||
if(can_print)
|
||||
{
|
||||
std::cerr << "[TOR DEBUG LOG] " << *it << std::endl;
|
||||
|
||||
// torLog_TB->setText(s) ;:
|
||||
QString s = QString::fromStdString(*it);
|
||||
int n = s.indexOf(QString("Bootstrapped"));
|
||||
|
||||
std::cerr << "Connexion Proxy: " << mTorManager->control()->socksAddress().toString().toStdString() << ":" << mTorManager->control()->socksPort() << std::endl;
|
||||
if(n >= 0)
|
||||
torBootstrapStatus_LB->setText(s.mid(n+QString("Bootstrapped").length()));
|
||||
}
|
||||
}
|
||||
//std::cerr << "Connexion Proxy: " << RsTor::socksAddress() << ":" << QString::number(RsTor::socksPort()).toStdString() << std::endl;
|
||||
}
|
||||
|
||||
TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
||||
|
@ -161,9 +182,9 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
|||
return TorControlDialog::TOR_STATUS_FAIL ;
|
||||
}
|
||||
|
||||
switch(mTorManager->control()->torStatus())
|
||||
switch(RsTor::torStatus())
|
||||
{
|
||||
case Tor::TorControl::TorReady: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
|
||||
case RsTorStatus::READY: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
|
||||
default:
|
||||
return TOR_STATUS_UNKNOWN ;
|
||||
}
|
||||
|
@ -171,60 +192,35 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
|||
|
||||
TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService()
|
||||
{
|
||||
std::cerr << "Checking for hidden services:" ;
|
||||
std::cerr << "Checking for hidden services:" ;
|
||||
|
||||
switch(mHiddenServiceStatus)
|
||||
{
|
||||
default:
|
||||
case HIDDEN_SERVICE_STATUS_UNKNOWN: {
|
||||
std::string service_id;
|
||||
|
||||
std::cerr << " trying to setup. " ;
|
||||
RsTorHiddenServiceStatus service_status = RsTor::getHiddenServiceStatus(service_id);
|
||||
|
||||
if(!mTorManager->setupHiddenService())
|
||||
{
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_FAIL ;
|
||||
std::cerr << "Failed." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
std::cerr << "Done." << std::endl;
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_REQUESTED ;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
if(service_id.empty())
|
||||
{
|
||||
std::cerr << "Not ready yet." << std::endl;
|
||||
return HIDDEN_SERVICE_STATUS_REQUESTED ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mHiddenService.empty())
|
||||
mHiddenService = service_id ;
|
||||
|
||||
case HIDDEN_SERVICE_STATUS_REQUESTED: {
|
||||
QList<Tor::HiddenService*> hidden_services = mTorManager->control()->hiddenServices();
|
||||
std::cerr << "New service acquired. Status is " << (int)service_status ;
|
||||
|
||||
if(hidden_services.empty())
|
||||
{
|
||||
std::cerr << "Not ready yet." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mHiddenService == NULL)
|
||||
mHiddenService = *(hidden_services.begin()) ;
|
||||
if(service_status == RsTorHiddenServiceStatus::ONLINE)
|
||||
{
|
||||
std::cerr << ": published and running!" << std::endl;
|
||||
|
||||
Tor::HiddenService::Status hss = mHiddenService->status();
|
||||
|
||||
std::cerr << "New service acquired. Status is " << hss ;
|
||||
|
||||
if(hss == Tor::HiddenService::Online)
|
||||
{
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_OK ;
|
||||
std::cerr << ": published and running!" << std::endl;
|
||||
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << ": not ready yet." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
}
|
||||
}
|
||||
case HIDDEN_SERVICE_STATUS_OK :
|
||||
std::cerr << "New service acquired." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
return HIDDEN_SERVICE_STATUS_OK ;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << ": not ready yet." << std::endl;
|
||||
return HIDDEN_SERVICE_STATUS_REQUESTED ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include "retroshare/rsevents.h"
|
||||
#include "retroshare/rstor.h"
|
||||
#include "ui_TorControlWindow.h"
|
||||
|
||||
class QTcpServer ;
|
||||
|
@ -12,7 +14,8 @@ class TorControlDialog: public QWidget, public Ui::TorControlDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TorControlDialog(Tor::TorManager *tm,QWidget *parent =NULL);
|
||||
TorControlDialog(QWidget *parent =NULL);
|
||||
virtual ~TorControlDialog();
|
||||
|
||||
enum TorStatus {
|
||||
TOR_STATUS_UNKNOWN = 0x00,
|
||||
|
@ -34,15 +37,14 @@ public:
|
|||
|
||||
protected slots:
|
||||
void showLog();
|
||||
void statusChanged();
|
||||
void onIncomingConnection();
|
||||
void statusChanged(RsTorStatus torstatus,RsTorConnectivityStatus tor_control_status);
|
||||
void onIncomingConnection();
|
||||
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
private:
|
||||
QString mErrorMsg ;
|
||||
HiddenServiceStatus mHiddenServiceStatus ;
|
||||
|
||||
Tor::TorManager *mTorManager ;
|
||||
Tor::HiddenService *mHiddenService ;
|
||||
std::string mHiddenService;
|
||||
|
||||
QTcpServer *mIncomingServer ;
|
||||
RsEventsHandlerId_t mEventHandlerId;
|
||||
};
|
||||
|
|
|
@ -40,6 +40,12 @@
|
|||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
|
@ -55,6 +61,12 @@
|
|||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="torStatusTxt_LB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tor status:</string>
|
||||
</property>
|
||||
|
@ -79,8 +91,14 @@
|
|||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="HiddenServiceAddressTxt_LB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hidden service address:</string>
|
||||
<string>Hidden address:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
|
@ -89,8 +107,14 @@
|
|||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="torBootstrapStatusTxt_LB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tor bootstrap status:</string>
|
||||
<string>Tor status:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
|
@ -106,6 +130,12 @@
|
|||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="onionAddressTxt_LB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Onion address:</string>
|
||||
</property>
|
||||
|
|
|
@ -1,526 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "TorManager.h"
|
||||
#include "TorProcess.h"
|
||||
#include "TorControl.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "HiddenService.h"
|
||||
#include "GetConfCommand.h"
|
||||
#include "Settings.h"
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
#include <QTcpServer>
|
||||
#include <QTextStream>
|
||||
|
||||
using namespace Tor;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
|
||||
class TorManagerPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TorManager *q;
|
||||
TorProcess *process;
|
||||
TorControl *control;
|
||||
QString dataDir;
|
||||
QString hiddenServiceDir;
|
||||
QStringList logMessages;
|
||||
QString errorMessage;
|
||||
bool configNeeded;
|
||||
|
||||
HiddenService *hiddenService ;
|
||||
|
||||
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)
|
||||
, hiddenService(NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool TorManager::isTorAvailable()
|
||||
{
|
||||
return !instance()->d->torExecutablePath().isNull();
|
||||
}
|
||||
|
||||
QString TorManager::torDataDirectory() const
|
||||
{
|
||||
return d->dataDir;
|
||||
}
|
||||
|
||||
void TorManager::setTorDataDirectory(const QString &path)
|
||||
{
|
||||
d->dataDir = QDir::fromNativeSeparators(path);
|
||||
|
||||
if (!d->dataDir.isEmpty() && !d->dataDir.endsWith(QLatin1Char('/')))
|
||||
d->dataDir.append(QLatin1Char('/'));
|
||||
}
|
||||
|
||||
QString TorManager::hiddenServiceDirectory() const
|
||||
{
|
||||
return d->hiddenServiceDir;
|
||||
}
|
||||
void TorManager::setHiddenServiceDirectory(const QString &path)
|
||||
{
|
||||
d->hiddenServiceDir = QDir::fromNativeSeparators(path);
|
||||
|
||||
if (!d->hiddenServiceDir.isEmpty() && !d->hiddenServiceDir.endsWith(QLatin1Char('/')))
|
||||
d->hiddenServiceDir.append(QLatin1Char('/'));
|
||||
}
|
||||
|
||||
bool TorManager::setupHiddenService()
|
||||
{
|
||||
if(d->hiddenService != NULL)
|
||||
{
|
||||
std::cerr << "TorManager: setupHiddenService() called twice! Not doing anything this time." << std::endl;
|
||||
return true ;
|
||||
}
|
||||
|
||||
QString keyData ;//= m_settings->read("serviceKey").toString();
|
||||
QString legacyDir = d->hiddenServiceDir;
|
||||
|
||||
std::cerr << "TorManager: setting up hidden service." << std::endl;
|
||||
|
||||
if(legacyDir.isNull())
|
||||
{
|
||||
std::cerr << "legacy dir not set! Cannot proceed." << std::endl;
|
||||
return false ;
|
||||
}
|
||||
|
||||
std::cerr << "Using legacy dir: " << legacyDir.toStdString() << std::endl;
|
||||
|
||||
if (!legacyDir.isEmpty() && QFile::exists(legacyDir + QLatin1String("/private_key")))
|
||||
{
|
||||
std::cerr << "Attempting to load key from legacy filesystem format in " << legacyDir.toStdString() << std::endl;
|
||||
|
||||
CryptoKey key;
|
||||
if (!key.loadFromFile(legacyDir + QLatin1String("/private_key")))
|
||||
{
|
||||
qWarning() << "Cannot load legacy format key from" << legacyDir << "for conversion";
|
||||
return false;
|
||||
}
|
||||
|
||||
d->hiddenService = new Tor::HiddenService(key, legacyDir, this);
|
||||
|
||||
std::cerr << "Got key from legacy dir: " << std::endl;
|
||||
std::cerr << key.bytes().toStdString() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
d->hiddenService = new Tor::HiddenService(legacyDir, this);
|
||||
|
||||
std::cerr << "Creating new hidden service." << std::endl;
|
||||
|
||||
connect(d->hiddenService, SIGNAL(privateKeyChanged()), this, SLOT(hiddenServicePrivateKeyChanged())) ;
|
||||
connect(d->hiddenService, SIGNAL(hostnameChanged()), this, SLOT(hiddenServiceHostnameChanged())) ;
|
||||
}
|
||||
|
||||
Q_ASSERT(d->hiddenService);
|
||||
connect(d->hiddenService, SIGNAL(statusChanged(int,int)), this, SLOT(hiddenServiceStatusChanged(int,int)));
|
||||
|
||||
// Generally, these are not used, and we bind to localhost and port 0
|
||||
// for an automatic (and portable) selection.
|
||||
|
||||
QHostAddress address = QHostAddress::LocalHost; // we only listen from localhost
|
||||
|
||||
quint16 port = 7934;//(quint16)m_settings->read("localListenPort").toInt();
|
||||
|
||||
std::cerr << "Testing host address: " << address.toString().toStdString() << ":" << port ;
|
||||
|
||||
if (!QTcpServer().listen(address, port))
|
||||
{
|
||||
// XXX error case
|
||||
std::cerr << " Failed to open incoming socket" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cerr << " OK - Adding hidden service to TorControl." << std::endl;
|
||||
|
||||
//d->hiddenService->addTarget(9878, mIncomingServer->serverAddress(), mIncomingServer->serverPort());
|
||||
d->hiddenService->addTarget(9878, QHostAddress::LocalHost,7934);
|
||||
control()->addHiddenService(d->hiddenService);
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
void TorManager::hiddenServiceStatusChanged(int old_status,int new_status)
|
||||
{
|
||||
std::cerr << "Hidden service status changed from " << old_status << " to " << new_status << std::endl;
|
||||
}
|
||||
|
||||
void TorManager::hiddenServicePrivateKeyChanged()
|
||||
{
|
||||
QString key = QString::fromLatin1(d->hiddenService->privateKey().bytes());
|
||||
|
||||
QFile outfile(d->hiddenServiceDir + QLatin1String("/private_key")) ;
|
||||
outfile.open( QIODevice::WriteOnly | QIODevice::Text );
|
||||
QTextStream s(&outfile);
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
s << "-----BEGIN RSA PRIVATE KEY-----" << endl;
|
||||
|
||||
for(int i=0;i<key.length();i+=64)
|
||||
s << key.mid(i,64) << endl ;
|
||||
|
||||
s << "-----END RSA PRIVATE KEY-----" << endl;
|
||||
#endif
|
||||
s << key ;
|
||||
|
||||
outfile.close();
|
||||
|
||||
std::cerr << "Hidden service private key changed!" << std::endl;
|
||||
std::cerr << key.toStdString() << std::endl;
|
||||
}
|
||||
|
||||
void TorManager::hiddenServiceHostnameChanged()
|
||||
{
|
||||
QFile outfile2(d->hiddenServiceDir + QLatin1String("/hostname")) ;
|
||||
outfile2.open( QIODevice::WriteOnly | QIODevice::Text );
|
||||
QTextStream t(&outfile2);
|
||||
|
||||
QString hostname(d->hiddenService->hostname());
|
||||
|
||||
t << hostname << endl;
|
||||
outfile2.close();
|
||||
|
||||
std::cerr << "Hidden service hostname changed: " << hostname.toStdString() << std::endl;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool 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 false;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
return true ;
|
||||
}
|
||||
|
||||
bool TorManager::getProxyServerInfo(QHostAddress& proxy_server_adress,uint16_t& proxy_server_port)
|
||||
{
|
||||
proxy_server_adress = control()->socksAddress();
|
||||
proxy_server_port = control()->socksPort();
|
||||
|
||||
return proxy_server_port > 1023 ;
|
||||
}
|
||||
|
||||
bool TorManager::getHiddenServiceInfo(QString& service_id,QString& service_onion_address,uint16_t& service_port, QHostAddress& service_target_address,uint16_t& target_port)
|
||||
{
|
||||
QList<Tor::HiddenService*> hidden_services = control()->hiddenServices();
|
||||
|
||||
if(hidden_services.empty())
|
||||
return false ;
|
||||
|
||||
// Only return the first one.
|
||||
|
||||
for(auto it(hidden_services.begin());it!=hidden_services.end();++it)
|
||||
{
|
||||
service_onion_address = (*it)->hostname();
|
||||
service_id = (*it)->serviceId();
|
||||
|
||||
for(auto it2((*it)->targets().begin());it2!=(*it)->targets().end();++it2)
|
||||
{
|
||||
service_port = (*it2).servicePort ;
|
||||
service_target_address = (*it2).targetAddress ;
|
||||
target_port = (*it2).targetPort;
|
||||
break ;
|
||||
}
|
||||
break ;
|
||||
}
|
||||
return true ;
|
||||
}
|
||||
|
||||
void TorManagerPrivate::processStateChanged(int state)
|
||||
{
|
||||
std::cerr << Q_FUNC_INFO << "state: " << state << " passwd=\"" << QString(process->controlPassword()).toStdString() << "\" " << process->controlHost().toString().toStdString()
|
||||
<< ":" << process->controlPort() << std::endl;
|
||||
if (state == TorProcess::Ready) {
|
||||
control->setAuthPassword(process->controlPassword());
|
||||
control->connect(process->controlHost(), process->controlPort());
|
||||
}
|
||||
}
|
||||
|
||||
void TorManagerPrivate::processErrorChanged(const QString &errorMessage)
|
||||
{
|
||||
std::cerr << "tor error:" << errorMessage.toStdString() << std::endl;
|
||||
setError(errorMessage);
|
||||
}
|
||||
|
||||
void TorManagerPrivate::processLogMessage(const QString &message)
|
||||
{
|
||||
std::cerr << "tor:" << message.toStdString() << std::endl;
|
||||
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() && QFile::exists(path))
|
||||
return path;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString filename(QStringLiteral("/tor/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
|
||||
|
||||
#ifdef __APPLE__
|
||||
// on MacOS, try traditional brew installation path
|
||||
|
||||
path = QStringLiteral("/usr/local/opt/tor/bin") ;
|
||||
|
||||
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" // (cyril) I removed this because it prevents Tor to bootstrap.
|
||||
"__ReloadTorrcOnSIGHUP 0\n";
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
return false;
|
||||
if (file.write(defaultTorrcContent) < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TorManagerPrivate::setError(const QString &message)
|
||||
{
|
||||
errorMessage = message;
|
||||
emit q->errorChanged();
|
||||
}
|
||||
|
||||
#include "TorManager.moc"
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// This code has been further modified to fit Retroshare context.
|
||||
|
||||
#ifndef TORMANAGER_H
|
||||
#define TORMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QHostAddress>
|
||||
|
||||
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 torDataDirectory READ torDataDirectory WRITE setTorDataDirectory)
|
||||
|
||||
public:
|
||||
static bool isTorAvailable() ;
|
||||
static TorManager *instance();
|
||||
|
||||
TorProcess *process();
|
||||
TorControl *control();
|
||||
|
||||
|
||||
QString torDataDirectory() const;
|
||||
void setTorDataDirectory(const QString &path);
|
||||
|
||||
QString hiddenServiceDirectory() const;
|
||||
void setHiddenServiceDirectory(const QString &path);
|
||||
|
||||
// Starts a hidden service, loading it from the config directory that has been set earlier.
|
||||
bool setupHiddenService() ;
|
||||
|
||||
// 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;
|
||||
|
||||
bool getHiddenServiceInfo(QString& service_id,QString& service_onion_address,uint16_t& service_port, QHostAddress& service_target_address,uint16_t& target_port);
|
||||
bool getProxyServerInfo(QHostAddress& proxy_server_adress,uint16_t& proxy_server_port);
|
||||
|
||||
public slots:
|
||||
bool start();
|
||||
|
||||
private slots:
|
||||
void hiddenServicePrivateKeyChanged();
|
||||
void hiddenServiceHostnameChanged();
|
||||
void hiddenServiceStatusChanged(int old_status,int new_status);
|
||||
|
||||
signals:
|
||||
void configurationNeededChanged();
|
||||
void errorChanged();
|
||||
|
||||
private:
|
||||
explicit TorManager(QObject *parent = 0);
|
||||
TorManagerPrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#
|
|
@ -1,311 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "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) {
|
||||
std::cerr << "Attempting reconnection of socket to" << m_host.toStdString() << ":" << m_port << std::endl;
|
||||
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);
|
||||
std::cerr << "Reconnecting socket to" << m_host.toStdString() << ":" << m_port << "in" << m_connectTimer.interval() / 1000 << "seconds" << std::endl;
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
|
@ -1,71 +0,0 @@
|
|||
/* Ricochet - https://ricochet.im/
|
||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the names of the copyright owners nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef 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
|
||||
|
|
@ -59,6 +59,10 @@
|
|||
#define COLUMN_COUNT 3
|
||||
#define COLUMN_DATA 0
|
||||
|
||||
#define COLUMN_NAME_NB_CHAR 30
|
||||
#define COLUMN_USER_COUNT_NB_CHAR 4
|
||||
#define COLUMN_TOPIC_NB_CHAR 25
|
||||
|
||||
#define ROLE_SORT Qt::UserRole
|
||||
#define ROLE_ID Qt::UserRole + 1
|
||||
#define ROLE_SUBSCRIBED Qt::UserRole + 2
|
||||
|
@ -88,6 +92,13 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
|
|||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
int H = QFontMetricsF(ui.lobbyTreeWidget->font()).height();
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
|
||||
int W = QFontMetricsF(ui.lobbyTreeWidget->font()).width("_");
|
||||
#else
|
||||
int W = QFontMetricsF(ui.lobbyTreeWidget->font()).horizontalAdvance("_");
|
||||
#endif
|
||||
|
||||
m_bProcessSettings = false;
|
||||
myChatLobbyUserNotify = NULL;
|
||||
myInviteYesButton = NULL;
|
||||
|
@ -108,6 +119,10 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
|
|||
compareRole = new RSTreeWidgetItemCompareRole;
|
||||
compareRole->setRole(COLUMN_NAME, ROLE_SORT);
|
||||
|
||||
RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this);
|
||||
itemDelegate->setSpacing(QSize(W/2, H/4));
|
||||
ui.lobbyTreeWidget->setItemDelegate(itemDelegate);
|
||||
|
||||
ui.lobbyTreeWidget->setColumnCount(COLUMN_COUNT);
|
||||
ui.lobbyTreeWidget->sortItems(COLUMN_NAME, Qt::AscendingOrder);
|
||||
|
||||
|
@ -159,14 +174,11 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
|
|||
ui.lobbyTreeWidget->setColumnHidden(COLUMN_USER_COUNT,true) ;
|
||||
ui.lobbyTreeWidget->setColumnHidden(COLUMN_TOPIC,true) ;
|
||||
ui.lobbyTreeWidget->setSortingEnabled(true) ;
|
||||
ui.lobbyTreeWidget->setItemDelegateForColumn(COLUMN_NAME, new RSElidedItemDelegate());
|
||||
|
||||
float fact = QFontMetricsF(font()).height()/14.0f;
|
||||
|
||||
ui.lobbyTreeWidget->adjustSize();
|
||||
ui.lobbyTreeWidget->setColumnWidth(COLUMN_NAME,100*fact);
|
||||
ui.lobbyTreeWidget->setColumnWidth(COLUMN_USER_COUNT, 50*fact);
|
||||
ui.lobbyTreeWidget->setColumnWidth(COLUMN_TOPIC, 50*fact);
|
||||
ui.lobbyTreeWidget->setColumnWidth(COLUMN_NAME,COLUMN_NAME_NB_CHAR*W);
|
||||
ui.lobbyTreeWidget->setColumnWidth(COLUMN_USER_COUNT, COLUMN_USER_COUNT_NB_CHAR*W);
|
||||
ui.lobbyTreeWidget->setColumnWidth(COLUMN_TOPIC, COLUMN_TOPIC_NB_CHAR*W);
|
||||
|
||||
/** Setup the actions for the header context menu */
|
||||
showUserCountAct= new QAction(headerItem->text(COLUMN_USER_COUNT),this);
|
||||
|
@ -181,7 +193,7 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
|
|||
ui.splitter->setStretchFactor(1, 1);
|
||||
|
||||
QList<int> sizes;
|
||||
sizes << 200*fact << width(); // Qt calculates the right sizes
|
||||
sizes << ui.lobbyTreeWidget->columnWidth(COLUMN_NAME) << width(); // Qt calculates the right sizes
|
||||
ui.splitter->setSizes(sizes);
|
||||
|
||||
lobbyChanged();
|
||||
|
@ -194,7 +206,6 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
|
|||
// load settings
|
||||
processSettings(true);
|
||||
|
||||
int S = QFontMetricsF(font()).height();
|
||||
QString help_str = tr("\
|
||||
<h1><img width=\"%1\" src=\":/icons/help_64.png\"> Chat Rooms</h1> \
|
||||
<p>Chat rooms work pretty much like IRC. \
|
||||
|
@ -212,11 +223,11 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
|
|||
Note: For the chat rooms to work properly, your computer needs be on time. So check your system clock!\
|
||||
</p> \
|
||||
"
|
||||
).arg(QString::number(2*S), QString::number(S)) ;
|
||||
).arg(QString::number(4*W), QString::number(2*W)) ;
|
||||
|
||||
registerHelpButton(ui.helpButton,help_str,"ChatLobbyDialog") ;
|
||||
|
||||
ui.lobbyTreeWidget->setIconSize(QSize(S*1.5,S*1.5));
|
||||
registerHelpButton(ui.helpButton,help_str,"ChatLobbyDialog") ;
|
||||
|
||||
ui.lobbyTreeWidget->setIconSize(QSize(H*1.5,H*1.5));
|
||||
}
|
||||
|
||||
ChatLobbyWidget::~ChatLobbyWidget()
|
||||
|
@ -232,7 +243,7 @@ ChatLobbyWidget::~ChatLobbyWidget()
|
|||
UserNotify *ChatLobbyWidget::createUserNotify(QObject *parent)
|
||||
{
|
||||
myChatLobbyUserNotify = new ChatLobbyUserNotify(parent);
|
||||
connect(myChatLobbyUserNotify, SIGNAL(countChanged(ChatLobbyId, uint)), this, SLOT(updateNotify(ChatLobbyId, uint)));
|
||||
connect(myChatLobbyUserNotify, SIGNAL(countChanged(ChatLobbyId,uint)), this, SLOT(updateNotify(ChatLobbyId,uint)));
|
||||
|
||||
return myChatLobbyUserNotify;
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="images.qrc">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/help_64.png</normaloff>:/icons/help_64.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
|
@ -191,6 +191,11 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="RSTreeWidget" name="lobbyTreeWidget">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
|
@ -459,7 +464,7 @@
|
|||
<customwidget>
|
||||
<class>LineEditClear</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/common/LineEditClear.h</header>
|
||||
<header location="global">gui/common/LineEditClear.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RSTreeWidget</class>
|
||||
|
@ -468,7 +473,6 @@
|
|||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="images.qrc"/>
|
||||
<include location="icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
|
|
|
@ -33,15 +33,15 @@
|
|||
|
||||
#include <rshare.h>
|
||||
#include "gui/settings/rsharesettings.h"
|
||||
#include "TorControl/TorManager.h"
|
||||
#include "util/misc.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
|
||||
#include <retroshare/rsidentity.h>
|
||||
#include <retroshare/rsinit.h>
|
||||
#include <retroshare/rsnotify.h>
|
||||
#include <rsserver/rsaccounts.h>
|
||||
#include <util/rsrandom.h>
|
||||
#include "retroshare/rstor.h"
|
||||
#include "retroshare/rsidentity.h"
|
||||
#include "retroshare/rsinit.h"
|
||||
#include "retroshare/rsnotify.h"
|
||||
#include "rsserver/rsaccounts.h"
|
||||
#include "util/rsrandom.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
@ -520,7 +520,7 @@ void GenCertDialog::genPerson()
|
|||
bool isHiddenLoc = (ui.nodeType_CB->currentIndex()>0);
|
||||
bool isAutoTor = (ui.nodeType_CB->currentIndex()==1);
|
||||
|
||||
if(isAutoTor && !Tor::TorManager::isTorAvailable())
|
||||
if(isAutoTor && !RsTor::isTorAvailable())
|
||||
{
|
||||
QMessageBox::critical(this,tr("Tor is not available"),tr("No Tor executable has been found on your system. You need to install Tor before creating a hidden identity.")) ;
|
||||
return ;
|
||||
|
|
|
@ -169,15 +169,18 @@ IdDialog::IdDialog(QWidget *parent)
|
|||
//connect(mCirclesBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateCirclesDisplay(bool)));
|
||||
|
||||
ownItem = new QTreeWidgetItem();
|
||||
ownItem->setText(0, tr("My own identities"));
|
||||
ownItem->setText(RSID_COL_NICKNAME, tr("My own identities"));
|
||||
ownItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font());
|
||||
ownItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); // this is in order to prevent displaying a reputaiton icon next to these items.
|
||||
|
||||
allItem = new QTreeWidgetItem();
|
||||
allItem->setText(0, tr("All"));
|
||||
allItem->setText(RSID_COL_NICKNAME, tr("All"));
|
||||
allItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font());
|
||||
allItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff);
|
||||
|
||||
contactsItem = new QTreeWidgetItem();
|
||||
contactsItem->setText(0, tr("My contacts"));
|
||||
contactsItem->setText(RSID_COL_NICKNAME, tr("My contacts"));
|
||||
contactsItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font());
|
||||
contactsItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff);
|
||||
|
||||
|
||||
|
@ -366,6 +369,7 @@ IdDialog::IdDialog(QWidget *parent)
|
|||
ui->idTreeWidget->setColumnWidth(RSID_COL_IDTYPE, 18 * fontWidth);
|
||||
ui->idTreeWidget->setColumnWidth(RSID_COL_VOTES, 2 * fontWidth);
|
||||
|
||||
ui->idTreeWidget->setItemDelegate(new RSElidedItemDelegate());
|
||||
ui->idTreeWidget->setItemDelegateForColumn(
|
||||
RSID_COL_NICKNAME,
|
||||
new GxsIdTreeItemDelegate());
|
||||
|
@ -648,15 +652,16 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
|
|||
if(!mExternalOtherCircleItem)
|
||||
{
|
||||
mExternalOtherCircleItem = new QTreeWidgetItem();
|
||||
mExternalOtherCircleItem->setText(0, tr("Other circles"));
|
||||
|
||||
mExternalOtherCircleItem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Other circles"));
|
||||
mExternalOtherCircleItem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, ui->treeWidget_membership->font());
|
||||
ui->treeWidget_membership->addTopLevelItem(mExternalOtherCircleItem);
|
||||
}
|
||||
|
||||
if(!mExternalBelongingCircleItem )
|
||||
{
|
||||
mExternalBelongingCircleItem = new QTreeWidgetItem();
|
||||
mExternalBelongingCircleItem->setText(0, tr("Circles I belong to"));
|
||||
mExternalBelongingCircleItem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Circles I belong to"));
|
||||
mExternalBelongingCircleItem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, ui->treeWidget_membership->font());
|
||||
ui->treeWidget_membership->addTopLevelItem(mExternalBelongingCircleItem);
|
||||
}
|
||||
#endif
|
||||
|
@ -732,14 +737,11 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
|
|||
|
||||
item->setToolTip(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,tooltip);
|
||||
|
||||
if (am_I_admin)
|
||||
{
|
||||
QFont font = item->font(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) ;
|
||||
font.setBold(true) ;
|
||||
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
|
||||
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
|
||||
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
|
||||
}
|
||||
QFont font = ui->treeWidget_membership->font() ;
|
||||
font.setBold(am_I_admin) ;
|
||||
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
|
||||
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
|
||||
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
|
||||
|
||||
// now determine for this circle wether we have pending invites
|
||||
// we add a sub-item to the circle (to show the invite system info) in the following two cases:
|
||||
|
@ -854,12 +856,10 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
|
|||
subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole, QVariant(it->second)) ;
|
||||
subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPID, Qt::UserRole, QString::fromStdString(it->first.toStdString())) ;
|
||||
|
||||
//subitem->setIcon(RSID_COL_NICKNAME, QIcon(pixmap));
|
||||
|
||||
new_sub_items.push_back(subitem);
|
||||
new_sub_items.push_back(subitem);
|
||||
}
|
||||
else
|
||||
subitem = item->child(subitem_index);
|
||||
else
|
||||
subitem = item->child(subitem_index);
|
||||
|
||||
if(invited && !subscrb)
|
||||
{
|
||||
|
@ -878,15 +878,12 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
|
|||
if(invited && subscrb)
|
||||
subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPID, tr("Member")) ;
|
||||
|
||||
if (is_own_id)
|
||||
{
|
||||
QFont font = subitem->font(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) ;
|
||||
font.setBold(true) ;
|
||||
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
|
||||
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
|
||||
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
|
||||
}
|
||||
}
|
||||
QFont font = ui->treeWidget_membership->font() ;
|
||||
font.setBold(is_own_id) ;
|
||||
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
|
||||
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
|
||||
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
|
||||
}
|
||||
|
||||
// add all items
|
||||
item->addChildren(new_sub_items);
|
||||
|
@ -1389,73 +1386,42 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
|
|||
uint32_t item_flags = 0;
|
||||
|
||||
/* do filtering */
|
||||
bool ok = false;
|
||||
if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)
|
||||
{
|
||||
if (isLinkedToOwnNode && (accept & RSID_FILTER_YOURSELF))
|
||||
{
|
||||
ok = true;
|
||||
item_flags |= RSID_FILTER_YOURSELF ;
|
||||
}
|
||||
{
|
||||
if (isLinkedToOwnNode && (accept & RSID_FILTER_YOURSELF))
|
||||
item_flags |= RSID_FILTER_YOURSELF ;
|
||||
|
||||
if (data.mPgpKnown && (accept & RSID_FILTER_FRIENDS))
|
||||
{
|
||||
ok = true;
|
||||
item_flags |= RSID_FILTER_FRIENDS ;
|
||||
}
|
||||
if (data.mPgpKnown && (accept & RSID_FILTER_FRIENDS))
|
||||
item_flags |= RSID_FILTER_FRIENDS ;
|
||||
|
||||
if (accept & RSID_FILTER_OTHERS)
|
||||
{
|
||||
ok = true;
|
||||
item_flags |= RSID_FILTER_OTHERS ;
|
||||
}
|
||||
}
|
||||
else if (accept & RSID_FILTER_PSEUDONYMS)
|
||||
{
|
||||
ok = true;
|
||||
item_flags |= RSID_FILTER_PSEUDONYMS ;
|
||||
}
|
||||
if (accept & RSID_FILTER_OTHERS)
|
||||
item_flags |= RSID_FILTER_OTHERS ;
|
||||
}
|
||||
else if (accept & RSID_FILTER_PSEUDONYMS)
|
||||
item_flags |= RSID_FILTER_PSEUDONYMS ;
|
||||
|
||||
if (isOwnId && (accept & RSID_FILTER_OWNED_BY_YOU))
|
||||
{
|
||||
ok = true;
|
||||
item_flags |= RSID_FILTER_OWNED_BY_YOU ;
|
||||
}
|
||||
if (isOwnId && (accept & RSID_FILTER_OWNED_BY_YOU))
|
||||
item_flags |= RSID_FILTER_OWNED_BY_YOU ;
|
||||
|
||||
if (isBanned && (accept & RSID_FILTER_BANNED))
|
||||
{
|
||||
ok = true;
|
||||
item_flags |= RSID_FILTER_BANNED ;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
if (item_flags == 0)
|
||||
return false;
|
||||
|
||||
if (!item)
|
||||
{
|
||||
item = new TreeWidgetItem();
|
||||
}
|
||||
|
||||
item = new TreeWidgetItem();
|
||||
|
||||
|
||||
item->setText(RSID_COL_NICKNAME, QString::fromUtf8(data.mMeta.mGroupName.c_str()).left(RSID_MAXIMUM_NICKNAME_SIZE));
|
||||
item->setData(RSID_COL_NICKNAME, Qt::UserRole, QString::fromStdString(data.mMeta.mGroupId.toStdString()));
|
||||
item->setText(RSID_COL_KEYID, QString::fromStdString(data.mMeta.mGroupId.toStdString()));
|
||||
|
||||
if(isBanned)
|
||||
{
|
||||
//TODO (Phenom): Add qproperty for these text colors in stylesheets
|
||||
item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QColor(Qt::red));
|
||||
item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QColor(Qt::red));
|
||||
item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QColor(Qt::red));
|
||||
item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QColor(Qt::red));
|
||||
}
|
||||
else
|
||||
{
|
||||
item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QVariant());
|
||||
item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QVariant());
|
||||
item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QVariant());
|
||||
item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QVariant());
|
||||
}
|
||||
//TODO (Phenom): Add qproperty for these text colors in stylesheets
|
||||
item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
|
||||
item->setData(RSID_COL_KEYID , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
|
||||
item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
|
||||
item->setData(RSID_COL_VOTES , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
|
||||
|
||||
item->setData(RSID_COL_KEYID, Qt::UserRole,QVariant(item_flags)) ;
|
||||
item->setTextAlignment(RSID_COL_VOTES, Qt::AlignRight | Qt::AlignVCenter);
|
||||
|
@ -1466,16 +1432,9 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
|
|||
RSID_COL_VOTES,SortRole,
|
||||
static_cast<uint32_t>(idd.mReputation.mOverallReputationLevel));
|
||||
|
||||
if(isOwnId)
|
||||
{
|
||||
QFont font = item->font(RSID_COL_NICKNAME) ;
|
||||
|
||||
font.setBold(true) ;
|
||||
item->setFont(RSID_COL_NICKNAME,font) ;
|
||||
item->setFont(RSID_COL_IDTYPE,font) ;
|
||||
item->setFont(RSID_COL_KEYID,font) ;
|
||||
|
||||
QString tooltip = tr("This identity is owned by you");
|
||||
if(isOwnId)
|
||||
{
|
||||
QString tooltip = tr("This identity is owned by you");
|
||||
|
||||
if(idd.mFlags & RS_IDENTITY_FLAGS_IS_DEPRECATED)
|
||||
{
|
||||
|
@ -1487,10 +1446,16 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
|
|||
tooltip += tr("\nThis identity has a unsecure fingerprint (It's probably quite old).\nYou should get rid of it now and use a new one.\nThese identities will soon be not supported anymore.") ;
|
||||
}
|
||||
|
||||
item->setToolTip(RSID_COL_NICKNAME, tooltip) ;
|
||||
item->setToolTip(RSID_COL_KEYID, tooltip) ;
|
||||
item->setToolTip(RSID_COL_IDTYPE, tooltip) ;
|
||||
}
|
||||
item->setToolTip(RSID_COL_NICKNAME, tooltip) ;
|
||||
item->setToolTip(RSID_COL_KEYID, tooltip) ;
|
||||
item->setToolTip(RSID_COL_IDTYPE, tooltip) ;
|
||||
}
|
||||
QFont font = ui->idTreeWidget->font() ;
|
||||
font.setBold(isOwnId) ;
|
||||
item->setFont(RSID_COL_NICKNAME,font) ;
|
||||
item->setFont(RSID_COL_IDTYPE,font) ;
|
||||
item->setFont(RSID_COL_KEYID,font) ;
|
||||
|
||||
|
||||
//QPixmap pixmap ;
|
||||
//
|
||||
|
@ -1501,8 +1466,6 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
|
|||
// Icon Place Holder
|
||||
item->setIcon(RSID_COL_NICKNAME,FilesDefs::getIconFromQtResourcePath(":/icons/png/anonymous.png"));
|
||||
|
||||
QString tooltip;
|
||||
|
||||
if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)
|
||||
{
|
||||
if (data.mPgpKnown)
|
||||
|
@ -1513,25 +1476,25 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
|
|||
item->setToolTip(RSID_COL_IDTYPE,"Verified signature from node "+QString::fromStdString(data.mPgpId.toStdString())) ;
|
||||
|
||||
|
||||
tooltip += tr("Node name:")+" " + QString::fromUtf8(details.name.c_str()) + "\n";
|
||||
tooltip += tr("Node Id :")+" " + QString::fromStdString(data.mPgpId.toStdString()) ;
|
||||
QString tooltip = tr("Node name:")+" " + QString::fromUtf8(details.name.c_str()) + "\n";
|
||||
tooltip += tr("Node Id :")+" " + QString::fromStdString(data.mPgpId.toStdString()) ;
|
||||
item->setToolTip(RSID_COL_KEYID,tooltip) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString txt = tr("[Unknown node]");
|
||||
QString txt = tr("[Unknown node]");
|
||||
item->setText(RSID_COL_IDTYPE, txt);
|
||||
|
||||
if(!data.mPgpId.isNull())
|
||||
{
|
||||
item->setToolTip(RSID_COL_IDTYPE,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
|
||||
item->setToolTip(RSID_COL_KEYID,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->setToolTip(RSID_COL_IDTYPE,tr("Unchecked signature")) ;
|
||||
item->setToolTip(RSID_COL_KEYID,tr("Unchecked signature")) ;
|
||||
}
|
||||
|
||||
if(!data.mPgpId.isNull())
|
||||
{
|
||||
item->setToolTip(RSID_COL_IDTYPE,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
|
||||
item->setToolTip(RSID_COL_KEYID,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->setToolTip(RSID_COL_IDTYPE,tr("Unchecked signature")) ;
|
||||
item->setToolTip(RSID_COL_KEYID,tr("Unchecked signature")) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2224,8 +2187,9 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
|
|||
|
||||
if(!one_item_owned_by_you)
|
||||
{
|
||||
QWidget *widget = new QWidget(contextMenu);
|
||||
widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
QFrame *widget = new QFrame(contextMenu);
|
||||
widget->setObjectName("gradFrame"); //Use qss
|
||||
//widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
|
||||
// create menu header
|
||||
QHBoxLayout *hbox = new QHBoxLayout(widget);
|
||||
|
@ -2233,12 +2197,14 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
|
|||
hbox->setSpacing(6);
|
||||
|
||||
QLabel *iconLabel = new QLabel(widget);
|
||||
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
|
||||
iconLabel->setObjectName("trans_Icon");
|
||||
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
|
||||
iconLabel->setPixmap(pix);
|
||||
iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width());
|
||||
hbox->addWidget(iconLabel);
|
||||
|
||||
QLabel *textLabel = new QLabel("<strong>" + ui->titleBarLabel->text() + "</strong>", widget);
|
||||
textLabel->setObjectName("trans_Text");
|
||||
hbox->addWidget(textLabel);
|
||||
|
||||
QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
|
|
@ -218,6 +218,11 @@
|
|||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
|
@ -1046,6 +1051,11 @@ border-image: url(:/images/closepressed.png)
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidget_membership">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QColorDialog>
|
||||
#include <QDesktopServices>
|
||||
#include <QIcon>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QPixmap>
|
||||
#include <QStatusBar>
|
||||
|
@ -1689,3 +1690,19 @@ void MainWindow::setCompactStatusMode(bool compact)
|
|||
ratesstatus->setCompactMode(compact);
|
||||
//opModeStatus: TODO Show only ???
|
||||
}
|
||||
|
||||
Gui_InputDialogReturn MainWindow::guiInputDialog(const QString& windowTitle, const QString& labelText, QLineEdit::EchoMode textEchoMode, bool modal)
|
||||
{
|
||||
|
||||
QInputDialog dialog(this);
|
||||
dialog.setWindowTitle(windowTitle);
|
||||
dialog.setLabelText(labelText);
|
||||
dialog.setTextEchoMode(textEchoMode);
|
||||
dialog.setModal(modal);
|
||||
|
||||
Gui_InputDialogReturn ret;
|
||||
ret.execReturn = dialog.exec();
|
||||
ret.textValue = dialog.textValue();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#ifndef _MainWindow_H
|
||||
#define _MainWindow_H
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <set>
|
||||
|
||||
|
@ -74,6 +75,14 @@ class MessengerWindow;
|
|||
class ApplicationWindow;
|
||||
#endif
|
||||
|
||||
|
||||
struct Gui_InputDialogReturn
|
||||
{
|
||||
int execReturn;
|
||||
QString textValue;
|
||||
};
|
||||
Q_DECLARE_METATYPE(Gui_InputDialogReturn);
|
||||
|
||||
class MainWindow : public RWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -192,7 +201,7 @@ public:
|
|||
}
|
||||
|
||||
static bool hiddenmode;
|
||||
|
||||
|
||||
public slots:
|
||||
void receiveNewArgs(QStringList args);
|
||||
void displayErrorMessage(int,int,const QString&) ;
|
||||
|
@ -210,9 +219,35 @@ public slots:
|
|||
void showBandwidthGraph();
|
||||
|
||||
void toggleStatusToolTip(bool toggle);
|
||||
|
||||
/**
|
||||
* @brief Create a QInputDialog. This must be called in MainWindow thread because Widgets must be created in the GUI thread.
|
||||
* Here an exemple how to call it:
|
||||
*
|
||||
* bool sameThread = QThread::currentThread() == qApp->thread();
|
||||
* Gui_InputDialogReturn ret;
|
||||
* qRegisterMetaType<Gui_InputDialogReturn>("Gui_InputDialogReturn");
|
||||
* QMetaObject::invokeMethod( MainWindow::getInstance()
|
||||
* , "guiInputDialog"
|
||||
* , sameThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection
|
||||
* , Q_RETURN_ARG(Gui_InputDialogReturn, ret)
|
||||
* , Q_ARG(QString, windowTitle)
|
||||
* , Q_ARG(QString, labelText)
|
||||
* , Q_ARG(QLineEdit::EchoMode, textEchoMode)
|
||||
* , Q_ARG(bool, modal)
|
||||
* );
|
||||
*
|
||||
* @param windowTitle: the window title (caption).
|
||||
* @param labelText: label's text which describes what needs to be input.
|
||||
* @param textEchoMode: the echo mode for the text value.
|
||||
* @param modal: pop up the dialog as modal or modeless.
|
||||
* @return Gui_InputDialogReturn ( Accepted(1)|Rejected(0), text value for the input dialog)
|
||||
*/
|
||||
Gui_InputDialogReturn guiInputDialog(const QString& windowTitle, const QString& labelText, QLineEdit::EchoMode textEchoMode, bool modal);
|
||||
|
||||
protected:
|
||||
/** Default Constructor */
|
||||
MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
|
||||
MainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
|
||||
void closeEvent(QCloseEvent *);
|
||||
|
||||
|
|
|
@ -122,14 +122,14 @@ void BoardPostDisplayWidgetBase::setReadStatus(bool isNew, bool isUnread)
|
|||
void BoardPostDisplayWidget_compact::doExpand(bool e)
|
||||
{
|
||||
#ifdef DEBUG_BOARDPOSTDISPLAYWIDGET
|
||||
std::cerr << "Expanding" << std::endl;
|
||||
std::cerr << "Expanding" << std::endl;
|
||||
#endif
|
||||
if(e)
|
||||
ui->frame_notes->show();
|
||||
else
|
||||
ui->frame_notes->hide();
|
||||
if(e)
|
||||
ui->frame_notes->show();
|
||||
else
|
||||
ui->frame_notes->hide();
|
||||
|
||||
emit expand(mPost.mMeta.mMsgId,e);
|
||||
emit expand(mPost.mMeta.mMsgId,e);
|
||||
}
|
||||
|
||||
void BoardPostDisplayWidgetBase::loadComments(bool e)
|
||||
|
@ -144,7 +144,7 @@ void BoardPostDisplayWidgetBase::readToggled()
|
|||
emit changeReadStatusRequested(mPost.mMeta.mMsgId,s);
|
||||
}
|
||||
|
||||
void BoardPostDisplayWidgetBase::setup()
|
||||
void BoardPostDisplayWidgetBase::baseSetup()
|
||||
{
|
||||
// show/hide things based on the view type
|
||||
|
||||
|
@ -166,8 +166,6 @@ void BoardPostDisplayWidgetBase::setup()
|
|||
QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this);
|
||||
connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(handleCopyLinkClicked()));
|
||||
|
||||
int S = QFontMetricsF(font()).height() ;
|
||||
|
||||
readButton()->setChecked(false);
|
||||
|
||||
QMenu *menu = new QMenu();
|
||||
|
@ -184,6 +182,7 @@ void BoardPostDisplayWidgetBase::setup()
|
|||
if(redacted)
|
||||
{
|
||||
commentButton()->setDisabled(true);
|
||||
shareButton()->setDisabled(true);
|
||||
voteUpButton()->setDisabled(true);
|
||||
voteDownButton()->setDisabled(true);
|
||||
fromLabel()->setId(mPost.mMeta.mAuthorId);
|
||||
|
@ -196,8 +195,6 @@ void BoardPostDisplayWidgetBase::setup()
|
|||
}
|
||||
else
|
||||
{
|
||||
QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png");
|
||||
|
||||
QDateTime qtime;
|
||||
qtime.setTime_t(mPost.mMeta.mPublishTs);
|
||||
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
|
||||
|
@ -295,16 +292,7 @@ BoardPostDisplayWidget_compact::BoardPostDisplayWidget_compact(const RsPostedPos
|
|||
: BoardPostDisplayWidgetBase(post,display_flags,parent), ui(new Ui::BoardPostDisplayWidget_compact())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setup();
|
||||
|
||||
ui->right_VL->addStretch();
|
||||
ui->right_VL->setAlignment(Qt::AlignTop);
|
||||
ui->topLayout->setAlignment(Qt::AlignTop);
|
||||
ui->arrowsLayout->addStretch();
|
||||
ui->arrowsLayout->setAlignment(Qt::AlignTop);
|
||||
ui->feedFrame_VL->addStretch();
|
||||
|
||||
adjustSize();
|
||||
BoardPostDisplayWidget_compact::setup();
|
||||
}
|
||||
|
||||
BoardPostDisplayWidget_compact::~BoardPostDisplayWidget_compact()
|
||||
|
@ -314,7 +302,7 @@ BoardPostDisplayWidget_compact::~BoardPostDisplayWidget_compact()
|
|||
|
||||
void BoardPostDisplayWidget_compact::setup()
|
||||
{
|
||||
BoardPostDisplayWidgetBase::setup();
|
||||
baseSetup();
|
||||
|
||||
// show/hide things based on the view type
|
||||
|
||||
|
@ -356,7 +344,7 @@ void BoardPostDisplayWidget_compact::setup()
|
|||
QObject::connect(ui->expandButton, SIGNAL(toggled(bool)), this, SLOT(doExpand(bool)));
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setHtml(notes()->text());
|
||||
doc.setHtml(BoardPostDisplayWidget_compact::notes()->text());
|
||||
|
||||
if(mDisplayFlags & SHOW_NOTES)
|
||||
{
|
||||
|
@ -427,16 +415,7 @@ BoardPostDisplayWidget_card::BoardPostDisplayWidget_card(const RsPostedPost& pos
|
|||
: BoardPostDisplayWidgetBase(post,display_flags,parent), ui(new Ui::BoardPostDisplayWidget_card())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setup();
|
||||
|
||||
ui->right_VL->addStretch();
|
||||
ui->right_VL->setAlignment(Qt::AlignTop);
|
||||
ui->topLayout->setAlignment(Qt::AlignTop);
|
||||
ui->arrowsLayout->addStretch();
|
||||
ui->arrowsLayout->setAlignment(Qt::AlignTop);
|
||||
ui->feedFrame_VL->addStretch();
|
||||
|
||||
adjustSize();
|
||||
BoardPostDisplayWidget_card::setup();
|
||||
}
|
||||
|
||||
BoardPostDisplayWidget_card::~BoardPostDisplayWidget_card()
|
||||
|
@ -446,7 +425,7 @@ BoardPostDisplayWidget_card::~BoardPostDisplayWidget_card()
|
|||
|
||||
void BoardPostDisplayWidget_card::setup()
|
||||
{
|
||||
BoardPostDisplayWidgetBase::setup();
|
||||
baseSetup();
|
||||
|
||||
RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId);
|
||||
bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE);
|
||||
|
@ -463,7 +442,6 @@ void BoardPostDisplayWidget_card::setup()
|
|||
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
|
||||
// Wiping data - as its been passed to thumbnail.
|
||||
|
||||
QPixmap scaledpixmap;
|
||||
if(pixmap.width() > 800){
|
||||
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
|
||||
ui->pictureLabel->setPixmap(scaledpixmap);
|
||||
|
@ -478,10 +456,10 @@ void BoardPostDisplayWidget_card::setup()
|
|||
}
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setHtml(notes()->text());
|
||||
doc.setHtml(BoardPostDisplayWidget_card::notes()->text());
|
||||
|
||||
if(doc.toPlainText().trimmed().isEmpty())
|
||||
notes()->hide();
|
||||
BoardPostDisplayWidget_card::notes()->hide();
|
||||
}
|
||||
|
||||
QToolButton *BoardPostDisplayWidget_card::voteUpButton() { return ui->voteUpButton; }
|
||||
|
|
|
@ -62,10 +62,11 @@ public:
|
|||
|
||||
static const char *DEFAULT_BOARD_IMAGE;
|
||||
|
||||
protected slots:
|
||||
protected:
|
||||
/* GxsGroupFeedItem */
|
||||
|
||||
virtual void setup(); // to be overloaded by the different views
|
||||
void baseSetup();
|
||||
virtual void setup() =0; // to be overloaded by the different views
|
||||
|
||||
virtual QToolButton *voteUpButton() =0;
|
||||
virtual QToolButton *commentButton() =0;
|
||||
|
@ -81,6 +82,7 @@ protected slots:
|
|||
virtual QToolButton *shareButton() =0;
|
||||
virtual QFrame *feedFrame() =0;
|
||||
|
||||
protected slots:
|
||||
void loadComments(bool e);
|
||||
void readToggled();
|
||||
void setReadStatus(bool isNew, bool isUnread) ;
|
||||
|
|
|
@ -90,7 +90,10 @@
|
|||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="voteFrame">
|
||||
<layout class="QVBoxLayout" name="arrowsLayout">
|
||||
<layout class="QVBoxLayout" name="voteFrame_VL">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -173,6 +176,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="voteFrame_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -277,7 +287,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="from_HSpacer">
|
||||
<spacer name="from_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -340,7 +350,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="pictureLabel_HSpacer">
|
||||
<spacer name="pictureLabel_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -401,16 +411,16 @@
|
|||
<iconset resource="Posted_images.qrc">
|
||||
<normaloff>:/images/share.png</normaloff>:/images/share.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="buttons_HSpacer">
|
||||
<spacer name="buttons_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -424,10 +434,30 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="right_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="feedFrame_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>542</width>
|
||||
<height>150</height>
|
||||
<height>151</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -90,7 +90,22 @@
|
|||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="voteFrame">
|
||||
<layout class="QVBoxLayout" name="arrowsLayout">
|
||||
<layout class="QVBoxLayout" name="voteFrame_VL">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="voteUpButton">
|
||||
<property name="sizePolicy">
|
||||
|
@ -161,6 +176,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="voteFrame_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -287,7 +309,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="from_HSpacer">
|
||||
<spacer name="from_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -305,7 +327,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttons_HM">
|
||||
<layout class="QHBoxLayout" name="buttons_HL">
|
||||
<item>
|
||||
<widget class="QToolButton" name="commentButton">
|
||||
<property name="text">
|
||||
|
@ -400,7 +422,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="buttons_HSpacer">
|
||||
<spacer name="buttons_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -414,6 +436,13 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="right_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -473,6 +502,13 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="feedFrame_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>614</width>
|
||||
<height>182</height>
|
||||
<height>198</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -16,7 +16,7 @@
|
|||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<layout class="QGridLayout" name="PostedCardView_GL">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -118,7 +118,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QHBoxLayout" name="from_HL">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
|
@ -216,7 +216,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<spacer name="from_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -241,30 +241,27 @@
|
|||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="voteFrame_VL">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="voteUpButton">
|
||||
|
@ -337,26 +334,20 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<spacer name="voteFrame_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="buttons_HL">
|
||||
<item>
|
||||
<widget class="QToolButton" name="commentButton">
|
||||
<property name="text">
|
||||
|
@ -389,7 +380,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<spacer name="buttons_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -461,7 +452,7 @@
|
|||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QFrame" name="picture_frame">
|
||||
<layout class="QHBoxLayout" name="horizontalPictureLayout">
|
||||
<layout class="QHBoxLayout" name="picture_frame_HL">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -485,7 +476,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<spacer name="picture_frame_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -513,9 +504,9 @@
|
|||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../images.qrc"/>
|
||||
<include location="../icons.qrc"/>
|
||||
<include location="Posted_images.qrc"/>
|
||||
<include location="../icons.qrc"/>
|
||||
<include location="../images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -41,9 +41,12 @@
|
|||
#include "gui/common/FilesDefs.h"
|
||||
|
||||
/* View Page */
|
||||
#define VIEW_POST 1
|
||||
#define VIEW_IMAGE 2
|
||||
#define VIEW_LINK 3
|
||||
#define VIEW_POST 0
|
||||
#define VIEW_IMAGE 1
|
||||
#define VIEW_LINK 2
|
||||
/* View Image */
|
||||
#define IMG_ATTACH 0
|
||||
#define IMG_PICTURE 1
|
||||
|
||||
PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGroupId& grpId, const RsGxsId& default_author, QWidget *parent):
|
||||
QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
|
||||
|
@ -54,7 +57,6 @@ PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGrou
|
|||
Settings->loadWidgetInformation(this);
|
||||
|
||||
connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createPost()));
|
||||
connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(ui->addPicButton, SIGNAL(clicked() ), this , SLOT(addPicture()));
|
||||
connect(ui->RichTextEditWidget, SIGNAL(textSizeOk(bool)),ui->postButton, SLOT(setEnabled(bool)));
|
||||
|
||||
|
@ -84,6 +86,7 @@ PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGrou
|
|||
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setPage(int)));
|
||||
|
||||
ui->removeButton->hide();
|
||||
ui->stackedWidgetPicture->setCurrentIndex(IMG_ATTACH);
|
||||
|
||||
/* load settings */
|
||||
processSettings(true);
|
||||
|
@ -202,7 +205,6 @@ void PostedCreatePostDialog::addPicture()
|
|||
|
||||
// select a picture file
|
||||
if (misc::getOpenFileName(window(), RshareSettings::LASTDIR_IMAGES, tr("Load Picture File"), "Pictures (*.png *.xpm *.jpg *.jpeg *.gif *.webp )", imagefilename)) {
|
||||
QString encodedImage;
|
||||
QImage image;
|
||||
if (image.load(imagefilename) == false) {
|
||||
fprintf (stderr, "RsHtml::makeEmbeddedImage() - image \"%s\" can't be load\n", imagefilename.toLatin1().constData());
|
||||
|
@ -213,7 +215,7 @@ void PostedCreatePostDialog::addPicture()
|
|||
QImage opt;
|
||||
if(ImageUtil::optimizeSizeBytes(imagebytes, image, opt, 640*480, MAXMESSAGESIZE - 2000)) { //Leave space for other stuff
|
||||
ui->imageLabel->setPixmap(QPixmap::fromImage(opt));
|
||||
ui->stackedWidgetPicture->setCurrentIndex(1);
|
||||
ui->stackedWidgetPicture->setCurrentIndex(IMG_PICTURE);
|
||||
ui->removeButton->show();
|
||||
} else {
|
||||
imagefilename = "";
|
||||
|
@ -259,45 +261,24 @@ int PostedCreatePostDialog::viewMode()
|
|||
|
||||
void PostedCreatePostDialog::setPage(int viewMode)
|
||||
{
|
||||
switch (viewMode) {
|
||||
case VIEW_POST:
|
||||
ui->stackedWidget->setCurrentIndex(0);
|
||||
if( (viewMode < 0) || (viewMode > ui->stackedWidget->count()-1) )
|
||||
viewMode = VIEW_POST;
|
||||
|
||||
ui->viewPostButton->setChecked(true);
|
||||
ui->viewImageButton->setChecked(false);
|
||||
ui->viewLinkButton->setChecked(false);
|
||||
ui->stackedWidget->setCurrentIndex(viewMode);
|
||||
|
||||
break;
|
||||
case VIEW_IMAGE:
|
||||
ui->stackedWidget->setCurrentIndex(1);
|
||||
ui->viewPostButton ->setChecked(viewMode==VIEW_POST);
|
||||
ui->viewImageButton->setChecked(viewMode==VIEW_IMAGE);
|
||||
ui->viewLinkButton ->setChecked(viewMode==VIEW_LINK);
|
||||
|
||||
ui->viewImageButton->setChecked(true);
|
||||
ui->viewPostButton->setChecked(false);
|
||||
ui->viewLinkButton->setChecked(false);
|
||||
|
||||
break;
|
||||
case VIEW_LINK:
|
||||
ui->stackedWidget->setCurrentIndex(2);
|
||||
|
||||
ui->viewLinkButton->setChecked(true);
|
||||
ui->viewPostButton->setChecked(false);
|
||||
ui->viewImageButton->setChecked(false);
|
||||
|
||||
break;
|
||||
default:
|
||||
setPage(VIEW_POST);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PostedCreatePostDialog::on_removeButton_clicked()
|
||||
{
|
||||
imagefilename = "";
|
||||
imagebytes.clear();
|
||||
QPixmap empty;
|
||||
ui->imageLabel->setPixmap(empty);
|
||||
ui->imageLabel->setPixmap(QPixmap());
|
||||
ui->removeButton->hide();
|
||||
ui->stackedWidgetPicture->setCurrentIndex(0);
|
||||
ui->stackedWidgetPicture->setCurrentIndex(IMG_ATTACH);
|
||||
}
|
||||
|
||||
void PostedCreatePostDialog::reject()
|
||||
|
|
|
@ -52,7 +52,7 @@ private slots:
|
|||
void addPicture();
|
||||
void on_removeButton_clicked();
|
||||
void fileHashingFinished(QList<HashedFile> hashedFiles);
|
||||
void reject();
|
||||
void reject() override; //QDialog
|
||||
|
||||
void setPage(int viewMode);
|
||||
private:
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
<item row="0" column="1">
|
||||
<widget class="QStackedWidget" name="stackedWidgetPicture">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="PageAttach">
|
||||
<layout class="QGridLayout" name="PageAttach_GL">
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<layout class="QGridLayout" name="PostedItem_GL">
|
||||
<property name="leftMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
|
@ -82,21 +82,21 @@
|
|||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="voteFrame_VL">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="voteUpButton">
|
||||
|
@ -169,26 +169,20 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<spacer name="voteFrame_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" rowspan="2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<layout class="QVBoxLayout" name="thumbnail_VL">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
|
@ -233,7 +227,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<spacer name="thumbnail_VS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
|
@ -248,7 +242,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<layout class="QVBoxLayout" name="title_VL">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
|
@ -286,7 +280,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="newCommHLayout">
|
||||
<layout class="QHBoxLayout" name="newComm_HL">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -312,7 +306,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QVBoxLayout" name="from_VL">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -320,7 +314,7 @@
|
|||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QHBoxLayout" name="from_HL">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
|
@ -416,7 +410,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<spacer name="from_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -434,7 +428,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonHLayout">
|
||||
<layout class="QHBoxLayout" name="buttons_HL">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
|
@ -567,7 +561,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<spacer name="buttons_HS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -631,9 +625,9 @@
|
|||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="frame_picture_HL">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<spacer name="frame_picture_LHS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -659,7 +653,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<spacer name="frame_picture_RHS">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -685,7 +679,7 @@
|
|||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<layout class="QGridLayout" name="frame_notes_GL">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
|
@ -726,17 +720,17 @@
|
|||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>GxsIdLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>gui/gxs/GxsIdLabel.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ElidedLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>gui/common/ElidedLabel.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GxsIdLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>gui/gxs/GxsIdLabel.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ClickableLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
|
|
|
@ -58,9 +58,7 @@
|
|||
// number of posts to show at once.
|
||||
#define POSTS_CHUNK_SIZE 25
|
||||
|
||||
/****
|
||||
* #define DEBUG_POSTED
|
||||
***/
|
||||
//#define DEBUG_POSTED
|
||||
|
||||
static const int POSTED_TABS_POSTS = 1;
|
||||
|
||||
|
@ -87,8 +85,12 @@ std::ostream& operator<<(std::ostream& o,const QSize& s) { return o << s.width()
|
|||
|
||||
void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
|
||||
{
|
||||
#ifdef DEBUG_POSTED
|
||||
if(option.state & QStyle::State_Selected) RS_DBG("Selected");
|
||||
#endif
|
||||
|
||||
if((option.state & QStyle::State_Selected)) // Avoids double display. The selected widget is never exactly the size of the rendered one,
|
||||
return; // so when selected, we only draw the selected one.
|
||||
return; // so when selected, we only draw the selected one.
|
||||
|
||||
// prepare
|
||||
painter->save();
|
||||
|
@ -98,7 +100,7 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem &
|
|||
|
||||
painter->save();
|
||||
|
||||
painter->fillRect( option.rect, option.palette.background());
|
||||
painter->fillRect( option.rect, option.palette.window());
|
||||
painter->restore();
|
||||
|
||||
QPixmap pixmap(option.rect.size());
|
||||
|
@ -109,9 +111,9 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem &
|
|||
BoardPostDisplayWidget_compact w(post,displayFlags(post.mMeta.mMsgId),nullptr);
|
||||
|
||||
w.setFixedSize(option.rect.size());
|
||||
|
||||
w.updateGeometry();
|
||||
w.adjustSize();
|
||||
|
||||
w.render(&pixmap,QPoint(0,0),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background
|
||||
}
|
||||
else
|
||||
|
@ -121,6 +123,7 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem &
|
|||
w.setFixedSize(option.rect.size());
|
||||
w.updateGeometry();
|
||||
w.adjustSize();
|
||||
|
||||
w.render(&pixmap,QPoint(0,0),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background
|
||||
}
|
||||
|
||||
|
@ -145,6 +148,10 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem &
|
|||
|
||||
painter->save();
|
||||
painter->drawPixmap(option.rect.topLeft(), pixmap /*,.scaled(option.rect.width(),option.rect.width()*w.height()/(float)w.width(),Qt::KeepAspectRatio,Qt::SmoothTransformation)*/);
|
||||
#ifdef DEBUG_POSTED
|
||||
painter->drawText(option.rect.bottomLeft(),QString::number(time(nullptr)));
|
||||
RS_DBG("DisplayMode=", mDisplayMode == BoardPostDisplayWidget_compact::DISPLAY_MODE_COMPACT? "Compact":"Card", " Title:", post.mMeta.mMsgName.c_str());
|
||||
#endif
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
|
@ -193,40 +200,46 @@ uint8_t PostedPostDelegate::displayFlags(const RsGxsMessageId &id) const
|
|||
|
||||
QWidget *PostedPostDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const
|
||||
{
|
||||
RsPostedPost post = index.data(Qt::UserRole).value<RsPostedPost>() ;
|
||||
|
||||
if(index.column() == RsPostedPostsModel::COLUMN_POSTS)
|
||||
{
|
||||
QWidget *w ;
|
||||
if (!index.isValid())
|
||||
return nullptr;
|
||||
|
||||
if(mDisplayMode==BoardPostDisplayWidget_compact::DISPLAY_MODE_COMPACT)
|
||||
w = new BoardPostDisplayWidget_compact(post,displayFlags(post.mMeta.mMsgId),parent);
|
||||
else
|
||||
w = new BoardPostDisplayWidget_card(post,displayFlags(post.mMeta.mMsgId),parent);
|
||||
if(index.column() != RsPostedPostsModel::COLUMN_POSTS)
|
||||
return nullptr;
|
||||
|
||||
QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(voteMsg(RsGxsGrpMsgIdPair,bool)));
|
||||
QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(expandItem(RsGxsMessageId,bool)));
|
||||
QObject::connect(w,SIGNAL(commentsRequested(const RsGxsMessageId&,bool)),mPostListWidget,SLOT(openComments(const RsGxsMessageId&)));
|
||||
QObject::connect(w,SIGNAL(changeReadStatusRequested(const RsGxsMessageId&,bool)),mPostListWidget,SLOT(changeReadStatus(const RsGxsMessageId&,bool)));
|
||||
QWidget *w ;
|
||||
RsPostedPost post = index.data(Qt::UserRole).value<RsPostedPost>() ;
|
||||
|
||||
// All other interactions with the widget should cause the msg to be set as read.
|
||||
QObject::connect(w,SIGNAL(thumbnailOpenned()),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(commentsRequested(const RsGxsMessageId&,bool)),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(shareButtonClicked()),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(copylinkClicked()),mPostListWidget,SLOT(copyMessageLink()));
|
||||
#ifdef DEBUG_POSTED
|
||||
RS_DBG("Title:", post.mMeta.mMsgName.c_str());
|
||||
#endif
|
||||
|
||||
w->setFixedSize(option.rect.size());
|
||||
w->adjustSize();
|
||||
w->updateGeometry();
|
||||
w->adjustSize();
|
||||
if(mDisplayMode==BoardPostDisplayWidget_compact::DISPLAY_MODE_COMPACT)
|
||||
w = new BoardPostDisplayWidget_compact(post,displayFlags(post.mMeta.mMsgId),parent);
|
||||
else
|
||||
w = new BoardPostDisplayWidget_card(post,displayFlags(post.mMeta.mMsgId),parent);
|
||||
|
||||
return w;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(voteMsg(RsGxsGrpMsgIdPair,bool)));
|
||||
QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(expandItem(RsGxsMessageId,bool)));
|
||||
QObject::connect(w,SIGNAL(commentsRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(openComments(RsGxsMessageId)));
|
||||
QObject::connect(w,SIGNAL(changeReadStatusRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(changeReadStatus(RsGxsMessageId,bool)));
|
||||
|
||||
// All other interactions with the widget should cause the msg to be set as read.
|
||||
QObject::connect(w,SIGNAL(thumbnailOpenned()),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(commentsRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(shareButtonClicked()),mPostListWidget,SLOT(markCurrentPostAsRead()));
|
||||
QObject::connect(w,SIGNAL(copylinkClicked()),mPostListWidget,SLOT(copyMessageLink()));
|
||||
|
||||
w->setGeometry(option.rect);
|
||||
w->adjustSize();
|
||||
w->updateGeometry();
|
||||
w->adjustSize();
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
void PostedPostDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
|
||||
{
|
||||
editor->setGeometry(option.rect);
|
||||
|
@ -264,13 +277,11 @@ PostedListWidgetWithModel::PostedListWidgetWithModel(const RsGxsGroupId& postedI
|
|||
connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(nextPosts()));
|
||||
connect(ui->prevButton,SIGNAL(clicked()),this,SLOT(prevPosts()));
|
||||
|
||||
connect(ui->postsTree,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(postContextMenu(const QPoint&)));
|
||||
connect(ui->postsTree,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(postContextMenu(QPoint)));
|
||||
connect(ui->viewModeButton,SIGNAL(clicked()),this,SLOT(switchDisplayMode()));
|
||||
|
||||
connect(mPostedPostsModel,SIGNAL(boardPostsLoaded()),this,SLOT(postPostLoad()));
|
||||
|
||||
QFontMetricsF fm(font());
|
||||
|
||||
/* Setup UI helper */
|
||||
|
||||
/* Connect signals */
|
||||
|
@ -374,23 +385,20 @@ void PostedListWidgetWithModel::filterItems(QString text)
|
|||
|
||||
void PostedListWidgetWithModel::nextPosts()
|
||||
{
|
||||
ui->postsTree->selectionModel()->clear();
|
||||
if(mPostedPostsModel->displayedStartPostIndex() + POSTS_CHUNK_SIZE < mPostedPostsModel->filteredPostsCount())
|
||||
{
|
||||
mPostedPostsModel->setPostsInterval(POSTS_CHUNK_SIZE+mPostedPostsModel->displayedStartPostIndex(),POSTS_CHUNK_SIZE);
|
||||
if(mPostedPostsModel->displayedStartPostIndex() + POSTS_CHUNK_SIZE < mPostedPostsModel->filteredPostsCount())
|
||||
{
|
||||
mPostedPostsModel->setPostsInterval(POSTS_CHUNK_SIZE+mPostedPostsModel->displayedStartPostIndex(),POSTS_CHUNK_SIZE);
|
||||
updateShowLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PostedListWidgetWithModel::prevPosts()
|
||||
{
|
||||
ui->postsTree->selectionModel()->clear();
|
||||
|
||||
if((int)mPostedPostsModel->displayedStartPostIndex() > 0)
|
||||
{
|
||||
mPostedPostsModel->setPostsInterval((int)mPostedPostsModel->displayedStartPostIndex()-POSTS_CHUNK_SIZE,POSTS_CHUNK_SIZE);
|
||||
updateShowLabel();
|
||||
}
|
||||
if((int)mPostedPostsModel->displayedStartPostIndex() > 0)
|
||||
{
|
||||
mPostedPostsModel->setPostsInterval((int)mPostedPostsModel->displayedStartPostIndex()-POSTS_CHUNK_SIZE,POSTS_CHUNK_SIZE);
|
||||
updateShowLabel();
|
||||
}
|
||||
}
|
||||
|
||||
void PostedListWidgetWithModel::showAuthorInPeople()
|
||||
|
@ -490,29 +498,30 @@ void PostedListWidgetWithModel::handleEvent_main_thread(std::shared_ptr<const Rs
|
|||
|
||||
switch(e->mPostedEventCode)
|
||||
{
|
||||
case RsPostedEventCode::NEW_COMMENT: // [[fallthrough]];
|
||||
case RsPostedEventCode::NEW_VOTE: // [[fallthrough]];
|
||||
{
|
||||
// special treatment here because the message might be a comment, so we need to refresh the comment tab if openned
|
||||
case RsPostedEventCode::NEW_COMMENT: [[fallthrough]];
|
||||
case RsPostedEventCode::NEW_VOTE:
|
||||
{
|
||||
// special treatment here because the message might be a comment, so we need to refresh the comment tab if openned
|
||||
|
||||
for(int i=2;i<ui->tabWidget->count();++i)
|
||||
{
|
||||
auto *t = dynamic_cast<GxsCommentDialog*>(ui->tabWidget->widget(i));
|
||||
for(int i=2;i<ui->tabWidget->count();++i)
|
||||
{
|
||||
auto *t = dynamic_cast<GxsCommentDialog*>(ui->tabWidget->widget(i));
|
||||
|
||||
if(t->groupId() == e->mPostedGroupId)
|
||||
t->refresh();
|
||||
}
|
||||
}
|
||||
case RsPostedEventCode::NEW_MESSAGE: // [[fallthrough]];
|
||||
case RsPostedEventCode::NEW_POSTED_GROUP: // [[fallthrough]];
|
||||
case RsPostedEventCode::UPDATED_POSTED_GROUP: // [[fallthrough]];
|
||||
case RsPostedEventCode::UPDATED_MESSAGE:
|
||||
case RsPostedEventCode::BOARD_DELETED:
|
||||
case RsPostedEventCode::SYNC_PARAMETERS_UPDATED:
|
||||
{
|
||||
if(t->groupId() == e->mPostedGroupId)
|
||||
t->refresh();
|
||||
}
|
||||
}
|
||||
[[clang::fallthrough]];
|
||||
case RsPostedEventCode::NEW_MESSAGE: [[fallthrough]];
|
||||
case RsPostedEventCode::NEW_POSTED_GROUP: [[fallthrough]];
|
||||
case RsPostedEventCode::UPDATED_POSTED_GROUP: [[fallthrough]];
|
||||
case RsPostedEventCode::UPDATED_MESSAGE: [[fallthrough]];
|
||||
case RsPostedEventCode::BOARD_DELETED: [[fallthrough]];
|
||||
case RsPostedEventCode::SYNC_PARAMETERS_UPDATED:
|
||||
{
|
||||
if(e->mPostedGroupId == groupId())
|
||||
updateDisplay(true);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -251,7 +251,7 @@ p, li { white-space: pre-wrap; }
|
|||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Preferred</enum>
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -310,7 +310,7 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
<pointsize>25</pointsize>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="frame_from_GL">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -232,9 +235,15 @@
|
|||
<item row="2" column="0">
|
||||
<widget class="QFrame" name="frame_input">
|
||||
<layout class="QVBoxLayout" name="frame_input_VL">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit_Pulse">
|
||||
<property name="font">
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QWidget" name="widget_indent" native="true">
|
||||
<property name="enabled">
|
||||
|
@ -53,7 +56,7 @@
|
|||
<property name="font">
|
||||
<font>
|
||||
<family>MS Sans Serif</family>
|
||||
<pointsize>10</pointsize>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QWidget" name="widget_prefix" native="true">
|
||||
<property name="sizePolicy">
|
||||
|
@ -149,6 +152,9 @@
|
|||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -440,10 +446,22 @@
|
|||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>40</height>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_authorName">
|
||||
<property name="font">
|
||||
|
@ -611,13 +629,19 @@
|
|||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>40</height>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
|
230
retroshare-gui/src/gui/TorControl/TorControlWindow.cpp
Normal file
230
retroshare-gui/src/gui/TorControl/TorControlWindow.cpp
Normal file
|
@ -0,0 +1,230 @@
|
|||
#include <unistd.h>
|
||||
#include <set>
|
||||
|
||||
#include <QTimer>
|
||||
#include <QFile>
|
||||
#include <QTcpServer>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
|
||||
#include <iostream>
|
||||
#include "util/rstime.h"
|
||||
|
||||
#include "TorControlWindow.h"
|
||||
#include "TorManager.h"
|
||||
#include "TorControl.h"
|
||||
#include "HiddenService.h"
|
||||
|
||||
TorControlDialog::TorControlDialog(Tor::TorManager *tm,QWidget *parent)
|
||||
: mTorManager(tm)
|
||||
{
|
||||
setupUi(this) ;
|
||||
|
||||
QObject::connect(tm->control(),SIGNAL(statusChanged(int,int)),this,SLOT(statusChanged())) ;
|
||||
QObject::connect(tm->control(),SIGNAL(connected()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm->control(),SIGNAL(disconnected()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm->control(),SIGNAL(bootstrapStatusChanged()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm->control(),SIGNAL(connectivityChanged()),this,SLOT(statusChanged()));
|
||||
QObject::connect(tm ,SIGNAL(errorChanged()),this,SLOT(statusChanged()));
|
||||
|
||||
//QTimer::singleShot(2000,this,SLOT(checkForHiddenService())) ;
|
||||
|
||||
mIncomingServer = new QTcpServer(this) ;
|
||||
mHiddenService = NULL ;
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_UNKNOWN;
|
||||
//mBootstrapPhaseFinished = false ;
|
||||
|
||||
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
|
||||
|
||||
QTimer *timer = new QTimer ;
|
||||
|
||||
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
|
||||
timer->start(500) ;
|
||||
|
||||
// Hide some debug output for the released version
|
||||
|
||||
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void TorControlDialog::onIncomingConnection()
|
||||
{
|
||||
std::cerr << "Incoming connection !!" << std::endl;
|
||||
}
|
||||
|
||||
void TorControlDialog::statusChanged()
|
||||
{
|
||||
int tor_control_status = mTorManager->control()->status();
|
||||
int torstatus = mTorManager->control()->torStatus();
|
||||
|
||||
QString tor_control_status_str,torstatus_str ;
|
||||
|
||||
if(mTorManager->hasError())
|
||||
mErrorMsg = mTorManager->errorMessage() ;
|
||||
|
||||
switch(tor_control_status)
|
||||
{
|
||||
default:
|
||||
case Tor::TorControl::Error : tor_control_status_str = "Error" ; break ;
|
||||
case Tor::TorControl::NotConnected: tor_control_status_str = "Not connected" ; break ;
|
||||
case Tor::TorControl::Connecting: tor_control_status_str = "Connecting" ; break ;
|
||||
case Tor::TorControl::Authenticating: tor_control_status_str = "Authenticating" ; break ;
|
||||
case Tor::TorControl::Connected: tor_control_status_str = "Connected" ; break ;
|
||||
}
|
||||
|
||||
switch(torstatus)
|
||||
{
|
||||
default:
|
||||
case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ;
|
||||
case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ;
|
||||
case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ;
|
||||
}
|
||||
|
||||
torStatus_LB->setText(torstatus_str) ;
|
||||
|
||||
if(torstatus == Tor::TorControl::TorUnknown)
|
||||
torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ;
|
||||
else
|
||||
torStatus_LB->setToolTip("") ;
|
||||
|
||||
QVariantMap qvm = mTorManager->control()->bootstrapStatus();
|
||||
QString bootstrapstatus_str ;
|
||||
|
||||
std::cerr << "Tor control status: " << tor_control_status_str.toStdString() << std::endl;
|
||||
std::cerr << "Tor status: " << torstatus_str.toStdString() << std::endl;
|
||||
|
||||
std::cerr << "Bootstrap status map: " << std::endl;
|
||||
|
||||
for(auto it(qvm.begin());it!=qvm.end();++it)
|
||||
std::cerr << " " << it.key().toStdString() << " : " << it.value().toString().toStdString() << std::endl;
|
||||
|
||||
if(!qvm["progress"].toString().isNull())
|
||||
torBootstrapStatus_LB->setText(qvm["progress"].toString() + " % (" + qvm["summary"].toString() + ")") ;
|
||||
else
|
||||
torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ;
|
||||
|
||||
QString service_id ;
|
||||
QString onion_address ;
|
||||
QHostAddress service_target_address ;
|
||||
uint16_t service_port ;
|
||||
uint16_t target_port ;
|
||||
|
||||
if(mTorManager->getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
|
||||
{
|
||||
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + service_target_address.toString() + ":" + QString::number(target_port));
|
||||
onionAddress_LB->setText(onion_address);
|
||||
}
|
||||
else
|
||||
{
|
||||
hiddenServiceAddress_LB->setText(QString("[Not ready]")) ;
|
||||
onionAddress_LB->setText(QString("[Not ready]")) ;
|
||||
}
|
||||
|
||||
showLog();
|
||||
adjustSize();
|
||||
|
||||
QCoreApplication::processEvents(); // forces update
|
||||
}
|
||||
|
||||
void TorControlDialog::showLog()
|
||||
{
|
||||
static std::set<QString> already_seen ;
|
||||
|
||||
QString s ;
|
||||
QStringList logmsgs = mTorManager->logMessages() ;
|
||||
bool can_print = false ;
|
||||
|
||||
for(QStringList::const_iterator it(logmsgs.begin());it!=logmsgs.end();++it)
|
||||
{
|
||||
s += *it + "\n" ;
|
||||
|
||||
if(already_seen.find(*it) == already_seen.end())
|
||||
{
|
||||
can_print = true ;
|
||||
already_seen.insert(*it);
|
||||
}
|
||||
|
||||
if(can_print)
|
||||
std::cerr << "[TOR DEBUG LOG] " << (*it).toStdString() << std::endl;
|
||||
}
|
||||
|
||||
// torLog_TB->setText(s) ;:
|
||||
|
||||
std::cerr << "Connexion Proxy: " << mTorManager->control()->socksAddress().toString().toStdString() << ":" << mTorManager->control()->socksPort() << std::endl;
|
||||
}
|
||||
|
||||
TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
||||
{
|
||||
if(!mErrorMsg.isNull())
|
||||
{
|
||||
error_msg = mErrorMsg ;
|
||||
return TorControlDialog::TOR_STATUS_FAIL ;
|
||||
}
|
||||
|
||||
switch(mTorManager->control()->torStatus())
|
||||
{
|
||||
case Tor::TorControl::TorReady: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
|
||||
default:
|
||||
return TOR_STATUS_UNKNOWN ;
|
||||
}
|
||||
}
|
||||
|
||||
TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService()
|
||||
{
|
||||
std::cerr << "Checking for hidden services:" ;
|
||||
|
||||
switch(mHiddenServiceStatus)
|
||||
{
|
||||
default:
|
||||
case HIDDEN_SERVICE_STATUS_UNKNOWN: {
|
||||
|
||||
std::cerr << " trying to setup. " ;
|
||||
|
||||
if(!mTorManager->setupHiddenService())
|
||||
{
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_FAIL ;
|
||||
std::cerr << "Failed." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
std::cerr << "Done." << std::endl;
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_REQUESTED ;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
|
||||
case HIDDEN_SERVICE_STATUS_REQUESTED: {
|
||||
QList<Tor::HiddenService*> hidden_services = mTorManager->control()->hiddenServices();
|
||||
|
||||
if(hidden_services.empty())
|
||||
{
|
||||
std::cerr << "Not ready yet." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mHiddenService == NULL)
|
||||
mHiddenService = *(hidden_services.begin()) ;
|
||||
|
||||
Tor::HiddenService::Status hss = mHiddenService->status();
|
||||
|
||||
std::cerr << "New service acquired. Status is " << hss ;
|
||||
|
||||
if(hss == Tor::HiddenService::Online)
|
||||
{
|
||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_OK ;
|
||||
std::cerr << ": published and running!" << std::endl;
|
||||
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << ": not ready yet." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
}
|
||||
}
|
||||
case HIDDEN_SERVICE_STATUS_OK :
|
||||
std::cerr << "New service acquired." << std::endl;
|
||||
return mHiddenServiceStatus ;
|
||||
}
|
||||
}
|
||||
|
48
retroshare-gui/src/gui/TorControl/TorControlWindow.h
Normal file
48
retroshare-gui/src/gui/TorControl/TorControlWindow.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "ui_TorControlWindow.h"
|
||||
|
||||
class QTcpServer ;
|
||||
|
||||
namespace Tor {
|
||||
class HiddenService ;
|
||||
class TorManager ;
|
||||
}
|
||||
|
||||
class TorControlDialog: public QWidget, public Ui::TorControlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TorControlDialog(Tor::TorManager *tm,QWidget *parent =NULL);
|
||||
|
||||
enum TorStatus {
|
||||
TOR_STATUS_UNKNOWN = 0x00,
|
||||
TOR_STATUS_OK = 0x01,
|
||||
TOR_STATUS_FAIL = 0x02
|
||||
};
|
||||
|
||||
enum HiddenServiceStatus {
|
||||
HIDDEN_SERVICE_STATUS_UNKNOWN = 0x00, // no information known.
|
||||
HIDDEN_SERVICE_STATUS_FAIL = 0x01, // some error occurred
|
||||
HIDDEN_SERVICE_STATUS_REQUESTED = 0x02, // one service at least has been requested. Still being tested.
|
||||
HIDDEN_SERVICE_STATUS_OK = 0x03 // one service responds and has been tested
|
||||
};
|
||||
|
||||
// Should be called multiple times in a loop until it returns something else than *_UNKNOWN
|
||||
|
||||
TorStatus checkForTor(QString& error_msg) ;
|
||||
HiddenServiceStatus checkForHiddenService() ;
|
||||
|
||||
protected slots:
|
||||
void showLog();
|
||||
void statusChanged();
|
||||
void onIncomingConnection();
|
||||
|
||||
private:
|
||||
QString mErrorMsg ;
|
||||
HiddenServiceStatus mHiddenServiceStatus ;
|
||||
|
||||
Tor::TorManager *mTorManager ;
|
||||
Tor::HiddenService *mHiddenService ;
|
||||
|
||||
QTcpServer *mIncomingServer ;
|
||||
};
|
134
retroshare-gui/src/gui/TorControl/TorControlWindow.ui
Normal file
134
retroshare-gui/src/gui/TorControl/TorControlWindow.ui
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TorControlDialog</class>
|
||||
<widget class="QWidget" name="TorControlDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>228</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Setting up Tor...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../gui/icons.qrc">:/icons/tor-logo.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="torStatusTxt_LB">
|
||||
<property name="text">
|
||||
<string>Tor status:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="torStatus_LB">
|
||||
<property name="text">
|
||||
<string>Unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="torBootstrapStatus_LB">
|
||||
<property name="text">
|
||||
<string>Not started</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="HiddenServiceAddressTxt_LB">
|
||||
<property name="text">
|
||||
<string>Hidden service address:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="torBootstrapStatusTxt_LB">
|
||||
<property name="text">
|
||||
<string>Tor bootstrap status:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="hiddenServiceAddress_LB">
|
||||
<property name="text">
|
||||
<string>Not set</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="onionAddressTxt_LB">
|
||||
<property name="text">
|
||||
<string>Onion address:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="onionAddress_LB">
|
||||
<property name="text">
|
||||
<string>Not set</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../gui/icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -50,7 +50,7 @@
|
|||
<height>510</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -63,33 +63,48 @@
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="GroupTreeWidget" name="groupTreeWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
<item row="0" column="0">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidget_Pages">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Page Name</string>
|
||||
<widget class="GroupTreeWidget" name="groupTreeWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Page Id</string>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Orig Id</string>
|
||||
</widget>
|
||||
<widget class="QTreeWidget" name="treeWidget_Pages">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Page Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Page Id</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Orig Id</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -381,7 +381,7 @@ void ChatWidget::init(const ChatId &chat_id, const QString &title)
|
|||
hist_chat_type = RS_HISTORY_TYPE_PUBLIC;
|
||||
messageCount = Settings->getPublicChatHistoryCount();
|
||||
|
||||
ui->titleBarFrame->setVisible(false);
|
||||
ui->headerBFrame->setVisible(false);
|
||||
}
|
||||
|
||||
if (rsHistory->getEnable(hist_chat_type))
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="titleBarFrame">
|
||||
<widget class="QFrame" name="headerBFrame">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="titleBarFrameHLayout">
|
||||
<layout class="QHBoxLayout" name="headerBFrameHLayout">
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
|
|
|
@ -126,7 +126,11 @@ bool ElidedLabel::paintElidedLine( QPainter* painter, QString plainText
|
|||
to.setWrapMode(wordWrap ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
|
||||
textLayout.setTextOption(to);
|
||||
|
||||
if (painter) painter->save();
|
||||
if (painter)
|
||||
{
|
||||
painter->save();
|
||||
painter->setFont(useFont);
|
||||
}
|
||||
textLayout.beginLayout();
|
||||
forever {
|
||||
//Get new line for text.
|
||||
|
@ -220,10 +224,7 @@ bool ElidedLabel::paintElidedLine( QPainter* painter, QString plainText
|
|||
|
||||
if(width+iTransX+cr.left() <= cr.right())
|
||||
if (painter)
|
||||
{
|
||||
painter->setFont(useFont);
|
||||
painter->drawText(QPoint(iTransX + cr.left(), y + fontMetrics.ascent() + cr.top()), elidedLastLine);
|
||||
}
|
||||
|
||||
//Draw button to get ToolTip
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "gui/chat/ChatUserNotify.h"
|
||||
#include "gui/connect/ConnectProgressDialog.h"
|
||||
#include "gui/common/ElidedLabel.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
|
||||
#include "FriendList.h"
|
||||
#include "ui_FriendList.h"
|
||||
|
@ -297,8 +298,9 @@ void FriendList::peerTreeWidgetCustomPopupMenu()
|
|||
|
||||
QMenu *contextMenu = new QMenu(this);
|
||||
|
||||
QWidget *widget = new QWidget(contextMenu);
|
||||
widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
QFrame *widget = new QFrame(contextMenu);
|
||||
widget->setObjectName("gradFrame"); //Use qss
|
||||
//widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
|
||||
// create menu header
|
||||
QHBoxLayout *hbox = new QHBoxLayout(widget);
|
||||
|
@ -306,12 +308,14 @@ void FriendList::peerTreeWidgetCustomPopupMenu()
|
|||
hbox->setSpacing(6);
|
||||
|
||||
QLabel *iconLabel = new QLabel(widget);
|
||||
iconLabel->setObjectName("trans_Icon");
|
||||
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
|
||||
iconLabel->setPixmap(pix);
|
||||
iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width());
|
||||
hbox->addWidget(iconLabel);
|
||||
|
||||
QLabel *textLabel = new QLabel("<strong>RetroShare</strong>", widget);
|
||||
textLabel->setObjectName("trans_Text");
|
||||
hbox->addWidget(textLabel);
|
||||
|
||||
QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
|
|
@ -60,16 +60,25 @@ GroupTreeWidget::GroupTreeWidget(QWidget *parent) :
|
|||
connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged()));
|
||||
|
||||
connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
|
||||
connect(ui->treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
|
||||
connect(ui->treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
|
||||
connect(ui->treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(itemActivated(QTreeWidgetItem*,int)));
|
||||
if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) {
|
||||
// need signal itemClicked too
|
||||
connect(ui->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(itemActivated(QTreeWidgetItem*,int)));
|
||||
}
|
||||
|
||||
int H = QFontMetricsF(ui->treeWidget->font()).height() ;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
|
||||
int W = QFontMetricsF(ui->treeWidget->font()).width("_") ;
|
||||
int D = QFontMetricsF(ui->treeWidget->font()).width("9999-99-99[]") ;
|
||||
#else
|
||||
int W = QFontMetricsF(ui->treeWidget->font()).horizontalAdvance("_") ;
|
||||
int D = QFontMetricsF(ui->treeWidget->font()).horizontalAdvance("9999-99-99[]") ;
|
||||
#endif
|
||||
|
||||
/* Add own item delegate */
|
||||
RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this);
|
||||
itemDelegate->setSpacing(QSize(0, 2));
|
||||
itemDelegate->setSpacing(QSize(W/2, H/4));
|
||||
ui->treeWidget->setItemDelegate(itemDelegate);
|
||||
|
||||
/* Set compare role for each column */
|
||||
|
@ -87,10 +96,6 @@ GroupTreeWidget::GroupTreeWidget(QWidget *parent) :
|
|||
ui->treeWidget->enableColumnCustomize(true);
|
||||
ui->treeWidget->setColumnCustomizable(GTW_COLUMN_NAME, false);
|
||||
|
||||
int S = QFontMetricsF(font()).height() ;
|
||||
int W = QFontMetricsF(font()).width("_") ;
|
||||
int D = QFontMetricsF(font()).width("9999-99-99[]") ;
|
||||
|
||||
QTreeWidgetItem *headerItem = ui->treeWidget->headerItem();
|
||||
headerItem->setText(GTW_COLUMN_NAME, tr("Name"));
|
||||
headerItem->setText(GTW_COLUMN_UNREAD, "");
|
||||
|
@ -143,7 +148,7 @@ GroupTreeWidget::GroupTreeWidget(QWidget *parent) :
|
|||
|
||||
connect(ui->distantSearchLineEdit,SIGNAL(returnPressed()),this,SLOT(distantSearch())) ;
|
||||
|
||||
ui->treeWidget->setIconSize(QSize(S*1.8,S*1.8));
|
||||
ui->treeWidget->setIconSize(QSize(H*1.8,H*1.8));
|
||||
}
|
||||
|
||||
GroupTreeWidget::~GroupTreeWidget()
|
||||
|
@ -205,9 +210,9 @@ void GroupTreeWidget::updateColors()
|
|||
|
||||
int color = item->data(GTW_COLUMN_DATA, ROLE_COLOR).toInt();
|
||||
if (color >= 0) {
|
||||
item->setData(GTW_COLUMN_NAME, Qt::TextColorRole, mTextColor[color]);
|
||||
item->setData(GTW_COLUMN_NAME, Qt::ForegroundRole, mTextColor[color]);
|
||||
} else {
|
||||
item->setData(GTW_COLUMN_NAME, Qt::TextColorRole, QVariant());
|
||||
item->setData(GTW_COLUMN_NAME, Qt::ForegroundRole, QVariant());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -246,7 +251,6 @@ void GroupTreeWidget::itemActivated(QTreeWidgetItem *item, int column)
|
|||
|
||||
QTreeWidgetItem *GroupTreeWidget::addCategoryItem(const QString &name, const QIcon &icon, bool expand, int sortOrder /*= -1*/)
|
||||
{
|
||||
QFont font;
|
||||
RSTreeWidgetItem *item = new RSTreeWidgetItem();
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
// To get StyleSheet for Items
|
||||
|
@ -255,15 +259,16 @@ QTreeWidgetItem *GroupTreeWidget::addCategoryItem(const QString &name, const QIc
|
|||
|
||||
item->setText(GTW_COLUMN_NAME, name);
|
||||
item->setData(GTW_COLUMN_DATA, ROLE_NAME, name);
|
||||
font = item->font(GTW_COLUMN_NAME);
|
||||
font.setBold(true);
|
||||
item->setFont(GTW_COLUMN_NAME, font);
|
||||
QFont itFont = item->font(GTW_COLUMN_NAME);
|
||||
itFont.setBold(true);
|
||||
itFont.setPointSize(ui->treeWidget->font().pointSize()); //use treeWidget font size defined in ui.
|
||||
item->setFont(GTW_COLUMN_NAME, itFont);
|
||||
item->setIcon(GTW_COLUMN_NAME, icon);
|
||||
|
||||
int S = QFontMetricsF(font).height();
|
||||
//int S = QFontMetricsF(itFont).height();
|
||||
|
||||
item->setSizeHint(GTW_COLUMN_NAME, QSize(S*1.9, S*1.9));
|
||||
item->setData(GTW_COLUMN_NAME, Qt::TextColorRole, textColorCategory());
|
||||
//item->setSizeHint(GTW_COLUMN_NAME, QSize(S*1.9, S*1.9)); //size hint is calculated by item delegate. Use itemDelegate->setSpacing() in constructor.
|
||||
item->setData(GTW_COLUMN_NAME, Qt::ForegroundRole, textColorCategory());
|
||||
item->setData(GTW_COLUMN_DATA, ROLE_COLOR, GROUPTREEWIDGET_COLOR_CATEGORY);
|
||||
|
||||
item->setExpanded(expand);
|
||||
|
@ -385,6 +390,7 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList<
|
|||
if (item == NULL) {
|
||||
item = new RSTreeWidgetItem(compareRole);
|
||||
item->setData(GTW_COLUMN_DATA, ROLE_ID, itemInfo.id);
|
||||
item->setFont(GTW_COLUMN_DATA, ui->treeWidget->font());
|
||||
//static_cast<RSTreeWidgetItem*>(item)->setNoDataAsLast(true); //Uncomment this to sort data with QVariant() always at end.
|
||||
categoryItem->addChild(item);
|
||||
}
|
||||
|
@ -506,18 +512,18 @@ void GroupTreeWidget::setUnreadCount(QTreeWidgetItem *item, int unreadCount)
|
|||
return;
|
||||
}
|
||||
|
||||
QFont font = item->font(GTW_COLUMN_NAME);
|
||||
QFont itFont = item->font(GTW_COLUMN_NAME);
|
||||
|
||||
if (unreadCount) {
|
||||
item->setText(GTW_COLUMN_UNREAD, QString::number(unreadCount));
|
||||
font.setBold(true);
|
||||
itFont.setBold(true);
|
||||
} else {
|
||||
item->setText(GTW_COLUMN_UNREAD, "");
|
||||
font.setBold(false);
|
||||
itFont.setBold(false);
|
||||
}
|
||||
item->setData(GTW_COLUMN_UNREAD, ROLE_SORT, unreadCount);
|
||||
|
||||
item->setFont(GTW_COLUMN_NAME, font);
|
||||
item->setFont(GTW_COLUMN_NAME, itFont);
|
||||
}
|
||||
|
||||
QTreeWidgetItem *GroupTreeWidget::getItemFromId(const QString &id)
|
||||
|
|
|
@ -70,6 +70,11 @@
|
|||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
|
|
|
@ -184,6 +184,13 @@ NewFriendList::NewFriendList(QWidget */*parent*/) : /* RsAutoUpdatePage(5000,par
|
|||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
int H = QFontMetricsF(ui->peerTreeWidget->font()).height();
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
|
||||
int W = QFontMetricsF(ui->peerTreeWidget->font()).width("_");
|
||||
#else
|
||||
int W = QFontMetricsF(ui->peerTreeWidget->font()).horizontalAdvance("_");
|
||||
#endif
|
||||
|
||||
ui->filterLineEdit->setPlaceholderText(tr("Search")) ;
|
||||
ui->filterLineEdit->showFilterIcon();
|
||||
|
||||
|
@ -205,7 +212,9 @@ NewFriendList::NewFriendList(QWidget */*parent*/) : /* RsAutoUpdatePage(5000,par
|
|||
mProxyModel->setFilterRegExp(QRegExp(RsFriendListModel::FilterString));
|
||||
|
||||
ui->peerTreeWidget->setModel(mProxyModel);
|
||||
ui->peerTreeWidget->setItemDelegate(new RSElidedItemDelegate());
|
||||
RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this);
|
||||
itemDelegate->setSpacing(QSize(W/2, H/4));
|
||||
ui->peerTreeWidget->setItemDelegate(itemDelegate);
|
||||
ui->peerTreeWidget->setWordWrap(false);
|
||||
|
||||
/* Add filter actions */
|
||||
|
@ -228,17 +237,13 @@ NewFriendList::NewFriendList(QWidget */*parent*/) : /* RsAutoUpdatePage(5000,par
|
|||
QShortcut *Shortcut = new QShortcut(QKeySequence(Qt::Key_Delete), ui->peerTreeWidget, 0, 0, Qt::WidgetShortcut);
|
||||
connect(Shortcut, SIGNAL(activated()), this, SLOT(removeItem()),Qt::QueuedConnection);
|
||||
|
||||
QFontMetricsF fontMetrics(ui->peerTreeWidget->font());
|
||||
/* Set initial column width */
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_NAME , 22 * W);
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_IP , 15 * W);
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_ID , 32 * W);
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_LAST_CONTACT, 12 * W);
|
||||
|
||||
/* Set initial column width */
|
||||
int fontWidth = fontMetrics.width("W");
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_NAME , 22 * fontWidth);
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_IP , 15 * fontWidth);
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_ID , 32 * fontWidth);
|
||||
ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_LAST_CONTACT, 12 * fontWidth);
|
||||
|
||||
int avatarHeight = fontMetrics.height() * 2;
|
||||
ui->peerTreeWidget->setIconSize(QSize(avatarHeight, avatarHeight));
|
||||
ui->peerTreeWidget->setIconSize(QSize(H*2, H*2));
|
||||
|
||||
mModel->checkInternalData(true);
|
||||
|
||||
|
@ -301,8 +306,9 @@ void NewFriendList::headerContextMenuRequested(QPoint /*p*/)
|
|||
{
|
||||
QMenu displayMenu(tr("Show Items"), this);
|
||||
|
||||
QWidget *widget = new QWidget(&displayMenu);
|
||||
widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
QFrame *widget = new QFrame(&displayMenu);
|
||||
widget->setObjectName("gradFrame"); //Use qss
|
||||
//widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
|
||||
// create menu header
|
||||
QHBoxLayout *hbox = new QHBoxLayout(widget);
|
||||
|
@ -310,12 +316,14 @@ void NewFriendList::headerContextMenuRequested(QPoint /*p*/)
|
|||
hbox->setSpacing(6);
|
||||
|
||||
QLabel *iconLabel = new QLabel(widget);
|
||||
iconLabel->setObjectName("trans_Icon");
|
||||
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
|
||||
iconLabel->setPixmap(pix);
|
||||
iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width());
|
||||
hbox->addWidget(iconLabel);
|
||||
|
||||
QLabel *textLabel = new QLabel("<strong>Show/hide...</strong>", widget);
|
||||
textLabel->setObjectName("trans_Text");
|
||||
hbox->addWidget(textLabel);
|
||||
|
||||
QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
@ -564,8 +572,9 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu()
|
|||
|
||||
QMenu contextMenu(this);
|
||||
|
||||
QWidget *widget = new QWidget(&contextMenu);
|
||||
widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
QFrame *widget = new QFrame();
|
||||
widget->setObjectName("gradFrame"); //Use qss
|
||||
//widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
|
||||
// create menu header
|
||||
QHBoxLayout *hbox = new QHBoxLayout(widget);
|
||||
|
@ -573,12 +582,14 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu()
|
|||
hbox->setSpacing(6);
|
||||
|
||||
QLabel *iconLabel = new QLabel(widget);
|
||||
iconLabel->setObjectName("trans_Icon");
|
||||
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
|
||||
iconLabel->setPixmap(pix);
|
||||
iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width());
|
||||
hbox->addWidget(iconLabel);
|
||||
|
||||
QLabel *textLabel = new QLabel("<strong>Friend list</strong>", widget);
|
||||
textLabel->setObjectName("trans_Text");
|
||||
hbox->addWidget(textLabel);
|
||||
|
||||
QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
|
|
@ -51,7 +51,7 @@ QTreeView::item:hover, QTreeWidget::item:hover, QListWidget::item:hover{
|
|||
color: #0000EF;
|
||||
background-color: #FEDCBA;
|
||||
}
|
||||
QQTreeView::item:selected:hover, TreeWidget::item:selected:hover, QListWidget::item:selected:hover{
|
||||
QTreeView::item:selected:hover, QTreeWidget::item:selected:hover, QListWidget::item:selected:hover{
|
||||
color: #ABCDEF;
|
||||
background-color: #FE0000;
|
||||
}
|
||||
|
@ -88,7 +88,12 @@ QSize RSElidedItemDelegate::sizeHint(const QStyleOptionViewItem &option, const Q
|
|||
|
||||
QSize contSize = ownStyle->sizeFromContents( QStyle::CT_ItemViewItem,&ownOption
|
||||
,QSize( checkRect.width()+iconRect.width()+textRect.width()
|
||||
,qMax(checkRect.height(),qMax(iconRect.height(),textRect.height()))),widget);
|
||||
, qMax(checkRect.height(),qMax(iconRect.height(),textRect.height()))
|
||||
), widget ) ;
|
||||
|
||||
contSize += QSize( 2*spacing().width()
|
||||
, qMax(checkRect.height(),iconRect.height()) > textRect.height()
|
||||
? 0 : 2*spacing().height() );
|
||||
|
||||
return contSize;
|
||||
}
|
||||
|
@ -97,7 +102,7 @@ inline QColor getImagePixelColor(QImage img, int x, int y)
|
|||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,6,0)
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
// RsDbg() << " RSEID: Found Color " << img.pixelColor(x,y).name(QColor::HexArgb).toStdString() << " at " << x << "," << y << std::endl;
|
||||
//RsDbg(" RSEID: Found Color ", img.pixelColor(x,y).name(QColor::HexArgb).toStdString(), " at ", x, ",", y);
|
||||
#endif
|
||||
return img.pixelColor(x,y);
|
||||
#else
|
||||
|
@ -109,7 +114,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
{
|
||||
if(!index.isValid())
|
||||
{
|
||||
RsErr() << __PRETTY_FUNCTION__ << " attempt to draw an invalid index." << std::endl;
|
||||
RS_ERR(" attempt to draw an invalid index.");
|
||||
return ;
|
||||
}
|
||||
painter->save();
|
||||
|
@ -120,7 +125,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
ownOption.icon = option.icon;
|
||||
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << __PRETTY_FUNCTION__ << std::endl << " RSEID: Enter for item with text:" << ownOption.text.toStdString() << std::endl;
|
||||
RS_DBG("\n RSEID: Enter for item with text:", ownOption.text.toStdString());
|
||||
#endif
|
||||
|
||||
const QWidget* widget = option.widget;
|
||||
|
@ -139,16 +144,16 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
ownOption.fontMetrics = QFontMetrics(font);
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
QFontInfo info(font);
|
||||
RsDbg() << " RSEID: Found font in model:" << info.family().toStdString() << std::endl;
|
||||
RsDbg(" RSEID: Found font in model:", info.family().toStdString());
|
||||
#endif
|
||||
}
|
||||
// Get Text color from model if one exists
|
||||
QColor textColor;
|
||||
if (index.data(Qt::TextColorRole).isValid()) {
|
||||
if (index.data(Qt::ForegroundRole).isValid()) {
|
||||
//textColor = QColor(index.data(Qt::TextColorRole).toString());//Needs to pass from string else loose RBG format.
|
||||
textColor = index.data(Qt::TextColorRole).value<QColor>();
|
||||
textColor = index.data(Qt::ForegroundRole).value<QColor>();
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << " RSEID: Found text color in model:" << textColor.name().toStdString() << std::endl;
|
||||
RsDbg(" RSEID: Found text color in model:", textColor.name().toStdString());
|
||||
#endif
|
||||
}
|
||||
// Get Brush from model if one exists
|
||||
|
@ -157,7 +162,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
if (index.data(Qt::BackgroundRole).isValid()) {
|
||||
bgBrush = index.data(Qt::BackgroundRole).value<QBrush>();
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << " RSEID: Found bg brush in model:" << bgBrush.color().name().toStdString() << std::endl;
|
||||
RsDbg(" RSEID: Found bg brush in model:", bgBrush.color().name().toStdString());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -165,10 +170,10 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
if ( (bgBrush.color().spec()==QColor::Invalid) || (textColor.spec()!=QColor::Invalid) )
|
||||
{
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << " RSEID:"
|
||||
<< ((bgBrush.color().spec()==QColor::Invalid) ? " Brush not defined" : "")
|
||||
<< ((textColor.spec()==QColor::Invalid) ? " Text Color not defined" : "")
|
||||
<< " so get it from base image." << std::endl;
|
||||
RsDbg( " RSEID:"
|
||||
, ((bgBrush.color().spec()==QColor::Invalid) ? " Brush not defined" : "")
|
||||
, ((textColor.spec()==QColor::Invalid) ? " Text Color not defined" : "")
|
||||
, " so get it from base image.");
|
||||
#endif
|
||||
// QPalette is not updated by StyleSheet all occurs in internal class. (QRenderRule)
|
||||
// https://code.woboq.org/qt5/qtbase/src/widgets/styles/qstylesheetstyle.cpp.html#4138
|
||||
|
@ -179,7 +184,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
if (moSize.width() <= 20)
|
||||
moSize.setWidth(20);
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << " RSEID: for item size = " << moSize.width() << "x" << moSize.height() << std::endl;
|
||||
RsDbg(" RSEID: for item size = ", moSize.width(), "x", moSize.height());
|
||||
#endif
|
||||
|
||||
QImage moImg(moSize,QImage::Format_ARGB32);
|
||||
|
@ -268,14 +273,14 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
{
|
||||
bgBrush = QBrush(moBGColor);
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << " RSEID: bg brush setted to " << moBGColor.name(QColor::HexArgb).toStdString() << std::endl;
|
||||
RsDbg(" RSEID: bg brush setted to ", moBGColor.name(QColor::HexArgb).toStdString());
|
||||
#endif
|
||||
}
|
||||
if (textColor.spec()==QColor::Invalid)
|
||||
{
|
||||
textColor = moColor;
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << " RSEID: text color setted to " << moColor.name(QColor::HexArgb).toStdString() << std::endl;
|
||||
RsDbg(" RSEID: text color setted to ", moColor.name(QColor::HexArgb).toStdString());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -389,7 +394,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
}
|
||||
painter->restore();
|
||||
#ifdef DEBUG_EID_PAINT
|
||||
RsDbg() << " RSEID: Finished" << std::endl;
|
||||
RsDbg(" RSEID: Finished");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -18,42 +18,73 @@
|
|||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include "RSTreeView.h"
|
||||
|
||||
RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent)
|
||||
#include "util/rsdebug.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
|
||||
//#define DEBUG_RSTREEVIEW
|
||||
|
||||
RSTreeView::RSTreeView(QWidget *parent)
|
||||
: QTreeView(parent), autoSelect(false)
|
||||
{
|
||||
setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future.
|
||||
setMouseTracking(false); // normally the default, but who knows if it's not going to change in the future.
|
||||
}
|
||||
|
||||
void RSTreeView::wheelEvent(QWheelEvent *e)
|
||||
{
|
||||
if(e->modifiers() == Qt::ControlModifier)
|
||||
{
|
||||
emit zoomRequested(e->delta() > 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
QTreeView::wheelEvent(e);
|
||||
if(e->modifiers() == Qt::ControlModifier)
|
||||
{
|
||||
emit zoomRequested(e->angleDelta().y() > 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
QTreeView::wheelEvent(e);
|
||||
}
|
||||
|
||||
void RSTreeView::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
QModelIndex idx = indexAt(e->pos());
|
||||
#ifdef DEBUG_RSTREEVIEW
|
||||
RS_DBG(e->localPos().x(), ":", e->localPos().y());
|
||||
#endif
|
||||
if (autoSelect)
|
||||
{
|
||||
QModelIndex idx = indexAt(e->pos());
|
||||
|
||||
if(idx.isValid() && idx != selectionModel()->currentIndex())
|
||||
selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect);
|
||||
if(idx.isValid() && idx != selectionModel()->currentIndex())
|
||||
{
|
||||
#ifdef DEBUG_RSTREEVIEW
|
||||
RS_DBG("Selection changed");
|
||||
#endif
|
||||
selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect);
|
||||
}
|
||||
}
|
||||
|
||||
QTreeView::mouseMoveEvent(e);
|
||||
QTreeView::mouseMoveEvent(e);
|
||||
}
|
||||
|
||||
void RSTreeView::leaveEvent(QEvent *e)
|
||||
{
|
||||
#ifdef DEBUG_RSTREEVIEW
|
||||
RS_DBG("");
|
||||
#endif
|
||||
if (autoSelect)
|
||||
{
|
||||
auto fp = focusPolicy();
|
||||
setFocusPolicy(Qt::NoFocus); // To not select first index when resetting current index.
|
||||
selectionModel()->setCurrentIndex(QModelIndex(),QItemSelectionModel::Clear); // Close editor
|
||||
setFocusPolicy(fp);
|
||||
}
|
||||
|
||||
QTreeView::leaveEvent(e);
|
||||
}
|
||||
|
||||
void RSTreeView::setAutoSelect(bool b)
|
||||
{
|
||||
if(b)
|
||||
setMouseTracking(true);
|
||||
else
|
||||
setMouseTracking(false);
|
||||
autoSelect = b; // Keep this because setMouseTracking can be called outside.
|
||||
setMouseTracking(b);
|
||||
}
|
||||
|
||||
void RSTreeView::resizeEvent(QResizeEvent *e)
|
||||
|
|
|
@ -29,26 +29,34 @@ class RSTreeView : public QTreeView
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RSTreeView(QWidget *parent = 0);
|
||||
RSTreeView(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief set Placeholder Text
|
||||
* @param text
|
||||
*/
|
||||
void setPlaceholderText(const QString &text);
|
||||
|
||||
// Use this to make selection automatic based on mouse position. This is useful to trigger selection and therefore editing mode
|
||||
// in trees that show editing widgets using a QStyledItemDelegate
|
||||
|
||||
void setAutoSelect(bool b);
|
||||
/**
|
||||
* @brief Use this to make selection automatic based on mouse position.
|
||||
* This is useful to trigger selection and therefore editing mode in trees that show editing widgets using a QStyledItemDelegate.
|
||||
* @param b
|
||||
*/
|
||||
void setAutoSelect(bool b);
|
||||
|
||||
signals:
|
||||
void sizeChanged(QSize);
|
||||
void zoomRequested(bool zoom_or_unzoom);
|
||||
void sizeChanged(QSize);
|
||||
void zoomRequested(bool zoom_or_unzoom);
|
||||
|
||||
protected:
|
||||
virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection
|
||||
virtual void leaveEvent(QEvent *e) override; // overriding so as to manage auto-selection clear
|
||||
virtual void wheelEvent(QWheelEvent *e) override; // overriding so as to manage zoom
|
||||
virtual void resizeEvent(QResizeEvent *e) override;
|
||||
virtual void resizeEvent(QResizeEvent *e) override;
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
QString placeholderText;
|
||||
bool autoSelect;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -253,8 +253,9 @@ QMenu *RSTreeWidget::createStandardContextMenu(QMenu *contextMenu)
|
|||
}
|
||||
|
||||
if(!mContextMenuActions.isEmpty() || !mContextMenuMenus.isEmpty() || mEnableColumnCustomize) {
|
||||
QWidget *widget = new QWidget(contextMenu);
|
||||
widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
QFrame *widget = new QFrame(contextMenu);
|
||||
widget->setObjectName("gradFrame"); //Use qss
|
||||
//widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
|
||||
|
||||
// create menu header
|
||||
QHBoxLayout *hbox = new QHBoxLayout(widget);
|
||||
|
@ -262,12 +263,14 @@ QMenu *RSTreeWidget::createStandardContextMenu(QMenu *contextMenu)
|
|||
hbox->setSpacing(6);
|
||||
|
||||
QLabel *iconLabel = new QLabel(widget);
|
||||
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/settings.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
|
||||
iconLabel->setObjectName("trans_Icon");
|
||||
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/settings.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
|
||||
iconLabel->setPixmap(pix);
|
||||
iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width());
|
||||
hbox->addWidget(iconLabel);
|
||||
|
||||
QLabel *textLabel = new QLabel("<strong>" + tr("Tree View Options") + "</strong>", widget);
|
||||
textLabel->setObjectName("trans_Text");
|
||||
hbox->addWidget(textLabel);
|
||||
|
||||
QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "ConnectFriendWizard.h"
|
||||
#include "ui_ConnectFriendWizard.h"
|
||||
#include "gui/common/PeerDefs.h"
|
||||
#include "gui/connect/ConfCertDialog.h"
|
||||
#include "gui/notifyqt.h"
|
||||
#include "gui/common/GroupDefs.h"
|
||||
#include "gui/msgs/MessageComposer.h"
|
||||
|
@ -572,6 +573,16 @@ void ConnectFriendWizard::initializePage(int id)
|
|||
ui->ipEdit->setText(s);
|
||||
ui->signersEdit->setPlainText(ts);
|
||||
|
||||
ui->knownIpLabel->setHidden(peerDetails.ipAddressList.empty());
|
||||
ui->knownIpEdit->setHidden(peerDetails.ipAddressList.empty());
|
||||
{
|
||||
QString ipList;
|
||||
for(auto& it : peerDetails.ipAddressList)
|
||||
ipList.append(QString::fromStdString(it) + "\n");
|
||||
|
||||
ui->knownIpEdit->setPlainText(ipList);
|
||||
}
|
||||
|
||||
fillGroups(this, ui->groupComboBox, groupId);
|
||||
|
||||
if(peerDetails.isHiddenNode)
|
||||
|
@ -587,15 +598,16 @@ void ConnectFriendWizard::initializePage(int id)
|
|||
}
|
||||
if(mIsShortInvite)
|
||||
{
|
||||
ui->nameEdit->setText(tr("[Unknown]"));
|
||||
ui->addKeyToKeyring_CB->setChecked(false);
|
||||
ui->addKeyToKeyring_CB->setEnabled(false);
|
||||
if(ui->nameEdit->text().isEmpty())
|
||||
ui->nameEdit->setText(tr("[Unknown]"));
|
||||
ui->addKeyToKeyring_CB->setChecked(false);
|
||||
ui->addKeyToKeyring_CB->setEnabled(false);
|
||||
ui->signersEdit->hide();
|
||||
ui->signersLabel->hide();
|
||||
ui->signGPGCheckBox->setChecked(false);
|
||||
ui->signGPGCheckBox->setEnabled(false);
|
||||
ui->acceptNoSignGPGCheckBox->setChecked(true);
|
||||
ui->acceptNoSignGPGCheckBox->setEnabled(false);
|
||||
ui->signGPGCheckBox->setChecked(false);
|
||||
ui->signGPGCheckBox->setEnabled(false);
|
||||
ui->acceptNoSignGPGCheckBox->setChecked(true);
|
||||
ui->acceptNoSignGPGCheckBox->setEnabled(false);
|
||||
}
|
||||
|
||||
ui->ipEdit->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
|
@ -856,30 +868,30 @@ void ConnectFriendWizard::cleanFriendCert()
|
|||
{
|
||||
bool certValid = false;
|
||||
QString errorMsg ;
|
||||
QString certDetail;
|
||||
std::string cert = ui->friendCertEdit->toPlainText().toUtf8().constData();
|
||||
|
||||
if (cert.empty()) {
|
||||
ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png"));
|
||||
ui->friendCertCleanLabel->setToolTip("");
|
||||
ui->friendCertCleanLabel->setStyleSheet("");
|
||||
errorMsg = tr("");
|
||||
|
||||
} else {
|
||||
std::string cleanCert;
|
||||
uint32_t error_code;
|
||||
RsPeerDetails details;
|
||||
|
||||
if (rsPeers->cleanCertificate(cert, cleanCert, mIsShortInvite, error_code))
|
||||
{
|
||||
if (rsPeers->cleanCertificate(cert, cleanCert, mIsShortInvite, error_code, details))
|
||||
{
|
||||
certValid = true;
|
||||
|
||||
if (cert != cleanCert)
|
||||
{
|
||||
{
|
||||
QTextCursor textCursor = ui->friendCertEdit->textCursor();
|
||||
|
||||
whileBlocking(ui->friendCertEdit)->setPlainText(QString::fromUtf8(cleanCert.c_str()));
|
||||
whileBlocking(ui->friendCertEdit)->setTextCursor(textCursor);
|
||||
|
||||
ui->friendCertCleanLabel->setStyleSheet("");
|
||||
certDetail = ConfCertDialog::getCertificateDescription(details,false,mIsShortInvite,!details.ipAddressList.empty());
|
||||
}
|
||||
|
||||
if (mIsShortInvite)
|
||||
|
@ -887,7 +899,7 @@ void ConnectFriendWizard::cleanFriendCert()
|
|||
else
|
||||
errorMsg = tr("Valid certificate") ;
|
||||
|
||||
ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png"));
|
||||
ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png"));
|
||||
} else {
|
||||
if (error_code > 0) {
|
||||
switch (error_code) {
|
||||
|
@ -903,16 +915,17 @@ void ConnectFriendWizard::cleanFriendCert()
|
|||
|
||||
default:
|
||||
errorMsg = tr("Not a valid Retroshare certificate!") ;
|
||||
ui->friendCertCleanLabel->setStyleSheet("QLabel#friendCertCleanLabel {border: 1px solid #DCDC41; border-radius: 6px; background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFFFD7, stop:1 #FFFFB2);}");
|
||||
}
|
||||
}
|
||||
ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png"));
|
||||
}
|
||||
}
|
||||
|
||||
ui->friendCertCleanLabel->setPixmap(certValid ? FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png") : FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png"));
|
||||
ui->friendCertCleanLabel->setToolTip(errorMsg);
|
||||
ui->friendCertCleanLabel->setPixmap(certValid ? FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png") : FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png"));
|
||||
ui->friendCertCleanLabel->setToolTip("<p>" + errorMsg + (certValid ? "\n" + certDetail : "") + "</p>");
|
||||
ui->friendCertCleanLabel->setText(errorMsg);
|
||||
ui->friendCertCleanLabel->setProperty("WrongValue", !certValid && !errorMsg.isEmpty());
|
||||
ui->friendCertCleanLabel->style()->unpolish(ui->friendCertCleanLabel);
|
||||
ui->friendCertCleanLabel->style()->polish( ui->friendCertCleanLabel);
|
||||
|
||||
ui->TextPage->setComplete(certValid);
|
||||
}
|
||||
|
|
|
@ -590,7 +590,7 @@
|
|||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">Email</string>
|
||||
<string notr="true">ProfilId</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -629,7 +629,7 @@
|
|||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Signers</string>
|
||||
<string>Signers:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -675,6 +675,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="knownIpLabel">
|
||||
<property name="text">
|
||||
<string>Known IP:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QPlainTextEdit" name="knownIpEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "gui/common/AvatarDefs.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
#include "gui/notifyqt.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
|
||||
#include <retroshare/rsmsgs.h>
|
||||
#include <retroshare/rspeers.h>
|
||||
|
@ -59,12 +60,13 @@ MsgItem::MsgItem(FeedHolder *parent, uint32_t feedId, const std::string &msgId,
|
|||
//connect( gotoButton, SIGNAL( clicked( void ) ), this, SLOT( gotoHome ( void ) ) );
|
||||
|
||||
/* specific ones */
|
||||
connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), this, SLOT(checkMessageReadStatus()));
|
||||
connect( playButton, SIGNAL( clicked( void ) ), this, SLOT( playMedia ( void ) ) );
|
||||
connect( deleteButton, SIGNAL( clicked( void ) ), this, SLOT( deleteMsg ( void ) ) );
|
||||
connect( replyButton, SIGNAL( clicked( void ) ), this, SLOT( replyMsg ( void ) ) );
|
||||
connect( sendinviteButton, SIGNAL( clicked( void ) ), this, SLOT( sendInvite ( void ) ) );
|
||||
|
||||
mEventHandlerId = 0;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS );
|
||||
|
||||
expandFrame->hide();
|
||||
info_Frame_Invite->hide();
|
||||
|
@ -73,6 +75,58 @@ MsgItem::MsgItem(FeedHolder *parent, uint32_t feedId, const std::string &msgId,
|
|||
updateItem();
|
||||
}
|
||||
|
||||
MsgItem::~MsgItem()
|
||||
{
|
||||
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||
}
|
||||
|
||||
void MsgItem::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
if(event->mType != RsEventType::MAIL_STATUS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RsMailStatusEvent *fe = dynamic_cast<const RsMailStatusEvent*>(event.get());
|
||||
if (!fe) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fe->mMailStatusEventCode) {
|
||||
case RsMailStatusEventCode::MESSAGE_CHANGED:
|
||||
if (fe->mChangedMsgIds.find(mMsgId) != fe->mChangedMsgIds.end()) {
|
||||
MessageInfo msgInfo;
|
||||
|
||||
if (!rsMail->getMessage(mMsgId, msgInfo)) {
|
||||
removeItem();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mCloseOnRead) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (msgInfo.msgflags & RS_MSG_NEW) {
|
||||
/* Message status is still "new" */
|
||||
break;
|
||||
}
|
||||
|
||||
removeItem();
|
||||
}
|
||||
break;
|
||||
case RsMailStatusEventCode::MESSAGE_REMOVED:
|
||||
if (fe->mChangedMsgIds.find(mMsgId) != fe->mChangedMsgIds.end()) {
|
||||
removeItem();
|
||||
}
|
||||
break;
|
||||
case RsMailStatusEventCode::MESSAGE_SENT:
|
||||
case RsMailStatusEventCode::NEW_MESSAGE:
|
||||
case RsMailStatusEventCode::TAG_CHANGED:
|
||||
case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK:
|
||||
case RsMailStatusEventCode::SIGNATURE_FAILED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MsgItem::updateItemStatic()
|
||||
{
|
||||
/* fill in */
|
||||
|
@ -246,7 +300,6 @@ void MsgItem::doExpand(bool open)
|
|||
|
||||
mCloseOnRead = false;
|
||||
rsMail->MessageRead(mMsgId, false);
|
||||
mCloseOnRead = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -332,26 +385,6 @@ void MsgItem::toggle()
|
|||
expand(expandFrame->isHidden());
|
||||
}
|
||||
|
||||
void MsgItem::checkMessageReadStatus()
|
||||
{
|
||||
if (!mCloseOnRead) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageInfo msgInfo;
|
||||
if (!rsMail->getMessage(mMsgId, msgInfo)) {
|
||||
std::cerr << "MsgItem::checkMessageReadStatus() Couldn't find Msg" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (msgInfo.msgflags & RS_MSG_NEW) {
|
||||
/* Message status is still "new" */
|
||||
return;
|
||||
}
|
||||
|
||||
removeItem();
|
||||
}
|
||||
|
||||
void MsgItem::sendInvite()
|
||||
{
|
||||
MessageInfo mi;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "ui_MsgItem.h"
|
||||
#include "FeedItem.h"
|
||||
#include <stdint.h>
|
||||
#include <retroshare/rsevents.h>
|
||||
|
||||
class FeedHolder;
|
||||
class SubFileItem;
|
||||
|
@ -35,6 +36,7 @@ class MsgItem : public FeedItem, private Ui::MsgItem
|
|||
public:
|
||||
/** Default Constructor */
|
||||
MsgItem(FeedHolder *parent, uint32_t feedId, const std::string &msgId, bool isHome);
|
||||
virtual ~MsgItem();
|
||||
|
||||
void updateItemStatic();
|
||||
|
||||
|
@ -46,6 +48,7 @@ protected:
|
|||
|
||||
private:
|
||||
void fillExpandFrame();
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
|
||||
private slots:
|
||||
/* default stuff */
|
||||
|
@ -56,7 +59,6 @@ private slots:
|
|||
void deleteMsg();
|
||||
void replyMsg();
|
||||
void sendInvite();
|
||||
void checkMessageReadStatus();
|
||||
|
||||
void updateItem();
|
||||
|
||||
|
@ -66,6 +68,7 @@ private:
|
|||
|
||||
bool mIsHome;
|
||||
bool mCloseOnRead;
|
||||
RsEventsHandlerId_t mEventHandlerId;
|
||||
|
||||
std::list<SubFileItem *> mFileItems;
|
||||
};
|
||||
|
|
|
@ -22,13 +22,20 @@
|
|||
#include "MessageUserNotify.h"
|
||||
#include "gui/notifyqt.h"
|
||||
#include "gui/MainWindow.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
|
||||
#include "gui/msgs/MessageInterface.h"
|
||||
|
||||
MessageUserNotify::MessageUserNotify(QObject *parent) :
|
||||
UserNotify(parent)
|
||||
{
|
||||
connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), this, SLOT(updateIcon()));
|
||||
mEventHandlerId = 0;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS );
|
||||
}
|
||||
|
||||
MessageUserNotify::~MessageUserNotify()
|
||||
{
|
||||
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||
}
|
||||
|
||||
bool MessageUserNotify::hasSetting(QString *name, QString *group)
|
||||
|
@ -72,3 +79,28 @@ void MessageUserNotify::iconClicked()
|
|||
{
|
||||
MainWindow::showWindow(MainWindow::Messages);
|
||||
}
|
||||
|
||||
void MessageUserNotify::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
if(event->mType != RsEventType::MAIL_STATUS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RsMailStatusEvent *fe = dynamic_cast<const RsMailStatusEvent*>(event.get());
|
||||
if (!fe) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fe->mMailStatusEventCode) {
|
||||
case RsMailStatusEventCode::NEW_MESSAGE:
|
||||
case RsMailStatusEventCode::MESSAGE_CHANGED:
|
||||
case RsMailStatusEventCode::MESSAGE_REMOVED:
|
||||
updateIcon();
|
||||
break;
|
||||
case RsMailStatusEventCode::MESSAGE_SENT:
|
||||
case RsMailStatusEventCode::TAG_CHANGED:
|
||||
case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK:
|
||||
case RsMailStatusEventCode::SIGNATURE_FAILED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#ifndef MESSAGEUSERNOTIFY_H
|
||||
#define MESSAGEUSERNOTIFY_H
|
||||
|
||||
#include <retroshare/rsevents.h>
|
||||
#include "gui/common/UserNotify.h"
|
||||
|
||||
class MessageUserNotify : public UserNotify
|
||||
|
@ -29,6 +30,7 @@ class MessageUserNotify : public UserNotify
|
|||
|
||||
public:
|
||||
MessageUserNotify(QObject *parent = 0);
|
||||
virtual ~MessageUserNotify();
|
||||
|
||||
virtual bool hasSetting(QString *name, QString *group) override;
|
||||
|
||||
|
@ -41,6 +43,11 @@ private:
|
|||
virtual QString getNotifyMessage(bool plural) override;
|
||||
|
||||
virtual void iconClicked() override;
|
||||
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
|
||||
private:
|
||||
RsEventsHandlerId_t mEventHandlerId;
|
||||
};
|
||||
|
||||
#endif // MESSAGEUSERNOTIFY_H
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "util/HandleRichText.h"
|
||||
#include "util/DateTime.h"
|
||||
#include "util/QtVersion.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
|
||||
#include <retroshare/rspeers.h>
|
||||
#include <retroshare/rsfiles.h>
|
||||
|
@ -159,9 +160,6 @@ MessageWidget::MessageWidget(bool controlled, QWidget *parent, Qt::WindowFlags f
|
|||
viewsource->setShortcut(QKeySequence("CTRL+O"));
|
||||
connect(viewsource, SIGNAL(triggered()), this, SLOT(viewSource()));
|
||||
|
||||
connect(NotifyQt::getInstance(), SIGNAL(messagesTagsChanged()), this, SLOT(messagesTagsChanged()));
|
||||
connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), this, SLOT(messagesChanged()));
|
||||
|
||||
ui.imageBlockWidget->addButtonAction(tr("Load images always for this message"), this, SLOT(loadImagesAlways()), true);
|
||||
ui.msgText->setImageBlockWidget(ui.imageBlockWidget);
|
||||
|
||||
|
@ -211,6 +209,9 @@ MessageWidget::MessageWidget(bool controlled, QWidget *parent, Qt::WindowFlags f
|
|||
ui.dateText-> setText("");
|
||||
|
||||
ui.info_Frame_Invite->hide();
|
||||
|
||||
mEventHandlerId = 0;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS );
|
||||
}
|
||||
|
||||
MessageWidget::~MessageWidget()
|
||||
|
@ -218,6 +219,47 @@ MessageWidget::~MessageWidget()
|
|||
if (isControlled == false) {
|
||||
processSettings("MessageWidget", false);
|
||||
}
|
||||
|
||||
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||
}
|
||||
|
||||
void MessageWidget::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
if(event->mType != RsEventType::MAIL_STATUS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RsMailStatusEvent *fe = dynamic_cast<const RsMailStatusEvent*>(event.get());
|
||||
if (!fe) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fe->mMailStatusEventCode) {
|
||||
case RsMailStatusEventCode::MESSAGE_REMOVED:
|
||||
if (fe->mChangedMsgIds.find(currMsgId) != fe->mChangedMsgIds.end()) {
|
||||
if (isControlled) {
|
||||
/* processed by MessagesDialog */
|
||||
return;
|
||||
}
|
||||
|
||||
/* messages was removed */
|
||||
if (isWindow) {
|
||||
window()->close();
|
||||
} else {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RsMailStatusEventCode::TAG_CHANGED:
|
||||
messagesTagsChanged();
|
||||
break;
|
||||
case RsMailStatusEventCode::MESSAGE_SENT:
|
||||
case RsMailStatusEventCode::MESSAGE_CHANGED:
|
||||
case RsMailStatusEventCode::NEW_MESSAGE:
|
||||
case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK:
|
||||
case RsMailStatusEventCode::SIGNATURE_FAILED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageWidget::connectAction(enumActionType actionType, QToolButton* button)
|
||||
|
@ -407,25 +449,6 @@ void MessageWidget::messagesTagsChanged()
|
|||
showTagLabels();
|
||||
}
|
||||
|
||||
void MessageWidget::messagesChanged()
|
||||
{
|
||||
if (isControlled) {
|
||||
/* processed by MessagesDialog */
|
||||
return;
|
||||
}
|
||||
|
||||
/* test Message */
|
||||
MessageInfo msgInfo;
|
||||
if (rsMail->getMessage(currMsgId, msgInfo) == false) {
|
||||
/* messages was removed */
|
||||
if (isWindow) {
|
||||
window()->close();
|
||||
} else {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageWidget::clearTagLabels()
|
||||
{
|
||||
/* clear all tags */
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define _MESSAGEWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <retroshare/rsevents.h>
|
||||
#include "ui_MessageWidget.h"
|
||||
|
||||
class QToolButton;
|
||||
|
@ -75,7 +76,6 @@ private slots:
|
|||
|
||||
void msgfilelistWidgetCostumPopupMenu(QPoint);
|
||||
void messagesTagsChanged();
|
||||
void messagesChanged();
|
||||
|
||||
void togglefileview(bool noUpdate = false);
|
||||
void getcurrentrecommended();
|
||||
|
@ -93,11 +93,14 @@ private:
|
|||
void showTagLabels();
|
||||
void setToolbarButtonStyle(Qt::ToolButtonStyle style);
|
||||
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
|
||||
bool isControlled;
|
||||
bool isWindow;
|
||||
std::string currMsgId;
|
||||
unsigned int currMsgFlags;
|
||||
bool expandFiles;
|
||||
RsEventsHandlerId_t mEventHandlerId;
|
||||
|
||||
QList<QLabel*> tagLabels;
|
||||
|
||||
|
|
|
@ -268,9 +268,6 @@ MessagesDialog::MessagesDialog(QWidget *parent)
|
|||
|
||||
registerHelpButton(ui.helpButton,help_str,"MessagesDialog") ;
|
||||
|
||||
connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), mMessageModel, SLOT(updateMessages()));
|
||||
connect(NotifyQt::getInstance(), SIGNAL(messagesTagsChanged()), this, SLOT(messagesTagsChanged()));
|
||||
|
||||
connect(ui.filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
|
||||
connect(ui.filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int)));
|
||||
|
||||
|
@ -294,6 +291,9 @@ MessagesDialog::MessagesDialog(QWidget *parent)
|
|||
|
||||
mEventHandlerId=0;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS );
|
||||
|
||||
mTagEventHandlerId = 0;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { RsQThreadUtils::postToObject( [this,event]() { handleTagEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_TAG );
|
||||
}
|
||||
|
||||
void MessagesDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
|
@ -310,9 +310,34 @@ void MessagesDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> even
|
|||
case RsMailStatusEventCode::MESSAGE_SENT:
|
||||
case RsMailStatusEventCode::MESSAGE_REMOVED:
|
||||
case RsMailStatusEventCode::NEW_MESSAGE:
|
||||
case RsMailStatusEventCode::MESSAGE_CHANGED:
|
||||
case RsMailStatusEventCode::TAG_CHANGED:
|
||||
mMessageModel->updateMessages();
|
||||
updateMessageSummaryList();
|
||||
break;
|
||||
default:
|
||||
case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK:
|
||||
case RsMailStatusEventCode::SIGNATURE_FAILED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesDialog::handleTagEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
if (event->mType != RsEventType::MAIL_TAG) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RsMailTagEvent *fe = dynamic_cast<const RsMailTagEvent*>(event.get());
|
||||
if (!fe) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fe->mMailTagEventCode) {
|
||||
case RsMailTagEventCode::TAG_ADDED:
|
||||
case RsMailTagEventCode::TAG_CHANGED:
|
||||
case RsMailTagEventCode::TAG_REMOVED:
|
||||
fillQuickView();
|
||||
mMessageModel->updateMessages();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -324,6 +349,12 @@ void MessagesDialog::preModelUpdate()
|
|||
mTmpSavedSelectedIds.clear();
|
||||
getSelectedMessages(mTmpSavedSelectedIds);
|
||||
|
||||
mTmpSavedCurrentId.clear();
|
||||
const QModelIndex& m = ui.messageTreeWidget->currentIndex();
|
||||
if (m.isValid()) {
|
||||
mTmpSavedCurrentId = m.sibling(m.row(), RsMessageModel::COLUMN_THREAD_MSGID).data(RsMessageModel::MsgIdRole).toString();
|
||||
}
|
||||
|
||||
std::cerr << "Pre-change: saving selection for " << mTmpSavedSelectedIds.size() << " indexes" << std::endl;
|
||||
}
|
||||
|
||||
|
@ -341,6 +372,13 @@ void MessagesDialog::postModelUpdate()
|
|||
}
|
||||
|
||||
ui.messageTreeWidget->selectionModel()->select(sel,QItemSelectionModel::SelectCurrent);
|
||||
|
||||
if (!mTmpSavedCurrentId.isEmpty()) {
|
||||
QModelIndex index = mMessageProxyModel->mapFromSource(mMessageModel->getIndexOfMessage(mTmpSavedCurrentId.toStdString()));
|
||||
if (index.isValid()) {
|
||||
ui.messageTreeWidget->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesDialog::sortColumn(int col,Qt::SortOrder so)
|
||||
|
@ -356,6 +394,9 @@ MessagesDialog::~MessagesDialog()
|
|||
{
|
||||
// save settings
|
||||
processSettings(false);
|
||||
|
||||
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||
rsEvents->unregisterEventsHandler(mTagEventHandlerId);
|
||||
}
|
||||
|
||||
UserNotify *MessagesDialog::createUserNotify(QObject *parent)
|
||||
|
@ -921,13 +962,6 @@ void MessagesDialog::changeQuickView(int newrow)
|
|||
mMessageProxyModel->setFilterRegExp(QRegExp(RsMessageModel::FilterString)); // this triggers the update of the proxy model
|
||||
}
|
||||
|
||||
void MessagesDialog::messagesTagsChanged()
|
||||
{
|
||||
fillQuickView();
|
||||
mMessageModel->updateMessages();
|
||||
}
|
||||
|
||||
|
||||
// click in messageTreeWidget
|
||||
void MessagesDialog::currentChanged(const QModelIndex& new_proxy_index,const QModelIndex& /*old_proxy_index*/)
|
||||
{
|
||||
|
|
|
@ -66,7 +66,6 @@ protected:
|
|||
|
||||
public slots:
|
||||
//void insertMessages();
|
||||
void messagesTagsChanged();
|
||||
void messageRemoved();
|
||||
void preModelUpdate();
|
||||
void postModelUpdate();
|
||||
|
@ -112,6 +111,7 @@ private slots:
|
|||
|
||||
private:
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
void handleTagEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
|
||||
void updateInterface();
|
||||
|
||||
|
@ -159,9 +159,11 @@ private:
|
|||
Ui::MessagesDialog ui;
|
||||
|
||||
QList<QString> mTmpSavedSelectedIds;
|
||||
QString mTmpSavedCurrentId;
|
||||
QModelIndex lastSelectedIndex;
|
||||
|
||||
RsEventsHandlerId_t mEventHandlerId;
|
||||
RsEventsHandlerId_t mTagEventHandlerId;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "gui/common/TagDefs.h"
|
||||
#include "gui/settings/NewTag.h"
|
||||
#include "gui/notifyqt.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
|
||||
#include "gui/msgs/MessageInterface.h"
|
||||
|
||||
|
@ -46,11 +47,18 @@ TagsMenu::TagsMenu(const QString &title, QWidget *parent)
|
|||
: QMenu (title, parent)
|
||||
{
|
||||
connect(this, SIGNAL(triggered (QAction*)), this, SLOT(tagTriggered(QAction*)));
|
||||
connect(NotifyQt::getInstance(), SIGNAL(messagesTagsChanged()), this, SLOT(fillTags()));
|
||||
|
||||
mEventHandlerId = 0;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_TAG );
|
||||
|
||||
fillTags();
|
||||
}
|
||||
|
||||
TagsMenu::~TagsMenu()
|
||||
{
|
||||
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||
}
|
||||
|
||||
void TagsMenu::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
QMenu::paintEvent(e);
|
||||
|
@ -89,6 +97,26 @@ void TagsMenu::paintEvent(QPaintEvent *e)
|
|||
}
|
||||
}
|
||||
|
||||
void TagsMenu::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
if (event->mType != RsEventType::MAIL_TAG) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RsMailTagEvent *fe = dynamic_cast<const RsMailTagEvent*>(event.get());
|
||||
if (!fe) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fe->mMailTagEventCode) {
|
||||
case RsMailTagEventCode::TAG_ADDED:
|
||||
case RsMailTagEventCode::TAG_CHANGED:
|
||||
case RsMailTagEventCode::TAG_REMOVED:
|
||||
fillTags();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TagsMenu::fillTags()
|
||||
{
|
||||
clear();
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <QMenu>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <retroshare/rsevents.h>
|
||||
|
||||
class TagsMenu : public QMenu
|
||||
{
|
||||
|
@ -31,6 +32,7 @@ class TagsMenu : public QMenu
|
|||
|
||||
public:
|
||||
TagsMenu(const QString &title, QWidget *parent);
|
||||
virtual ~TagsMenu();
|
||||
|
||||
void activateActions(std::list<uint32_t>& tagIds);
|
||||
|
||||
|
@ -42,8 +44,14 @@ protected:
|
|||
virtual void paintEvent(QPaintEvent *e);
|
||||
|
||||
private slots:
|
||||
void fillTags();
|
||||
void tagTriggered(QAction *action);
|
||||
|
||||
private:
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
void fillTags();
|
||||
|
||||
private:
|
||||
RsEventsHandlerId_t mEventHandlerId;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,12 +21,6 @@
|
|||
#include "gui/common/FilesDefs.h"
|
||||
#include <retroshare/rsgxsifacehelper.h>
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
//#include <QMutexLocker>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
#include "notifyqt.h"
|
||||
#include <retroshare/rsnotify.h>
|
||||
#include <retroshare/rspeers.h>
|
||||
|
@ -55,6 +49,13 @@
|
|||
|
||||
#include "retroshare/rsplugin.h"
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
//#include <QMutexLocker>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
/*****
|
||||
* #define NOTIFY_DEBUG
|
||||
****/
|
||||
|
@ -223,37 +224,51 @@ bool NotifyQt::askForPassword(const std::string& title, const std::string& key_d
|
|||
{
|
||||
RsAutoUpdatePage::lockAllEvents() ;
|
||||
|
||||
QInputDialog dialog;
|
||||
QString windowTitle;
|
||||
if (title == "") {
|
||||
dialog.setWindowTitle(tr("Passphrase required"));
|
||||
windowTitle = tr("Passphrase required");
|
||||
} else if (title == "AuthSSLimpl::SignX509ReqWithGPG()") {
|
||||
dialog.setWindowTitle(tr("You need to sign your node's certificate."));
|
||||
windowTitle = tr("You need to sign your node's certificate.");
|
||||
} else if (title == "p3IdService::service_CreateGroup()") {
|
||||
dialog.setWindowTitle(tr("You need to sign your forum/chatrooms identity."));
|
||||
windowTitle = tr("You need to sign your forum/chatrooms identity.");
|
||||
} else {
|
||||
dialog.setWindowTitle(QString::fromStdString(title));
|
||||
windowTitle = QString::fromStdString(title);
|
||||
}
|
||||
|
||||
dialog.setLabelText((prev_is_bad ? QString("%1<br/><br/>").arg(tr("Wrong password !")) : QString()) + QString("<b>%1</b><br/>Profile: <i>%2</i>\n").arg(tr("Please enter your Retroshare passphrase"), QString::fromUtf8(key_details.c_str())));
|
||||
dialog.setTextEchoMode(QLineEdit::Password);
|
||||
dialog.setModal(true);
|
||||
QString labelText = ( prev_is_bad ? QString("%1<br/><br/>").arg(tr("Wrong password !")) : QString() )
|
||||
+ QString("<b>%1</b><br/>Profile: <i>%2</i>\n")
|
||||
.arg( tr("Please enter your Retroshare passphrase")
|
||||
, QString::fromUtf8(key_details.c_str()) );
|
||||
QLineEdit::EchoMode textEchoMode = QLineEdit::Password;
|
||||
bool modal = true;
|
||||
|
||||
int ret = dialog.exec();
|
||||
bool sameThread = QThread::currentThread() == qApp->thread();
|
||||
Gui_InputDialogReturn ret;
|
||||
qRegisterMetaType<Gui_InputDialogReturn>("Gui_InputDialogReturn");
|
||||
QMetaObject::invokeMethod( MainWindow::getInstance()
|
||||
, "guiInputDialog"
|
||||
, sameThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection
|
||||
, Q_RETURN_ARG(Gui_InputDialogReturn, ret)
|
||||
, Q_ARG(QString, windowTitle)
|
||||
, Q_ARG(QString, labelText)
|
||||
, Q_ARG(QLineEdit::EchoMode, textEchoMode)
|
||||
, Q_ARG(bool, modal)
|
||||
);
|
||||
|
||||
cancelled = false ;
|
||||
cancelled = false ;
|
||||
|
||||
RsAutoUpdatePage::unlockAllEvents() ;
|
||||
|
||||
if (ret == QDialog::Rejected) {
|
||||
password.clear() ;
|
||||
cancelled = true ;
|
||||
return true ;
|
||||
}
|
||||
if (ret.execReturn == QDialog::Rejected) {
|
||||
password.clear() ;
|
||||
cancelled = true ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
if (ret == QDialog::Accepted) {
|
||||
password = dialog.textValue().toUtf8().constData();
|
||||
return true;
|
||||
}
|
||||
if (ret.execReturn == QDialog::Accepted) {
|
||||
password = ret.textValue.toUtf8().constData();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -548,18 +563,6 @@ void NotifyQt::notifyListChange(int list, int type)
|
|||
break;
|
||||
case NOTIFY_LIST_SEARCHLIST:
|
||||
break;
|
||||
case NOTIFY_LIST_MESSAGELIST:
|
||||
#ifdef NOTIFY_DEBUG
|
||||
std::cerr << "received msg changed" << std::endl ;
|
||||
#endif
|
||||
emit messagesChanged() ;
|
||||
break;
|
||||
case NOTIFY_LIST_MESSAGE_TAGS:
|
||||
#ifdef NOTIFY_DEBUG
|
||||
std::cerr << "received msg tags changed" << std::endl ;
|
||||
#endif
|
||||
emit messagesTagsChanged();
|
||||
break;
|
||||
case NOTIFY_LIST_CHANNELLIST:
|
||||
break;
|
||||
case NOTIFY_LIST_TRANSFERLIST:
|
||||
|
@ -648,8 +651,6 @@ void NotifyQt::notifyListPreChange(int list, int /*type*/)
|
|||
break;
|
||||
case NOTIFY_LIST_SEARCHLIST:
|
||||
break;
|
||||
case NOTIFY_LIST_MESSAGELIST:
|
||||
break;
|
||||
case NOTIFY_LIST_CHANNELLIST:
|
||||
break;
|
||||
case NOTIFY_LIST_TRANSFERLIST:
|
||||
|
@ -682,7 +683,6 @@ void NotifyQt::UpdateGUI()
|
|||
// the gui is running, then they get updated by callbacks.
|
||||
if(!already_updated)
|
||||
{
|
||||
emit messagesChanged() ;
|
||||
emit neighboursChanged();
|
||||
emit configChanged();
|
||||
|
||||
|
|
|
@ -109,8 +109,6 @@ class NotifyQt: public QObject, public NotifyClient
|
|||
void lobbyListChanged() const ;
|
||||
void chatLobbyEvent(qulonglong,int,const RsGxsId&,const QString&) ;
|
||||
void neighboursChanged() const ;
|
||||
void messagesChanged() const ;
|
||||
void messagesTagsChanged() const;
|
||||
void configChanged() const ;
|
||||
void logInfoChanged(const QString&) const ;
|
||||
void chatStatusChanged(const ChatId&,const QString&) const ;
|
||||
|
|
|
@ -2230,6 +2230,15 @@ PlotWidget {
|
|||
/* RetroShare specific part */
|
||||
/**********************************/
|
||||
|
||||
/**** Fix QPushButton ****/
|
||||
|
||||
QPushButton,
|
||||
QPushButton:disabled,
|
||||
QPushButton:checked {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
/**** Fix QTreeView Items ****/
|
||||
|
||||
QTreeView::item,
|
||||
|
@ -2248,13 +2257,13 @@ QTreeView:branch:selected:!active {
|
|||
/**** Fix QSplitter ****/
|
||||
|
||||
QSplitter {
|
||||
background-color: #19232D;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
QSplitter::handle {
|
||||
background-color: #455364;
|
||||
border: 0px solid #455364;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(0,0,0,0);
|
||||
border: 0px;
|
||||
border-radius: 2px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2286,7 +2295,7 @@ QFrame#bottomFrame,/* Frame used at the bottom of dialog*/
|
|||
QFrame#toasterFrame,/* Frame used in Toasters*/
|
||||
QFrame#toolBarFrame {/* Frame used for buttons*/
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #19232D, stop:1 #32414B);
|
||||
border: 1px solid #CCCCCC;
|
||||
border: 1px solid #455364;
|
||||
}
|
||||
QFrame#toolBarFrame > LineEditClear {
|
||||
background-color: #29333D;
|
||||
|
@ -2316,7 +2325,7 @@ QFrame#titleBarFrame {
|
|||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #455364, stop: 0.5 #54687A,stop: 0.6 #44586A, stop:1 #455364);
|
||||
border: 0px;
|
||||
}
|
||||
QFrame[objectName^="#headerBFrame"] {
|
||||
QFrame[objectName^="headerBFrame"] {
|
||||
border: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
|
|
@ -2230,6 +2230,15 @@ PlotWidget {
|
|||
/* RetroShare specific part */
|
||||
/**********************************/
|
||||
|
||||
/**** Fix QPushButton ****/
|
||||
|
||||
QPushButton,
|
||||
QPushButton:disabled,
|
||||
QPushButton:checked {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
/**** Fix QTreeView Items ****/
|
||||
|
||||
QTreeView::item,
|
||||
|
@ -2248,13 +2257,13 @@ QTreeView:branch:selected:!active {
|
|||
/**** Fix QSplitter ****/
|
||||
|
||||
QSplitter {
|
||||
background-color: white;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
QSplitter::handle {
|
||||
background-color: #C9CDD0;
|
||||
border: 0px solid #C9CDD0;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(0,0,0,0);
|
||||
border: 0px;
|
||||
border-radius: 2px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2307,25 +2316,34 @@ QLabel#avatarLabel{
|
|||
}
|
||||
|
||||
|
||||
/* HeaderFrame & TitleBarFrame */
|
||||
/* HeaderFrame */
|
||||
|
||||
QFrame[objectName^="headerFrame"],
|
||||
QFrame[objectName^="headerBFrame"],
|
||||
QToolBar#headerToolBar,
|
||||
QFrame#titleBarFrame {
|
||||
QToolBar#headerToolBar {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD);
|
||||
border: 0px;
|
||||
}
|
||||
QFrame[objectName^="#headerBFrame"] {
|
||||
QFrame[objectName^="headerBFrame"] {
|
||||
border: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QFrame[objectName^="headerFrame"] > *:!hover,
|
||||
QFrame[objectName^="headerBFrame"] > *:!hover,
|
||||
QFrame#titleBarFrame > *:!hover {
|
||||
QFrame[objectName^="headerBFrame"] > *:!hover {
|
||||
background: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* TitleBarFrame */
|
||||
|
||||
QFrame#titleBarFrame {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #F0F8FD, stop:1 #E6F2FD);
|
||||
border: 0px;
|
||||
}
|
||||
QFrame#titleBarFrame > *:!hover {
|
||||
background: transparent;
|
||||
color: black;
|
||||
}
|
||||
QFrame#titleBarFrame QComboBox,
|
||||
QFrame#titleBarFrame QLineEdit,
|
||||
QFrame#titleBarFrame QTextEdit {
|
||||
|
|
|
@ -418,31 +418,3 @@ OpModeStatus[opMode="Minimal"] {
|
|||
[WrongValue="true"] {
|
||||
background-color: #FF8080;
|
||||
}
|
||||
|
||||
/* HeaderFrame & TitleBarFrame */
|
||||
|
||||
QFrame[objectName^="headerFrame"],
|
||||
QFrame[objectName^="headerBFrame"] {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD);
|
||||
border: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QFrame[objectName^="#headerBFrame"] {
|
||||
border: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QFrame[objectName^="headerFrame"] > *:!hover,
|
||||
QFrame[objectName^="headerBFrame"] > *:!hover{
|
||||
background: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
QFrame#toolBarFrame, QFrame#toolBarFrameTop {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8);
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
|
||||
ChatWidget QFrame#titleBarFrame {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8);
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "gui/common/TagDefs.h"
|
||||
#include <algorithm>
|
||||
#include "NewTag.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
|
||||
MessagePage::MessagePage(QWidget * parent, Qt::WindowFlags flags)
|
||||
: ConfigPage(parent, flags)
|
||||
|
@ -54,10 +55,14 @@ MessagePage::MessagePage(QWidget * parent, Qt::WindowFlags flags)
|
|||
connect(ui.loadEmbeddedImages, SIGNAL(toggled(bool)), this,SLOT(updateLoadEmbededImages() ));
|
||||
connect(ui.openComboBox, SIGNAL(currentIndexChanged(int)),this,SLOT(updateMsgOpen() ));
|
||||
connect(ui.emoticonscheckBox, SIGNAL(toggled(bool)), this,SLOT(updateLoadEmoticons() ));
|
||||
|
||||
mTagEventHandlerId = 0;
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mTagEventHandlerId, RsEventType::MAIL_TAG );
|
||||
}
|
||||
|
||||
MessagePage::~MessagePage()
|
||||
{
|
||||
rsEvents->unregisterEventsHandler(mTagEventHandlerId);
|
||||
delete(m_pTags);
|
||||
}
|
||||
|
||||
|
@ -134,6 +139,27 @@ MessagePage::load()
|
|||
fillTags();
|
||||
}
|
||||
|
||||
void MessagePage::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
if (event->mType != RsEventType::MAIL_TAG) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RsMailTagEvent *fe = dynamic_cast<const RsMailTagEvent*>(event.get());
|
||||
if (!fe) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fe->mMailTagEventCode) {
|
||||
case RsMailTagEventCode::TAG_ADDED:
|
||||
case RsMailTagEventCode::TAG_CHANGED:
|
||||
case RsMailTagEventCode::TAG_REMOVED:
|
||||
rsMail->getMessageTagTypes(*m_pTags);
|
||||
fillTags();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fill tags
|
||||
void MessagePage::fillTags()
|
||||
{
|
||||
|
|
|
@ -62,11 +62,13 @@ private slots:
|
|||
void updateLoadEmoticons();
|
||||
|
||||
private:
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
void fillTags();
|
||||
|
||||
/* Pointer for not include of rsmsgs.h */
|
||||
MsgTagType *m_pTags;
|
||||
std::list<uint32_t> m_changedTagIds;
|
||||
RsEventsHandlerId_t mTagEventHandlerId;
|
||||
|
||||
Ui::MessagePage ui;
|
||||
};
|
||||
|
|
|
@ -28,11 +28,10 @@
|
|||
#include "retroshare/rsconfig.h"
|
||||
#include "retroshare/rsinit.h"
|
||||
#include "retroshare/rspeers.h"
|
||||
#include "retroshare/rstor.h"
|
||||
#include <QTcpSocket>
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "TorControl/TorManager.h"
|
||||
#include "TorControl/TorControl.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
|
||||
#include <iomanip>
|
||||
|
@ -92,8 +91,8 @@ void TorStatus::getTorStatus()
|
|||
if(RsAccounts::isTorAuto())
|
||||
{
|
||||
// get Tor status
|
||||
int tor_control_status = Tor::TorManager::instance()->control()->status();
|
||||
int torstatus = Tor::TorManager::instance()->control()->torStatus();
|
||||
RsTorConnectivityStatus tor_control_status = RsTor::torConnectivityStatus();
|
||||
RsTorStatus torstatus = RsTor::torStatus();
|
||||
|
||||
QString tor_control_status_str,torstatus_str ;
|
||||
bool tor_control_ok ;
|
||||
|
@ -101,30 +100,31 @@ void TorStatus::getTorStatus()
|
|||
switch(tor_control_status)
|
||||
{
|
||||
default:
|
||||
case Tor::TorControl::Error : tor_control_ok = false ; tor_control_status_str = "Error" ; break ;
|
||||
case Tor::TorControl::NotConnected: tor_control_ok = false ; tor_control_status_str = "Not connected" ; break ;
|
||||
case Tor::TorControl::Connecting: tor_control_ok = false ; tor_control_status_str = "Connecting" ; break ;
|
||||
case Tor::TorControl::Authenticating: tor_control_ok = false ; tor_control_status_str = "Authenticating" ; break ;
|
||||
case Tor::TorControl::Connected: tor_control_ok = true ; tor_control_status_str = "Connected" ; break ;
|
||||
}
|
||||
case RsTorConnectivityStatus::ERROR : tor_control_ok = false ; tor_control_status_str = "Error" ; break ;
|
||||
case RsTorConnectivityStatus::NOT_CONNECTED: tor_control_ok = false ; tor_control_status_str = "Not connected" ; break ;
|
||||
case RsTorConnectivityStatus::CONNECTING: tor_control_ok = false ; tor_control_status_str = "Connecting" ; break ;
|
||||
case RsTorConnectivityStatus::AUTHENTICATING: tor_control_ok = false ; tor_control_status_str = "Authenticating" ; break ;
|
||||
case RsTorConnectivityStatus::AUTHENTICATED: tor_control_ok = false ; tor_control_status_str = "Connected" ; break ;
|
||||
case RsTorConnectivityStatus::HIDDEN_SERVICE_READY: tor_control_ok = true ; tor_control_status_str = "Hidden service ready" ; break ;
|
||||
}
|
||||
|
||||
switch(torstatus)
|
||||
{
|
||||
default:
|
||||
case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ;
|
||||
case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ;
|
||||
case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ;
|
||||
case RsTorStatus::UNKNOWN: torstatus_str = "Unknown" ; break ;
|
||||
case RsTorStatus::OFFLINE: torstatus_str = "Tor offline" ; break ;
|
||||
case RsTorStatus::READY: torstatus_str = "Tor ready" ; break ;
|
||||
}
|
||||
|
||||
#define MIN_RS_NET_SIZE 10
|
||||
|
||||
if(torstatus == Tor::TorControl::TorOffline || !online || !tor_control_ok)
|
||||
if(torstatus == RsTorStatus::OFFLINE || !online || !tor_control_ok)
|
||||
{
|
||||
// RED - some issue.
|
||||
torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-stopping.png").scaledToHeight(1.5*S,Qt::SmoothTransformation));
|
||||
torstatusLabel->setToolTip( text + tr("Tor is currently offline"));
|
||||
}
|
||||
else if(torstatus == Tor::TorControl::TorReady && online && tor_control_ok)
|
||||
else if(torstatus == RsTorStatus::READY && online && tor_control_ok)
|
||||
{
|
||||
torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-on.png").scaledToHeight(1.5*S,Qt::SmoothTransformation));
|
||||
torstatusLabel->setToolTip( text + tr("Tor is OK"));
|
||||
|
@ -141,12 +141,12 @@ void TorStatus::getTorStatus()
|
|||
if(!_updated)
|
||||
{
|
||||
RsPeerDetails pd;
|
||||
uint32_t hiddentype;
|
||||
if (rsPeers->getPeerDetails(rsPeers->getOwnId(), pd)) {
|
||||
uint32_t hiddentype = RS_HIDDEN_TYPE_UNKNOWN;
|
||||
|
||||
if (rsPeers->getPeerDetails(rsPeers->getOwnId(), pd))
|
||||
{
|
||||
if(pd.netMode == RS_NETMODE_HIDDEN)
|
||||
{
|
||||
hiddentype = pd.hiddenType;
|
||||
}
|
||||
}
|
||||
std::string proxyaddr;
|
||||
uint16_t proxyport;
|
||||
|
|
|
@ -66,7 +66,7 @@ CrashStackTrace gCrashStackTrace;
|
|||
# include "gui/settings/JsonApiPage.h"
|
||||
#endif // RS_JSONAPI
|
||||
|
||||
#include "TorControl/TorManager.h"
|
||||
#include "retroshare/rstor.h"
|
||||
#include "TorControl/TorControlWindow.h"
|
||||
|
||||
#include "retroshare/rsidentity.h"
|
||||
|
@ -99,10 +99,6 @@ __declspec(dllexport) __cdecl BOOL _OPENSSL_isservice(void)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/*** WINDOWS DON'T LIKE THIS - REDEFINES VER numbers.
|
||||
#include <gui/qskinobject/qskinobject.h>
|
||||
****/
|
||||
|
||||
#include <util/stringutil.h>
|
||||
#include <retroshare/rsinit.h>
|
||||
#include <retroshare/rsiface.h>
|
||||
|
@ -399,24 +395,23 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
|||
{
|
||||
// Now that we know the Tor service running, and we know the SSL id, we can make sure it provides a viable hidden service
|
||||
|
||||
QString tor_hidden_service_dir = QString::fromStdString(RsAccounts::AccountDirectory()) + QString("/hidden_service/") ;
|
||||
std::string tor_hidden_service_dir = RsAccounts::AccountDirectory() + "/hidden_service/" ;
|
||||
|
||||
Tor::TorManager *torManager = Tor::TorManager::instance();
|
||||
torManager->setTorDataDirectory(Rshare::dataDirectory() + QString("/tor/"));
|
||||
torManager->setHiddenServiceDirectory(tor_hidden_service_dir); // re-set it, because now it's changed to the specific location that is run
|
||||
RsTor::setTorDataDirectory(Rshare::dataDirectory().toStdString() + "/tor/");
|
||||
RsTor::setHiddenServiceDirectory(tor_hidden_service_dir); // re-set it, because now it's changed to the specific location that is run
|
||||
|
||||
RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir.toUtf8())) ;
|
||||
RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir)) ;
|
||||
|
||||
torManager->setupHiddenService();
|
||||
//RsTor::setupHiddenService();
|
||||
|
||||
if(! torManager->start() || torManager->hasError())
|
||||
if(! RsTor::start() || RsTor::hasError())
|
||||
{
|
||||
QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+torManager->errorMessage()) ;
|
||||
QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+QString::fromStdString(RsTor::errorMessage())) ;
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
{
|
||||
TorControlDialog tcd(torManager) ;
|
||||
TorControlDialog tcd;
|
||||
QString error_msg ;
|
||||
tcd.show();
|
||||
|
||||
|
@ -460,30 +455,29 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
|||
{
|
||||
// Tor works with viable hidden service. Let's use it!
|
||||
|
||||
QString service_id ;
|
||||
QString onion_address ;
|
||||
std::string service_id ;
|
||||
std::string onion_address ;
|
||||
uint16_t service_port ;
|
||||
uint16_t service_target_port ;
|
||||
uint16_t proxy_server_port ;
|
||||
QHostAddress service_target_address ;
|
||||
QHostAddress proxy_server_address ;
|
||||
std::string service_target_address ;
|
||||
std::string proxy_server_address ;
|
||||
|
||||
Tor::TorManager *torManager = Tor::TorManager::instance();
|
||||
torManager->getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port);
|
||||
torManager->getProxyServerInfo(proxy_server_address,proxy_server_port) ;
|
||||
RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port);
|
||||
RsTor::getProxyServerInfo(proxy_server_address,proxy_server_port) ;
|
||||
|
||||
std::cerr << "Got hidden service info: " << std::endl;
|
||||
std::cerr << " onion address : " << onion_address.toStdString() << std::endl;
|
||||
std::cerr << " service_id : " << service_id.toStdString() << std::endl;
|
||||
std::cerr << " onion address : " << onion_address << std::endl;
|
||||
std::cerr << " service_id : " << service_id << std::endl;
|
||||
std::cerr << " service port : " << service_port << std::endl;
|
||||
std::cerr << " target port : " << service_target_port << std::endl;
|
||||
std::cerr << " target address : " << service_target_address.toString().toStdString() << std::endl;
|
||||
std::cerr << " target address : " << service_target_address << std::endl;
|
||||
|
||||
std::cerr << "Setting proxy server to " << service_target_address.toString().toStdString() << ":" << service_target_port << std::endl;
|
||||
std::cerr << "Setting proxy server to " << service_target_address << ":" << service_target_port << std::endl;
|
||||
|
||||
rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address.toString().toStdString(), service_target_port);
|
||||
rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address.toStdString(), service_port);
|
||||
rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address.toString().toStdString(),proxy_server_port) ;
|
||||
rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address, service_target_port);
|
||||
rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address, service_port);
|
||||
rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address,proxy_server_port) ;
|
||||
}
|
||||
|
||||
Rshare::initPlugins();
|
||||
|
@ -597,6 +591,10 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
|||
RsGxsUpdateBroadcast::cleanup();
|
||||
#endif
|
||||
|
||||
if (is_auto_tor) {
|
||||
RsTor::stop();
|
||||
}
|
||||
|
||||
RsControl::instance()->rsGlobalShutDown();
|
||||
|
||||
delete(soundManager);
|
||||
|
|
21
retroshare-gui/src/qss/retroclassic.qss
Normal file
21
retroshare-gui/src/qss/retroclassic.qss
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* HeaderFrame & TitleBarFrame */
|
||||
|
||||
QFrame[objectName^="headerFrame"] {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD);
|
||||
}
|
||||
QFrame[objectName^="headerBFrame"] {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD);
|
||||
border: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QFrame[objectName^="headerFrame"] > *:!hover,
|
||||
QFrame[objectName^="headerBFrame"] > *:!hover{
|
||||
background: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
QFrame[objectName^="toolBarFrame"],
|
||||
QFrame[objectName^="toolBarFrameTop"] {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8);
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 494 B |
Binary file not shown.
Before Width: | Height: | Size: 248 B |
Binary file not shown.
Before Width: | Height: | Size: 248 B |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue