1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

style: reformat current C++ codebase using clang-format

This commit is contained in:
Zetok Zalbavar 2017-02-26 11:52:45 +00:00
parent 4367dc601d
commit 80f5de31b3
No known key found for this signature in database
GPG Key ID: C953D3880212068A
259 changed files with 5914 additions and 7772 deletions

View File

@ -27,8 +27,8 @@
#include <QMutexLocker> #include <QMutexLocker>
#include <QPointer> #include <QPointer>
#include <QThread> #include <QThread>
#include <QtMath>
#include <QWaitCondition> #include <QWaitCondition>
#include <QtMath>
#include <cassert> #include <cassert>
@ -56,8 +56,8 @@ public:
static const ALchar* outDeviceNames() static const ALchar* outDeviceNames()
{ {
return (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) return (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
? alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) ? alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER)
: alcGetString(NULL, ALC_DEVICE_SPECIFIER); : alcGetString(NULL, ALC_DEVICE_SPECIFIER);
} }
qreal inputGain() const qreal inputGain() const
@ -77,12 +77,12 @@ public:
} }
public: public:
qreal minInputGain; qreal minInputGain;
qreal maxInputGain; qreal maxInputGain;
private: private:
qreal gain; qreal gain;
qreal gainFactor; qreal gainFactor;
}; };
/** /**
@ -102,7 +102,8 @@ private:
* @param s Name of the sound to get the path of. * @param s Name of the sound to get the path of.
* @return The path of the requested sound. * @return The path of the requested sound.
* *
* @fn void Audio::frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate); * @fn void Audio::frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels,
* uint32_t sampling_rate);
* *
* When there are input subscribers, we regularly emit captured audio frames with this signal * When there are input subscribers, we regularly emit captured audio frames with this signal
* Always connect with a blocking queued connection lambda, else the behaviour is undefined * Always connect with a blocking queued connection lambda, else the behaviour is undefined
@ -150,7 +151,7 @@ Audio::Audio()
moveToThread(audioThread); moveToThread(audioThread);
connect(&captureTimer, &QTimer::timeout, this, &Audio::doCapture); connect(&captureTimer, &QTimer::timeout, this, &Audio::doCapture);
captureTimer.setInterval(AUDIO_FRAME_DURATION/2); captureTimer.setInterval(AUDIO_FRAME_DURATION / 2);
captureTimer.setSingleShot(false); captureTimer.setSingleShot(false);
captureTimer.start(); captureTimer.start();
connect(&playMono16Timer, &QTimer::timeout, this, &Audio::playMono16SoundCleanup); connect(&playMono16Timer, &QTimer::timeout, this, &Audio::playMono16SoundCleanup);
@ -175,7 +176,7 @@ void Audio::checkAlError() noexcept
qWarning("OpenAL error: %d", al_err); qWarning("OpenAL error: %d", al_err);
} }
void Audio::checkAlcError(ALCdevice *device) noexcept void Audio::checkAlcError(ALCdevice* device) noexcept
{ {
const ALCenum alc_err = alcGetError(device); const ALCenum alc_err = alcGetError(device);
if (alc_err) if (alc_err)
@ -191,8 +192,7 @@ qreal Audio::outputVolume() const
ALfloat volume = 0.0; ALfloat volume = 0.0;
if (alOutDev) if (alOutDev) {
{
alGetListenerf(AL_GAIN, &volume); alGetListenerf(AL_GAIN, &volume);
checkAlError(); checkAlError();
} }
@ -302,8 +302,7 @@ void Audio::subscribeInput()
{ {
QMutexLocker locker(&audioLock); QMutexLocker locker(&audioLock);
if (!autoInitInput()) if (!autoInitInput()) {
{
qWarning("Failed to subscribe to audio input device."); qWarning("Failed to subscribe to audio input device.");
return; return;
} }
@ -325,7 +324,8 @@ void Audio::unsubscribeInput()
return; return;
inSubscriptions--; inSubscriptions--;
qDebug() << "Unsubscribed from audio input device [" << inSubscriptions << "subscriptions left ]"; qDebug() << "Unsubscribed from audio input device [" << inSubscriptions
<< "subscriptions left ]";
if (!inSubscriptions) if (!inSubscriptions)
cleanupInput(); cleanupInput();
@ -367,14 +367,11 @@ bool Audio::initInput(const QString& deviceName)
const ALCsizei bufSize = (frameDuration * sampleRate * 4) / 1000 * chnls; const ALCsizei bufSize = (frameDuration * sampleRate * 4) / 1000 * chnls;
const QByteArray qDevName = deviceName.toUtf8(); const QByteArray qDevName = deviceName.toUtf8();
const ALchar* tmpDevName = qDevName.isEmpty() const ALchar* tmpDevName = qDevName.isEmpty() ? nullptr : qDevName.constData();
? nullptr
: qDevName.constData();
alInDev = alcCaptureOpenDevice(tmpDevName, sampleRate, stereoFlag, bufSize); alInDev = alcCaptureOpenDevice(tmpDevName, sampleRate, stereoFlag, bufSize);
// Restart the capture if necessary // Restart the capture if necessary
if (!alInDev) if (!alInDev) {
{
qWarning() << "Failed to initialize audio input device:" << deviceName; qWarning() << "Failed to initialize audio input device:" << deviceName;
return false; return false;
} }
@ -402,13 +399,10 @@ bool Audio::initOutput(const QString& deviceName)
assert(!alOutDev); assert(!alOutDev);
const QByteArray qDevName = deviceName.toUtf8(); const QByteArray qDevName = deviceName.toUtf8();
const ALchar* tmpDevName = qDevName.isEmpty() const ALchar* tmpDevName = qDevName.isEmpty() ? nullptr : qDevName.constData();
? nullptr
: qDevName.constData();
alOutDev = alcOpenDevice(tmpDevName); alOutDev = alcOpenDevice(tmpDevName);
if (!alOutDev) if (!alOutDev) {
{
qWarning() << "Cannot open output audio device" << deviceName; qWarning() << "Cannot open output audio device" << deviceName;
return false; return false;
} }
@ -417,8 +411,7 @@ bool Audio::initOutput(const QString& deviceName)
alOutContext = alcCreateContext(alOutDev, nullptr); alOutContext = alcCreateContext(alOutDev, nullptr);
checkAlcError(alOutDev); checkAlcError(alOutDev);
if (!alcMakeContextCurrent(alOutContext)) if (!alcMakeContextCurrent(alOutContext)) {
{
qWarning() << "Cannot create output audio context"; qWarning() << "Cannot create output audio context";
return false; return false;
} }
@ -431,8 +424,7 @@ bool Audio::initOutput(const QString& deviceName)
checkAlError(); checkAlError();
Core* core = Core::getInstance(); Core* core = Core::getInstance();
if (core) if (core) {
{
// reset each call's audio source // reset each call's audio source
core->getAv()->invalidateCallSources(); core->getAv()->invalidateCallSources();
} }
@ -466,8 +458,7 @@ void Audio::playMono16Sound(const QByteArray& data)
ALint state; ALint state;
alGetSourcei(alMainSource, AL_SOURCE_STATE, &state); alGetSourcei(alMainSource, AL_SOURCE_STATE, &state);
if (state == AL_PLAYING) if (state == AL_PLAYING) {
{
alSourceStop(alMainSource); alSourceStop(alMainSource);
alSourcei(alMainSource, AL_BUFFER, AL_NONE); alSourcei(alMainSource, AL_BUFFER, AL_NONE);
} }
@ -480,7 +471,8 @@ void Audio::playMono16Sound(const QByteArray& data)
playMono16Timer.start(durationMs + 50); playMono16Timer.start(durationMs + 50);
} }
void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate) void Audio::playAudioBuffer(ALuint alSource, const int16_t* data, int samples, unsigned channels,
int sampleRate)
{ {
assert(channels == 1 || channels == 2); assert(channels == 1 || channels == 2);
QMutexLocker locker(&audioLock); QMutexLocker locker(&audioLock);
@ -494,24 +486,19 @@ void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, u
alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued); alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
alSourcei(alSource, AL_LOOPING, AL_FALSE); alSourcei(alSource, AL_LOOPING, AL_FALSE);
if (processed) if (processed) {
{
ALuint bufids[processed]; ALuint bufids[processed];
alSourceUnqueueBuffers(alSource, processed, bufids); alSourceUnqueueBuffers(alSource, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1); alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0]; bufid = bufids[0];
} } else if (queued < 16) {
else if (queued < 16)
{
alGenBuffers(1, &bufid); alGenBuffers(1, &bufid);
} } else {
else
{
return; return;
} }
alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data,
samples * 2 * channels, sampleRate); samples * 2 * channels, sampleRate);
alSourceQueueBuffers(alSource, 1, &bufid); alSourceQueueBuffers(alSource, 1, &bufid);
ALint state; ALint state;
@ -543,14 +530,12 @@ void Audio::cleanupOutput()
{ {
outputInitialized = false; outputInitialized = false;
if (alOutDev) if (alOutDev) {
{
alSourcei(alMainSource, AL_LOOPING, AL_FALSE); alSourcei(alMainSource, AL_LOOPING, AL_FALSE);
alSourceStop(alMainSource); alSourceStop(alMainSource);
alDeleteSources(1, &alMainSource); alDeleteSources(1, &alMainSource);
if (alMainBuffer) if (alMainBuffer) {
{
alDeleteBuffers(1, &alMainBuffer); alDeleteBuffers(1, &alMainBuffer);
alMainBuffer = 0; alMainBuffer = 0;
} }
@ -578,8 +563,7 @@ void Audio::playMono16SoundCleanup()
ALint state; ALint state;
alGetSourcei(alMainSource, AL_SOURCE_STATE, &state); alGetSourcei(alMainSource, AL_SOURCE_STATE, &state);
if (state == AL_STOPPED) if (state == AL_STOPPED) {
{
alSourcei(alMainSource, AL_BUFFER, AL_NONE); alSourcei(alMainSource, AL_BUFFER, AL_NONE);
alDeleteBuffers(1, &alMainBuffer); alDeleteBuffers(1, &alMainBuffer);
alMainBuffer = 0; alMainBuffer = 0;
@ -604,12 +588,11 @@ void Audio::doCapture()
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS]; int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS];
alcCaptureSamples(alInDev, buf, AUDIO_FRAME_SAMPLE_COUNT); alcCaptureSamples(alInDev, buf, AUDIO_FRAME_SAMPLE_COUNT);
for (quint32 i = 0; i < AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; ++i) for (quint32 i = 0; i < AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; ++i) {
{
// gain amplification with clipping to 16-bit boundaries // gain amplification with clipping to 16-bit boundaries
int ampPCM = qBound<int>(std::numeric_limits<int16_t>::min(), int ampPCM =
qRound(buf[i] * d->inputGainFactor()), qBound<int>(std::numeric_limits<int16_t>::min(), qRound(buf[i] * d->inputGainFactor()),
std::numeric_limits<int16_t>::max()); std::numeric_limits<int16_t>::max());
buf[i] = static_cast<int16_t>(ampPCM); buf[i] = static_cast<int16_t>(ampPCM);
} }
@ -631,13 +614,11 @@ QStringList Audio::outDeviceNames()
QStringList list; QStringList list;
const ALchar* pDeviceList = Private::outDeviceNames(); const ALchar* pDeviceList = Private::outDeviceNames();
if (pDeviceList) if (pDeviceList) {
{ while (*pDeviceList) {
while (*pDeviceList)
{
int len = static_cast<int>(strlen(pDeviceList)); int len = static_cast<int>(strlen(pDeviceList));
list << QString::fromUtf8(pDeviceList, len); list << QString::fromUtf8(pDeviceList, len);
pDeviceList += len+1; pDeviceList += len + 1;
} }
} }
@ -649,13 +630,11 @@ QStringList Audio::inDeviceNames()
QStringList list; QStringList list;
const ALchar* pDeviceList = Private::inDeviceNames(); const ALchar* pDeviceList = Private::inDeviceNames();
if (pDeviceList) if (pDeviceList) {
{ while (*pDeviceList) {
while (*pDeviceList)
{
int len = static_cast<int>(strlen(pDeviceList)); int len = static_cast<int>(strlen(pDeviceList));
list << QString::fromUtf8(pDeviceList, len); list << QString::fromUtf8(pDeviceList, len);
pDeviceList += len+1; pDeviceList += len + 1;
} }
} }
@ -666,14 +645,12 @@ void Audio::subscribeOutput(ALuint& sid)
{ {
QMutexLocker locker(&audioLock); QMutexLocker locker(&audioLock);
if (!autoInitOutput()) if (!autoInitOutput()) {
{
qWarning("Failed to subscribe to audio output device."); qWarning("Failed to subscribe to audio output device.");
return; return;
} }
if (!alcMakeContextCurrent(alOutContext)) if (!alcMakeContextCurrent(alOutContext)) {
{
qWarning("Failed to activate output context."); qWarning("Failed to activate output context.");
return; return;
} }
@ -682,23 +659,19 @@ void Audio::subscribeOutput(ALuint& sid)
assert(sid); assert(sid);
outSources << sid; outSources << sid;
qDebug() << "Audio source" << sid << "created. Sources active:" qDebug() << "Audio source" << sid << "created. Sources active:" << outSources.size();
<< outSources.size();
} }
void Audio::unsubscribeOutput(ALuint &sid) void Audio::unsubscribeOutput(ALuint& sid)
{ {
QMutexLocker locker(&audioLock); QMutexLocker locker(&audioLock);
outSources.removeAll(sid); outSources.removeAll(sid);
if (sid) if (sid) {
{ if (alIsSource(sid)) {
if (alIsSource(sid))
{
alDeleteSources(1, &sid); alDeleteSources(1, &sid);
qDebug() << "Audio source" << sid << "deleted. Sources active:" qDebug() << "Audio source" << sid << "deleted. Sources active:" << outSources.size();
<< outSources.size();
} else { } else {
qWarning() << "Trying to delete invalid audio source" << sid; qWarning() << "Trying to delete invalid audio source" << sid;
} }
@ -724,8 +697,7 @@ void Audio::stopLoop()
ALint state; ALint state;
alGetSourcei(alMainSource, AL_SOURCE_STATE, &state); alGetSourcei(alMainSource, AL_SOURCE_STATE, &state);
if (state == AL_STOPPED) if (state == AL_STOPPED) {
{
alSourcei(alMainSource, AL_BUFFER, AL_NONE); alSourcei(alMainSource, AL_BUFFER, AL_NONE);
alDeleteBuffers(1, &alMainBuffer); alDeleteBuffers(1, &alMainBuffer);
alMainBuffer = 0; alMainBuffer = 0;

View File

@ -24,18 +24,18 @@
#include <atomic> #include <atomic>
#include <cmath> #include <cmath>
#include <QObject>
#include <QMutex> #include <QMutex>
#include <QObject>
#include <QTimer> #include <QTimer>
#include <cassert> #include <cassert>
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
#include <OpenAL/al.h> #include <OpenAL/al.h>
#include <OpenAL/alc.h> #include <OpenAL/alc.h>
#else #else
#include <AL/al.h> #include <AL/al.h>
#include <AL/alc.h> #include <AL/alc.h>
#endif #endif
@ -51,12 +51,16 @@ class Audio : public QObject
class Private; class Private;
public: public:
enum class Sound
{
NewMessage,
Test,
IncomingCall
};
enum class Sound { NewMessage, Test, IncomingCall }; inline static QString getSound(Sound s)
{
inline static QString getSound(Sound s) { switch (s) {
switch (s)
{
case Sound::Test: case Sound::Test:
return QStringLiteral(":/audio/notification.pcm"); return QStringLiteral(":/audio/notification.pcm");
case Sound::NewMessage: case Sound::NewMessage:
@ -100,25 +104,26 @@ public:
void playMono16Sound(const QByteArray& data); void playMono16Sound(const QByteArray& data);
void playMono16Sound(const QString& path); void playMono16Sound(const QString& path);
void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, void playAudioBuffer(ALuint alSource, const int16_t* data, int samples, unsigned channels,
unsigned channels, int sampleRate); int sampleRate);
public: public:
// Public default audio settings // Public default audio settings
static constexpr uint32_t AUDIO_SAMPLE_RATE = 48000; static constexpr uint32_t AUDIO_SAMPLE_RATE = 48000;
static constexpr uint32_t AUDIO_FRAME_DURATION = 20; static constexpr uint32_t AUDIO_FRAME_DURATION = 20;
static constexpr ALint AUDIO_FRAME_SAMPLE_COUNT = AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE/1000; static constexpr ALint AUDIO_FRAME_SAMPLE_COUNT = AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE / 1000;
static constexpr uint32_t AUDIO_CHANNELS = 2; static constexpr uint32_t AUDIO_CHANNELS = 2;
signals: signals:
void frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate); void frameAvailable(const int16_t* pcm, size_t sample_count, uint8_t channels,
uint32_t sampling_rate);
private: private:
Audio(); Audio();
~Audio(); ~Audio();
static void checkAlError() noexcept; static void checkAlError() noexcept;
static void checkAlcError(ALCdevice *device) noexcept; static void checkAlcError(ALCdevice* device) noexcept;
bool autoInitInput(); bool autoInitInput();
bool autoInitOutput(); bool autoInitOutput();
@ -133,20 +138,20 @@ private:
Private* d; Private* d;
private: private:
QThread* audioThread; QThread* audioThread;
mutable QMutex audioLock; mutable QMutex audioLock;
ALCdevice* alInDev; ALCdevice* alInDev;
quint32 inSubscriptions; quint32 inSubscriptions;
QTimer captureTimer, playMono16Timer; QTimer captureTimer, playMono16Timer;
ALCdevice* alOutDev; ALCdevice* alOutDev;
ALCcontext* alOutContext; ALCcontext* alOutContext;
ALuint alMainSource; ALuint alMainSource;
ALuint alMainBuffer; ALuint alMainBuffer;
bool outputInitialized; bool outputInitialized;
QList<ALuint> outSources; QList<ALuint> outSources;
}; };
#endif // AUDIO_H #endif // AUDIO_H

View File

@ -25,13 +25,11 @@
ChatLine::ChatLine() ChatLine::ChatLine()
{ {
} }
ChatLine::~ChatLine() ChatLine::~ChatLine()
{ {
for (ChatLineContent* c : content) for (ChatLineContent* c : content) {
{
if (c->scene()) if (c->scene())
c->scene()->removeItem(c); c->scene()->removeItem(c);
@ -49,8 +47,7 @@ void ChatLine::setRow(int idx)
void ChatLine::visibilityChanged(bool visible) void ChatLine::visibilityChanged(bool visible)
{ {
if (isVisible != visible) if (isVisible != visible) {
{
for (ChatLineContent* c : content) for (ChatLineContent* c : content)
c->visibilityChanged(visible); c->visibilityChanged(visible);
} }
@ -63,7 +60,7 @@ int ChatLine::getRow() const
return row; return row;
} }
ChatLineContent *ChatLine::getContent(int col) const ChatLineContent* ChatLine::getContent(int col) const
{ {
if (col < static_cast<int>(content.size()) && col >= 0) if (col < static_cast<int>(content.size()) && col >= 0)
return content[col]; return content[col];
@ -71,10 +68,9 @@ ChatLineContent *ChatLine::getContent(int col) const
return nullptr; return nullptr;
} }
ChatLineContent *ChatLine::getContent(QPointF scenePos) const ChatLineContent* ChatLine::getContent(QPointF scenePos) const
{ {
for (ChatLineContent* c: content) for (ChatLineContent* c : content) {
{
if (c->sceneBoundingRect().contains(scenePos)) if (c->sceneBoundingRect().contains(scenePos))
return c; return c;
} }
@ -84,14 +80,13 @@ ChatLineContent *ChatLine::getContent(QPointF scenePos) const
void ChatLine::removeFromScene() void ChatLine::removeFromScene()
{ {
for (ChatLineContent* c : content) for (ChatLineContent* c : content) {
{
if (c->scene()) if (c->scene())
c->scene()->removeItem(c); c->scene()->removeItem(c);
} }
} }
void ChatLine::addToScene(QGraphicsScene *scene) void ChatLine::addToScene(QGraphicsScene* scene)
{ {
if (!scene) if (!scene)
return; return;
@ -135,7 +130,8 @@ void ChatLine::updateBBox()
bbox.setWidth(width); bbox.setWidth(width);
for (ChatLineContent* c : content) for (ChatLineContent* c : content)
bbox.setHeight(qMax(c->sceneBoundingRect().top() - bbox.top() + c->sceneBoundingRect().height(), bbox.height())); bbox.setHeight(qMax(c->sceneBoundingRect().top() - bbox.top() + c->sceneBoundingRect().height(),
bbox.height()));
} }
QRectF ChatLine::sceneBoundingRect() const QRectF ChatLine::sceneBoundingRect() const
@ -152,10 +148,9 @@ void ChatLine::addColumn(ChatLineContent* item, ColumnFormat fmt)
content.push_back(item); content.push_back(item);
} }
void ChatLine::replaceContent(int col, ChatLineContent *lineContent) void ChatLine::replaceContent(int col, ChatLineContent* lineContent)
{ {
if (col >= 0 && col < static_cast<int>(content.size()) && lineContent) if (col >= 0 && col < static_cast<int>(content.size()) && lineContent) {
{
QGraphicsScene* scene = content[col]->scene(); QGraphicsScene* scene = content[col]->scene();
delete content[col]; delete content[col];
@ -179,11 +174,10 @@ void ChatLine::layout(qreal w, QPointF scenePos)
width = w; width = w;
bbox.setTopLeft(scenePos); bbox.setTopLeft(scenePos);
qreal fixedWidth = (content.size()-1) * columnSpacing; qreal fixedWidth = (content.size() - 1) * columnSpacing;
qreal varWidth = 0.0; // used for normalisation qreal varWidth = 0.0; // used for normalisation
for (int i = 0; i < format.size(); ++i) for (int i = 0; i < format.size(); ++i) {
{
if (format[i].policy == ColumnFormat::FixedSize) if (format[i].policy == ColumnFormat::FixedSize)
fixedWidth += format[i].size; fixedWidth += format[i].size;
else else
@ -199,8 +193,7 @@ void ChatLine::layout(qreal w, QPointF scenePos)
qreal xOffset = 0.0; qreal xOffset = 0.0;
QVector<qreal> xPos(content.size()); QVector<qreal> xPos(content.size());
for (int i = 0; i < content.size(); ++i) for (int i = 0; i < content.size(); ++i) {
{
// calculate the effective width of the current column // calculate the effective width of the current column
qreal width; qreal width;
if (format[i].policy == ColumnFormat::FixedSize) if (format[i].policy == ColumnFormat::FixedSize)
@ -214,8 +207,7 @@ void ChatLine::layout(qreal w, QPointF scenePos)
// calculate horizontal alignment // calculate horizontal alignment
qreal xAlign = 0.0; qreal xAlign = 0.0;
switch(format[i].hAlign) switch (format[i].hAlign) {
{
case ColumnFormat::Left: case ColumnFormat::Left:
break; break;
case ColumnFormat::Right: case ColumnFormat::Right:
@ -233,8 +225,7 @@ void ChatLine::layout(qreal w, QPointF scenePos)
maxVOffset = qMax(maxVOffset, content[i]->getAscent()); maxVOffset = qMax(maxVOffset, content[i]->getAscent());
} }
for (int i = 0; i < content.size(); ++i) for (int i = 0; i < content.size(); ++i) {
{
// calculate vertical alignment // calculate vertical alignment
// vertical alignment may depend on width, so we do it in a second pass // vertical alignment may depend on width, so we do it in a second pass
qreal yOffset = maxVOffset - content[i]->getAscent(); qreal yOffset = maxVOffset - content[i]->getAscent();

View File

@ -20,10 +20,10 @@
#ifndef CHATLINE_H #ifndef CHATLINE_H
#define CHATLINE_H #define CHATLINE_H
#include <memory>
#include <QPointF> #include <QPointF>
#include <QRectF> #include <QRectF>
#include <QVector> #include <QVector>
#include <memory>
class ChatLog; class ChatLog;
class ChatLineContent; class ChatLineContent;
@ -33,23 +33,28 @@ class QFont;
struct ColumnFormat struct ColumnFormat
{ {
enum Policy { enum Policy
{
FixedSize, FixedSize,
VariableSize, VariableSize,
}; };
enum Align { enum Align
{
Left, Left,
Center, Center,
Right, Right,
}; };
ColumnFormat() {} ColumnFormat()
{
}
ColumnFormat(qreal s, Policy p, Align halign = Left) ColumnFormat(qreal s, Policy p, Align halign = Left)
: size(s) : size(s)
, policy(p) , policy(p)
, hAlign(halign) , hAlign(halign)
{} {
}
qreal size = 1.0; qreal size = 1.0;
Policy policy = VariableSize; Policy policy = VariableSize;
@ -86,7 +91,7 @@ public:
bool isOverSelection(QPointF scenePos); bool isOverSelection(QPointF scenePos);
//comparators // comparators
static bool lessThanBSRectTop(const ChatLine::Ptr& lhs, const qreal& rhs); static bool lessThanBSRectTop(const ChatLine::Ptr& lhs, const qreal& rhs);
static bool lessThanBSRectBottom(const ChatLine::Ptr& lhs, const qreal& rhs); static bool lessThanBSRectBottom(const ChatLine::Ptr& lhs, const qreal& rhs);
static bool lessThanRowIndex(const ChatLine::Ptr& lhs, const ChatLine::Ptr& rhs); static bool lessThanRowIndex(const ChatLine::Ptr& lhs, const ChatLine::Ptr& rhs);
@ -109,7 +114,6 @@ private:
qreal columnSpacing = 15.0; qreal columnSpacing = 15.0;
QRectF bbox; QRectF bbox;
bool isVisible = false; bool isVisible = false;
}; };
#endif // CHATLINE_H #endif // CHATLINE_H

View File

@ -42,27 +42,22 @@ int ChatLineContent::type() const
void ChatLineContent::selectionMouseMove(QPointF) void ChatLineContent::selectionMouseMove(QPointF)
{ {
} }
void ChatLineContent::selectionStarted(QPointF) void ChatLineContent::selectionStarted(QPointF)
{ {
} }
void ChatLineContent::selectionCleared() void ChatLineContent::selectionCleared()
{ {
} }
void ChatLineContent::selectionDoubleClick(QPointF) void ChatLineContent::selectionDoubleClick(QPointF)
{ {
} }
void ChatLineContent::selectionFocusChanged(bool) void ChatLineContent::selectionFocusChanged(bool)
{ {
} }
bool ChatLineContent::isOverSelection(QPointF) const bool ChatLineContent::isOverSelection(QPointF) const
@ -86,7 +81,6 @@ qreal ChatLineContent::getAscent() const
void ChatLineContent::visibilityChanged(bool) void ChatLineContent::visibilityChanged(bool)
{ {
} }
QString ChatLineContent::getText() const QString ChatLineContent::getText() const

View File

@ -19,10 +19,10 @@
#include "chatlinecontentproxy.h" #include "chatlinecontentproxy.h"
#include "src/chatlog/content/filetransferwidget.h" #include "src/chatlog/content/filetransferwidget.h"
#include <QLayout>
#include <QWidget>
#include <QPainter>
#include <QDebug> #include <QDebug>
#include <QLayout>
#include <QPainter>
#include <QWidget>
/** /**
* @enum ChatLineContentProxy::ChatLineContentProxyType * @enum ChatLineContentProxy::ChatLineContentProxyType
@ -32,7 +32,8 @@
* @value FileTransferWidgetType = 0 * @value FileTransferWidgetType = 0
*/ */
ChatLineContentProxy::ChatLineContentProxy(QWidget* widget, ChatLineContentProxyType type, int minWidth, float widthInPercent) ChatLineContentProxy::ChatLineContentProxy(QWidget* widget, ChatLineContentProxyType type,
int minWidth, float widthInPercent)
: widthPercent(widthInPercent) : widthPercent(widthInPercent)
, widthMin(minWidth) , widthMin(minWidth)
, widgetType{type} , widgetType{type}
@ -58,7 +59,8 @@ QRectF ChatLineContentProxy::boundingRect() const
return result; return result;
} }
void ChatLineContentProxy::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) void ChatLineContentProxy::paint(QPainter* painter, const QStyleOptionGraphicsItem* option,
QWidget* widget)
{ {
painter->setClipRect(boundingRect()); painter->setClipRect(boundingRect());
proxy->paint(painter, option, widget); proxy->paint(painter, option, widget);
@ -76,7 +78,7 @@ QWidget* ChatLineContentProxy::getWidget() const
void ChatLineContentProxy::setWidth(qreal width) void ChatLineContentProxy::setWidth(qreal width)
{ {
proxy->widget()->setFixedWidth(qMax(static_cast<int>(width*widthPercent), widthMin)); proxy->widget()->setFixedWidth(qMax(static_cast<int>(width * widthPercent), widthMin));
} }
ChatLineContentProxy::ChatLineContentProxyType ChatLineContentProxy::getWidgetType() const ChatLineContentProxy::ChatLineContentProxyType ChatLineContentProxy::getWidgetType() const

View File

@ -20,8 +20,8 @@
#ifndef CHATLINECONTENTPROXY_H #ifndef CHATLINECONTENTPROXY_H
#define CHATLINECONTENTPROXY_H #define CHATLINECONTENTPROXY_H
#include <QGraphicsProxyWidget>
#include "chatlinecontent.h" #include "chatlinecontent.h"
#include <QGraphicsProxyWidget>
class FileTransferWidget; class FileTransferWidget;
@ -33,7 +33,7 @@ public:
enum ChatLineContentProxyType enum ChatLineContentProxyType
{ {
GenericType, GenericType,
FileTransferWidgetType=0, FileTransferWidgetType = 0,
}; };
public: public:
@ -49,7 +49,8 @@ public:
ChatLineContentProxyType getWidgetType() const; ChatLineContentProxyType getWidgetType() const;
protected: protected:
ChatLineContentProxy(QWidget* widget, ChatLineContentProxyType type, int minWidth, float widthInPercent); ChatLineContentProxy(QWidget* widget, ChatLineContentProxyType type, int minWidth,
float widthInPercent);
private: private:
QGraphicsProxyWidget* proxy; QGraphicsProxyWidget* proxy;

View File

@ -18,27 +18,27 @@
*/ */
#include "chatlog.h" #include "chatlog.h"
#include "chatmessage.h"
#include "chatlinecontent.h" #include "chatlinecontent.h"
#include "chatlinecontentproxy.h" #include "chatlinecontentproxy.h"
#include "chatmessage.h"
#include "content/filetransferwidget.h" #include "content/filetransferwidget.h"
#include "src/widget/translator.h" #include "src/widget/translator.h"
#include <QDebug> #include <QAction>
#include <QScrollBar>
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
#include <QAction> #include <QDebug>
#include <QTimer>
#include <QMouseEvent> #include <QMouseEvent>
#include <QScrollBar>
#include <QShortcut> #include <QShortcut>
#include <QTimer>
/** /**
* @var ChatLog::repNameAfter * @var ChatLog::repNameAfter
* @brief repetition interval sender name (sec) * @brief repetition interval sender name (sec)
*/ */
template<class T> template <class T>
T clamp(T x, T min, T max) T clamp(T x, T min, T max)
{ {
if (x > max) if (x > max)
@ -68,7 +68,7 @@ ChatLog::ChatLog(QWidget* parent)
setBackgroundBrush(QBrush(Qt::white, Qt::SolidPattern)); setBackgroundBrush(QBrush(Qt::white, Qt::SolidPattern));
// The selection rect for multi-line selection // The selection rect for multi-line selection
selGraphItem = scene->addRect(0,0,0,0,selectionRectColor.darker(120),selectionRectColor); selGraphItem = scene->addRect(0, 0, 0, 0, selectionRectColor.darker(120), selectionRectColor);
selGraphItem->setZValue(-1.0); // behind all other items selGraphItem->setZValue(-1.0); // behind all other items
// copy action (ie. Ctrl+C) // copy action (ie. Ctrl+C)
@ -76,33 +76,24 @@ ChatLog::ChatLog(QWidget* parent)
copyAction->setIcon(QIcon::fromTheme("edit-copy")); copyAction->setIcon(QIcon::fromTheme("edit-copy"));
copyAction->setShortcut(QKeySequence::Copy); copyAction->setShortcut(QKeySequence::Copy);
copyAction->setEnabled(false); copyAction->setEnabled(false);
connect(copyAction, &QAction::triggered, this, [this]() connect(copyAction, &QAction::triggered, this, [this]() { copySelectedText(); });
{
copySelectedText();
});
addAction(copyAction); addAction(copyAction);
// Ctrl+Insert shortcut // Ctrl+Insert shortcut
QShortcut* copyCtrlInsShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Insert), this); QShortcut* copyCtrlInsShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Insert), this);
connect(copyCtrlInsShortcut, &QShortcut::activated, this, [this]() connect(copyCtrlInsShortcut, &QShortcut::activated, this, [this]() { copySelectedText(); });
{
copySelectedText();
});
// select all action (ie. Ctrl+A) // select all action (ie. Ctrl+A)
selectAllAction = new QAction(this); selectAllAction = new QAction(this);
selectAllAction->setIcon(QIcon::fromTheme("edit-select-all")); selectAllAction->setIcon(QIcon::fromTheme("edit-select-all"));
selectAllAction->setShortcut(QKeySequence::SelectAll); selectAllAction->setShortcut(QKeySequence::SelectAll);
connect(selectAllAction, &QAction::triggered, this, [this]() connect(selectAllAction, &QAction::triggered, this, [this]() { selectAll(); });
{
selectAll();
});
addAction(selectAllAction); addAction(selectAllAction);
// This timer is used to scroll the view while the user is // This timer is used to scroll the view while the user is
// moving the mouse past the top/bottom edge of the widget while selecting. // moving the mouse past the top/bottom edge of the widget while selecting.
selectionTimer = new QTimer(this); selectionTimer = new QTimer(this);
selectionTimer->setInterval(1000/30); selectionTimer->setInterval(1000 / 30);
selectionTimer->setSingleShot(false); selectionTimer->setSingleShot(false);
selectionTimer->start(); selectionTimer->start();
connect(selectionTimer, &QTimer::timeout, this, &ChatLog::onSelectionTimerTimeout); connect(selectionTimer, &QTimer::timeout, this, &ChatLog::onSelectionTimerTimeout);
@ -144,7 +135,7 @@ void ChatLog::clearSelection()
if (selectionMode == None) if (selectionMode == None)
return; return;
for (int i=selFirstRow; i<=selLastRow; ++i) for (int i = selFirstRow; i <= selLastRow; ++i)
lines[i]->selectionCleared(); lines[i]->selectionCleared();
selFirstRow = -1; selFirstRow = -1;
@ -183,8 +174,7 @@ void ChatLog::layout(int start, int end, qreal width)
start = clamp<int>(start, 0, lines.size()); start = clamp<int>(start, 0, lines.size());
end = clamp<int>(end + 1, 0, lines.size()); end = clamp<int>(end + 1, 0, lines.size());
for (int i = start; i < end; ++i) for (int i = start; i < end; ++i) {
{
ChatLine* l = lines[i].get(); ChatLine* l = lines[i].get();
l->layout(width, QPointF(0.0, h)); l->layout(width, QPointF(0.0, h));
@ -196,8 +186,7 @@ void ChatLog::mousePressEvent(QMouseEvent* ev)
{ {
QGraphicsView::mousePressEvent(ev); QGraphicsView::mousePressEvent(ev);
if (ev->button() == Qt::LeftButton) if (ev->button() == Qt::LeftButton) {
{
clickPos = ev->pos(); clickPos = ev->pos();
clearSelection(); clearSelection();
} }
@ -216,9 +205,8 @@ void ChatLog::mouseMoveEvent(QMouseEvent* ev)
QPointF scenePos = mapToScene(ev->pos()); QPointF scenePos = mapToScene(ev->pos());
if (ev->buttons() & Qt::LeftButton) if (ev->buttons() & Qt::LeftButton) {
{ // autoscroll
//autoscroll
if (ev->pos().y() < 0) if (ev->pos().y() < 0)
selectionScrollDir = Up; selectionScrollDir = Up;
else if (ev->pos().y() > height()) else if (ev->pos().y() > height())
@ -226,15 +214,14 @@ void ChatLog::mouseMoveEvent(QMouseEvent* ev)
else else
selectionScrollDir = NoDirection; selectionScrollDir = NoDirection;
//select // select
if (selectionMode == None && (clickPos - ev->pos()).manhattanLength() > QApplication::startDragDistance()) if (selectionMode == None
{ && (clickPos - ev->pos()).manhattanLength() > QApplication::startDragDistance()) {
QPointF sceneClickPos = mapToScene(clickPos.toPoint()); QPointF sceneClickPos = mapToScene(clickPos.toPoint());
ChatLine::Ptr line = findLineByPosY(scenePos.y()); ChatLine::Ptr line = findLineByPosY(scenePos.y());
ChatLineContent* content = getContentFromPos(sceneClickPos); ChatLineContent* content = getContentFromPos(sceneClickPos);
if (content) if (content) {
{
selClickedRow = content->getRow(); selClickedRow = content->getRow();
selClickedCol = content->getColumn(); selClickedCol = content->getColumn();
selFirstRow = content->getRow(); selFirstRow = content->getRow();
@ -247,9 +234,7 @@ void ChatLog::mouseMoveEvent(QMouseEvent* ev)
// ungrab mouse grabber // ungrab mouse grabber
if (scene->mouseGrabberItem()) if (scene->mouseGrabberItem())
scene->mouseGrabberItem()->ungrabMouse(); scene->mouseGrabberItem()->ungrabMouse();
} } else if (line.get()) {
else if (line.get())
{
selClickedRow = line->getRow(); selClickedRow = line->getRow();
selFirstRow = selClickedRow; selFirstRow = selClickedRow;
selLastRow = selClickedRow; selLastRow = selClickedRow;
@ -258,44 +243,34 @@ void ChatLog::mouseMoveEvent(QMouseEvent* ev)
} }
} }
if (selectionMode != None) if (selectionMode != None) {
{
ChatLineContent* content = getContentFromPos(scenePos); ChatLineContent* content = getContentFromPos(scenePos);
ChatLine::Ptr line = findLineByPosY(scenePos.y()); ChatLine::Ptr line = findLineByPosY(scenePos.y());
int row; int row;
if (content) if (content) {
{
row = content->getRow(); row = content->getRow();
int col = content->getColumn(); int col = content->getColumn();
if (row == selClickedRow && col == selClickedCol) if (row == selClickedRow && col == selClickedCol) {
{
selectionMode = Precise; selectionMode = Precise;
content->selectionMouseMove(scenePos); content->selectionMouseMove(scenePos);
selGraphItem->hide(); selGraphItem->hide();
} } else if (col != selClickedCol) {
else if (col != selClickedCol)
{
selectionMode = Multi; selectionMode = Multi;
lines[selClickedRow]->selectionCleared(); lines[selClickedRow]->selectionCleared();
} }
} } else if (line.get()) {
else if (line.get())
{
row = line->getRow(); row = line->getRow();
if (row != selClickedRow) if (row != selClickedRow) {
{
selectionMode = Multi; selectionMode = Multi;
lines[selClickedRow]->selectionCleared(); lines[selClickedRow]->selectionCleared();
} }
} } else {
else
{
return; return;
} }
@ -312,15 +287,16 @@ void ChatLog::mouseMoveEvent(QMouseEvent* ev)
} }
} }
//Much faster than QGraphicsScene::itemAt()! // Much faster than QGraphicsScene::itemAt()!
ChatLineContent* ChatLog::getContentFromPos(QPointF scenePos) const ChatLineContent* ChatLog::getContentFromPos(QPointF scenePos) const
{ {
if (lines.empty()) if (lines.empty())
return nullptr; return nullptr;
auto itr = std::lower_bound(lines.cbegin(), lines.cend(), scenePos.y(), ChatLine::lessThanBSRectBottom); auto itr =
std::lower_bound(lines.cbegin(), lines.cend(), scenePos.y(), ChatLine::lessThanBSRectBottom);
//find content // find content
if (itr != lines.cend() && (*itr)->sceneBoundingRect().contains(scenePos)) if (itr != lines.cend() && (*itr)->sceneBoundingRect().contains(scenePos))
return (*itr)->getContent(scenePos); return (*itr)->getContent(scenePos);
@ -329,15 +305,12 @@ ChatLineContent* ChatLog::getContentFromPos(QPointF scenePos) const
bool ChatLog::isOverSelection(QPointF scenePos) const bool ChatLog::isOverSelection(QPointF scenePos) const
{ {
if (selectionMode == Precise) if (selectionMode == Precise) {
{
ChatLineContent* content = getContentFromPos(scenePos); ChatLineContent* content = getContentFromPos(scenePos);
if (content) if (content)
return content->isOverSelection(scenePos); return content->isOverSelection(scenePos);
} } else if (selectionMode == Multi) {
else if (selectionMode == Multi)
{
if (selGraphItem->rect().contains(scenePos)) if (selGraphItem->rect().contains(scenePos))
return true; return true;
} }
@ -358,8 +331,7 @@ void ChatLog::reposition(int start, int end, qreal deltaY)
start = clamp<int>(start, 0, lines.size() - 1); start = clamp<int>(start, 0, lines.size() - 1);
end = clamp<int>(end + 1, 0, lines.size()); end = clamp<int>(end + 1, 0, lines.size());
for (int i = start; i < end; ++i) for (int i = start; i < end; ++i) {
{
ChatLine* l = lines[i].get(); ChatLine* l = lines[i].get();
l->moveBy(deltaY); l->moveBy(deltaY);
} }
@ -372,12 +344,12 @@ void ChatLog::insertChatlineAtBottom(ChatLine::Ptr l)
bool stickToBtm = stickToBottom(); bool stickToBtm = stickToBottom();
//insert // insert
l->setRow(lines.size()); l->setRow(lines.size());
l->addToScene(scene); l->addToScene(scene);
lines.append(l); lines.append(l);
//partial refresh // partial refresh
layout(lines.last()->getRow(), lines.size(), useableWidth()); layout(lines.last()->getRow(), lines.size(), useableWidth());
updateSceneRect(); updateSceneRect();
@ -410,8 +382,7 @@ void ChatLog::insertChatlineOnTop(const QList<ChatLine::Ptr>& newLines)
// add the new lines // add the new lines
int i = 0; int i = 0;
for (ChatLine::Ptr l : newLines) for (ChatLine::Ptr l : newLines) {
{
l->addToScene(scene); l->addToScene(scene);
l->visibilityChanged(false); l->visibilityChanged(false);
l->setRow(i++); l->setRow(i++);
@ -419,8 +390,7 @@ void ChatLog::insertChatlineOnTop(const QList<ChatLine::Ptr>& newLines)
} }
// add the old lines // add the old lines
for (ChatLine::Ptr l : lines) for (ChatLine::Ptr l : lines) {
{
l->setRow(i++); l->setRow(i++);
combLines.push_back(l); combLines.push_back(l);
} }
@ -450,8 +420,7 @@ void ChatLog::startResizeWorker()
return; return;
// (re)start the worker // (re)start the worker
if (!workerTimer->isActive()) if (!workerTimer->isActive()) {
{
// these values must not be reevaluated while the worker is running // these values must not be reevaluated while the worker is running
workerStb = stickToBottom(); workerStb = stickToBottom();
@ -462,8 +431,7 @@ void ChatLog::startResizeWorker()
// switch to busy scene displaying the busy notification if there is a lot // switch to busy scene displaying the busy notification if there is a lot
// of text to be resized // of text to be resized
int txt = 0; int txt = 0;
for (ChatLine::Ptr line : lines) for (ChatLine::Ptr line : lines) {
{
if (txt > 500000) if (txt > 500000)
break; break;
for (ChatLineContent* content : line->content) for (ChatLineContent* content : line->content)
@ -478,13 +446,12 @@ void ChatLog::startResizeWorker()
verticalScrollBar()->hide(); verticalScrollBar()->hide();
} }
void ChatLog::mouseDoubleClickEvent(QMouseEvent *ev) void ChatLog::mouseDoubleClickEvent(QMouseEvent* ev)
{ {
QPointF scenePos = mapToScene(ev->pos()); QPointF scenePos = mapToScene(ev->pos());
ChatLineContent* content = getContentFromPos(scenePos); ChatLineContent* content = getContentFromPos(scenePos);
if (content) if (content) {
{
content->selectionDoubleClick(scenePos); content->selectionDoubleClick(scenePos);
selClickedCol = content->getColumn(); selClickedCol = content->getColumn();
selClickedRow = content->getRow(); selClickedRow = content->getRow();
@ -498,25 +465,24 @@ void ChatLog::mouseDoubleClickEvent(QMouseEvent *ev)
QString ChatLog::getSelectedText() const QString ChatLog::getSelectedText() const
{ {
if (selectionMode == Precise) if (selectionMode == Precise) {
{
return lines[selClickedRow]->content[selClickedCol]->getSelectedText(); return lines[selClickedRow]->content[selClickedCol]->getSelectedText();
} } else if (selectionMode == Multi) {
else if (selectionMode == Multi)
{
// build a nicely formatted message // build a nicely formatted message
QString out; QString out;
for (int i=selFirstRow; i<=selLastRow; ++i) for (int i = selFirstRow; i <= selLastRow; ++i) {
{
if (lines[i]->content[1]->getText().isEmpty()) if (lines[i]->content[1]->getText().isEmpty())
continue; continue;
QString timestamp = lines[i]->content[2]->getText().isEmpty() ? tr("pending") : lines[i]->content[2]->getText(); QString timestamp = lines[i]->content[2]->getText().isEmpty()
? tr("pending")
: lines[i]->content[2]->getText();
QString author = lines[i]->content[0]->getText(); QString author = lines[i]->content[0]->getText();
QString msg = lines[i]->content[1]->getText(); QString msg = lines[i]->content[1]->getText();
out += QString(out.isEmpty() ? "[%2] %1: %3" : "\n[%2] %1: %3").arg(author, timestamp, msg); out +=
QString(out.isEmpty() ? "[%2] %1: %3" : "\n[%2] %1: %3").arg(author, timestamp, msg);
} }
return out; return out;
@ -547,8 +513,7 @@ QVector<ChatLine::Ptr> ChatLog::getLines()
ChatLine::Ptr ChatLog::getLatestLine() const ChatLine::Ptr ChatLog::getLatestLine() const
{ {
if (!lines.empty()) if (!lines.empty()) {
{
return lines.last(); return lines.last();
} }
return nullptr; return nullptr;
@ -559,7 +524,7 @@ ChatLine::Ptr ChatLog::getLatestLine() const
* @param pos Position on screen in global coordinates * @param pos Position on screen in global coordinates
* @sa getContentFromPos() * @sa getContentFromPos()
*/ */
ChatLineContent *ChatLog::getContentFromGlobalPos(QPoint pos) const ChatLineContent* ChatLog::getContentFromGlobalPos(QPoint pos) const
{ {
return getContentFromPos(mapToScene(mapFromGlobal(pos))); return getContentFromPos(mapToScene(mapFromGlobal(pos)));
} }
@ -570,8 +535,7 @@ void ChatLog::clear()
QVector<ChatLine::Ptr> savedLines; QVector<ChatLine::Ptr> savedLines;
for (ChatLine::Ptr l : lines) for (ChatLine::Ptr l : lines) {
{
if (isActiveFileTransfer(l)) if (isActiveFileTransfer(l))
savedLines.push_back(l); savedLines.push_back(l);
else else
@ -616,8 +580,7 @@ void ChatLog::setTypingNotification(ChatLine::Ptr notification)
void ChatLog::setTypingNotificationVisible(bool visible) void ChatLog::setTypingNotificationVisible(bool visible)
{ {
if (typingNotification.get()) if (typingNotification.get()) {
{
typingNotification->setVisible(visible); typingNotification->setVisible(visible);
updateTypingNotification(); updateTypingNotification();
} }
@ -641,7 +604,7 @@ void ChatLog::selectAll()
selectionMode = Multi; selectionMode = Multi;
selFirstRow = 0; selFirstRow = 0;
selLastRow = lines.size()-1; selLastRow = lines.size() - 1;
emit selectionChanged(); emit selectionChanged();
updateMultiSelectionRect(); updateMultiSelectionRect();
@ -649,8 +612,7 @@ void ChatLog::selectAll()
void ChatLog::fontChanged(const QFont& font) void ChatLog::fontChanged(const QFont& font)
{ {
for (ChatLine::Ptr l : lines) for (ChatLine::Ptr l : lines) {
{
l->fontChanged(font); l->fontChanged(font);
} }
} }
@ -666,15 +628,16 @@ void ChatLog::checkVisibility()
return; return;
// find first visible line // find first visible line
auto lowerBound = std::lower_bound(lines.cbegin(), lines.cend(), getVisibleRect().top(), ChatLine::lessThanBSRectBottom); auto lowerBound = std::lower_bound(lines.cbegin(), lines.cend(), getVisibleRect().top(),
ChatLine::lessThanBSRectBottom);
// find last visible line // find last visible line
auto upperBound = std::lower_bound(lowerBound, lines.cend(), getVisibleRect().bottom(), ChatLine::lessThanBSRectTop); auto upperBound = std::lower_bound(lowerBound, lines.cend(), getVisibleRect().bottom(),
ChatLine::lessThanBSRectTop);
// set visibilty // set visibilty
QList<ChatLine::Ptr> newVisibleLines; QList<ChatLine::Ptr> newVisibleLines;
for (auto itr = lowerBound; itr != upperBound; ++itr) for (auto itr = lowerBound; itr != upperBound; ++itr) {
{
newVisibleLines.append(*itr); newVisibleLines.append(*itr);
if (!visibleLines.contains(*itr)) if (!visibleLines.contains(*itr))
@ -692,8 +655,9 @@ void ChatLog::checkVisibility()
// enforce order // enforce order
std::sort(visibleLines.begin(), visibleLines.end(), ChatLine::lessThanRowIndex); std::sort(visibleLines.begin(), visibleLines.end(), ChatLine::lessThanRowIndex);
//if (!visibleLines.empty()) // if (!visibleLines.empty())
// qDebug() << "visible from " << visibleLines.first()->getRow() << "to " << visibleLines.last()->getRow() << " total " << visibleLines.size(); // qDebug() << "visible from " << visibleLines.first()->getRow() << "to " <<
// visibleLines.last()->getRow() << " total " << visibleLines.size();
} }
void ChatLog::scrollContentsBy(int dx, int dy) void ChatLog::scrollContentsBy(int dx, int dy)
@ -706,8 +670,7 @@ void ChatLog::resizeEvent(QResizeEvent* ev)
{ {
bool stb = stickToBottom(); bool stb = stickToBottom();
if (ev->size().width() != ev->oldSize().width()) if (ev->size().width() != ev->oldSize().width()) {
{
startResizeWorker(); startResizeWorker();
stb = false; // let the resize worker handle it stb = false; // let the resize worker handle it
} }
@ -722,8 +685,7 @@ void ChatLog::resizeEvent(QResizeEvent* ev)
void ChatLog::updateMultiSelectionRect() void ChatLog::updateMultiSelectionRect()
{ {
if (selectionMode == Multi && selFirstRow >= 0 && selLastRow >= 0) if (selectionMode == Multi && selFirstRow >= 0 && selLastRow >= 0) {
{
QRectF selBBox; QRectF selBBox;
selBBox = selBBox.united(lines[selFirstRow]->sceneBoundingRect()); selBBox = selBBox.united(lines[selFirstRow]->sceneBoundingRect());
selBBox = selBBox.united(lines[selLastRow]->sceneBoundingRect()); selBBox = selBBox.united(lines[selLastRow]->sceneBoundingRect());
@ -733,9 +695,7 @@ void ChatLog::updateMultiSelectionRect()
selGraphItem->setRect(selBBox); selGraphItem->setRect(selBBox);
selGraphItem->show(); selGraphItem->show();
} } else {
else
{
selGraphItem->hide(); selGraphItem->hide();
} }
} }
@ -756,10 +716,10 @@ void ChatLog::updateTypingNotification()
void ChatLog::updateBusyNotification() void ChatLog::updateBusyNotification()
{ {
if (busyNotification.get()) if (busyNotification.get()) {
{ // repoisition the busy notification (centered)
//repoisition the busy notification (centered) busyNotification->layout(useableWidth(), getVisibleRect().topLeft()
busyNotification->layout(useableWidth(), getVisibleRect().topLeft() + QPointF(0, getVisibleRect().height()/2.0)); + QPointF(0, getVisibleRect().height() / 2.0));
} }
} }
@ -780,15 +740,15 @@ QRectF ChatLog::calculateSceneRect() const
if (typingNotification.get() != nullptr) if (typingNotification.get() != nullptr)
bottom += typingNotification->sceneBoundingRect().height() + lineSpacing; bottom += typingNotification->sceneBoundingRect().height() + lineSpacing;
return QRectF(-margins.left(), -margins.top(), useableWidth(), bottom + margins.bottom() + margins.top()); return QRectF(-margins.left(), -margins.top(), useableWidth(),
bottom + margins.bottom() + margins.top());
} }
void ChatLog::onSelectionTimerTimeout() void ChatLog::onSelectionTimerTimeout()
{ {
const int scrollSpeed = 10; const int scrollSpeed = 10;
switch(selectionScrollDir) switch (selectionScrollDir) {
{
case Up: case Up:
verticalScrollBar()->setValue(verticalScrollBar()->value() - scrollSpeed); verticalScrollBar()->setValue(verticalScrollBar()->value() - scrollSpeed);
break; break;
@ -806,12 +766,11 @@ void ChatLog::onWorkerTimeout()
// large values will make the UI unresponsive // large values will make the UI unresponsive
const int stepSize = 50; const int stepSize = 50;
layout(workerLastIndex, workerLastIndex+stepSize, useableWidth()); layout(workerLastIndex, workerLastIndex + stepSize, useableWidth());
workerLastIndex += stepSize; workerLastIndex += stepSize;
// done? // done?
if (workerLastIndex >= lines.size()) if (workerLastIndex >= lines.size()) {
{
workerTimer->stop(); workerTimer->stop();
// switch back to the scene containing the chat messages // switch back to the scene containing the chat messages
@ -848,11 +807,10 @@ void ChatLog::focusInEvent(QFocusEvent* ev)
{ {
QGraphicsView::focusInEvent(ev); QGraphicsView::focusInEvent(ev);
if (selectionMode != None) if (selectionMode != None) {
{
selGraphItem->setBrush(QBrush(selectionRectColor)); selGraphItem->setBrush(QBrush(selectionRectColor));
for (int i=selFirstRow; i<=selLastRow; ++i) for (int i = selFirstRow; i <= selLastRow; ++i)
lines[i]->selectionFocusChanged(true); lines[i]->selectionFocusChanged(true);
} }
} }
@ -861,11 +819,10 @@ void ChatLog::focusOutEvent(QFocusEvent* ev)
{ {
QGraphicsView::focusOutEvent(ev); QGraphicsView::focusOutEvent(ev);
if (selectionMode != None) if (selectionMode != None) {
{
selGraphItem->setBrush(QBrush(selectionRectColor.lighter(120))); selGraphItem->setBrush(QBrush(selectionRectColor.lighter(120)));
for (int i=selFirstRow; i<=selLastRow; ++i) for (int i = selFirstRow; i <= selLastRow; ++i)
lines[i]->selectionFocusChanged(false); lines[i]->selectionFocusChanged(false);
} }
} }
@ -879,15 +836,14 @@ void ChatLog::retranslateUi()
bool ChatLog::isActiveFileTransfer(ChatLine::Ptr l) bool ChatLog::isActiveFileTransfer(ChatLine::Ptr l)
{ {
int count = l->getColumnCount(); int count = l->getColumnCount();
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i) {
{ ChatLineContent* content = l->getContent(i);
ChatLineContent *content = l->getContent(i); ChatLineContentProxy* proxy = qobject_cast<ChatLineContentProxy*>(content);
ChatLineContentProxy *proxy = qobject_cast<ChatLineContentProxy*>(content);
if (!proxy) if (!proxy)
continue; continue;
QWidget* widget = proxy->getWidget(); QWidget* widget = proxy->getWidget();
FileTransferWidget *transferWidget = qobject_cast<FileTransferWidget*>(widget); FileTransferWidget* transferWidget = qobject_cast<FileTransferWidget*>(widget);
if (transferWidget && transferWidget->isActive()) if (transferWidget && transferWidget->isActive())
return true; return true;
} }

View File

@ -20,8 +20,8 @@
#ifndef CHATLOG_H #ifndef CHATLOG_H
#define CHATLOG_H #define CHATLOG_H
#include <QGraphicsView>
#include <QDateTime> #include <QDateTime>
#include <QGraphicsView>
#include <QMargins> #include <QMargins>
#include "chatline.h" #include "chatline.h"
@ -63,7 +63,7 @@ public:
QVector<ChatLine::Ptr> getLines(); QVector<ChatLine::Ptr> getLines();
ChatLine::Ptr getLatestLine() const; ChatLine::Ptr getLatestLine() const;
ChatLineContent* getContentFromGlobalPos(QPoint pos) const; ChatLineContent* getContentFromGlobalPos(QPoint pos) const;
const uint repNameAfter = 5*60; const uint repNameAfter = 5 * 60;
signals: signals:
void selectionChanged(); void selectionChanged();
@ -113,13 +113,15 @@ private:
bool isActiveFileTransfer(ChatLine::Ptr l); bool isActiveFileTransfer(ChatLine::Ptr l);
private: private:
enum SelectionMode { enum SelectionMode
{
None, None,
Precise, Precise,
Multi, Multi,
}; };
enum AutoScrollDirection { enum AutoScrollDirection
{
NoDirection, NoDirection,
Up, Up,
Down, Down,
@ -135,7 +137,7 @@ private:
ChatLine::Ptr busyNotification; ChatLine::Ptr busyNotification;
// selection // selection
int selClickedRow = -1; //These 4 are only valid while selectionMode != None int selClickedRow = -1; // These 4 are only valid while selectionMode != None
int selClickedCol = -1; int selClickedCol = -1;
int selFirstRow = -1; int selFirstRow = -1;
int selLastRow = -1; int selLastRow = -1;
@ -147,13 +149,13 @@ private:
QTimer* workerTimer = nullptr; QTimer* workerTimer = nullptr;
AutoScrollDirection selectionScrollDir = NoDirection; AutoScrollDirection selectionScrollDir = NoDirection;
//worker vars // worker vars
int workerLastIndex = 0; int workerLastIndex = 0;
bool workerStb = false; bool workerStb = false;
ChatLine::Ptr workerAnchorLine; ChatLine::Ptr workerAnchorLine;
// layout // layout
QMargins margins = QMargins(10,10,10,10); QMargins margins = QMargins(10, 10, 10, 10);
qreal lineSpacing = 5.0f; qreal lineSpacing = 5.0f;
}; };

View File

@ -20,12 +20,12 @@
#include "chatmessage.h" #include "chatmessage.h"
#include "chatlinecontentproxy.h" #include "chatlinecontentproxy.h"
#include "textformatter.h" #include "textformatter.h"
#include "content/text.h"
#include "content/timestamp.h"
#include "content/spinner.h"
#include "content/filetransferwidget.h" #include "content/filetransferwidget.h"
#include "content/image.h" #include "content/image.h"
#include "content/notificationicon.h" #include "content/notificationicon.h"
#include "content/spinner.h"
#include "content/text.h"
#include "content/timestamp.h"
#include <QDebug> #include <QDebug>
@ -37,36 +37,35 @@
ChatMessage::ChatMessage() ChatMessage::ChatMessage()
{ {
} }
ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QString &rawMessage, MessageType type, bool isMe, const QDateTime &date) ChatMessage::Ptr ChatMessage::createChatMessage(const QString& sender, const QString& rawMessage,
MessageType type, bool isMe, const QDateTime& date)
{ {
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage); ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
QString text = rawMessage.toHtmlEscaped(); QString text = rawMessage.toHtmlEscaped();
QString senderText = sender; QString senderText = sender;
const QColor actionColor = QColor("#1818FF"); // has to match the color in innerStyle.css (div.action) const QColor actionColor =
QColor("#1818FF"); // has to match the color in innerStyle.css (div.action)
//smileys // smileys
if (Settings::getInstance().getUseEmoticons()) if (Settings::getInstance().getUseEmoticons())
text = SmileyPack::getInstance().smileyfied(text); text = SmileyPack::getInstance().smileyfied(text);
//quotes (green text) // quotes (green text)
text = detectQuotes(detectAnchors(text), type); text = detectQuotes(detectAnchors(text), type);
//text styling // text styling
Settings::StyleType styleType = Settings::getInstance().getStylePreference(); Settings::StyleType styleType = Settings::getInstance().getStylePreference();
if (styleType != Settings::StyleType::NONE) if (styleType != Settings::StyleType::NONE) {
{
TextFormatter tf = TextFormatter(text); TextFormatter tf = TextFormatter(text);
text = tf.applyStyling(styleType == Settings::StyleType::WITH_CHARS); text = tf.applyStyling(styleType == Settings::StyleType::WITH_CHARS);
} }
switch(type) switch (type) {
{
case NORMAL: case NORMAL:
text = wrapDiv(text, "msg"); text = wrapDiv(text, "msg");
break; break;
@ -86,9 +85,15 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
if (isMe) if (isMe)
authorFont.setBold(true); authorFont.setBold(true);
msg->addColumn(new Text(senderText, authorFont, true, sender, type == ACTION ? actionColor : Qt::black), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new Text(senderText, authorFont, true, sender,
msg->addColumn(new Text(text, baseFont, false, ((type == ACTION) && isMe) ? QString("%1 %2").arg(sender, rawMessage) : rawMessage), ColumnFormat(1.0, ColumnFormat::VariableSize)); type == ACTION ? actionColor : Qt::black),
msg->addColumn(new Spinner(":/ui/chatArea/spinner.svg", QSize(16, 16), 360.0/1.6), ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text(text, baseFont, false, ((type == ACTION) && isMe)
? QString("%1 %2").arg(sender, rawMessage)
: rawMessage),
ColumnFormat(1.0, ColumnFormat::VariableSize));
msg->addColumn(new Spinner(":/ui/chatArea/spinner.svg", QSize(16, 16), 360.0 / 1.6),
ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
if (!date.isNull()) if (!date.isNull())
msg->markAsSent(date); msg->markAsSent(date);
@ -96,29 +101,39 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
return msg; return msg;
} }
ChatMessage::Ptr ChatMessage::createChatInfoMessage(const QString &rawMessage, SystemMessageType type, const QDateTime &date) ChatMessage::Ptr ChatMessage::createChatInfoMessage(const QString& rawMessage,
SystemMessageType type, const QDateTime& date)
{ {
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage); ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
QString text = rawMessage.toHtmlEscaped(); QString text = rawMessage.toHtmlEscaped();
QString img; QString img;
switch(type) switch (type) {
{ case INFO:
case INFO: img = ":/ui/chatArea/info.svg"; break; img = ":/ui/chatArea/info.svg";
case ERROR: img = ":/ui/chatArea/error.svg"; break; break;
case TYPING: img = ":/ui/chatArea/typing.svg"; break; case ERROR:
img = ":/ui/chatArea/error.svg";
break;
case TYPING:
img = ":/ui/chatArea/typing.svg";
break;
} }
QFont baseFont = Settings::getInstance().getChatMessageFont(); QFont baseFont = Settings::getInstance().getChatMessageFont();
msg->addColumn(new Image(QSize(18, 18), img), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new Image(QSize(18, 18), img),
msg->addColumn(new Text("<b>" + text + "</b>", baseFont, false, ""), ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left)); ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont), ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new Text("<b>" + text + "</b>", baseFont, false, ""),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left));
msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
return msg; return msg;
} }
ChatMessage::Ptr ChatMessage::createFileTransferMessage(const QString& sender, ToxFile file, bool isMe, const QDateTime& date) ChatMessage::Ptr ChatMessage::createFileTransferMessage(const QString& sender, ToxFile file,
bool isMe, const QDateTime& date)
{ {
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage); ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
@ -127,9 +142,12 @@ ChatMessage::Ptr ChatMessage::createFileTransferMessage(const QString& sender, T
if (isMe) if (isMe)
authorFont.setBold(true); authorFont.setBold(true);
msg->addColumn(new Text(sender, authorFont, true), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new Text(sender, authorFont, true),
msg->addColumn(new ChatLineContentProxy(new FileTransferWidget(0, file), 320, 0.6f), ColumnFormat(1.0, ColumnFormat::VariableSize)); ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont), ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new ChatLineContentProxy(new FileTransferWidget(0, file), 320, 0.6f),
ColumnFormat(1.0, ColumnFormat::VariableSize));
msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
return msg; return msg;
} }
@ -140,14 +158,17 @@ ChatMessage::Ptr ChatMessage::createTypingNotification()
QFont baseFont = Settings::getInstance().getChatMessageFont(); QFont baseFont = Settings::getInstance().getChatMessageFont();
// Note: "[user]..." is just a placeholder. The actual text is set in ChatForm::setFriendTyping() // Note: "[user]..." is just a placeholder. The actual text is set in
// ChatForm::setFriendTyping()
// //
// FIXME: Due to circumstances, placeholder is being used in a case where // FIXME: Due to circumstances, placeholder is being used in a case where
// user received typing notifications constantly since contact came online. // user received typing notifications constantly since contact came online.
// This causes "[user]..." to be displayed in place of user nick, as long // This causes "[user]..." to be displayed in place of user nick, as long
// as user will keep typing. Issue #1280 // as user will keep typing. Issue #1280
msg->addColumn(new NotificationIcon(QSize(18, 18)), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new NotificationIcon(QSize(18, 18)),
msg->addColumn(new Text("[user]...", baseFont, false, ""), ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left)); ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text("[user]...", baseFont, false, ""),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left));
return msg; return msg;
} }
@ -159,12 +180,13 @@ ChatMessage::Ptr ChatMessage::createBusyNotification()
baseFont.setPixelSize(baseFont.pixelSize() + 2); baseFont.setPixelSize(baseFont.pixelSize() + 2);
baseFont.setBold(true); baseFont.setBold(true);
msg->addColumn(new Text(QObject::tr("Resizing"), baseFont, false, ""), ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Center)); msg->addColumn(new Text(QObject::tr("Resizing"), baseFont, false, ""),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Center));
return msg; return msg;
} }
void ChatMessage::markAsSent(const QDateTime &time) void ChatMessage::markAsSent(const QDateTime& time)
{ {
QFont baseFont = Settings::getInstance().getChatMessageFont(); QFont baseFont = Settings::getInstance().getChatMessageFont();
@ -205,30 +227,29 @@ void ChatMessage::hideDate()
c->hide(); c->hide();
} }
QString ChatMessage::detectAnchors(const QString &str) QString ChatMessage::detectAnchors(const QString& str)
{ {
QString out = str; QString out = str;
// detect URIs // detect URIs
QRegExp exp("(" QRegExp exp(
"(?:\\b)((www\\.)|(http[s]?|ftp)://)" // (protocol)://(printable - non-special character) "("
// http://ONEORMOREALHPA-DIGIT "(?:\\b)((www\\.)|(http[s]?|ftp)://)" // (protocol)://(printable - non-special character)
"\\w+\\S+)" // any other character, lets domains and other // http://ONEORMOREALHPA-DIGIT
// ↓ link to a file, or samba share "\\w+\\S+)" // any other character, lets domains and other
// https://en.wikipedia.org/wiki/File_URI_scheme // ↓ link to a file, or samba share
"|(?:\\b)((file|smb)://)([\\S| ]*)" // https://en.wikipedia.org/wiki/File_URI_scheme
"|(?:\\b)(tox:[a-zA-Z\\d]{76})" //link with full user address "|(?:\\b)((file|smb)://)([\\S| ]*)"
"|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)" //@mail link "|(?:\\b)(tox:[a-zA-Z\\d]{76})" // link with full user address
"|(?:\\b)(tox:\\S+@\\S+)"); // starts with `tox` then : and only alpha-digits till the end "|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)" //@mail link
// also accepts tox:agilob@net as simplified TOX ID "|(?:\\b)(tox:\\S+@\\S+)"); // starts with `tox` then : and only alpha-digits till the end
// also accepts tox:agilob@net as simplified TOX ID
int offset = 0; int offset = 0;
while ((offset = exp.indexIn(out, offset)) != -1) while ((offset = exp.indexIn(out, offset)) != -1) {
{
QString url = exp.cap(); QString url = exp.cap();
// If there's a trailing " it's a HTML attribute, e.g. a smiley img's title=":tox:" // If there's a trailing " it's a HTML attribute, e.g. a smiley img's title=":tox:"
if (url == "tox:\"") if (url == "tox:\"") {
{
offset += url.length(); offset += url.length();
continue; continue;
} }
@ -250,25 +271,20 @@ QString ChatMessage::detectQuotes(const QString& str, MessageType type)
// detect text quotes // detect text quotes
QStringList messageLines = str.split("\n"); QStringList messageLines = str.split("\n");
QString quotedText; QString quotedText;
for (int i = 0; i < messageLines.size(); ++i) for (int i = 0; i < messageLines.size(); ++i) {
{
// don't quote first line in action message. This makes co-existence of // don't quote first line in action message. This makes co-existence of
// quotes and action messages possible, since only first line can cause // quotes and action messages possible, since only first line can cause
// problems in case where there is quote in it used. // problems in case where there is quote in it used.
if (QRegExp("^(&gt;|).*").exactMatch(messageLines[i])) if (QRegExp("^(&gt;|).*").exactMatch(messageLines[i])) {
{
if (i > 0 || type != ACTION) if (i > 0 || type != ACTION)
quotedText += "<span class=quote>" + messageLines[i] + "</span>"; quotedText += "<span class=quote>" + messageLines[i] + "</span>";
else else
quotedText += messageLines[i]; quotedText += messageLines[i];
} } else {
else
{
quotedText += messageLines[i]; quotedText += messageLines[i];
} }
if (i < messageLines.size() - 1) if (i < messageLines.size() - 1) {
{
quotedText += '\n'; quotedText += '\n';
} }
} }
@ -276,7 +292,7 @@ QString ChatMessage::detectQuotes(const QString& str, MessageType type)
return quotedText; return quotedText;
} }
QString ChatMessage::wrapDiv(const QString &str, const QString &div) QString ChatMessage::wrapDiv(const QString& str, const QString& div)
{ {
return QString("<p class=%1>%2</p>").arg(div, /*QChar(0x200E) + */QString(str)); return QString("<p class=%1>%2</p>").arg(div, /*QChar(0x200E) + */ QString(str));
} }

View File

@ -47,9 +47,13 @@ public:
ChatMessage(); ChatMessage();
static ChatMessage::Ptr createChatMessage(const QString& sender, const QString& rawMessage, MessageType type, bool isMe, const QDateTime& date = QDateTime()); static ChatMessage::Ptr createChatMessage(const QString& sender, const QString& rawMessage,
static ChatMessage::Ptr createChatInfoMessage(const QString& rawMessage, SystemMessageType type, const QDateTime& date); MessageType type, bool isMe,
static ChatMessage::Ptr createFileTransferMessage(const QString& sender, ToxFile file, bool isMe, const QDateTime& date); const QDateTime& date = QDateTime());
static ChatMessage::Ptr createChatInfoMessage(const QString& rawMessage, SystemMessageType type,
const QDateTime& date);
static ChatMessage::Ptr createFileTransferMessage(const QString& sender, ToxFile file,
bool isMe, const QDateTime& date);
static ChatMessage::Ptr createTypingNotification(); static ChatMessage::Ptr createTypingNotification();
static ChatMessage::Ptr createBusyNotification(); static ChatMessage::Ptr createBusyNotification();

View File

@ -20,23 +20,23 @@
#include "filetransferwidget.h" #include "filetransferwidget.h"
#include "ui_filetransferwidget.h" #include "ui_filetransferwidget.h"
#include "src/nexus.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/nexus.h"
#include "src/persistence/settings.h"
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/widget/style.h" #include "src/widget/style.h"
#include "src/widget/widget.h" #include "src/widget/widget.h"
#include "src/persistence/settings.h"
#include <QMouseEvent>
#include <QFileDialog>
#include <QFile>
#include <QBuffer> #include <QBuffer>
#include <QMessageBox> #include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QVariantAnimation> #include <QVariantAnimation>
#include <QDebug>
#include <math.h> #include <math.h>
@ -63,10 +63,11 @@ FileTransferWidget::FileTransferWidget(QWidget* parent, ToxFile file)
backgroundColorAnimation = new QVariantAnimation(this); backgroundColorAnimation = new QVariantAnimation(this);
backgroundColorAnimation->setDuration(500); backgroundColorAnimation->setDuration(500);
backgroundColorAnimation->setEasingCurve(QEasingCurve::OutCubic); backgroundColorAnimation->setEasingCurve(QEasingCurve::OutCubic);
connect(backgroundColorAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& val) { connect(backgroundColorAnimation, &QVariantAnimation::valueChanged, this,
backgroundColor = val.value<QColor>(); [this](const QVariant& val) {
update(); backgroundColor = val.value<QColor>();
}); update();
});
buttonColorAnimation = new QVariantAnimation(this); buttonColorAnimation = new QVariantAnimation(this);
buttonColorAnimation->setDuration(500); buttonColorAnimation->setDuration(500);
@ -78,27 +79,32 @@ FileTransferWidget::FileTransferWidget(QWidget* parent, ToxFile file)
setBackgroundColor(Style::getColor(Style::LightGrey), false); setBackgroundColor(Style::getColor(Style::LightGrey), false);
connect(Core::getInstance(), &Core::fileTransferInfo, this, &FileTransferWidget::onFileTransferInfo); connect(Core::getInstance(), &Core::fileTransferInfo, this,
connect(Core::getInstance(), &Core::fileTransferAccepted, this, &FileTransferWidget::onFileTransferAccepted); &FileTransferWidget::onFileTransferInfo);
connect(Core::getInstance(), &Core::fileTransferCancelled, this, &FileTransferWidget::onFileTransferCancelled); connect(Core::getInstance(), &Core::fileTransferAccepted, this,
connect(Core::getInstance(), &Core::fileTransferPaused, this, &FileTransferWidget::onFileTransferPaused); &FileTransferWidget::onFileTransferAccepted);
connect(Core::getInstance(), &Core::fileTransferFinished, this, &FileTransferWidget::onFileTransferFinished); connect(Core::getInstance(), &Core::fileTransferCancelled, this,
connect(Core::getInstance(), &Core::fileTransferRemotePausedUnpaused, this, &FileTransferWidget::fileTransferRemotePausedUnpaused); &FileTransferWidget::onFileTransferCancelled);
connect(Core::getInstance(), &Core::fileTransferBrokenUnbroken, this, &FileTransferWidget::fileTransferBrokenUnbroken); connect(Core::getInstance(), &Core::fileTransferPaused, this,
&FileTransferWidget::onFileTransferPaused);
connect(Core::getInstance(), &Core::fileTransferFinished, this,
&FileTransferWidget::onFileTransferFinished);
connect(Core::getInstance(), &Core::fileTransferRemotePausedUnpaused, this,
&FileTransferWidget::fileTransferRemotePausedUnpaused);
connect(Core::getInstance(), &Core::fileTransferBrokenUnbroken, this,
&FileTransferWidget::fileTransferBrokenUnbroken);
connect(ui->topButton, &QPushButton::clicked, this, &FileTransferWidget::onTopButtonClicked); connect(ui->topButton, &QPushButton::clicked, this, &FileTransferWidget::onTopButtonClicked);
connect(ui->bottomButton, &QPushButton::clicked, this, &FileTransferWidget::onBottomButtonClicked); connect(ui->bottomButton, &QPushButton::clicked, this, &FileTransferWidget::onBottomButtonClicked);
connect(ui->previewButton, &QPushButton::clicked, this, &FileTransferWidget::onPreviewButtonClicked); connect(ui->previewButton, &QPushButton::clicked, this,
&FileTransferWidget::onPreviewButtonClicked);
setupButtons(); setupButtons();
//preview // preview
if (fileInfo.direction == ToxFile::SENDING) if (fileInfo.direction == ToxFile::SENDING) {
{
showPreview(fileInfo.filePath); showPreview(fileInfo.filePath);
ui->progressLabel->setText(tr("Waiting to send...", "file transfer widget")); ui->progressLabel->setText(tr("Waiting to send...", "file transfer widget"));
} } else {
else
{
ui->progressLabel->setText(tr("Accept to receive this file", "file transfer widget")); ui->progressLabel->setText(tr("Accept to receive this file", "file transfer widget"));
} }
@ -110,7 +116,7 @@ FileTransferWidget::~FileTransferWidget()
delete ui; delete ui;
} }
void FileTransferWidget::autoAcceptTransfer(const QString &path) void FileTransferWidget::autoAcceptTransfer(const QString& path)
{ {
QString filepath; QString filepath;
int number = 0; int number = 0;
@ -118,15 +124,16 @@ void FileTransferWidget::autoAcceptTransfer(const QString &path)
QString suffix = QFileInfo(fileInfo.fileName).completeSuffix(); QString suffix = QFileInfo(fileInfo.fileName).completeSuffix();
QString base = QFileInfo(fileInfo.fileName).baseName(); QString base = QFileInfo(fileInfo.fileName).baseName();
do do {
{ filepath = QString("%1/%2%3.%4")
filepath = QString("%1/%2%3.%4").arg(path, base, number > 0 ? QString(" (%1)").arg(QString::number(number)) : QString(), suffix); .arg(path, base,
number > 0 ? QString(" (%1)").arg(QString::number(number)) : QString(),
suffix);
++number; ++number;
} } while (QFileInfo(filepath).exists());
while (QFileInfo(filepath).exists());
//Do not automatically accept the file-transfer if the path is not writable. // Do not automatically accept the file-transfer if the path is not writable.
//The user can still accept it manually. // The user can still accept it manually.
if (Nexus::tryRemoveFile(filepath)) if (Nexus::tryRemoveFile(filepath))
Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath); Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath);
else else
@ -138,27 +145,27 @@ bool FileTransferWidget::isActive() const
return active; return active;
} }
void FileTransferWidget::acceptTransfer(const QString &filepath) void FileTransferWidget::acceptTransfer(const QString& filepath)
{ {
if (filepath.isEmpty()) if (filepath.isEmpty())
return; return;
//test if writable // test if writable
if (!Nexus::tryRemoveFile(filepath)) if (!Nexus::tryRemoveFile(filepath)) {
{
GUI::showWarning(tr("Location not writable", "Title of permissions popup"), GUI::showWarning(tr("Location not writable", "Title of permissions popup"),
tr("You do not have permission to write that location. Choose another, or cancel the save dialog.", "text of permissions popup")); tr("You do not have permission to write that location. Choose another, or "
"cancel the save dialog.",
"text of permissions popup"));
return; return;
} }
//everything ok! // everything ok!
Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath); Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath);
} }
void FileTransferWidget::setBackgroundColor(const QColor &c, bool whiteFont) void FileTransferWidget::setBackgroundColor(const QColor& c, bool whiteFont)
{ {
if (c != backgroundColor) if (c != backgroundColor) {
{
backgroundColorAnimation->setStartValue(backgroundColor); backgroundColorAnimation->setStartValue(backgroundColor);
backgroundColorAnimation->setEndValue(c); backgroundColorAnimation->setEndValue(c);
backgroundColorAnimation->start(); backgroundColorAnimation->start();
@ -172,10 +179,9 @@ void FileTransferWidget::setBackgroundColor(const QColor &c, bool whiteFont)
update(); update();
} }
void FileTransferWidget::setButtonColor(const QColor &c) void FileTransferWidget::setButtonColor(const QColor& c)
{ {
if (c != buttonColor) if (c != buttonColor) {
{
buttonColorAnimation->setStartValue(buttonColor); buttonColorAnimation->setStartValue(buttonColor);
buttonColorAnimation->setEndValue(c); buttonColorAnimation->setEndValue(c);
buttonColorAnimation->start(); buttonColorAnimation->start();
@ -184,11 +190,11 @@ void FileTransferWidget::setButtonColor(const QColor &c)
bool FileTransferWidget::drawButtonAreaNeeded() const bool FileTransferWidget::drawButtonAreaNeeded() const
{ {
return (ui->bottomButton->isVisible() || ui->topButton->isVisible()) && return (ui->bottomButton->isVisible() || ui->topButton->isVisible())
!(ui->topButton->isVisible() && ui->topButton->objectName() == "ok"); && !(ui->topButton->isVisible() && ui->topButton->objectName() == "ok");
} }
void FileTransferWidget::paintEvent(QPaintEvent *) void FileTransferWidget::paintEvent(QPaintEvent*)
{ {
// required by Hi-DPI support as border-image doesn't work. // required by Hi-DPI support as border-image doesn't work.
QPainter painter(this); QPainter painter(this);
@ -202,21 +208,22 @@ void FileTransferWidget::paintEvent(QPaintEvent *)
// draw background // draw background
if (drawButtonAreaNeeded()) if (drawButtonAreaNeeded())
painter.setClipRect(QRect(0, 0, width()-buttonFieldWidth, height())); painter.setClipRect(QRect(0, 0, width() - buttonFieldWidth, height()));
painter.setBrush(QBrush(backgroundColor)); painter.setBrush(QBrush(backgroundColor));
painter.drawRoundRect(geometry(), r * ratio, r); painter.drawRoundRect(geometry(), r * ratio, r);
if (drawButtonAreaNeeded()) if (drawButtonAreaNeeded()) {
{
// draw button background (top) // draw button background (top)
painter.setBrush(QBrush(buttonColor)); painter.setBrush(QBrush(buttonColor));
painter.setClipRect(QRect(width()-buttonFieldWidth+lineWidth,0,buttonFieldWidth,height()/2-ceil(lineWidth/2.0))); painter.setClipRect(QRect(width() - buttonFieldWidth + lineWidth, 0, buttonFieldWidth,
height() / 2 - ceil(lineWidth / 2.0)));
painter.drawRoundRect(geometry(), r * ratio, r); painter.drawRoundRect(geometry(), r * ratio, r);
// draw button background (bottom) // draw button background (bottom)
painter.setBrush(QBrush(buttonColor)); painter.setBrush(QBrush(buttonColor));
painter.setClipRect(QRect(width()-buttonFieldWidth+lineWidth,height()/2+lineWidth/2,buttonFieldWidth,height()/2)); painter.setClipRect(QRect(width() - buttonFieldWidth + lineWidth,
height() / 2 + lineWidth / 2, buttonFieldWidth, height() / 2));
painter.drawRoundRect(geometry(), r * ratio, r); painter.drawRoundRect(geometry(), r * ratio, r);
} }
} }
@ -224,15 +231,14 @@ void FileTransferWidget::paintEvent(QPaintEvent *)
void FileTransferWidget::onFileTransferInfo(ToxFile file) void FileTransferWidget::onFileTransferInfo(ToxFile file)
{ {
QTime now = QTime::currentTime(); QTime now = QTime::currentTime();
qint64 dt = lastTick.msecsTo(now); //ms qint64 dt = lastTick.msecsTo(now); // ms
if (fileInfo != file || dt < 1000) if (fileInfo != file || dt < 1000)
return; return;
fileInfo = file; fileInfo = file;
if (fileInfo.status == ToxFile::TRANSMITTING) if (fileInfo.status == ToxFile::TRANSMITTING) {
{
// update progress // update progress
qreal progress = static_cast<qreal>(file.bytesSent) / static_cast<qreal>(file.filesize); qreal progress = static_cast<qreal>(file.bytesSent) / static_cast<qreal>(file.filesize);
ui->progressBar->setValue(static_cast<int>(progress * 100.0)); ui->progressBar->setValue(static_cast<int>(progress * 100.0));
@ -241,9 +247,8 @@ void FileTransferWidget::onFileTransferInfo(ToxFile file)
qreal deltaSecs = dt / 1000.0; qreal deltaSecs = dt / 1000.0;
// (can't use ::abs or ::max on unsigned types substraction, they'd just overflow) // (can't use ::abs or ::max on unsigned types substraction, they'd just overflow)
quint64 deltaBytes = file.bytesSent > lastBytesSent quint64 deltaBytes = file.bytesSent > lastBytesSent ? file.bytesSent - lastBytesSent
? file.bytesSent - lastBytesSent : lastBytesSent - file.bytesSent;
: lastBytesSent - file.bytesSent;
qreal bytesPerSec = static_cast<int>(static_cast<qreal>(deltaBytes) / deltaSecs); qreal bytesPerSec = static_cast<int>(static_cast<qreal>(deltaBytes) / deltaSecs);
// calculate mean // calculate mean
@ -257,15 +262,12 @@ void FileTransferWidget::onFileTransferInfo(ToxFile file)
meanBytesPerSec /= static_cast<qreal>(TRANSFER_ROLLING_AVG_COUNT); meanBytesPerSec /= static_cast<qreal>(TRANSFER_ROLLING_AVG_COUNT);
// update UI // update UI
if (meanBytesPerSec > 0) if (meanBytesPerSec > 0) {
{
// ETA // ETA
QTime toGo = QTime(0,0).addSecs((file.filesize - file.bytesSent) / meanBytesPerSec); QTime toGo = QTime(0, 0).addSecs((file.filesize - file.bytesSent) / meanBytesPerSec);
QString format = toGo.hour() > 0 ? "hh:mm:ss" : "mm:ss"; QString format = toGo.hour() > 0 ? "hh:mm:ss" : "mm:ss";
ui->etaLabel->setText(toGo.toString(format)); ui->etaLabel->setText(toGo.toString(format));
} } else {
else
{
ui->etaLabel->setText(""); ui->etaLabel->setText("");
} }
@ -320,7 +322,7 @@ void FileTransferWidget::onFileTransferPaused(ToxFile file)
// reset mean // reset mean
meanIndex = 0; meanIndex = 0;
for (size_t i=0; i<TRANSFER_ROLLING_AVG_COUNT; ++i) for (size_t i = 0; i < TRANSFER_ROLLING_AVG_COUNT; ++i)
meanData[i] = 0.0; meanData[i] = 0.0;
setBackgroundColor(Style::getColor(Style::LightGrey), false); setBackgroundColor(Style::getColor(Style::LightGrey), false);
@ -340,7 +342,7 @@ void FileTransferWidget::onFileTransferResumed(ToxFile file)
// reset mean // reset mean
meanIndex = 0; meanIndex = 0;
for (size_t i=0; i<TRANSFER_ROLLING_AVG_COUNT; ++i) for (size_t i = 0; i < TRANSFER_ROLLING_AVG_COUNT; ++i)
meanData[i] = 0.0; meanData[i] = 0.0;
setBackgroundColor(Style::getColor(Style::LightGrey), false); setBackgroundColor(Style::getColor(Style::LightGrey), false);
@ -395,11 +397,11 @@ void FileTransferWidget::fileTransferBrokenUnbroken(ToxFile file, bool broken)
QString FileTransferWidget::getHumanReadableSize(qint64 size) QString FileTransferWidget::getHumanReadableSize(qint64 size)
{ {
static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"}; static const char* suffix[] = {"B", "kiB", "MiB", "GiB", "TiB"};
int exp = 0; int exp = 0;
if (size > 0) if (size > 0)
exp = std::min( (int) (log(size) / log(1024)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1)); exp = std::min((int)(log(size) / log(1024)), (int)(sizeof(suffix) / sizeof(suffix[0]) - 1));
return QString().setNum(size / pow(1024, exp), 'f', exp > 1 ? 2 : 0).append(suffix[exp]); return QString().setNum(size / pow(1024, exp), 'f', exp > 1 ? 2 : 0).append(suffix[exp]);
} }
@ -415,8 +417,7 @@ void FileTransferWidget::hideWidgets()
void FileTransferWidget::setupButtons() void FileTransferWidget::setupButtons()
{ {
switch(fileInfo.status) switch (fileInfo.status) {
{
case ToxFile::TRANSMITTING: case ToxFile::TRANSMITTING:
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg")); ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg"));
ui->topButton->setObjectName("pause"); ui->topButton->setObjectName("pause");
@ -447,14 +448,11 @@ void FileTransferWidget::setupButtons()
ui->bottomButton->setObjectName("cancel"); ui->bottomButton->setObjectName("cancel");
ui->bottomButton->setToolTip(tr("Cancel transfer")); ui->bottomButton->setToolTip(tr("Cancel transfer"));
if (fileInfo.direction == ToxFile::SENDING) if (fileInfo.direction == ToxFile::SENDING) {
{
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg")); ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg"));
ui->topButton->setObjectName("pause"); ui->topButton->setObjectName("pause");
ui->topButton->setToolTip(tr("Pause transfer")); ui->topButton->setToolTip(tr("Pause transfer"));
} } else {
else
{
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg")); ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg"));
ui->topButton->setObjectName("accept"); ui->topButton->setObjectName("accept");
ui->topButton->setToolTip(tr("Accept transfer")); ui->topButton->setToolTip(tr("Accept transfer"));
@ -463,18 +461,16 @@ void FileTransferWidget::setupButtons()
} }
} }
void FileTransferWidget::handleButton(QPushButton *btn) void FileTransferWidget::handleButton(QPushButton* btn)
{ {
if (fileInfo.direction == ToxFile::SENDING) if (fileInfo.direction == ToxFile::SENDING) {
{
if (btn->objectName() == "cancel") if (btn->objectName() == "cancel")
Core::getInstance()->cancelFileSend(fileInfo.friendId, fileInfo.fileNum); Core::getInstance()->cancelFileSend(fileInfo.friendId, fileInfo.fileNum);
else if (btn->objectName() == "pause") else if (btn->objectName() == "pause")
Core::getInstance()->pauseResumeFileSend(fileInfo.friendId, fileInfo.fileNum); Core::getInstance()->pauseResumeFileSend(fileInfo.friendId, fileInfo.fileNum);
else if (btn->objectName() == "resume") else if (btn->objectName() == "resume")
Core::getInstance()->pauseResumeFileSend(fileInfo.friendId, fileInfo.fileNum); Core::getInstance()->pauseResumeFileSend(fileInfo.friendId, fileInfo.fileNum);
} } else // receiving or paused
else // receiving or paused
{ {
if (btn->objectName() == "cancel") if (btn->objectName() == "cancel")
Core::getInstance()->cancelFileRecv(fileInfo.friendId, fileInfo.fileNum); Core::getInstance()->cancelFileRecv(fileInfo.friendId, fileInfo.fileNum);
@ -482,37 +478,31 @@ void FileTransferWidget::handleButton(QPushButton *btn)
Core::getInstance()->pauseResumeFileRecv(fileInfo.friendId, fileInfo.fileNum); Core::getInstance()->pauseResumeFileRecv(fileInfo.friendId, fileInfo.fileNum);
else if (btn->objectName() == "resume") else if (btn->objectName() == "resume")
Core::getInstance()->pauseResumeFileRecv(fileInfo.friendId, fileInfo.fileNum); Core::getInstance()->pauseResumeFileRecv(fileInfo.friendId, fileInfo.fileNum);
else if (btn->objectName() == "accept") else if (btn->objectName() == "accept") {
{ QString path =
QString path = QFileDialog::getSaveFileName(parentWidget(), QFileDialog::getSaveFileName(parentWidget(),
tr("Save a file", "Title of the file saving dialog"), tr("Save a file", "Title of the file saving dialog"),
Settings::getInstance().getGlobalAutoAcceptDir() + "/" + fileInfo.fileName, Settings::getInstance().getGlobalAutoAcceptDir() + "/"
0, + fileInfo.fileName,
0, 0, 0, QFileDialog::DontUseNativeDialog);
QFileDialog::DontUseNativeDialog);
acceptTransfer(path); acceptTransfer(path);
} }
} }
if (btn->objectName() == "ok" || btn->objectName() == "previewButton") if (btn->objectName() == "ok" || btn->objectName() == "previewButton") {
{
Widget::confirmExecutableOpen(QFileInfo(fileInfo.filePath)); Widget::confirmExecutableOpen(QFileInfo(fileInfo.filePath));
} } else if (btn->objectName() == "dir") {
else if (btn->objectName() == "dir")
{
QString dirPath = QFileInfo(fileInfo.filePath).dir().path(); QString dirPath = QFileInfo(fileInfo.filePath).dir().path();
QDesktopServices::openUrl(QUrl::fromLocalFile(dirPath)); QDesktopServices::openUrl(QUrl::fromLocalFile(dirPath));
} }
} }
void FileTransferWidget::showPreview(const QString &filename) void FileTransferWidget::showPreview(const QString& filename)
{ {
static const QStringList previewExtensions = { "png", "jpeg", "jpg", "gif", "svg", static const QStringList previewExtensions = {"png", "jpeg", "jpg", "gif", "svg",
"PNG", "JPEG", "JPG", "GIF", "SVG" }; "PNG", "JPEG", "JPG", "GIF", "SVG"};
if (previewExtensions.contains(QFileInfo(filename).suffix())) if (previewExtensions.contains(QFileInfo(filename).suffix())) {
{
// Subtract to make border visible // Subtract to make border visible
const int size = qMax(ui->previewButton->width(), ui->previewButton->height()) - 4; const int size = qMax(ui->previewButton->width(), ui->previewButton->height()) - 4;
@ -524,15 +514,15 @@ void FileTransferWidget::showPreview(const QString &filename)
ui->previewButton->show(); ui->previewButton->show();
// Show mouseover preview, but make sure it's not larger than 50% of the screen width/height // Show mouseover preview, but make sure it's not larger than 50% of the screen width/height
const QRect desktopSize = QApplication::desktop()->screenGeometry(); const QRect desktopSize = QApplication::desktop()->screenGeometry();
const QImage previewImage = image.scaled(0.5 * desktopSize.width(), const QImage previewImage = image.scaled(0.5 * desktopSize.width(), 0.5 * desktopSize.height(),
0.5 * desktopSize.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
Qt::KeepAspectRatio, Qt::SmoothTransformation);
QByteArray imageData; QByteArray imageData;
QBuffer buffer(&imageData); QBuffer buffer(&imageData);
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
previewImage.save(&buffer, "PNG"); previewImage.save(&buffer, "PNG");
buffer.close(); buffer.close();
ui->previewButton->setToolTip("<img src=data:image/png;base64," + imageData.toBase64() + "/>"); ui->previewButton->setToolTip("<img src=data:image/png;base64," + imageData.toBase64()
+ "/>");
} }
} }
@ -551,19 +541,15 @@ void FileTransferWidget::onPreviewButtonClicked()
handleButton(ui->previewButton); handleButton(ui->previewButton);
} }
QPixmap FileTransferWidget::scaleCropIntoSquare(const QPixmap &source, const int targetSize) QPixmap FileTransferWidget::scaleCropIntoSquare(const QPixmap& source, const int targetSize)
{ {
QPixmap result; QPixmap result;
// Make sure smaller-than-icon images (at least one dimension is smaller) will not be upscaled // Make sure smaller-than-icon images (at least one dimension is smaller) will not be upscaled
if (source.width() < targetSize || source.height() < targetSize) if (source.width() < targetSize || source.height() < targetSize) {
{
result = source; result = source;
} } else {
else result = source.scaled(targetSize, targetSize, Qt::KeepAspectRatioByExpanding,
{
result = source.scaled(targetSize, targetSize,
Qt::KeepAspectRatioByExpanding,
Qt::SmoothTransformation); Qt::SmoothTransformation);
} }

View File

@ -20,8 +20,8 @@
#ifndef FILETRANSFERWIDGET_H #ifndef FILETRANSFERWIDGET_H
#define FILETRANSFERWIDGET_H #define FILETRANSFERWIDGET_H
#include <QWidget>
#include <QTime> #include <QTime>
#include <QWidget>
#include "src/chatlog/chatlinecontent.h" #include "src/chatlog/chatlinecontent.h"
#include "src/core/corestructs.h" #include "src/core/corestructs.h"
@ -74,10 +74,10 @@ private slots:
void onPreviewButtonClicked(); void onPreviewButtonClicked();
private: private:
static QPixmap scaleCropIntoSquare(const QPixmap &source, int targetSize); static QPixmap scaleCropIntoSquare(const QPixmap& source, int targetSize);
private: private:
Ui::FileTransferWidget *ui; Ui::FileTransferWidget* ui;
ToxFile fileInfo; ToxFile fileInfo;
QTime lastTick; QTime lastTick;
quint64 lastBytesSent = 0; quint64 lastBytesSent = 0;

View File

@ -27,17 +27,17 @@
class Image : public ChatLineContent class Image : public ChatLineContent
{ {
public: public:
Image(QSize size, const QString &filename); Image(QSize size, const QString& filename);
virtual QRectF boundingRect() const override; virtual QRectF boundingRect() const override;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option,
QWidget* widget) override;
virtual void setWidth(qreal width) override; virtual void setWidth(qreal width) override;
virtual qreal getAscent() const override; virtual qreal getAscent() const override;
private: private:
QSize size; QSize size;
QPixmap pmap; QPixmap pmap;
}; };
#endif // IMAGE_H #endif // IMAGE_H

View File

@ -20,9 +20,9 @@
#include "notificationicon.h" #include "notificationicon.h"
#include "../pixmapcache.h" #include "../pixmapcache.h"
#include <QGraphicsScene>
#include <QPainter> #include <QPainter>
#include <QTimer> #include <QTimer>
#include <QGraphicsScene>
NotificationIcon::NotificationIcon(QSize Size) NotificationIcon::NotificationIcon(QSize Size)
: size(Size) : size(Size)
@ -30,7 +30,7 @@ NotificationIcon::NotificationIcon(QSize Size)
pmap = PixmapCache::getInstance().get(":/ui/chatArea/typing.svg", size); pmap = PixmapCache::getInstance().get(":/ui/chatArea/typing.svg", size);
updateTimer = new QTimer(this); updateTimer = new QTimer(this);
updateTimer->setInterval(1000/30); updateTimer->setInterval(1000 / 30);
updateTimer->setSingleShot(false); updateTimer->setSingleShot(false);
updateTimer->start(); updateTimer->start();
@ -74,7 +74,7 @@ void NotificationIcon::updateGradient()
if (alpha + dotWidth >= 1.0) if (alpha + dotWidth >= 1.0)
alpha = 0.0; alpha = 0.0;
grad = QLinearGradient(QPointF(-0.5*size.width(),0), QPointF(3.0/2.0*size.width(),0)); grad = QLinearGradient(QPointF(-0.5 * size.width(), 0), QPointF(3.0 / 2.0 * size.width(), 0));
grad.setColorAt(0, Qt::lightGray); grad.setColorAt(0, Qt::lightGray);
grad.setColorAt(qMax(0.0, alpha - dotWidth), Qt::lightGray); grad.setColorAt(qMax(0.0, alpha - dotWidth), Qt::lightGray);
grad.setColorAt(alpha, Qt::black); grad.setColorAt(alpha, Qt::black);

View File

@ -34,7 +34,8 @@ public:
explicit NotificationIcon(QSize size); explicit NotificationIcon(QSize size);
virtual QRectF boundingRect() const override; virtual QRectF boundingRect() const override;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option,
QWidget* widget) override;
virtual void setWidth(qreal width) override; virtual void setWidth(qreal width) override;
virtual qreal getAscent() const override; virtual qreal getAscent() const override;
@ -49,7 +50,6 @@ private:
qreal dotWidth = 0.2; qreal dotWidth = 0.2;
qreal alpha = 0.0; qreal alpha = 0.0;
}; };
#endif // NOTIFICATIONICON_H #endif // NOTIFICATIONICON_H

View File

@ -20,19 +20,19 @@
#include "spinner.h" #include "spinner.h"
#include "../pixmapcache.h" #include "../pixmapcache.h"
#include <QPainter> #include <QDebug>
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QPainter>
#include <QTime> #include <QTime>
#include <QVariantAnimation> #include <QVariantAnimation>
#include <QDebug>
Spinner::Spinner(const QString &img, QSize Size, qreal speed) Spinner::Spinner(const QString& img, QSize Size, qreal speed)
: size(Size) : size(Size)
, rotSpeed(speed) , rotSpeed(speed)
{ {
pmap = PixmapCache::getInstance().get(img, size); pmap = PixmapCache::getInstance().get(img, size);
timer.setInterval(1000/30); // 30Hz timer.setInterval(1000 / 30); // 30Hz
timer.setSingleShot(false); timer.setSingleShot(false);
blendAnimation = new QVariantAnimation(this); blendAnimation = new QVariantAnimation(this);
@ -41,7 +41,8 @@ Spinner::Spinner(const QString &img, QSize Size, qreal speed)
blendAnimation->setDuration(350); blendAnimation->setDuration(350);
blendAnimation->setEasingCurve(QEasingCurve::InCubic); blendAnimation->setEasingCurve(QEasingCurve::InCubic);
blendAnimation->start(QAbstractAnimation::DeleteWhenStopped); blendAnimation->start(QAbstractAnimation::DeleteWhenStopped);
connect(blendAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& val) { alpha = val.toDouble(); }); connect(blendAnimation, &QVariantAnimation::valueChanged, this,
[this](const QVariant& val) { alpha = val.toDouble(); });
QObject::connect(&timer, &QTimer::timeout, this, &Spinner::timeout); QObject::connect(&timer, &QTimer::timeout, this, &Spinner::timeout);
} }
@ -55,8 +56,9 @@ void Spinner::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, Q
{ {
painter->setClipRect(boundingRect()); painter->setClipRect(boundingRect());
QTransform trans = QTransform().rotate(QTime::currentTime().msecsSinceStartOfDay() / 1000.0 * rotSpeed) QTransform trans = QTransform()
.translate(-size.width()/2.0, -size.height()/2.0); .rotate(QTime::currentTime().msecsSinceStartOfDay() / 1000.0 * rotSpeed)
.translate(-size.width() / 2.0, -size.height() / 2.0);
painter->setOpacity(alpha); painter->setOpacity(alpha);
painter->setTransform(trans, true); painter->setTransform(trans, true);
painter->setRenderHint(QPainter::SmoothPixmapTransform); painter->setRenderHint(QPainter::SmoothPixmapTransform);

View File

@ -22,9 +22,9 @@
#include "../chatlinecontent.h" #include "../chatlinecontent.h"
#include <QTimer>
#include <QObject> #include <QObject>
#include <QPixmap> #include <QPixmap>
#include <QTimer>
class QVariantAnimation; class QVariantAnimation;
@ -35,7 +35,8 @@ public:
Spinner(const QString& img, QSize size, qreal speed); Spinner(const QString& img, QSize size, qreal speed);
virtual QRectF boundingRect() const override; virtual QRectF boundingRect() const override;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option,
QWidget* widget) override;
virtual void setWidth(qreal width) override; virtual void setWidth(qreal width) override;
virtual void visibilityChanged(bool visible) override; virtual void visibilityChanged(bool visible) override;
virtual qreal getAscent() const override; virtual qreal getAscent() const override;
@ -50,7 +51,6 @@ private:
QTimer timer; QTimer timer;
qreal alpha = 0.0; qreal alpha = 0.0;
QVariantAnimation* blendAnimation; QVariantAnimation* blendAnimation;
}; };
#endif // SPINNER_H #endif // SPINNER_H

View File

@ -20,20 +20,21 @@
#include "text.h" #include "text.h"
#include "../documentcache.h" #include "../documentcache.h"
#include <QFontMetrics>
#include <QPainter>
#include <QPalette>
#include <QDebug>
#include <QTextBlock>
#include <QAbstractTextDocumentLayout> #include <QAbstractTextDocumentLayout>
#include <QApplication> #include <QApplication>
#include <QGraphicsSceneMouseEvent> #include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
#include <QFontMetrics>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QPalette>
#include <QTextBlock>
#include <QTextFragment> #include <QTextFragment>
#include "src/widget/style.h" #include "src/widget/style.h"
Text::Text(const QString& txt, const QFont& font, bool enableElide, const QString &rwText, const QColor c) Text::Text(const QString& txt, const QFont& font, bool enableElide, const QString& rwText,
const QColor c)
: rawText(rwText) : rawText(rwText)
, elide(enableElide) , elide(enableElide)
, defFont(font) , defFont(font)
@ -71,8 +72,7 @@ void Text::selectionMouseMove(QPointF scenePos)
return; return;
int cur = cursorFromPos(scenePos); int cur = cursorFromPos(scenePos);
if (cur >= 0) if (cur >= 0) {
{
selectionEnd = cur; selectionEnd = cur;
selectedText = extractSanitizedText(getSelectionStart(), getSelectionEnd()); selectedText = extractSanitizedText(getSelectionStart(), getSelectionEnd());
} }
@ -83,8 +83,7 @@ void Text::selectionMouseMove(QPointF scenePos)
void Text::selectionStarted(QPointF scenePos) void Text::selectionStarted(QPointF scenePos)
{ {
int cur = cursorFromPos(scenePos); int cur = cursorFromPos(scenePos);
if (cur >= 0) if (cur >= 0) {
{
selectionEnd = cur; selectionEnd = cur;
selectionAnchor = cur; selectionAnchor = cur;
} }
@ -108,8 +107,7 @@ void Text::selectionDoubleClick(QPointF scenePos)
int cur = cursorFromPos(scenePos); int cur = cursorFromPos(scenePos);
if (cur >= 0) if (cur >= 0) {
{
QTextCursor cursor(doc); QTextCursor cursor(doc);
cursor.setPosition(cur); cursor.setPosition(cur);
cursor.select(QTextCursor::WordUnderCursor); cursor.select(QTextCursor::WordUnderCursor);
@ -167,8 +165,7 @@ void Text::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWid
QAbstractTextDocumentLayout::PaintContext ctx; QAbstractTextDocumentLayout::PaintContext ctx;
QAbstractTextDocumentLayout::Selection sel; QAbstractTextDocumentLayout::Selection sel;
if (hasSelection()) if (hasSelection()) {
{
sel.cursor = QTextCursor(doc); sel.cursor = QTextCursor(doc);
sel.cursor.setPosition(getSelectionStart()); sel.cursor.setPosition(getSelectionStart());
sel.cursor.setPosition(getSelectionEnd(), QTextCursor::KeepAnchor); sel.cursor.setPosition(getSelectionEnd(), QTextCursor::KeepAnchor);
@ -197,13 +194,13 @@ qreal Text::getAscent() const
return ascent; return ascent;
} }
void Text::mousePressEvent(QGraphicsSceneMouseEvent *event) void Text::mousePressEvent(QGraphicsSceneMouseEvent* event)
{ {
if (event->button() == Qt::LeftButton) if (event->button() == Qt::LeftButton)
event->accept(); // grabber event->accept(); // grabber
} }
void Text::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) void Text::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{ {
if (!doc) if (!doc)
return; return;
@ -215,7 +212,7 @@ void Text::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
QDesktopServices::openUrl(anchor); QDesktopServices::openUrl(anchor);
} }
void Text::hoverMoveEvent(QGraphicsSceneHoverEvent *event) void Text::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{ {
if (!doc) if (!doc)
return; return;
@ -250,25 +247,20 @@ QString Text::getLinkAt(QPointF scenePos) const
void Text::regenerate() void Text::regenerate()
{ {
if (!doc) if (!doc) {
{
doc = DocumentCache::getInstance().pop(); doc = DocumentCache::getInstance().pop();
dirty = true; dirty = true;
} }
if (dirty) if (dirty) {
{
doc->setDefaultFont(defFont); doc->setDefaultFont(defFont);
if (elide) if (elide) {
{
QFontMetrics metrics = QFontMetrics(defFont); QFontMetrics metrics = QFontMetrics(defFont);
QString elidedText = metrics.elidedText(text, Qt::ElideRight, qRound(width)); QString elidedText = metrics.elidedText(text, Qt::ElideRight, qRound(width));
doc->setPlainText(elidedText); doc->setPlainText(elidedText);
} } else {
else
{
doc->setDefaultStyleSheet(defStyleSheet); doc->setDefaultStyleSheet(defStyleSheet);
doc->setHtml(text); doc->setHtml(text);
} }
@ -318,7 +310,8 @@ QSizeF Text::idealSize()
int Text::cursorFromPos(QPointF scenePos, bool fuzzy) const int Text::cursorFromPos(QPointF scenePos, bool fuzzy) const
{ {
if (doc) if (doc)
return doc->documentLayout()->hitTest(mapFromScene(scenePos), fuzzy ? Qt::FuzzyHit : Qt::ExactHit); return doc->documentLayout()->hitTest(mapFromScene(scenePos),
fuzzy ? Qt::FuzzyHit : Qt::ExactHit);
return -1; return -1;
} }
@ -346,26 +339,22 @@ QString Text::extractSanitizedText(int from, int to) const
QString txt; QString txt;
QTextBlock block = doc->firstBlock(); QTextBlock block = doc->firstBlock();
for (QTextBlock::Iterator itr = block.begin(); itr!=block.end(); ++itr) for (QTextBlock::Iterator itr = block.begin(); itr != block.end(); ++itr) {
{ int pos =
int pos = itr.fragment().position(); //fragment position -> position of the first character in the fragment itr.fragment()
.position(); // fragment position -> position of the first character in the fragment
if (itr.fragment().charFormat().isImageFormat()) if (itr.fragment().charFormat().isImageFormat()) {
{
QTextImageFormat imgFmt = itr.fragment().charFormat().toImageFormat(); QTextImageFormat imgFmt = itr.fragment().charFormat().toImageFormat();
QString key = imgFmt.name(); //img key (eg. key::D for :D) QString key = imgFmt.name(); // img key (eg. key::D for :D)
QString rune = key.mid(4); QString rune = key.mid(4);
if (pos >= from && pos < to) if (pos >= from && pos < to) {
{
txt += rune; txt += rune;
++pos; ++pos;
} }
} } else {
else for (QChar c : itr.fragment().text()) {
{
for (QChar c : itr.fragment().text())
{
if (pos >= from && pos < to) if (pos >= from && pos < to)
txt += c; txt += c;
@ -379,10 +368,8 @@ QString Text::extractSanitizedText(int from, int to) const
QString Text::extractImgTooltip(int pos) const QString Text::extractImgTooltip(int pos) const
{ {
for (QTextBlock::Iterator itr = doc->firstBlock().begin(); itr!=doc->firstBlock().end(); ++itr) for (QTextBlock::Iterator itr = doc->firstBlock().begin(); itr != doc->firstBlock().end(); ++itr) {
{ if (itr.fragment().contains(pos) && itr.fragment().charFormat().isImageFormat()) {
if (itr.fragment().contains(pos) && itr.fragment().charFormat().isImageFormat())
{
QTextImageFormat imgFmt = itr.fragment().charFormat().toImageFormat(); QTextImageFormat imgFmt = itr.fragment().charFormat().toImageFormat();
return imgFmt.toolTip(); return imgFmt.toolTip();
} }

View File

@ -31,7 +31,8 @@ class Text : public ChatLineContent
Q_OBJECT Q_OBJECT
public: public:
Text(const QString& txt = "", const QFont& font = QFont(), bool enableElide = false, const QString& rawText = QString(), const QColor c = Qt::black); Text(const QString& txt = "", const QFont& font = QFont(), bool enableElide = false,
const QString& rawText = QString(), const QColor c = Qt::black);
virtual ~Text(); virtual ~Text();
void setText(const QString& txt); void setText(const QString& txt);
@ -53,8 +54,8 @@ public:
virtual void visibilityChanged(bool keepInMemory) final; virtual void visibilityChanged(bool keepInMemory) final;
virtual qreal getAscent() const final; virtual qreal getAscent() const final;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) final override; virtual void mousePressEvent(QGraphicsSceneMouseEvent* event) final override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) final override; virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) final override;
void hoverMoveEvent(QGraphicsSceneHoverEvent* event) final override; void hoverMoveEvent(QGraphicsSceneHoverEvent* event) final override;
virtual QString getText() const final; virtual QString getText() const final;
@ -90,7 +91,6 @@ private:
QFont defFont; QFont defFont;
QString defStyleSheet; QString defStyleSheet;
QColor color; QColor color;
}; };
#endif // TEXT_H #endif // TEXT_H

View File

@ -19,7 +19,7 @@
#include "timestamp.h" #include "timestamp.h"
Timestamp::Timestamp(const QDateTime &time, const QString &format, const QFont &font) Timestamp::Timestamp(const QDateTime& time, const QString& format, const QFont& font)
: Text(time.toString(format), font, false, time.toString(format)) : Text(time.toString(format), font, false, time.toString(format))
{ {
this->time = time; this->time = time;

View File

@ -20,8 +20,8 @@
#ifndef TIMESTAMP_H #ifndef TIMESTAMP_H
#define TIMESTAMP_H #define TIMESTAMP_H
#include <QDateTime>
#include "text.h" #include "text.h"
#include <QDateTime>
class Timestamp : public Text class Timestamp : public Text
{ {

View File

@ -22,22 +22,22 @@
#include "src/persistence/smileypack.h" #include "src/persistence/smileypack.h"
#include "src/widget/style.h" #include "src/widget/style.h"
#include <QIcon>
#include <QDebug> #include <QDebug>
#include <QIcon>
#include <QUrl> #include <QUrl>
CustomTextDocument::CustomTextDocument(QObject *parent) CustomTextDocument::CustomTextDocument(QObject* parent)
: QTextDocument(parent) : QTextDocument(parent)
{ {
setUndoRedoEnabled(false); setUndoRedoEnabled(false);
setUseDesignMetrics(false); setUseDesignMetrics(false);
} }
QVariant CustomTextDocument::loadResource(int type, const QUrl &name) QVariant CustomTextDocument::loadResource(int type, const QUrl& name)
{ {
if (type == QTextDocument::ImageResource && name.scheme() == "key") if (type == QTextDocument::ImageResource && name.scheme() == "key") {
{ QSize size = QSize(Settings::getInstance().getEmojiFontPointSize(),
QSize size = QSize(Settings::getInstance().getEmojiFontPointSize(),Settings::getInstance().getEmojiFontPointSize()); Settings::getInstance().getEmojiFontPointSize());
QString fileName = QUrl::fromPercentEncoding(name.toEncoded()).mid(4).toHtmlEscaped(); QString fileName = QUrl::fromPercentEncoding(name.toEncoded()).mid(4).toHtmlEscaped();
return SmileyPack::getInstance().getAsIcon(fileName).pixmap(size); return SmileyPack::getInstance().getAsIcon(fileName).pixmap(size);

View File

@ -26,10 +26,10 @@ class CustomTextDocument : public QTextDocument
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CustomTextDocument(QObject *parent = 0); explicit CustomTextDocument(QObject* parent = 0);
protected: protected:
virtual QVariant loadResource(int type, const QUrl &name); virtual QVariant loadResource(int type, const QUrl& name);
}; };
#endif // CUSTOMTEXTDOCUMENT_H #endif // CUSTOMTEXTDOCUMENT_H

View File

@ -34,10 +34,9 @@ QTextDocument* DocumentCache::pop()
return documents.pop(); return documents.pop();
} }
void DocumentCache::push(QTextDocument *doc) void DocumentCache::push(QTextDocument* doc)
{ {
if (doc) if (doc) {
{
doc->clear(); doc->clear();
documents.push(doc); documents.push(doc);
} }
@ -46,7 +45,7 @@ void DocumentCache::push(QTextDocument *doc)
/** /**
* @brief Returns the singleton instance. * @brief Returns the singleton instance.
*/ */
DocumentCache &DocumentCache::getInstance() DocumentCache& DocumentCache::getInstance()
{ {
static DocumentCache instance; static DocumentCache instance;
return instance; return instance;

View File

@ -19,12 +19,11 @@
#include "pixmapcache.h" #include "pixmapcache.h"
QPixmap PixmapCache::get(const QString &filename, QSize size) QPixmap PixmapCache::get(const QString& filename, QSize size)
{ {
auto itr = cache.find(filename); auto itr = cache.find(filename);
if (itr == cache.end()) if (itr == cache.end()) {
{
QIcon icon; QIcon icon;
icon.addFile(filename); icon.addFile(filename);
@ -38,9 +37,8 @@ QPixmap PixmapCache::get(const QString &filename, QSize size)
/** /**
* @brief Returns the singleton instance. * @brief Returns the singleton instance.
*/ */
PixmapCache &PixmapCache::getInstance() PixmapCache& PixmapCache::getInstance()
{ {
static PixmapCache instance; static PixmapCache instance;
return instance; return instance;
} }

View File

@ -20,9 +20,9 @@
#ifndef ICONCACHE_H #ifndef ICONCACHE_H
#define ICONCACHE_H #define ICONCACHE_H
#include <QHash>
#include <QIcon> #include <QIcon>
#include <QPixmap> #include <QPixmap>
#include <QHash>
class PixmapCache class PixmapCache
{ {
@ -31,7 +31,9 @@ public:
static PixmapCache& getInstance(); static PixmapCache& getInstance();
protected: protected:
PixmapCache() {} PixmapCache()
{
}
PixmapCache(PixmapCache&) = delete; PixmapCache(PixmapCache&) = delete;
PixmapCache& operator=(const PixmapCache&) = delete; PixmapCache& operator=(const PixmapCache&) = delete;

View File

@ -24,7 +24,8 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <QVector> #include <QVector>
enum TextStyle { enum TextStyle
{
BOLD = 0, BOLD = 0,
ITALIC, ITALIC,
UNDERLINE, UNDERLINE,
@ -49,31 +50,27 @@ static const QString MULTILINE_CODE = QStringLiteral("(?<=^|[^`])"
"(?=$|[^`])"); "(?=$|[^`])");
// Items in vector associated with TextStyle values respectively. Do NOT change this order // Items in vector associated with TextStyle values respectively. Do NOT change this order
static const QVector<QString> fontStylePatterns static const QVector<QString> fontStylePatterns{QStringLiteral("<b>%1</b>"),
{ QStringLiteral("<i>%1</i>"),
QStringLiteral("<b>%1</b>"), QStringLiteral("<u>%1</u>"),
QStringLiteral("<i>%1</i>"), QStringLiteral("<s>%1</s>"),
QStringLiteral("<u>%1</u>"), QStringLiteral(
QStringLiteral("<s>%1</s>"), "<font color=#595959><code>%1</code></font>")};
QStringLiteral("<font color=#595959><code>%1</code></font>")
};
// Unfortunately, can't use simple QMap because ordered applying of styles is required // Unfortunately, can't use simple QMap because ordered applying of styles is required
static const QVector<QPair<QRegularExpression, QString>> textPatternStyle static const QVector<QPair<QRegularExpression, QString>> textPatternStyle{
{ {QRegularExpression(COMMON_PATTERN.arg("*", "1")), fontStylePatterns[BOLD]},
{ QRegularExpression(COMMON_PATTERN.arg("*", "1")), fontStylePatterns[BOLD] }, {QRegularExpression(COMMON_PATTERN.arg("/", "1")), fontStylePatterns[ITALIC]},
{ QRegularExpression(COMMON_PATTERN.arg("/", "1")), fontStylePatterns[ITALIC] }, {QRegularExpression(COMMON_PATTERN.arg("_", "1")), fontStylePatterns[UNDERLINE]},
{ QRegularExpression(COMMON_PATTERN.arg("_", "1")), fontStylePatterns[UNDERLINE] }, {QRegularExpression(COMMON_PATTERN.arg("~", "1")), fontStylePatterns[STRIKE]},
{ QRegularExpression(COMMON_PATTERN.arg("~", "1")), fontStylePatterns[STRIKE] }, {QRegularExpression(COMMON_PATTERN.arg("`", "1")), fontStylePatterns[CODE]},
{ QRegularExpression(COMMON_PATTERN.arg("`", "1")), fontStylePatterns[CODE] }, {QRegularExpression(COMMON_PATTERN.arg("*", "2")), fontStylePatterns[BOLD]},
{ QRegularExpression(COMMON_PATTERN.arg("*", "2")), fontStylePatterns[BOLD] }, {QRegularExpression(COMMON_PATTERN.arg("/", "2")), fontStylePatterns[ITALIC]},
{ QRegularExpression(COMMON_PATTERN.arg("/", "2")), fontStylePatterns[ITALIC] }, {QRegularExpression(COMMON_PATTERN.arg("_", "2")), fontStylePatterns[UNDERLINE]},
{ QRegularExpression(COMMON_PATTERN.arg("_", "2")), fontStylePatterns[UNDERLINE] }, {QRegularExpression(COMMON_PATTERN.arg("~", "2")), fontStylePatterns[STRIKE]},
{ QRegularExpression(COMMON_PATTERN.arg("~", "2")), fontStylePatterns[STRIKE] }, {QRegularExpression(MULTILINE_CODE), fontStylePatterns[CODE]}};
{ QRegularExpression(MULTILINE_CODE), fontStylePatterns[CODE] }
};
TextFormatter::TextFormatter(const QString &str) TextFormatter::TextFormatter(const QString& str)
: sourceString(str) : sourceString(str)
{ {
} }
@ -88,8 +85,7 @@ static int patternSignsCount(const QString& str)
QChar escapeSign = str.at(0); QChar escapeSign = str.at(0);
int result = 0; int result = 0;
int length = str.length(); int length = str.length();
while (result < length && str[result] == escapeSign) while (result < length && str[result] == escapeSign) {
{
++result; ++result;
} }
return result; return result;
@ -108,34 +104,29 @@ static bool isTagIntersection(const QString& str)
int closingTagCount = 0; int closingTagCount = 0;
QRegularExpressionMatchIterator iter = TAG_PATTERN.globalMatch(str); QRegularExpressionMatchIterator iter = TAG_PATTERN.globalMatch(str);
while (iter.hasNext()) while (iter.hasNext()) {
{ iter.next().captured()[0] == '/' ? ++closingTagCount : ++openingTagCount;
iter.next().captured()[0] == '/'
? ++closingTagCount
: ++openingTagCount;
} }
return openingTagCount != closingTagCount; return openingTagCount != closingTagCount;
} }
/** /**
* @brief Applies styles to the font of text that was passed to the constructor * @brief Applies styles to the font of text that was passed to the constructor
* @param showFormattingSymbols True, if it is supposed to include formatting symbols into resulting string * @param showFormattingSymbols True, if it is supposed to include formatting symbols into resulting
* string
* @return Source text with styled font * @return Source text with styled font
*/ */
QString TextFormatter::applyHtmlFontStyling(bool showFormattingSymbols) QString TextFormatter::applyHtmlFontStyling(bool showFormattingSymbols)
{ {
QString out = sourceString; QString out = sourceString;
for (QPair<QRegularExpression, QString> pair : textPatternStyle) for (QPair<QRegularExpression, QString> pair : textPatternStyle) {
{
QRegularExpressionMatchIterator matchesIterator = pair.first.globalMatch(out); QRegularExpressionMatchIterator matchesIterator = pair.first.globalMatch(out);
int insertedTagSymbolsCount = 0; int insertedTagSymbolsCount = 0;
while (matchesIterator.hasNext()) while (matchesIterator.hasNext()) {
{
QRegularExpressionMatch match = matchesIterator.next(); QRegularExpressionMatch match = matchesIterator.next();
if (isTagIntersection(match.captured())) if (isTagIntersection(match.captured())) {
{
continue; continue;
} }
@ -159,7 +150,8 @@ QString TextFormatter::applyHtmlFontStyling(bool showFormattingSymbols)
/** /**
* @brief Applies all styling for the text * @brief Applies all styling for the text
* @param showFormattingSymbols True, if it is supposed to include formatting symbols into resulting string * @param showFormattingSymbols True, if it is supposed to include formatting symbols into resulting
* string
* @return Styled string * @return Styled string
*/ */
QString TextFormatter::applyStyling(bool showFormattingSymbols) QString TextFormatter::applyStyling(bool showFormattingSymbols)

View File

@ -25,7 +25,6 @@
class TextFormatter class TextFormatter
{ {
private: private:
QString sourceString; QString sourceString;
QString applyHtmlFontStyling(bool showFormattingSymbols); QString applyHtmlFontStyling(bool showFormattingSymbols);

File diff suppressed because it is too large Load Diff

View File

@ -21,19 +21,20 @@
#ifndef CORE_HPP #ifndef CORE_HPP
#define CORE_HPP #define CORE_HPP
#include <cstdint>
#include <QObject>
#include <QMutex> #include <QMutex>
#include <QObject>
#include <cstdint>
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
#include "corestructs.h"
#include "coredefines.h" #include "coredefines.h"
#include "corestructs.h"
#include "toxid.h" #include "toxid.h"
class Profile; class Profile;
template <typename T> class QList; template <typename T>
class QList;
class QTimer; class QTimer;
class QString; class QString;
class CString; class CString;
@ -54,7 +55,7 @@ public:
static const QString TOX_EXT; static const QString TOX_EXT;
static const QString CONFIG_FILE_NAME; static const QString CONFIG_FILE_NAME;
static QString sanitize(QString name); static QString sanitize(QString name);
static QList<CString> splitMessage(const QString &message, int maxLen); static QList<CString> splitMessage(const QString& message, int maxLen);
static QByteArray getSaltFromFile(QString filename); static QByteArray getSaltFromFile(QString filename);
@ -69,8 +70,8 @@ public:
QString getFriendUsername(uint32_t friendNumber) const; QString getFriendUsername(uint32_t friendNumber) const;
bool isFriendOnline(uint32_t friendId) const; bool isFriendOnline(uint32_t friendId) const;
bool hasFriendWithPublicKey(const ToxPk &publicKey) const; bool hasFriendWithPublicKey(const ToxPk& publicKey) const;
uint32_t joinGroupchat(int32_t friendId, uint8_t type, const uint8_t* pubkey,uint16_t length) const; uint32_t joinGroupchat(int32_t friendId, uint8_t type, const uint8_t* pubkey, uint16_t length) const;
void quitGroupChat(int groupId) const; void quitGroupChat(int groupId) const;
QString getUsername() const; QString getUsername() const;
@ -91,8 +92,8 @@ public slots:
QByteArray getToxSaveData(); QByteArray getToxSaveData();
void acceptFriendRequest(const ToxPk &friendPk); void acceptFriendRequest(const ToxPk& friendPk);
void requestFriendship(const ToxId &friendAddress, const QString& message); void requestFriendship(const ToxId& friendAddress, const QString& message);
void groupInviteFriend(uint32_t friendId, int groupId); void groupInviteFriend(uint32_t friendId, int groupId);
int createGroup(uint8_t type = TOX_CONFERENCE_TYPE_AV); int createGroup(uint8_t type = TOX_CONFERENCE_TYPE_AV);
@ -186,35 +187,28 @@ signals:
void fileSendFailed(uint32_t friendId, const QString& fname); void fileSendFailed(uint32_t friendId, const QString& fname);
private: private:
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage,
const uint8_t* cMessage, size_t cMessageSize,
void* core);
static void onFriendMessage(Tox* tox, uint32_t friendId,
TOX_MESSAGE_TYPE type, const uint8_t* cMessage,
size_t cMessageSize, void* core); size_t cMessageSize, void* core);
static void onFriendNameChange(Tox* tox, uint32_t friendId, static void onFriendMessage(Tox* tox, uint32_t friendId, TOX_MESSAGE_TYPE type,
const uint8_t* cName, size_t cNameSize, const uint8_t* cMessage, size_t cMessageSize, void* core);
void* core); static void onFriendNameChange(Tox* tox, uint32_t friendId, const uint8_t* cName,
static void onFriendTypingChange(Tox* tox, uint32_t friendId, bool isTyping, size_t cNameSize, void* core);
void* core); static void onFriendTypingChange(Tox* tox, uint32_t friendId, bool isTyping, void* core);
static void onStatusMessageChanged(Tox* tox, uint32_t friendId, static void onStatusMessageChanged(Tox* tox, uint32_t friendId, const uint8_t* cMessage,
const uint8_t* cMessage,
size_t cMessageSize, void* core); size_t cMessageSize, void* core);
static void onUserStatusChanged(Tox* tox, uint32_t friendId, static void onUserStatusChanged(Tox* tox, uint32_t friendId, TOX_USER_STATUS userstatus,
TOX_USER_STATUS userstatus, void* core); void* core);
static void onConnectionStatusChanged(Tox* tox, uint32_t friendId, static void onConnectionStatusChanged(Tox* tox, uint32_t friendId, TOX_CONNECTION status,
TOX_CONNECTION status, void* core); void* core);
static void onGroupInvite(Tox* tox, uint32_t friendId, TOX_CONFERENCE_TYPE type, static void onGroupInvite(Tox* tox, uint32_t friendId, TOX_CONFERENCE_TYPE type,
const uint8_t* data, size_t length, void* vCore); const uint8_t* data, size_t length, void* vCore);
static void onGroupMessage(Tox* tox, uint32_t groupId, uint32_t peerId, static void onGroupMessage(Tox* tox, uint32_t groupId, uint32_t peerId, TOX_MESSAGE_TYPE type,
TOX_MESSAGE_TYPE type, const uint8_t* cMessage, const uint8_t* cMessage, size_t length, void* vCore);
size_t length, void* vCore);
static void onGroupNamelistChange(Tox* tox, uint32_t groupId, uint32_t peerId, static void onGroupNamelistChange(Tox* tox, uint32_t groupId, uint32_t peerId,
TOX_CONFERENCE_STATE_CHANGE change, void* core); TOX_CONFERENCE_STATE_CHANGE change, void* core);
static void onGroupTitleChange(Tox* tox, uint32_t groupId, uint32_t peerId, static void onGroupTitleChange(Tox* tox, uint32_t groupId, uint32_t peerId,
const uint8_t* cTitle, size_t length, void* vCore); const uint8_t* cTitle, size_t length, void* vCore);
static void onReadReceiptCallback(Tox* tox, uint32_t friendId, static void onReadReceiptCallback(Tox* tox, uint32_t friendId, uint32_t receipt, void* core);
uint32_t receipt, void* core);
void sendGroupMessageWithType(int groupId, const QString& message, TOX_MESSAGE_TYPE type); void sendGroupMessageWithType(int groupId, const QString& message, TOX_MESSAGE_TYPE type);
bool parsePeerQueryError(TOX_ERR_CONFERENCE_PEER_QUERY error) const; bool parsePeerQueryError(TOX_ERR_CONFERENCE_PEER_QUERY error) const;
@ -235,17 +229,16 @@ private slots:
private: private:
Tox* tox; Tox* tox;
CoreAV* av; CoreAV* av;
QTimer *toxTimer; QTimer* toxTimer;
Profile& profile; Profile& profile;
QMutex messageSendMutex; QMutex messageSendMutex;
bool ready; bool ready;
static QThread *coreThread; static QThread* coreThread;
friend class Audio; ///< Audio can access our calls directly to reduce latency friend class Audio; ///< Audio can access our calls directly to reduce latency
friend class CoreFile; ///< CoreFile can access tox* and emit our signals friend class CoreFile; ///< CoreFile can access tox* and emit our signals
friend class CoreAV; ///< CoreAV accesses our toxav* for now friend class CoreAV; ///< CoreAV accesses our toxav* for now
}; };
#endif // CORE_HPP #endif // CORE_HPP

View File

@ -24,14 +24,14 @@
#include "src/friend.h" #include "src/friend.h"
#include "src/group.h" #include "src/group.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include "src/video/videoframe.h"
#include "src/video/corevideosource.h" #include "src/video/corevideosource.h"
#include <cassert> #include "src/video/videoframe.h"
#include <QCoreApplication>
#include <QDebug>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QDebug>
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#include <cassert>
/** /**
* @fn void CoreAV::avInvite(uint32_t friendId, bool video) * @fn void CoreAV::avInvite(uint32_t friendId, bool video)
@ -57,16 +57,19 @@
/** /**
* @var std::atomic_flag CoreAV::threadSwitchLock * @var std::atomic_flag CoreAV::threadSwitchLock
* @brief This flag is to be acquired before switching in a blocking way between the UI and CoreAV thread. * @brief This flag is to be acquired before switching in a blocking way between the UI and CoreAV
* thread.
* *
* The CoreAV thread must have priority for the flag, other threads should back off or release it quickly. * The CoreAV thread must have priority for the flag, other threads should back off or release it
* quickly.
* CoreAV needs to interface with three threads, the toxcore/Core thread that fires non-payload * CoreAV needs to interface with three threads, the toxcore/Core thread that fires non-payload
* toxav callbacks, the toxav/CoreAV thread that fires AV payload callbacks and manages * toxav callbacks, the toxav/CoreAV thread that fires AV payload callbacks and manages
* most of CoreAV's members, and the UI thread, which calls our [start/answer/cancel]Call functions * most of CoreAV's members, and the UI thread, which calls our [start/answer/cancel]Call functions
* and which we call via signals. * and which we call via signals.
* When the UI calls us, we switch from the UI thread to the CoreAV thread to do the processing, * When the UI calls us, we switch from the UI thread to the CoreAV thread to do the processing,
* when toxcore fires a non-payload av callback, we do the processing in the CoreAV thread and then * when toxcore fires a non-payload av callback, we do the processing in the CoreAV thread and then
* switch to the UI thread to send it a signal. Both switches block both threads, so this would deadlock. * switch to the UI thread to send it a signal. Both switches block both threads, so this would
* deadlock.
*/ */
/** /**
@ -79,9 +82,10 @@ IndexedList<ToxFriendCall> CoreAV::calls;
*/ */
IndexedList<ToxGroupCall> CoreAV::groupCalls; IndexedList<ToxGroupCall> CoreAV::groupCalls;
CoreAV::CoreAV(Tox *tox) CoreAV::CoreAV(Tox* tox)
: coreavThread{new QThread}, iterateTimer{new QTimer{this}}, : coreavThread{new QThread}
threadSwitchLock{false} , iterateTimer{new QTimer{this}}
, threadSwitchLock{false}
{ {
coreavThread->setObjectName("qTox CoreAV"); coreavThread->setObjectName("qTox CoreAV");
moveToThread(coreavThread.get()); moveToThread(coreavThread.get());
@ -107,14 +111,13 @@ CoreAV::~CoreAV()
killTimerFromThread(); killTimerFromThread();
toxav_kill(toxav); toxav_kill(toxav);
coreavThread->exit(0); coreavThread->exit(0);
while (coreavThread->isRunning()) while (coreavThread->isRunning()) {
{
qApp->processEvents(); qApp->processEvents();
coreavThread->wait(100); coreavThread->wait(100);
} }
} }
const ToxAV *CoreAV::getToxAv() const const ToxAV* CoreAV::getToxAv() const
{ {
return toxav; return toxav;
} }
@ -148,7 +151,8 @@ void CoreAV::killTimerFromThread()
{ {
// Timers can only be touched from their own thread // Timers can only be touched from their own thread
if (QThread::currentThread() != coreavThread.get()) if (QThread::currentThread() != coreavThread.get())
return (void)QMetaObject::invokeMethod(this, "killTimerFromThread", Qt::BlockingQueuedConnection); return (void)QMetaObject::invokeMethod(this, "killTimerFromThread",
Qt::BlockingQueuedConnection);
iterateTimer.release(); iterateTimer.release();
} }
@ -195,9 +199,7 @@ bool CoreAV::isCallStarted(const Group* g) const
*/ */
bool CoreAV::isCallActive(const Friend* f) const bool CoreAV::isCallActive(const Friend* f) const
{ {
return isCallStarted(f) return isCallStarted(f) ? !(calls[f->getFriendId()].inactive) : false;
? !(calls[f->getFriendId()].inactive)
: false;
} }
/** /**
@ -207,24 +209,18 @@ bool CoreAV::isCallActive(const Friend* f) const
*/ */
bool CoreAV::isCallActive(const Group* g) const bool CoreAV::isCallActive(const Group* g) const
{ {
return isCallStarted(g) return isCallStarted(g) ? !(groupCalls[g->getGroupId()].inactive) : false;
? !(groupCalls[g->getGroupId()].inactive)
: false;
} }
bool CoreAV::isCallVideoEnabled(const Friend* f) const bool CoreAV::isCallVideoEnabled(const Friend* f) const
{ {
return isCallStarted(f) return isCallStarted(f) ? calls[f->getFriendId()].videoEnabled : false;
? calls[f->getFriendId()].videoEnabled
: false;
} }
bool CoreAV::answerCall(uint32_t friendNum) bool CoreAV::answerCall(uint32_t friendNum)
{ {
if (QThread::currentThread() != coreavThread.get()) if (QThread::currentThread() != coreavThread.get()) {
{ if (threadSwitchLock.test_and_set(std::memory_order_acquire)) {
if (threadSwitchLock.test_and_set(std::memory_order_acquire))
{
qDebug() << "CoreAV::answerCall: Backed off of thread-switch lock"; qDebug() << "CoreAV::answerCall: Backed off of thread-switch lock";
return false; return false;
} }
@ -240,14 +236,11 @@ bool CoreAV::answerCall(uint32_t friendNum)
qDebug() << QString("answering call %1").arg(friendNum); qDebug() << QString("answering call %1").arg(friendNum);
assert(calls.contains(friendNum)); assert(calls.contains(friendNum));
TOXAV_ERR_ANSWER err; TOXAV_ERR_ANSWER err;
if (toxav_answer(toxav, friendNum, AUDIO_DEFAULT_BITRATE, VIDEO_DEFAULT_BITRATE, &err)) if (toxav_answer(toxav, friendNum, AUDIO_DEFAULT_BITRATE, VIDEO_DEFAULT_BITRATE, &err)) {
{
calls[friendNum].inactive = false; calls[friendNum].inactive = false;
return true; return true;
} } else {
else qWarning() << "Failed to answer call with error" << err;
{
qWarning() << "Failed to answer call with error"<<err;
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr); toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
calls.remove(friendNum); calls.remove(friendNum);
return false; return false;
@ -256,18 +249,15 @@ bool CoreAV::answerCall(uint32_t friendNum)
bool CoreAV::startCall(uint32_t friendNum, bool video) bool CoreAV::startCall(uint32_t friendNum, bool video)
{ {
if (QThread::currentThread() != coreavThread.get()) if (QThread::currentThread() != coreavThread.get()) {
{ if (threadSwitchLock.test_and_set(std::memory_order_acquire)) {
if (threadSwitchLock.test_and_set(std::memory_order_acquire))
{
qDebug() << "CoreAV::startCall: Backed off of thread-switch lock"; qDebug() << "CoreAV::startCall: Backed off of thread-switch lock";
return false; return false;
} }
bool ret; bool ret;
QMetaObject::invokeMethod(this, "startCall", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(this, "startCall", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, ret), Q_RETURN_ARG(bool, ret), Q_ARG(uint32_t, friendNum),
Q_ARG(uint32_t, friendNum),
Q_ARG(bool, video)); Q_ARG(bool, video));
threadSwitchLock.clear(std::memory_order_release); threadSwitchLock.clear(std::memory_order_release);
@ -275,8 +265,7 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
} }
qDebug() << QString("Starting call with %1").arg(friendNum); qDebug() << QString("Starting call with %1").arg(friendNum);
if (calls.contains(friendNum)) if (calls.contains(friendNum)) {
{
qWarning() << QString("Can't start call with %1, we're already in this call!").arg(friendNum); qWarning() << QString("Can't start call with %1, we're already in this call!").arg(friendNum);
return false; return false;
} }
@ -292,27 +281,22 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
bool CoreAV::cancelCall(uint32_t friendNum) bool CoreAV::cancelCall(uint32_t friendNum)
{ {
if (QThread::currentThread() != coreavThread.get()) if (QThread::currentThread() != coreavThread.get()) {
{ if (threadSwitchLock.test_and_set(std::memory_order_acquire)) {
if (threadSwitchLock.test_and_set(std::memory_order_acquire))
{
qDebug() << "CoreAV::cancelCall: Backed off of thread-switch lock"; qDebug() << "CoreAV::cancelCall: Backed off of thread-switch lock";
return false; return false;
} }
bool ret; bool ret;
QMetaObject::invokeMethod(this, "cancelCall", QMetaObject::invokeMethod(this, "cancelCall", Qt::BlockingQueuedConnection,
Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret), Q_ARG(uint32_t, friendNum));
Q_RETURN_ARG(bool, ret),
Q_ARG(uint32_t, friendNum));
threadSwitchLock.clear(std::memory_order_release); threadSwitchLock.clear(std::memory_order_release);
return ret; return ret;
} }
qDebug() << QString("Cancelling call with %1").arg(friendNum); qDebug() << QString("Cancelling call with %1").arg(friendNum);
if (!toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr)) if (!toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr)) {
{
qWarning() << QString("Failed to cancel call with %1").arg(friendNum); qWarning() << QString("Failed to cancel call with %1").arg(friendNum);
return false; return false;
} }
@ -326,19 +310,17 @@ void CoreAV::timeoutCall(uint32_t friendNum)
{ {
// Non-blocking switch to the CoreAV thread, we really don't want to be coming // Non-blocking switch to the CoreAV thread, we really don't want to be coming
// blocking-queued from the UI thread while we emit blocking-queued to it // blocking-queued from the UI thread while we emit blocking-queued to it
if (QThread::currentThread() != coreavThread.get()) if (QThread::currentThread() != coreavThread.get()) {
{
QMetaObject::invokeMethod(this, "timeoutCall", Qt::QueuedConnection, QMetaObject::invokeMethod(this, "timeoutCall", Qt::QueuedConnection,
Q_ARG(uint32_t, friendNum)); Q_ARG(uint32_t, friendNum));
return; return;
} }
if (!cancelCall(friendNum)) if (!cancelCall(friendNum)) {
{
qWarning() << QString("Failed to timeout call with %1").arg(friendNum); qWarning() << QString("Failed to timeout call with %1").arg(friendNum);
return; return;
} }
qDebug() << "Call with friend"<<friendNum<<"timed out"; qDebug() << "Call with friend" << friendNum << "timed out";
} }
/** /**
@ -350,16 +332,15 @@ void CoreAV::timeoutCall(uint32_t friendNum)
* @param rate Audio sampling rate used in this frame. * @param rate Audio sampling rate used in this frame.
* @return False only on error, but not if there's nothing to send. * @return False only on error, but not if there's nothing to send.
*/ */
bool CoreAV::sendCallAudio(uint32_t callId, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) bool CoreAV::sendCallAudio(uint32_t callId, const int16_t* pcm, size_t samples, uint8_t chans,
uint32_t rate)
{ {
if (!calls.contains(callId)) if (!calls.contains(callId))
return false; return false;
ToxFriendCall& call = calls[callId]; ToxFriendCall& call = calls[callId];
if (call.muteMic || call.inactive if (call.muteMic || call.inactive || !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)) {
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A))
{
return true; return true;
} }
@ -367,16 +348,12 @@ bool CoreAV::sendCallAudio(uint32_t callId, const int16_t *pcm, size_t samples,
TOXAV_ERR_SEND_FRAME err; TOXAV_ERR_SEND_FRAME err;
int retries = 0; int retries = 0;
do { do {
if (!toxav_audio_send_frame(toxav, callId, pcm, samples, chans, rate, &err)) if (!toxav_audio_send_frame(toxav, callId, pcm, samples, chans, rate, &err)) {
{ if (err == TOXAV_ERR_SEND_FRAME_SYNC) {
if (err == TOXAV_ERR_SEND_FRAME_SYNC)
{
++retries; ++retries;
QThread::usleep(500); QThread::usleep(500);
} } else {
else qDebug() << "toxav_audio_send_frame error: " << err;
{
qDebug() << "toxav_audio_send_frame error: "<<err;
} }
} }
} while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5); } while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5);
@ -395,21 +372,18 @@ void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
ToxFriendCall& call = calls[callId]; ToxFriendCall& call = calls[callId];
if (!call.videoEnabled || call.inactive if (!call.videoEnabled || call.inactive || !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V))
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V))
return; return;
if (call.nullVideoBitrate) if (call.nullVideoBitrate) {
{ qDebug() << "Restarting video stream to friend" << callId;
qDebug() << "Restarting video stream to friend"<<callId;
toxav_bit_rate_set(toxav, call.callId, -1, VIDEO_DEFAULT_BITRATE, nullptr); toxav_bit_rate_set(toxav, call.callId, -1, VIDEO_DEFAULT_BITRATE, nullptr);
call.nullVideoBitrate = false; call.nullVideoBitrate = false;
} }
ToxYUVFrame frame = vframe->toToxYUVFrame(); ToxYUVFrame frame = vframe->toToxYUVFrame();
if(!frame) if (!frame) {
{
return; return;
} }
@ -418,17 +392,13 @@ void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
TOXAV_ERR_SEND_FRAME err; TOXAV_ERR_SEND_FRAME err;
int retries = 0; int retries = 0;
do { do {
if (!toxav_video_send_frame(toxav, callId, frame.width, frame.height, if (!toxav_video_send_frame(toxav, callId, frame.width, frame.height, frame.y, frame.u,
frame.y, frame.u, frame.v, &err)) frame.v, &err)) {
{ if (err == TOXAV_ERR_SEND_FRAME_SYNC) {
if (err == TOXAV_ERR_SEND_FRAME_SYNC)
{
++retries; ++retries;
QThread::usleep(500); QThread::usleep(500);
} } else {
else qDebug() << "toxav_video_send_frame error: " << err;
{
qDebug() << "toxav_video_send_frame error: "<<err;
} }
} }
} while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5); } while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5);
@ -442,8 +412,7 @@ void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
*/ */
void CoreAV::toggleMuteCallInput(const Friend* f) void CoreAV::toggleMuteCallInput(const Friend* f)
{ {
if (f && calls.contains(f->getFriendId())) if (f && calls.contains(f->getFriendId())) {
{
ToxCall& call = calls[f->getFriendId()]; ToxCall& call = calls[f->getFriendId()];
call.muteMic = !call.muteMic; call.muteMic = !call.muteMic;
} }
@ -455,8 +424,7 @@ void CoreAV::toggleMuteCallInput(const Friend* f)
*/ */
void CoreAV::toggleMuteCallOutput(const Friend* f) void CoreAV::toggleMuteCallOutput(const Friend* f)
{ {
if (f && calls.contains(f->getFriendId())) if (f && calls.contains(f->getFriendId())) {
{
ToxCall& call = calls[f->getFriendId()]; ToxCall& call = calls[f->getFriendId()];
call.muteVol = !call.muteVol; call.muteVol = !call.muteVol;
} }
@ -474,18 +442,15 @@ void CoreAV::toggleMuteCallOutput(const Friend* f)
* @param[in] sample_rate the audio sample rate * @param[in] sample_rate the audio sample rate
* @param[in] core the qTox Core class * @param[in] core the qTox Core class
*/ */
void CoreAV::groupCallCallback(void* tox, int group, int peer, void CoreAV::groupCallCallback(void* tox, int group, int peer, const int16_t* data,
const int16_t* data, unsigned samples, unsigned samples, uint8_t channels, unsigned sample_rate, void* core)
uint8_t channels, unsigned sample_rate,
void* core)
{ {
Q_UNUSED(tox); Q_UNUSED(tox);
Core* c = static_cast<Core*>(core); Core* c = static_cast<Core*>(core);
CoreAV* cav = c->getAv(); CoreAV* cav = c->getAv();
if (!cav->groupCalls.contains(group)) if (!cav->groupCalls.contains(group)) {
{
return; return;
} }
@ -500,8 +465,7 @@ void CoreAV::groupCallCallback(void* tox, int group, int peer,
if (!call.peers[peer]) if (!call.peers[peer])
audio.subscribeOutput(call.peers[peer]); audio.subscribeOutput(call.peers[peer]);
audio.playAudioBuffer(call.peers[peer], data, samples, channels, audio.playAudioBuffer(call.peers[peer], data, samples, channels, sample_rate);
sample_rate);
} }
/** /**
@ -509,8 +473,9 @@ void CoreAV::groupCallCallback(void* tox, int group, int peer,
* @param group Group Index * @param group Group Index
* @param peer Peer Index * @param peer Peer Index
*/ */
void CoreAV::invalidateGroupCallPeerSource(int group, int peer) { void CoreAV::invalidateGroupCallPeerSource(int group, int peer)
Audio &audio = Audio::getInstance(); {
Audio& audio = Audio::getInstance();
audio.unsubscribeOutput(groupCalls[group].peers[peer]); audio.unsubscribeOutput(groupCalls[group].peers[peer]);
groupCalls[group].peers[peer] = 0; groupCalls[group].peers[peer] = 0;
} }
@ -520,11 +485,11 @@ void CoreAV::invalidateGroupCallPeerSource(int group, int peer) {
* @param friendNum Id of friend in call list. * @param friendNum Id of friend in call list.
* @return Video surface to show * @return Video surface to show
*/ */
VideoSource *CoreAV::getVideoSourceFromCall(int friendNum) VideoSource* CoreAV::getVideoSourceFromCall(int friendNum)
{ {
if (!calls.contains(friendNum)) if (!calls.contains(friendNum)) {
{ qWarning() << "CoreAV::getVideoSourceFromCall: No such call, did it die before we finished "
qWarning() << "CoreAV::getVideoSourceFromCall: No such call, did it die before we finished answering?"; "answering?";
return nullptr; return nullptr;
} }
@ -556,7 +521,8 @@ void CoreAV::leaveGroupCall(int groupId)
groupCalls.remove(groupId); groupCalls.remove(groupId);
} }
bool CoreAV::sendGroupCallAudio(int groupId, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) bool CoreAV::sendGroupCallAudio(int groupId, const int16_t* pcm, size_t samples, uint8_t chans,
uint32_t rate)
{ {
if (!groupCalls.contains(groupId)) if (!groupCalls.contains(groupId))
return false; return false;
@ -601,9 +567,7 @@ void CoreAV::muteCallOutput(const Group* g, bool mute)
*/ */
bool CoreAV::isGroupCallInputMuted(const Group* g) const bool CoreAV::isGroupCallInputMuted(const Group* g) const
{ {
return g && groupCalls.contains(g->getGroupId()) return g && groupCalls.contains(g->getGroupId()) ? groupCalls[g->getGroupId()].muteMic : false;
? groupCalls[g->getGroupId()].muteMic
: false;
} }
/** /**
@ -613,9 +577,7 @@ bool CoreAV::isGroupCallInputMuted(const Group* g) const
*/ */
bool CoreAV::isGroupCallOutputMuted(const Group* g) const bool CoreAV::isGroupCallOutputMuted(const Group* g) const
{ {
return g && groupCalls.contains(g->getGroupId()) return g && groupCalls.contains(g->getGroupId()) ? groupCalls[g->getGroupId()].muteVol : false;
? groupCalls[g->getGroupId()].muteVol
: false;
} }
/** /**
@ -628,12 +590,11 @@ bool CoreAV::isGroupAvEnabled(int groupId) const
Tox* tox = Core::getInstance()->tox; Tox* tox = Core::getInstance()->tox;
TOX_ERR_CONFERENCE_GET_TYPE error; TOX_ERR_CONFERENCE_GET_TYPE error;
TOX_CONFERENCE_TYPE type = tox_conference_get_type(tox, groupId, &error); TOX_CONFERENCE_TYPE type = tox_conference_get_type(tox, groupId, &error);
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_GET_TYPE_OK: case TOX_ERR_CONFERENCE_GET_TYPE_OK:
break; break;
case TOX_ERR_CONFERENCE_GET_TYPE_CONFERENCE_NOT_FOUND: case TOX_ERR_CONFERENCE_GET_TYPE_CONFERENCE_NOT_FOUND:
qCritical() << "Conference not found"; qCritical() << "Conference not found";
break; break;
default: default:
break; break;
@ -649,9 +610,7 @@ bool CoreAV::isGroupAvEnabled(int groupId) const
*/ */
bool CoreAV::isCallInputMuted(const Friend* f) const bool CoreAV::isCallInputMuted(const Friend* f) const
{ {
return f && calls.contains(f->getFriendId()) return f && calls.contains(f->getFriendId()) ? calls[f->getFriendId()].muteMic : false;
? calls[f->getFriendId()].muteMic
: false;
} }
/** /**
@ -661,9 +620,7 @@ bool CoreAV::isCallInputMuted(const Friend* f) const
*/ */
bool CoreAV::isCallOutputMuted(const Friend* f) const bool CoreAV::isCallOutputMuted(const Friend* f) const
{ {
return f && calls.contains(f->getFriendId()) return f && calls.contains(f->getFriendId()) ? calls[f->getFriendId()].muteVol : false;
? calls[f->getFriendId()].muteVol
: false;
} }
/** /**
@ -671,13 +628,11 @@ bool CoreAV::isCallOutputMuted(const Friend* f) const
*/ */
void CoreAV::invalidateCallSources() void CoreAV::invalidateCallSources()
{ {
for (ToxGroupCall& call : groupCalls) for (ToxGroupCall& call : groupCalls) {
{
call.peers.clear(); call.peers.clear();
} }
for (ToxFriendCall& call : calls) for (ToxFriendCall& call : calls) {
{
call.alSource = 0; call.alSource = 0;
} }
} }
@ -690,8 +645,7 @@ void CoreAV::sendNoVideo()
{ {
// We don't change the audio bitrate, but we signal that we're not sending video anymore // We don't change the audio bitrate, but we signal that we're not sending video anymore
qDebug() << "CoreAV: Signaling end of video sending"; qDebug() << "CoreAV: Signaling end of video sending";
for (ToxFriendCall& call : calls) for (ToxFriendCall& call : calls) {
{
toxav_bit_rate_set(toxav, call.callId, -1, 0, nullptr); toxav_bit_rate_set(toxav, call.callId, -1, 0, nullptr);
call.nullVideoBitrate = true; call.nullVideoBitrate = true;
} }
@ -701,12 +655,12 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
{ {
CoreAV* self = static_cast<CoreAV*>(vSelf); CoreAV* self = static_cast<CoreAV*>(vSelf);
// Run this slow callback asynchronously on the AV thread to avoid deadlocks with what our caller (toxcore) holds // Run this slow callback asynchronously on the AV thread to avoid deadlocks with what our
// caller (toxcore) holds
// Also run the code to switch to the CoreAV thread in yet another thread, in case CoreAV // Also run the code to switch to the CoreAV thread in yet another thread, in case CoreAV
// has threadSwitchLock and wants a toxcore lock that our call stack is holding... // has threadSwitchLock and wants a toxcore lock that our call stack is holding...
if (QThread::currentThread() != self->coreavThread.get()) if (QThread::currentThread() != self->coreavThread.get()) {
{ QtConcurrent::run([=]() {
QtConcurrent::run([=](){
// We assume the original caller doesn't come from the CoreAV thread here // We assume the original caller doesn't come from the CoreAV thread here
while (self->threadSwitchLock.test_and_set(std::memory_order_acquire)) while (self->threadSwitchLock.test_and_set(std::memory_order_acquire))
QThread::yieldCurrentThread(); // Shouldn't spin for long, we have priority QThread::yieldCurrentThread(); // Shouldn't spin for long, we have priority
@ -718,11 +672,11 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
return; return;
} }
if (self->calls.contains(friendNum)) if (self->calls.contains(friendNum)) {
{
/// Hanging up from a callback is supposed to be UB, /// Hanging up from a callback is supposed to be UB,
/// but since currently the toxav callbacks are fired from the toxcore thread, /// but since currently the toxav callbacks are fired from the toxcore thread,
/// we'll always reach this point through a non-blocking queud connection, so not in the callback. /// we'll always reach this point through a non-blocking queud connection, so not in the
/// callback.
qWarning() << QString("Rejecting call invite from %1, we're already in that call!").arg(friendNum); qWarning() << QString("Rejecting call invite from %1, we're already in that call!").arg(friendNum);
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr); toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
return; return;
@ -746,25 +700,24 @@ void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, voi
{ {
CoreAV* self = static_cast<CoreAV*>(vSelf); CoreAV* self = static_cast<CoreAV*>(vSelf);
// Run this slow callback asynchronously on the AV thread to avoid deadlocks with what our caller (toxcore) holds // Run this slow callback asynchronously on the AV thread to avoid deadlocks with what our
// caller (toxcore) holds
// Also run the code to switch to the CoreAV thread in yet another thread, in case CoreAV // Also run the code to switch to the CoreAV thread in yet another thread, in case CoreAV
// has threadSwitchLock and wants a toxcore lock that our call stack is holding... // has threadSwitchLock and wants a toxcore lock that our call stack is holding...
if (QThread::currentThread() != self->coreavThread.get()) if (QThread::currentThread() != self->coreavThread.get()) {
{ QtConcurrent::run([=]() {
QtConcurrent::run([=](){
// We assume the original caller doesn't come from the CoreAV thread here // We assume the original caller doesn't come from the CoreAV thread here
while (self->threadSwitchLock.test_and_set(std::memory_order_acquire)) while (self->threadSwitchLock.test_and_set(std::memory_order_acquire))
QThread::yieldCurrentThread(); // Shouldn't spin for long, we have priority QThread::yieldCurrentThread(); // Shouldn't spin for long, we have priority
QMetaObject::invokeMethod(self, "stateCallback", Qt::QueuedConnection, QMetaObject::invokeMethod(self, "stateCallback", Qt::QueuedConnection,
Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum), Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum),
Q_ARG(uint32_t, state), Q_ARG(void*, vSelf)); Q_ARG(uint32_t, state), Q_ARG(void*, vSelf));
}); });
return; return;
} }
if (!self->calls.contains(friendNum)) if (!self->calls.contains(friendNum)) {
{
qWarning() << QString("stateCallback called, but call %1 is already dead").arg(friendNum); qWarning() << QString("stateCallback called, but call %1 is already dead").arg(friendNum);
self->threadSwitchLock.clear(std::memory_order_release); self->threadSwitchLock.clear(std::memory_order_release);
return; return;
@ -772,40 +725,32 @@ void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, voi
ToxFriendCall& call = self->calls[friendNum]; ToxFriendCall& call = self->calls[friendNum];
if (state & TOXAV_FRIEND_CALL_STATE_ERROR) if (state & TOXAV_FRIEND_CALL_STATE_ERROR) {
{ qWarning() << "Call with friend" << friendNum << "died of unnatural causes!";
qWarning() << "Call with friend"<<friendNum<<"died of unnatural causes!";
calls.remove(friendNum); calls.remove(friendNum);
emit self->avEnd(friendNum); emit self->avEnd(friendNum);
} } else if (state & TOXAV_FRIEND_CALL_STATE_FINISHED) {
else if (state & TOXAV_FRIEND_CALL_STATE_FINISHED) qDebug() << "Call with friend" << friendNum << "finished quietly";
{
qDebug() << "Call with friend"<<friendNum<<"finished quietly";
calls.remove(friendNum); calls.remove(friendNum);
emit self->avEnd(friendNum); emit self->avEnd(friendNum);
} } else {
else
{
// If our state was null, we started the call and were still ringing // If our state was null, we started the call and were still ringing
if (!call.state && state) if (!call.state && state) {
{
call.stopTimeout(); call.stopTimeout();
call.inactive = false; call.inactive = false;
emit self->avStart(friendNum, call.videoEnabled); emit self->avStart(friendNum, call.videoEnabled);
} } else if ((call.state & TOXAV_FRIEND_CALL_STATE_SENDING_V)
else if ((call.state & TOXAV_FRIEND_CALL_STATE_SENDING_V) && !(state & TOXAV_FRIEND_CALL_STATE_SENDING_V)) {
&& !(state & TOXAV_FRIEND_CALL_STATE_SENDING_V)) qDebug() << "Friend" << friendNum << "stopped sending video";
{
qDebug() << "Friend"<<friendNum<<"stopped sending video";
if (call.videoSource) if (call.videoSource)
call.videoSource->stopSource(); call.videoSource->stopSource();
} } else if (!(call.state & TOXAV_FRIEND_CALL_STATE_SENDING_V)
else if (!(call.state & TOXAV_FRIEND_CALL_STATE_SENDING_V) && (state & TOXAV_FRIEND_CALL_STATE_SENDING_V)) {
&& (state & TOXAV_FRIEND_CALL_STATE_SENDING_V)) // Workaround toxav sometimes firing callbacks for "send last frame" -> "stop sending
{ // video"
// Workaround toxav sometimes firing callbacks for "send last frame" -> "stop sending video"
// out of orders (even though they were sent in order by the other end). // out of orders (even though they were sent in order by the other end).
// We simply stop the videoSource from emitting anything while the other end says it's not sending // We simply stop the videoSource from emitting anything while the other end says it's
// not sending
if (call.videoSource) if (call.videoSource)
call.videoSource->restartSource(); call.videoSource->restartSource();
} }
@ -815,23 +760,25 @@ void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, voi
self->threadSwitchLock.clear(std::memory_order_release); self->threadSwitchLock.clear(std::memory_order_release);
} }
void CoreAV::bitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t arate, uint32_t vrate, void* vSelf) void CoreAV::bitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t arate, uint32_t vrate,
void* vSelf)
{ {
CoreAV* self = static_cast<CoreAV*>(vSelf); CoreAV* self = static_cast<CoreAV*>(vSelf);
// Run this slow path callback asynchronously on the AV thread to avoid deadlocks // Run this slow path callback asynchronously on the AV thread to avoid deadlocks
if (QThread::currentThread() != self->coreavThread.get()) if (QThread::currentThread() != self->coreavThread.get()) {
{
return (void)QMetaObject::invokeMethod(self, "bitrateCallback", Qt::QueuedConnection, return (void)QMetaObject::invokeMethod(self, "bitrateCallback", Qt::QueuedConnection,
Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum), Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum),
Q_ARG(uint32_t, arate), Q_ARG(uint32_t, vrate), Q_ARG(void*, vSelf)); Q_ARG(uint32_t, arate), Q_ARG(uint32_t, vrate),
Q_ARG(void*, vSelf));
} }
qDebug() << "Recommended bitrate with"<<friendNum<<" is now "<<arate<<"/"<<vrate<<", ignoring it"; qDebug() << "Recommended bitrate with" << friendNum << " is now " << arate << "/" << vrate
<< ", ignoring it";
} }
void CoreAV::audioFrameCallback(ToxAV *, uint32_t friendNum, const int16_t *pcm, void CoreAV::audioFrameCallback(ToxAV*, uint32_t friendNum, const int16_t* pcm, size_t sampleCount,
size_t sampleCount, uint8_t channels, uint32_t samplingRate, void* vSelf) uint8_t channels, uint32_t samplingRate, void* vSelf)
{ {
CoreAV* self = static_cast<CoreAV*>(vSelf); CoreAV* self = static_cast<CoreAV*>(vSelf);
if (!self->calls.contains(friendNum)) if (!self->calls.contains(friendNum))
@ -849,9 +796,9 @@ void CoreAV::audioFrameCallback(ToxAV *, uint32_t friendNum, const int16_t *pcm,
audio.playAudioBuffer(call.alSource, pcm, sampleCount, channels, samplingRate); audio.playAudioBuffer(call.alSource, pcm, sampleCount, channels, samplingRate);
} }
void CoreAV::videoFrameCallback(ToxAV *, uint32_t friendNum, uint16_t w, uint16_t h, void CoreAV::videoFrameCallback(ToxAV*, uint32_t friendNum, uint16_t w, uint16_t h,
const uint8_t *y, const uint8_t *u, const uint8_t *v, const uint8_t* y, const uint8_t* u, const uint8_t* v,
int32_t ystride, int32_t ustride, int32_t vstride, void *) int32_t ystride, int32_t ustride, int32_t vstride, void*)
{ {
if (!calls.contains(friendNum)) if (!calls.contains(friendNum))
return; return;

View File

@ -21,10 +21,10 @@
#ifndef COREAV_H #ifndef COREAV_H
#define COREAV_H #define COREAV_H
#include <QObject>
#include <memory>
#include <atomic>
#include "src/core/toxcall.h" #include "src/core/toxcall.h"
#include <QObject>
#include <atomic>
#include <memory>
#include <tox/toxav.h> #include <tox/toxav.h>
class Friend; class Friend;
@ -54,9 +54,11 @@ public:
bool isCallActive(const Friend* f) const; bool isCallActive(const Friend* f) const;
bool isCallActive(const Group* g) const; bool isCallActive(const Group* g) const;
bool isCallVideoEnabled(const Friend* f) const; bool isCallVideoEnabled(const Friend* f) const;
bool sendCallAudio(uint32_t friendNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate); bool sendCallAudio(uint32_t friendNum, const int16_t* pcm, size_t samples, uint8_t chans,
uint32_t rate);
void sendCallVideo(uint32_t friendNum, std::shared_ptr<VideoFrame> frame); void sendCallVideo(uint32_t friendNum, std::shared_ptr<VideoFrame> frame);
bool sendGroupCallAudio(int groupNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate); bool sendGroupCallAudio(int groupNum, const int16_t* pcm, size_t samples, uint8_t chans,
uint32_t rate);
VideoSource* getVideoSourceFromCall(int callNumber); VideoSource* getVideoSourceFromCall(int callNumber);
void invalidateCallSources(); void invalidateCallSources();
@ -75,14 +77,12 @@ public:
void toggleMuteCallInput(const Friend* f); void toggleMuteCallInput(const Friend* f);
void toggleMuteCallOutput(const Friend* f); void toggleMuteCallOutput(const Friend* f);
static void groupCallCallback(void* tox, int group, int peer, static void groupCallCallback(void* tox, int group, int peer, const int16_t* data, unsigned samples,
const int16_t* data, unsigned samples, uint8_t channels, unsigned sample_rate, void* core);
uint8_t channels, unsigned sample_rate,
void* core);
static void invalidateGroupCallPeerSource(int group, int peer); static void invalidateGroupCallPeerSource(int group, int peer);
public slots: public slots:
bool startCall(uint32_t friendNum, bool video=false); bool startCall(uint32_t friendNum, bool video = false);
bool answerCall(uint32_t friendNum); bool answerCall(uint32_t friendNum);
bool cancelCall(uint32_t friendNum); bool cancelCall(uint32_t friendNum);
void timeoutCall(uint32_t friendNum); void timeoutCall(uint32_t friendNum);
@ -95,17 +95,19 @@ signals:
void avEnd(uint32_t friendId); void avEnd(uint32_t friendId);
private slots: private slots:
static void callCallback(ToxAV *toxAV, uint32_t friendNum, bool audio, bool video, void* self); static void callCallback(ToxAV* toxAV, uint32_t friendNum, bool audio, bool video, void* self);
static void stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void* self); static void stateCallback(ToxAV*, uint32_t friendNum, uint32_t state, void* self);
static void bitrateCallback(ToxAV *toxAV, uint32_t friendNum, uint32_t arate, uint32_t vrate, void* self); static void bitrateCallback(ToxAV* toxAV, uint32_t friendNum, uint32_t arate, uint32_t vrate,
void* self);
void killTimerFromThread(); void killTimerFromThread();
private: private:
void process(); void process();
static void audioFrameCallback(ToxAV *toxAV, uint32_t friendNum, const int16_t *pcm, size_t sampleCount, static void audioFrameCallback(ToxAV* toxAV, uint32_t friendNum, const int16_t* pcm,
uint8_t channels, uint32_t samplingRate, void* self); size_t sampleCount, uint8_t channels, uint32_t samplingRate,
static void videoFrameCallback(ToxAV *toxAV, uint32_t friendNum, uint16_t w, uint16_t h, void* self);
const uint8_t *y, const uint8_t *u, const uint8_t *v, static void videoFrameCallback(ToxAV* toxAV, uint32_t friendNum, uint16_t w, uint16_t h,
const uint8_t* y, const uint8_t* u, const uint8_t* v,
int32_t ystride, int32_t ustride, int32_t vstride, void* self); int32_t ystride, int32_t ustride, int32_t vstride, void* self);
private: private:

View File

@ -22,12 +22,12 @@
#include "corefile.h" #include "corefile.h"
#include "corestructs.h" #include "corestructs.h"
#include "src/core/cstring.h" #include "src/core/cstring.h"
#include "src/persistence/settings.h"
#include "src/persistence/profile.h" #include "src/persistence/profile.h"
#include "src/persistence/settings.h"
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QFile> #include <QFile>
#include <QThread> #include <QThread>
#include <QDir>
#include <memory> #include <memory>
/** /**
@ -55,13 +55,10 @@ unsigned CoreFile::corefileIterationInterval()
comes to CPU usage just keep the CPU usage low when there are no file comes to CPU usage just keep the CPU usage low when there are no file
transfers, and speed things up when there is an ongoing file transfer. transfers, and speed things up when there is an ongoing file transfer.
*/ */
constexpr unsigned fileInterval = 10, constexpr unsigned fileInterval = 10, idleInterval = 1000;
idleInterval = 1000;
for (ToxFile& file : fileMap) for (ToxFile& file : fileMap) {
{ if (file.status == ToxFile::TRANSMITTING) {
if (file.status == ToxFile::TRANSMITTING)
{
return fileInterval; return fileInterval;
} }
} }
@ -72,10 +69,8 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
{ {
QMutexLocker mlocker(&fileSendMutex); QMutexLocker mlocker(&fileSendMutex);
if (data.isEmpty()) if (data.isEmpty()) {
{ tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, 0, nullptr, nullptr, 0, nullptr);
tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, 0,
nullptr, nullptr, 0, nullptr);
return; return;
} }
@ -86,10 +81,9 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
TOX_ERR_FILE_SEND error; TOX_ERR_FILE_SEND error;
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, filesize, uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, filesize,
avatarHash, avatarHash, TOX_HASH_LENGTH, &error); avatarHash, avatarHash, TOX_HASH_LENGTH, &error);
switch (error) switch (error) {
{
case TOX_ERR_FILE_SEND_OK: case TOX_ERR_FILE_SEND_OK:
break; break;
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED: case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED:
@ -121,15 +115,15 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
addFile(friendId, fileNum, file); addFile(friendId, fileNum, file);
} }
void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString filePath, long long filesize) void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString filePath,
long long filesize)
{ {
QMutexLocker mlocker(&fileSendMutex); QMutexLocker mlocker(&fileSendMutex);
QByteArray fileName = filename.toUtf8(); QByteArray fileName = filename.toUtf8();
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_DATA, filesize, nullptr, uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_DATA, filesize, nullptr,
(uint8_t*)fileName.data(), fileName.size(), nullptr); (uint8_t*)fileName.data(), fileName.size(), nullptr);
if (fileNum == std::numeric_limits<uint32_t>::max()) if (fileNum == std::numeric_limits<uint32_t>::max()) {
{
qWarning() << "sendFile: Can't create the Tox file sender"; qWarning() << "sendFile: Can't create the Tox file sender";
emit core->fileSendFailed(friendId, filename); emit core->fileSendFailed(friendId, filename);
return; return;
@ -140,8 +134,7 @@ void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString
file.filesize = filesize; file.filesize = filesize;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH); file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr); tox_file_get_file_id(core->tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr);
if (!file.open(false)) if (!file.open(false)) {
{
qWarning() << QString("sendFile: Can't open file, error: %1").arg(file.file->errorString()); qWarning() << QString("sendFile: Can't open file, error: %1").arg(file.file->errorString());
} }
@ -153,25 +146,19 @@ void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString
void CoreFile::pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileId) void CoreFile::pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileId)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("pauseResumeFileSend: No such file in queue"); qWarning("pauseResumeFileSend: No such file in queue");
return; return;
} }
if (file->status == ToxFile::TRANSMITTING) if (file->status == ToxFile::TRANSMITTING) {
{
file->status = ToxFile::PAUSED; file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file); emit core->fileTransferPaused(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr); tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr);
} } else if (file->status == ToxFile::PAUSED) {
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING; file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file); emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr); tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
} } else {
else
{
qWarning() << "pauseResumeFileSend: File is stopped"; qWarning() << "pauseResumeFileSend: File is stopped";
} }
} }
@ -179,25 +166,19 @@ void CoreFile::pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileI
void CoreFile::pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileId) void CoreFile::pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("cancelFileRecv: No such file in queue"); qWarning("cancelFileRecv: No such file in queue");
return; return;
} }
if (file->status == ToxFile::TRANSMITTING) if (file->status == ToxFile::TRANSMITTING) {
{
file->status = ToxFile::PAUSED; file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file); emit core->fileTransferPaused(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr); tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr);
} } else if (file->status == ToxFile::PAUSED) {
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING; file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file); emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr); tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
} } else {
else
{
qWarning() << "pauseResumeFileRecv: File is stopped or broken"; qWarning() << "pauseResumeFileRecv: File is stopped or broken";
} }
} }
@ -205,8 +186,7 @@ void CoreFile::pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileI
void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId) void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("cancelFileSend: No such file in queue"); qWarning("cancelFileSend: No such file in queue");
return; return;
} }
@ -220,8 +200,7 @@ void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId)
void CoreFile::cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId) void CoreFile::cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("cancelFileRecv: No such file in queue"); qWarning("cancelFileRecv: No such file in queue");
return; return;
} }
@ -234,8 +213,7 @@ void CoreFile::cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
void CoreFile::rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId) void CoreFile::rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("rejectFileRecvRequest: No such file in queue"); qWarning("rejectFileRecvRequest: No such file in queue");
return; return;
} }
@ -248,14 +226,12 @@ void CoreFile::rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fil
void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path) void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("acceptFileRecvRequest: No such file in queue"); qWarning("acceptFileRecvRequest: No such file in queue");
return; return;
} }
file->setFilePath(path); file->setFilePath(path);
if (!file->open(true)) if (!file->open(true)) {
{
qWarning() << "acceptFileRecvRequest: Unable to open file"; qWarning() << "acceptFileRecvRequest: Unable to open file";
return; return;
} }
@ -267,13 +243,11 @@ void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fil
ToxFile* CoreFile::findFile(uint32_t friendId, uint32_t fileId) ToxFile* CoreFile::findFile(uint32_t friendId, uint32_t fileId)
{ {
uint64_t key = getFriendKey(friendId, fileId); uint64_t key = getFriendKey(friendId, fileId);
if (fileMap.contains(key)) if (fileMap.contains(key)) {
{
return &fileMap[key]; return &fileMap[key];
} }
qWarning() << "findFile: File transfer with ID" << friendId << ':' qWarning() << "findFile: File transfer with ID" << friendId << ':' << fileId << "doesn't exist";
<< fileId << "doesn't exist";
return nullptr; return nullptr;
} }
@ -281,10 +255,9 @@ void CoreFile::addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file)
{ {
uint64_t key = getFriendKey(friendId, fileId); uint64_t key = getFriendKey(friendId, fileId);
if (fileMap.contains(key)) if (fileMap.contains(key)) {
{ qWarning() << "addFile: Overwriting existing file transfer with same ID" << friendId << ':'
qWarning() << "addFile: Overwriting existing file transfer with same ID" << fileId;
<< friendId << ':' << fileId;
} }
fileMap.insert(key, file); fileMap.insert(key, file);
@ -293,8 +266,7 @@ void CoreFile::addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file)
void CoreFile::removeFile(uint32_t friendId, uint32_t fileId) void CoreFile::removeFile(uint32_t friendId, uint32_t fileId)
{ {
uint64_t key = getFriendKey(friendId, fileId); uint64_t key = getFriendKey(friendId, fileId);
if (!fileMap.contains(key)) if (!fileMap.contains(key)) {
{
qWarning() << "removeFile: No such file in queue"; qWarning() << "removeFile: No such file in queue";
return; return;
} }
@ -302,52 +274,49 @@ void CoreFile::removeFile(uint32_t friendId, uint32_t fileId)
fileMap.remove(key); fileMap.remove(key);
} }
void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, uint32_t kind,
uint32_t kind, uint64_t filesize, uint64_t filesize, const uint8_t* fname, size_t fnameLen,
const uint8_t* fname, size_t fnameLen,
void* vCore) void* vCore)
{ {
Core* core = static_cast<Core*>(vCore); Core* core = static_cast<Core*>(vCore);
if (kind == TOX_FILE_KIND_AVATAR) if (kind == TOX_FILE_KIND_AVATAR) {
{
// TODO: port this to ToxPk // TODO: port this to ToxPk
QString friendAddr = core->getFriendPublicKey(friendId).toString(); QString friendAddr = core->getFriendPublicKey(friendId).toString();
if (!filesize) if (!filesize) {
{
qDebug() << QString("Received empty avatar request %1:%2").arg(friendId).arg(fileId); qDebug() << QString("Received empty avatar request %1:%2").arg(friendId).arg(fileId);
// Avatars of size 0 means explicitely no avatar // Avatars of size 0 means explicitely no avatar
emit core->friendAvatarRemoved(friendId); emit core->friendAvatarRemoved(friendId);
core->profile.removeAvatar(friendAddr); core->profile.removeAvatar(friendAddr);
return; return;
} } else {
else static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH,
{ "TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!");
static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH, "TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!");
uint8_t avatarHash[TOX_FILE_ID_LENGTH]; uint8_t avatarHash[TOX_FILE_ID_LENGTH];
tox_file_get_file_id(core->tox, friendId, fileId, avatarHash, nullptr); tox_file_get_file_id(core->tox, friendId, fileId, avatarHash, nullptr);
if (core->profile.getAvatarHash(friendAddr) == QByteArray((char*)avatarHash, TOX_HASH_LENGTH)) if (core->profile.getAvatarHash(friendAddr)
{ == QByteArray((char*)avatarHash, TOX_HASH_LENGTH)) {
// If it's an avatar but we already have it cached, cancel // If it's an avatar but we already have it cached, cancel
qDebug() << QString("Received avatar request %1:%2, reject, since we have it in cache.").arg(friendId).arg(fileId); qDebug() << QString(
"Received avatar request %1:%2, reject, since we have it in cache.")
.arg(friendId)
.arg(fileId);
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr); tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return; return;
} } else {
else
{
// It's an avatar and we don't have it, autoaccept the transfer // It's an avatar and we don't have it, autoaccept the transfer
qDebug() << QString("Received avatar request %1:%2, accept, since we don't have it in cache.").arg(friendId).arg(fileId); qDebug() << QString("Received avatar request %1:%2, accept, since we don't have it "
"in cache.")
.arg(friendId)
.arg(fileId);
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr); tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr);
} }
} }
} } else {
else qDebug() << QString("Received file request %1:%2 kind %3").arg(friendId).arg(fileId).arg(kind);
{
qDebug() << QString("Received file request %1:%2 kind %3")
.arg(friendId).arg(fileId).arg(kind);
} }
ToxFile file{fileId, friendId, QByteArray((char*)fname,fnameLen), "", ToxFile::RECEIVING}; ToxFile file{fileId, friendId, QByteArray((char*)fname, fnameLen), "", ToxFile::RECEIVING};
file.filesize = filesize; file.filesize = filesize;
file.fileKind = kind; file.fileKind = kind;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH); file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
@ -357,59 +326,48 @@ void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId,
emit core->fileReceiveRequested(file); emit core->fileReceiveRequested(file);
} }
void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId, void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId,
TOX_FILE_CONTROL control, void *core) TOX_FILE_CONTROL control, void* core)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("onFileControlCallback: No such file in queue"); qWarning("onFileControlCallback: No such file in queue");
return; return;
} }
if (control == TOX_FILE_CONTROL_CANCEL) if (control == TOX_FILE_CONTROL_CANCEL) {
{
if (file->fileKind != TOX_FILE_KIND_AVATAR) if (file->fileKind != TOX_FILE_KIND_AVATAR)
qDebug() << "File tranfer"<<friendId<<":"<<fileId<<"cancelled by friend"; qDebug() << "File tranfer" << friendId << ":" << fileId << "cancelled by friend";
emit static_cast<Core*>(core)->fileTransferCancelled(*file); emit static_cast<Core*>(core)->fileTransferCancelled(*file);
removeFile(friendId, fileId); removeFile(friendId, fileId);
} } else if (control == TOX_FILE_CONTROL_PAUSE) {
else if (control == TOX_FILE_CONTROL_PAUSE) qDebug() << "onFileControlCallback: Received pause for file " << friendId << ":" << fileId;
{
qDebug() << "onFileControlCallback: Received pause for file "<<friendId<<":"<<fileId;
file->status = ToxFile::PAUSED; file->status = ToxFile::PAUSED;
emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, true); emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, true);
} } else if (control == TOX_FILE_CONTROL_RESUME) {
else if (control == TOX_FILE_CONTROL_RESUME)
{
if (file->direction == ToxFile::SENDING && file->fileKind == TOX_FILE_KIND_AVATAR) if (file->direction == ToxFile::SENDING && file->fileKind == TOX_FILE_KIND_AVATAR)
qDebug() << "Avatar transfer"<<fileId<<"to friend"<<friendId<<"accepted"; qDebug() << "Avatar transfer" << fileId << "to friend" << friendId << "accepted";
else else
qDebug() << "onFileControlCallback: Received resume for file "<<friendId<<":"<<fileId; qDebug() << "onFileControlCallback: Received resume for file " << friendId << ":" << fileId;
file->status = ToxFile::TRANSMITTING; file->status = ToxFile::TRANSMITTING;
emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, false); emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, false);
} } else {
else qWarning() << "Unhandled file control " << control << " for file " << friendId << ':' << fileId;
{
qWarning() << "Unhandled file control "<<control<<" for file "<<friendId<<':'<<fileId;
} }
} }
void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId, void CoreFile::onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t pos,
uint64_t pos, size_t length, void* core) size_t length, void* core)
{ {
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("onFileDataCallback: No such file in queue"); qWarning("onFileDataCallback: No such file in queue");
return; return;
} }
// If we reached EOF, ack and cleanup the transfer // If we reached EOF, ack and cleanup the transfer
if (!length) if (!length) {
{ if (file->fileKind != TOX_FILE_KIND_AVATAR) {
if (file->fileKind != TOX_FILE_KIND_AVATAR)
{
emit static_cast<Core*>(core)->fileTransferFinished(*file); emit static_cast<Core*>(core)->fileTransferFinished(*file);
emit static_cast<Core*>(core)->fileUploadFinished(file->filePath); emit static_cast<Core*>(core)->fileUploadFinished(file->filePath);
} }
@ -420,18 +378,14 @@ void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
unique_ptr<uint8_t[]> data(new uint8_t[length]); unique_ptr<uint8_t[]> data(new uint8_t[length]);
int64_t nread; int64_t nread;
if (file->fileKind == TOX_FILE_KIND_AVATAR) if (file->fileKind == TOX_FILE_KIND_AVATAR) {
{
QByteArray chunk = file->avatarData.mid(pos, length); QByteArray chunk = file->avatarData.mid(pos, length);
nread = chunk.size(); nread = chunk.size();
memcpy(data.get(), chunk.data(), nread); memcpy(data.get(), chunk.data(), nread);
} } else {
else
{
file->file->seek(pos); file->file->seek(pos);
nread = file->file->read((char*)data.get(), length); nread = file->file->read((char*)data.get(), length);
if (nread <= 0) if (nread <= 0) {
{
qWarning("onFileDataCallback: Failed to read from file"); qWarning("onFileDataCallback: Failed to read from file");
emit static_cast<Core*>(core)->fileTransferCancelled(*file); emit static_cast<Core*>(core)->fileTransferCancelled(*file);
tox_file_send_chunk(tox, friendId, fileId, pos, nullptr, 0, nullptr); tox_file_send_chunk(tox, friendId, fileId, pos, nullptr, 0, nullptr);
@ -441,8 +395,7 @@ void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
file->bytesSent += length; file->bytesSent += length;
} }
if (!tox_file_send_chunk(tox, friendId, fileId, pos, data.get(), nread, nullptr)) if (!tox_file_send_chunk(tox, friendId, fileId, pos, data.get(), nread, nullptr)) {
{
qWarning("onFileDataCallback: Failed to send data chunk"); qWarning("onFileDataCallback: Failed to send data chunk");
return; return;
} }
@ -450,22 +403,18 @@ void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
emit static_cast<Core*>(core)->fileTransferInfo(*file); emit static_cast<Core*>(core)->fileTransferInfo(*file);
} }
void CoreFile::onFileRecvChunkCallback(Tox *tox, uint32_t friendId, void CoreFile::onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t position,
uint32_t fileId, uint64_t position, const uint8_t* data, size_t length, void* vCore)
const uint8_t* data, size_t length,
void* vCore)
{ {
Core* core = static_cast<Core*>(vCore); Core* core = static_cast<Core*>(vCore);
ToxFile* file = findFile(friendId, fileId); ToxFile* file = findFile(friendId, fileId);
if (!file) if (!file) {
{
qWarning("onFileRecvChunkCallback: No such file in queue"); qWarning("onFileRecvChunkCallback: No such file in queue");
tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr); tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return; return;
} }
if (file->bytesSent != position) if (file->bytesSent != position) {
{
qWarning("onFileRecvChunkCallback: Received a chunk out-of-order, aborting transfer"); qWarning("onFileRecvChunkCallback: Received a chunk out-of-order, aborting transfer");
if (file->fileKind != TOX_FILE_KIND_AVATAR) if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit core->fileTransferCancelled(*file); emit core->fileTransferCancelled(*file);
@ -474,21 +423,17 @@ void CoreFile::onFileRecvChunkCallback(Tox *tox, uint32_t friendId,
return; return;
} }
if (!length) if (!length) {
{ if (file->fileKind == TOX_FILE_KIND_AVATAR) {
if (file->fileKind == TOX_FILE_KIND_AVATAR)
{
QPixmap pic; QPixmap pic;
pic.loadFromData(file->avatarData); pic.loadFromData(file->avatarData);
if (!pic.isNull()) if (!pic.isNull()) {
{ qDebug() << "Got" << file->avatarData.size() << "bytes of avatar data from" << friendId;
qDebug() << "Got"<<file->avatarData.size()<<"bytes of avatar data from" <<friendId; core->profile.saveAvatar(file->avatarData,
core->profile.saveAvatar(file->avatarData, core->getFriendPublicKey(friendId).toString()); core->getFriendPublicKey(friendId).toString());
emit core->friendAvatarChanged(friendId, pic); emit core->friendAvatarChanged(friendId, pic);
} }
} } else {
else
{
emit core->fileTransferFinished(*file); emit core->fileTransferFinished(*file);
emit core->fileDownloadFinished(file->filePath); emit core->fileDownloadFinished(file->filePath);
} }
@ -499,7 +444,7 @@ void CoreFile::onFileRecvChunkCallback(Tox *tox, uint32_t friendId,
if (file->fileKind == TOX_FILE_KIND_AVATAR) if (file->fileKind == TOX_FILE_KIND_AVATAR)
file->avatarData.append((char*)data, length); file->avatarData.append((char*)data, length);
else else
file->file->write((char*)data,length); file->file->write((char*)data, length);
file->bytesSent += length; file->bytesSent += length;
if (file->fileKind != TOX_FILE_KIND_AVATAR) if (file->fileKind != TOX_FILE_KIND_AVATAR)
@ -513,11 +458,11 @@ void CoreFile::onConnectionStatusChanged(Core* core, uint32_t friendId, bool onl
// - Start a new file transfer with the same 32byte file ID with toxcore // - Start a new file transfer with the same 32byte file ID with toxcore
// - Seek to the correct position again // - Seek to the correct position again
// - Update the fileNum in our ToxFile // - Update the fileNum in our ToxFile
// - Update the users of our signals to check the 32byte tox file ID, not the uint32_t file_num (fileId) // - Update the users of our signals to check the 32byte tox file ID, not the uint32_t file_num
// (fileId)
ToxFile::FileStatus status = online ? ToxFile::TRANSMITTING : ToxFile::BROKEN; ToxFile::FileStatus status = online ? ToxFile::TRANSMITTING : ToxFile::BROKEN;
for (uint64_t key : fileMap.keys()) for (uint64_t key : fileMap.keys()) {
{ if (key >> 32 != friendId)
if (key>>32 != friendId)
continue; continue;
fileMap[key].status = status; fileMap[key].status = status;
emit core->fileTransferBrokenUnbroken(fileMap[key], !online); emit core->fileTransferBrokenUnbroken(fileMap[key], !online);

View File

@ -21,16 +21,16 @@
#ifndef COREFILE_H #ifndef COREFILE_H
#define COREFILE_H #define COREFILE_H
#include <cstdint>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <memory> #include <memory>
#include <tox/tox.h> #include <tox/tox.h>
#include "corestructs.h" #include "corestructs.h"
#include <QString>
#include <QMutex>
#include <QHash> #include <QHash>
#include <QMutex>
#include <QString>
struct Tox; struct Tox;
class Core; class Core;
@ -40,38 +40,20 @@ class CoreFile
friend class Core; friend class Core;
private: private:
CoreFile()=delete; CoreFile() = delete;
// Internal file sending APIs, used by Core. Public API in core.h // Internal file sending APIs, used by Core. Public API in core.h
private: private:
static void sendFile(Core *core, static void sendFile(Core* core, uint32_t friendId, QString filename, QString filePath,
uint32_t friendId,
QString filename,
QString filePath,
long long filesize); long long filesize);
static void sendAvatarFile(Core* core, static void sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& data);
uint32_t friendId, static void pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileId);
const QByteArray& data); static void pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileId);
static void pauseResumeFileSend(Core* core, static void cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId);
uint32_t friendId, static void cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId);
uint32_t fileId); static void rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId);
static void pauseResumeFileRecv(Core* core, static void acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path);
uint32_t friendId, static ToxFile* findFile(uint32_t friendId, uint32_t fileId);
uint32_t fileId);
static void cancelFileSend(Core* core,
uint32_t friendId,
uint32_t fileId);
static void cancelFileRecv(Core* core,
uint32_t friendId,
uint32_t fileId);
static void rejectFileRecvRequest(Core* core,
uint32_t friendId,
uint32_t fileId);
static void acceptFileRecvRequest(Core* core,
uint32_t friendId,
uint32_t fileId,
QString path);
static ToxFile *findFile(uint32_t friendId, uint32_t fileId);
static void addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file); static void addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file);
static void removeFile(uint32_t friendId, uint32_t fileId); static void removeFile(uint32_t friendId, uint32_t fileId);
static unsigned corefileIterationInterval(); static unsigned corefileIterationInterval();
@ -81,28 +63,16 @@ private:
} }
private: private:
static void onFileReceiveCallback(Tox*, static void onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, uint32_t kind,
uint32_t friendId, uint64_t filesize, const uint8_t* fname, size_t fnameLen,
uint32_t fileId, void* vCore);
uint32_t kind, static void onFileControlCallback(Tox* tox, uint32_t friendId, uint32_t fileId,
uint64_t filesize, TOX_FILE_CONTROL control, void* core);
const uint8_t* fname, static void onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t pos,
size_t fnameLen, size_t length, void* core);
void *vCore); static void onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t position,
static void onFileControlCallback(Tox *tox, uint32_t friendId, uint32_t fileId, const uint8_t* data, size_t length, void* vCore);
TOX_FILE_CONTROL control, void *core); static void onConnectionStatusChanged(Core* core, uint32_t friendId, bool online);
static void onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
uint64_t pos, size_t length, void *core);
static void onFileRecvChunkCallback(Tox *tox,
uint32_t friendId,
uint32_t fileId,
uint64_t position,
const uint8_t* data,
size_t length,
void *vCore);
static void onConnectionStatusChanged(Core* core,
uint32_t friendId,
bool online);
private: private:
static QMutex fileSendMutex; static QMutex fileSendMutex;

View File

@ -1,10 +1,10 @@
#include "src/core/corestructs.h" #include "src/core/corestructs.h"
#include "src/core/core.h" #include "src/core/core.h"
#include <tox/tox.h>
#include <QFile> #include <QFile>
#include <QRegularExpression> #include <QRegularExpression>
#include <tox/tox.h>
#define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE #define TOX_HEX_ID_LENGTH 2 * TOX_ADDRESS_SIZE
/** /**
* @file corestructs.h * @file corestructs.h
@ -23,9 +23,8 @@
*/ */
bool DhtServer::operator==(const DhtServer& other) const bool DhtServer::operator==(const DhtServer& other) const
{ {
return this == &other || return this == &other || (port == other.port && address == other.address
(port == other.port && address == other.address && && userId == other.userId && name == other.name);
userId == other.userId && name == other.name);
} }
/** /**
@ -41,26 +40,35 @@ bool DhtServer::operator!=(const DhtServer& other) const
/** /**
* @brief ToxFile constructor * @brief ToxFile constructor
*/ */
ToxFile::ToxFile(uint32_t fileNum, uint32_t friendId, QByteArray filename, QString filePath, FileDirection Direction) ToxFile::ToxFile(uint32_t fileNum, uint32_t friendId, QByteArray filename, QString filePath,
: fileKind{TOX_FILE_KIND_DATA}, fileNum(fileNum), friendId(friendId), fileName{filename}, FileDirection Direction)
filePath{filePath}, file{new QFile(filePath)}, bytesSent{0}, filesize{0}, : fileKind{TOX_FILE_KIND_DATA}
status{STOPPED}, direction{Direction} , fileNum(fileNum)
, friendId(friendId)
, fileName{filename}
, filePath{filePath}
, file{new QFile(filePath)}
, bytesSent{0}
, filesize{0}
, status{STOPPED}
, direction{Direction}
{ {
} }
bool ToxFile::operator==(const ToxFile &other) const bool ToxFile::operator==(const ToxFile& other) const
{ {
return (fileNum == other.fileNum) && (friendId == other.friendId) && (direction == other.direction); return (fileNum == other.fileNum) && (friendId == other.friendId)
&& (direction == other.direction);
} }
bool ToxFile::operator!=(const ToxFile &other) const bool ToxFile::operator!=(const ToxFile& other) const
{ {
return !(*this == other); return !(*this == other);
} }
void ToxFile::setFilePath(QString path) void ToxFile::setFilePath(QString path)
{ {
filePath=path; filePath = path;
file->setFileName(path); file->setFileName(path);
} }

View File

@ -7,7 +7,13 @@
class QFile; class QFile;
class QTimer; class QTimer;
enum class Status : int {Online = 0, Away, Busy, Offline}; enum class Status : int
{
Online = 0,
Away,
Busy,
Offline
};
struct DhtServer struct DhtServer
{ {
@ -37,8 +43,11 @@ struct ToxFile
}; };
ToxFile() = default; ToxFile() = default;
ToxFile(uint32_t FileNum, uint32_t FriendId, QByteArray FileName, QString filePath, FileDirection Direction); ToxFile(uint32_t FileNum, uint32_t FriendId, QByteArray FileName, QString filePath,
~ToxFile(){} FileDirection Direction);
~ToxFile()
{
}
bool operator==(const ToxFile& other) const; bool operator==(const ToxFile& other) const;
bool operator!=(const ToxFile& other) const; bool operator!=(const ToxFile& other) const;

View File

@ -21,8 +21,8 @@
#include "cstring.h" #include "cstring.h"
#include <QString> #include <QString>
CString::CString(const QString& string) : CString::CString(const QString& string)
CString(string.toUtf8()) : CString(string.toUtf8())
{ {
} }
@ -33,7 +33,7 @@ CString::CString(const QByteArray& ba_string)
memcpy(cString, reinterpret_cast<const uint8_t*>(ba_string.data()), cStringSize); memcpy(cString, reinterpret_cast<const uint8_t*>(ba_string.data()), cStringSize);
} }
CString::CString(const CString &cstr) CString::CString(const CString& cstr)
{ {
cStringSize = cstr.cStringSize; cStringSize = cstr.cStringSize;
cString = new uint8_t[cStringSize](); cString = new uint8_t[cStringSize]();

View File

@ -1,8 +1,8 @@
#ifndef INDEXEDLIST_H #ifndef INDEXEDLIST_H
#define INDEXEDLIST_H #define INDEXEDLIST_H
#include <vector>
#include <algorithm> #include <algorithm>
#include <vector>
template <typename T> template <typename T>
class IndexedList class IndexedList
@ -19,28 +19,22 @@ public:
template <typename cmp_type> template <typename cmp_type>
bool contains(cmp_type i) bool contains(cmp_type i)
{ {
return std::find_if(begin(), end(), [i](T& t) return std::find_if(begin(), end(), [i](T& t) { return static_cast<cmp_type>(t) == i; })
{ != end();
return static_cast<cmp_type>(t) == i;
}) != end();
} }
template <typename cmp_type> template <typename cmp_type>
void remove(cmp_type i) void remove(cmp_type i)
{ {
v.erase(std::remove_if(begin(), end(), [i](T& t) v.erase(std::remove_if(begin(), end(), [i](T& t) { return static_cast<cmp_type>(t) == i; }),
{ end());
return static_cast<cmp_type>(t) == i;
}), end());
} }
template <typename cmp_type> template <typename cmp_type>
T& operator[](cmp_type i) T& operator[](cmp_type i)
{ {
iterator it = std::find_if(begin(), end(), [i](T& t) iterator it =
{ std::find_if(begin(), end(), [i](T& t) { return static_cast<cmp_type>(t) == i; });
return static_cast<cmp_type>(t) == i;
});
if (it == end()) if (it == end())
it = insert({}); it = insert({});

View File

@ -53,8 +53,7 @@ void RecursiveSignalBlocker::recursiveBlock(QObject* object)
{ {
mBlockers << new QSignalBlocker(object); mBlockers << new QSignalBlocker(object);
for (QObject* child : object->children()) for (QObject* child : object->children()) {
{
recursiveBlock(child); recursiveBlock(child);
} }
} }

View File

@ -1,5 +1,5 @@
#include "src/audio/audio.h"
#include "src/core/toxcall.h" #include "src/core/toxcall.h"
#include "src/audio/audio.h"
#include "src/core/coreav.h" #include "src/core/coreav.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include "src/video/camerasource.h" #include "src/video/camerasource.h"
@ -12,7 +12,8 @@
* @brief Could be a friendNum or groupNum, must uniquely identify the call. Do not modify! * @brief Could be a friendNum or groupNum, must uniquely identify the call. Do not modify!
* *
* @var bool ToxCall::inactive * @var bool ToxCall::inactive
* @brief True while we're not participating. (stopped group call, ringing but hasn't started yet, ...) * @brief True while we're not participating. (stopped group call, ringing but hasn't started yet,
* ...)
* *
* @var bool ToxFriendCall::videoEnabled * @var bool ToxFriendCall::videoEnabled
* @brief True if our user asked for a video call, sending and recieving. * @brief True if our user asked for a video call, sending and recieving.
@ -30,17 +31,23 @@
using namespace std; using namespace std;
ToxCall::ToxCall(uint32_t CallId) ToxCall::ToxCall(uint32_t CallId)
: callId{CallId}, alSource{0}, : callId{CallId}
inactive{true}, muteMic{false}, muteVol{false} , alSource{0}
, inactive{true}
, muteMic{false}
, muteVol{false}
{ {
Audio& audio = Audio::getInstance(); Audio& audio = Audio::getInstance();
audio.subscribeInput(); audio.subscribeInput();
audio.subscribeOutput(alSource); audio.subscribeOutput(alSource);
} }
ToxCall::ToxCall(ToxCall&& other) noexcept ToxCall::ToxCall(ToxCall&& other) noexcept : audioInConn{other.audioInConn},
: audioInConn{other.audioInConn}, callId{other.callId}, alSource{other.alSource}, callId{other.callId},
inactive{other.inactive}, muteMic{other.muteMic}, muteVol{other.muteVol} alSource{other.alSource},
inactive{other.inactive},
muteMic{other.muteMic},
muteVol{other.muteVol}
{ {
other.audioInConn = QMetaObject::Connection(); other.audioInConn = QMetaObject::Connection();
other.callId = numeric_limits<decltype(callId)>::max(); other.callId = numeric_limits<decltype(callId)>::max();
@ -81,15 +88,13 @@ ToxCall& ToxCall::operator=(ToxCall&& other) noexcept
void ToxFriendCall::startTimeout() void ToxFriendCall::startTimeout()
{ {
if (!timeoutTimer) if (!timeoutTimer) {
{
timeoutTimer = new QTimer(); timeoutTimer = new QTimer();
// We might move, so we need copies of members. CoreAV won't move while we're alive // We might move, so we need copies of members. CoreAV won't move while we're alive
CoreAV* avCopy = av; CoreAV* avCopy = av;
auto callIdCopy = callId; auto callIdCopy = callId;
QObject::connect(timeoutTimer, &QTimer::timeout, [avCopy, callIdCopy](){ QObject::connect(timeoutTimer, &QTimer::timeout,
avCopy->timeoutCall(callIdCopy); [avCopy, callIdCopy]() { avCopy->timeoutCall(callIdCopy); });
});
} }
if (!timeoutTimer->isActive()) if (!timeoutTimer->isActive())
@ -107,19 +112,21 @@ void ToxFriendCall::stopTimeout()
} }
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av) ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
: ToxCall(FriendNum), : ToxCall(FriendNum)
videoEnabled{VideoEnabled}, nullVideoBitrate{false}, videoSource{nullptr}, , videoEnabled{VideoEnabled}
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)}, , nullVideoBitrate{false}
av{&av}, timeoutTimer{nullptr} , videoSource{nullptr}
, state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)}
, av{&av}
, timeoutTimer{nullptr}
{ {
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable, audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
[&av,FriendNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) [&av, FriendNum](const int16_t* pcm, size_t samples,
{ uint8_t chans, uint32_t rate) {
av.sendCallAudio(FriendNum, pcm, samples, chans, rate); av.sendCallAudio(FriendNum, pcm, samples, chans, rate);
}); });
if (videoEnabled) if (videoEnabled) {
{
videoSource = new CoreVideoSource; videoSource = new CoreVideoSource;
CameraSource& source = CameraSource::getInstance(); CameraSource& source = CameraSource::getInstance();
@ -127,15 +134,20 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
source.open(); source.open();
source.subscribe(); source.subscribe();
QObject::connect(&source, &VideoSource::frameAvailable, QObject::connect(&source, &VideoSource::frameAvailable,
[FriendNum,&av](shared_ptr<VideoFrame> frame){av.sendCallVideo(FriendNum,frame);}); [FriendNum, &av](shared_ptr<VideoFrame> frame) {
av.sendCallVideo(FriendNum, frame);
});
} }
} }
ToxFriendCall::ToxFriendCall(ToxFriendCall&& other) noexcept ToxFriendCall::ToxFriendCall(ToxFriendCall&& other) noexcept
: ToxCall(move(other)), : ToxCall(move(other)),
videoEnabled{other.videoEnabled}, nullVideoBitrate{other.nullVideoBitrate}, videoEnabled{other.videoEnabled},
videoSource{other.videoSource}, state{other.state}, nullVideoBitrate{other.nullVideoBitrate},
av{other.av}, timeoutTimer{other.timeoutTimer} videoSource{other.videoSource},
state{other.state},
av{other.av},
timeoutTimer{other.timeoutTimer}
{ {
other.videoEnabled = false; other.videoEnabled = false;
other.videoSource = nullptr; other.videoSource = nullptr;
@ -147,14 +159,14 @@ ToxFriendCall::~ToxFriendCall()
if (timeoutTimer) if (timeoutTimer)
delete timeoutTimer; delete timeoutTimer;
if (videoEnabled) if (videoEnabled) {
{
// This destructor could be running in a toxav callback while holding toxav locks. // This destructor could be running in a toxav callback while holding toxav locks.
// If the CameraSource thread calls toxav *_send_frame, we might deadlock the toxav and CameraSource locks, // If the CameraSource thread calls toxav *_send_frame, we might deadlock the toxav and
// so we unsuscribe asynchronously, it's fine if the webcam takes a couple milliseconds more to poweroff. // CameraSource locks,
QtConcurrent::run([](){CameraSource::getInstance().unsubscribe();}); // so we unsuscribe asynchronously, it's fine if the webcam takes a couple milliseconds more
if (videoSource) // to poweroff.
{ QtConcurrent::run([]() { CameraSource::getInstance().unsubscribe(); });
if (videoSource) {
videoSource->setDeleteOnClose(true); videoSource->setDeleteOnClose(true);
videoSource = nullptr; videoSource = nullptr;
} }
@ -163,7 +175,7 @@ ToxFriendCall::~ToxFriendCall()
ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other) noexcept ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other) noexcept
{ {
ToxCall::operator =(move(other)); ToxCall::operator=(move(other));
videoEnabled = other.videoEnabled; videoEnabled = other.videoEnabled;
other.videoEnabled = false; other.videoEnabled = false;
videoSource = other.videoSource; videoSource = other.videoSource;
@ -177,21 +189,21 @@ ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other) noexcept
return *this; return *this;
} }
ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV &av) ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV& av)
: ToxCall(static_cast<decltype(callId)>(GroupNum)) : ToxCall(static_cast<decltype(callId)>(GroupNum))
{ {
static_assert(numeric_limits<decltype(callId)>::max() >= numeric_limits<decltype(GroupNum)>::max(), static_assert(
"The callId must be able to represent any group number, change its type if needed"); numeric_limits<decltype(callId)>::max() >= numeric_limits<decltype(GroupNum)>::max(),
"The callId must be able to represent any group number, change its type if needed");
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable, audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
[&av,GroupNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) [&av, GroupNum](const int16_t* pcm, size_t samples,
{ uint8_t chans, uint32_t rate) {
av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate); av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate);
}); });
} }
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept : ToxCall(move(other))
: ToxCall(move(other))
{ {
} }
@ -199,15 +211,14 @@ ToxGroupCall::~ToxGroupCall()
{ {
Audio& audio = Audio::getInstance(); Audio& audio = Audio::getInstance();
for(quint32 v : peers) for (quint32 v : peers) {
{
audio.unsubscribeOutput(v); audio.unsubscribeOutput(v);
} }
} }
ToxGroupCall &ToxGroupCall::operator=(ToxGroupCall &&other) noexcept ToxGroupCall& ToxGroupCall::operator=(ToxGroupCall&& other) noexcept
{ {
ToxCall::operator =(move(other)); ToxCall::operator=(move(other));
return *this; return *this;
} }

View File

@ -1,10 +1,10 @@
#ifndef TOXCALL_H #ifndef TOXCALL_H
#define TOXCALL_H #define TOXCALL_H
#include <cstdint>
#include <QtGlobal>
#include <QMetaObject>
#include <QMap> #include <QMap>
#include <QMetaObject>
#include <QtGlobal>
#include <cstdint>
#include "src/core/indexedlist.h" #include "src/core/indexedlist.h"
@ -18,19 +18,23 @@ class CoreAV;
struct ToxCall struct ToxCall
{ {
protected: protected:
ToxCall() = default; ToxCall() = default;
explicit ToxCall(uint32_t CallId); explicit ToxCall(uint32_t CallId);
~ToxCall(); ~ToxCall();
public:
ToxCall(const ToxCall& other) = delete;
ToxCall(ToxCall&& other) noexcept;
inline operator int() {return callId;} public:
ToxCall& operator=(const ToxCall& other) = delete; ToxCall(const ToxCall& other) = delete;
ToxCall& operator=(ToxCall&& other) noexcept; ToxCall(ToxCall&& other) noexcept;
inline operator int()
{
return callId;
}
ToxCall& operator=(const ToxCall& other) = delete;
ToxCall& operator=(ToxCall&& other) noexcept;
protected: protected:
QMetaObject::Connection audioInConn; QMetaObject::Connection audioInConn;
public: public:
uint32_t callId; uint32_t callId;
@ -80,4 +84,3 @@ struct ToxGroupCall : public ToxCall
}; };
#endif // TOXCALL_H #endif // TOXCALL_H

View File

@ -20,10 +20,10 @@
#include "toxencrypt.h" #include "toxencrypt.h"
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
#include <memory>
#include <QByteArray> #include <QByteArray>
#include <QDebug> #include <QDebug>
#include <QString> #include <QString>
#include <memory>
// functions for nice debug output // functions for nice debug output
static QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error); static QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error);
@ -51,9 +51,10 @@ ToxEncrypt::~ToxEncrypt()
* @brief Constructs a ToxEncrypt object from a Tox_Pass_Key. * @brief Constructs a ToxEncrypt object from a Tox_Pass_Key.
* @param key Derived key to use for encryption and decryption. * @param key Derived key to use for encryption and decryption.
*/ */
ToxEncrypt::ToxEncrypt(Tox_Pass_Key* key) : ToxEncrypt::ToxEncrypt(Tox_Pass_Key* key)
passKey{key} : passKey{key}
{} {
}
/** /**
* @brief Gets the minimum number of bytes needed for isEncrypted() * @brief Gets the minimum number of bytes needed for isEncrypted()
@ -72,8 +73,7 @@ int ToxEncrypt::getMinBytes()
*/ */
bool ToxEncrypt::isEncrypted(const QByteArray& ciphertext) bool ToxEncrypt::isEncrypted(const QByteArray& ciphertext)
{ {
if (ciphertext.length() < TOX_PASS_ENCRYPTION_EXTRA_LENGTH) if (ciphertext.length() < TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
{
return false; return false;
} }
@ -89,8 +89,7 @@ bool ToxEncrypt::isEncrypted(const QByteArray& ciphertext)
*/ */
QByteArray ToxEncrypt::encryptPass(const QString& password, const QByteArray& plaintext) QByteArray ToxEncrypt::encryptPass(const QString& password, const QByteArray& plaintext)
{ {
if (password.length() == 0) if (password.length() == 0) {
{
qWarning() << "Empty password supplied, probably not what you intended."; qWarning() << "Empty password supplied, probably not what you intended.";
} }
@ -103,8 +102,7 @@ QByteArray ToxEncrypt::encryptPass(const QString& password, const QByteArray& pl
static_cast<size_t>(pass.size()), static_cast<size_t>(pass.size()),
reinterpret_cast<uint8_t*>(ciphertext.data()), &error); reinterpret_cast<uint8_t*>(ciphertext.data()), &error);
if (error != TOX_ERR_ENCRYPTION_OK) if (error != TOX_ERR_ENCRYPTION_OK) {
{
qCritical() << getEncryptionError(error); qCritical() << getEncryptionError(error);
return QByteArray{}; return QByteArray{};
} }
@ -121,14 +119,12 @@ QByteArray ToxEncrypt::encryptPass(const QString& password, const QByteArray& pl
*/ */
QByteArray ToxEncrypt::decryptPass(const QString& password, const QByteArray& ciphertext) QByteArray ToxEncrypt::decryptPass(const QString& password, const QByteArray& ciphertext)
{ {
if (!isEncrypted(ciphertext)) if (!isEncrypted(ciphertext)) {
{
qWarning() << "The data was not encrypted using this module or it's corrupted."; qWarning() << "The data was not encrypted using this module or it's corrupted.";
return QByteArray{}; return QByteArray{};
} }
if (password.length() == 0) if (password.length() == 0) {
{
qDebug() << "Empty password supplied, probably not what you intended."; qDebug() << "Empty password supplied, probably not what you intended.";
} }
@ -138,11 +134,10 @@ QByteArray ToxEncrypt::decryptPass(const QString& password, const QByteArray& ci
tox_pass_decrypt(reinterpret_cast<const uint8_t*>(ciphertext.constData()), tox_pass_decrypt(reinterpret_cast<const uint8_t*>(ciphertext.constData()),
static_cast<size_t>(ciphertext.size()), static_cast<size_t>(ciphertext.size()),
reinterpret_cast<const uint8_t*>(pass.constData()), reinterpret_cast<const uint8_t*>(pass.constData()),
static_cast<size_t>(pass.size()), static_cast<size_t>(pass.size()), reinterpret_cast<uint8_t*>(plaintext.data()),
reinterpret_cast<uint8_t*>(plaintext.data()), &error); &error);
if (error != TOX_ERR_DECRYPTION_OK) if (error != TOX_ERR_DECRYPTION_OK) {
{
qWarning() << getDecryptionError(error); qWarning() << getDecryptionError(error);
return QByteArray{}; return QByteArray{};
} }
@ -166,8 +161,7 @@ std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password)
tox_pass_key_derive(passKey, reinterpret_cast<const uint8_t*>(pass.constData()), tox_pass_key_derive(passKey, reinterpret_cast<const uint8_t*>(pass.constData()),
static_cast<size_t>(pass.length()), &error); static_cast<size_t>(pass.length()), &error);
if (error != TOX_ERR_KEY_DERIVATION_OK) if (error != TOX_ERR_KEY_DERIVATION_OK) {
{
tox_pass_key_free(passKey); tox_pass_key_free(passKey);
qCritical() << getKeyDerivationError(error); qCritical() << getKeyDerivationError(error);
return std::unique_ptr<ToxEncrypt>{}; return std::unique_ptr<ToxEncrypt>{};
@ -187,8 +181,7 @@ std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password)
*/ */
std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password, const QByteArray& toxSave) std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password, const QByteArray& toxSave)
{ {
if (!isEncrypted(toxSave)) if (!isEncrypted(toxSave)) {
{
qWarning() << "The data was not encrypted using this module or it's corrupted."; qWarning() << "The data was not encrypted using this module or it's corrupted.";
return std::unique_ptr<ToxEncrypt>{}; return std::unique_ptr<ToxEncrypt>{};
} }
@ -197,8 +190,7 @@ std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password,
uint8_t salt[TOX_PASS_SALT_LENGTH]; uint8_t salt[TOX_PASS_SALT_LENGTH];
tox_get_salt(reinterpret_cast<const uint8_t*>(toxSave.constData()), salt, &saltError); tox_get_salt(reinterpret_cast<const uint8_t*>(toxSave.constData()), salt, &saltError);
if (saltError != TOX_ERR_GET_SALT_OK) if (saltError != TOX_ERR_GET_SALT_OK) {
{
qWarning() << getSaltError(saltError); qWarning() << getSaltError(saltError);
return std::unique_ptr<ToxEncrypt>{}; return std::unique_ptr<ToxEncrypt>{};
} }
@ -207,10 +199,9 @@ std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password,
QByteArray pass = password.toUtf8(); QByteArray pass = password.toUtf8();
TOX_ERR_KEY_DERIVATION keyError; TOX_ERR_KEY_DERIVATION keyError;
tox_pass_key_derive_with_salt(passKey, reinterpret_cast<const uint8_t*>(pass.constData()), tox_pass_key_derive_with_salt(passKey, reinterpret_cast<const uint8_t*>(pass.constData()),
static_cast<size_t>(pass.length()), salt, &keyError); static_cast<size_t>(pass.length()), salt, &keyError);
if (keyError != TOX_ERR_KEY_DERIVATION_OK) if (keyError != TOX_ERR_KEY_DERIVATION_OK) {
{
tox_pass_key_free(passKey); tox_pass_key_free(passKey);
qWarning() << getKeyDerivationError(keyError); qWarning() << getKeyDerivationError(keyError);
return std::unique_ptr<ToxEncrypt>{}; return std::unique_ptr<ToxEncrypt>{};
@ -226,21 +217,18 @@ std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password,
*/ */
QByteArray ToxEncrypt::encrypt(const QByteArray& plaintext) const QByteArray ToxEncrypt::encrypt(const QByteArray& plaintext) const
{ {
if (!passKey) if (!passKey) {
{
qCritical() << "The passKey is invalid."; qCritical() << "The passKey is invalid.";
return QByteArray{}; return QByteArray{};
} }
QByteArray ciphertext(plaintext.length() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0x00); QByteArray ciphertext(plaintext.length() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0x00);
TOX_ERR_ENCRYPTION error; TOX_ERR_ENCRYPTION error;
tox_pass_key_encrypt(passKey, tox_pass_key_encrypt(passKey, reinterpret_cast<const uint8_t*>(plaintext.constData()),
reinterpret_cast<const uint8_t*>(plaintext.constData()),
static_cast<size_t>(plaintext.size()), static_cast<size_t>(plaintext.size()),
reinterpret_cast<uint8_t*>(ciphertext.data()), &error); reinterpret_cast<uint8_t*>(ciphertext.data()), &error);
if (error != TOX_ERR_ENCRYPTION_OK) if (error != TOX_ERR_ENCRYPTION_OK) {
{
qCritical() << getEncryptionError(error); qCritical() << getEncryptionError(error);
return QByteArray{}; return QByteArray{};
} }
@ -256,21 +244,18 @@ QByteArray ToxEncrypt::encrypt(const QByteArray& plaintext) const
*/ */
QByteArray ToxEncrypt::decrypt(const QByteArray& ciphertext) const QByteArray ToxEncrypt::decrypt(const QByteArray& ciphertext) const
{ {
if (!isEncrypted(ciphertext)) if (!isEncrypted(ciphertext)) {
{
qWarning() << "The data was not encrypted using this module or it's corrupted."; qWarning() << "The data was not encrypted using this module or it's corrupted.";
return QByteArray{}; return QByteArray{};
} }
QByteArray plaintext(ciphertext.length() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0x00); QByteArray plaintext(ciphertext.length() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0x00);
TOX_ERR_DECRYPTION error; TOX_ERR_DECRYPTION error;
tox_pass_key_decrypt(passKey, tox_pass_key_decrypt(passKey, reinterpret_cast<const uint8_t*>(ciphertext.constData()),
reinterpret_cast<const uint8_t*>(ciphertext.constData()),
static_cast<size_t>(ciphertext.size()), static_cast<size_t>(ciphertext.size()),
reinterpret_cast<uint8_t*>(plaintext.data()), &error); reinterpret_cast<uint8_t*>(plaintext.data()), &error);
if (error != TOX_ERR_DECRYPTION_OK) if (error != TOX_ERR_DECRYPTION_OK) {
{
qWarning() << getDecryptionError(error); qWarning() << getDecryptionError(error);
return QByteArray{}; return QByteArray{};
} }
@ -285,14 +270,15 @@ QByteArray ToxEncrypt::decrypt(const QByteArray& ciphertext) const
*/ */
QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error) QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error)
{ {
switch(error) switch (error) {
{
case TOX_ERR_KEY_DERIVATION_OK: case TOX_ERR_KEY_DERIVATION_OK:
return QStringLiteral("The function returned successfully."); return QStringLiteral("The function returned successfully.");
case TOX_ERR_KEY_DERIVATION_NULL: case TOX_ERR_KEY_DERIVATION_NULL:
return QStringLiteral("One of the arguments to the function was NULL when it was not expected."); return QStringLiteral(
"One of the arguments to the function was NULL when it was not expected.");
case TOX_ERR_KEY_DERIVATION_FAILED: case TOX_ERR_KEY_DERIVATION_FAILED:
return QStringLiteral("The crypto lib was unable to derive a key from the given passphrase."); return QStringLiteral(
"The crypto lib was unable to derive a key from the given passphrase.");
default: default:
return QStringLiteral("Unknown key derivation error."); return QStringLiteral("Unknown key derivation error.");
} }
@ -305,14 +291,15 @@ QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error)
*/ */
QString getEncryptionError(TOX_ERR_ENCRYPTION error) QString getEncryptionError(TOX_ERR_ENCRYPTION error)
{ {
switch(error) switch (error) {
{
case TOX_ERR_ENCRYPTION_OK: case TOX_ERR_ENCRYPTION_OK:
return QStringLiteral("The function returned successfully."); return QStringLiteral("The function returned successfully.");
case TOX_ERR_ENCRYPTION_NULL: case TOX_ERR_ENCRYPTION_NULL:
return QStringLiteral("One of the arguments to the function was NULL when it was not expected."); return QStringLiteral(
"One of the arguments to the function was NULL when it was not expected.");
case TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED: case TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED:
return QStringLiteral("The crypto lib was unable to derive a key from the given passphrase."); return QStringLiteral(
"The crypto lib was unable to derive a key from the given passphrase.");
case TOX_ERR_ENCRYPTION_FAILED: case TOX_ERR_ENCRYPTION_FAILED:
return QStringLiteral("The encryption itself failed."); return QStringLiteral("The encryption itself failed.");
default: default:
@ -327,14 +314,15 @@ QString getEncryptionError(TOX_ERR_ENCRYPTION error)
*/ */
QString getDecryptionError(TOX_ERR_DECRYPTION error) QString getDecryptionError(TOX_ERR_DECRYPTION error)
{ {
switch(error) switch (error) {
{
case TOX_ERR_DECRYPTION_OK: case TOX_ERR_DECRYPTION_OK:
return QStringLiteral("The function returned successfully."); return QStringLiteral("The function returned successfully.");
case TOX_ERR_DECRYPTION_NULL: case TOX_ERR_DECRYPTION_NULL:
return QStringLiteral("One of the arguments to the function was NULL when it was not expected."); return QStringLiteral(
"One of the arguments to the function was NULL when it was not expected.");
case TOX_ERR_DECRYPTION_INVALID_LENGTH: case TOX_ERR_DECRYPTION_INVALID_LENGTH:
return QStringLiteral("The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes."); return QStringLiteral(
"The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes.");
case TOX_ERR_DECRYPTION_BAD_FORMAT: case TOX_ERR_DECRYPTION_BAD_FORMAT:
return QStringLiteral("The input data is missing the magic number or is corrupted."); return QStringLiteral("The input data is missing the magic number or is corrupted.");
default: default:
@ -349,12 +337,12 @@ QString getDecryptionError(TOX_ERR_DECRYPTION error)
*/ */
QString getSaltError(TOX_ERR_GET_SALT error) QString getSaltError(TOX_ERR_GET_SALT error)
{ {
switch(error) switch (error) {
{
case TOX_ERR_GET_SALT_OK: case TOX_ERR_GET_SALT_OK:
return QStringLiteral("The function returned successfully."); return QStringLiteral("The function returned successfully.");
case TOX_ERR_GET_SALT_NULL: case TOX_ERR_GET_SALT_NULL:
return QStringLiteral("One of the arguments to the function was NULL when it was not expected."); return QStringLiteral(
"One of the arguments to the function was NULL when it was not expected.");
case TOX_ERR_GET_SALT_BAD_FORMAT: case TOX_ERR_GET_SALT_BAD_FORMAT:
return QStringLiteral("The input data is missing the magic number or is corrupted."); return QStringLiteral("The input data is missing the magic number or is corrupted.");
default: default:

View File

@ -17,8 +17,8 @@
along with qTox. If not, see <http://www.gnu.org/licenses/>. along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QString>
#include <QByteArray> #include <QByteArray>
#include <QString>
#ifndef TOXENCRYPT_H #ifndef TOXENCRYPT_H
#define TOXENCRYPT_H #define TOXENCRYPT_H
@ -40,7 +40,8 @@ public:
static QByteArray encryptPass(const QString& password, const QByteArray& plaintext); static QByteArray encryptPass(const QString& password, const QByteArray& plaintext);
static QByteArray decryptPass(const QString& password, const QByteArray& ciphertext); static QByteArray decryptPass(const QString& password, const QByteArray& ciphertext);
static std::unique_ptr<ToxEncrypt> makeToxEncrypt(const QString& password); static std::unique_ptr<ToxEncrypt> makeToxEncrypt(const QString& password);
static std::unique_ptr<ToxEncrypt> makeToxEncrypt(const QString& password, const QByteArray& toxSave); static std::unique_ptr<ToxEncrypt> makeToxEncrypt(const QString& password,
const QByteArray& toxSave);
QByteArray encrypt(const QByteArray& plaintext) const; QByteArray encrypt(const QByteArray& plaintext) const;
QByteArray decrypt(const QByteArray& ciphertext) const; QByteArray decrypt(const QByteArray& ciphertext) const;

View File

@ -24,17 +24,17 @@
#include <tox/tox.h> #include <tox/tox.h>
#include <cstdint>
#include <QRegularExpression> #include <QRegularExpression>
#include <cstdint>
// Tox doesn't publicly define these // Tox doesn't publicly define these
#define NOSPAM_BYTES 4 #define NOSPAM_BYTES 4
#define CHECKSUM_BYTES 2 #define CHECKSUM_BYTES 2
#define PUBLIC_KEY_HEX_CHARS (2*TOX_PUBLIC_KEY_SIZE) #define PUBLIC_KEY_HEX_CHARS (2 * TOX_PUBLIC_KEY_SIZE)
#define NOSPAM_HEX_CHARS (2*NOSPAM_BYTES) #define NOSPAM_HEX_CHARS (2 * NOSPAM_BYTES)
#define CHECKSUM_HEX_CHARS (2*CHECKSUM_BYTES) #define CHECKSUM_HEX_CHARS (2 * CHECKSUM_BYTES)
#define TOXID_HEX_CHARS (2*TOX_ADDRESS_SIZE) #define TOXID_HEX_CHARS (2 * TOX_ADDRESS_SIZE)
const QRegularExpression ToxId::ToxIdRegEx(QString("(^|\\s)[A-Fa-f0-9]{%1}($|\\s)").arg(TOXID_HEX_CHARS)); const QRegularExpression ToxId::ToxIdRegEx(QString("(^|\\s)[A-Fa-f0-9]{%1}($|\\s)").arg(TOXID_HEX_CHARS));
@ -59,16 +59,18 @@ const QRegularExpression ToxId::ToxIdRegEx(QString("(^|\\s)[A-Fa-f0-9]{%1}($|\\s
* @brief The default constructor. Creates an empty Tox ID. * @brief The default constructor. Creates an empty Tox ID.
*/ */
ToxId::ToxId() ToxId::ToxId()
: toxId() : toxId()
{} {
}
/** /**
* @brief The copy constructor. * @brief The copy constructor.
* @param other ToxId to copy * @param other ToxId to copy
*/ */
ToxId::ToxId(const ToxId& other) ToxId::ToxId(const ToxId& other)
: toxId(other.toxId) : toxId(other.toxId)
{} {
}
/** /**
* @brief Create a Tox ID from a QString. * @brief Create a Tox ID from a QString.
@ -82,17 +84,12 @@ ToxId::ToxId(const ToxId& other)
ToxId::ToxId(const QString& id) ToxId::ToxId(const QString& id)
{ {
// TODO: remove construction from PK only // TODO: remove construction from PK only
if (isToxId(id)) if (isToxId(id)) {
{
toxId = QByteArray::fromHex(id.toLatin1()); toxId = QByteArray::fromHex(id.toLatin1());
} } else if (id.length() >= PUBLIC_KEY_HEX_CHARS) {
else if(id.length() >= PUBLIC_KEY_HEX_CHARS)
{
toxId = QByteArray::fromHex(id.left(PUBLIC_KEY_HEX_CHARS).toLatin1()); toxId = QByteArray::fromHex(id.left(PUBLIC_KEY_HEX_CHARS).toLatin1());
} } else {
else toxId = QByteArray(); // invalid id string
{
toxId = QByteArray(); // invalid id string
} }
} }
@ -123,7 +120,7 @@ ToxId::ToxId(const QByteArray& rawId)
*/ */
ToxId::ToxId(const uint8_t* rawId, int len) ToxId::ToxId(const uint8_t* rawId, int len)
{ {
QByteArray tmpId(reinterpret_cast<const char *>(rawId), len); QByteArray tmpId(reinterpret_cast<const char*>(rawId), len);
constructToxId(tmpId); constructToxId(tmpId);
} }
@ -131,18 +128,12 @@ ToxId::ToxId(const uint8_t* rawId, int len)
void ToxId::constructToxId(const QByteArray& rawId) void ToxId::constructToxId(const QByteArray& rawId)
{ {
// TODO: remove construction from PK only // TODO: remove construction from PK only
if(rawId.length() == TOX_SECRET_KEY_SIZE) if (rawId.length() == TOX_SECRET_KEY_SIZE) {
{ toxId = QByteArray(rawId); // construct from PK only
toxId = QByteArray(rawId); // construct from PK only } else if (rawId.length() == TOX_ADDRESS_SIZE && isToxId(rawId.toHex().toUpper())) {
} toxId = QByteArray(rawId); // construct from full toxid
else if (rawId.length() == TOX_ADDRESS_SIZE } else {
&& isToxId(rawId.toHex().toUpper())) toxId = QByteArray(); // invalid id
{
toxId = QByteArray(rawId); // construct from full toxid
}
else
{
toxId = QByteArray(); // invalid id
} }
} }
@ -190,8 +181,7 @@ void ToxId::clear()
*/ */
const uint8_t* ToxId::getBytes() const const uint8_t* ToxId::getBytes() const
{ {
if(isValid()) if (isValid()) {
{
return reinterpret_cast<const uint8_t*>(toxId.constData()); return reinterpret_cast<const uint8_t*>(toxId.constData());
} }
@ -213,8 +203,7 @@ ToxPk ToxId::getPublicKey() const
*/ */
QString ToxId::getNoSpamString() const QString ToxId::getNoSpamString() const
{ {
if(toxId.length() == TOX_ADDRESS_SIZE) if (toxId.length() == TOX_ADDRESS_SIZE) {
{
return toxId.mid(TOX_PUBLIC_KEY_SIZE, NOSPAM_BYTES).toHex().toUpper(); return toxId.mid(TOX_PUBLIC_KEY_SIZE, NOSPAM_BYTES).toHex().toUpper();
} }
@ -249,8 +238,7 @@ bool ToxId::isToxId(const QString& id)
*/ */
bool ToxId::isValid() const bool ToxId::isValid() const
{ {
if(toxId.length() != TOX_ADDRESS_SIZE) if (toxId.length() != TOX_ADDRESS_SIZE) {
{
return false; return false;
} }
@ -260,8 +248,7 @@ bool ToxId::isValid() const
QByteArray checksum = toxId.right(CHECKSUM_BYTES); QByteArray checksum = toxId.right(CHECKSUM_BYTES);
QByteArray calculated(CHECKSUM_BYTES, 0x00); QByteArray calculated(CHECKSUM_BYTES, 0x00);
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++) {
{
calculated[i % 2] = calculated[i % 2] ^ data[i]; calculated[i % 2] = calculated[i % 2] ^ data[i];
} }

View File

@ -23,9 +23,9 @@
#include "toxpk.h" #include "toxpk.h"
#include <cstdint>
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <cstdint>
class ToxId class ToxId
{ {
@ -43,7 +43,7 @@ public:
bool isValid() const; bool isValid() const;
static bool isValidToxId(const QString& id); static bool isValidToxId(const QString& id);
static bool isToxId(const QString &id); static bool isToxId(const QString& id);
const uint8_t* getBytes() const; const uint8_t* getBytes() const;
QByteArray getToxId() const; QByteArray getToxId() const;
ToxPk getPublicKey() const; ToxPk getPublicKey() const;

View File

@ -14,16 +14,18 @@
* @brief The default constructor. Creates an empty Tox key. * @brief The default constructor. Creates an empty Tox key.
*/ */
ToxPk::ToxPk() ToxPk::ToxPk()
: key() : key()
{} {
}
/** /**
* @brief The copy constructor. * @brief The copy constructor.
* @param other ToxPk to copy * @param other ToxPk to copy
*/ */
ToxPk::ToxPk(const ToxPk& other) ToxPk::ToxPk(const ToxPk& other)
: key(other.key) : key(other.key)
{} {
}
/** /**
* @brief Constructs a ToxPk from bytes. * @brief Constructs a ToxPk from bytes.
@ -32,12 +34,9 @@ ToxPk::ToxPk(const ToxPk& other)
*/ */
ToxPk::ToxPk(const QByteArray& rawId) ToxPk::ToxPk(const QByteArray& rawId)
{ {
if(rawId.length() == TOX_PUBLIC_KEY_SIZE) if (rawId.length() == TOX_PUBLIC_KEY_SIZE) {
{
key = QByteArray(rawId); key = QByteArray(rawId);
} } else {
else
{
key = QByteArray(); key = QByteArray();
} }
} }
@ -88,8 +87,7 @@ QString ToxPk::toString() const
*/ */
const uint8_t* ToxPk::getBytes() const const uint8_t* ToxPk::getBytes() const
{ {
if(key.isEmpty()) if (key.isEmpty()) {
{
return nullptr; return nullptr;
} }

View File

@ -1,9 +1,9 @@
#ifndef TOXPK_H #ifndef TOXPK_H
#define TOXPK_H #define TOXPK_H
#include <cstdint>
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <cstdint>
class ToxPk class ToxPk
{ {
@ -21,6 +21,7 @@ public:
bool isEmpty() const; bool isEmpty() const;
static int getPkSize(); static int getPkSize();
private: private:
QByteArray key; QByteArray key;
}; };

View File

@ -23,8 +23,8 @@
#include "src/group.h" #include "src/group.h"
#include "src/grouplist.h" #include "src/grouplist.h"
#include "src/nexus.h" #include "src/nexus.h"
#include "src/persistence/settings.h"
#include "src/persistence/profile.h" #include "src/persistence/profile.h"
#include "src/persistence/settings.h"
#include "src/widget/form/chatform.h" #include "src/widget/form/chatform.h"
Friend::Friend(uint32_t friendId, const ToxPk& friendPk) Friend::Friend(uint32_t friendId, const ToxPk& friendPk)
@ -35,8 +35,7 @@ Friend::Friend(uint32_t friendId, const ToxPk& friendPk)
, hasNewEvents(false) , hasNewEvents(false)
, friendStatus(Status::Offline) , friendStatus(Status::Offline)
{ {
if (userName.isEmpty()) if (userName.isEmpty()) {
{
userName = friendPk.toString(); userName = friendPk.toString();
} }
@ -55,8 +54,7 @@ Friend::~Friend()
*/ */
void Friend::loadHistory() void Friend::loadHistory()
{ {
if (Nexus::getProfile()->isHistoryEnabled()) if (Nexus::getProfile()->isHistoryEnabled()) {
{
chatForm->loadHistory(QDateTime::currentDateTime().addDays(-7), true); chatForm->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
} }
@ -65,13 +63,11 @@ void Friend::loadHistory()
void Friend::setName(QString name) void Friend::setName(QString name)
{ {
if (name.isEmpty()) if (name.isEmpty()) {
{
name = friendPk.toString(); name = friendPk.toString();
} }
if (userName != name) if (userName != name) {
{
userName = name; userName = name;
emit nameChanged(friendId, name); emit nameChanged(friendId, name);
} }
@ -79,8 +75,7 @@ void Friend::setName(QString name)
void Friend::setAlias(QString alias) void Friend::setAlias(QString alias)
{ {
if (userAlias != alias) if (userAlias != alias) {
{
userAlias = alias; userAlias = alias;
emit aliasChanged(friendId, alias); emit aliasChanged(friendId, alias);
} }
@ -88,8 +83,7 @@ void Friend::setAlias(QString alias)
void Friend::setStatusMessage(QString message) void Friend::setStatusMessage(QString message)
{ {
if (statusMessage != message) if (statusMessage != message) {
{
statusMessage = message; statusMessage = message;
emit statusMessageChanged(friendId, message); emit statusMessageChanged(friendId, message);
} }
@ -102,8 +96,7 @@ QString Friend::getStatusMessage()
QString Friend::getDisplayedName() const QString Friend::getDisplayedName() const
{ {
if (userAlias.isEmpty()) if (userAlias.isEmpty()) {
{
return userName; return userName;
} }
@ -137,8 +130,7 @@ bool Friend::getEventFlag() const
void Friend::setStatus(Status s) void Friend::setStatus(Status s)
{ {
if (friendStatus != s) if (friendStatus != s) {
{
friendStatus = s; friendStatus = s;
emit statusChanged(friendId, friendStatus); emit statusChanged(friendId, friendStatus);
} }
@ -149,7 +141,7 @@ Status Friend::getStatus() const
return friendStatus; return friendStatus;
} }
ChatForm *Friend::getChatForm() ChatForm* Friend::getChatForm()
{ {
return chatForm; return chatForm;
} }

View File

@ -20,10 +20,10 @@
#ifndef FRIEND_H #ifndef FRIEND_H
#define FRIEND_H #define FRIEND_H
#include "core/toxid.h"
#include "src/core/corestructs.h"
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include "src/core/corestructs.h"
#include "core/toxid.h"
class FriendWidget; class FriendWidget;
class ChatForm; class ChatForm;
@ -56,7 +56,7 @@ public:
void setStatus(Status s); void setStatus(Status s);
Status getStatus() const; Status getStatus() const;
ChatForm *getChatForm(); ChatForm* getChatForm();
signals: signals:
// TODO: move signals to DB object // TODO: move signals to DB object

View File

@ -20,9 +20,9 @@
#include "friend.h" #include "friend.h"
#include "friendlist.h" #include "friendlist.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include <QMenu>
#include <QDebug> #include <QDebug>
#include <QHash> #include <QHash>
#include <QMenu>
QHash<int, Friend*> FriendList::friendList; QHash<int, Friend*> FriendList::friendList;
QHash<QByteArray, int> FriendList::key2id; QHash<QByteArray, int> FriendList::key2id;
@ -52,8 +52,7 @@ Friend* FriendList::findFriend(int friendId)
void FriendList::removeFriend(int friendId, bool fake) void FriendList::removeFriend(int friendId, bool fake)
{ {
auto f_it = friendList.find(friendId); auto f_it = friendList.find(friendId);
if (f_it != friendList.end()) if (f_it != friendList.end()) {
{
if (!fake) if (!fake)
Settings::getInstance().removeFriendSettings(f_it.value()->getPublicKey()); Settings::getInstance().removeFriendSettings(f_it.value()->getPublicKey());
friendList.erase(f_it); friendList.erase(f_it);
@ -70,8 +69,7 @@ void FriendList::clear()
Friend* FriendList::findFriend(const ToxPk& friendPk) Friend* FriendList::findFriend(const ToxPk& friendPk)
{ {
auto id = key2id.find(friendPk.getKey()); auto id = key2id.find(friendPk.getKey());
if (id != key2id.end()) if (id != key2id.end()) {
{
Friend* f = findFriend(*id); Friend* f = findFriend(*id);
if (!f) if (!f)
return nullptr; return nullptr;

View File

@ -20,8 +20,10 @@
#ifndef FRIENDLIST_H #ifndef FRIENDLIST_H
#define FRIENDLIST_H #define FRIENDLIST_H
template <class T> class QList; template <class T>
template <class A, class B> class QHash; class QList;
template <class A, class B>
class QHash;
class Friend; class Friend;
class QByteArray; class QByteArray;
class ToxPk; class ToxPk;

View File

@ -18,22 +18,24 @@
*/ */
#include "group.h" #include "group.h"
#include "widget/groupwidget.h"
#include "widget/form/groupchatform.h"
#include "friendlist.h"
#include "friend.h" #include "friend.h"
#include "friendlist.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "widget/form/groupchatform.h"
#include "widget/groupwidget.h"
#include "widget/gui.h" #include "widget/gui.h"
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
Group::Group(int GroupId, QString Name, bool IsAvGroupchat) Group::Group(int GroupId, QString Name, bool IsAvGroupchat)
: groupId(GroupId), nPeers{0}, avGroupchat{IsAvGroupchat} : groupId(GroupId)
, nPeers{0}
, avGroupchat{IsAvGroupchat}
{ {
widget = new GroupWidget(groupId, Name); widget = new GroupWidget(groupId, Name);
chatForm = new GroupChatForm(this); chatForm = new GroupChatForm(this);
//in groupchats, we only notify on messages containing your name <-- dumb // in groupchats, we only notify on messages containing your name <-- dumb
// sound notifications should be on all messages, but system popup notification // sound notifications should be on all messages, but system popup notification
// on naming is appropriate // on naming is appropriate
hasNewMessages = 0; hasNewMessages = 0;
@ -54,13 +56,10 @@ void Group::updatePeer(int peerId, QString name)
toxids[peerPk] = name; toxids[peerPk] = name;
Friend* f = FriendList::findFriend(peerKey); Friend* f = FriendList::findFriend(peerKey);
if (f != nullptr && f->hasAlias()) if (f != nullptr && f->hasAlias()) {
{
peers[peerId] = f->getDisplayedName(); peers[peerId] = f->getDisplayedName();
toxids[peerPk] = f->getDisplayedName(); toxids[peerPk] = f->getDisplayedName();
} } else {
else
{
widget->onUserListChanged(); widget->onUserListChanged();
chatForm->onUserListChanged(); chatForm->onUserListChanged();
emit userListChanged(getGroupWidget()); emit userListChanged(getGroupWidget());
@ -88,8 +87,7 @@ void Group::regeneratePeerList()
peers = core->getGroupPeerNames(groupId); peers = core->getGroupPeerNames(groupId);
toxids.clear(); toxids.clear();
nPeers = peers.size(); nPeers = peers.size();
for (int i = 0; i < nPeers; ++i) for (int i = 0; i < nPeers; ++i) {
{
ToxPk id = core->getGroupPeerPk(groupId, i); ToxPk id = core->getGroupPeerPk(groupId, i);
ToxPk self = core->getSelfId().getPublicKey(); ToxPk self = core->getSelfId().getPublicKey();
if (id == self) if (id == self)
@ -98,11 +96,11 @@ void Group::regeneratePeerList()
QByteArray peerPk = id.getKey(); QByteArray peerPk = id.getKey();
toxids[peerPk] = peers[i]; toxids[peerPk] = peers[i];
if (toxids[peerPk].isEmpty()) if (toxids[peerPk].isEmpty())
toxids[peerPk] = tr("<Empty>", "Placeholder when someone's name in a group chat is empty"); toxids[peerPk] =
tr("<Empty>", "Placeholder when someone's name in a group chat is empty");
Friend* f = FriendList::findFriend(id); Friend* f = FriendList::findFriend(id);
if (f != nullptr && f->hasAlias()) if (f != nullptr && f->hasAlias()) {
{
peers[i] = f->getDisplayedName(); peers[i] = f->getDisplayedName();
toxids[peerPk] = f->getDisplayedName(); toxids[peerPk] = f->getDisplayedName();
} }
@ -128,12 +126,12 @@ int Group::getPeersCount() const
return nPeers; return nPeers;
} }
GroupChatForm *Group::getChatForm() GroupChatForm* Group::getChatForm()
{ {
return chatForm; return chatForm;
} }
GroupWidget *Group::getGroupWidget() GroupWidget* Group::getGroupWidget()
{ {
return widget; return widget;
} }
@ -168,7 +166,7 @@ int Group::getMentionedFlag() const
return userWasMentioned; return userWasMentioned;
} }
QString Group::resolveToxId(const ToxPk &id) const QString Group::resolveToxId(const ToxPk& id) const
{ {
QByteArray key = id.getKey(); QByteArray key = id.getKey();
auto it = toxids.find(key); auto it = toxids.find(key);

View File

@ -45,8 +45,8 @@ public:
QStringList getPeerList() const; QStringList getPeerList() const;
bool isSelfPeerNumber(int peernumber) const; bool isSelfPeerNumber(int peernumber) const;
GroupChatForm *getChatForm(); GroupChatForm* getChatForm();
GroupWidget *getGroupWidget(); GroupWidget* getGroupWidget();
void setEventFlag(int f); void setEventFlag(int f);
int getEventFlag() const; int getEventFlag() const;
@ -74,7 +74,6 @@ private:
int nPeers; int nPeers;
int selfPeerNum = -1; int selfPeerNum = -1;
bool avGroupchat; bool avGroupchat;
}; };
#endif // GROUP_H #endif // GROUP_H

View File

@ -26,19 +26,17 @@
*/ */
GroupInvite::GroupInvite(int32_t friendID, uint8_t inviteType, const QByteArray& data) GroupInvite::GroupInvite(int32_t friendID, uint8_t inviteType, const QByteArray& data)
: friendId { friendID } : friendId{friendID}
, type { inviteType } , type{inviteType}
, invite { data } , invite{data}
, date { QDateTime::currentDateTime() } , date{QDateTime::currentDateTime()}
{ {
} }
bool GroupInvite::operator ==(const GroupInvite& other) const bool GroupInvite::operator==(const GroupInvite& other) const
{ {
return friendId == other.friendId && return friendId == other.friendId && type == other.type && invite == other.invite
type == other.type && && date == other.date;
invite == other.invite &&
date == other.date;
} }
int32_t GroupInvite::getFriendId() const int32_t GroupInvite::getFriendId() const

View File

@ -20,15 +20,15 @@
#ifndef GROUPINVITE_H #ifndef GROUPINVITE_H
#define GROUPINVITE_H #define GROUPINVITE_H
#include <cstdint>
#include <QByteArray> #include <QByteArray>
#include <QDateTime> #include <QDateTime>
#include <cstdint>
class GroupInvite class GroupInvite
{ {
public: public:
GroupInvite(int32_t friendID, uint8_t inviteType, const QByteArray& data); GroupInvite(int32_t friendID, uint8_t inviteType, const QByteArray& data);
bool operator ==(const GroupInvite& other) const; bool operator==(const GroupInvite& other) const;
int32_t getFriendId() const; int32_t getFriendId() const;
uint8_t getType() const; uint8_t getType() const;

View File

@ -19,8 +19,8 @@
#include "grouplist.h" #include "grouplist.h"
#include "group.h" #include "group.h"
#include <QHash>
#include <QDebug> #include <QDebug>
#include <QHash>
QHash<int, Group*> GroupList::groupList; QHash<int, Group*> GroupList::groupList;
@ -48,13 +48,12 @@ Group* GroupList::findGroup(int groupId)
void GroupList::removeGroup(int groupId, bool /*fake*/) void GroupList::removeGroup(int groupId, bool /*fake*/)
{ {
auto g_it = groupList.find(groupId); auto g_it = groupList.find(groupId);
if (g_it != groupList.end()) if (g_it != groupList.end()) {
{
groupList.erase(g_it); groupList.erase(g_it);
} }
} }
QList<Group *> GroupList::getAllGroups() QList<Group*> GroupList::getAllGroups()
{ {
QList<Group*> res; QList<Group*> res;

View File

@ -20,8 +20,10 @@
#ifndef GROUPLIST_H #ifndef GROUPLIST_H
#define GROUPLIST_H #define GROUPLIST_H
template <class A, class B> class QHash; template <class A, class B>
template <class T> class QList; class QHash;
template <class T>
class QList;
class Group; class Group;
class QString; class QString;

View File

@ -51,33 +51,26 @@ IPC::IPC()
// Every time it processes events it updates the global shared timestamp "lastProcessed" // Every time it processes events it updates the global shared timestamp "lastProcessed"
// If the timestamp isn't updated, that's a timeout and someone else can take ownership // If the timestamp isn't updated, that's a timeout and someone else can take ownership
// This is a safety measure, in case one of the clients crashes // This is a safety measure, in case one of the clients crashes
// If the owner exits normally, it can set the timestamp to 0 first to immediately give ownership // If the owner exits normally, it can set the timestamp to 0 first to immediately give
// ownership
std::default_random_engine randEngine((std::random_device())()); std::default_random_engine randEngine((std::random_device())());
std::uniform_int_distribution<uint64_t> distribution; std::uniform_int_distribution<uint64_t> distribution;
globalId = distribution(randEngine); globalId = distribution(randEngine);
qDebug() << "Our global IPC ID is " << globalId; qDebug() << "Our global IPC ID is " << globalId;
if (globalMemory.create(sizeof(IPCMemory))) if (globalMemory.create(sizeof(IPCMemory))) {
{ if (globalMemory.lock()) {
if (globalMemory.lock())
{
IPCMemory* mem = global(); IPCMemory* mem = global();
memset(mem, 0, sizeof(IPCMemory)); memset(mem, 0, sizeof(IPCMemory));
mem->globalId = globalId; mem->globalId = globalId;
mem->lastProcessed = time(0); mem->lastProcessed = time(0);
globalMemory.unlock(); globalMemory.unlock();
} } else {
else
{
qWarning() << "Couldn't lock to take ownership"; qWarning() << "Couldn't lock to take ownership";
} }
} } else if (globalMemory.attach()) {
else if (globalMemory.attach())
{
qDebug() << "Attaching to the global shared memory"; qDebug() << "Attaching to the global shared memory";
} } else {
else
{
qDebug() << "Failed to attach to the global shared memory, giving up"; qDebug() << "Failed to attach to the global shared memory, giving up";
return; // We won't be able to do any IPC without being attached, let's get outta here return; // We won't be able to do any IPC without being attached, let's get outta here
} }
@ -87,10 +80,8 @@ IPC::IPC()
IPC::~IPC() IPC::~IPC()
{ {
if (isCurrentOwner()) if (isCurrentOwner()) {
{ if (globalMemory.lock()) {
if (globalMemory.lock())
{
global()->globalId = 0; global()->globalId = 0;
globalMemory.unlock(); globalMemory.unlock();
} }
@ -113,7 +104,7 @@ IPC& IPC::getInstance()
* @param dest Settings::getCurrentProfileId() or 0 (main instance, default). * @param dest Settings::getCurrentProfileId() or 0 (main instance, default).
* @return Time the event finished. * @return Time the event finished.
*/ */
time_t IPC::postEvent(const QString &name, const QByteArray& data, uint32_t dest) time_t IPC::postEvent(const QString& name, const QByteArray& data, uint32_t dest)
{ {
QByteArray binName = name.toUtf8(); QByteArray binName = name.toUtf8();
if (binName.length() > (int32_t)sizeof(IPCEvent::name)) if (binName.length() > (int32_t)sizeof(IPCEvent::name))
@ -122,20 +113,17 @@ time_t IPC::postEvent(const QString &name, const QByteArray& data, uint32_t dest
if (data.length() > (int32_t)sizeof(IPCEvent::data)) if (data.length() > (int32_t)sizeof(IPCEvent::data))
return 0; return 0;
if (globalMemory.lock()) if (globalMemory.lock()) {
{
IPCEvent* evt = nullptr; IPCEvent* evt = nullptr;
IPCMemory* mem = global(); IPCMemory* mem = global();
time_t result = 0; time_t result = 0;
for (uint32_t i = 0; !evt && i < EVENT_QUEUE_SIZE; ++i) for (uint32_t i = 0; !evt && i < EVENT_QUEUE_SIZE; ++i) {
{
if (mem->events[i].posted == 0) if (mem->events[i].posted == 0)
evt = &mem->events[i]; evt = &mem->events[i];
} }
if (evt) if (evt) {
{
memset(evt, 0, sizeof(IPCEvent)); memset(evt, 0, sizeof(IPCEvent));
memcpy(evt->name, binName.constData(), binName.length()); memcpy(evt->name, binName.constData(), binName.length());
memcpy(evt->data, data.constData(), data.length()); memcpy(evt->data, data.constData(), data.length());
@ -146,8 +134,7 @@ time_t IPC::postEvent(const QString &name, const QByteArray& data, uint32_t dest
} }
globalMemory.unlock(); globalMemory.unlock();
return result; return result;
} } else
else
qDebug() << "Failed to lock in postEvent()"; qDebug() << "Failed to lock in postEvent()";
return 0; return 0;
@ -155,11 +142,9 @@ time_t IPC::postEvent(const QString &name, const QByteArray& data, uint32_t dest
bool IPC::isCurrentOwner() bool IPC::isCurrentOwner()
{ {
if (globalMemory.lock()) if (globalMemory.lock()) {
{
void* data = globalMemory.data(); void* data = globalMemory.data();
if (!data) if (!data) {
{
qWarning() << "isCurrentOwner failed to access the memory, returning false"; qWarning() << "isCurrentOwner failed to access the memory, returning false";
globalMemory.unlock(); globalMemory.unlock();
return false; return false;
@ -167,9 +152,7 @@ bool IPC::isCurrentOwner()
bool isOwner = ((*(uint64_t*)data) == globalId); bool isOwner = ((*(uint64_t*)data) == globalId);
globalMemory.unlock(); globalMemory.unlock();
return isOwner; return isOwner;
} } else {
else
{
qWarning() << "isCurrentOwner failed to lock, returning false"; qWarning() << "isCurrentOwner failed to lock, returning false";
return false; return false;
} }
@ -179,7 +162,7 @@ bool IPC::isCurrentOwner()
* @brief Register a handler for an IPC event * @brief Register a handler for an IPC event
* @param handler The handler callback. Should not block for more than a second, at worst * @param handler The handler callback. Should not block for more than a second, at worst
*/ */
void IPC::registerEventHandler(const QString &name, IPCEventHandler handler) void IPC::registerEventHandler(const QString& name, IPCEventHandler handler)
{ {
eventHandlers[name] = handler; eventHandlers[name] = handler;
} }
@ -187,15 +170,11 @@ void IPC::registerEventHandler(const QString &name, IPCEventHandler handler)
bool IPC::isEventAccepted(time_t time) bool IPC::isEventAccepted(time_t time)
{ {
bool result = false; bool result = false;
if (globalMemory.lock()) if (globalMemory.lock()) {
{ if (difftime(global()->lastProcessed, time) > 0) {
if (difftime(global()->lastProcessed, time) > 0)
{
IPCMemory* mem = global(); IPCMemory* mem = global();
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
{ if (mem->events[i].posted == time && mem->events[i].processed) {
if (mem->events[i].posted == time && mem->events[i].processed)
{
result = mem->events[i].accepted; result = mem->events[i].accepted;
break; break;
} }
@ -206,7 +185,7 @@ bool IPC::isEventAccepted(time_t time)
return result; return result;
} }
bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout/*=-1*/) bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout /*=-1*/)
{ {
bool result = false; bool result = false;
time_t start = time(0); time_t start = time(0);
@ -226,23 +205,22 @@ bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout/*=-1*/)
* @brief Only called when global memory IS LOCKED. * @brief Only called when global memory IS LOCKED.
* @return nullptr if no evnts present, IPC event otherwise * @return nullptr if no evnts present, IPC event otherwise
*/ */
IPC::IPCEvent *IPC::fetchEvent() IPC::IPCEvent* IPC::fetchEvent()
{ {
IPCMemory* mem = global(); IPCMemory* mem = global();
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
{
IPCEvent* evt = &mem->events[i]; IPCEvent* evt = &mem->events[i];
// Garbage-collect events that were not processed in EVENT_GC_TIMEOUT // Garbage-collect events that were not processed in EVENT_GC_TIMEOUT
// and events that were processed and EVENT_GC_TIMEOUT passed after // and events that were processed and EVENT_GC_TIMEOUT passed after
// so sending instance has time to react to those events. // so sending instance has time to react to those events.
if ((evt->processed && difftime(time(0), evt->processed) > EVENT_GC_TIMEOUT) || if ((evt->processed && difftime(time(0), evt->processed) > EVENT_GC_TIMEOUT)
(!evt->processed && difftime(time(0), evt->posted) > EVENT_GC_TIMEOUT)) || (!evt->processed && difftime(time(0), evt->posted) > EVENT_GC_TIMEOUT))
memset(evt, 0, sizeof(IPCEvent)); memset(evt, 0, sizeof(IPCEvent));
if (evt->posted && !evt->processed && evt->sender != getpid() if (evt->posted && !evt->processed && evt->sender != getpid()
&& (evt->dest == Settings::getInstance().getCurrentProfileId() && (evt->dest == Settings::getInstance().getCurrentProfileId()
|| (evt->dest == 0 && isCurrentOwner()))) || (evt->dest == 0 && isCurrentOwner())))
return evt; return evt;
} }
@ -255,10 +233,8 @@ bool IPC::runEventHandler(IPCEventHandler handler, const QByteArray& arg)
if (QThread::currentThread() == qApp->thread()) if (QThread::currentThread() == qApp->thread())
result = handler(arg); result = handler(arg);
else else
QMetaObject::invokeMethod(this, "runEventHandler", QMetaObject::invokeMethod(this, "runEventHandler", Qt::BlockingQueuedConnection,
Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(IPCEventHandler, handler),
Q_RETURN_ARG(bool, result),
Q_ARG(IPCEventHandler, handler),
Q_ARG(const QByteArray&, arg)); Q_ARG(const QByteArray&, arg));
return result; return result;
@ -266,21 +242,18 @@ bool IPC::runEventHandler(IPCEventHandler handler, const QByteArray& arg)
void IPC::processEvents() void IPC::processEvents()
{ {
if (globalMemory.lock()) if (globalMemory.lock()) {
{
IPCMemory* mem = global(); IPCMemory* mem = global();
if (mem->globalId == globalId) if (mem->globalId == globalId) {
{
// We're the owner, let's process those events // We're the owner, let's process those events
mem->lastProcessed = time(0); mem->lastProcessed = time(0);
} } else {
else // Only the owner processes events. But if the previous owner's dead, we can take
{ // ownership now
// Only the owner processes events. But if the previous owner's dead, we can take ownership now if (difftime(time(0), mem->lastProcessed) >= OWNERSHIP_TIMEOUT_S) {
if (difftime(time(0), mem->lastProcessed) >= OWNERSHIP_TIMEOUT_S) qDebug() << "Previous owner timed out, taking ownership" << mem->globalId << "->"
{ << globalId;
qDebug() << "Previous owner timed out, taking ownership" << mem->globalId << "->" << globalId;
// Ignore events that were not meant for this instance // Ignore events that were not meant for this instance
memset(mem, 0, sizeof(IPCMemory)); memset(mem, 0, sizeof(IPCMemory));
mem->globalId = globalId; mem->globalId = globalId;
@ -289,27 +262,22 @@ void IPC::processEvents()
// Non-main instance is limited to events destined for specific profile it runs // Non-main instance is limited to events destined for specific profile it runs
} }
while (IPCEvent* evt = fetchEvent()) while (IPCEvent* evt = fetchEvent()) {
{
QString name = QString::fromUtf8(evt->name); QString name = QString::fromUtf8(evt->name);
auto it = eventHandlers.find(name); auto it = eventHandlers.find(name);
if (it != eventHandlers.end()) if (it != eventHandlers.end()) {
{
qDebug() << "Processing event: " << name << ":" << evt->posted << "=" << evt->accepted; qDebug() << "Processing event: " << name << ":" << evt->posted << "=" << evt->accepted;
evt->accepted = runEventHandler(it.value(), evt->data); evt->accepted = runEventHandler(it.value(), evt->data);
if (evt->dest == 0) if (evt->dest == 0) {
{ // Global events should be processed only by instance that accepted event.
// Global events should be processed only by instance that accepted event. Otherwise global // Otherwise global
// event would be consumed by very first instance that gets to check it. // event would be consumed by very first instance that gets to check it.
if (evt->accepted) if (evt->accepted)
evt->processed = time(0); evt->processed = time(0);
} } else {
else
{
evt->processed = time(0); evt->processed = time(0);
} }
} }
} }
globalMemory.unlock(); globalMemory.unlock();
@ -317,7 +285,7 @@ void IPC::processEvents()
timer.start(); timer.start();
} }
IPC::IPCMemory *IPC::global() IPC::IPCMemory* IPC::global()
{ {
return static_cast<IPCMemory*>(globalMemory.data()); return static_cast<IPCMemory*>(globalMemory.data());
} }

View File

@ -21,15 +21,15 @@
#ifndef IPC_H #ifndef IPC_H
#define IPC_H #define IPC_H
#include <ctime>
#include <functional>
#include <QMap> #include <QMap>
#include <QObject> #include <QObject>
#include <QSharedMemory> #include <QSharedMemory>
#include <QTimer> #include <QTimer>
#include <QVector> #include <QVector>
#include <ctime>
#include <functional>
using IPCEventHandler = std::function<bool (const QByteArray&)>; using IPCEventHandler = std::function<bool(const QByteArray&)>;
#define IPC_PROTOCOL_VERSION "2" #define IPC_PROTOCOL_VERSION "2"
@ -37,6 +37,7 @@ class IPC : public QObject
{ {
Q_OBJECT Q_OBJECT
IPC(); IPC();
protected: protected:
static const int EVENT_TIMER_MS = 1000; static const int EVENT_TIMER_MS = 1000;
static const int EVENT_GC_TIMEOUT = 5; static const int EVENT_GC_TIMEOUT = 5;
@ -69,11 +70,11 @@ public:
IPCEvent events[IPC::EVENT_QUEUE_SIZE]; IPCEvent events[IPC::EVENT_QUEUE_SIZE];
}; };
time_t postEvent(const QString& name, const QByteArray &data=QByteArray(), uint32_t dest=0); time_t postEvent(const QString& name, const QByteArray& data = QByteArray(), uint32_t dest = 0);
bool isCurrentOwner(); bool isCurrentOwner();
void registerEventHandler(const QString& name, IPCEventHandler handler); void registerEventHandler(const QString& name, IPCEventHandler handler);
bool isEventAccepted(time_t time); bool isEventAccepted(time_t time);
bool waitUntilAccepted(time_t time, int32_t timeout=-1); bool waitUntilAccepted(time_t time, int32_t timeout = -1);
protected slots: protected slots:
void processEvents(); void processEvents();

View File

@ -17,25 +17,25 @@
along with qTox. If not, see <http://www.gnu.org/licenses/>. along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "widget/widget.h"
#include "persistence/settings.h" #include "persistence/settings.h"
#include "src/nexus.h"
#include "src/ipc.h" #include "src/ipc.h"
#include "src/net/toxuri.h"
#include "src/net/autoupdate.h" #include "src/net/autoupdate.h"
#include "src/persistence/toxsave.h" #include "src/net/toxuri.h"
#include "src/nexus.h"
#include "src/persistence/profile.h" #include "src/persistence/profile.h"
#include "src/persistence/toxsave.h"
#include "src/video/camerasource.h"
#include "src/widget/loginscreen.h" #include "src/widget/loginscreen.h"
#include "src/widget/translator.h" #include "src/widget/translator.h"
#include "src/video/camerasource.h" #include "widget/widget.h"
#include <QApplication> #include <QApplication>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QMutex>
#include <QFontDatabase> #include <QFontDatabase>
#include <QMutex>
#include <QMutexLocker> #include <QMutexLocker>
#include <sodium.h> #include <sodium.h>
@ -47,7 +47,8 @@
#ifdef LOG_TO_FILE #ifdef LOG_TO_FILE
static QAtomicPointer<FILE> logFileFile = nullptr; static QAtomicPointer<FILE> logFileFile = nullptr;
static QList<QByteArray>* logBuffer = new QList<QByteArray>(); //Store log messages until log file opened static QList<QByteArray>* logBuffer =
new QList<QByteArray>(); // Store log messages until log file opened
QMutex* logBufferMutex = new QMutex(); QMutex* logBufferMutex = new QMutex();
#endif #endif
@ -55,7 +56,7 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QSt
{ {
// Silence qWarning spam due to bug in QTextBrowser (trying to open a file for base64 images) // Silence qWarning spam due to bug in QTextBrowser (trying to open a file for base64 images)
if (ctxt.function == QString("virtual bool QFSFileEngine::open(QIODevice::OpenMode)") if (ctxt.function == QString("virtual bool QFSFileEngine::open(QIODevice::OpenMode)")
&& msg == QString("QFSFileEngine::open: No file name specified")) && msg == QString("QFSFileEngine::open: No file name specified"))
return; return;
QString file = ctxt.file; QString file = ctxt.file;
@ -63,31 +64,29 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QSt
// nullptr in release builds. // nullptr in release builds.
QString path = QString(__FILE__); QString path = QString(__FILE__);
path = path.left(path.lastIndexOf('/') + 1); path = path.left(path.lastIndexOf('/') + 1);
if (file.startsWith(path)) if (file.startsWith(path)) {
{
file = file.mid(path.length()); file = file.mid(path.length());
} }
// Time should be in UTC to save user privacy on log sharing // Time should be in UTC to save user privacy on log sharing
QTime time = QDateTime::currentDateTime().toUTC().time(); QTime time = QDateTime::currentDateTime().toUTC().time();
QString LogMsg = QString("[%1 UTC] %2:%3 : ") QString LogMsg =
.arg(time.toString("HH:mm:ss.zzz")).arg(file).arg(ctxt.line); QString("[%1 UTC] %2:%3 : ").arg(time.toString("HH:mm:ss.zzz")).arg(file).arg(ctxt.line);
switch (type) switch (type) {
{ case QtDebugMsg:
case QtDebugMsg: LogMsg += "Debug";
LogMsg += "Debug"; break;
break; case QtWarningMsg:
case QtWarningMsg: LogMsg += "Warning";
LogMsg += "Warning"; break;
break; case QtCriticalMsg:
case QtCriticalMsg: LogMsg += "Critical";
LogMsg += "Critical"; break;
break; case QtFatalMsg:
case QtFatalMsg: LogMsg += "Fatal";
LogMsg += "Fatal"; break;
break; default:
default: break;
break;
} }
LogMsg += ": " + msg + "\n"; LogMsg += ": " + msg + "\n";
@ -95,25 +94,21 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QSt
fwrite(LogMsgBytes.constData(), 1, LogMsgBytes.size(), stderr); fwrite(LogMsgBytes.constData(), 1, LogMsgBytes.size(), stderr);
#ifdef LOG_TO_FILE #ifdef LOG_TO_FILE
FILE * logFilePtr = logFileFile.load(); // atomically load the file pointer FILE* logFilePtr = logFileFile.load(); // atomically load the file pointer
if (!logFilePtr) if (!logFilePtr) {
{
logBufferMutex->lock(); logBufferMutex->lock();
if (logBuffer) if (logBuffer)
logBuffer->append(LogMsgBytes); logBuffer->append(LogMsgBytes);
logBufferMutex->unlock(); logBufferMutex->unlock();
} } else {
else
{
logBufferMutex->lock(); logBufferMutex->lock();
if (logBuffer) if (logBuffer) {
{
// empty logBuffer to file // empty logBuffer to file
foreach (QByteArray msg, *logBuffer) foreach (QByteArray msg, *logBuffer)
fwrite(msg.constData(), 1, msg.size(), logFilePtr); fwrite(msg.constData(), 1, msg.size(), logFilePtr);
delete logBuffer; // no longer needed delete logBuffer; // no longer needed
logBuffer = nullptr; logBuffer = nullptr;
} }
logBufferMutex->unlock(); logBufferMutex->unlock();
@ -124,7 +119,7 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QSt
#endif #endif
} }
int main(int argc, char *argv[]) int main(int argc, char* argv[])
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
@ -141,7 +136,7 @@ int main(int argc, char *argv[])
#if defined(Q_OS_OSX) #if defined(Q_OS_OSX)
// TODO: Add setting to enable this feature. // TODO: Add setting to enable this feature.
//osx::moveToAppFolder(); // osx::moveToAppFolder();
osx::migrateProfiles(); osx::migrateProfiles();
#endif #endif
@ -151,11 +146,14 @@ int main(int argc, char *argv[])
// Process arguments // Process arguments
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("qTox, version: " + QString(GIT_VERSION) + "\nBuilt: " + __TIME__ + " " + __DATE__); parser.setApplicationDescription("qTox, version: " + QString(GIT_VERSION) + "\nBuilt: "
+ __TIME__ + " " + __DATE__);
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
parser.addPositionalArgument("uri", QObject::tr("Tox URI to parse")); parser.addPositionalArgument("uri", QObject::tr("Tox URI to parse"));
parser.addOption(QCommandLineOption("p", QObject::tr("Starts new instance and loads specified profile."), QObject::tr("profile"))); parser.addOption(
QCommandLineOption("p", QObject::tr("Starts new instance and loads specified profile."),
QObject::tr("profile")));
parser.process(a); parser.process(a);
IPC& ipc = IPC::getInstance(); IPC& ipc = IPC::getInstance();
@ -171,18 +169,17 @@ int main(int argc, char *argv[])
QDir(logFileDir).mkpath("."); QDir(logFileDir).mkpath(".");
QString logfile = logFileDir + "qtox.log"; QString logfile = logFileDir + "qtox.log";
FILE * mainLogFilePtr = fopen(logfile.toLocal8Bit().constData(), "a"); FILE* mainLogFilePtr = fopen(logfile.toLocal8Bit().constData(), "a");
// Trim log file if over 1MB // Trim log file if over 1MB
if (QFileInfo(logfile).size() > 1000000) if (QFileInfo(logfile).size() > 1000000) {
{
qDebug() << "Log file over 1MB, rotating..."; qDebug() << "Log file over 1MB, rotating...";
// close old logfile (need for windows) // close old logfile (need for windows)
if (mainLogFilePtr) if (mainLogFilePtr)
fclose(mainLogFilePtr); fclose(mainLogFilePtr);
QDir dir (logFileDir); QDir dir(logFileDir);
// Check if log.1 already exists, and if so, delete it // Check if log.1 already exists, and if so, delete it
if (dir.remove(logFileDir + "qtox.log.1")) if (dir.remove(logFileDir + "qtox.log.1"))
@ -200,7 +197,7 @@ int main(int argc, char *argv[])
if (!mainLogFilePtr) if (!mainLogFilePtr)
qCritical() << "Couldn't open logfile" << logfile; qCritical() << "Couldn't open logfile" << logfile;
logFileFile.store(mainLogFilePtr); // atomically set the logFile logFileFile.store(mainLogFilePtr); // atomically set the logFile
#endif #endif
// Windows platform plugins DLL hell fix // Windows platform plugins DLL hell fix
@ -213,7 +210,7 @@ int main(int argc, char *argv[])
// Install Unicode 6.1 supporting font // Install Unicode 6.1 supporting font
QFontDatabase::addApplicationFont("://DejaVuSans.ttf"); QFontDatabase::addApplicationFont("://DejaVuSans.ttf");
// Check whether we have an update waiting to be installed // Check whether we have an update waiting to be installed
#if AUTOUPDATE_ENABLED #if AUTOUPDATE_ENABLED
if (AutoUpdater::isLocalUpdateReady()) if (AutoUpdater::isLocalUpdateReady())
AutoUpdater::installLocalUpdate(); ///< NORETURN AutoUpdater::installLocalUpdate(); ///< NORETURN
@ -229,64 +226,49 @@ int main(int argc, char *argv[])
uint32_t ipcDest = 0; uint32_t ipcDest = 0;
QString eventType, firstParam; QString eventType, firstParam;
if (parser.isSet("p")) if (parser.isSet("p")) {
{
profileName = parser.value("p"); profileName = parser.value("p");
if (!Profile::exists(profileName)) if (!Profile::exists(profileName)) {
{ qCritical() << "-p profile" << profileName + ".tox"
qCritical() << "-p profile" << profileName + ".tox" << "doesn't exist"; << "doesn't exist";
return EXIT_FAILURE; return EXIT_FAILURE;
} }
ipcDest = Settings::makeProfileId(profileName); ipcDest = Settings::makeProfileId(profileName);
autoLogin = true; autoLogin = true;
} } else {
else
{
profileName = Settings::getInstance().getCurrentProfile(); profileName = Settings::getInstance().getCurrentProfile();
} }
if (parser.positionalArguments().size() == 0) if (parser.positionalArguments().size() == 0) {
{
eventType = "activate"; eventType = "activate";
} } else {
else
{
firstParam = parser.positionalArguments()[0]; firstParam = parser.positionalArguments()[0];
// Tox URIs. If there's already another qTox instance running, we ask it to handle the URI and we exit // Tox URIs. If there's already another qTox instance running, we ask it to handle the URI
// and we exit
// Otherwise we start a new qTox instance and process it ourselves // Otherwise we start a new qTox instance and process it ourselves
if (firstParam.startsWith("tox:")) if (firstParam.startsWith("tox:")) {
{
eventType = "uri"; eventType = "uri";
} } else if (firstParam.endsWith(".tox")) {
else if (firstParam.endsWith(".tox"))
{
eventType = "save"; eventType = "save";
} } else {
else
{
qCritical() << "Invalid argument"; qCritical() << "Invalid argument";
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
if (!ipc.isCurrentOwner()) if (!ipc.isCurrentOwner()) {
{
time_t event = ipc.postEvent(eventType, firstParam.toUtf8(), ipcDest); time_t event = ipc.postEvent(eventType, firstParam.toUtf8(), ipcDest);
// If someone else processed it, we're done here, no need to actually start qTox // If someone else processed it, we're done here, no need to actually start qTox
if (ipc.waitUntilAccepted(event, 2)) if (ipc.waitUntilAccepted(event, 2)) {
{
qDebug() << "Event" << eventType << "was handled by other client."; qDebug() << "Event" << eventType << "was handled by other client.";
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
} }
// Autologin // Autologin
if (autoLogin) if (autoLogin) {
{ if (Profile::exists(profileName)) {
if (Profile::exists(profileName)) if (!Profile::isEncrypted(profileName)) {
{
if (!Profile::isEncrypted(profileName))
{
Profile* profile = Profile::loadProfile(profileName); Profile* profile = Profile::loadProfile(profileName);
if (profile) if (profile)
Nexus::getInstance().setProfile(profile); Nexus::getInstance().setProfile(profile);
@ -315,7 +297,7 @@ int main(int argc, char *argv[])
qDebug() << "Clean exit with status" << errorcode; qDebug() << "Clean exit with status" << errorcode;
#ifdef LOG_TO_FILE #ifdef LOG_TO_FILE
logFileFile.store(nullptr); // atomically disable logging to file logFileFile.store(nullptr); // atomically disable logging to file
fclose(mainLogFilePtr); fclose(mainLogFilePtr);
#endif #endif
return errorcode; return errorcode;

View File

@ -18,32 +18,34 @@
*/ */
#include "src/net/autoupdate.h" #include "src/net/autoupdate.h"
#include "src/nexus.h"
#include "src/persistence/serialize.h" #include "src/persistence/serialize.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include "src/widget/widget.h"
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/nexus.h" #include "src/widget/widget.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QCoreApplication> #include <QCoreApplication>
#include <QFile>
#include <QDir> #include <QDir>
#include <QProcess> #include <QFile>
#include <QtConcurrent/QtConcurrent>
#include <QMessageBox> #include <QMessageBox>
#include <QMutexLocker> #include <QMutexLocker>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QProcess>
#include <QtConcurrent/QtConcurrent>
#include <iostream> #include <iostream>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h>
#include <shellapi.h> #include <shellapi.h>
#include <windows.h>
#endif #endif
/** /**
* @file autoupdate.cpp * @file autoupdate.cpp
* *
* For now we only support auto updates on Windows and OS X, although extending it is not a technical issue. * For now we only support auto updates on Windows and OS X, although extending it is not a
* Linux users are expected to use their package managers or update manually through official channels. * technical issue.
* Linux users are expected to use their package managers or update manually through official
* channels.
*/ */
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -55,22 +57,24 @@ const QString AutoUpdater::platform = "win32";
const QString AutoUpdater::updaterBin = "qtox-updater.exe"; const QString AutoUpdater::updaterBin = "qtox-updater.exe";
const QString AutoUpdater::updateServer = "https://qtox-win.pkg.tox.chat"; const QString AutoUpdater::updateServer = "https://qtox-win.pkg.tox.chat";
unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES] = unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES] = {0x20, 0x89, 0x39, 0xaa, 0x9a, 0xe8,
{ 0xb5, 0x21, 0x0e, 0xac, 0x02, 0xa9,
0x20, 0x89, 0x39, 0xaa, 0x9a, 0xe8, 0xb5, 0x21, 0x0e, 0xac, 0x02, 0xa9, 0xc4, 0x92, 0xd9, 0xa2, 0xc4, 0x92, 0xd9, 0xa2, 0x17, 0x83,
0x17, 0x83, 0xbd, 0x78, 0x0a, 0xda, 0x33, 0xcd, 0xa5, 0xc6, 0x44, 0xc7, 0xfc, 0xed, 0x00, 0x13 0xbd, 0x78, 0x0a, 0xda, 0x33, 0xcd,
}; 0xa5, 0xc6, 0x44, 0xc7, 0xfc, 0xed,
0x00, 0x13};
#elif defined(Q_OS_OSX) #elif defined(Q_OS_OSX)
const QString AutoUpdater::platform = "osx"; const QString AutoUpdater::platform = "osx";
const QString AutoUpdater::updaterBin = "/Applications/qtox.app/Contents/MacOS/updater"; const QString AutoUpdater::updaterBin = "/Applications/qtox.app/Contents/MacOS/updater";
const QString AutoUpdater::updateServer = "https://dist-build.tox.im"; const QString AutoUpdater::updateServer = "https://dist-build.tox.im";
unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES] = unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00};
#else #else
const QString AutoUpdater::platform; const QString AutoUpdater::platform;
@ -122,9 +126,12 @@ unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES];
* @brief No, we can't just make the QString atomic * @brief No, we can't just make the QString atomic
*/ */
const QString AutoUpdater::checkURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/version"; const QString AutoUpdater::checkURI =
const QString AutoUpdater::flistURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/flist"; AutoUpdater::updateServer + "/qtox/" + AutoUpdater::platform + "/version";
const QString AutoUpdater::filesURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/files/"; const QString AutoUpdater::flistURI =
AutoUpdater::updateServer + "/qtox/" + AutoUpdater::platform + "/flist";
const QString AutoUpdater::filesURI =
AutoUpdater::updateServer + "/qtox/" + AutoUpdater::platform + "/files/";
std::atomic_bool AutoUpdater::abortFlag{false}; std::atomic_bool AutoUpdater::abortFlag{false};
std::atomic_bool AutoUpdater::isDownloadingUpdate{false}; std::atomic_bool AutoUpdater::isDownloadingUpdate{false};
std::atomic<float> AutoUpdater::progressValue{0}; std::atomic<float> AutoUpdater::progressValue{0};
@ -149,7 +156,8 @@ bool AutoUpdater::isUpdateAvailable()
if (isDownloadingUpdate) if (isDownloadingUpdate)
return false; return false;
QString updaterPath = updaterBin.startsWith('/') ? updaterBin : qApp->applicationDirPath()+'/'+updaterBin; QString updaterPath =
updaterBin.startsWith('/') ? updaterBin : qApp->applicationDirPath() + '/' + updaterBin;
if (!QFile::exists(updaterPath)) if (!QFile::exists(updaterPath))
return false; return false;
@ -175,18 +183,16 @@ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion()
if (abortFlag) if (abortFlag)
return versionInfo; return versionInfo;
QNetworkAccessManager *manager = new QNetworkAccessManager; QNetworkAccessManager* manager = new QNetworkAccessManager;
manager->setProxy(Settings::getInstance().getProxy()); manager->setProxy(Settings::getInstance().getProxy());
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(checkURI))); QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(checkURI)));
while (!reply->isFinished()) while (!reply->isFinished()) {
{
if (abortFlag) if (abortFlag)
return versionInfo; return versionInfo;
qApp->processEvents(); qApp->processEvents();
} }
if (reply->error() != QNetworkReply::NoError) if (reply->error() != QNetworkReply::NoError) {
{
qWarning() << "getUpdateVersion: network error: " + reply->errorString(); qWarning() << "getUpdateVersion: network error: " + reply->errorString();
reply->deleteLater(); reply->deleteLater();
manager->deleteLater(); manager->deleteLater();
@ -196,12 +202,11 @@ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion()
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
reply->deleteLater(); reply->deleteLater();
manager->deleteLater(); manager->deleteLater();
if (data.size() < (int)(1+crypto_sign_BYTES)) if (data.size() < (int)(1 + crypto_sign_BYTES))
return versionInfo; return versionInfo;
// Check updater protocol version // Check updater protocol version
if ((int)data[0] != '3') if ((int)data[0] != '3') {
{
qWarning() << "getUpdateVersion: Bad version " << (uint8_t)data[0]; qWarning() << "getUpdateVersion: Bad version " << (uint8_t)data[0];
return versionInfo; return versionInfo;
} }
@ -209,20 +214,19 @@ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion()
// Check the signature // Check the signature
QByteArray sigData = data.mid(1, crypto_sign_BYTES); QByteArray sigData = data.mid(1, crypto_sign_BYTES);
unsigned char* sig = (unsigned char*)sigData.data(); unsigned char* sig = (unsigned char*)sigData.data();
QByteArray msgData = data.mid(1+crypto_sign_BYTES); QByteArray msgData = data.mid(1 + crypto_sign_BYTES);
unsigned char* msg = (unsigned char*)msgData.data(); unsigned char* msg = (unsigned char*)msgData.data();
if (crypto_sign_verify_detached(sig, msg, msgData.size(), key) != 0) if (crypto_sign_verify_detached(sig, msg, msgData.size(), key) != 0) {
{ qCritical() << "getUpdateVersion: RECEIVED FORGED VERSION FILE FROM " << updateServer;
qCritical() << "getUpdateVersion: RECEIVED FORGED VERSION FILE FROM "<<updateServer;
return versionInfo; return versionInfo;
} }
int sepPos = msgData.indexOf('!'); int sepPos = msgData.indexOf('!');
versionInfo.timestamp = QString(msgData.left(sepPos)).toInt(); versionInfo.timestamp = QString(msgData.left(sepPos)).toInt();
versionInfo.versionString = msgData.mid(sepPos+1); versionInfo.versionString = msgData.mid(sepPos + 1);
qDebug() << "timestamp:"<<versionInfo.timestamp << ", str:"<<versionInfo.versionString; qDebug() << "timestamp:" << versionInfo.timestamp << ", str:" << versionInfo.versionString;
return versionInfo; return versionInfo;
} }
@ -236,32 +240,27 @@ QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseFlist(QByteArray flistData)
{ {
QList<UpdateFileMeta> flist; QList<UpdateFileMeta> flist;
if (flistData.isEmpty()) if (flistData.isEmpty()) {
{
qWarning() << "parseflist: Empty data"; qWarning() << "parseflist: Empty data";
return flist; return flist;
} }
// Check version // Check version
if (flistData[0] != '1') if (flistData[0] != '1') {
{ qWarning() << "parseflist: Bad version " << (uint8_t)flistData[0];
qWarning() << "parseflist: Bad version "<<(uint8_t)flistData[0];
return flist; return flist;
} }
flistData = flistData.mid(1); flistData = flistData.mid(1);
// Check signature // Check signature
if (flistData.size() < (int)(crypto_sign_BYTES)) if (flistData.size() < (int)(crypto_sign_BYTES)) {
{
qWarning() << "parseflist: Truncated data"; qWarning() << "parseflist: Truncated data";
return flist; return flist;
} } else {
else
{
QByteArray msgData = flistData.mid(crypto_sign_BYTES); QByteArray msgData = flistData.mid(crypto_sign_BYTES);
unsigned char* msg = (unsigned char*)msgData.data(); unsigned char* msg = (unsigned char*)msgData.data();
if (crypto_sign_verify_detached((unsigned char*)flistData.data(), msg, msgData.size(), key) != 0) if (crypto_sign_verify_detached((unsigned char*)flistData.data(), msg, msgData.size(), key)
{ != 0) {
qCritical() << "parseflist: FORGED FLIST FILE"; qCritical() << "parseflist: FORGED FLIST FILE";
return flist; return flist;
} }
@ -269,8 +268,7 @@ QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseFlist(QByteArray flistData)
} }
// Parse. We assume no errors handling needed since the signature is valid. // Parse. We assume no errors handling needed since the signature is valid.
while (!flistData.isEmpty()) while (!flistData.isEmpty()) {
{
UpdateFileMeta newFile; UpdateFileMeta newFile;
memcpy(newFile.sig, flistData.data(), crypto_sign_BYTES); memcpy(newFile.sig, flistData.data(), crypto_sign_BYTES);
@ -300,18 +298,16 @@ QByteArray AutoUpdater::getUpdateFlist()
{ {
QByteArray flist; QByteArray flist;
QNetworkAccessManager *manager = new QNetworkAccessManager; QNetworkAccessManager* manager = new QNetworkAccessManager;
manager->setProxy(Settings::getInstance().getProxy()); manager->setProxy(Settings::getInstance().getProxy());
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(flistURI))); QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(flistURI)));
while (!reply->isFinished()) while (!reply->isFinished()) {
{
if (abortFlag) if (abortFlag)
return flist; return flist;
qApp->processEvents(); qApp->processEvents();
} }
if (reply->error() != QNetworkReply::NoError) if (reply->error() != QNetworkReply::NoError) {
{
qWarning() << "getUpdateFlist: network error: " + reply->errorString(); qWarning() << "getUpdateFlist: network error: " + reply->errorString();
reply->deleteLater(); reply->deleteLater();
manager->deleteLater(); manager->deleteLater();
@ -349,7 +345,7 @@ QList<AutoUpdater::UpdateFileMeta> AutoUpdater::genUpdateDiff(QList<UpdateFileMe
bool AutoUpdater::isUpToDate(AutoUpdater::UpdateFileMeta fileMeta) bool AutoUpdater::isUpToDate(AutoUpdater::UpdateFileMeta fileMeta)
{ {
QString appDir = qApp->applicationDirPath(); QString appDir = qApp->applicationDirPath();
QFile file(appDir+QDir::separator()+fileMeta.installpath); QFile file(appDir + QDir::separator() + fileMeta.installpath);
if (!file.open(QIODevice::ReadOnly)) if (!file.open(QIODevice::ReadOnly))
return false; return false;
@ -363,31 +359,31 @@ bool AutoUpdater::isUpToDate(AutoUpdater::UpdateFileMeta fileMeta)
/** /**
* @brief Tries to fetch the file from the update server. * @brief Tries to fetch the file from the update server.
* @note Note that a file with an empty but non-null QByteArray is not an error, merely a file of size 0. * @note Note that a file with an empty but non-null QByteArray is not an error, merely a file of
* size 0.
* @note Will try to follow qTox's proxy settings, may block and processEvents. * @note Will try to follow qTox's proxy settings, may block and processEvents.
* @param fileMeta Meta data fo file to update. * @param fileMeta Meta data fo file to update.
* @param progressCallback Callback function, which will connected with QNetworkReply::downloadProgress * @param progressCallback Callback function, which will connected with
* QNetworkReply::downloadProgress
* @return A file with a null QByteArray on error. * @return A file with a null QByteArray on error.
*/ */
AutoUpdater::UpdateFile AutoUpdater::getUpdateFile(UpdateFileMeta fileMeta, AutoUpdater::UpdateFile AutoUpdater::getUpdateFile(UpdateFileMeta fileMeta,
std::function<void(int,int)> progressCallback) std::function<void(int, int)> progressCallback)
{ {
UpdateFile file; UpdateFile file;
file.metadata = fileMeta; file.metadata = fileMeta;
QNetworkAccessManager *manager = new QNetworkAccessManager; QNetworkAccessManager* manager = new QNetworkAccessManager;
manager->setProxy(Settings::getInstance().getProxy()); manager->setProxy(Settings::getInstance().getProxy());
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(filesURI+fileMeta.id))); QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(filesURI + fileMeta.id)));
QObject::connect(reply, &QNetworkReply::downloadProgress, progressCallback); QObject::connect(reply, &QNetworkReply::downloadProgress, progressCallback);
while (!reply->isFinished()) while (!reply->isFinished()) {
{
if (abortFlag) if (abortFlag)
return file; return file;
qApp->processEvents(); qApp->processEvents();
} }
if (reply->error() != QNetworkReply::NoError) if (reply->error() != QNetworkReply::NoError) {
{
qWarning() << "getUpdateFile: network error: " + reply->errorString(); qWarning() << "getUpdateFile: network error: " + reply->errorString();
reply->deleteLater(); reply->deleteLater();
manager->deleteLater(); manager->deleteLater();
@ -413,7 +409,7 @@ bool AutoUpdater::downloadUpdate()
return false; return false;
bool expectFalse = false; bool expectFalse = false;
if (!isDownloadingUpdate.compare_exchange_strong(expectFalse,true)) if (!isDownloadingUpdate.compare_exchange_strong(expectFalse, true))
return false; return false;
// Get a list of files to update // Get a list of files to update
@ -424,8 +420,7 @@ bool AutoUpdater::downloadUpdate()
// Progress // Progress
progressValue = 0; progressValue = 0;
if (abortFlag) if (abortFlag) {
{
isDownloadingUpdate = false; isDownloadingUpdate = false;
return false; return false;
} }
@ -438,17 +433,15 @@ bool AutoUpdater::downloadUpdate()
if (!updateDir.exists()) if (!updateDir.exists())
QDir().mkdir(updateDirStr); QDir().mkdir(updateDirStr);
updateDir = QDir(updateDirStr); updateDir = QDir(updateDirStr);
if (!updateDir.exists()) if (!updateDir.exists()) {
{
qWarning() << "downloadUpdate: Can't create update directory, aborting..."; qWarning() << "downloadUpdate: Can't create update directory, aborting...";
isDownloadingUpdate = false; isDownloadingUpdate = false;
return false; return false;
} }
// Write the new flist for the updater // Write the new flist for the updater
QFile newFlistFile(updateDirStr+"flist"); QFile newFlistFile(updateDirStr + "flist");
if (!newFlistFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) if (!newFlistFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
{
qWarning() << "downloadUpdate: Can't save new flist file, aborting..."; qWarning() << "downloadUpdate: Can't save new flist file, aborting...";
isDownloadingUpdate = false; isDownloadingUpdate = false;
return false; return false;
@ -459,25 +452,21 @@ bool AutoUpdater::downloadUpdate()
progressValue = 1; progressValue = 1;
// Download and write each new file // Download and write each new file
for (UpdateFileMeta fileMeta : diff) for (UpdateFileMeta fileMeta : diff) {
{ float initialProgress = progressValue, step = 99. / diff.size();
float initialProgress = progressValue, step = 99./diff.size(); auto stepProgressCallback = [&](int current, int total) {
auto stepProgressCallback = [&](int current, int total) progressValue = initialProgress + step * (float)current / total;
{
progressValue = initialProgress + step * (float)current/total;
}; };
if (abortFlag) if (abortFlag) {
{
isDownloadingUpdate = false; isDownloadingUpdate = false;
return false; return false;
} }
// Skip files we already have // Skip files we already have
QFile fileFile(updateDirStr+fileMeta.installpath); QFile fileFile(updateDirStr + fileMeta.installpath);
if (fileFile.open(QIODevice::ReadOnly) && fileFile.size() == (qint64)fileMeta.size) if (fileFile.open(QIODevice::ReadOnly) && fileFile.size() == (qint64)fileMeta.size) {
{ qDebug() << "Skipping already downloaded file '" + fileMeta.installpath + "'";
qDebug() << "Skipping already downloaded file '" + fileMeta.installpath+ "'";
progressValue = initialProgress + step; progressValue = initialProgress + step;
continue; continue;
} }
@ -486,7 +475,7 @@ bool AutoUpdater::downloadUpdate()
qDebug() << "Downloading '" + fileMeta.installpath + "' ..."; qDebug() << "Downloading '" + fileMeta.installpath + "' ...";
// Create subdirs if necessary // Create subdirs if necessary
QString fileDirStr{QFileInfo(updateDirStr+fileMeta.installpath).absolutePath()}; QString fileDirStr{QFileInfo(updateDirStr + fileMeta.installpath).absolutePath()};
if (!QDir(fileDirStr).exists()) if (!QDir(fileDirStr).exists())
QDir().mkpath(fileDirStr); QDir().mkpath(fileDirStr);
@ -494,23 +483,21 @@ bool AutoUpdater::downloadUpdate()
UpdateFile file = getUpdateFile(fileMeta, stepProgressCallback); UpdateFile file = getUpdateFile(fileMeta, stepProgressCallback);
if (abortFlag) if (abortFlag)
goto fail; goto fail;
if (file.data.isNull()) if (file.data.isNull()) {
{
qCritical() << "downloadUpdate: Error downloading a file, aborting..."; qCritical() << "downloadUpdate: Error downloading a file, aborting...";
goto fail; goto fail;
} }
// Check signature // Check signature
if (crypto_sign_verify_detached(file.metadata.sig, (unsigned char*)file.data.data(), if (crypto_sign_verify_detached(file.metadata.sig, (unsigned char*)file.data.data(),
file.data.size(), key) != 0) file.data.size(), key)
{ != 0) {
qCritical() << "downloadUpdate: RECEIVED FORGED FILE, aborting..."; qCritical() << "downloadUpdate: RECEIVED FORGED FILE, aborting...";
goto fail; goto fail;
} }
// Save // Save
if (!fileFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) if (!fileFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
{
qCritical() << "downloadUpdate: Can't save new update file, aborting..."; qCritical() << "downloadUpdate: Can't save new update file, aborting...";
goto fail; goto fail;
} }
@ -556,7 +543,7 @@ bool AutoUpdater::isLocalUpdateReady()
return false; return false;
// Check that we have a flist and generate a diff // Check that we have a flist and generate a diff
QFile updateFlistFile(updateDirStr+"flist"); QFile updateFlistFile(updateDirStr + "flist");
if (!updateFlistFile.open(QIODevice::ReadOnly)) if (!updateFlistFile.open(QIODevice::ReadOnly))
return false; return false;
QByteArray updateFlistData = updateFlistFile.readAll(); QByteArray updateFlistData = updateFlistFile.readAll();
@ -566,12 +553,11 @@ bool AutoUpdater::isLocalUpdateReady()
QList<UpdateFileMeta> diff = genUpdateDiff(updateFlist); QList<UpdateFileMeta> diff = genUpdateDiff(updateFlist);
// Check that we have every file // Check that we have every file
for (UpdateFileMeta fileMeta : diff) for (UpdateFileMeta fileMeta : diff) {
{ if (!QFile::exists(updateDirStr + fileMeta.installpath))
if (!QFile::exists(updateDirStr+fileMeta.installpath))
return false; return false;
QFile f(updateDirStr+fileMeta.installpath); QFile f(updateDirStr + fileMeta.installpath);
if (f.size() != (int64_t)fileMeta.size) if (f.size() != (int64_t)fileMeta.size)
return false; return false;
} }
@ -591,8 +577,7 @@ void AutoUpdater::installLocalUpdate()
qDebug() << "About to start the qTox updater to install a local update"; qDebug() << "About to start the qTox updater to install a local update";
// Prepare to delete the update if we fail so we don't fail again. // Prepare to delete the update if we fail so we don't fail again.
auto failExit = []() auto failExit = []() {
{
qCritical() << "Failed to start the qTox updater, removing the update and exiting"; qCritical() << "Failed to start the qTox updater, removing the update and exiting";
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/"; QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
QDir(updateDirStr).removeRecursively(); QDir(updateDirStr).removeRecursively();
@ -603,17 +588,16 @@ void AutoUpdater::installLocalUpdate()
if (platform.isEmpty()) if (platform.isEmpty())
failExit(); failExit();
// Workaround QTBUG-7645 // Workaround QTBUG-7645
// QProcess fails silently when elevation is required instead of showing a UAC prompt on Win7/Vista // QProcess fails silently when elevation is required instead of showing a UAC prompt on Win7/Vista
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QString modulePath = qApp->applicationDirPath().replace('/', '\\'); QString modulePath = qApp->applicationDirPath().replace('/', '\\');
HINSTANCE result = ::ShellExecuteW(0, L"open", updaterBin.toStdWString().c_str(), HINSTANCE result = ::ShellExecuteW(0, L"open", updaterBin.toStdWString().c_str(), 0,
0, modulePath.toStdWString().c_str(), SW_SHOWNORMAL); modulePath.toStdWString().c_str(), SW_SHOWNORMAL);
if (result == (HINSTANCE)SE_ERR_ACCESSDENIED) if (result == (HINSTANCE)SE_ERR_ACCESSDENIED) {
{
// Requesting elevation // Requesting elevation
result = ::ShellExecuteW(0, L"runas", updaterBin.toStdWString().c_str(), result = ::ShellExecuteW(0, L"runas", updaterBin.toStdWString().c_str(), 0,
0, modulePath.toStdWString().c_str(), SW_SHOWNORMAL); modulePath.toStdWString().c_str(), SW_SHOWNORMAL);
} }
if (result <= (HINSTANCE)32) if (result <= (HINSTANCE)32)
failExit(); failExit();
@ -656,9 +640,7 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
QDir updateDir(updateDirStr); QDir updateDir(updateDirStr);
if (updateDir.exists() && QFile(updateDirStr + "flist").exists()) {
if (updateDir.exists() && QFile(updateDirStr+"flist").exists())
{
setProgressVersion(getUpdateVersion().versionString); setProgressVersion(getUpdateVersion().versionString);
downloadUpdate(); downloadUpdate();
return; return;
@ -668,16 +650,17 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
QString contentText = QObject::tr("An update is available, do you want to download it now?\n" QString contentText = QObject::tr("An update is available, do you want to download it now?\n"
"It will be installed when qTox restarts."); "It will be installed when qTox restarts.");
if (!newVersion.versionString.isEmpty()) if (!newVersion.versionString.isEmpty())
contentText += "\n\n" + QObject::tr("Version %1, %2").arg(newVersion.versionString, contentText +=
QDateTime::fromMSecsSinceEpoch(newVersion.timestamp*1000).toString()); "\n\n"
+ QObject::tr("Version %1, %2")
.arg(newVersion.versionString,
QDateTime::fromMSecsSinceEpoch(newVersion.timestamp * 1000).toString());
if (abortFlag) if (abortFlag)
return; return;
if (GUI::askQuestion(QObject::tr("Update", "The title of a message box"), if (GUI::askQuestion(QObject::tr("Update", "The title of a message box"), contentText, true, false)) {
contentText, true, false))
{
setProgressVersion(newVersion.versionString); setProgressVersion(newVersion.versionString);
GUI::showUpdateDownloadProgress(); GUI::showUpdateDownloadProgress();
downloadUpdate(); downloadUpdate();

View File

@ -21,12 +21,12 @@
#ifndef AUTOUPDATE_H #ifndef AUTOUPDATE_H
#define AUTOUPDATE_H #define AUTOUPDATE_H
#include <QString>
#include <QList> #include <QList>
#include <QMutex> #include <QMutex>
#include <sodium.h> #include <QString>
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <sodium.h>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#define AUTOUPDATE_ENABLED 1 #define AUTOUPDATE_ENABLED 1
@ -48,8 +48,7 @@ public:
bool operator==(const UpdateFileMeta& other) bool operator==(const UpdateFileMeta& other)
{ {
return (size == other.size return (size == other.size && id == other.id && installpath == other.installpath
&& id == other.id && installpath == other.installpath
&& memcmp(sig, other.sig, crypto_sign_BYTES) == 0); && memcmp(sig, other.sig, crypto_sign_BYTES) == 0);
} }
}; };
@ -72,7 +71,7 @@ public:
static VersionInfo getUpdateVersion(); static VersionInfo getUpdateVersion();
static bool downloadUpdate(); static bool downloadUpdate();
static bool isLocalUpdateReady(); static bool isLocalUpdateReady();
[[ noreturn ]] static void installLocalUpdate(); [[noreturn]] static void installLocalUpdate();
static void abortUpdates(); static void abortUpdates();
static QString getProgressVersion(); static QString getProgressVersion();
static int getProgressValue(); static int getProgressValue();
@ -82,7 +81,8 @@ protected:
static QByteArray getUpdateFlist(); static QByteArray getUpdateFlist();
static QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist); static QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist);
static bool isUpToDate(UpdateFileMeta file); static bool isUpToDate(UpdateFileMeta file);
static UpdateFile getUpdateFile(UpdateFileMeta fileMeta, std::function<void(int,int)> progressCallback); static UpdateFile getUpdateFile(UpdateFileMeta fileMeta,
std::function<void(int, int)> progressCallback);
static void checkUpdatesAsyncInteractiveWorker(); static void checkUpdatesAsyncInteractiveWorker();
static void setProgressVersion(QString version); static void setProgressVersion(QString version);

View File

@ -20,8 +20,8 @@
#include "avatarbroadcaster.h" #include "avatarbroadcaster.h"
#include "src/core/core.h" #include "src/core/core.h"
#include <QObject>
#include <QDebug> #include <QDebug>
#include <QObject>
/** /**
* @class AvatarBroadcaster * @class AvatarBroadcaster
@ -35,8 +35,7 @@ QByteArray AvatarBroadcaster::avatarData;
QMap<uint32_t, bool> AvatarBroadcaster::friendsSentTo; QMap<uint32_t, bool> AvatarBroadcaster::friendsSentTo;
static QMetaObject::Connection autoBroadcastConn; static QMetaObject::Connection autoBroadcastConn;
static auto autoBroadcast = [](uint32_t friendId, Status) static auto autoBroadcast = [](uint32_t friendId, Status) {
{
AvatarBroadcaster::sendAvatarTo(friendId); AvatarBroadcaster::sendAvatarTo(friendId);
}; };
@ -79,5 +78,6 @@ void AvatarBroadcaster::enableAutoBroadcast(bool state)
{ {
QObject::disconnect(autoBroadcastConn); QObject::disconnect(autoBroadcastConn);
if (state) if (state)
autoBroadcastConn = QObject::connect(Core::getInstance(), &Core::friendStatusChanged, autoBroadcast); autoBroadcastConn =
QObject::connect(Core::getInstance(), &Core::friendStatusChanged, autoBroadcast);
} }

View File

@ -27,7 +27,7 @@
class AvatarBroadcaster class AvatarBroadcaster
{ {
private: private:
AvatarBroadcaster()=delete; AvatarBroadcaster() = delete;
public: public:
static void setAvatar(QByteArray data); static void setAvatar(QByteArray data);

View File

@ -19,16 +19,16 @@
#include "toxme.h" #include "toxme.h"
#include "src/core/core.h" #include "src/core/core.h"
#include <src/persistence/settings.h> #include <QCoreApplication>
#include <QtDebug>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QCoreApplication>
#include <QThread> #include <QThread>
#include <QtDebug>
#include <ctime>
#include <sodium/crypto_box.h> #include <sodium/crypto_box.h>
#include <sodium/randombytes.h> #include <sodium/randombytes.h>
#include <src/persistence/settings.h>
#include <string> #include <string>
#include <ctime>
/** /**
* @class Toxme * @class Toxme
@ -38,7 +38,7 @@
* @note May process events while waiting for blocking calls * @note May process events while waiting for blocking calls
*/ */
QByteArray Toxme::makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error) QByteArray Toxme::makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError& error)
{ {
if (error) if (error)
return QByteArray(); return QByteArray();
@ -49,8 +49,7 @@ QByteArray Toxme::makeJsonRequest(QString url, QString json, QNetworkReply::Netw
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply* reply = netman.post(request, json.toUtf8()); QNetworkReply* reply = netman.post(request, json.toUtf8());
while (!reply->isFinished()) while (!reply->isFinished()) {
{
QThread::msleep(1); QThread::msleep(1);
qApp->processEvents(); qApp->processEvents();
} }
@ -60,7 +59,7 @@ QByteArray Toxme::makeJsonRequest(QString url, QString json, QNetworkReply::Netw
return result; return result;
} }
QByteArray Toxme::getServerPubkey(QString url, QNetworkReply::NetworkError &error) QByteArray Toxme::getServerPubkey(QString url, QNetworkReply::NetworkError& error)
{ {
if (error) if (error)
return QByteArray(); return QByteArray();
@ -72,15 +71,13 @@ QByteArray Toxme::getServerPubkey(QString url, QNetworkReply::NetworkError &erro
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply* reply = netman.get(request); QNetworkReply* reply = netman.get(request);
while (!reply->isFinished()) while (!reply->isFinished()) {
{
QThread::msleep(1); QThread::msleep(1);
qApp->processEvents(); qApp->processEvents();
} }
error = reply->error(); error = reply->error();
if (error) if (error) {
{
qWarning() << "getServerPubkey: A network error occured:" << reply->errorString(); qWarning() << "getServerPubkey: A network error occured:" << reply->errorString();
return QByteArray(); return QByteArray();
} }
@ -94,12 +91,12 @@ QByteArray Toxme::getServerPubkey(QString url, QNetworkReply::NetworkError &erro
int start = json.indexOf(pattern) + pattern.length(); int start = json.indexOf(pattern) + pattern.length();
int end = json.indexOf("\"", start); int end = json.indexOf("\"", start);
int pubkeySize = (end - start) / 2; int pubkeySize = (end - start) / 2;
QString rawKey = json.mid(start, pubkeySize*2); QString rawKey = json.mid(start, pubkeySize * 2);
QByteArray key; QByteArray key;
// I think, exist more easy way to convert key to ByteArray // I think, exist more easy way to convert key to ByteArray
for (int i = 0; i < pubkeySize; ++i) { for (int i = 0; i < pubkeySize; ++i) {
QString byte = rawKey.mid(i*2, 2); QString byte = rawKey.mid(i * 2, 2);
key[i] = byte.toInt(nullptr, 16); key[i] = byte.toInt(nullptr, 16);
} }
@ -109,8 +106,7 @@ QByteArray Toxme::getServerPubkey(QString url, QNetworkReply::NetworkError &erro
QByteArray Toxme::prepareEncryptedJson(QString url, int action, QString payload) QByteArray Toxme::prepareEncryptedJson(QString url, int action, QString payload)
{ {
QPair<QByteArray, QByteArray> keypair = Core::getInstance()->getKeypair(); QPair<QByteArray, QByteArray> keypair = Core::getInstance()->getKeypair();
if (keypair.first.isEmpty() || keypair.second.isEmpty()) if (keypair.first.isEmpty() || keypair.second.isEmpty()) {
{
qWarning() << "prepareEncryptedJson: Couldn't get our keypair, aborting"; qWarning() << "prepareEncryptedJson: Couldn't get our keypair, aborting";
return QByteArray(); return QByteArray();
} }
@ -124,12 +120,12 @@ QByteArray Toxme::prepareEncryptedJson(QString url, int action, QString payload)
randombytes((uint8_t*)nonce.data(), crypto_box_NONCEBYTES); randombytes((uint8_t*)nonce.data(), crypto_box_NONCEBYTES);
QByteArray payloadData = payload.toUtf8(); QByteArray payloadData = payload.toUtf8();
const size_t cypherlen = crypto_box_MACBYTES+payloadData.size(); const size_t cypherlen = crypto_box_MACBYTES + payloadData.size();
unsigned char* payloadEnc = new unsigned char[cypherlen]; unsigned char* payloadEnc = new unsigned char[cypherlen];
int cryptResult = crypto_box_easy(payloadEnc,(uint8_t*)payloadData.data(),payloadData.size(), int cryptResult = crypto_box_easy(payloadEnc, (uint8_t*)payloadData.data(), payloadData.size(),
(uint8_t*)nonce.data(),(unsigned char*)key.constData(), (uint8_t*)nonce.data(), (unsigned char*)key.constData(),
(uint8_t*)keypair.second.data()); (uint8_t*)keypair.second.data());
if (cryptResult != 0) // error if (cryptResult != 0) // error
return QByteArray(); return QByteArray();
@ -137,10 +133,13 @@ QByteArray Toxme::prepareEncryptedJson(QString url, int action, QString payload)
QByteArray payloadEncData(reinterpret_cast<char*>(payloadEnc), cypherlen); QByteArray payloadEncData(reinterpret_cast<char*>(payloadEnc), cypherlen);
delete[] payloadEnc; delete[] payloadEnc;
const QString json{"{\"action\":"+QString().setNum(action)+"," const QString json{"{\"action\":" + QString().setNum(action) + ","
"\"public_key\":\""+keypair.first.toHex()+"\"," "\"public_key\":\""
"\"encrypted\":\""+payloadEncData.toBase64()+"\"," + keypair.first.toHex() + "\","
"\"nonce\":\""+nonce.toBase64()+"\"}"}; "\"encrypted\":\""
+ payloadEncData.toBase64() + "\","
"\"nonce\":\""
+ nonce.toBase64() + "\"}"};
return json.toUtf8(); return json.toUtf8();
} }
@ -153,10 +152,10 @@ ToxId Toxme::lookup(QString address)
{ {
// JSON injection ? // JSON injection ?
address = address.trimmed(); address = address.trimmed();
address.replace('\\',"\\\\"); address.replace('\\', "\\\\");
address.replace('"',"\""); address.replace('"', "\"");
const QString json{"{\"action\":3,\"name\":\""+address+"\"}"}; const QString json{"{\"action\":3,\"name\":\"" + address + "\"}"};
QString apiUrl = "https://" + address.split(QLatin1Char('@')).last() + "/api"; QString apiUrl = "https://" + address.split(QLatin1Char('@')).last() + "/api";
QNetworkReply::NetworkError error = QNetworkReply::NoError; QNetworkReply::NetworkError error = QNetworkReply::NoError;
@ -170,13 +169,13 @@ ToxId Toxme::lookup(QString address)
if (index == -1) if (index == -1)
return ToxId(); return ToxId();
response = response.mid(index+pattern.size()); response = response.mid(index + pattern.size());
const int idStart = response.indexOf('"'); const int idStart = response.indexOf('"');
if (idStart == -1) if (idStart == -1)
return ToxId(); return ToxId();
response = response.mid(idStart+1); response = response.mid(idStart + 1);
const int idEnd = response.indexOf('"'); const int idEnd = response.indexOf('"');
if (idEnd == -1) if (idEnd == -1)
@ -199,10 +198,9 @@ Toxme::ExecCode Toxme::extractError(QString json)
if (start == -1) if (start == -1)
return ServerError; return ServerError;
json = json.mid(start+pattern.size()); json = json.mid(start + pattern.size());
int end = json.indexOf(","); int end = json.indexOf(",");
if (end == -1) if (end == -1) {
{
end = json.indexOf("}"); end = json.indexOf("}");
if (end == -1) if (end == -1)
return IncorrectResponse; return IncorrectResponse;
@ -227,16 +225,16 @@ Toxme::ExecCode Toxme::extractError(QString json)
* @param[in] bio A short optional description of yourself if you want to publish your address. * @param[in] bio A short optional description of yourself if you want to publish your address.
* @return password on success, else sets code parameter and returns an empty QString. * @return password on success, else sets code parameter and returns an empty QString.
*/ */
QString Toxme::createAddress(ExecCode &code, QString server, ToxId id, QString address, QString Toxme::createAddress(ExecCode& code, QString server, ToxId id, QString address,
bool keepPrivate, QString bio) bool keepPrivate, QString bio)
{ {
int privacy = keepPrivate ? 0 : 2; int privacy = keepPrivate ? 0 : 2;
// JSON injection ? // JSON injection ?
bio.replace('\\',"\\\\"); bio.replace('\\', "\\\\");
bio.replace('"',"\""); bio.replace('"', "\"");
address.replace('\\',"\\\\"); address.replace('\\', "\\\\");
address.replace('"',"\""); address.replace('"', "\"");
bio = bio.trimmed(); bio = bio.trimmed();
address = address.trimmed(); address = address.trimmed();
@ -244,14 +242,18 @@ QString Toxme::createAddress(ExecCode &code, QString server, ToxId id, QString a
if (!server.contains("://")) if (!server.contains("://"))
server = "https://" + server; server = "https://" + server;
const QString payload{"{\"tox_id\":\""+id.toString()+"\"," const QString payload{"{\"tox_id\":\"" + id.toString() + "\","
"\"name\":\""+address+"\"," "\"name\":\""
"\"privacy\":"+QString().setNum(privacy)+"," + address + "\","
"\"bio\":\""+bio+"\"," "\"privacy\":"
"\"timestamp\":"+QString().setNum(time(0))+"}"}; + QString().setNum(privacy) + ","
"\"bio\":\""
+ bio + "\","
"\"timestamp\":"
+ QString().setNum(time(0)) + "}"};
QString pubkeyUrl = server + "/pk"; QString pubkeyUrl = server + "/pk";
QString apiUrl = server + "/api"; QString apiUrl = server + "/api";
QNetworkReply::NetworkError error = QNetworkReply::NoError; QNetworkReply::NetworkError error = QNetworkReply::NoError;
QByteArray encrypted = prepareEncryptedJson(pubkeyUrl, 1, payload); QByteArray encrypted = prepareEncryptedJson(pubkeyUrl, 1, payload);
QByteArray response = makeJsonRequest(apiUrl, encrypted, error); QByteArray response = makeJsonRequest(apiUrl, encrypted, error);
@ -263,28 +265,26 @@ QString Toxme::createAddress(ExecCode &code, QString server, ToxId id, QString a
return getPass(response, code); return getPass(response, code);
} }
QString Toxme::getPass(QString json, ExecCode &code) { QString Toxme::getPass(QString json, ExecCode& code)
{
static const QByteArray pattern{"password\":"}; static const QByteArray pattern{"password\":"};
json = json.remove(' '); json = json.remove(' ');
const int start = json.indexOf(pattern); const int start = json.indexOf(pattern);
if (start == -1) if (start == -1) {
{
code = NoPassword; code = NoPassword;
return QString(); return QString();
} }
json = json.mid(start+pattern.size()); json = json.mid(start + pattern.size());
if (json.startsWith("null")) if (json.startsWith("null")) {
{
code = Updated; code = Updated;
return QString(); return QString();
} }
json = json.mid(1, json.length()); json = json.mid(1, json.length());
int end = json.indexOf("\""); int end = json.indexOf("\"");
if (end == -1) if (end == -1) {
{
code = IncorrectResponse; code = IncorrectResponse;
return QString(); return QString();
} }
@ -302,8 +302,9 @@ QString Toxme::getPass(QString json, ExecCode &code) {
*/ */
Toxme::ExecCode Toxme::deleteAddress(QString server, ToxPk id) Toxme::ExecCode Toxme::deleteAddress(QString server, ToxPk id)
{ {
const QString payload{"{\"public_key\":\""+id.toString()+"\"," const QString payload{"{\"public_key\":\"" + id.toString() + "\","
"\"timestamp\":"+QString().setNum(time(0))+"}"}; "\"timestamp\":"
+ QString().setNum(time(0)) + "}"};
server = server.trimmed(); server = server.trimmed();
if (!server.contains("://")) if (!server.contains("://"))

View File

@ -21,19 +21,20 @@
#ifndef TOXME_H #ifndef TOXME_H
#define TOXME_H #define TOXME_H
#include <QString> #include "src/core/toxid.h"
#include <QMap> #include <QMap>
#include <QMutex> #include <QMutex>
#include <QNetworkReply> #include <QNetworkReply>
#include <QString>
#include <memory> #include <memory>
#include "src/core/toxid.h"
class QNetworkAccessManager; class QNetworkAccessManager;
class Toxme class Toxme
{ {
public: public:
enum ExecCode { enum ExecCode
{
ExecError = -50, ExecError = -50,
Ok = 0, Ok = 0,
Updated = 1, Updated = 1,
@ -43,18 +44,18 @@ public:
}; };
static ToxId lookup(QString address); static ToxId lookup(QString address);
static QString createAddress(ExecCode &code, QString server, ToxId id, QString address, static QString createAddress(ExecCode& code, QString server, ToxId id, QString address,
bool keepPrivate=true, QString bio=QString()); bool keepPrivate = true, QString bio = QString());
static ExecCode deleteAddress(QString server, ToxPk id); static ExecCode deleteAddress(QString server, ToxPk id);
static QString getErrorMessage(int errorCode); static QString getErrorMessage(int errorCode);
static QString translateErrorMessage(int errorCode); static QString translateErrorMessage(int errorCode);
private: private:
Toxme() = delete; Toxme() = delete;
static QByteArray makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error); static QByteArray makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError& error);
static QByteArray prepareEncryptedJson(QString url, int action, QString payload); static QByteArray prepareEncryptedJson(QString url, int action, QString payload);
static QByteArray getServerPubkey(QString url, QNetworkReply::NetworkError &error); static QByteArray getServerPubkey(QString url, QNetworkReply::NetworkError& error);
static QString getPass(QString json, ExecCode &code); static QString getPass(QString json, ExecCode& code);
static ExecCode extractError(QString json); static ExecCode extractError(QString json);
private: private:

View File

@ -19,21 +19,21 @@
#include "src/net/toxuri.h" #include "src/net/toxuri.h"
#include "src/net/toxme.h"
#include "src/widget/tool/friendrequestdialog.h"
#include "src/nexus.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/net/toxme.h"
#include "src/nexus.h"
#include "src/widget/tool/friendrequestdialog.h"
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QCoreApplication>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QMessageBox>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QPushButton> #include <QPushButton>
#include <QCoreApplication> #include <QString>
#include <QThread> #include <QThread>
#include <QVBoxLayout>
bool toxURIEventHandler(const QByteArray& eventData) bool toxURIEventHandler(const QByteArray& eventData)
{ {
@ -50,13 +50,12 @@ bool toxURIEventHandler(const QByteArray& eventData)
* @param toxURI Tox URI to try to add. * @param toxURI Tox URI to try to add.
* @return True, if tox URI is correct, false otherwise. * @return True, if tox URI is correct, false otherwise.
*/ */
bool handleToxURI(const QString &toxURI) bool handleToxURI(const QString& toxURI)
{ {
Nexus& nexus = Nexus::getInstance(); Nexus& nexus = Nexus::getInstance();
Core* core = nexus.getCore(); Core* core = nexus.getCore();
while (!core) while (!core) {
{
if (!nexus.isRunning()) if (!nexus.isRunning())
return false; return false;
@ -65,8 +64,7 @@ bool handleToxURI(const QString &toxURI)
QThread::msleep(10); QThread::msleep(10);
} }
while (!core->isReady()) while (!core->isReady()) {
{
if (!nexus.isRunning()) if (!nexus.isRunning())
return false; return false;
@ -77,15 +75,13 @@ bool handleToxURI(const QString &toxURI)
QString toxaddr = toxURI.mid(4); QString toxaddr = toxURI.mid(4);
ToxId toxId(toxaddr); ToxId toxId(toxaddr);
if (!toxId.isValid()) if (!toxId.isValid()) {
{
toxId = Toxme::lookup(toxaddr); toxId = Toxme::lookup(toxaddr);
if (!toxId.isValid()) if (!toxId.isValid()) {
{ QMessageBox* messageBox =
QMessageBox *messageBox = new QMessageBox(QMessageBox::Warning, new QMessageBox(QMessageBox::Warning, QMessageBox::tr("Couldn't add friend"),
QMessageBox::tr("Couldn't add friend"), QMessageBox::tr("%1 is not a valid Toxme address.").arg(toxaddr),
QMessageBox::tr("%1 is not a valid Toxme address.") QMessageBox::Ok, nullptr);
.arg(toxaddr), QMessageBox::Ok, nullptr);
messageBox->setButtonText(QMessageBox::Ok, QMessageBox::tr("Ok")); messageBox->setButtonText(QMessageBox::Ok, QMessageBox::tr("Ok"));
QObject::connect(messageBox, &QMessageBox::finished, messageBox, &QMessageBox::deleteLater); QObject::connect(messageBox, &QMessageBox::finished, messageBox, &QMessageBox::deleteLater);
messageBox->show(); messageBox->show();
@ -93,25 +89,26 @@ bool handleToxURI(const QString &toxURI)
} }
} }
if (toxId == core->getSelfId()) if (toxId == core->getSelfId()) {
{ QMessageBox* messageBox =
QMessageBox *messageBox = new QMessageBox(QMessageBox::Warning, new QMessageBox(QMessageBox::Warning, QMessageBox::tr("Couldn't add friend"),
QMessageBox::tr("Couldn't add friend"), QMessageBox::tr("You can't add yourself as a friend!",
QMessageBox::tr("You can't add yourself as a friend!", "When trying to add your own Tox ID as friend"),
"When trying to add your own Tox ID as friend"), QMessageBox::Ok, nullptr);
QMessageBox::Ok, nullptr);
messageBox->setButtonText(QMessageBox::Ok, QMessageBox::tr("Ok")); messageBox->setButtonText(QMessageBox::Ok, QMessageBox::tr("Ok"));
QObject::connect(messageBox, &QMessageBox::finished, messageBox, &QMessageBox::deleteLater); QObject::connect(messageBox, &QMessageBox::finished, messageBox, &QMessageBox::deleteLater);
messageBox->show(); messageBox->show();
return false; return false;
} }
ToxURIDialog *dialog = new ToxURIDialog(0, toxaddr, QObject::tr("%1 here! Tox me maybe?", ToxURIDialog* dialog = new ToxURIDialog(
"Default message in Tox URI friend requests. Write something appropriate!") 0, toxaddr,
.arg(Nexus::getCore()->getUsername())); QObject::tr("%1 here! Tox me maybe?",
"Default message in Tox URI friend requests. Write something appropriate!")
.arg(Nexus::getCore()->getUsername()));
QObject::connect(dialog, &ToxURIDialog::finished, [=](int result) { QObject::connect(dialog, &ToxURIDialog::finished, [=](int result) {
if (result == QDialog::Accepted) if (result == QDialog::Accepted)
Core::getInstance()->requestFriendship(toxId, dialog->getRequestMessage()); Core::getInstance()->requestFriendship(toxId, dialog->getRequestMessage());
dialog->deleteLater(); dialog->deleteLater();
}); });
@ -120,21 +117,21 @@ bool handleToxURI(const QString &toxURI)
return true; return true;
} }
ToxURIDialog::ToxURIDialog(QWidget* parent, const QString &userId, const QString &message) : ToxURIDialog::ToxURIDialog(QWidget* parent, const QString& userId, const QString& message)
QDialog(parent) : QDialog(parent)
{ {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Add a friend", "Title of the window to add a friend through Tox URI")); setWindowTitle(tr("Add a friend", "Title of the window to add a friend through Tox URI"));
QLabel *friendsLabel = new QLabel(tr("Do you want to add %1 as a friend?").arg(userId), this); QLabel* friendsLabel = new QLabel(tr("Do you want to add %1 as a friend?").arg(userId), this);
QLabel *userIdLabel = new QLabel(tr("User ID:"), this); QLabel* userIdLabel = new QLabel(tr("User ID:"), this);
QLineEdit *userIdEdit = new QLineEdit(userId, this); QLineEdit* userIdEdit = new QLineEdit(userId, this);
userIdEdit->setCursorPosition(0); userIdEdit->setCursorPosition(0);
userIdEdit->setReadOnly(true); userIdEdit->setReadOnly(true);
QLabel *messageLabel = new QLabel(tr("Friend request message:"), this); QLabel* messageLabel = new QLabel(tr("Friend request message:"), this);
messageEdit = new QPlainTextEdit(message, this); messageEdit = new QPlainTextEdit(message, this);
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this); QDialogButtonBox* buttonBox = new QDialogButtonBox(Qt::Horizontal, this);
buttonBox->addButton(tr("Send", "Send a friend request"), QDialogButtonBox::AcceptRole); buttonBox->addButton(tr("Send", "Send a friend request"), QDialogButtonBox::AcceptRole);
buttonBox->addButton(tr("Cancel", "Don't send a friend request"), QDialogButtonBox::RejectRole); buttonBox->addButton(tr("Cancel", "Don't send a friend request"), QDialogButtonBox::RejectRole);
@ -142,7 +139,7 @@ ToxURIDialog::ToxURIDialog(QWidget* parent, const QString &userId, const QString
connect(buttonBox, &QDialogButtonBox::accepted, this, &FriendRequestDialog::accept); connect(buttonBox, &QDialogButtonBox::accepted, this, &FriendRequestDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &FriendRequestDialog::reject); connect(buttonBox, &QDialogButtonBox::rejected, this, &FriendRequestDialog::reject);
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(friendsLabel); layout->addWidget(friendsLabel);
layout->addSpacing(12); layout->addSpacing(12);

View File

@ -33,11 +33,11 @@ class ToxURIDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ToxURIDialog(QWidget* parent, const QString &userId, const QString &message); explicit ToxURIDialog(QWidget* parent, const QString& userId, const QString& message);
QString getRequestMessage(); QString getRequestMessage();
private: private:
QPlainTextEdit *messageEdit; QPlainTextEdit* messageEdit;
}; };
#endif // TOXURI_H #endif // TOXURI_H

View File

@ -19,28 +19,28 @@
#include "nexus.h" #include "nexus.h"
#include "src/persistence/profile.h" #include "persistence/settings.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/core/coreav.h" #include "src/core/coreav.h"
#include "src/persistence/profile.h"
#include "src/widget/widget.h" #include "src/widget/widget.h"
#include "persistence/settings.h"
#include "video/camerasource.h" #include "video/camerasource.h"
#include "widget/gui.h" #include "widget/gui.h"
#include "widget/loginscreen.h" #include "widget/loginscreen.h"
#include <QThread>
#include <QDebug>
#include <QImageReader>
#include <QFile>
#include <QApplication> #include <QApplication>
#include <QDebug>
#include <QDesktopWidget>
#include <QFile>
#include <QImageReader>
#include <QThread>
#include <cassert> #include <cassert>
#include <vpx/vpx_image.h> #include <vpx/vpx_image.h>
#include <QDesktopWidget>
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
#include <QWindow>
#include <QMenuBar>
#include <QActionGroup> #include <QActionGroup>
#include <QMenuBar>
#include <QSignalMapper> #include <QSignalMapper>
#include <QWindow>
#endif #endif
/** /**
@ -55,13 +55,13 @@ Q_DECLARE_OPAQUE_POINTER(ToxAV*)
static Nexus* nexus{nullptr}; static Nexus* nexus{nullptr};
Nexus::Nexus(QObject *parent) : Nexus::Nexus(QObject* parent)
QObject(parent), : QObject(parent)
profile{nullptr}, , profile{nullptr}
widget{nullptr}, , widget{nullptr}
loginScreen{nullptr}, , loginScreen{nullptr}
running{true}, , running{true}
quitOnLastWindowClosed{true} , quitOnLastWindowClosed{true}
{ {
} }
@ -132,8 +132,7 @@ void Nexus::start()
minimizeAction = windowMenu->addAction(QString()); minimizeAction = windowMenu->addAction(QString());
minimizeAction->setShortcut(Qt::CTRL + Qt::Key_M); minimizeAction->setShortcut(Qt::CTRL + Qt::Key_M);
connect(minimizeAction, &QAction::triggered, [this]() connect(minimizeAction, &QAction::triggered, [this]() {
{
minimizeAction->setEnabled(false); minimizeAction->setEnabled(false);
QApplication::focusWindow()->showMinimized(); QApplication::focusWindow()->showMinimized();
}); });
@ -172,7 +171,8 @@ void Nexus::showLogin()
profile = nullptr; profile = nullptr;
loginScreen->reset(); loginScreen->reset();
loginScreen->move(QApplication::desktop()->screen()->rect().center() - loginScreen->rect().center()); loginScreen->move(QApplication::desktop()->screen()->rect().center()
- loginScreen->rect().center());
loginScreen->show(); loginScreen->show();
quitOnLastWindowClosed = true; quitOnLastWindowClosed = true;
} }
@ -199,35 +199,36 @@ void Nexus::showMainGUI()
// Connections // Connections
Core* core = profile->getCore(); Core* core = profile->getCore();
connect(core, &Core::connected, widget, &Widget::onConnected); connect(core, &Core::connected, widget, &Widget::onConnected);
connect(core, &Core::disconnected, widget, &Widget::onDisconnected); connect(core, &Core::disconnected, widget, &Widget::onDisconnected);
connect(core, &Core::failedToStart, widget, &Widget::onFailedToStartCore, Qt::BlockingQueuedConnection); connect(core, &Core::failedToStart, widget, &Widget::onFailedToStartCore,
connect(core, &Core::badProxy, widget, &Widget::onBadProxyCore, Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
connect(core, &Core::statusSet, widget, &Widget::onStatusSet); connect(core, &Core::badProxy, widget, &Widget::onBadProxyCore, Qt::BlockingQueuedConnection);
connect(core, &Core::usernameSet, widget, &Widget::setUsername); connect(core, &Core::statusSet, widget, &Widget::onStatusSet);
connect(core, &Core::statusMessageSet, widget, &Widget::setStatusMessage); connect(core, &Core::usernameSet, widget, &Widget::setUsername);
connect(core, &Core::selfAvatarChanged, widget, &Widget::onSelfAvatarLoaded); connect(core, &Core::statusMessageSet, widget, &Widget::setStatusMessage);
connect(core, &Core::friendAdded, widget, &Widget::addFriend); connect(core, &Core::selfAvatarChanged, widget, &Widget::onSelfAvatarLoaded);
connect(core, &Core::friendshipChanged, widget, &Widget::onFriendshipChanged); connect(core, &Core::friendAdded, widget, &Widget::addFriend);
connect(core, &Core::failedToAddFriend, widget, &Widget::addFriendFailed); connect(core, &Core::friendshipChanged, widget, &Widget::onFriendshipChanged);
connect(core, &Core::friendUsernameChanged, widget, &Widget::onFriendUsernameChanged); connect(core, &Core::failedToAddFriend, widget, &Widget::addFriendFailed);
connect(core, &Core::friendStatusChanged, widget, &Widget::onFriendStatusChanged); connect(core, &Core::friendUsernameChanged, widget, &Widget::onFriendUsernameChanged);
connect(core, &Core::friendStatusChanged, widget, &Widget::onFriendStatusChanged);
connect(core, &Core::friendStatusMessageChanged, widget, &Widget::onFriendStatusMessageChanged); connect(core, &Core::friendStatusMessageChanged, widget, &Widget::onFriendStatusMessageChanged);
connect(core, &Core::friendRequestReceived, widget, &Widget::onFriendRequestReceived); connect(core, &Core::friendRequestReceived, widget, &Widget::onFriendRequestReceived);
connect(core, &Core::friendMessageReceived, widget, &Widget::onFriendMessageReceived); connect(core, &Core::friendMessageReceived, widget, &Widget::onFriendMessageReceived);
connect(core, &Core::receiptRecieved, widget, &Widget::onReceiptRecieved); connect(core, &Core::receiptRecieved, widget, &Widget::onReceiptRecieved);
connect(core, &Core::groupInviteReceived, widget, &Widget::onGroupInviteReceived); connect(core, &Core::groupInviteReceived, widget, &Widget::onGroupInviteReceived);
connect(core, &Core::groupMessageReceived, widget, &Widget::onGroupMessageReceived); connect(core, &Core::groupMessageReceived, widget, &Widget::onGroupMessageReceived);
connect(core, &Core::groupNamelistChanged, widget, &Widget::onGroupNamelistChanged); connect(core, &Core::groupNamelistChanged, widget, &Widget::onGroupNamelistChanged);
connect(core, &Core::groupTitleChanged, widget, &Widget::onGroupTitleChanged); connect(core, &Core::groupTitleChanged, widget, &Widget::onGroupTitleChanged);
connect(core, &Core::groupPeerAudioPlaying, widget, &Widget::onGroupPeerAudioPlaying); connect(core, &Core::groupPeerAudioPlaying, widget, &Widget::onGroupPeerAudioPlaying);
connect(core, &Core::emptyGroupCreated, widget, &Widget::onEmptyGroupCreated); connect(core, &Core::emptyGroupCreated, widget, &Widget::onEmptyGroupCreated);
connect(core, &Core::friendTypingChanged, widget, &Widget::onFriendTypingChanged); connect(core, &Core::friendTypingChanged, widget, &Widget::onFriendTypingChanged);
connect(core, &Core::messageSentResult, widget, &Widget::onMessageSendResult); connect(core, &Core::messageSentResult, widget, &Widget::onMessageSendResult);
connect(core, &Core::groupSentResult, widget, &Widget::onGroupSendResult); connect(core, &Core::groupSentResult, widget, &Widget::onGroupSendResult);
connect(widget, &Widget::statusSet, core, &Core::setStatus); connect(widget, &Widget::statusSet, core, &Core::setStatus);
connect(widget, &Widget::friendRequested, core, &Core::requestFriendship); connect(widget, &Widget::friendRequested, core, &Core::requestFriendship);
connect(widget, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest); connect(widget, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
profile->startCore(); profile->startCore();
@ -314,11 +315,11 @@ Widget* Nexus::getDesktopGUI()
QString Nexus::getSupportedImageFilter() QString Nexus::getSupportedImageFilter()
{ {
QString res; QString res;
for (auto type : QImageReader::supportedImageFormats()) for (auto type : QImageReader::supportedImageFormats())
res += QString("*.%1 ").arg(QString(type)); res += QString("*.%1 ").arg(QString(type));
return tr("Images (%1)", "filetype filter").arg(res.left(res.size()-1)); return tr("Images (%1)", "filetype filter").arg(res.left(res.size() - 1));
} }
/** /**
@ -362,8 +363,7 @@ void Nexus::onWindowStateChanged(Qt::WindowStates state)
{ {
minimizeAction->setEnabled(QApplication::activeWindow() != nullptr); minimizeAction->setEnabled(QApplication::activeWindow() != nullptr);
if (QApplication::activeWindow() != nullptr && sender() == QApplication::activeWindow()) if (QApplication::activeWindow() != nullptr && sender() == QApplication::activeWindow()) {
{
if (state & Qt::WindowFullScreen) if (state & Qt::WindowFullScreen)
minimizeAction->setEnabled(false); minimizeAction->setEnabled(false);
@ -404,8 +404,7 @@ void Nexus::updateWindowsArg(QWindow* closedWindow)
else else
activeWindow = nullptr; activeWindow = nullptr;
for (int i = 0; i < windowList.size(); ++i) for (int i = 0; i < windowList.size(); ++i) {
{
if (closedWindow == windowList[i]) if (closedWindow == windowList[i])
continue; continue;
@ -432,10 +431,8 @@ void Nexus::updateWindowsStates()
bool exists = false; bool exists = false;
QWindowList windowList = QApplication::topLevelWindows(); QWindowList windowList = QApplication::topLevelWindows();
for (QWindow* window : windowList) for (QWindow* window : windowList) {
{ if (!(window->windowState() & Qt::WindowMinimized)) {
if (!(window->windowState() & Qt::WindowMinimized))
{
exists = true; exists = true;
break; break;
} }

View File

@ -80,7 +80,7 @@ public slots:
void bringAllToFront(); void bringAllToFront();
private: private:
void updateWindowsArg(QWindow *closedWindow); void updateWindowsArg(QWindow* closedWindow);
QSignalMapper* windowMapper; QSignalMapper* windowMapper;
QActionGroup* windowActions = nullptr; QActionGroup* windowActions = nullptr;
@ -90,7 +90,7 @@ private slots:
void onLastWindowClosed(); void onLastWindowClosed();
private: private:
explicit Nexus(QObject *parent = 0); explicit Nexus(QObject* parent = 0);
~Nexus(); ~Nexus();
private: private:

View File

@ -42,7 +42,8 @@
* @brief Implements a low level RAII interface to a SQLCipher (SQlite3) database. * @brief Implements a low level RAII interface to a SQLCipher (SQlite3) database.
* *
* Thread-safe, does all database operations on a worker thread. * Thread-safe, does all database operations on a worker thread.
* The queries must not contain transaction commands (BEGIN/COMMIT/...) or the behavior is undefined. * The queries must not contain transaction commands (BEGIN/COMMIT/...) or the behavior is
* undefined.
* *
* @var QMutex RawDatabase::transactionsMutex; * @var QMutex RawDatabase::transactionsMutex;
* @brief Protects pendingTransactions * @brief Protects pendingTransactions
@ -94,7 +95,7 @@
RawDatabase::RawDatabase(const QString& path, const QString& password, const QByteArray& salt) RawDatabase::RawDatabase(const QString& path, const QString& password, const QByteArray& salt)
: workerThread{new QThread} : workerThread{new QThread}
, path{path} , path{path}
, currentSalt{salt} // we need the salt later if a new password should be set , currentSalt{salt} // we need the salt later if a new password should be set
, currentHexKey{deriveKey(password, salt)} , currentHexKey{deriveKey(password, salt)}
{ {
workerThread->setObjectName("qTox Database"); workerThread->setObjectName("qTox Database");
@ -102,8 +103,7 @@ RawDatabase::RawDatabase(const QString& path, const QString& password, const QBy
workerThread->start(); workerThread->start();
// first try with the new salt // first try with the new salt
if (open(path, currentHexKey)) if (open(path, currentHexKey)) {
{
return; return;
} }
@ -112,32 +112,24 @@ RawDatabase::RawDatabase(const QString& path, const QString& password, const QBy
// create a backup before trying to upgrade to new salt // create a backup before trying to upgrade to new salt
bool upgrade = true; bool upgrade = true;
if(!QFile::copy(path, path + ".bak")) if (!QFile::copy(path, path + ".bak")) {
{
qDebug() << "Couldn't create the backup of the database, won't upgrade"; qDebug() << "Couldn't create the backup of the database, won't upgrade";
upgrade = false; upgrade = false;
} }
// fall back to the old salt // fall back to the old salt
currentHexKey = deriveKey(password); currentHexKey = deriveKey(password);
if(open(path, currentHexKey)) if (open(path, currentHexKey)) {
{
// upgrade only if backup successful // upgrade only if backup successful
if(upgrade) if (upgrade) {
{
// still using old salt, upgrade // still using old salt, upgrade
if(setPassword(password)) if (setPassword(password)) {
{
qDebug() << "Successfully upgraded to dynamic salt"; qDebug() << "Successfully upgraded to dynamic salt";
} } else {
else
{
qWarning() << "Failed to set password with new salt"; qWarning() << "Failed to set password with new salt";
} }
} }
} } else {
else
{
qDebug() << "Failed to open database with old salt"; qDebug() << "Failed to open database with old salt";
} }
} }
@ -156,40 +148,36 @@ RawDatabase::~RawDatabase()
* @param hexKey Hex representation of the key in string. * @param hexKey Hex representation of the key in string.
* @return True if success, false otherwise. * @return True if success, false otherwise.
*/ */
bool RawDatabase::open(const QString& path, const QString &hexKey) bool RawDatabase::open(const QString& path, const QString& hexKey)
{ {
if (QThread::currentThread() != workerThread.get()) if (QThread::currentThread() != workerThread.get()) {
{
bool ret; bool ret;
QMetaObject::invokeMethod(this, "open", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret), QMetaObject::invokeMethod(this, "open", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret),
Q_ARG(const QString&, path), Q_ARG(const QString&, hexKey)); Q_ARG(const QString&, path), Q_ARG(const QString&, hexKey));
return ret; return ret;
} }
if (!QFile::exists(path) && QFile::exists(path+".tmp")) if (!QFile::exists(path) && QFile::exists(path + ".tmp")) {
{ qWarning() << "Restoring database from temporary export file! Did we crash while changing "
qWarning() << "Restoring database from temporary export file! Did we crash while changing the password?"; "the password?";
QFile::rename(path+".tmp", path); QFile::rename(path + ".tmp", path);
} }
if (sqlite3_open_v2(path.toUtf8().data(), &sqlite, if (sqlite3_open_v2(path.toUtf8().data(), &sqlite,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, nullptr) != SQLITE_OK) SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, nullptr)
{ != SQLITE_OK) {
qWarning() << "Failed to open database"<<path<<"with error:"<<sqlite3_errmsg(sqlite); qWarning() << "Failed to open database" << path << "with error:" << sqlite3_errmsg(sqlite);
return false; return false;
} }
if (!hexKey.isEmpty()) if (!hexKey.isEmpty()) {
{ if (!execNow("PRAGMA key = \"x'" + hexKey + "'\"")) {
if (!execNow("PRAGMA key = \"x'"+hexKey+"'\""))
{
qWarning() << "Failed to set encryption key"; qWarning() << "Failed to set encryption key";
close(); close();
return false; return false;
} }
if (!execNow("SELECT count(*) FROM sqlite_master")) if (!execNow("SELECT count(*) FROM sqlite_master")) {
{
qWarning() << "Database is unusable, check that the password is correct"; qWarning() << "Database is unusable, check that the password is correct";
close(); close();
return false; return false;
@ -212,7 +200,7 @@ void RawDatabase::close()
if (sqlite3_close(sqlite) == SQLITE_OK) if (sqlite3_close(sqlite) == SQLITE_OK)
sqlite = nullptr; sqlite = nullptr;
else else
qWarning() << "Error closing database:"<<sqlite3_errmsg(sqlite); qWarning() << "Error closing database:" << sqlite3_errmsg(sqlite);
} }
/** /**
@ -240,7 +228,7 @@ bool RawDatabase::execNow(const QString& statement)
* @param statement Statement to execute. * @param statement Statement to execute.
* @return Whether the transaction was successful. * @return Whether the transaction was successful.
*/ */
bool RawDatabase::execNow(const RawDatabase::Query &statement) bool RawDatabase::execNow(const RawDatabase::Query& statement)
{ {
return execNow(QVector<Query>{statement}); return execNow(QVector<Query>{statement});
} }
@ -250,10 +238,9 @@ bool RawDatabase::execNow(const RawDatabase::Query &statement)
* @param statements List of statements to execute. * @param statements List of statements to execute.
* @return Whether the transaction was successful. * @return Whether the transaction was successful.
*/ */
bool RawDatabase::execNow(const QVector<RawDatabase::Query> &statements) bool RawDatabase::execNow(const QVector<RawDatabase::Query>& statements)
{ {
if (!sqlite) if (!sqlite) {
{
qWarning() << "Trying to exec, but the database is not open"; qWarning() << "Trying to exec, but the database is not open";
return false; return false;
} }
@ -283,20 +270,19 @@ bool RawDatabase::execNow(const QVector<RawDatabase::Query> &statements)
* @brief Executes a SQL transaction asynchronously. * @brief Executes a SQL transaction asynchronously.
* @param statement Statement to execute. * @param statement Statement to execute.
*/ */
void RawDatabase::execLater(const QString &statement) void RawDatabase::execLater(const QString& statement)
{ {
execLater(Query{statement}); execLater(Query{statement});
} }
void RawDatabase::execLater(const RawDatabase::Query &statement) void RawDatabase::execLater(const RawDatabase::Query& statement)
{ {
execLater(QVector<Query>{statement}); execLater(QVector<Query>{statement});
} }
void RawDatabase::execLater(const QVector<RawDatabase::Query> &statements) void RawDatabase::execLater(const QVector<RawDatabase::Query>& statements)
{ {
if (!sqlite) if (!sqlite) {
{
qWarning() << "Trying to exec, but the database is not open"; qWarning() << "Trying to exec, but the database is not open";
return; return;
} }
@ -327,17 +313,15 @@ void RawDatabase::sync()
*/ */
bool RawDatabase::setPassword(const QString& password) bool RawDatabase::setPassword(const QString& password)
{ {
if (!sqlite) if (!sqlite) {
{
qWarning() << "Trying to change the password, but the database is not open"; qWarning() << "Trying to change the password, but the database is not open";
return false; return false;
} }
if (QThread::currentThread() != workerThread.get()) if (QThread::currentThread() != workerThread.get()) {
{
bool ret; bool ret;
QMetaObject::invokeMethod(this, "setPassword", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(this, "setPassword", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, password)); Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, password));
return ret; return ret;
} }
@ -345,59 +329,50 @@ bool RawDatabase::setPassword(const QString& password)
// so we always process the pending queue before rekeying for consistency // so we always process the pending queue before rekeying for consistency
process(); process();
if (QFile::exists(path+".tmp")) if (QFile::exists(path + ".tmp")) {
{
qWarning() << "Found old temporary export file while rekeying, deleting it"; qWarning() << "Found old temporary export file while rekeying, deleting it";
QFile::remove(path+".tmp"); QFile::remove(path + ".tmp");
} }
if (!password.isEmpty()) if (!password.isEmpty()) {
{
QString newHexKey = deriveKey(password, currentSalt); QString newHexKey = deriveKey(password, currentSalt);
if (!currentHexKey.isEmpty()) if (!currentHexKey.isEmpty()) {
{ if (!execNow("PRAGMA rekey = \"x'" + newHexKey + "'\"")) {
if (!execNow("PRAGMA rekey = \"x'"+newHexKey+"'\""))
{
qWarning() << "Failed to change encryption key"; qWarning() << "Failed to change encryption key";
close(); close();
return false; return false;
} }
} } else {
else
{
// Need to encrypt the database // Need to encrypt the database
if (!execNow("ATTACH DATABASE '"+path+".tmp' AS encrypted KEY \"x'"+newHexKey+"'\";" if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS encrypted KEY \"x'" + newHexKey
"SELECT sqlcipher_export('encrypted');" + "'\";"
"DETACH DATABASE encrypted;")) "SELECT sqlcipher_export('encrypted');"
{ "DETACH DATABASE encrypted;")) {
qWarning() << "Failed to export encrypted database"; qWarning() << "Failed to export encrypted database";
close(); close();
return false; return false;
} }
// This is racy as hell, but nobody will race with us since we hold the profile lock // This is racy as hell, but nobody will race with us since we hold the profile lock
// If we crash or die here, the rename should be atomic, so we can recover no matter what // If we crash or die here, the rename should be atomic, so we can recover no matter
// what
close(); close();
QFile::remove(path); QFile::remove(path);
QFile::rename(path+".tmp", path); QFile::rename(path + ".tmp", path);
currentHexKey = newHexKey; currentHexKey = newHexKey;
if (!open(path, currentHexKey)) if (!open(path, currentHexKey)) {
{
qWarning() << "Failed to open encrypted database"; qWarning() << "Failed to open encrypted database";
return false; return false;
} }
} }
} } else {
else
{
if (currentHexKey.isEmpty()) if (currentHexKey.isEmpty())
return true; return true;
// Need to decrypt the database // Need to decrypt the database
if (!execNow("ATTACH DATABASE '"+path+".tmp' AS plaintext KEY '';" if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS plaintext KEY '';"
"SELECT sqlcipher_export('plaintext');" "SELECT sqlcipher_export('plaintext');"
"DETACH DATABASE plaintext;")) "DETACH DATABASE plaintext;")) {
{
qWarning() << "Failed to export decrypted database"; qWarning() << "Failed to export decrypted database";
close(); close();
return false; return false;
@ -407,10 +382,9 @@ bool RawDatabase::setPassword(const QString& password)
// If we crash or die here, the rename should be atomic, so we can recover no matter what // If we crash or die here, the rename should be atomic, so we can recover no matter what
close(); close();
QFile::remove(path); QFile::remove(path);
QFile::rename(path+".tmp", path); QFile::rename(path + ".tmp", path);
currentHexKey.clear(); currentHexKey.clear();
if (!open(path)) if (!open(path)) {
{
qCritical() << "Failed to open decrypted database"; qCritical() << "Failed to open decrypted database";
return false; return false;
} }
@ -425,19 +399,17 @@ bool RawDatabase::setPassword(const QString& password)
* *
* @note Will process all transactions before renaming * @note Will process all transactions before renaming
*/ */
bool RawDatabase::rename(const QString &newPath) bool RawDatabase::rename(const QString& newPath)
{ {
if (!sqlite) if (!sqlite) {
{
qWarning() << "Trying to change the password, but the database is not open"; qWarning() << "Trying to change the password, but the database is not open";
return false; return false;
} }
if (QThread::currentThread() != workerThread.get()) if (QThread::currentThread() != workerThread.get()) {
{
bool ret; bool ret;
QMetaObject::invokeMethod(this, "rename", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(this, "rename", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, newPath)); Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, newPath));
return ret; return ret;
} }
@ -463,20 +435,19 @@ bool RawDatabase::rename(const QString &newPath)
*/ */
bool RawDatabase::remove() bool RawDatabase::remove()
{ {
if (!sqlite) if (!sqlite) {
{
qWarning() << "Trying to remove the database, but it is not open"; qWarning() << "Trying to remove the database, but it is not open";
return false; return false;
} }
if (QThread::currentThread() != workerThread.get()) if (QThread::currentThread() != workerThread.get()) {
{
bool ret; bool ret;
QMetaObject::invokeMethod(this, "remove", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret)); QMetaObject::invokeMethod(this, "remove", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, ret));
return ret; return ret;
} }
qDebug() << "Removing database "<< path; qDebug() << "Removing database " << path;
close(); close();
return QFile::remove(path); return QFile::remove(path);
} }
@ -489,7 +460,8 @@ bool RawDatabase::remove()
*/ */
struct PassKeyDeleter struct PassKeyDeleter
{ {
void operator()(Tox_Pass_Key *pass_key) { void operator()(Tox_Pass_Key* pass_key)
{
tox_pass_key_free(pass_key); tox_pass_key_free(pass_key);
} }
}; };
@ -500,7 +472,7 @@ struct PassKeyDeleter
* @return String representation of key * @return String representation of key
* @deprecated deprecated on 2016-11-06, kept for compatibility, replaced by the salted version * @deprecated deprecated on 2016-11-06, kept for compatibility, replaced by the salted version
*/ */
QString RawDatabase::deriveKey(const QString &password) QString RawDatabase::deriveKey(const QString& password)
{ {
if (password.isEmpty()) if (password.isEmpty())
return {}; return {};
@ -509,11 +481,13 @@ QString RawDatabase::deriveKey(const QString &password)
static_assert(TOX_PASS_KEY_LENGTH >= 32, "toxcore must provide 256bit or longer keys"); static_assert(TOX_PASS_KEY_LENGTH >= 32, "toxcore must provide 256bit or longer keys");
static const uint8_t expandConstant[TOX_PASS_SALT_LENGTH+1] = "L'ignorance est le pire des maux"; static const uint8_t expandConstant[TOX_PASS_SALT_LENGTH + 1] =
"L'ignorance est le pire des maux";
std::unique_ptr<Tox_Pass_Key, PassKeyDeleter> key(tox_pass_key_new()); std::unique_ptr<Tox_Pass_Key, PassKeyDeleter> key(tox_pass_key_new());
tox_pass_key_derive_with_salt(key.get(), reinterpret_cast<uint8_t*>(passData.data()), tox_pass_key_derive_with_salt(key.get(), reinterpret_cast<uint8_t*>(passData.data()),
static_cast<std::size_t>(passData.size()), expandConstant, nullptr); static_cast<std::size_t>(passData.size()), expandConstant, nullptr);
return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();; return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();
;
} }
/** /**
@ -524,13 +498,11 @@ QString RawDatabase::deriveKey(const QString &password)
*/ */
QString RawDatabase::deriveKey(const QString& password, const QByteArray& salt) QString RawDatabase::deriveKey(const QString& password, const QByteArray& salt)
{ {
if (password.isEmpty()) if (password.isEmpty()) {
{
return {}; return {};
} }
if (salt.length() != TOX_PASS_SALT_LENGTH) if (salt.length() != TOX_PASS_SALT_LENGTH) {
{
qWarning() << "Salt length doesn't match toxencryptsave expections"; qWarning() << "Salt length doesn't match toxencryptsave expections";
return {}; return {};
} }
@ -543,7 +515,8 @@ QString RawDatabase::deriveKey(const QString& password, const QByteArray& salt)
tox_pass_key_derive_with_salt(key.get(), reinterpret_cast<uint8_t*>(passData.data()), tox_pass_key_derive_with_salt(key.get(), reinterpret_cast<uint8_t*>(passData.data()),
static_cast<std::size_t>(passData.size()), static_cast<std::size_t>(passData.size()),
reinterpret_cast<const uint8_t*>(salt.constData()), nullptr); reinterpret_cast<const uint8_t*>(salt.constData()), nullptr);
return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();; return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();
;
} }
/** /**
@ -575,28 +548,27 @@ void RawDatabase::process()
trans.success->store(false, std::memory_order_release); trans.success->store(false, std::memory_order_release);
// Add transaction commands if necessary // Add transaction commands if necessary
if (trans.queries.size() > 1) if (trans.queries.size() > 1) {
{
trans.queries.prepend({"BEGIN;"}); trans.queries.prepend({"BEGIN;"});
trans.queries.append({"COMMIT;"}); trans.queries.append({"COMMIT;"});
} }
// Compile queries // Compile queries
for (Query& query : trans.queries) for (Query& query : trans.queries) {
{
assert(query.statements.isEmpty()); assert(query.statements.isEmpty());
// sqlite3_prepare_v2 only compiles one statement at a time in the query, // sqlite3_prepare_v2 only compiles one statement at a time in the query,
// we need to loop over them all // we need to loop over them all
int curParam=0; int curParam = 0;
const char* compileTail = query.query.data(); const char* compileTail = query.query.data();
do { do {
// Compile the next statement // Compile the next statement
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
int r; int r;
if ((r = sqlite3_prepare_v2(sqlite, compileTail, if ((r = sqlite3_prepare_v2(sqlite, compileTail,
query.query.size() - static_cast<int>(compileTail - query.query.data()), query.query.size()
&stmt, &compileTail)) != SQLITE_OK) - static_cast<int>(compileTail - query.query.data()),
{ &stmt, &compileTail))
!= SQLITE_OK) {
qWarning() << "Failed to prepare statement" << anonymizeQuery(query.query) qWarning() << "Failed to prepare statement" << anonymizeQuery(query.query)
<< "with error" << r; << "with error" << r;
goto cleanupStatements; goto cleanupStatements;
@ -605,39 +577,34 @@ void RawDatabase::process()
// Now we can bind our params to this statement // Now we can bind our params to this statement
int nParams = sqlite3_bind_parameter_count(stmt); int nParams = sqlite3_bind_parameter_count(stmt);
if (query.blobs.size() < curParam+nParams) if (query.blobs.size() < curParam + nParams) {
{
qWarning() << "Not enough parameters to bind to query " qWarning() << "Not enough parameters to bind to query "
<< anonymizeQuery(query.query); << anonymizeQuery(query.query);
goto cleanupStatements; goto cleanupStatements;
} }
for (int i=0; i<nParams; ++i) for (int i = 0; i < nParams; ++i) {
{ const QByteArray& blob = query.blobs[curParam + i];
const QByteArray& blob = query.blobs[curParam+i]; if (sqlite3_bind_blob(stmt, i + 1, blob.data(), blob.size(), SQLITE_STATIC)
if (sqlite3_bind_blob(stmt, i+1, blob.data(), blob.size(), SQLITE_STATIC) != SQLITE_OK) != SQLITE_OK) {
{ qWarning() << "Failed to bind param" << curParam + i << "to query"
qWarning() << "Failed to bind param" << curParam + i << anonymizeQuery(query.query);
<< "to query" << anonymizeQuery(query.query);
goto cleanupStatements; goto cleanupStatements;
} }
} }
curParam += nParams; curParam += nParams;
} while (compileTail != query.query.data()+query.query.size()); } while (compileTail != query.query.data() + query.query.size());
} }
// Execute each statement of each query of our transaction // Execute each statement of each query of our transaction
for (Query& query : trans.queries) for (Query& query : trans.queries) {
{ for (sqlite3_stmt* stmt : query.statements) {
for (sqlite3_stmt* stmt : query.statements)
{
int column_count = sqlite3_column_count(stmt); int column_count = sqlite3_column_count(stmt);
int result; int result;
do { do {
result = sqlite3_step(stmt); result = sqlite3_step(stmt);
// Execute our row callback // Execute our row callback
if (result == SQLITE_ROW && query.rowCallback) if (result == SQLITE_ROW && query.rowCallback) {
{
QVector<QVariant> row; QVector<QVariant> row;
for (int i = 0; i < column_count; ++i) for (int i = 0; i < column_count; ++i)
row += extractData(stmt, i); row += extractData(stmt, i);
@ -661,8 +628,7 @@ void RawDatabase::process()
qWarning() << "Constraint error executing query" << anonQuery; qWarning() << "Constraint error executing query" << anonQuery;
goto cleanupStatements; goto cleanupStatements;
default: default:
qWarning() << "Unknown error" << result qWarning() << "Unknown error" << result << "executing query" << anonQuery;
<< "executing query" << anonQuery;
goto cleanupStatements; goto cleanupStatements;
} }
} }
@ -674,10 +640,9 @@ void RawDatabase::process()
if (trans.success != nullptr) if (trans.success != nullptr)
trans.success->store(true, std::memory_order_release); trans.success->store(true, std::memory_order_release);
// Free our statements // Free our statements
cleanupStatements: cleanupStatements:
for (Query& query : trans.queries) for (Query& query : trans.queries) {
{
for (sqlite3_stmt* stmt : query.statements) for (sqlite3_stmt* stmt : query.statements)
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
query.statements.clear(); query.statements.clear();
@ -711,25 +676,18 @@ QString RawDatabase::anonymizeQuery(const QByteArray& query)
* @param col Number of column to extract. * @param col Number of column to extract.
* @return Extracted data. * @return Extracted data.
*/ */
QVariant RawDatabase::extractData(sqlite3_stmt *stmt, int col) QVariant RawDatabase::extractData(sqlite3_stmt* stmt, int col)
{ {
int type = sqlite3_column_type(stmt, col); int type = sqlite3_column_type(stmt, col);
if (type == SQLITE_INTEGER) if (type == SQLITE_INTEGER) {
{
return sqlite3_column_int64(stmt, col); return sqlite3_column_int64(stmt, col);
} } else if (type == SQLITE_TEXT) {
else if (type == SQLITE_TEXT)
{
const char* str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, col)); const char* str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, col));
int len = sqlite3_column_bytes(stmt, col); int len = sqlite3_column_bytes(stmt, col);
return QString::fromUtf8(str, len); return QString::fromUtf8(str, len);
} } else if (type == SQLITE_NULL) {
else if (type == SQLITE_NULL)
{
return QVariant{}; return QVariant{};
} } else {
else
{
const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(stmt, col)); const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(stmt, col));
int len = sqlite3_column_bytes(stmt, col); int len = sqlite3_column_bytes(stmt, col);
return QByteArray::fromRawData(data, len); return QByteArray::fromRawData(data, len);

View File

@ -1,16 +1,16 @@
#ifndef RAWDATABASE_H #ifndef RAWDATABASE_H
#define RAWDATABASE_H #define RAWDATABASE_H
#include <QString>
#include <QByteArray> #include <QByteArray>
#include <QThread>
#include <QQueue>
#include <QVector>
#include <QPair>
#include <QMutex> #include <QMutex>
#include <QPair>
#include <QQueue>
#include <QString>
#include <QThread>
#include <QVariant> #include <QVariant>
#include <memory> #include <QVector>
#include <atomic> #include <atomic>
#include <memory>
struct sqlite3; struct sqlite3;
struct sqlite3_stmt; struct sqlite3_stmt;
@ -23,13 +23,25 @@ public:
class Query class Query
{ {
public: public:
Query(QString query, QVector<QByteArray> blobs = {}, std::function<void(int64_t)> insertCallback={}) Query(QString query, QVector<QByteArray> blobs = {},
: query{query.toUtf8()}, blobs{blobs}, insertCallback{insertCallback} {} std::function<void(int64_t)> insertCallback = {})
: query{query.toUtf8()}
, blobs{blobs}
, insertCallback{insertCallback}
{
}
Query(QString query, std::function<void(int64_t)> insertCallback) Query(QString query, std::function<void(int64_t)> insertCallback)
: query{query.toUtf8()}, insertCallback{insertCallback} {} : query{query.toUtf8()}
, insertCallback{insertCallback}
{
}
Query(QString query, std::function<void(const QVector<QVariant>&)> rowCallback) Query(QString query, std::function<void(const QVector<QVariant>&)> rowCallback)
: query{query.toUtf8()}, rowCallback{rowCallback} {} : query{query.toUtf8()}
, rowCallback{rowCallback}
{
}
Query() = default; Query() = default;
private: private:
QByteArray query; QByteArray query;
QVector<QByteArray> blobs; QVector<QByteArray> blobs;
@ -64,6 +76,7 @@ protected slots:
bool open(const QString& path, const QString& hexKey = {}); bool open(const QString& path, const QString& hexKey = {});
void close(); void close();
void process(); void process();
private: private:
QString anonymizeQuery(const QByteArray& query); QString anonymizeQuery(const QByteArray& query);

View File

@ -20,10 +20,10 @@
#include <QDebug> #include <QDebug>
#include <cassert> #include <cassert>
#include "db/rawdatabase.h"
#include "history.h" #include "history.h"
#include "profile.h" #include "profile.h"
#include "settings.h" #include "settings.h"
#include "db/rawdatabase.h"
/** /**
* @class History * @class History
@ -41,31 +41,31 @@
History::History(std::shared_ptr<RawDatabase> db) History::History(std::shared_ptr<RawDatabase> db)
: db(db) : db(db)
{ {
if (!isValid()) if (!isValid()) {
{
qWarning() << "Database not open, init failed"; qWarning() << "Database not open, init failed";
return; return;
} }
db->execLater("CREATE TABLE IF NOT EXISTS peers (id INTEGER PRIMARY KEY, public_key TEXT NOT NULL UNIQUE);" db->execLater(
"CREATE TABLE IF NOT EXISTS aliases (id INTEGER PRIMARY KEY, owner INTEGER," "CREATE TABLE IF NOT EXISTS peers (id INTEGER PRIMARY KEY, public_key TEXT NOT NULL "
"display_name BLOB NOT NULL, UNIQUE(owner, display_name));" "UNIQUE);"
"CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, " "CREATE TABLE IF NOT EXISTS aliases (id INTEGER PRIMARY KEY, owner INTEGER,"
"chat_id INTEGER NOT NULL, sender_alias INTEGER NOT NULL, " "display_name BLOB NOT NULL, UNIQUE(owner, display_name));"
"message BLOB NOT NULL);" "CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, "
"CREATE TABLE IF NOT EXISTS faux_offline_pending (id INTEGER PRIMARY KEY);"); "chat_id INTEGER NOT NULL, sender_alias INTEGER NOT NULL, "
"message BLOB NOT NULL);"
"CREATE TABLE IF NOT EXISTS faux_offline_pending (id INTEGER PRIMARY KEY);");
// Cache our current peers // Cache our current peers
db->execLater(RawDatabase::Query{"SELECT public_key, id FROM peers;", [this](const QVector<QVariant>& row) db->execLater(RawDatabase::Query{"SELECT public_key, id FROM peers;",
{ [this](const QVector<QVariant>& row) {
peers[row[0].toString()] = row[1].toInt(); peers[row[0].toString()] = row[1].toInt();
}}); }});
} }
History::~History() History::~History()
{ {
if (!isValid()) if (!isValid()) {
{
return; return;
} }
@ -88,16 +88,15 @@ bool History::isValid()
*/ */
void History::eraseHistory() void History::eraseHistory()
{ {
if (!isValid()) if (!isValid()) {
{
return; return;
} }
db->execNow("DELETE FROM faux_offline_pending;" db->execNow("DELETE FROM faux_offline_pending;"
"DELETE FROM history;" "DELETE FROM history;"
"DELETE FROM aliases;" "DELETE FROM aliases;"
"DELETE FROM peers;" "DELETE FROM peers;"
"VACUUM;"); "VACUUM;");
} }
/** /**
@ -106,36 +105,31 @@ void History::eraseHistory()
*/ */
void History::removeFriendHistory(const QString& friendPk) void History::removeFriendHistory(const QString& friendPk)
{ {
if (!isValid()) if (!isValid()) {
{
return; return;
} }
if (!peers.contains(friendPk)) if (!peers.contains(friendPk)) {
{
return; return;
} }
int64_t id = peers[friendPk]; int64_t id = peers[friendPk];
QString queryText = QString( QString queryText = QString("DELETE FROM faux_offline_pending "
"DELETE FROM faux_offline_pending " "WHERE faux_offline_pending.id IN ( "
"WHERE faux_offline_pending.id IN ( " " SELECT faux_offline_pending.id FROM faux_offline_pending "
" SELECT faux_offline_pending.id FROM faux_offline_pending " " LEFT JOIN history ON faux_offline_pending.id = history.id "
" LEFT JOIN history ON faux_offline_pending.id = history.id " " WHERE chat_id=%1 "
" WHERE chat_id=%1 " "); "
"); " "DELETE FROM history WHERE chat_id=%1; "
"DELETE FROM history WHERE chat_id=%1; " "DELETE FROM aliases WHERE owner=%1; "
"DELETE FROM aliases WHERE owner=%1; " "DELETE FROM peers WHERE id=%1; "
"DELETE FROM peers WHERE id=%1; " "VACUUM;")
"VACUUM;").arg(id); .arg(id);
if (db->execNow(queryText)) if (db->execNow(queryText)) {
{
peers.remove(friendPk); peers.remove(friendPk);
} } else {
else
{
qWarning() << "Failed to remove friend's history"; qWarning() << "Failed to remove friend's history";
} }
} }
@ -150,78 +144,73 @@ void History::removeFriendHistory(const QString& friendPk)
* @param dispName Name, which should be displayed. * @param dispName Name, which should be displayed.
* @param insertIdCallback Function, called after query execution. * @param insertIdCallback Function, called after query execution.
*/ */
QVector<RawDatabase::Query> History::generateNewMessageQueries(const QString& friendPk, const QString& message, QVector<RawDatabase::Query>
const QString& sender, const QDateTime& time, bool isSent, QString dispName, History::generateNewMessageQueries(const QString& friendPk, const QString& message,
std::function<void(int64_t)> insertIdCallback) const QString& sender, const QDateTime& time, bool isSent,
QString dispName, std::function<void(int64_t)> insertIdCallback)
{ {
QVector<RawDatabase::Query> queries; QVector<RawDatabase::Query> queries;
// Get the db id of the peer we're chatting with // Get the db id of the peer we're chatting with
int64_t peerId; int64_t peerId;
if (peers.contains(friendPk)) if (peers.contains(friendPk)) {
{
peerId = peers[friendPk]; peerId = peers[friendPk];
} } else {
else if (peers.isEmpty()) {
{
if (peers.isEmpty())
{
peerId = 0; peerId = 0;
} } else {
else
{
peerId = *std::max_element(peers.begin(), peers.end()) + 1; peerId = *std::max_element(peers.begin(), peers.end()) + 1;
} }
peers[friendPk] = peerId; peers[friendPk] = peerId;
queries += RawDatabase::Query(("INSERT INTO peers (id, public_key) " queries += RawDatabase::Query(("INSERT INTO peers (id, public_key) "
"VALUES (%1, '" + friendPk + "');") "VALUES (%1, '"
.arg(peerId)); + friendPk + "');")
.arg(peerId));
} }
// Get the db id of the sender of the message // Get the db id of the sender of the message
int64_t senderId; int64_t senderId;
if (peers.contains(sender)) if (peers.contains(sender)) {
{
senderId = peers[sender]; senderId = peers[sender];
} } else {
else if (peers.isEmpty()) {
{
if (peers.isEmpty())
{
senderId = 0; senderId = 0;
} } else {
else
{
senderId = *std::max_element(peers.begin(), peers.end()) + 1; senderId = *std::max_element(peers.begin(), peers.end()) + 1;
} }
peers[sender] = senderId; peers[sender] = senderId;
queries += RawDatabase::Query{("INSERT INTO peers (id, public_key) " queries += RawDatabase::Query{("INSERT INTO peers (id, public_key) "
"VALUES (%1, '" + sender + "');") "VALUES (%1, '"
.arg(senderId)}; + sender + "');")
.arg(senderId)};
} }
queries += RawDatabase::Query(QString("INSERT OR IGNORE INTO aliases (owner, display_name) VALUES (%1, ?);") queries += RawDatabase::Query(
.arg(senderId), {dispName.toUtf8()}); QString("INSERT OR IGNORE INTO aliases (owner, display_name) VALUES (%1, ?);").arg(senderId),
{dispName.toUtf8()});
// If the alias already existed, the insert will ignore the conflict and last_insert_rowid() will return garbage, // If the alias already existed, the insert will ignore the conflict and last_insert_rowid()
// will return garbage,
// so we have to check changes() and manually fetch the row ID in this case // so we have to check changes() and manually fetch the row ID in this case
queries += RawDatabase::Query(QString("INSERT INTO history (timestamp, chat_id, message, sender_alias) " queries +=
"VALUES (%1, %2, ?, (" RawDatabase::Query(QString(
" CASE WHEN changes() IS 0 THEN (" "INSERT INTO history (timestamp, chat_id, message, sender_alias) "
" SELECT id FROM aliases WHERE owner=%3 AND display_name=?)" "VALUES (%1, %2, ?, ("
" ELSE last_insert_rowid() END" " CASE WHEN changes() IS 0 THEN ("
"));") " SELECT id FROM aliases WHERE owner=%3 AND display_name=?)"
.arg(time.toMSecsSinceEpoch()).arg(peerId).arg(senderId), " ELSE last_insert_rowid() END"
{message.toUtf8(), dispName.toUtf8()}, insertIdCallback); "));")
.arg(time.toMSecsSinceEpoch())
.arg(peerId)
.arg(senderId),
{message.toUtf8(), dispName.toUtf8()}, insertIdCallback);
if (!isSent) if (!isSent) {
{ queries += RawDatabase::Query{"INSERT INTO faux_offline_pending (id) VALUES ("
queries += RawDatabase::Query{ " last_insert_rowid()"
"INSERT INTO faux_offline_pending (id) VALUES (" ");"};
" last_insert_rowid()"
");"};
} }
return queries; return queries;
@ -237,18 +226,16 @@ QVector<RawDatabase::Query> History::generateNewMessageQueries(const QString& fr
* @param dispName Name, which should be displayed. * @param dispName Name, which should be displayed.
* @param insertIdCallback Function, called after query execution. * @param insertIdCallback Function, called after query execution.
*/ */
void History::addNewMessage(const QString& friendPk, const QString& message, void History::addNewMessage(const QString& friendPk, const QString& message, const QString& sender,
const QString& sender, const QDateTime& time, const QDateTime& time, bool isSent, QString dispName,
bool isSent, QString dispName,
std::function<void(int64_t)> insertIdCallback) std::function<void(int64_t)> insertIdCallback)
{ {
if (!isValid()) if (!isValid()) {
{
return; return;
} }
db->execLater(generateNewMessageQueries(friendPk, message, sender, time, db->execLater(generateNewMessageQueries(friendPk, message, sender, time, isSent, dispName,
isSent, dispName, insertIdCallback)); insertIdCallback));
} }
/** /**
@ -258,33 +245,30 @@ void History::addNewMessage(const QString& friendPk, const QString& message,
* @param to End of period to fetch. * @param to End of period to fetch.
* @return List of messages. * @return List of messages.
*/ */
QList<History::HistMessage> History::getChatHistory(const QString& friendPk, QList<History::HistMessage> History::getChatHistory(const QString& friendPk, const QDateTime& from,
const QDateTime& from,
const QDateTime& to) const QDateTime& to)
{ {
if (!isValid()) if (!isValid()) {
{
return {}; return {};
} }
QList<HistMessage> messages; QList<HistMessage> messages;
auto rowCallback = [&messages](const QVector<QVariant>& row) auto rowCallback = [&messages](const QVector<QVariant>& row) {
{
// dispName and message could have null bytes, QString::fromUtf8 // dispName and message could have null bytes, QString::fromUtf8
// truncates on null bytes so we strip them // truncates on null bytes so we strip them
messages += {row[0].toLongLong(), messages += {row[0].toLongLong(),
row[1].isNull(), row[1].isNull(),
QDateTime::fromMSecsSinceEpoch(row[2].toLongLong()), QDateTime::fromMSecsSinceEpoch(row[2].toLongLong()),
row[3].toString(), row[3].toString(),
QString::fromUtf8(row[4].toByteArray().replace('\0',"")), QString::fromUtf8(row[4].toByteArray().replace('\0', "")),
row[5].toString(), row[5].toString(),
QString::fromUtf8(row[6].toByteArray().replace('\0',""))}; QString::fromUtf8(row[6].toByteArray().replace('\0', ""))};
}; };
// Don't forget to update the rowCallback if you change the selected columns! // Don't forget to update the rowCallback if you change the selected columns!
QString queryText = QString( QString queryText =
"SELECT history.id, faux_offline_pending.id, timestamp, " QString("SELECT history.id, faux_offline_pending.id, timestamp, "
"chat.public_key, aliases.display_name, sender.public_key, " "chat.public_key, aliases.display_name, sender.public_key, "
"message FROM history " "message FROM history "
"LEFT JOIN faux_offline_pending ON history.id = faux_offline_pending.id " "LEFT JOIN faux_offline_pending ON history.id = faux_offline_pending.id "
@ -292,7 +276,9 @@ QList<History::HistMessage> History::getChatHistory(const QString& friendPk,
"JOIN aliases ON sender_alias = aliases.id " "JOIN aliases ON sender_alias = aliases.id "
"JOIN peers sender ON aliases.owner = sender.id " "JOIN peers sender ON aliases.owner = sender.id "
"WHERE timestamp BETWEEN %1 AND %2 AND chat.public_key='%3';") "WHERE timestamp BETWEEN %1 AND %2 AND chat.public_key='%3';")
.arg(from.toMSecsSinceEpoch()).arg(to.toMSecsSinceEpoch()).arg(friendPk); .arg(from.toMSecsSinceEpoch())
.arg(to.toMSecsSinceEpoch())
.arg(friendPk);
db->execNow({queryText, rowCallback}); db->execNow({queryText, rowCallback});
@ -307,11 +293,9 @@ QList<History::HistMessage> History::getChatHistory(const QString& friendPk,
*/ */
void History::markAsSent(qint64 messageId) void History::markAsSent(qint64 messageId)
{ {
if (!isValid()) if (!isValid()) {
{
return; return;
} }
db->execLater(QString("DELETE FROM faux_offline_pending WHERE id=%1;") db->execLater(QString("DELETE FROM faux_offline_pending WHERE id=%1;").arg(messageId));
.arg(messageId));
} }

View File

@ -21,8 +21,8 @@
#define HISTORY_H #define HISTORY_H
#include <QDateTime> #include <QDateTime>
#include <QVector>
#include <QHash> #include <QHash>
#include <QVector>
#include <cstdint> #include <cstdint>
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
@ -33,14 +33,19 @@ class Profile;
class HistoryKeeper; class HistoryKeeper;
class History class History
{ {
public: public:
struct HistMessage struct HistMessage
{ {
HistMessage(qint64 id, bool isSent, QDateTime timestamp, QString chat, HistMessage(qint64 id, bool isSent, QDateTime timestamp, QString chat, QString dispName,
QString dispName, QString sender, QString message) QString sender, QString message)
: chat{chat}, sender{sender}, message{message}, dispName{dispName} : chat{chat}
, timestamp{timestamp}, id{id}, isSent{isSent} , sender{sender}
, message{message}
, dispName{dispName}
, timestamp{timestamp}
, id{id}
, isSent{isSent}
{ {
} }
@ -62,21 +67,19 @@ public:
void eraseHistory(); void eraseHistory();
void removeFriendHistory(const QString& friendPk); void removeFriendHistory(const QString& friendPk);
void addNewMessage(const QString& friendPk, const QString& message, void addNewMessage(const QString& friendPk, const QString& message, const QString& sender,
const QString& sender, const QDateTime& time, const QDateTime& time, bool isSent, QString dispName,
bool isSent, QString dispName, std::function<void(int64_t)> insertIdCallback = {});
std::function<void(int64_t)> insertIdCallback={});
QList<HistMessage> getChatHistory(const QString& friendPk, QList<HistMessage> getChatHistory(const QString& friendPk, const QDateTime& from,
const QDateTime& from,
const QDateTime& to); const QDateTime& to);
void markAsSent(qint64 messageId); void markAsSent(qint64 messageId);
protected: protected:
QVector<RawDatabase::Query> generateNewMessageQueries( QVector<RawDatabase::Query>
const QString& friendPk, const QString& message, generateNewMessageQueries(const QString& friendPk, const QString& message,
const QString& sender, const QDateTime& time, const QString& sender, const QDateTime& time, bool isSent,
bool isSent, QString dispName, QString dispName, std::function<void(int64_t)> insertIdCallback = {});
std::function<void(int64_t)> insertIdCallback={});
private: private:
std::shared_ptr<RawDatabase> db; std::shared_ptr<RawDatabase> db;

View File

@ -18,11 +18,11 @@
*/ */
#include "offlinemsgengine.h" #include "offlinemsgengine.h"
#include "src/friend.h"
#include "src/persistence/settings.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/friend.h"
#include "src/nexus.h" #include "src/nexus.h"
#include "src/persistence/profile.h" #include "src/persistence/profile.h"
#include "src/persistence/settings.h"
#include <QMutexLocker> #include <QMutexLocker>
#include <QTimer> #include <QTimer>
@ -38,9 +38,9 @@
const int OfflineMsgEngine::offlineTimeout = 20000; const int OfflineMsgEngine::offlineTimeout = 20000;
QMutex OfflineMsgEngine::globalMutex; QMutex OfflineMsgEngine::globalMutex;
OfflineMsgEngine::OfflineMsgEngine(Friend* frnd) : OfflineMsgEngine::OfflineMsgEngine(Friend* frnd)
mutex(QMutex::Recursive), : mutex(QMutex::Recursive)
f(frnd) , f(frnd)
{ {
} }
@ -54,12 +54,10 @@ void OfflineMsgEngine::dischargeReceipt(int receipt)
Profile* profile = Nexus::getProfile(); Profile* profile = Nexus::getProfile();
auto it = receipts.find(receipt); auto it = receipts.find(receipt);
if (it != receipts.end()) if (it != receipts.end()) {
{
int mID = it.value(); int mID = it.value();
auto msgIt = undeliveredMsgs.find(mID); auto msgIt = undeliveredMsgs.find(mID);
if (msgIt != undeliveredMsgs.end()) if (msgIt != undeliveredMsgs.end()) {
{
if (profile->isHistoryEnabled()) if (profile->isHistoryEnabled())
profile->getHistory()->markAsSent(mID); profile->getHistory()->markAsSent(mID);
msgIt.value().msg->markAsSent(QDateTime::currentDateTime()); msgIt.value().msg->markAsSent(QDateTime::currentDateTime());
@ -69,7 +67,8 @@ void OfflineMsgEngine::dischargeReceipt(int receipt)
} }
} }
void OfflineMsgEngine::registerReceipt(int receipt, int64_t messageID, ChatMessage::Ptr msg, const QDateTime &timestamp) void OfflineMsgEngine::registerReceipt(int receipt, int64_t messageID, ChatMessage::Ptr msg,
const QDateTime& timestamp)
{ {
QMutexLocker ml(&mutex); QMutexLocker ml(&mutex);
@ -94,24 +93,19 @@ void OfflineMsgEngine::deliverOfflineMsgs()
removeAllReceipts(); removeAllReceipts();
undeliveredMsgs.clear(); undeliveredMsgs.clear();
for (auto iter = msgs.begin(); iter != msgs.end(); ++iter) for (auto iter = msgs.begin(); iter != msgs.end(); ++iter) {
{
auto val = iter.value(); auto val = iter.value();
auto key = iter.key(); auto key = iter.key();
if (val.timestamp.msecsTo(QDateTime::currentDateTime()) < offlineTimeout) if (val.timestamp.msecsTo(QDateTime::currentDateTime()) < offlineTimeout) {
{
registerReceipt(val.receipt, key, val.msg, val.timestamp); registerReceipt(val.receipt, key, val.msg, val.timestamp);
continue; continue;
} }
QString messageText = val.msg->toString(); QString messageText = val.msg->toString();
int rec; int rec;
if (val.msg->isAction()) if (val.msg->isAction()) {
{
rec = Core::getInstance()->sendAction(f->getFriendId(), messageText); rec = Core::getInstance()->sendAction(f->getFriendId(), messageText);
} } else {
else
{
rec = Core::getInstance()->sendMessage(f->getFriendId(), messageText); rec = Core::getInstance()->sendMessage(f->getFriendId(), messageText);
} }

View File

@ -20,12 +20,12 @@
#ifndef OFFLINEMSGENGINE_H #ifndef OFFLINEMSGENGINE_H
#define OFFLINEMSGENGINE_H #define OFFLINEMSGENGINE_H
#include <QObject> #include "src/chatlog/chatmessage.h"
#include <QSet>
#include <QMutex>
#include <QDateTime> #include <QDateTime>
#include <QMap> #include <QMap>
#include "src/chatlog/chatmessage.h" #include <QMutex>
#include <QObject>
#include <QSet>
class Friend; class Friend;
class QTimer; class QTimer;
@ -34,19 +34,21 @@ class OfflineMsgEngine : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit OfflineMsgEngine(Friend *); explicit OfflineMsgEngine(Friend*);
virtual ~OfflineMsgEngine(); virtual ~OfflineMsgEngine();
static QMutex globalMutex; static QMutex globalMutex;
void dischargeReceipt(int receipt); void dischargeReceipt(int receipt);
void registerReceipt(int receipt, int64_t messageID, ChatMessage::Ptr msg, const QDateTime &timestamp = QDateTime::currentDateTime()); void registerReceipt(int receipt, int64_t messageID, ChatMessage::Ptr msg,
const QDateTime& timestamp = QDateTime::currentDateTime());
public slots: public slots:
void deliverOfflineMsgs(); void deliverOfflineMsgs();
void removeAllReceipts(); void removeAllReceipts();
private: private:
struct MsgPtr { struct MsgPtr
{
ChatMessage::Ptr msg; ChatMessage::Ptr msg;
QDateTime timestamp; QDateTime timestamp;
int receipt; int receipt;

View File

@ -50,8 +50,10 @@
QVector<QString> Profile::profiles; QVector<QString> Profile::profiles;
Profile::Profile(QString name, const QString& password, bool isNewProfile) Profile::Profile(QString name, const QString& password, bool isNewProfile)
: name{name}, password{password} : name{name}
, newProfile{isNewProfile}, isRemoved{false} , password{password}
, newProfile{isNewProfile}
, isRemoved{false}
{ {
Settings& s = Settings::getInstance(); Settings& s = Settings::getInstance();
s.setCurrentProfile(name); s.setCurrentProfile(name);
@ -75,15 +77,14 @@ Profile::Profile(QString name, const QString& password, bool isNewProfile)
*/ */
Profile* Profile::loadProfile(QString name, const QString& password) Profile* Profile::loadProfile(QString name, const QString& password)
{ {
if (ProfileLocker::hasLock()) if (ProfileLocker::hasLock()) {
{ qCritical() << "Tried to load profile " << name
qCritical() << "Tried to load profile "<<name<<", but another profile is already locked!"; << ", but another profile is already locked!";
return nullptr; return nullptr;
} }
if (!ProfileLocker::lock(name)) if (!ProfileLocker::lock(name)) {
{ qWarning() << "Failed to lock profile " << name;
qWarning() << "Failed to lock profile "<<name;
return nullptr; return nullptr;
} }
@ -93,60 +94,50 @@ Profile* Profile::loadProfile(QString name, const QString& password)
{ {
QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox"; QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox";
QFile saveFile(path); QFile saveFile(path);
qDebug() << "Loading tox save "<<path; qDebug() << "Loading tox save " << path;
if (!saveFile.exists()) if (!saveFile.exists()) {
{ qWarning() << "The tox save file " << path << " was not found";
qWarning() << "The tox save file "<<path<<" was not found";
ProfileLocker::unlock(); ProfileLocker::unlock();
return nullptr; return nullptr;
} }
if (!saveFile.open(QIODevice::ReadOnly)) if (!saveFile.open(QIODevice::ReadOnly)) {
{
qCritical() << "The tox save file " << path << " couldn't' be opened"; qCritical() << "The tox save file " << path << " couldn't' be opened";
ProfileLocker::unlock(); ProfileLocker::unlock();
return nullptr; return nullptr;
} }
qint64 fileSize = saveFile.size(); qint64 fileSize = saveFile.size();
if (fileSize <= 0) if (fileSize <= 0) {
{ qWarning() << "The tox save file" << path << " is empty!";
qWarning() << "The tox save file"<<path<<" is empty!";
ProfileLocker::unlock(); ProfileLocker::unlock();
return nullptr; return nullptr;
} }
QByteArray data = saveFile.readAll(); QByteArray data = saveFile.readAll();
if (ToxEncrypt::isEncrypted(data)) if (ToxEncrypt::isEncrypted(data)) {
{ if (password.isEmpty()) {
if (password.isEmpty())
{
qCritical() << "The tox save file is encrypted, but we don't have a password!"; qCritical() << "The tox save file is encrypted, but we don't have a password!";
ProfileLocker::unlock(); ProfileLocker::unlock();
return nullptr; return nullptr;
} }
tmpKey = ToxEncrypt::makeToxEncrypt(password, data); tmpKey = ToxEncrypt::makeToxEncrypt(password, data);
if (!tmpKey) if (!tmpKey) {
{
qCritical() << "Failed to derive key of the tox save file"; qCritical() << "Failed to derive key of the tox save file";
ProfileLocker::unlock(); ProfileLocker::unlock();
return nullptr; return nullptr;
} }
data = tmpKey->decrypt(data); data = tmpKey->decrypt(data);
if (data.isEmpty()) if (data.isEmpty()) {
{
qCritical() << "Failed to decrypt the tox save file"; qCritical() << "Failed to decrypt the tox save file";
ProfileLocker::unlock(); ProfileLocker::unlock();
return nullptr; return nullptr;
} }
} } else {
else if (!password.isEmpty()) {
{
if (!password.isEmpty())
{
qWarning() << "We have a password, but the tox save file is not encrypted"; qWarning() << "We have a password, but the tox save file is not encrypted";
} }
} }
@ -169,31 +160,27 @@ Profile* Profile::loadProfile(QString name, const QString& password)
Profile* Profile::createProfile(QString name, QString password) Profile* Profile::createProfile(QString name, QString password)
{ {
std::unique_ptr<ToxEncrypt> tmpKey; std::unique_ptr<ToxEncrypt> tmpKey;
if(!password.isEmpty()) if (!password.isEmpty()) {
{
tmpKey = ToxEncrypt::makeToxEncrypt(password); tmpKey = ToxEncrypt::makeToxEncrypt(password);
if (!tmpKey) if (!tmpKey) {
{
qCritical() << "Failed to derive key for the tox save"; qCritical() << "Failed to derive key for the tox save";
return nullptr; return nullptr;
} }
} }
if (ProfileLocker::hasLock()) if (ProfileLocker::hasLock()) {
{ qCritical() << "Tried to create profile " << name
qCritical() << "Tried to create profile "<<name<<", but another profile is already locked!"; << ", but another profile is already locked!";
return nullptr; return nullptr;
} }
if (exists(name)) if (exists(name)) {
{ qCritical() << "Tried to create profile " << name << ", but it already exists!";
qCritical() << "Tried to create profile "<<name<<", but it already exists!";
return nullptr; return nullptr;
} }
if (!ProfileLocker::lock(name)) if (!ProfileLocker::lock(name)) {
{ qWarning() << "Failed to lock profile " << name;
qWarning() << "Failed to lock profile "<<name;
return nullptr; return nullptr;
} }
@ -206,8 +193,7 @@ Profile* Profile::createProfile(QString name, QString password)
Profile::~Profile() Profile::~Profile()
{ {
if (!isRemoved && core->isReady()) if (!isRemoved && core->isReady()) {
{
saveToxSave(); saveToxSave();
} }
@ -216,8 +202,7 @@ Profile::~Profile()
qApp->processEvents(); qApp->processEvents();
delete coreThread; delete coreThread;
if (!isRemoved) if (!isRemoved) {
{
Settings::getInstance().savePersonal(this); Settings::getInstance().savePersonal(this);
Settings::getInstance().sync(); Settings::getInstance().sync();
ProfileLocker::assertLock(); ProfileLocker::assertLock();
@ -236,11 +221,10 @@ QVector<QString> Profile::getFilesByExt(QString extension)
QDir dir(Settings::getInstance().getSettingsDirPath()); QDir dir(Settings::getInstance().getSettingsDirPath());
QVector<QString> out; QVector<QString> out;
dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
dir.setNameFilters(QStringList("*."+extension)); dir.setNameFilters(QStringList("*." + extension));
QFileInfoList list = dir.entryInfoList(); QFileInfoList list = dir.entryInfoList();
out.reserve(list.size()); out.reserve(list.size());
for (QFileInfo file : list) for (QFileInfo file : list) {
{
out += file.completeBaseName(); out += file.completeBaseName();
} }
@ -255,10 +239,8 @@ void Profile::scanProfiles()
{ {
profiles.clear(); profiles.clear();
QVector<QString> toxfiles = getFilesByExt("tox"), inifiles = getFilesByExt("ini"); QVector<QString> toxfiles = getFilesByExt("tox"), inifiles = getFilesByExt("ini");
for (QString toxfile : toxfiles) for (QString toxfile : toxfiles) {
{ if (!inifiles.contains(toxfile)) {
if (!inifiles.contains(toxfile))
{
Settings::getInstance().createPersonal(toxfile); Settings::getInstance().createPersonal(toxfile);
} }
@ -306,49 +288,40 @@ QByteArray Profile::loadToxSave()
QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox"; QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox";
QFile saveFile(path); QFile saveFile(path);
qint64 fileSize; qint64 fileSize;
qDebug() << "Loading tox save "<<path; qDebug() << "Loading tox save " << path;
if (!saveFile.exists()) if (!saveFile.exists()) {
{ qWarning() << "The tox save file " << path << " was not found";
qWarning() << "The tox save file "<<path<<" was not found";
goto fail; goto fail;
} }
if (!saveFile.open(QIODevice::ReadOnly)) if (!saveFile.open(QIODevice::ReadOnly)) {
{
qCritical() << "The tox save file " << path << " couldn't' be opened"; qCritical() << "The tox save file " << path << " couldn't' be opened";
goto fail; goto fail;
} }
fileSize = saveFile.size(); fileSize = saveFile.size();
if (fileSize <= 0) if (fileSize <= 0) {
{ qWarning() << "The tox save file" << path << " is empty!";
qWarning() << "The tox save file"<<path<<" is empty!";
goto fail; goto fail;
} }
data = saveFile.readAll(); data = saveFile.readAll();
if (ToxEncrypt::isEncrypted(data)) if (ToxEncrypt::isEncrypted(data)) {
{ if (password.isEmpty()) {
if (password.isEmpty())
{
qCritical() << "The tox save file is encrypted, but we don't have a password!"; qCritical() << "The tox save file is encrypted, but we don't have a password!";
data.clear(); data.clear();
goto fail; goto fail;
} }
data = passkey->decrypt(data); data = passkey->decrypt(data);
if (data.isEmpty()) if (data.isEmpty()) {
{
qCritical() << "Failed to decrypt the tox save file"; qCritical() << "Failed to decrypt the tox save file";
data.clear(); data.clear();
goto fail; goto fail;
} }
} } else {
else if (!password.isEmpty()) {
{
if (!password.isEmpty())
{
qWarning() << "We have a password, but the tox save file is not encrypted"; qWarning() << "We have a password, but the tox save file is not encrypted";
} }
} }
@ -382,19 +355,16 @@ void Profile::saveToxSave(QByteArray data)
assert(ProfileLocker::getCurLockName() == name); assert(ProfileLocker::getCurLockName() == name);
QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox"; QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox";
qDebug() << "Saving tox save to "<<path; qDebug() << "Saving tox save to " << path;
QSaveFile saveFile(path); QSaveFile saveFile(path);
if (!saveFile.open(QIODevice::WriteOnly)) if (!saveFile.open(QIODevice::WriteOnly)) {
{
qCritical() << "Tox save file " << path << " couldn't be opened"; qCritical() << "Tox save file " << path << " couldn't be opened";
return; return;
} }
if (!password.isEmpty()) if (!password.isEmpty()) {
{
data = passkey->encrypt(data); data = passkey->encrypt(data);
if (data.isEmpty()) if (data.isEmpty()) {
{
qCritical() << "Failed to encrypt, can't save!"; qCritical() << "Failed to encrypt, can't save!";
saveFile.cancelWriting(); saveFile.cancelWriting();
return; return;
@ -404,22 +374,21 @@ void Profile::saveToxSave(QByteArray data)
saveFile.write(data); saveFile.write(data);
// check if everything got written // check if everything got written
if (saveFile.flush()) if (saveFile.flush()) {
{
saveFile.commit(); saveFile.commit();
newProfile = false; newProfile = false;
} } else {
else
{
saveFile.cancelWriting(); saveFile.cancelWriting();
qCritical() << "Failed to write, can't save!"; qCritical() << "Failed to write, can't save!";
} }
} }
/** /**
* @brief Gets the path of the avatar file cached by this profile and corresponding to this owner ID. * @brief Gets the path of the avatar file cached by this profile and corresponding to this owner
* ID.
* @param ownerId Path to avatar of friend with this ID will returned. * @param ownerId Path to avatar of friend with this ID will returned.
* @param forceUnencrypted If true, return the path to the plaintext file even if this is an encrypted profile. * @param forceUnencrypted If true, return the path to the plaintext file even if this is an
* encrypted profile.
* @return Path to the avatar. * @return Path to the avatar.
*/ */
QString Profile::avatarPath(const QString& ownerId, bool forceUnencrypted) QString Profile::avatarPath(const QString& ownerId, bool forceUnencrypted)
@ -430,13 +399,16 @@ QString Profile::avatarPath(const QString& ownerId, bool forceUnencrypted)
QByteArray idData = ownerId.toUtf8(); QByteArray idData = ownerId.toUtf8();
QByteArray pubkeyData = core->getSelfId().getPublicKey().getKey(); QByteArray pubkeyData = core->getSelfId().getPublicKey().getKey();
constexpr int hashSize = TOX_PUBLIC_KEY_SIZE; constexpr int hashSize = TOX_PUBLIC_KEY_SIZE;
static_assert(hashSize >= crypto_generichash_BYTES_MIN static_assert(hashSize >= crypto_generichash_BYTES_MIN && hashSize <= crypto_generichash_BYTES_MAX,
&& hashSize <= crypto_generichash_BYTES_MAX, "Hash size not supported by libsodium"); "Hash size not supported by libsodium");
static_assert(hashSize >= crypto_generichash_KEYBYTES_MIN static_assert(hashSize >= crypto_generichash_KEYBYTES_MIN
&& hashSize <= crypto_generichash_KEYBYTES_MAX, "Key size not supported by libsodium"); && hashSize <= crypto_generichash_KEYBYTES_MAX,
"Key size not supported by libsodium");
QByteArray hash(hashSize, 0); QByteArray hash(hashSize, 0);
crypto_generichash((uint8_t*)hash.data(), hashSize, (uint8_t*)idData.data(), idData.size(), (uint8_t*)pubkeyData.data(), pubkeyData.size()); crypto_generichash((uint8_t*)hash.data(), hashSize, (uint8_t*)idData.data(), idData.size(),
return Settings::getInstance().getSettingsDirPath() + "avatars/" + hash.toHex().toUpper() + ".png"; (uint8_t*)pubkeyData.data(), pubkeyData.size());
return Settings::getInstance().getSettingsDirPath() + "avatars/" + hash.toHex().toUpper()
+ ".png";
} }
/** /**
@ -467,7 +439,7 @@ QPixmap Profile::loadAvatar(const QString& ownerId)
*/ */
QByteArray Profile::loadAvatarData(const QString& ownerId) QByteArray Profile::loadAvatarData(const QString& ownerId)
{ {
return loadAvatarData(ownerId, password); return loadAvatarData(ownerId, password);
} }
/** /**
@ -482,21 +454,18 @@ QByteArray Profile::loadAvatarData(const QString& ownerId, const QString& passwo
bool encrypted = !password.isEmpty(); bool encrypted = !password.isEmpty();
// If the encrypted avatar isn't found, try loading the unencrypted one for the same ID // If the encrypted avatar isn't found, try loading the unencrypted one for the same ID
if (!password.isEmpty() && !QFile::exists(path)) if (!password.isEmpty() && !QFile::exists(path)) {
{
encrypted = false; encrypted = false;
path = avatarPath(ownerId, true); path = avatarPath(ownerId, true);
} }
QFile file(path); QFile file(path);
if (!file.open(QIODevice::ReadOnly)) if (!file.open(QIODevice::ReadOnly)) {
{
return {}; return {};
} }
QByteArray pic = file.readAll(); QByteArray pic = file.readAll();
if (encrypted && !pic.isEmpty()) if (encrypted && !pic.isEmpty()) {
{
// TODO: check if we can use passkey-decrypt(pic) here // TODO: check if we can use passkey-decrypt(pic) here
pic = ToxEncrypt::decryptPass(password, pic); pic = ToxEncrypt::decryptPass(password, pic);
} }
@ -506,29 +475,27 @@ QByteArray Profile::loadAvatarData(const QString& ownerId, const QString& passwo
void Profile::loadDatabase(const ToxId& id) void Profile::loadDatabase(const ToxId& id)
{ {
if(isRemoved) if (isRemoved) {
{
qDebug() << "Can't load database of removed profile"; qDebug() << "Can't load database of removed profile";
return; return;
} }
QByteArray salt = id.getPublicKey().getKey(); QByteArray salt = id.getPublicKey().getKey();
if(salt.size() != TOX_PASS_SALT_LENGTH) if (salt.size() != TOX_PASS_SALT_LENGTH) {
{
qWarning() << "Couldn't compute salt from public key" << name; qWarning() << "Couldn't compute salt from public key" << name;
GUI::showError(QObject::tr("Error"), QObject::tr("qTox couldn't open your chat logs, they will be disabled.")); GUI::showError(QObject::tr("Error"),
QObject::tr("qTox couldn't open your chat logs, they will be disabled."));
} }
// At this point it's too early to load the personal settings (Nexus will do it), so we always load // At this point it's too early to load the personal settings (Nexus will do it), so we always
// load
// the history, and if it fails we can't change the setting now, but we keep a nullptr // the history, and if it fails we can't change the setting now, but we keep a nullptr
database = std::make_shared<RawDatabase>(getDbPath(name), password, salt); database = std::make_shared<RawDatabase>(getDbPath(name), password, salt);
if (database && database->isOpen()) if (database && database->isOpen()) {
{
history.reset(new History(database)); history.reset(new History(database));
} } else {
else
{
qWarning() << "Failed to open database for profile" << name; qWarning() << "Failed to open database for profile" << name;
GUI::showError(QObject::tr("Error"), QObject::tr("qTox couldn't open your chat logs, they will be disabled.")); GUI::showError(QObject::tr("Error"),
QObject::tr("qTox couldn't open your chat logs, they will be disabled."));
} }
} }
@ -539,22 +506,17 @@ void Profile::loadDatabase(const ToxId& id)
*/ */
void Profile::saveAvatar(QByteArray pic, const QString& ownerId) void Profile::saveAvatar(QByteArray pic, const QString& ownerId)
{ {
if (!password.isEmpty() && !pic.isEmpty()) if (!password.isEmpty() && !pic.isEmpty()) {
{
pic = passkey->encrypt(pic); pic = passkey->encrypt(pic);
} }
QString path = avatarPath(ownerId); QString path = avatarPath(ownerId);
QDir(Settings::getInstance().getSettingsDirPath()).mkdir("avatars"); QDir(Settings::getInstance().getSettingsDirPath()).mkdir("avatars");
if (pic.isEmpty()) if (pic.isEmpty()) {
{
QFile::remove(path); QFile::remove(path);
} } else {
else
{
QSaveFile file(path); QSaveFile file(path);
if (!file.open(QIODevice::WriteOnly)) if (!file.open(QIODevice::WriteOnly)) {
{
qWarning() << "Tox avatar " << path << " couldn't be saved"; qWarning() << "Tox avatar " << path << " couldn't be saved";
return; return;
} }
@ -585,7 +547,8 @@ void Profile::removeAvatar()
} }
/** /**
* @brief Checks that the history is enabled in the settings, and loaded successfully for this profile. * @brief Checks that the history is enabled in the settings, and loaded successfully for this
* profile.
* @return True if enabled, false otherwise. * @return True if enabled, false otherwise.
*/ */
bool Profile::isHistoryEnabled() bool Profile::isHistoryEnabled()
@ -616,7 +579,7 @@ void Profile::removeAvatar(const QString& ownerId)
bool Profile::exists(QString name) bool Profile::exists(QString name)
{ {
QString path = Settings::getInstance().getSettingsDirPath() + name; QString path = Settings::getInstance().getSettingsDirPath() + name;
return QFile::exists(path+".tox"); return QFile::exists(path + ".tox");
} }
/** /**
@ -639,9 +602,8 @@ bool Profile::isEncrypted(QString name)
uint8_t data[TOX_PASS_ENCRYPTION_EXTRA_LENGTH] = {0}; uint8_t data[TOX_PASS_ENCRYPTION_EXTRA_LENGTH] = {0};
QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox"; QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox";
QFile saveFile(path); QFile saveFile(path);
if (!saveFile.open(QIODevice::ReadOnly)) if (!saveFile.open(QIODevice::ReadOnly)) {
{ qWarning() << "Couldn't open tox save " << path;
qWarning() << "Couldn't open tox save "<<path;
return false; return false;
} }
@ -659,18 +621,15 @@ bool Profile::isEncrypted(QString name)
*/ */
QVector<QString> Profile::remove() QVector<QString> Profile::remove()
{ {
if (isRemoved) if (isRemoved) {
{
qWarning() << "Profile " << name << " is already removed!"; qWarning() << "Profile " << name << " is already removed!";
return {}; return {};
} }
isRemoved = true; isRemoved = true;
qDebug() << "Removing profile" << name; qDebug() << "Removing profile" << name;
for (int i=0; i<profiles.size(); ++i) for (int i = 0; i < profiles.size(); ++i) {
{ if (profiles[i] == name) {
if (profiles[i] == name)
{
profiles.removeAt(i); profiles.removeAt(i);
i--; i--;
} }
@ -678,25 +637,22 @@ QVector<QString> Profile::remove()
QString path = Settings::getInstance().getSettingsDirPath() + name; QString path = Settings::getInstance().getSettingsDirPath() + name;
ProfileLocker::unlock(); ProfileLocker::unlock();
QFile profileMain {path + ".tox"}; QFile profileMain{path + ".tox"};
QFile profileConfig {path + ".ini"}; QFile profileConfig{path + ".ini"};
QVector<QString> ret; QVector<QString> ret;
if (!profileMain.remove() && profileMain.exists()) if (!profileMain.remove() && profileMain.exists()) {
{
ret.push_back(profileMain.fileName()); ret.push_back(profileMain.fileName());
qWarning() << "Could not remove file " << profileMain.fileName(); qWarning() << "Could not remove file " << profileMain.fileName();
} }
if (!profileConfig.remove() && profileConfig.exists()) if (!profileConfig.remove() && profileConfig.exists()) {
{
ret.push_back(profileConfig.fileName()); ret.push_back(profileConfig.fileName());
qWarning() << "Could not remove file " << profileConfig.fileName(); qWarning() << "Could not remove file " << profileConfig.fileName();
} }
QString dbPath = getDbPath(name); QString dbPath = getDbPath(name);
if (database && database->isOpen() && !database->remove() && QFile::exists(dbPath)) if (database && database->isOpen() && !database->remove() && QFile::exists(dbPath)) {
{
ret.push_back(dbPath); ret.push_back(dbPath);
qWarning() << "Could not remove file " << dbPath; qWarning() << "Could not remove file " << dbPath;
} }
@ -717,23 +673,20 @@ bool Profile::rename(QString newName)
QString path = Settings::getInstance().getSettingsDirPath() + name, QString path = Settings::getInstance().getSettingsDirPath() + name,
newPath = Settings::getInstance().getSettingsDirPath() + newName; newPath = Settings::getInstance().getSettingsDirPath() + newName;
if (!ProfileLocker::lock(newName)) if (!ProfileLocker::lock(newName)) {
{
return false; return false;
} }
QFile::rename(path + ".tox", newPath + ".tox"); QFile::rename(path + ".tox", newPath + ".tox");
QFile::rename(path + ".ini", newPath + ".ini"); QFile::rename(path + ".ini", newPath + ".ini");
if (database) if (database) {
{
database->rename(newName); database->rename(newName);
} }
bool resetAutorun = Settings::getInstance().getAutorun(); bool resetAutorun = Settings::getInstance().getAutorun();
Settings::getInstance().setAutorun(false); Settings::getInstance().setAutorun(false);
Settings::getInstance().setCurrentProfile(newName); Settings::getInstance().setCurrentProfile(newName);
if (resetAutorun) if (resetAutorun) {
{
Settings::getInstance().setAutorun(true); // fixes -p flag in autostart command line Settings::getInstance().setAutorun(true); // fixes -p flag in autostart command line
} }
@ -747,8 +700,7 @@ bool Profile::rename(QString newName)
*/ */
bool Profile::checkPassword() bool Profile::checkPassword()
{ {
if (isRemoved) if (isRemoved) {
{
return false; return false;
} }
@ -771,8 +723,7 @@ const ToxEncrypt& Profile::getPasskey() const
void Profile::restartCore() void Profile::restartCore()
{ {
GUI::setEnabled(false); // Core::reset re-enables it GUI::setEnabled(false); // Core::reset re-enables it
if (!isRemoved && core->isReady()) if (!isRemoved && core->isReady()) {
{
saveToxSave(); saveToxSave();
} }
@ -790,8 +741,7 @@ void Profile::setPassword(const QString& newPassword)
std::unique_ptr<ToxEncrypt> oldpasskey = std::move(passkey); std::unique_ptr<ToxEncrypt> oldpasskey = std::move(passkey);
password = newPassword; password = newPassword;
passkey = ToxEncrypt::makeToxEncrypt(password); passkey = ToxEncrypt::makeToxEncrypt(password);
if(!passkey) if (!passkey) {
{
qCritical() << "Failed to derive key from password, the profile won't use the new password"; qCritical() << "Failed to derive key from password, the profile won't use the new password";
password = oldPassword; password = oldPassword;
passkey = std::move(oldpasskey); passkey = std::move(oldpasskey);
@ -800,8 +750,7 @@ void Profile::setPassword(const QString& newPassword)
saveToxSave(); saveToxSave();
// TODO: ensure the database and the tox save file use the same password // TODO: ensure the database and the tox save file use the same password
if (database) if (database) {
{
database->setPassword(newPassword); database->setPassword(newPassword);
} }
@ -810,8 +759,7 @@ void Profile::setPassword(const QString& newPassword)
QVector<uint32_t> friendList = core->getFriendList(); QVector<uint32_t> friendList = core->getFriendList();
QVectorIterator<uint32_t> i(friendList); QVectorIterator<uint32_t> i(friendList);
while (i.hasNext()) while (i.hasNext()) {
{
QString friendPublicKey = core->getFriendPublicKey(i.next()).toString(); QString friendPublicKey = core->getFriendPublicKey(i.next()).toString();
saveAvatar(loadAvatarData(friendPublicKey, oldPassword), friendPublicKey); saveAvatar(loadAvatarData(friendPublicKey, oldPassword), friendPublicKey);
} }

View File

@ -21,17 +21,17 @@
#ifndef PROFILE_H #ifndef PROFILE_H
#define PROFILE_H #define PROFILE_H
#include "src/core/toxid.h"
#include "src/core/toxencrypt.h" #include "src/core/toxencrypt.h"
#include "src/core/toxid.h"
#include "src/persistence/history.h" #include "src/persistence/history.h"
#include <memory>
#include <QByteArray> #include <QByteArray>
#include <QObject> #include <QObject>
#include <QPixmap> #include <QPixmap>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include <memory>
class Core; class Core;
class QThread; class QThread;
@ -86,6 +86,7 @@ public:
private slots: private slots:
void loadDatabase(const ToxId& id); void loadDatabase(const ToxId& id);
private: private:
Profile(QString name, const QString& password, bool newProfile); Profile(QString name, const QString& password, bool newProfile);
static QVector<QString> getFilesByExt(QString extension); static QVector<QString> getFilesByExt(QString extension);

View File

@ -20,8 +20,8 @@
#include "profilelocker.h" #include "profilelocker.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include <QDir>
#include <QDebug> #include <QDebug>
#include <QDir>
/** /**
* @class ProfileLocker * @class ProfileLocker
@ -38,7 +38,7 @@ QString ProfileLocker::curLockName;
QString ProfileLocker::lockPathFromName(const QString& name) QString ProfileLocker::lockPathFromName(const QString& name)
{ {
return Settings::getInstance().getSettingsDirPath()+'/'+name+".lock"; return Settings::getInstance().getSettingsDirPath() + '/' + name + ".lock";
} }
/** /**
@ -71,8 +71,7 @@ bool ProfileLocker::lock(QString profile)
QLockFile* newLock = new QLockFile(lockPathFromName(profile)); QLockFile* newLock = new QLockFile(lockPathFromName(profile));
newLock->setStaleLockTime(0); newLock->setStaleLockTime(0);
if (!newLock->tryLock()) if (!newLock->tryLock()) {
{
delete newLock; delete newLock;
return false; return false;
} }
@ -105,22 +104,17 @@ void ProfileLocker::unlock()
*/ */
void ProfileLocker::assertLock() void ProfileLocker::assertLock()
{ {
if (!lockfile) if (!lockfile) {
{
qCritical() << "assertLock: We don't seem to own any lock!"; qCritical() << "assertLock: We don't seem to own any lock!";
deathByBrokenLock(); deathByBrokenLock();
} }
if (!QFile(lockPathFromName(curLockName)).exists()) if (!QFile(lockPathFromName(curLockName)).exists()) {
{
QString tmp = curLockName; QString tmp = curLockName;
unlock(); unlock();
if (lock(tmp)) if (lock(tmp)) {
{
qCritical() << "assertLock: Lock file was lost, but could be restored"; qCritical() << "assertLock: Lock file was lost, but could be restored";
} } else {
else
{
qCritical() << "assertLock: Lock file was lost, and could *NOT* be restored"; qCritical() << "assertLock: Lock file was lost, and could *NOT* be restored";
deathByBrokenLock(); deathByBrokenLock();
} }

View File

@ -27,7 +27,7 @@
class ProfileLocker class ProfileLocker
{ {
private: private:
ProfileLocker()=delete; ProfileLocker() = delete;
public: public:
static bool isLockable(QString profile); static bool isLockable(QString profile);

View File

@ -30,9 +30,8 @@ QString dataToString(QByteArray data)
char num3; char num3;
int strlen = 0; int strlen = 0;
int num2 = 0; int num2 = 0;
int i=0; int i = 0;
do do {
{
num3 = data[i++]; num3 = data[i++];
strlen |= (num3 & 0x7f) << num2; strlen |= (num3 & 0x7f) << num2;
num2 += 7; num2 += 7;
@ -50,14 +49,10 @@ QString dataToString(QByteArray data)
uint64_t dataToUint64(const QByteArray& data) uint64_t dataToUint64(const QByteArray& data)
{ {
return static_cast<uint64_t>(data[0]) return static_cast<uint64_t>(data[0]) | (static_cast<uint64_t>(data[1]) << 8)
| (static_cast<uint64_t>(data[1]) << 8) | (static_cast<uint64_t>(data[2]) << 16) | (static_cast<uint64_t>(data[3]) << 24)
| (static_cast<uint64_t>(data[2]) << 16) | (static_cast<uint64_t>(data[4]) << 32) | (static_cast<uint64_t>(data[5]) << 40)
| (static_cast<uint64_t>(data[3]) << 24) | (static_cast<uint64_t>(data[6]) << 48) | (static_cast<uint64_t>(data[7]) << 56);
| (static_cast<uint64_t>(data[4]) << 32)
| (static_cast<uint64_t>(data[5]) << 40)
| (static_cast<uint64_t>(data[6]) << 48)
| (static_cast<uint64_t>(data[7]) << 56);
} }
int dataToVInt(const QByteArray& data) int dataToVInt(const QByteArray& data)
@ -65,9 +60,8 @@ int dataToVInt(const QByteArray& data)
char num3; char num3;
int num = 0; int num = 0;
int num2 = 0; int num2 = 0;
int i=0; int i = 0;
do do {
{
num3 = data[i++]; num3 = data[i++];
num |= static_cast<int>(num3 & 0x7f) << num2; num |= static_cast<int>(num3 & 0x7f) << num2;
num2 += 7; num2 += 7;
@ -80,9 +74,8 @@ size_t dataToVUint(const QByteArray& data)
char num3; char num3;
size_t num = 0; size_t num = 0;
int num2 = 0; int num2 = 0;
int i=0; int i = 0;
do do {
{
num3 = data[i++]; num3 = data[i++];
num |= static_cast<size_t>(num3 & 0x7f) << num2; num |= static_cast<size_t>(num3 & 0x7f) << num2;
num2 += 7; num2 += 7;
@ -92,7 +85,7 @@ size_t dataToVUint(const QByteArray& data)
unsigned getVUint32Size(QByteArray data) unsigned getVUint32Size(QByteArray data)
{ {
unsigned lensize=0; unsigned lensize = 0;
char num3; char num3;
do { do {
@ -107,15 +100,14 @@ QByteArray vintToData(int num)
{ {
QByteArray data(sizeof(int), 0); QByteArray data(sizeof(int), 0);
// Write the size in a Uint of variable lenght (8-32 bits) // Write the size in a Uint of variable lenght (8-32 bits)
int i=0; int i = 0;
while (num >= 0x80) while (num >= 0x80) {
{
data[i] = static_cast<char>(num | 0x80); data[i] = static_cast<char>(num | 0x80);
++i; ++i;
num = num >> 7; num = num >> 7;
} }
data[i] = static_cast<char>(num); data[i] = static_cast<char>(num);
data.resize(i+1); data.resize(i + 1);
return data; return data;
} }
@ -123,14 +115,13 @@ QByteArray vuintToData(size_t num)
{ {
QByteArray data(sizeof(size_t), 0); QByteArray data(sizeof(size_t), 0);
// Write the size in a Uint of variable lenght (8-32 bits) // Write the size in a Uint of variable lenght (8-32 bits)
int i=0; int i = 0;
while (num >= 0x80) while (num >= 0x80) {
{
data[i] = static_cast<char>(num | 0x80); data[i] = static_cast<char>(num | 0x80);
++i; ++i;
num = num >> 7; num = num >> 7;
} }
data[i] = static_cast<char>(num); data[i] = static_cast<char>(num);
data.resize(i+1); data.resize(i + 1);
return data; return data;
} }

View File

@ -21,9 +21,9 @@
#ifndef SERIALIZE_H #ifndef SERIALIZE_H
#define SERIALIZE_H #define SERIALIZE_H
#include <cstdint>
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <cstdint>
QString dataToString(QByteArray data); QString dataToString(QByteArray data);
uint64_t dataToUint64(const QByteArray& data); uint64_t dataToUint64(const QByteArray& data);

File diff suppressed because it is too large Load Diff

View File

@ -21,15 +21,15 @@
#ifndef SETTINGS_HPP #ifndef SETTINGS_HPP
#define SETTINGS_HPP #define SETTINGS_HPP
#include "src/core/corestructs.h"
#include <QDate>
#include <QFlags>
#include <QFont> #include <QFont>
#include <QHash> #include <QHash>
#include <QMutex>
#include <QNetworkProxy>
#include <QObject> #include <QObject>
#include <QPixmap> #include <QPixmap>
#include <QMutex>
#include <QDate>
#include <QNetworkProxy>
#include <QFlags>
#include "src/core/corestructs.h"
class ToxPk; class ToxPk;
class Profile; class Profile;
@ -46,100 +46,88 @@ class Settings : public QObject
Q_ENUMS(StyleType) Q_ENUMS(StyleType)
// general // general
Q_PROPERTY(bool compactLayout READ getCompactLayout WRITE setCompactLayout Q_PROPERTY(bool compactLayout READ getCompactLayout WRITE setCompactLayout NOTIFY compactLayoutChanged FINAL)
NOTIFY compactLayoutChanged FINAL) Q_PROPERTY(bool autorun READ getAutorun WRITE setAutorun NOTIFY autorunChanged FINAL)
Q_PROPERTY(bool autorun READ getAutorun WRITE setAutorun
NOTIFY autorunChanged FINAL)
// GUI // GUI
Q_PROPERTY(bool separateWindow READ getSeparateWindow Q_PROPERTY(bool separateWindow READ getSeparateWindow WRITE setSeparateWindow NOTIFY
WRITE setSeparateWindow NOTIFY separateWindowChanged FINAL) separateWindowChanged FINAL)
Q_PROPERTY(QString smileyPack READ getSmileyPack WRITE setSmileyPack Q_PROPERTY(QString smileyPack READ getSmileyPack WRITE setSmileyPack NOTIFY smileyPackChanged FINAL)
NOTIFY smileyPackChanged FINAL) Q_PROPERTY(int emojiFontPointSize READ getEmojiFontPointSize WRITE setEmojiFontPointSize NOTIFY
Q_PROPERTY(int emojiFontPointSize READ getEmojiFontPointSize emojiFontPointSizeChanged FINAL)
WRITE setEmojiFontPointSize NOTIFY emojiFontPointSizeChanged Q_PROPERTY(bool minimizeOnClose READ getMinimizeOnClose WRITE setMinimizeOnClose NOTIFY
FINAL) minimizeOnCloseChanged FINAL)
Q_PROPERTY(bool minimizeOnClose READ getMinimizeOnClose Q_PROPERTY(QByteArray windowGeometry READ getWindowGeometry WRITE setWindowGeometry NOTIFY
WRITE setMinimizeOnClose NOTIFY minimizeOnCloseChanged FINAL) windowGeometryChanged FINAL)
Q_PROPERTY(QByteArray windowGeometry READ getWindowGeometry Q_PROPERTY(QByteArray windowState READ getWindowState WRITE setWindowState NOTIFY windowStateChanged FINAL)
WRITE setWindowGeometry NOTIFY windowGeometryChanged FINAL) Q_PROPERTY(QByteArray splitterState READ getSplitterState WRITE setSplitterState NOTIFY
Q_PROPERTY(QByteArray windowState READ getWindowState WRITE setWindowState splitterStateChanged FINAL)
NOTIFY windowStateChanged FINAL) Q_PROPERTY(QByteArray dialogGeometry READ getDialogGeometry WRITE setDialogGeometry NOTIFY
Q_PROPERTY(QByteArray splitterState READ getSplitterState dialogGeometryChanged FINAL)
WRITE setSplitterState NOTIFY splitterStateChanged FINAL) Q_PROPERTY(QByteArray dialogSplitterState READ getDialogSplitterState WRITE
Q_PROPERTY(QByteArray dialogGeometry READ getDialogGeometry setDialogSplitterState NOTIFY dialogSplitterStateChanged FINAL)
WRITE setDialogGeometry NOTIFY dialogGeometryChanged FINAL) Q_PROPERTY(QByteArray dialogSettingsGeometry READ getDialogSettingsGeometry WRITE
Q_PROPERTY(QByteArray dialogSplitterState READ getDialogSplitterState setDialogSettingsGeometry NOTIFY dialogSettingsGeometryChanged FINAL)
WRITE setDialogSplitterState NOTIFY dialogSplitterStateChanged Q_PROPERTY(QString style READ getStyle WRITE setStyle NOTIFY styleChanged FINAL)
FINAL) Q_PROPERTY(bool showSystemTray READ getShowSystemTray WRITE setShowSystemTray NOTIFY
Q_PROPERTY(QByteArray dialogSettingsGeometry READ getDialogSettingsGeometry showSystemTrayChanged FINAL)
WRITE setDialogSettingsGeometry
NOTIFY dialogSettingsGeometryChanged FINAL)
Q_PROPERTY(QString style READ getStyle WRITE setStyle NOTIFY styleChanged
FINAL)
Q_PROPERTY(bool showSystemTray READ getShowSystemTray
WRITE setShowSystemTray NOTIFY showSystemTrayChanged FINAL)
// ChatView // ChatView
Q_PROPERTY(bool groupchatPosition READ getGroupchatPosition Q_PROPERTY(bool groupchatPosition READ getGroupchatPosition WRITE setGroupchatPosition NOTIFY
WRITE setGroupchatPosition NOTIFY groupchatPositionChanged FINAL) groupchatPositionChanged FINAL)
Q_PROPERTY(QFont chatMessageFont READ getChatMessageFont Q_PROPERTY(QFont chatMessageFont READ getChatMessageFont WRITE setChatMessageFont NOTIFY
WRITE setChatMessageFont NOTIFY chatMessageFontChanged FINAL) chatMessageFontChanged FINAL)
Q_PROPERTY(StyleType stylePreference READ getStylePreference Q_PROPERTY(StyleType stylePreference READ getStylePreference WRITE setStylePreference NOTIFY
WRITE setStylePreference NOTIFY stylePreferenceChanged FINAL) stylePreferenceChanged FINAL)
Q_PROPERTY(QString timestampFormat READ getTimestampFormat Q_PROPERTY(QString timestampFormat READ getTimestampFormat WRITE setTimestampFormat NOTIFY
WRITE setTimestampFormat NOTIFY timestampFormatChanged FINAL) timestampFormatChanged FINAL)
Q_PROPERTY(QString dateFormat READ getDateFormat WRITE setDateFormat Q_PROPERTY(QString dateFormat READ getDateFormat WRITE setDateFormat NOTIFY dateFormatChanged FINAL)
NOTIFY dateFormatChanged FINAL) Q_PROPERTY(bool statusChangeNotificationEnabled READ getStatusChangeNotificationEnabled WRITE
Q_PROPERTY(bool statusChangeNotificationEnabled setStatusChangeNotificationEnabled NOTIFY statusChangeNotificationEnabledChanged FINAL)
READ getStatusChangeNotificationEnabled
WRITE setStatusChangeNotificationEnabled
NOTIFY statusChangeNotificationEnabledChanged FINAL)
// Privacy // Privacy
Q_PROPERTY(bool typingNotification READ getTypingNotification Q_PROPERTY(bool typingNotification READ getTypingNotification WRITE setTypingNotification NOTIFY
WRITE setTypingNotification NOTIFY typingNotificationChanged typingNotificationChanged FINAL)
FINAL)
// Audio // Audio
Q_PROPERTY(QString inDev READ getInDev WRITE setInDev Q_PROPERTY(QString inDev READ getInDev WRITE setInDev NOTIFY inDevChanged FINAL)
NOTIFY inDevChanged FINAL) Q_PROPERTY(bool audioInDevEnabled READ getAudioInDevEnabled WRITE setAudioInDevEnabled NOTIFY
Q_PROPERTY(bool audioInDevEnabled READ getAudioInDevEnabled audioInDevEnabledChanged FINAL)
WRITE setAudioInDevEnabled NOTIFY audioInDevEnabledChanged FINAL) Q_PROPERTY(qreal audioInGainDecibel READ getAudioInGainDecibel WRITE setAudioInGainDecibel
Q_PROPERTY(qreal audioInGainDecibel READ getAudioInGainDecibel NOTIFY audioInGainDecibelChanged FINAL)
WRITE setAudioInGainDecibel NOTIFY audioInGainDecibelChanged Q_PROPERTY(QString outDev READ getOutDev WRITE setOutDev NOTIFY outDevChanged FINAL)
FINAL) Q_PROPERTY(bool audioOutDevEnabled READ getAudioOutDevEnabled WRITE setAudioOutDevEnabled NOTIFY
Q_PROPERTY(QString outDev READ getOutDev WRITE setOutDev audioOutDevEnabledChanged FINAL)
NOTIFY outDevChanged FINAL) Q_PROPERTY(int outVolume READ getOutVolume WRITE setOutVolume NOTIFY outVolumeChanged FINAL)
Q_PROPERTY(bool audioOutDevEnabled READ getAudioOutDevEnabled
WRITE setAudioOutDevEnabled NOTIFY audioOutDevEnabledChanged
FINAL)
Q_PROPERTY(int outVolume READ getOutVolume WRITE setOutVolume
NOTIFY outVolumeChanged FINAL)
// Video // Video
Q_PROPERTY(QString videoDev READ getVideoDev WRITE setVideoDev Q_PROPERTY(QString videoDev READ getVideoDev WRITE setVideoDev NOTIFY videoDevChanged FINAL)
NOTIFY videoDevChanged FINAL) Q_PROPERTY(QRect camVideoRes READ getCamVideoRes WRITE setCamVideoRes NOTIFY camVideoResChanged FINAL)
Q_PROPERTY(QRect camVideoRes READ getCamVideoRes WRITE setCamVideoRes Q_PROPERTY(QRect screenRegion READ getScreenRegion WRITE setScreenRegion NOTIFY screenRegionChanged FINAL)
NOTIFY camVideoResChanged FINAL) Q_PROPERTY(bool screenGrabbed READ getScreenGrabbed WRITE setScreenGrabbed NOTIFY screenGrabbedChanged FINAL)
Q_PROPERTY(QRect screenRegion READ getScreenRegion WRITE setScreenRegion Q_PROPERTY(quint16 camVideoFPS READ getCamVideoFPS WRITE setCamVideoFPS NOTIFY camVideoFPSChanged FINAL)
NOTIFY screenRegionChanged FINAL)
Q_PROPERTY(bool screenGrabbed READ getScreenGrabbed WRITE setScreenGrabbed
NOTIFY screenGrabbedChanged FINAL)
Q_PROPERTY(quint16 camVideoFPS READ getCamVideoFPS
WRITE setCamVideoFPS NOTIFY camVideoFPSChanged FINAL)
public: public:
enum class ProxyType {ptNone = 0, ptSOCKS5 = 1, ptHTTP = 2}; enum class ProxyType
enum class StyleType {NONE = 0, WITH_CHARS = 1, WITHOUT_CHARS = 2}; {
ptNone = 0,
ptSOCKS5 = 1,
ptHTTP = 2
};
enum class StyleType
{
NONE = 0,
WITH_CHARS = 1,
WITHOUT_CHARS = 2
};
enum class AutoAcceptCall enum class AutoAcceptCall
{ {
None = 0x00, None = 0x00,
Audio = 0x01, Audio = 0x01,
Video = 0x02, Video = 0x02,
AV = Audio | Video AV = Audio | Video
}; };
Q_DECLARE_FLAGS(AutoAcceptCallFlags, AutoAcceptCall) Q_DECLARE_FLAGS(AutoAcceptCallFlags, AutoAcceptCall)
public: public:
static Settings& getInstance(); static Settings& getInstance();
@ -468,7 +456,7 @@ public:
void setFriendCircleID(const ToxPk& id, int circleID); void setFriendCircleID(const ToxPk& id, int circleID);
QDate getFriendActivity(const ToxPk& id) const; QDate getFriendActivity(const ToxPk& id) const;
void setFriendActivity(const ToxPk& id, const QDate &date); void setFriendActivity(const ToxPk& id, const QDate& date);
void removeFriendSettings(const ToxPk& id); void removeFriendSettings(const ToxPk& id);
@ -529,7 +517,7 @@ public:
private: private:
Settings(); Settings();
~Settings(); ~Settings();
Settings(Settings &settings) = delete; Settings(Settings& settings) = delete;
Settings& operator=(const Settings&) = delete; Settings& operator=(const Settings&) = delete;
private slots: private slots:

View File

@ -23,11 +23,11 @@
#include "src/core/toxencrypt.h" #include "src/core/toxencrypt.h"
#include "src/persistence/profile.h" #include "src/persistence/profile.h"
#include <QSaveFile>
#include <QFile>
#include <QDebug> #include <QDebug>
#include <memory> #include <QFile>
#include <QSaveFile>
#include <cassert> #include <cassert>
#include <memory>
/** /**
* @class SettingsSerializer * @class SettingsSerializer
@ -58,7 +58,7 @@ enum class RecordTag : uint8_t
* @var static const char magic[]; * @var static const char magic[];
* @brief Little endian ASCII "QTOX" magic * @brief Little endian ASCII "QTOX" magic
*/ */
const char SettingsSerializer::magic[] = {0x51,0x54,0x4F,0x58}; const char SettingsSerializer::magic[] = {0x51, 0x54, 0x4F, 0x58};
QDataStream& writeStream(QDataStream& dataStream, const SettingsSerializer::RecordTag& tag) QDataStream& writeStream(QDataStream& dataStream, const SettingsSerializer::RecordTag& tag)
{ {
@ -80,7 +80,7 @@ QDataStream& writeStream(QDataStream& dataStream, const QString& str)
QDataStream& readStream(QDataStream& dataStream, SettingsSerializer::RecordTag& tag) QDataStream& readStream(QDataStream& dataStream, SettingsSerializer::RecordTag& tag)
{ {
return dataStream >> reinterpret_cast<quint8&>(tag) ; return dataStream >> reinterpret_cast<quint8&>(tag);
} }
@ -89,8 +89,7 @@ QDataStream& readStream(QDataStream& dataStream, QByteArray& data)
char num3; char num3;
int num = 0; int num = 0;
int num2 = 0; int num2 = 0;
do do {
{
dataStream.readRawData(&num3, 1); dataStream.readRawData(&num3, 1);
num |= (num3 & 0x7f) << num2; num |= (num3 & 0x7f) << num2;
num2 += 7; num2 += 7;
@ -100,23 +99,23 @@ QDataStream& readStream(QDataStream& dataStream, QByteArray& data)
return dataStream; return dataStream;
} }
SettingsSerializer::SettingsSerializer(QString filePath, const QString &password) SettingsSerializer::SettingsSerializer(QString filePath, const QString& password)
: path{filePath}, password{password}, : path{filePath}
group{-1}, array{-1}, arrayIndex{-1} , password{password}
, group{-1}
, array{-1}
, arrayIndex{-1}
{ {
} }
void SettingsSerializer::beginGroup(const QString &prefix) void SettingsSerializer::beginGroup(const QString& prefix)
{ {
if (prefix.isEmpty()) if (prefix.isEmpty())
endGroup(); endGroup();
int index = groups.indexOf(prefix); int index = groups.indexOf(prefix);
if (index >= 0) if (index >= 0) {
{
group = index; group = index;
} } else {
else
{
group = groups.size(); group = groups.size();
groups.append(prefix); groups.append(prefix);
} }
@ -127,22 +126,16 @@ void SettingsSerializer::endGroup()
group = -1; group = -1;
} }
int SettingsSerializer::beginReadArray(const QString &prefix) int SettingsSerializer::beginReadArray(const QString& prefix)
{ {
auto index = std::find_if(std::begin(arrays), std::end(arrays), auto index = std::find_if(std::begin(arrays), std::end(arrays),
[=](const Array& a) [=](const Array& a) { return a.name == prefix; });
{
return a.name==prefix;
});
if (index != std::end(arrays)) if (index != std::end(arrays)) {
{
array = static_cast<int>(index - std::begin(arrays)); array = static_cast<int>(index - std::begin(arrays));
arrayIndex = -1; arrayIndex = -1;
return index->size; return index->size;
} } else {
else
{
array = arrays.size(); array = arrays.size();
arrays.push_back({group, 0, prefix, {}}); arrays.push_back({group, 0, prefix, {}});
arrayIndex = -1; arrayIndex = -1;
@ -150,23 +143,17 @@ int SettingsSerializer::beginReadArray(const QString &prefix)
} }
} }
void SettingsSerializer::beginWriteArray(const QString &prefix, int size) void SettingsSerializer::beginWriteArray(const QString& prefix, int size)
{ {
auto index = std::find_if(std::begin(arrays), std::end(arrays), auto index = std::find_if(std::begin(arrays), std::end(arrays),
[=](const Array& a) [=](const Array& a) { return a.name == prefix; });
{
return a.name==prefix;
});
if (index != std::end(arrays)) if (index != std::end(arrays)) {
{
array = static_cast<int>(index - std::begin(arrays)); array = static_cast<int>(index - std::begin(arrays));
arrayIndex = -1; arrayIndex = -1;
if (size > 0) if (size > 0)
index->size = std::max(index->size, size); index->size = std::max(index->size, size);
} } else {
else
{
if (size < 0) if (size < 0)
size = 0; size = 0;
array = arrays.size(); array = arrays.size();
@ -185,15 +172,12 @@ void SettingsSerializer::setArrayIndex(int i)
arrayIndex = i; arrayIndex = i;
} }
void SettingsSerializer::setValue(const QString &key, const QVariant &value) void SettingsSerializer::setValue(const QString& key, const QVariant& value)
{ {
Value* v = findValue(key); Value* v = findValue(key);
if (v) if (v) {
{
v->value = value; v->value = value;
} } else {
else
{
Value nv{group, array, arrayIndex, key, value}; Value nv{group, array, arrayIndex, key, value};
if (array >= 0) if (array >= 0)
arrays[array].values.append(values.size()); arrays[array].values.append(values.size());
@ -201,7 +185,7 @@ void SettingsSerializer::setValue(const QString &key, const QVariant &value)
} }
} }
QVariant SettingsSerializer::value(const QString &key, const QVariant &defaultValue) const QVariant SettingsSerializer::value(const QString& key, const QVariant& defaultValue) const
{ {
const Value* v = findValue(key); const Value* v = findValue(key);
if (v) if (v)
@ -212,23 +196,18 @@ QVariant SettingsSerializer::value(const QString &key, const QVariant &defaultVa
const SettingsSerializer::Value* SettingsSerializer::findValue(const QString& key) const const SettingsSerializer::Value* SettingsSerializer::findValue(const QString& key) const
{ {
if (array != -1) if (array != -1) {
{ for (const Array& a : arrays) {
for (const Array& a : arrays)
{
if (a.group != group) if (a.group != group)
continue; continue;
for (int vi : a.values) for (int vi : a.values) {
{
const Value& v = values[vi]; const Value& v = values[vi];
if (v.arrayIndex == arrayIndex && v.key == key) if (v.arrayIndex == arrayIndex && v.key == key)
return &v; return &v;
} }
} }
} } else {
else
{
for (const Value& v : values) for (const Value& v : values)
if (v.group == group && v.array == -1 && v.key == key) if (v.group == group && v.array == -1 && v.key == key)
return &v; return &v;
@ -274,8 +253,7 @@ void SettingsSerializer::load()
void SettingsSerializer::save() void SettingsSerializer::save()
{ {
QSaveFile f(path); QSaveFile f(path);
if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly)) if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly)) {
{
qWarning() << "Couldn't open file"; qWarning() << "Couldn't open file";
return; return;
} }
@ -284,18 +262,15 @@ void SettingsSerializer::save()
QDataStream stream(&data, QIODevice::ReadWrite | QIODevice::Append); QDataStream stream(&data, QIODevice::ReadWrite | QIODevice::Append);
stream.setVersion(QDataStream::Qt_5_0); stream.setVersion(QDataStream::Qt_5_0);
for (int g=-1; g<groups.size(); ++g) for (int g = -1; g < groups.size(); ++g) {
{
// Save the group name, if any // Save the group name, if any
if (g!=-1) if (g != -1) {
{
writeStream(stream, RecordTag::GroupStart); writeStream(stream, RecordTag::GroupStart);
writeStream(stream, groups[g].toUtf8()); writeStream(stream, groups[g].toUtf8());
} }
// Save all the arrays of this group // Save all the arrays of this group
for (const Array& a : arrays) for (const Array& a : arrays) {
{
if (a.group != g) if (a.group != g)
continue; continue;
if (a.size <= 0) if (a.size <= 0)
@ -304,8 +279,7 @@ void SettingsSerializer::save()
writeStream(stream, a.name.toUtf8()); writeStream(stream, a.name.toUtf8());
writeStream(stream, vintToData(a.size)); writeStream(stream, vintToData(a.size));
for (int vi : a.values) for (int vi : a.values) {
{
const Value& v = values[vi]; const Value& v = values[vi];
writeStream(stream, RecordTag::ArrayValue); writeStream(stream, RecordTag::ArrayValue);
writeStream(stream, vintToData(values[vi].arrayIndex)); writeStream(stream, vintToData(values[vi].arrayIndex));
@ -316,8 +290,7 @@ void SettingsSerializer::save()
} }
// Save all the values of this group that aren't in an array // Save all the values of this group that aren't in an array
for (const Value& v : values) for (const Value& v : values) {
{
if (v.group != g || v.array != -1) if (v.group != g || v.array != -1)
continue; continue;
writeStream(stream, RecordTag::Value); writeStream(stream, RecordTag::Value);
@ -327,8 +300,7 @@ void SettingsSerializer::save()
} }
// Encrypt // Encrypt
if (!password.isEmpty()) if (!password.isEmpty()) {
{
// TODO: use passkey // TODO: use passkey
data = ToxEncrypt::encryptPass(password, data); data = ToxEncrypt::encryptPass(password, data);
} }
@ -336,12 +308,9 @@ void SettingsSerializer::save()
f.write(data); f.write(data);
// check if everything got written // check if everything got written
if (f.flush()) if (f.flush()) {
{
f.commit(); f.commit();
} } else {
else
{
f.cancelWriting(); f.cancelWriting();
qCritical() << "Failed to write, can't save!"; qCritical() << "Failed to write, can't save!";
} }
@ -350,8 +319,7 @@ void SettingsSerializer::save()
void SettingsSerializer::readSerialized() void SettingsSerializer::readSerialized()
{ {
QFile f(path); QFile f(path);
if (!f.open(QIODevice::ReadOnly)) if (!f.open(QIODevice::ReadOnly)) {
{
qWarning() << "Couldn't open file"; qWarning() << "Couldn't open file";
return; return;
} }
@ -359,29 +327,23 @@ void SettingsSerializer::readSerialized()
f.close(); f.close();
// Decrypt // Decrypt
if (tox_is_data_encrypted(reinterpret_cast<uint8_t*>(data.data()))) if (tox_is_data_encrypted(reinterpret_cast<uint8_t*>(data.data()))) {
{ if (password.isEmpty()) {
if (password.isEmpty())
{
qCritical() << "The settings file is encrypted, but we don't have a password!"; qCritical() << "The settings file is encrypted, but we don't have a password!";
return; return;
} }
data = ToxEncrypt::decryptPass(password, data); data = ToxEncrypt::decryptPass(password, data);
if (data.isEmpty()) if (data.isEmpty()) {
{
qCritical() << "Failed to decrypt the settings file"; qCritical() << "Failed to decrypt the settings file";
return; return;
} }
} } else {
else
{
if (!password.isEmpty()) if (!password.isEmpty())
qWarning() << "We have a password, but the settings file is not encrypted"; qWarning() << "We have a password, but the settings file is not encrypted";
} }
if (memcmp(data.data(), magic, 4)) if (memcmp(data.data(), magic, 4)) {
{
qWarning() << "Bad magic!"; qWarning() << "Bad magic!";
return; return;
} }
@ -390,45 +352,35 @@ void SettingsSerializer::readSerialized()
QDataStream stream(&data, QIODevice::ReadOnly); QDataStream stream(&data, QIODevice::ReadOnly);
stream.setVersion(QDataStream::Qt_5_0); stream.setVersion(QDataStream::Qt_5_0);
while (!stream.atEnd()) while (!stream.atEnd()) {
{
RecordTag tag; RecordTag tag;
readStream(stream, tag); readStream(stream, tag);
if (tag == RecordTag::Value) if (tag == RecordTag::Value) {
{
QByteArray key; QByteArray key;
QByteArray value; QByteArray value;
readStream(stream, key); readStream(stream, key);
readStream(stream, value); readStream(stream, value);
setValue(QString::fromUtf8(key), QVariant(QString::fromUtf8(value))); setValue(QString::fromUtf8(key), QVariant(QString::fromUtf8(value)));
} } else if (tag == RecordTag::GroupStart) {
else if (tag == RecordTag::GroupStart)
{
QByteArray prefix; QByteArray prefix;
readStream(stream, prefix); readStream(stream, prefix);
beginGroup(QString::fromUtf8(prefix)); beginGroup(QString::fromUtf8(prefix));
} } else if (tag == RecordTag::ArrayStart) {
else if (tag == RecordTag::ArrayStart)
{
QByteArray prefix; QByteArray prefix;
readStream(stream, prefix); readStream(stream, prefix);
beginReadArray(QString::fromUtf8(prefix)); beginReadArray(QString::fromUtf8(prefix));
QByteArray sizeData; QByteArray sizeData;
readStream(stream, sizeData); readStream(stream, sizeData);
if (sizeData.isEmpty()) if (sizeData.isEmpty()) {
{
qWarning("The personal save file is corrupted!"); qWarning("The personal save file is corrupted!");
return; return;
} }
int size = dataToVInt(sizeData); int size = dataToVInt(sizeData);
arrays[array].size = qMax(size, arrays[array].size); arrays[array].size = qMax(size, arrays[array].size);
} } else if (tag == RecordTag::ArrayValue) {
else if (tag == RecordTag::ArrayValue)
{
QByteArray indexData; QByteArray indexData;
readStream(stream, indexData); readStream(stream, indexData);
if (indexData.isEmpty()) if (indexData.isEmpty()) {
{
qWarning("The personal save file is corrupted!"); qWarning("The personal save file is corrupted!");
return; return;
} }
@ -438,9 +390,7 @@ void SettingsSerializer::readSerialized()
readStream(stream, key); readStream(stream, key);
readStream(stream, value); readStream(stream, value);
setValue(QString::fromUtf8(key), QVariant(QString::fromUtf8(value))); setValue(QString::fromUtf8(key), QVariant(QString::fromUtf8(value)));
} } else if (tag == RecordTag::ArrayEnd) {
else if (tag == RecordTag::ArrayEnd)
{
endArray(); endArray();
} }
} }
@ -459,8 +409,7 @@ void SettingsSerializer::readIni()
if (!s.group().isEmpty()) if (!s.group().isEmpty())
beginGroup(s.group()); beginGroup(s.group());
for (QString k : s.childKeys()) for (QString k : s.childKeys()) {
{
setValue(k, s.value(k)); setValue(k, s.value(k));
} }
@ -470,18 +419,14 @@ void SettingsSerializer::readIni()
gstack.push_back(g); gstack.push_back(g);
// Visit the next group, if any // Visit the next group, if any
while (!gstack.isEmpty()) while (!gstack.isEmpty()) {
{
QString g = gstack.takeLast(); QString g = gstack.takeLast();
if (g.isEmpty()) if (g.isEmpty()) {
{
if (gstack.isEmpty()) if (gstack.isEmpty())
break; break;
else else
s.endGroup(); s.endGroup();
} } else {
else
{
s.beginGroup(g); s.beginGroup(g);
break; break;
} }
@ -495,8 +440,7 @@ void SettingsSerializer::readIni()
// Find groups that only have 1 key // Find groups that only have 1 key
std::unique_ptr<int[]> groupSizes{new int[groups.size()]}; std::unique_ptr<int[]> groupSizes{new int[groups.size()]};
memset(groupSizes.get(), 0, static_cast<size_t>(groups.size()) * sizeof(int)); memset(groupSizes.get(), 0, static_cast<size_t>(groups.size()) * sizeof(int));
for (const Value& v : values) for (const Value& v : values) {
{
if (v.group < 0 || v.group > groups.size()) if (v.group < 0 || v.group > groups.size())
continue; continue;
groupSizes[static_cast<size_t>(v.group)]++; groupSizes[static_cast<size_t>(v.group)]++;
@ -504,8 +448,7 @@ void SettingsSerializer::readIni()
// Find arrays, remove their size key from the values, and add them to `arrays` // Find arrays, remove their size key from the values, and add them to `arrays`
QVector<int> groupsToKill; QVector<int> groupsToKill;
for (int i=values.size()-1; i>=0; i--) for (int i = values.size() - 1; i >= 0; i--) {
{
const Value& v = values[i]; const Value& v = values[i];
if (v.group < 0 || v.group > groups.size()) if (v.group < 0 || v.group > groups.size())
continue; continue;
@ -519,20 +462,16 @@ void SettingsSerializer::readIni()
Array a; Array a;
a.size = v.value.toInt(); a.size = v.value.toInt();
int slashIndex = groups[static_cast<int>(v.group)].lastIndexOf('/'); int slashIndex = groups[static_cast<int>(v.group)].lastIndexOf('/');
if (slashIndex == -1) if (slashIndex == -1) {
{
a.group = -1; a.group = -1;
a.name = groups[static_cast<int>(v.group)]; a.name = groups[static_cast<int>(v.group)];
a.size = v.value.toInt(); a.size = v.value.toInt();
} } else {
else
{
a.group = -1; a.group = -1;
for (int i=0; i<groups.size(); ++i) for (int i = 0; i < groups.size(); ++i)
if (groups[i] == groups[static_cast<int>(v.group)].left(slashIndex)) if (groups[i] == groups[static_cast<int>(v.group)].left(slashIndex))
a.group = i; a.group = i;
a.name = groups[static_cast<int>(v.group)].mid(slashIndex+1); a.name = groups[static_cast<int>(v.group)].mid(slashIndex + 1);
} }
groupSizes[static_cast<size_t>(v.group)]--; groupSizes[static_cast<size_t>(v.group)]--;
groupsToKill.append(static_cast<int>(v.group)); groupsToKill.append(static_cast<int>(v.group));
@ -541,17 +480,15 @@ void SettingsSerializer::readIni()
} }
// Associate each array's values with the array // Associate each array's values with the array
for (int ai=0; ai<arrays.size(); ++ai) for (int ai = 0; ai < arrays.size(); ++ai) {
{
Array& a = arrays[ai]; Array& a = arrays[ai];
QString arrayPrefix; QString arrayPrefix;
if (a.group != -1) if (a.group != -1)
arrayPrefix += groups[static_cast<int>(a.group)]+'/'; arrayPrefix += groups[static_cast<int>(a.group)] + '/';
arrayPrefix += a.name+'/'; arrayPrefix += a.name + '/';
// Find groups which represent each array index // Find groups which represent each array index
for (int g=0; g<groups.size(); ++g) for (int g = 0; g < groups.size(); ++g) {
{
if (!groups[g].startsWith(arrayPrefix)) if (!groups[g].startsWith(arrayPrefix))
continue; continue;
bool ok; bool ok;
@ -564,8 +501,7 @@ void SettingsSerializer::readIni()
a.size = groupArrayIndex; a.size = groupArrayIndex;
// Associate the values for this array index // Associate the values for this array index
for (int vi = values.size() - 1; vi >= 0; vi--) for (int vi = values.size() - 1; vi >= 0; vi--) {
{
Value& v = values[vi]; Value& v = values[vi];
if (v.group != g) if (v.group != g)
continue; continue;
@ -579,11 +515,9 @@ void SettingsSerializer::readIni()
} }
// Clean up spurious array element groups // Clean up spurious array element groups
std::sort(std::begin(groupsToKill), std::end(groupsToKill), std::sort(std::begin(groupsToKill), std::end(groupsToKill), std::greater_equal<int>());
std::greater_equal<int>());
for (int g : groupsToKill) for (int g : groupsToKill) {
{
if (groupSizes[static_cast<size_t>(g)]) if (groupSizes[static_cast<size_t>(g)])
continue; continue;
@ -600,15 +534,13 @@ void SettingsSerializer::readIni()
*/ */
void SettingsSerializer::removeGroup(int group) void SettingsSerializer::removeGroup(int group)
{ {
assert(group<groups.size()); assert(group < groups.size());
for (Array& a : arrays) for (Array& a : arrays) {
{
assert(a.group != group); assert(a.group != group);
if (a.group > group) if (a.group > group)
a.group--; a.group--;
} }
for (Value& v : values) for (Value& v : values) {
{
assert(v.group != group); assert(v.group != group);
if (v.group > group) if (v.group > group)
v.group--; v.group--;

View File

@ -20,49 +20,62 @@
#ifndef SETTINGSSERIALIZER_H #ifndef SETTINGSSERIALIZER_H
#define SETTINGSSERIALIZER_H #define SETTINGSSERIALIZER_H
#include <QSettings>
#include <QVector>
#include <QString>
#include <QDataStream> #include <QDataStream>
#include <QSettings>
#include <QString>
#include <QVector>
class SettingsSerializer class SettingsSerializer
{ {
public: public:
SettingsSerializer(QString filePath, const QString &password=QString()); SettingsSerializer(QString filePath, const QString& password = QString());
static bool isSerializedFormat(QString filePath); static bool isSerializedFormat(QString filePath);
void load(); void load();
void save(); void save();
void beginGroup(const QString &prefix); void beginGroup(const QString& prefix);
void endGroup(); void endGroup();
int beginReadArray(const QString &prefix); int beginReadArray(const QString& prefix);
void beginWriteArray(const QString &prefix, int size = -1); void beginWriteArray(const QString& prefix, int size = -1);
void endArray(); void endArray();
void setArrayIndex(int i); void setArrayIndex(int i);
void setValue(const QString &key, const QVariant &value); void setValue(const QString& key, const QVariant& value);
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; QVariant value(const QString& key, const QVariant& defaultValue = QVariant()) const;
private: private:
enum class RecordTag : uint8_t enum class RecordTag : uint8_t
{ {
Value=0, Value = 0,
GroupStart=1, GroupStart = 1,
ArrayStart=2, ArrayStart = 2,
ArrayValue=3, ArrayValue = 3,
ArrayEnd=4, ArrayEnd = 4,
}; };
friend QDataStream& writeStream(QDataStream& dataStream, const SettingsSerializer::RecordTag& tag); friend QDataStream& writeStream(QDataStream& dataStream, const SettingsSerializer::RecordTag& tag);
friend QDataStream& readStream(QDataStream& dataStream, SettingsSerializer::RecordTag& tag); friend QDataStream& readStream(QDataStream& dataStream, SettingsSerializer::RecordTag& tag);
struct Value struct Value
{ {
Value() : group{-2},array{-2},arrayIndex{-2},key{QString()},value{}{} Value()
: group{-2}
, array{-2}
, arrayIndex{-2}
, key{QString()}
, value{}
{
}
Value(qint64 group, qint64 array, int arrayIndex, QString key, QVariant value) Value(qint64 group, qint64 array, int arrayIndex, QString key, QVariant value)
: group{group}, array{array}, arrayIndex{arrayIndex}, key{key}, value{value} {} : group{group}
, array{array}
, arrayIndex{arrayIndex}
, key{key}
, value{value}
{
}
qint64 group; qint64 group;
qint64 array; qint64 array;
int arrayIndex; int arrayIndex;
@ -79,8 +92,8 @@ private:
}; };
private: private:
const Value *findValue(const QString& key) const; const Value* findValue(const QString& key) const;
Value *findValue(const QString& key); Value* findValue(const QString& key);
void readSerialized(); void readSerialized();
void readIni(); void readIni();
void removeValue(const QString& key); void removeValue(const QString& key);

View File

@ -22,9 +22,14 @@
#include "src/widget/style.h" #include "src/widget/style.h"
#include <QBuffer> #include <QBuffer>
#include <QBuffer>
#include <QCoreApplication>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QDir>
#include <QDomDocument> #include <QDomDocument>
#include <QDomDocument>
#include <QDomElement>
#include <QDomElement> #include <QDomElement>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
@ -32,11 +37,6 @@
#include <QFontInfo> #include <QFontInfo>
#include <QIcon> #include <QIcon>
#include <QPixmap> #include <QPixmap>
#include <QDir>
#include <QCoreApplication>
#include <QDomDocument>
#include <QDomElement>
#include <QBuffer>
#include <QStandardPaths> #include <QStandardPaths>
#include <QStringBuilder> #include <QStringBuilder>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
@ -73,8 +73,8 @@ SmileyPack::SmileyPack()
{ {
loadingMutex.lock(); loadingMutex.lock();
QtConcurrent::run(this, &SmileyPack::load, Settings::getInstance().getSmileyPack()); QtConcurrent::run(this, &SmileyPack::load, Settings::getInstance().getSmileyPack());
connect(&Settings::getInstance(), &Settings::smileyPackChanged, connect(&Settings::getInstance(), &Settings::smileyPackChanged, this,
this, &SmileyPack::onSmileyPackChanged); &SmileyPack::onSmileyPackChanged);
} }
/** /**
@ -93,7 +93,8 @@ QStringList SmileyPack::loadDefaultPaths()
// Workaround to fix https://bugreports.qt.io/browse/QTBUG-57522 // Workaround to fix https://bugreports.qt.io/browse/QTBUG-57522
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
#endif #endif
QStringList paths = QStringList{":/smileys", "~/.kde4/share/emoticons", "~/.kde/share/emoticons"}; QStringList paths =
QStringList{":/smileys", "~/.kde4/share/emoticons", "~/.kde/share/emoticons"};
// qTox should find emoticons next to the binary // qTox should find emoticons next to the binary
paths.append('.' + QDir::separator() + EMOTICONS_SUB_DIR); paths.append('.' + QDir::separator() + EMOTICONS_SUB_DIR);
@ -105,21 +106,17 @@ QStringList SmileyPack::loadDefaultPaths()
#warning "Qt < 5.4.0 has a trouble with unicode symbols in path on few systems" #warning "Qt < 5.4.0 has a trouble with unicode symbols in path on few systems"
location = QStandardPaths::DataLocation; location = QStandardPaths::DataLocation;
#endif #endif
for(auto qtoxPath : QStandardPaths::standardLocations(location)) for (auto qtoxPath : QStandardPaths::standardLocations(location)) {
{
qtoxPath += QDir::separator() + EMOTICONS_SUB_DIR; qtoxPath += QDir::separator() + EMOTICONS_SUB_DIR;
if(!paths.contains(qtoxPath)) if (!paths.contains(qtoxPath)) {
{
paths << qtoxPath; paths << qtoxPath;
} }
} }
// system wide emoticons // system wide emoticons
for(auto genericPath : QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) for (auto genericPath : QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) {
{
genericPath += QDir::separator() + EMOTICONS_SUB_DIR; genericPath += QDir::separator() + EMOTICONS_SUB_DIR;
if(!paths.contains(genericPath)) if (!paths.contains(genericPath)) {
{
paths << genericPath; paths << genericPath;
} }
} }
@ -134,33 +131,27 @@ QList<QPair<QString, QString>> SmileyPack::listSmileyPacks()
QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& paths) QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& paths)
{ {
QList<QPair<QString, QString> > smileyPacks; QList<QPair<QString, QString>> smileyPacks;
for (QString path : paths) for (QString path : paths) {
{ if (path.leftRef(1) == "~") {
if (path.leftRef(1) == "~")
{
path.replace(0, 1, QDir::homePath()); path.replace(0, 1, QDir::homePath());
} }
QDir dir(path); QDir dir(path);
if (!dir.exists()) if (!dir.exists()) {
{
continue; continue;
} }
for (const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) for (const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
{
dir.cd(subdirectory); dir.cd(subdirectory);
QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files); QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files);
// Does it contain a file called emoticons.xml? // Does it contain a file called emoticons.xml?
if (entries.size() > 0) if (entries.size() > 0) {
{
QString packageName = dir.dirName(); QString packageName = dir.dirName();
QString absPath = entries[0].absoluteFilePath(); QString absPath = entries[0].absoluteFilePath();
if (!smileyPacks.contains(QPair<QString, QString>(packageName, absPath))) if (!smileyPacks.contains(QPair<QString, QString>(packageName, absPath))) {
{
smileyPacks << QPair<QString, QString>(packageName, absPath); smileyPacks << QPair<QString, QString>(packageName, absPath);
} }
} }
@ -193,8 +184,7 @@ bool SmileyPack::load(const QString& filename)
// open emoticons.xml // open emoticons.xml
QFile xmlFile(filename); QFile xmlFile(filename);
if (!xmlFile.open(QIODevice::ReadOnly)) if (!xmlFile.open(QIODevice::ReadOnly)) {
{
loadingMutex.unlock(); loadingMutex.unlock();
return false; // cannot open file return false; // cannot open file
} }
@ -220,32 +210,26 @@ bool SmileyPack::load(const QString& filename)
doc.setContent(xmlFile.readAll()); doc.setContent(xmlFile.readAll());
QDomNodeList emoticonElements = doc.elementsByTagName("emoticon"); QDomNodeList emoticonElements = doc.elementsByTagName("emoticon");
for (int i = 0; i < emoticonElements.size(); ++i) for (int i = 0; i < emoticonElements.size(); ++i) {
{
QString file = emoticonElements.at(i).attributes().namedItem("file").nodeValue(); QString file = emoticonElements.at(i).attributes().namedItem("file").nodeValue();
QDomElement stringElement = emoticonElements.at(i).firstChildElement("string"); QDomElement stringElement = emoticonElements.at(i).firstChildElement("string");
QStringList emoticonSet; // { ":)", ":-)" } etc. QStringList emoticonSet; // { ":)", ":-)" } etc.
while (!stringElement.isNull()) while (!stringElement.isNull()) {
{ QString emoticon = stringElement.text().replace("<", "&lt;").replace(">", "&gt;");
QString emoticon = stringElement.text()
.replace("<","&lt;").replace(">","&gt;");
filenameTable.insert(emoticon, file); filenameTable.insert(emoticon, file);
cacheSmiley(file); // preload all smileys cacheSmiley(file); // preload all smileys
if (!getCachedSmiley(emoticon).isNull()) if (!getCachedSmiley(emoticon).isNull()) {
{
emoticonSet.push_back(emoticon); emoticonSet.push_back(emoticon);
} }
stringElement = stringElement.nextSibling().toElement(); stringElement = stringElement.nextSibling().toElement();
} }
if (emoticonSet.size() > 0) if (emoticonSet.size() > 0) {
{
emoticons.push_back(emoticonSet); emoticons.push_back(emoticonSet);
} }
} }
@ -264,11 +248,9 @@ QString SmileyPack::smileyfied(QString msg)
int index = msg.indexOf(exp); int index = msg.indexOf(exp);
// if a word is key of a smiley, replace it by its corresponding image in Rich Text // if a word is key of a smiley, replace it by its corresponding image in Rich Text
while (index >= 0) while (index >= 0) {
{
QString key = exp.cap(); QString key = exp.cap();
if (filenameTable.contains(key)) if (filenameTable.contains(key)) {
{
QString imgRichText = getAsRichText(key); QString imgRichText = getAsRichText(key);
msg.replace(index, key.length(), imgRichText); msg.replace(index, key.length(), imgRichText);
@ -309,15 +291,13 @@ void SmileyPack::cacheSmiley(const QString& name)
QIcon SmileyPack::getCachedSmiley(const QString& key) QIcon SmileyPack::getCachedSmiley(const QString& key)
{ {
// valid key? // valid key?
if (!filenameTable.contains(key)) if (!filenameTable.contains(key)) {
{
return QPixmap(); return QPixmap();
} }
// cache it if needed // cache it if needed
QString file = filenameTable.value(key); QString file = filenameTable.value(key);
if (!iconCache.contains(file)) if (!iconCache.contains(file)) {
{
cacheSmiley(file); cacheSmiley(file);
} }

View File

@ -21,11 +21,11 @@
#define SMILEYPACK_H #define SMILEYPACK_H
#include <QHash> #include <QHash>
#include <QIcon>
#include <QMutex>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QIcon>
#include <QMutex>
class SmileyPack : public QObject class SmileyPack : public QObject
{ {

View File

@ -18,9 +18,9 @@
*/ */
#include "toxsave.h" #include "toxsave.h"
#include "src/widget/gui.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include "src/widget/gui.h"
#include "src/widget/tool/profileimporter.h" #include "src/widget/tool/profileimporter.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QFileInfo> #include <QFileInfo>
@ -44,8 +44,7 @@ bool handleToxSave(const QString& path)
{ {
Core* core = Core::getInstance(); Core* core = Core::getInstance();
while (!core) while (!core) {
{
core = Core::getInstance(); core = Core::getInstance();
qApp->processEvents(); qApp->processEvents();
} }

View File

@ -23,10 +23,9 @@
#define PLATFORM_AUTORUN_H #define PLATFORM_AUTORUN_H
namespace Platform namespace Platform {
{ bool setAutorun(bool on);
bool setAutorun(bool on); bool getAutorun();
bool getAutorun();
} }
#endif // PLATFORM_AUTORUN_H #endif // PLATFORM_AUTORUN_H

View File

@ -19,21 +19,24 @@
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
#include "src/platform/autorun.h" #include "src/platform/autorun.h"
#include <QSettings> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QSettings>
#include <QStandardPaths> #include <QStandardPaths>
#include <QCoreApplication>
int state ; int state;
bool Platform::setAutorun(bool on) bool Platform::setAutorun(bool on)
{ {
QString qtoxPlist = QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QDir::separator() + QString qtoxPlist =
"Library" + QDir::separator() + "LaunchAgents" + QDir::separator() + "chat.tox.qtox.autorun.plist"); QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
QString qtoxDir = QDir::cleanPath(QCoreApplication::applicationDirPath() + QDir::separator() + "qtox"); + QDir::separator() + "Library" + QDir::separator() + "LaunchAgents"
+ QDir::separator() + "chat.tox.qtox.autorun.plist");
QString qtoxDir =
QDir::cleanPath(QCoreApplication::applicationDirPath() + QDir::separator() + "qtox");
QSettings autoRun(qtoxPlist, QSettings::NativeFormat); QSettings autoRun(qtoxPlist, QSettings::NativeFormat);
autoRun.setValue("Label","chat.tox.qtox.autorun"); autoRun.setValue("Label", "chat.tox.qtox.autorun");
autoRun.setValue("Program", qtoxDir); autoRun.setValue("Program", qtoxDir);
state = on; state = on;
@ -46,4 +49,4 @@ bool Platform::getAutorun()
return state; return state;
} }
#endif // defined(__APPLE__) && defined(__MACH__) #endif // defined(__APPLE__) && defined(__MACH__)

View File

@ -19,10 +19,10 @@
#include <QApplication> #include <QApplication>
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
#include "src/platform/autorun.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include <windows.h> #include "src/platform/autorun.h"
#include <string> #include <string>
#include <windows.h>
#ifdef UNICODE #ifdef UNICODE
/** /**
@ -31,43 +31,48 @@
* easier to reuse and compatible with both setups. * easier to reuse and compatible with both setups.
*/ */
using tstring = std::wstring; using tstring = std::wstring;
static inline tstring toTString(QString s) { return s.toStdWString(); } static inline tstring toTString(QString s)
{
return s.toStdWString();
}
#else #else
using tstring = std::string; using tstring = std::string;
static inline tstring toTString(QString s) { return s.toStdString(); } static inline tstring toTString(QString s)
{
return s.toStdString();
}
#endif #endif
namespace Platform namespace Platform {
inline tstring currentCommandLine()
{ {
inline tstring currentCommandLine() return toTString("\"" + QApplication::applicationFilePath().replace('/', '\\') + "\" -p \""
{ + Settings::getInstance().getCurrentProfile() + "\"");
return toTString("\"" + QApplication::applicationFilePath().replace('/', '\\') + "\" -p \"" + }
Settings::getInstance().getCurrentProfile() + "\"");
}
inline tstring currentRegistryKeyName() inline tstring currentRegistryKeyName()
{ {
return toTString("qTox - " + Settings::getInstance().getCurrentProfile()); return toTString("qTox - " + Settings::getInstance().getCurrentProfile());
} }
} }
bool Platform::setAutorun(bool on) bool Platform::setAutorun(bool on)
{ {
HKEY key = 0; HKEY key = 0;
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"),
0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) 0, KEY_ALL_ACCESS, &key)
!= ERROR_SUCCESS)
return false; return false;
bool result = false; bool result = false;
tstring keyName = currentRegistryKeyName(); tstring keyName = currentRegistryKeyName();
if (on) if (on) {
{
tstring path = currentCommandLine(); tstring path = currentCommandLine();
result = RegSetValueEx(key, keyName.c_str(), 0, REG_SZ, (PBYTE)path.c_str(), result = RegSetValueEx(key, keyName.c_str(), 0, REG_SZ, (PBYTE)path.c_str(),
path.length() * sizeof(TCHAR)) == ERROR_SUCCESS; path.length() * sizeof(TCHAR))
} == ERROR_SUCCESS;
else } else
result = RegDeleteValue(key, keyName.c_str()) == ERROR_SUCCESS; result = RegDeleteValue(key, keyName.c_str()) == ERROR_SUCCESS;
RegCloseKey(key); RegCloseKey(key);
@ -78,21 +83,23 @@ bool Platform::getAutorun()
{ {
HKEY key = 0; HKEY key = 0;
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"),
0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) 0, KEY_ALL_ACCESS, &key)
!= ERROR_SUCCESS)
return false; return false;
tstring keyName = currentRegistryKeyName(); tstring keyName = currentRegistryKeyName();
TCHAR path[MAX_PATH] = { 0 }; TCHAR path[MAX_PATH] = {0};
DWORD length = sizeof(path); DWORD length = sizeof(path);
DWORD type = REG_SZ; DWORD type = REG_SZ;
bool result = false; bool result = false;
if (RegQueryValueEx(key, keyName.c_str(), 0, &type, (PBYTE)path, &length) == ERROR_SUCCESS && type == REG_SZ) if (RegQueryValueEx(key, keyName.c_str(), 0, &type, (PBYTE)path, &length) == ERROR_SUCCESS
&& type == REG_SZ)
result = true; result = true;
RegCloseKey(key); RegCloseKey(key);
return result; return result;
} }
#endif // Q_OS_WIN32 #endif // Q_OS_WIN32

View File

@ -19,43 +19,39 @@
#include <QApplication> #include <QApplication>
#if defined(Q_OS_UNIX) && !defined(__APPLE__) && !defined(__MACH__) #if defined(Q_OS_UNIX) && !defined(__APPLE__) && !defined(__MACH__)
#include "src/platform/autorun.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include <QProcessEnvironment> #include "src/platform/autorun.h"
#include <QDir> #include <QDir>
#include <QProcessEnvironment>
namespace Platform namespace Platform {
QString getAutostartDirPath()
{ {
QString getAutostartDirPath() QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
{ QString config = env.value("XDG_CONFIG_HOME");
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); if (config.isEmpty())
QString config = env.value("XDG_CONFIG_HOME"); config = QDir::homePath() + "/" + ".config";
if (config.isEmpty()) return config + "/autostart";
config = QDir::homePath() + "/" + ".config"; }
return config + "/autostart";
}
QString getAutostartFilePath(QString dir) QString getAutostartFilePath(QString dir)
{ {
return dir + "/qTox - " + Settings::getInstance().getCurrentProfile() + return dir + "/qTox - " + Settings::getInstance().getCurrentProfile() + ".desktop";
".desktop"; }
}
inline QString currentCommandLine() inline QString currentCommandLine()
{ {
return "\"" + QApplication::applicationFilePath() + "\" -p \"" + return "\"" + QApplication::applicationFilePath() + "\" -p \""
Settings::getInstance().getCurrentProfile() + "\""; + Settings::getInstance().getCurrentProfile() + "\"";
} }
} }
bool Platform::setAutorun(bool on) bool Platform::setAutorun(bool on)
{ {
QString dirPath = getAutostartDirPath(); QString dirPath = getAutostartDirPath();
QFile desktop(getAutostartFilePath(dirPath)); QFile desktop(getAutostartFilePath(dirPath));
if (on) if (on) {
{ if (!QDir().mkpath(dirPath) || !desktop.open(QFile::WriteOnly | QFile::Truncate))
if (!QDir().mkpath(dirPath) ||
!desktop.open(QFile::WriteOnly | QFile::Truncate))
return false; return false;
desktop.write("[Desktop Entry]\n"); desktop.write("[Desktop Entry]\n");
desktop.write("Type=Application\n"); desktop.write("Type=Application\n");
@ -65,8 +61,7 @@ bool Platform::setAutorun(bool on)
desktop.write("\n"); desktop.write("\n");
desktop.close(); desktop.close();
return true; return true;
} } else
else
return desktop.remove(); return desktop.remove();
} }
@ -75,4 +70,4 @@ bool Platform::getAutorun()
return QFile(getAutostartFilePath(getAutostartDirPath())).exists(); return QFile(getAutostartFilePath(getAutostartDirPath())).exists();
} }
#endif // defined(Q_OS_UNIX) && !defined(__APPLE__) && !defined(__MACH__) #endif // defined(Q_OS_UNIX) && !defined(__APPLE__) && !defined(__MACH__)

View File

@ -19,21 +19,20 @@
#ifndef AVFOUNDATION_H #ifndef AVFOUNDATION_H
#define AVFOUNDATION_H #define AVFOUNDATION_H
#include "src/video/videomode.h"
#include <QPair>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include <QPair>
#include "src/video/videomode.h"
#ifndef Q_OS_MACX #ifndef Q_OS_MACX
#error "This file is only meant to be compiled for Mac OS X targets" #error "This file is only meant to be compiled for Mac OS X targets"
#endif #endif
namespace avfoundation namespace avfoundation {
{ const QString CAPTURE_SCREEN{"Capture screen"};
const QString CAPTURE_SCREEN{"Capture screen"};
QVector<VideoMode> getDeviceModes(QString devName); QVector<VideoMode> getDeviceModes(QString devName);
QVector<QPair<QString, QString>> getDeviceList(); QVector<QPair<QString, QString>> getDeviceList();
} }
#endif // AVFOUNDATION_H #endif // AVFOUNDATION_H

View File

@ -21,14 +21,14 @@
#include "directshow.h" #include "directshow.h"
#include <QDebug>
#include <amvideo.h>
#include <cassert>
#include <cstdint> #include <cstdint>
#include <dvdmedia.h>
#include <objbase.h> #include <objbase.h>
#include <strmif.h> #include <strmif.h>
#include <amvideo.h>
#include <dvdmedia.h>
#include <uuids.h> #include <uuids.h>
#include <cassert>
#include <QDebug>
/** /**
* Most of this file is adapted from libavdevice's dshow.c, * Most of this file is adapted from libavdevice's dshow.c,
@ -36,38 +36,38 @@
* stdout and is not part of the public API for some reason. * stdout and is not part of the public API for some reason.
*/ */
static char *wcharToUtf8(wchar_t *w) static char* wcharToUtf8(wchar_t* w)
{ {
int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0); int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
char *s = new char[l]; char* s = new char[l];
if (s) if (s)
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0); WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
return s; return s;
} }
QVector<QPair<QString,QString>> DirectShow::getDeviceList() QVector<QPair<QString, QString>> DirectShow::getDeviceList()
{ {
IMoniker* m = nullptr; IMoniker* m = nullptr;
QVector<QPair<QString,QString>> devices; QVector<QPair<QString, QString>> devices;
ICreateDevEnum* devenum = nullptr; ICreateDevEnum* devenum = nullptr;
if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
IID_ICreateDevEnum, (void**) &devenum) != S_OK) (void**)&devenum)
!= S_OK)
return devices; return devices;
IEnumMoniker* classenum = nullptr; IEnumMoniker* classenum = nullptr;
if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, (IEnumMoniker**)&classenum, 0)
(IEnumMoniker**)&classenum, 0) != S_OK) != S_OK)
return devices; return devices;
while (classenum->Next(1, &m, nullptr) == S_OK) while (classenum->Next(1, &m, nullptr) == S_OK) {
{
VARIANT var; VARIANT var;
IPropertyBag* bag = nullptr; IPropertyBag* bag = nullptr;
LPMALLOC coMalloc = nullptr; LPMALLOC coMalloc = nullptr;
IBindCtx* bindCtx = nullptr; IBindCtx* bindCtx = nullptr;
LPOLESTR olestr = nullptr; LPOLESTR olestr = nullptr;
char *devIdString=nullptr, *devHumanName=nullptr; char *devIdString = nullptr, *devHumanName = nullptr;
if (CoGetMalloc(1, &coMalloc) != S_OK) if (CoGetMalloc(1, &coMalloc) != S_OK)
goto fail; goto fail;
@ -93,9 +93,9 @@ QVector<QPair<QString,QString>> DirectShow::getDeviceList()
goto fail; goto fail;
devHumanName = wcharToUtf8(var.bstrVal); devHumanName = wcharToUtf8(var.bstrVal);
devices += {QString("video=")+devIdString, devHumanName}; devices += {QString("video=") + devIdString, devHumanName};
fail: fail:
if (olestr && coMalloc) if (olestr && coMalloc)
coMalloc->Free(olestr); coMalloc->Free(olestr);
if (bindCtx) if (bindCtx)
@ -120,17 +120,17 @@ static IBaseFilter* getDevFilter(QString devName)
IMoniker* m = nullptr; IMoniker* m = nullptr;
ICreateDevEnum* devenum = nullptr; ICreateDevEnum* devenum = nullptr;
if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
IID_ICreateDevEnum, (void**) &devenum) != S_OK) (void**)&devenum)
!= S_OK)
return devFilter; return devFilter;
IEnumMoniker* classenum = nullptr; IEnumMoniker* classenum = nullptr;
if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, (IEnumMoniker**)&classenum, 0)
(IEnumMoniker**)&classenum, 0) != S_OK) != S_OK)
return devFilter; return devFilter;
while (classenum->Next(1, &m, nullptr) == S_OK) while (classenum->Next(1, &m, nullptr) == S_OK) {
{
LPMALLOC coMalloc = nullptr; LPMALLOC coMalloc = nullptr;
IBindCtx* bindCtx = nullptr; IBindCtx* bindCtx = nullptr;
LPOLESTR olestr = nullptr; LPOLESTR olestr = nullptr;
@ -156,7 +156,7 @@ static IBaseFilter* getDevFilter(QString devName)
if (m->BindToObject(0, 0, IID_IBaseFilter, (void**)&devFilter) != S_OK) if (m->BindToObject(0, 0, IID_IBaseFilter, (void**)&devFilter) != S_OK)
goto fail; goto fail;
fail: fail:
if (olestr && coMalloc) if (olestr && coMalloc)
coMalloc->Free(olestr); coMalloc->Free(olestr);
if (bindCtx) if (bindCtx)
@ -167,7 +167,7 @@ fail:
classenum->Release(); classenum->Release();
if (!devFilter) if (!devFilter)
qWarning() << "Could't find the device "<<devName; qWarning() << "Could't find the device " << devName;
return devFilter; return devFilter;
} }
@ -183,14 +183,13 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
// The outter loop tries to find a valid output pin // The outter loop tries to find a valid output pin
GUID category; GUID category;
DWORD r2; DWORD r2;
IEnumPins *pins = nullptr; IEnumPins* pins = nullptr;
IPin *pin; IPin* pin;
if (devFilter->EnumPins(&pins) != S_OK) if (devFilter->EnumPins(&pins) != S_OK)
return modes; return modes;
while (pins->Next(1, &pin, nullptr) == S_OK) while (pins->Next(1, &pin, nullptr) == S_OK) {
{ IKsPropertySet* p = nullptr;
IKsPropertySet *p = nullptr;
PIN_INFO info; PIN_INFO info;
pin->QueryPinInfo(&info); pin->QueryPinInfo(&info);
@ -199,8 +198,8 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
goto next; goto next;
if (pin->QueryInterface(IID_IKsPropertySet, (void**)&p) != S_OK) if (pin->QueryInterface(IID_IKsPropertySet, (void**)&p) != S_OK)
goto next; goto next;
if (p->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, if (p->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, nullptr, 0, &category, sizeof(GUID), &r2)
nullptr, 0, &category, sizeof(GUID), &r2) != S_OK) != S_OK)
goto next; goto next;
if (!IsEqualGUID(category, PIN_CATEGORY_CAPTURE)) if (!IsEqualGUID(category, PIN_CATEGORY_CAPTURE))
goto next; goto next;
@ -208,8 +207,8 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
// Now we can list the video modes for the current pin // Now we can list the video modes for the current pin
// Prepare for another wall of spaghetti DIRECT SHOW QUALITY code // Prepare for another wall of spaghetti DIRECT SHOW QUALITY code
{ {
IAMStreamConfig *config = nullptr; IAMStreamConfig* config = nullptr;
VIDEO_STREAM_CONFIG_CAPS *vcaps = nullptr; VIDEO_STREAM_CONFIG_CAPS* vcaps = nullptr;
int size, n; int size, n;
if (pin->QueryInterface(IID_IAMStreamConfig, (void**)&config) != S_OK) if (pin->QueryInterface(IID_IAMStreamConfig, (void**)&config) != S_OK)
goto next; goto next;
@ -219,8 +218,7 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
assert(size == sizeof(VIDEO_STREAM_CONFIG_CAPS)); assert(size == sizeof(VIDEO_STREAM_CONFIG_CAPS));
vcaps = new VIDEO_STREAM_CONFIG_CAPS; vcaps = new VIDEO_STREAM_CONFIG_CAPS;
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i) {
{
AM_MEDIA_TYPE* type = nullptr; AM_MEDIA_TYPE* type = nullptr;
VideoMode mode; VideoMode mode;
if (config->GetStreamCaps(i, &type, (BYTE*)vcaps) != S_OK) if (config->GetStreamCaps(i, &type, (BYTE*)vcaps) != S_OK)
@ -236,16 +234,16 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
if (!modes.contains(mode)) if (!modes.contains(mode))
modes.append(std::move(mode)); modes.append(std::move(mode));
nextformat: nextformat:
if (type->pbFormat) if (type->pbFormat)
CoTaskMemFree(type->pbFormat); CoTaskMemFree(type->pbFormat);
CoTaskMemFree(type); CoTaskMemFree(type);
} }
pinend: pinend:
config->Release(); config->Release();
delete vcaps; delete vcaps;
} }
next: next:
if (p) if (p)
p->Release(); p->Release();
pin->Release(); pin->Release();

View File

@ -21,19 +21,18 @@
#ifndef DIRECTSHOW_H #ifndef DIRECTSHOW_H
#define DIRECTSHOW_H #define DIRECTSHOW_H
#include "src/video/videomode.h"
#include <QPair>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include <QPair>
#include "src/video/videomode.h"
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
#error "This file is only meant to be compiled for Windows targets" #error "This file is only meant to be compiled for Windows targets"
#endif #endif
namespace DirectShow namespace DirectShow {
{ QVector<QPair<QString, QString>> getDeviceList();
QVector<QPair<QString,QString>> getDeviceList(); QVector<VideoMode> getDeviceModes(QString devName);
QVector<VideoMode> getDeviceModes(QString devName);
} }
#endif // DIRECTSHOW_H #endif // DIRECTSHOW_H

View File

@ -22,14 +22,14 @@
#include "v4l2.h" #include "v4l2.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <dirent.h>
#include <map>
#include <QDebug> #include <QDebug>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <map>
#include <sys/ioctl.h>
#include <unistd.h>
/** /**
* Most of this file is adapted from libavdevice's v4l2.c, * Most of this file is adapted from libavdevice's v4l2.c,
@ -37,25 +37,25 @@
* stdout and is not part of the public API for some reason. * stdout and is not part of the public API for some reason.
*/ */
static std::map<uint32_t,uint8_t> createPixFmtToQuality() static std::map<uint32_t, uint8_t> createPixFmtToQuality()
{ {
std::map<uint32_t,uint8_t> m; std::map<uint32_t, uint8_t> m;
m[V4L2_PIX_FMT_H264] = 3; m[V4L2_PIX_FMT_H264] = 3;
m[V4L2_PIX_FMT_MJPEG] = 2; m[V4L2_PIX_FMT_MJPEG] = 2;
m[V4L2_PIX_FMT_YUYV] = 1; m[V4L2_PIX_FMT_YUYV] = 1;
return m; return m;
} }
const std::map<uint32_t,uint8_t> pixFmtToQuality = createPixFmtToQuality(); const std::map<uint32_t, uint8_t> pixFmtToQuality = createPixFmtToQuality();
static std::map<uint32_t,QString> createPixFmtToName() static std::map<uint32_t, QString> createPixFmtToName()
{ {
std::map<uint32_t,QString> m; std::map<uint32_t, QString> m;
m[V4L2_PIX_FMT_H264] = QString("h264"); m[V4L2_PIX_FMT_H264] = QString("h264");
m[V4L2_PIX_FMT_MJPEG] = QString("mjpeg"); m[V4L2_PIX_FMT_MJPEG] = QString("mjpeg");
m[V4L2_PIX_FMT_YUYV] = QString("yuyv422"); m[V4L2_PIX_FMT_YUYV] = QString("yuyv422");
return m; return m;
} }
const std::map<uint32_t,QString> pixFmtToName = createPixFmtToName(); const std::map<uint32_t, QString> pixFmtToName = createPixFmtToName();
static int deviceOpen(QString devName, int* error) static int deviceOpen(QString devName, int* error)
{ {
@ -90,7 +90,8 @@ fail:
return -1; return -1;
} }
static QVector<unsigned short> getDeviceModeFramerates(int fd, unsigned w, unsigned h, uint32_t pixelFormat) static QVector<unsigned short> getDeviceModeFramerates(int fd, unsigned w, unsigned h,
uint32_t pixelFormat)
{ {
QVector<unsigned short> rates; QVector<unsigned short> rates;
v4l2_frmivalenum vfve{}; v4l2_frmivalenum vfve{};
@ -105,7 +106,7 @@ static QVector<unsigned short> getDeviceModeFramerates(int fd, unsigned w, unsig
rate = vfve.discrete.denominator / vfve.discrete.numerator; rate = vfve.discrete.denominator / vfve.discrete.numerator;
if (!rates.contains(rate)) if (!rates.contains(rate))
rates.append(rate); rates.append(rate);
break; break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS: case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE: case V4L2_FRMSIZE_TYPE_STEPWISE:
rate = vfve.stepwise.min.denominator / vfve.stepwise.min.numerator; rate = vfve.stepwise.min.denominator / vfve.stepwise.min.numerator;
@ -129,34 +130,32 @@ QVector<VideoMode> v4l2::getDeviceModes(QString devName)
v4l2_fmtdesc vfd{}; v4l2_fmtdesc vfd{};
vfd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vfd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while (!ioctl(fd, VIDIOC_ENUM_FMT, &vfd)) while (!ioctl(fd, VIDIOC_ENUM_FMT, &vfd)) {
{
vfd.index++; vfd.index++;
v4l2_frmsizeenum vfse{}; v4l2_frmsizeenum vfse{};
vfse.pixel_format = vfd.pixelformat; vfse.pixel_format = vfd.pixelformat;
while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse)) while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse)) {
{
VideoMode mode; VideoMode mode;
mode.pixel_format = vfse.pixel_format; mode.pixel_format = vfse.pixel_format;
switch (vfse.type) { switch (vfse.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE: case V4L2_FRMSIZE_TYPE_DISCRETE:
mode.width = vfse.discrete.width; mode.width = vfse.discrete.width;
mode.height = vfse.discrete.height; mode.height = vfse.discrete.height;
break; break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS: case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE: case V4L2_FRMSIZE_TYPE_STEPWISE:
mode.width = vfse.stepwise.max_width; mode.width = vfse.stepwise.max_width;
mode.height = vfse.stepwise.max_height; mode.height = vfse.stepwise.max_height;
break; break;
default: default:
continue; continue;
} }
QVector<unsigned short> rates = getDeviceModeFramerates(fd, mode.width, mode.height, vfd.pixelformat); QVector<unsigned short> rates =
for (unsigned short rate : rates) getDeviceModeFramerates(fd, mode.width, mode.height, vfd.pixelformat);
{ for (unsigned short rate : rates) {
mode.FPS = rate; mode.FPS = rate;
if (!modes.contains(mode)) if (!modes.contains(mode))
modes.append(std::move(mode)); modes.append(std::move(mode));
@ -173,7 +172,7 @@ QVector<QPair<QString, QString>> v4l2::getDeviceList()
QVector<QPair<QString, QString>> devices; QVector<QPair<QString, QString>> devices;
QVector<QString> deviceFiles; QVector<QString> deviceFiles;
DIR *dir = opendir("/dev"); DIR* dir = opendir("/dev");
if (!dir) if (!dir)
return devices; return devices;
@ -183,8 +182,7 @@ QVector<QPair<QString, QString>> v4l2::getDeviceList()
deviceFiles += QString("/dev/") + e->d_name; deviceFiles += QString("/dev/") + e->d_name;
closedir(dir); closedir(dir);
for (QString file : deviceFiles) for (QString file : deviceFiles) {
{
int fd = open(file.toStdString().c_str(), O_RDWR); int fd = open(file.toStdString().c_str(), O_RDWR);
if (fd < 0) if (fd < 0)
continue; continue;
@ -200,8 +198,7 @@ QVector<QPair<QString, QString>> v4l2::getDeviceList()
QString v4l2::getPixelFormatString(uint32_t pixel_format) QString v4l2::getPixelFormatString(uint32_t pixel_format)
{ {
if (pixFmtToName.find(pixel_format) == pixFmtToName.end()) if (pixFmtToName.find(pixel_format) == pixFmtToName.end()) {
{
qWarning() << "Pixel format not found"; qWarning() << "Pixel format not found";
return QString("unknown"); return QString("unknown");
} }
@ -210,14 +207,10 @@ QString v4l2::getPixelFormatString(uint32_t pixel_format)
bool v4l2::betterPixelFormat(uint32_t a, uint32_t b) bool v4l2::betterPixelFormat(uint32_t a, uint32_t b)
{ {
if (pixFmtToQuality.find(a) == pixFmtToQuality.end()) if (pixFmtToQuality.find(a) == pixFmtToQuality.end()) {
{
return false; return false;
} } else if (pixFmtToQuality.find(b) == pixFmtToQuality.end()) {
else if (pixFmtToQuality.find(b) == pixFmtToQuality.end())
{
return true; return true;
} }
return pixFmtToQuality.at(a) > pixFmtToQuality.at(b); return pixFmtToQuality.at(a) > pixFmtToQuality.at(b);
} }

View File

@ -19,22 +19,20 @@
#ifndef V4L2_H #ifndef V4L2_H
#define V4L2_H #define V4L2_H
#include "src/video/videomode.h"
#include <QPair>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include <QPair>
#include "src/video/videomode.h"
#if !(defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)) #if !(defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD))
#error "This file is only meant to be compiled for Linux or FreeBSD targets" #error "This file is only meant to be compiled for Linux or FreeBSD targets"
#endif #endif
namespace v4l2 namespace v4l2 {
{ QVector<VideoMode> getDeviceModes(QString devName);
QVector<VideoMode> getDeviceModes(QString devName); QVector<QPair<QString, QString>> getDeviceList();
QVector<QPair<QString, QString>> getDeviceList(); QString getPixelFormatString(uint32_t pixel_format);
QString getPixelFormatString(uint32_t pixel_format); bool betterPixelFormat(uint32_t a, uint32_t b);
bool betterPixelFormat(uint32_t a, uint32_t b);
} }
#endif // V4L2_H #endif // V4L2_H

View File

@ -23,9 +23,8 @@
#define PLATFORM_CAPSLOCK_H #define PLATFORM_CAPSLOCK_H
namespace Platform namespace Platform {
{ bool capsLockEnabled();
bool capsLockEnabled();
} }
#endif // PLATFORM_CAPSLOCK_H #endif // PLATFORM_CAPSLOCK_H

Some files were not shown because too many files have changed in this diff Show More