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>
@ -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
@ -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,19 +486,14 @@ 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;
} }
@ -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,11 +588,10 @@ 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,10 +614,8 @@ 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,10 +630,8 @@ 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,8 +659,7 @@ 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)
@ -692,13 +668,10 @@ void Audio::unsubscribeOutput(ALuint &sid)
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,8 +24,8 @@
#include <atomic> #include <atomic>
#include <cmath> #include <cmath>
#include <QObject>
#include <QMutex> #include <QMutex>
#include <QObject>
#include <QTimer> #include <QTimer>
#include <cassert> #include <cassert>
@ -51,12 +51,16 @@ class Audio : public QObject
class Private; class Private;
public: public:
enum class Sound
enum class Sound { NewMessage, Test, IncomingCall };
inline static QString getSound(Sound s) {
switch (s)
{ {
NewMessage,
Test,
IncomingCall
};
inline static QString getSound(Sound 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,8 +104,8 @@ 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
@ -111,7 +115,8 @@ public:
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();

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);
} }
@ -73,8 +70,7 @@ ChatLineContent *ChatLine::getContent(int col) const
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,8 +80,7 @@ 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);
} }
@ -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
@ -154,8 +150,7 @@ void ChatLine::addColumn(ChatLineContent* item, ColumnFormat fmt)
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];
@ -182,8 +177,7 @@ void ChatLine::layout(qreal w, QPointF 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;
@ -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);

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;
@ -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,20 +18,20 @@
*/ */
#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
@ -76,27 +76,18 @@ 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
@ -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,8 +205,7 @@ 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;
@ -227,14 +215,13 @@ void ChatLog::mouseMoveEvent(QMouseEvent* ev)
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;
} }
@ -318,7 +293,8 @@ 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))
@ -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);
} }
@ -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)
@ -483,8 +451,7 @@ 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;
@ -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();
} }
@ -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))
@ -693,7 +656,8 @@ void ChatLog::checkVisibility()
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() + QPointF(0, getVisibleRect().height()/2.0)); busyNotification->layout(useableWidth(), getVisibleRect().topLeft()
+ 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;
@ -810,8 +770,7 @@ void ChatLog::onWorkerTimeout()
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,8 +807,7 @@ 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)
@ -861,8 +819,7 @@ 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)
@ -879,8 +836,7 @@ 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)

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"
@ -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,

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,17 +37,18 @@
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())
@ -58,15 +59,13 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
// 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,7 +180,8 @@ 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;
} }
@ -210,7 +232,8 @@ 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) "(?:\\b)((www\\.)|(http[s]?|ftp)://)" // (protocol)://(printable - non-special character)
// http://ONEORMOREALHPA-DIGIT // http://ONEORMOREALHPA-DIGIT
"\\w+\\S+)" // any other character, lets domains and other "\\w+\\S+)" // any other character, lets domains and other
@ -223,12 +246,10 @@ QString ChatMessage::detectAnchors(const QString &str)
// also accepts tox:agilob@net as simplified TOX ID // 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';
} }
} }

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,7 +63,8 @@ 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,
[this](const QVariant& val) {
backgroundColor = val.value<QColor>(); backgroundColor = val.value<QColor>();
update(); update();
}); });
@ -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"));
} }
@ -118,12 +124,13 @@ 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.
@ -144,10 +151,11 @@ void FileTransferWidget::acceptTransfer(const QString &filepath)
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;
} }
@ -157,8 +165,7 @@ void FileTransferWidget::acceptTransfer(const QString &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();
@ -174,8 +181,7 @@ void FileTransferWidget::setBackgroundColor(const QColor &c, bool whiteFont)
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,8 +190,8 @@ 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*)
@ -207,16 +213,17 @@ void FileTransferWidget::paintEvent(QPaintEvent *)
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);
} }
} }
@ -231,8 +238,7 @@ void FileTransferWidget::onFileTransferInfo(ToxFile file)
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,8 +247,7 @@ 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);
@ -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("");
} }
@ -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"));
@ -465,16 +463,14 @@ 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,28 +478,23 @@ 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)
@ -511,8 +502,7 @@ 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()
+ "/>");
} }
} }
@ -556,14 +546,10 @@ QPixmap FileTransferWidget::scaleCropIntoSquare(const QPixmap &source, const int
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"

View File

@ -30,14 +30,14 @@ 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)

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,11 +20,11 @@
#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)
@ -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,7 +56,8 @@ 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()
.rotate(QTime::currentTime().msecsSinceStartOfDay() / 1000.0 * rotSpeed)
.translate(-size.width() / 2.0, -size.height() / 2.0); .translate(-size.width() / 2.0, -size.height() / 2.0);
painter->setOpacity(alpha); painter->setOpacity(alpha);
painter->setTransform(trans, true); painter->setTransform(trans, true);

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);
@ -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);
@ -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

@ -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,8 +22,8 @@
#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)
@ -35,9 +35,9 @@ CustomTextDocument::CustomTextDocument(QObject *parent)
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

@ -36,8 +36,7 @@ QTextDocument* DocumentCache::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);
} }

View File

@ -23,8 +23,7 @@ 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);
@ -43,4 +42,3 @@ 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,18 +50,15 @@ 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("<b>%1</b>"),
QStringLiteral("<i>%1</i>"), QStringLiteral("<i>%1</i>"),
QStringLiteral("<u>%1</u>"), QStringLiteral("<u>%1</u>"),
QStringLiteral("<s>%1</s>"), QStringLiteral("<s>%1</s>"),
QStringLiteral("<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]},
@ -70,8 +68,7 @@ static const QVector<QPair<QRegularExpression, QString>> textPatternStyle
{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);

View File

@ -19,37 +19,37 @@
*/ */
#include "core.h" #include "core.h"
#include "src/nexus.h"
#include "src/core/cstring.h"
#include "src/core/coreav.h"
#include "src/persistence/settings.h"
#include "src/widget/gui.h"
#include "src/persistence/profilelocker.h"
#include "src/net/avatarbroadcaster.h"
#include "src/persistence/profile.h"
#include "corefile.h" #include "corefile.h"
#include "src/core/coreav.h"
#include "src/core/cstring.h"
#include "src/net/avatarbroadcaster.h"
#include "src/nexus.h"
#include "src/persistence/profile.h"
#include "src/persistence/profilelocker.h"
#include "src/persistence/settings.h"
#include "src/video/camerasource.h" #include "src/video/camerasource.h"
#include "src/widget/gui.h"
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxav.h> #include <tox/toxav.h>
#include <ctime>
#include <cassert> #include <cassert>
#include <limits> #include <ctime>
#include <functional> #include <functional>
#include <limits>
#include <QBuffer>
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QList>
#include <QMutexLocker>
#include <QSaveFile> #include <QSaveFile>
#include <QStandardPaths> #include <QStandardPaths>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QCoreApplication>
#include <QDateTime>
#include <QList>
#include <QBuffer>
#include <QMutexLocker>
const QString Core::CONFIG_FILE_NAME = "data"; const QString Core::CONFIG_FILE_NAME = "data";
const QString Core::TOX_EXT = ".tox"; const QString Core::TOX_EXT = ".tox";
@ -57,8 +57,11 @@ QThread* Core::coreThread{nullptr};
#define MAX_GROUP_MESSAGE_LEN 1024 #define MAX_GROUP_MESSAGE_LEN 1024
Core::Core(QThread *CoreThread, Profile& profile) : Core::Core(QThread* CoreThread, Profile& profile)
tox(nullptr), av(nullptr), profile(profile), ready{false} : tox(nullptr)
, av(nullptr)
, profile(profile)
, ready{false}
{ {
coreThread = CoreThread; coreThread = CoreThread;
@ -66,18 +69,15 @@ Core::Core(QThread *CoreThread, Profile& profile) :
toxTimer->setSingleShot(true); toxTimer->setSingleShot(true);
connect(toxTimer, &QTimer::timeout, this, &Core::process); connect(toxTimer, &QTimer::timeout, this, &Core::process);
connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::process); connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::process);
} }
void Core::deadifyTox() void Core::deadifyTox()
{ {
if (av) if (av) {
{
delete av; delete av;
av = nullptr; av = nullptr;
} }
if (tox) if (tox) {
{
tox_kill(tox); tox_kill(tox);
tox = nullptr; tox = nullptr;
} }
@ -85,8 +85,7 @@ void Core::deadifyTox()
Core::~Core() Core::~Core()
{ {
if (coreThread->isRunning()) if (coreThread->isRunning()) {
{
if (QThread::currentThread() == coreThread) if (QThread::currentThread() == coreThread)
killTimers(false); killTimers(false);
else else
@ -94,10 +93,8 @@ Core::~Core()
Q_ARG(bool, false)); Q_ARG(bool, false));
} }
coreThread->exit(0); coreThread->exit(0);
if (QThread::currentThread() != coreThread) if (QThread::currentThread() != coreThread) {
{ while (coreThread->isRunning()) {
while (coreThread->isRunning())
{
qApp->processEvents(); qApp->processEvents();
coreThread->wait(500); coreThread->wait(500);
} }
@ -126,7 +123,8 @@ CoreAV *Core::getAv()
void Core::makeTox(QByteArray savedata) void Core::makeTox(QByteArray savedata)
{ {
// IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be disabled in options. // IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be
// disabled in options.
bool enableIPv6 = Settings::getInstance().getEnableIPv6(); bool enableIPv6 = Settings::getInstance().getEnableIPv6();
bool forceTCP = Settings::getInstance().getForceTCP(); bool forceTCP = Settings::getInstance().getForceTCP();
Settings::ProxyType proxyType = Settings::getInstance().getProxyType(); Settings::ProxyType proxyType = Settings::getInstance().getProxyType();
@ -150,18 +148,15 @@ void Core::makeTox(QByteArray savedata)
toxOptions.proxy_host = nullptr; toxOptions.proxy_host = nullptr;
toxOptions.proxy_port = 0; toxOptions.proxy_port = 0;
toxOptions.savedata_type = (!savedata.isNull() ? TOX_SAVEDATA_TYPE_TOX_SAVE : TOX_SAVEDATA_TYPE_NONE); toxOptions.savedata_type =
(!savedata.isNull() ? TOX_SAVEDATA_TYPE_TOX_SAVE : TOX_SAVEDATA_TYPE_NONE);
toxOptions.savedata_data = (uint8_t*)savedata.data(); toxOptions.savedata_data = (uint8_t*)savedata.data();
toxOptions.savedata_length = savedata.size(); toxOptions.savedata_length = savedata.size();
if (proxyType != Settings::ProxyType::ptNone) if (proxyType != Settings::ProxyType::ptNone) {
{ if (proxyAddr.length() > 255) {
if (proxyAddr.length() > 255)
{
qWarning() << "proxy address" << proxyAddr << "is too long"; qWarning() << "proxy address" << proxyAddr << "is too long";
} } else if (proxyAddr != "" && proxyPort > 0) {
else if (proxyAddr != "" && proxyPort > 0)
{
qDebug() << "using proxy" << proxyAddr << ":" << proxyPort; qDebug() << "using proxy" << proxyAddr << ":" << proxyPort;
// protection against changings in TOX_PROXY_TYPE enum // protection against changings in TOX_PROXY_TYPE enum
if (proxyType == Settings::ProxyType::ptSOCKS5) if (proxyType == Settings::ProxyType::ptSOCKS5)
@ -177,21 +172,19 @@ void Core::makeTox(QByteArray savedata)
TOX_ERR_NEW tox_err; TOX_ERR_NEW tox_err;
tox = tox_new(&toxOptions, &tox_err); tox = tox_new(&toxOptions, &tox_err);
switch (tox_err) switch (tox_err) {
{
case TOX_ERR_NEW_OK: case TOX_ERR_NEW_OK:
break; break;
case TOX_ERR_NEW_LOAD_BAD_FORMAT: case TOX_ERR_NEW_LOAD_BAD_FORMAT:
qWarning() << "failed to parse Tox save data"; qWarning() << "failed to parse Tox save data";
break; break;
case TOX_ERR_NEW_PORT_ALLOC: case TOX_ERR_NEW_PORT_ALLOC:
if (enableIPv6) if (enableIPv6) {
{
toxOptions.ipv6_enabled = false; toxOptions.ipv6_enabled = false;
tox = tox_new(&toxOptions, &tox_err); tox = tox_new(&toxOptions, &tox_err);
if (tox_err == TOX_ERR_NEW_OK) if (tox_err == TOX_ERR_NEW_OK) {
{ qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery "
qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery may not work properly."; "may not work properly.";
break; break;
} }
} }
@ -228,8 +221,7 @@ void Core::makeTox(QByteArray savedata)
} }
av = new CoreAV(tox); av = new CoreAV(tox);
if (av->getToxAv() == nullptr) if (av->getToxAv() == nullptr) {
{
qCritical() << "Toxav core failed to start"; qCritical() << "Toxav core failed to start";
emit failedToStart(); emit failedToStart();
return; return;
@ -242,19 +234,15 @@ void Core::makeTox(QByteArray savedata)
void Core::start() void Core::start()
{ {
bool isNewProfile = profile.isNewProfile(); bool isNewProfile = profile.isNewProfile();
if (isNewProfile) if (isNewProfile) {
{
qDebug() << "Creating a new profile"; qDebug() << "Creating a new profile";
makeTox(QByteArray()); makeTox(QByteArray());
setStatusMessage(tr("Toxing on qTox")); setStatusMessage(tr("Toxing on qTox"));
setUsername(profile.getName()); setUsername(profile.getName());
} } else {
else
{
qDebug() << "Loading user profile"; qDebug() << "Loading user profile";
QByteArray savedata = profile.loadToxSave(); QByteArray savedata = profile.loadToxSave();
if (savedata.isEmpty()) if (savedata.isEmpty()) {
{
emit failedToStart(); emit failedToStart();
return; return;
} }
@ -263,8 +251,7 @@ void Core::start()
qsrand(time(nullptr)); qsrand(time(nullptr));
if (!tox) if (!tox) {
{
ready = true; ready = true;
GUI::setEnabled(true); GUI::setEnabled(true);
return; return;
@ -303,25 +290,21 @@ void Core::start()
tox_callback_file_recv_control(tox, CoreFile::onFileControlCallback); tox_callback_file_recv_control(tox, CoreFile::onFileControlCallback);
QPixmap pic = profile.loadAvatar(); QPixmap pic = profile.loadAvatar();
if (!pic.isNull() && !pic.size().isEmpty()) if (!pic.isNull() && !pic.size().isEmpty()) {
{
QByteArray data; QByteArray data;
QBuffer buffer(&data); QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
pic.save(&buffer, "PNG"); pic.save(&buffer, "PNG");
buffer.close(); buffer.close();
setAvatar(data); setAvatar(data);
} } else {
else
{
qDebug() << "Self avatar not found, will broadcast empty avatar to friends"; qDebug() << "Self avatar not found, will broadcast empty avatar to friends";
setAvatar({}); setAvatar({});
} }
ready = true; ready = true;
if (isNewProfile) if (isNewProfile) {
{
profile.saveToxSave(); profile.saveToxSave();
} }
@ -346,8 +329,7 @@ void Core::start()
*/ */
void Core::process() void Core::process()
{ {
if (!isReady()) if (!isReady()) {
{
av->stop(); av->stop();
return; return;
} }
@ -360,12 +342,9 @@ void Core::process()
fflush(stdout); fflush(stdout);
#endif #endif
if (checkConnection()) if (checkConnection()) {
{
tolerance = CORE_DISCONNECT_TOLERANCE; tolerance = CORE_DISCONNECT_TOLERANCE;
} } else if (!(--tolerance)) {
else if (!(--tolerance))
{
bootstrapDht(); bootstrapDht();
tolerance = 3 * CORE_DISCONNECT_TOLERANCE; tolerance = 3 * CORE_DISCONNECT_TOLERANCE;
} }
@ -380,16 +359,13 @@ bool Core::checkConnection()
// static int count = 0; // static int count = 0;
bool toxConnected = tox_self_get_connection_status(tox) != TOX_CONNECTION_NONE; bool toxConnected = tox_self_get_connection_status(tox) != TOX_CONNECTION_NONE;
if (toxConnected && !isConnected) if (toxConnected && !isConnected) {
{
qDebug() << "Connected to the DHT"; qDebug() << "Connected to the DHT";
emit connected(); emit connected();
isConnected = true; isConnected = true;
// if (count) qDebug() << "disconnect count:" << count; // if (count) qDebug() << "disconnect count:" << count;
// count = 0; // count = 0;
} } else if (!toxConnected && isConnected) {
else if (!toxConnected && isConnected)
{
qDebug() << "Disconnected from the DHT"; qDebug() << "Disconnected from the DHT";
emit disconnected(); emit disconnected();
isConnected = false; isConnected = false;
@ -407,19 +383,19 @@ void Core::bootstrapDht()
QList<DhtServer> dhtServerList = s.getDhtServerList(); QList<DhtServer> dhtServerList = s.getDhtServerList();
int listSize = dhtServerList.size(); int listSize = dhtServerList.size();
if (listSize == 0) if (listSize == 0) {
{
qWarning() << "no bootstrap list?!?"; qWarning() << "no bootstrap list?!?";
return; return;
} }
static int j = qrand() % listSize; static int j = qrand() % listSize;
int i = 0; int i = 0;
while (i < 2) // i think the more we bootstrap, the more we jitter because the more we overwrite nodes while (i < 2) // i think the more we bootstrap, the more we jitter because the more we overwrite
// nodes
{ {
const DhtServer& dhtServer = dhtServerList[j % listSize]; const DhtServer& dhtServer = dhtServerList[j % listSize];
qDebug() << "Connecting to "+QString(dhtServer.address.toLatin1().data()) qDebug() << "Connecting to " + QString(dhtServer.address.toLatin1().data()) + ':'
+':'+QString().setNum(dhtServer.port)+" ("+dhtServer.name+')'; + QString().setNum(dhtServer.port) + " (" + dhtServer.name + ')';
QByteArray address = dhtServer.address.toLatin1(); QByteArray address = dhtServer.address.toLatin1();
// TODO: constucting the pk via ToxId is a workaround // TODO: constucting the pk via ToxId is a workaround
@ -427,13 +403,11 @@ void Core::bootstrapDht()
const uint8_t* pkPtr = reinterpret_cast<const uint8_t*>(pk.getBytes()); const uint8_t* pkPtr = reinterpret_cast<const uint8_t*>(pk.getBytes());
if (!tox_bootstrap(tox, address.constData(), dhtServer.port, pkPtr, nullptr)) if (!tox_bootstrap(tox, address.constData(), dhtServer.port, pkPtr, nullptr)) {
{
qDebug() << "Error bootstrapping from " + dhtServer.name; qDebug() << "Error bootstrapping from " + dhtServer.name;
} }
if (!tox_add_tcp_relay(tox, address.constData(), dhtServer.port, pkPtr, nullptr)) if (!tox_add_tcp_relay(tox, address.constData(), dhtServer.port, pkPtr, nullptr)) {
{
qDebug() << "Error adding TCP relay from " + dhtServer.name; qDebug() << "Error adding TCP relay from " + dhtServer.name;
} }
@ -442,22 +416,25 @@ void Core::bootstrapDht()
} }
} }
void Core::onFriendRequest(Tox*/* tox*/, const uint8_t* cFriendPk, void Core::onFriendRequest(Tox* /* tox*/, const uint8_t* cFriendPk, const uint8_t* cMessage,
const uint8_t* cMessage, size_t cMessageSize, void* core) size_t cMessageSize, void* core)
{ {
ToxPk friendPk(cFriendPk); ToxPk friendPk(cFriendPk);
emit static_cast<Core*>(core)->friendRequestReceived(friendPk, CString::toString(cMessage, cMessageSize)); emit static_cast<Core*>(core)->friendRequestReceived(friendPk,
CString::toString(cMessage, cMessageSize));
} }
void Core::onFriendMessage(Tox* /* tox*/, uint32_t friendId, TOX_MESSAGE_TYPE type, void Core::onFriendMessage(Tox* /* tox*/, uint32_t friendId, TOX_MESSAGE_TYPE type,
const uint8_t* cMessage, size_t cMessageSize, void* core) const uint8_t* cMessage, size_t cMessageSize, void* core)
{ {
bool isAction = (type == TOX_MESSAGE_TYPE_ACTION); bool isAction = (type == TOX_MESSAGE_TYPE_ACTION);
emit static_cast<Core*>(core)->friendMessageReceived(friendId,CString::toString(cMessage, cMessageSize), isAction); emit static_cast<Core*>(core)->friendMessageReceived(friendId,
CString::toString(cMessage, cMessageSize),
isAction);
} }
void Core::onFriendNameChange(Tox*/* tox*/, uint32_t friendId, void Core::onFriendNameChange(Tox* /* tox*/, uint32_t friendId, const uint8_t* cName,
const uint8_t* cName, size_t cNameSize, void* core) size_t cNameSize, void* core)
{ {
emit static_cast<Core*>(core)->friendUsernameChanged(friendId, CString::toString(cName, cNameSize)); emit static_cast<Core*>(core)->friendUsernameChanged(friendId, CString::toString(cName, cNameSize));
} }
@ -470,14 +447,14 @@ void Core::onFriendTypingChange(Tox*/* tox*/, uint32_t friendId, bool isTyping,
void Core::onStatusMessageChanged(Tox* /* tox*/, uint32_t friendId, const uint8_t* cMessage, void Core::onStatusMessageChanged(Tox* /* tox*/, uint32_t friendId, const uint8_t* cMessage,
size_t cMessageSize, void* core) size_t cMessageSize, void* core)
{ {
emit static_cast<Core*>(core)->friendStatusMessageChanged(friendId, CString::toString(cMessage, cMessageSize)); emit static_cast<Core*>(core)->friendStatusMessageChanged(friendId,
CString::toString(cMessage, cMessageSize));
} }
void Core::onUserStatusChanged(Tox* /* tox*/, uint32_t friendId, TOX_USER_STATUS userstatus, void* core) void Core::onUserStatusChanged(Tox* /* tox*/, uint32_t friendId, TOX_USER_STATUS userstatus, void* core)
{ {
Status status; Status status;
switch (userstatus) switch (userstatus) {
{
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
status = Status::Online; status = Status::Online;
break; break;
@ -501,26 +478,22 @@ void Core::onConnectionStatusChanged(Tox*/* tox*/, uint32_t friendId, TOX_CONNEC
emit static_cast<Core*>(core)->friendStatusChanged(friendId, friendStatus); emit static_cast<Core*>(core)->friendStatusChanged(friendId, friendStatus);
if (friendStatus == Status::Offline) if (friendStatus == Status::Offline)
static_cast<Core*>(core)->checkLastOnline(friendId); static_cast<Core*>(core)->checkLastOnline(friendId);
CoreFile::onConnectionStatusChanged(static_cast<Core*>(core), friendId, friendStatus != Status::Offline); CoreFile::onConnectionStatusChanged(static_cast<Core*>(core), friendId,
friendStatus != Status::Offline);
} }
void Core::onGroupInvite(Tox*, uint32_t friendId, TOX_CONFERENCE_TYPE type, void Core::onGroupInvite(Tox*, uint32_t friendId, TOX_CONFERENCE_TYPE type, const uint8_t* data,
const uint8_t* data, size_t length, void* vCore) size_t length, void* vCore)
{ {
Core* core = static_cast<Core*>(vCore); Core* core = static_cast<Core*>(vCore);
QByteArray pk((char*)data, length); QByteArray pk((char*)data, length);
if (type == TOX_CONFERENCE_TYPE_TEXT) if (type == TOX_CONFERENCE_TYPE_TEXT) {
{
qDebug() << QString("Text group invite by %1").arg(friendId); qDebug() << QString("Text group invite by %1").arg(friendId);
emit core->groupInviteReceived(friendId, type, pk); emit core->groupInviteReceived(friendId, type, pk);
} } else if (type == TOX_CONFERENCE_TYPE_AV) {
else if (type == TOX_CONFERENCE_TYPE_AV)
{
qDebug() << QString("AV group invite by %1").arg(friendId); qDebug() << QString("AV group invite by %1").arg(friendId);
emit core->groupInviteReceived(friendId, type, pk); emit core->groupInviteReceived(friendId, type, pk);
} } else {
else
{
qWarning() << "Group invite with unknown type " << type; qWarning() << "Group invite with unknown type " << type;
} }
} }
@ -545,8 +518,8 @@ void Core::onGroupNamelistChange(Tox*, uint32_t groupId, uint32_t peerId,
emit static_cast<Core*>(core)->groupNamelistChanged(groupId, peerId, change); emit static_cast<Core*>(core)->groupNamelistChanged(groupId, peerId, change);
} }
void Core::onGroupTitleChange(Tox*, uint32_t groupId, uint32_t peerId, void Core::onGroupTitleChange(Tox*, uint32_t groupId, uint32_t peerId, const uint8_t* cTitle,
const uint8_t* cTitle, size_t length, void* vCore) size_t length, void* vCore)
{ {
Core* core = static_cast<Core*>(vCore); Core* core = static_cast<Core*>(vCore);
QString author = core->getGroupPeerName(groupId, peerId); QString author = core->getGroupPeerName(groupId, peerId);
@ -563,12 +536,9 @@ void Core::acceptFriendRequest(const ToxPk& friendPk)
{ {
// TODO: error handling // TODO: error handling
uint32_t friendId = tox_friend_add_norequest(tox, friendPk.getBytes(), nullptr); uint32_t friendId = tox_friend_add_norequest(tox, friendPk.getBytes(), nullptr);
if (friendId == std::numeric_limits<uint32_t>::max()) if (friendId == std::numeric_limits<uint32_t>::max()) {
{
emit failedToAddFriend(friendPk); emit failedToAddFriend(friendPk);
} } else {
else
{
profile.saveToxSave(); profile.saveToxSave();
emit friendAdded(friendId, friendPk); emit friendAdded(friendId, friendPk);
emit friendshipChanged(friendId); emit friendshipChanged(friendId);
@ -579,39 +549,23 @@ void Core::requestFriendship(const ToxId& friendAddress, const QString& message)
{ {
ToxPk friendPk = friendAddress.getPublicKey(); ToxPk friendPk = friendAddress.getPublicKey();
if (!friendAddress.isValid()) if (!friendAddress.isValid()) {
{ emit failedToAddFriend(friendPk, tr("Invalid Tox ID"));
emit failedToAddFriend(friendPk, } else if (message.isEmpty()) {
tr("Invalid Tox ID")); emit failedToAddFriend(friendPk, tr("You need to write a message with your request"));
} } else if (message.size() > TOX_MAX_FRIEND_REQUEST_LENGTH) {
else if (message.isEmpty()) emit failedToAddFriend(friendPk, tr("Your message is too long!"));
{ } else if (hasFriendWithPublicKey(friendPk)) {
emit failedToAddFriend(friendPk, emit failedToAddFriend(friendPk, tr("Friend is already added"));
tr("You need to write a message with your request")); } else {
}
else if (message.size() > TOX_MAX_FRIEND_REQUEST_LENGTH)
{
emit failedToAddFriend(friendPk,
tr("Your message is too long!"));
}
else if (hasFriendWithPublicKey(friendPk))
{
emit failedToAddFriend(friendPk,
tr("Friend is already added"));
}
else
{
CString cMessage(message); CString cMessage(message);
uint32_t friendId = tox_friend_add(tox, friendAddress.getBytes(), uint32_t friendId =
cMessage.data(), cMessage.size(), nullptr); tox_friend_add(tox, friendAddress.getBytes(), cMessage.data(), cMessage.size(), nullptr);
if (friendId == std::numeric_limits<uint32_t>::max()) if (friendId == std::numeric_limits<uint32_t>::max()) {
{
qDebug() << "Failed to request friendship"; qDebug() << "Failed to request friendship";
emit failedToAddFriend(friendPk); emit failedToAddFriend(friendPk);
} } else {
else
{
qDebug() << "Requested friendship of " << friendId; qDebug() << "Requested friendship of " << friendId;
// Update our friendAddresses // Update our friendAddresses
Settings::getInstance().updateFriendAddress(friendAddress.toString()); Settings::getInstance().updateFriendAddress(friendAddress.toString());
@ -622,14 +576,10 @@ void Core::requestFriendship(const ToxId& friendAddress, const QString& message)
inviteStr = tr("/me offers friendship, \"%1\"").arg(message); inviteStr = tr("/me offers friendship, \"%1\"").arg(message);
Profile* profile = Nexus::getProfile(); Profile* profile = Nexus::getProfile();
if (profile->isHistoryEnabled()) if (profile->isHistoryEnabled()) {
{ profile->getHistory()->addNewMessage(friendAddress.toString(), inviteStr,
profile->getHistory()->addNewMessage(friendAddress.toString(),
inviteStr,
getSelfId().getPublicKey().toString(), getSelfId().getPublicKey().toString(),
QDateTime::currentDateTime(), QDateTime::currentDateTime(), true, QString());
true,
QString());
} }
// TODO: end // TODO: end
@ -644,8 +594,8 @@ int Core::sendMessage(uint32_t friendId, const QString& message)
{ {
QMutexLocker ml(&messageSendMutex); QMutexLocker ml(&messageSendMutex);
CString cMessage(message); CString cMessage(message);
int receipt = tox_friend_send_message(tox, friendId, TOX_MESSAGE_TYPE_NORMAL, int receipt = tox_friend_send_message(tox, friendId, TOX_MESSAGE_TYPE_NORMAL, cMessage.data(),
cMessage.data(), cMessage.size(), nullptr); cMessage.size(), nullptr);
emit messageSentResult(friendId, message, receipt); emit messageSentResult(friendId, message, receipt);
return receipt; return receipt;
} }
@ -654,8 +604,8 @@ int Core::sendAction(uint32_t friendId, const QString &action)
{ {
QMutexLocker ml(&messageSendMutex); QMutexLocker ml(&messageSendMutex);
CString cMessage(action); CString cMessage(action);
int receipt = tox_friend_send_message(tox, friendId, TOX_MESSAGE_TYPE_ACTION, int receipt = tox_friend_send_message(tox, friendId, TOX_MESSAGE_TYPE_ACTION, cMessage.data(),
cMessage.data(), cMessage.size(), nullptr); cMessage.size(), nullptr);
emit messageSentResult(friendId, action, receipt); emit messageSentResult(friendId, action, receipt);
return receipt; return receipt;
} }
@ -671,20 +621,17 @@ void Core::sendGroupMessageWithType(int groupId, const QString& message, TOX_MES
{ {
QList<CString> cMessages = splitMessage(message, MAX_GROUP_MESSAGE_LEN); QList<CString> cMessages = splitMessage(message, MAX_GROUP_MESSAGE_LEN);
for (auto &cMsg :cMessages) for (auto& cMsg : cMessages) {
{
TOX_ERR_CONFERENCE_SEND_MESSAGE error; TOX_ERR_CONFERENCE_SEND_MESSAGE error;
bool success = tox_conference_send_message(tox, groupId, type, bool success =
cMsg.data(), cMsg.size(), &error); tox_conference_send_message(tox, groupId, type, cMsg.data(), cMsg.size(), &error);
if (success && error == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK) if (success && error == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK) {
{
return; return;
} }
qCritical() << "Fail of tox_conference_send_message"; qCritical() << "Fail of tox_conference_send_message";
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_SEND_MESSAGE_CONFERENCE_NOT_FOUND: case TOX_ERR_CONFERENCE_SEND_MESSAGE_CONFERENCE_NOT_FOUND:
qCritical() << "Conference not found"; qCritical() << "Conference not found";
return; return;
@ -718,18 +665,15 @@ void Core::changeGroupTitle(int groupId, const QString& title)
{ {
CString cTitle(title); CString cTitle(title);
TOX_ERR_CONFERENCE_TITLE error; TOX_ERR_CONFERENCE_TITLE error;
bool success = tox_conference_set_title(tox, groupId, cTitle.data(), bool success = tox_conference_set_title(tox, groupId, cTitle.data(), cTitle.size(), &error);
cTitle.size(), &error);
if (success && error == TOX_ERR_CONFERENCE_TITLE_OK) if (success && error == TOX_ERR_CONFERENCE_TITLE_OK) {
{
emit groupTitleChanged(groupId, getUsername(), title); emit groupTitleChanged(groupId, getUsername(), title);
return; return;
} }
qCritical() << "Fail of tox_conference_set_title"; qCritical() << "Fail of tox_conference_set_title";
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_TITLE_CONFERENCE_NOT_FOUND: case TOX_ERR_CONFERENCE_TITLE_CONFERENCE_NOT_FOUND:
qCritical() << "Conference not found"; qCritical() << "Conference not found";
break; break;
@ -789,8 +733,7 @@ void Core::removeFriend(uint32_t friendId, bool fake)
if (!isReady() || fake) if (!isReady() || fake)
return; return;
if (!tox_friend_delete(tox, friendId, nullptr)) if (!tox_friend_delete(tox, friendId, nullptr)) {
{
emit failedToRemoveFriend(friendId); emit failedToRemoveFriend(friendId);
return; return;
} }
@ -807,15 +750,13 @@ void Core::removeGroup(int groupId, bool fake)
TOX_ERR_CONFERENCE_DELETE error; TOX_ERR_CONFERENCE_DELETE error;
bool success = tox_conference_delete(tox, groupId, &error); bool success = tox_conference_delete(tox, groupId, &error);
if (success && error == TOX_ERR_CONFERENCE_DELETE_OK) if (success && error == TOX_ERR_CONFERENCE_DELETE_OK) {
{
av->leaveGroupCall(groupId); av->leaveGroupCall(groupId);
return; return;
} }
qCritical() << "Fail of tox_conference_delete"; qCritical() << "Fail of tox_conference_delete";
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND: case TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND:
qCritical() << "Conference not found"; qCritical() << "Conference not found";
break; break;
@ -847,8 +788,7 @@ void Core::setUsername(const QString& username)
CString cUsername(username); CString cUsername(username);
if (!tox_self_set_name(tox, cUsername.data(), cUsername.size(), nullptr)) if (!tox_self_set_name(tox, cUsername.data(), cUsername.size(), nullptr)) {
{
emit failedToSetUsername(username); emit failedToSetUsername(username);
return; return;
} }
@ -860,15 +800,12 @@ void Core::setUsername(const QString& username)
void Core::setAvatar(const QByteArray& data) void Core::setAvatar(const QByteArray& data)
{ {
if (!data.isEmpty()) if (!data.isEmpty()) {
{
QPixmap pic; QPixmap pic;
pic.loadFromData(data); pic.loadFromData(data);
profile.saveAvatar(data, getSelfId().getPublicKey().toString()); profile.saveAvatar(data, getSelfId().getPublicKey().toString());
emit selfAvatarChanged(pic); emit selfAvatarChanged(pic);
} } else {
else
{
emit selfAvatarChanged(QPixmap(":/img/contact_dark.svg")); emit selfAvatarChanged(QPixmap(":/img/contact_dark.svg"));
} }
@ -937,8 +874,7 @@ void Core::setStatusMessage(const QString& message)
CString cMessage(message); CString cMessage(message);
if (!tox_self_set_status_message(tox, cMessage.data(), cMessage.size(), nullptr)) if (!tox_self_set_status_message(tox, cMessage.data(), cMessage.size(), nullptr)) {
{
emit failedToSetStatusMessage(message); emit failedToSetStatusMessage(message);
return; return;
} }
@ -951,8 +887,7 @@ void Core::setStatusMessage(const QString& message)
void Core::setStatus(Status status) void Core::setStatus(Status status)
{ {
TOX_USER_STATUS userstatus; TOX_USER_STATUS userstatus;
switch (status) switch (status) {
{
case Status::Online: case Status::Online:
userstatus = TOX_USER_STATUS_NONE; userstatus = TOX_USER_STATUS_NONE;
break; break;
@ -1004,47 +939,43 @@ QByteArray Core::getToxSaveData()
void Core::loadFriends() void Core::loadFriends()
{ {
const uint32_t friendCount = tox_self_get_friend_list_size(tox); const uint32_t friendCount = tox_self_get_friend_list_size(tox);
if (friendCount > 0) if (friendCount > 0) {
{
// assuming there are not that many friends to fill up the whole stack // assuming there are not that many friends to fill up the whole stack
uint32_t* ids = new uint32_t[friendCount]; uint32_t* ids = new uint32_t[friendCount];
tox_self_get_friend_list(tox, ids); tox_self_get_friend_list(tox, ids);
uint8_t friendPk[TOX_PUBLIC_KEY_SIZE] = {0x00}; uint8_t friendPk[TOX_PUBLIC_KEY_SIZE] = {0x00};
for (int32_t i = 0; i < static_cast<int32_t>(friendCount); ++i) for (int32_t i = 0; i < static_cast<int32_t>(friendCount); ++i) {
{ if (tox_friend_get_public_key(tox, ids[i], friendPk, nullptr)) {
if (tox_friend_get_public_key(tox, ids[i], friendPk, nullptr))
{
emit friendAdded(ids[i], ToxPk(friendPk)); emit friendAdded(ids[i], ToxPk(friendPk));
const size_t nameSize = tox_friend_get_name_size(tox, ids[i], nullptr); const size_t nameSize = tox_friend_get_name_size(tox, ids[i], nullptr);
if (nameSize && nameSize != SIZE_MAX) if (nameSize && nameSize != SIZE_MAX) {
{
uint8_t* name = new uint8_t[nameSize]; uint8_t* name = new uint8_t[nameSize];
if (tox_friend_get_name(tox, ids[i], name, nullptr)) if (tox_friend_get_name(tox, ids[i], name, nullptr))
emit friendUsernameChanged(ids[i], CString::toString(name, nameSize)); emit friendUsernameChanged(ids[i], CString::toString(name, nameSize));
delete[] name; delete[] name;
} }
const size_t statusMessageSize = tox_friend_get_status_message_size(tox, ids[i], nullptr); const size_t statusMessageSize =
if (statusMessageSize != SIZE_MAX) tox_friend_get_status_message_size(tox, ids[i], nullptr);
{ if (statusMessageSize != SIZE_MAX) {
uint8_t* statusMessage = new uint8_t[statusMessageSize]; uint8_t* statusMessage = new uint8_t[statusMessageSize];
if (tox_friend_get_status_message(tox, ids[i], statusMessage, nullptr)) if (tox_friend_get_status_message(tox, ids[i], statusMessage, nullptr)) {
{ emit friendStatusMessageChanged(ids[i], CString::toString(statusMessage,
emit friendStatusMessageChanged(ids[i], CString::toString(statusMessage, statusMessageSize)); statusMessageSize));
} }
delete[] statusMessage; delete[] statusMessage;
} }
checkLastOnline(ids[i]); checkLastOnline(ids[i]);
} }
} }
delete[] ids; delete[] ids;
} }
} }
void Core::checkLastOnline(uint32_t friendId) { void Core::checkLastOnline(uint32_t friendId)
{
const uint64_t lastOnline = tox_friend_get_last_online(tox, friendId, nullptr); const uint64_t lastOnline = tox_friend_get_last_online(tox, friendId, nullptr);
if (lastOnline != std::numeric_limits<uint64_t>::max()) if (lastOnline != std::numeric_limits<uint64_t>::max())
emit friendLastSeenChanged(friendId, QDateTime::fromTime_t(lastOnline)); emit friendLastSeenChanged(friendId, QDateTime::fromTime_t(lastOnline));
@ -1068,8 +999,7 @@ QVector<uint32_t> Core::getFriendList() const
*/ */
bool Core::parsePeerQueryError(TOX_ERR_CONFERENCE_PEER_QUERY error) const bool Core::parsePeerQueryError(TOX_ERR_CONFERENCE_PEER_QUERY error) const
{ {
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_PEER_QUERY_OK: case TOX_ERR_CONFERENCE_PEER_QUERY_OK:
return true; return true;
case TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND: case TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND:
@ -1113,8 +1043,7 @@ QString Core::getGroupPeerName(int groupId, int peerId) const
return QString{}; return QString{};
bool success = tox_conference_peer_get_name(tox, groupId, peerId, nameArray, &error); bool success = tox_conference_peer_get_name(tox, groupId, peerId, nameArray, &error);
if (!parsePeerQueryError(error) || !success) if (!parsePeerQueryError(error) || !success) {
{
qWarning() << "getGroupPeerName: Unknown error"; qWarning() << "getGroupPeerName: Unknown error";
return QString{}; return QString{};
} }
@ -1130,8 +1059,7 @@ ToxPk Core::getGroupPeerPk(int groupId, int peerId) const
uint8_t friendPk[TOX_PUBLIC_KEY_SIZE] = {0x00}; uint8_t friendPk[TOX_PUBLIC_KEY_SIZE] = {0x00};
TOX_ERR_CONFERENCE_PEER_QUERY error; TOX_ERR_CONFERENCE_PEER_QUERY error;
bool success = tox_conference_peer_get_public_key(tox, groupId, peerId, friendPk, &error); bool success = tox_conference_peer_get_public_key(tox, groupId, peerId, friendPk, &error);
if (!parsePeerQueryError(error) || !success) if (!parsePeerQueryError(error) || !success) {
{
qWarning() << "getGroupPeerToxId: Unknown error"; qWarning() << "getGroupPeerToxId: Unknown error";
return ToxPk(); return ToxPk();
} }
@ -1144,45 +1072,38 @@ ToxPk Core::getGroupPeerPk(int groupId, int peerId) const
*/ */
QList<QString> Core::getGroupPeerNames(int groupId) const QList<QString> Core::getGroupPeerNames(int groupId) const
{ {
if (!tox) if (!tox) {
{
qWarning() << "Can't get group peer names, tox is null"; qWarning() << "Can't get group peer names, tox is null";
return {}; return {};
} }
uint32_t nPeers = getGroupNumberPeers(groupId); uint32_t nPeers = getGroupNumberPeers(groupId);
if (nPeers == std::numeric_limits<uint32_t>::max()) if (nPeers == std::numeric_limits<uint32_t>::max()) {
{
qWarning() << "getGroupPeerNames: Unable to get number of peers"; qWarning() << "getGroupPeerNames: Unable to get number of peers";
return {}; return {};
} }
// TODO: Change to std::vector // TODO: Change to std::vector
std::unique_ptr<uint8_t[][TOX_MAX_NAME_LENGTH]> namesArray{ std::unique_ptr<uint8_t[][TOX_MAX_NAME_LENGTH]> namesArray{new uint8_t[nPeers][TOX_MAX_NAME_LENGTH]};
new uint8_t[nPeers][TOX_MAX_NAME_LENGTH]};
std::unique_ptr<uint16_t[]> lengths{new uint16_t[nPeers]}; std::unique_ptr<uint16_t[]> lengths{new uint16_t[nPeers]};
TOX_ERR_CONFERENCE_PEER_QUERY error; TOX_ERR_CONFERENCE_PEER_QUERY error;
uint32_t count = tox_conference_peer_count(tox, groupId, &error); uint32_t count = tox_conference_peer_count(tox, groupId, &error);
if (!parsePeerQueryError(error)) if (!parsePeerQueryError(error)) {
{
return {}; return {};
} }
if (count != nPeers) if (count != nPeers) {
{
qWarning() << "getGroupPeerNames: Unexpected peer count"; qWarning() << "getGroupPeerNames: Unexpected peer count";
return {}; return {};
} }
QList<QString> names; QList<QString> names;
for (uint32_t i = 0; i < nPeers; ++i) for (uint32_t i = 0; i < nPeers; ++i) {
{
lengths[i] = tox_conference_peer_get_name_size(tox, groupId, i, &error); lengths[i] = tox_conference_peer_get_name_size(tox, groupId, i, &error);
bool ok = tox_conference_peer_get_name(tox, groupId, i, namesArray[i], &error); bool ok = tox_conference_peer_get_name(tox, groupId, i, namesArray[i], &error);
if (parsePeerQueryError(error) && ok) if (parsePeerQueryError(error) && ok) {
{
names.push_back(CString::toString(namesArray[i], lengths[i])); names.push_back(CString::toString(namesArray[i], lengths[i]));
} }
} }
@ -1197,8 +1118,7 @@ QList<QString> Core::getGroupPeerNames(int groupId) const
*/ */
bool Core::parseConferenceJoinError(TOX_ERR_CONFERENCE_JOIN error) const bool Core::parseConferenceJoinError(TOX_ERR_CONFERENCE_JOIN error) const
{ {
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_JOIN_OK: case TOX_ERR_CONFERENCE_JOIN_OK:
return true; return true;
case TOX_ERR_CONFERENCE_JOIN_DUPLICATE: case TOX_ERR_CONFERENCE_JOIN_DUPLICATE:
@ -1236,25 +1156,20 @@ bool Core::parseConferenceJoinError(TOX_ERR_CONFERENCE_JOIN error) const
uint32_t Core::joinGroupchat(int32_t friendnumber, uint8_t type, uint32_t Core::joinGroupchat(int32_t friendnumber, uint8_t type,
const uint8_t* friend_group_public_key, uint16_t length) const const uint8_t* friend_group_public_key, uint16_t length) const
{ {
if (type == TOX_CONFERENCE_TYPE_TEXT) if (type == TOX_CONFERENCE_TYPE_TEXT) {
{
qDebug() << QString("Trying to join text groupchat invite sent by friend %1").arg(friendnumber); qDebug() << QString("Trying to join text groupchat invite sent by friend %1").arg(friendnumber);
TOX_ERR_CONFERENCE_JOIN error; TOX_ERR_CONFERENCE_JOIN error;
uint32_t groupId = tox_conference_join(tox, friendnumber, friend_group_public_key, length, &error); uint32_t groupId =
tox_conference_join(tox, friendnumber, friend_group_public_key, length, &error);
if (parseConferenceJoinError(error)) if (parseConferenceJoinError(error))
return groupId; return groupId;
else else
return std::numeric_limits<uint32_t>::max(); return std::numeric_limits<uint32_t>::max();
} } else if (type == TOX_CONFERENCE_TYPE_AV) {
else if (type == TOX_CONFERENCE_TYPE_AV)
{
qDebug() << QString("Trying to join AV groupchat invite sent by friend %1").arg(friendnumber); qDebug() << QString("Trying to join AV groupchat invite sent by friend %1").arg(friendnumber);
return toxav_join_av_groupchat(tox, friendnumber, friend_group_public_key, length, return toxav_join_av_groupchat(tox, friendnumber, friend_group_public_key, length,
CoreAV::groupCallCallback, CoreAV::groupCallCallback, const_cast<Core*>(this));
const_cast<Core*>(this)); } else {
}
else
{
qWarning() << "joinGroupchat: Unknown groupchat type " << type; qWarning() << "joinGroupchat: Unknown groupchat type " << type;
return std::numeric_limits<uint32_t>::max(); return std::numeric_limits<uint32_t>::max();
} }
@ -1268,8 +1183,7 @@ void Core::quitGroupChat(int groupId) const
TOX_ERR_CONFERENCE_DELETE error; TOX_ERR_CONFERENCE_DELETE error;
tox_conference_delete(tox, groupId, &error); tox_conference_delete(tox, groupId, &error);
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_DELETE_OK: case TOX_ERR_CONFERENCE_DELETE_OK:
return; return;
case TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND: case TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND:
@ -1285,8 +1199,7 @@ void Core::groupInviteFriend(uint32_t friendId, int groupId)
TOX_ERR_CONFERENCE_INVITE error; TOX_ERR_CONFERENCE_INVITE error;
tox_conference_invite(tox, friendId, groupId, &error); tox_conference_invite(tox, friendId, groupId, &error);
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_INVITE_OK: case TOX_ERR_CONFERENCE_INVITE_OK:
break; break;
case TOX_ERR_CONFERENCE_INVITE_CONFERENCE_NOT_FOUND: case TOX_ERR_CONFERENCE_INVITE_CONFERENCE_NOT_FOUND:
@ -1302,13 +1215,11 @@ void Core::groupInviteFriend(uint32_t friendId, int groupId)
int Core::createGroup(uint8_t type) int Core::createGroup(uint8_t type)
{ {
if (type == TOX_CONFERENCE_TYPE_TEXT) if (type == TOX_CONFERENCE_TYPE_TEXT) {
{
TOX_ERR_CONFERENCE_NEW error; TOX_ERR_CONFERENCE_NEW error;
uint32_t groupId = tox_conference_new(tox, &error); uint32_t groupId = tox_conference_new(tox, &error);
switch (error) switch (error) {
{
case TOX_ERR_CONFERENCE_NEW_OK: case TOX_ERR_CONFERENCE_NEW_OK:
emit emptyGroupCreated(groupId); emit emptyGroupCreated(groupId);
return groupId; return groupId;
@ -1318,15 +1229,11 @@ int Core::createGroup(uint8_t type)
default: default:
return std::numeric_limits<uint32_t>::max(); return std::numeric_limits<uint32_t>::max();
} }
} } else if (type == TOX_CONFERENCE_TYPE_AV) {
else if (type == TOX_CONFERENCE_TYPE_AV)
{
uint32_t groupId = toxav_add_av_groupchat(tox, CoreAV::groupCallCallback, this); uint32_t groupId = toxav_add_av_groupchat(tox, CoreAV::groupCallCallback, this);
emit emptyGroupCreated(groupId); emit emptyGroupCreated(groupId);
return groupId; return groupId;
} } else {
else
{
qWarning() << "createGroup: Unknown type " << type; qWarning() << "createGroup: Unknown type " << type;
return -1; return -1;
} }
@ -1347,8 +1254,7 @@ bool Core::isFriendOnline(uint32_t friendId) const
bool Core::hasFriendWithPublicKey(const ToxPk& publicKey) const bool Core::hasFriendWithPublicKey(const ToxPk& publicKey) const
{ {
// Validity check // Validity check
if (publicKey.isEmpty()) if (publicKey.isEmpty()) {
{
return false; return false;
} }
@ -1364,8 +1270,7 @@ bool Core::hasFriendWithPublicKey(const ToxPk &publicKey) const
ToxPk Core::getFriendPublicKey(uint32_t friendNumber) const ToxPk Core::getFriendPublicKey(uint32_t friendNumber) const
{ {
uint8_t rawid[TOX_PUBLIC_KEY_SIZE]; uint8_t rawid[TOX_PUBLIC_KEY_SIZE];
if (!tox_friend_get_public_key(tox, friendNumber, rawid, nullptr)) if (!tox_friend_get_public_key(tox, friendNumber, rawid, nullptr)) {
{
qWarning() << "getFriendPublicKey: Getting public key failed"; qWarning() << "getFriendPublicKey: Getting public key failed";
return ToxPk(); return ToxPk();
} }
@ -1379,8 +1284,7 @@ ToxPk Core::getFriendPublicKey(uint32_t friendNumber) const
QString Core::getFriendUsername(uint32_t friendnumber) const QString Core::getFriendUsername(uint32_t friendnumber) const
{ {
size_t namesize = tox_friend_get_name_size(tox, friendnumber, nullptr); size_t namesize = tox_friend_get_name_size(tox, friendnumber, nullptr);
if (namesize == SIZE_MAX) if (namesize == SIZE_MAX) {
{
qWarning() << "getFriendUsername: Failed to get name size for friend " << friendnumber; qWarning() << "getFriendUsername: Failed to get name size for friend " << friendnumber;
return QString(); return QString();
} }
@ -1396,14 +1300,11 @@ QList<CString> Core::splitMessage(const QString &message, int maxLen)
QList<CString> splittedMsgs; QList<CString> splittedMsgs;
QByteArray ba_message(message.toUtf8()); QByteArray ba_message(message.toUtf8());
while (ba_message.size() > maxLen) while (ba_message.size() > maxLen) {
{
int splitPos = ba_message.lastIndexOf(' ', maxLen - 1); int splitPos = ba_message.lastIndexOf(' ', maxLen - 1);
if (splitPos <= 0) if (splitPos <= 0) {
{
splitPos = maxLen; splitPos = maxLen;
if (ba_message[splitPos] & 0x80) if (ba_message[splitPos] & 0x80) {
{
do { do {
splitPos--; splitPos--;
} while (!(ba_message[splitPos] & 0x40)); } while (!(ba_message[splitPos] & 0x40));
@ -1424,8 +1325,7 @@ QString Core::getPeerName(const ToxPk& id) const
{ {
QString name; QString name;
uint32_t friendId = tox_friend_by_public_key(tox, id.getBytes(), nullptr); uint32_t friendId = tox_friend_by_public_key(tox, id.getBytes(), nullptr);
if (friendId == std::numeric_limits<uint32_t>::max()) if (friendId == std::numeric_limits<uint32_t>::max()) {
{
qWarning() << "getPeerName: No such peer"; qWarning() << "getPeerName: No such peer";
return name; return name;
} }
@ -1435,8 +1335,7 @@ QString Core::getPeerName(const ToxPk& id) const
return name; return name;
uint8_t* cname = new uint8_t[nameSize < TOX_MAX_NAME_LENGTH ? TOX_MAX_NAME_LENGTH : nameSize]; uint8_t* cname = new uint8_t[nameSize < TOX_MAX_NAME_LENGTH ? TOX_MAX_NAME_LENGTH : nameSize];
if (!tox_friend_get_name(tox, friendId, cname, nullptr)) if (!tox_friend_get_name(tox, friendId, cname, nullptr)) {
{
qWarning() << "getPeerName: Can't get name of friend " + QString().setNum(friendId); qWarning() << "getPeerName: Can't get name of friend " + QString().setNum(friendId);
delete[] cname; delete[] cname;
return name; return name;
@ -1475,8 +1374,7 @@ void Core::killTimers(bool onlyStop)
assert(QThread::currentThread() == coreThread); assert(QThread::currentThread() == coreThread);
av->stop(); av->stop();
toxTimer->stop(); toxTimer->stop();
if (!onlyStop) if (!onlyStop) {
{
delete toxTimer; delete toxTimer;
toxTimer = nullptr; toxTimer = nullptr;
} }

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;
@ -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;
@ -248,4 +242,3 @@ private:
}; };
#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.
*/ */
/** /**
@ -80,8 +83,9 @@ 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,8 +111,7 @@ 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);
} }
@ -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,13 +236,10 @@ 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);
@ -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,15 +310,13 @@ 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;
} }
@ -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,15 +348,11 @@ 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;
} }
} }
@ -395,12 +372,10 @@ 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;
@ -408,8 +383,7 @@ void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
ToxYUVFrame frame = vframe->toToxYUVFrame(); ToxYUVFrame frame = vframe->toToxYUVFrame();
if(!frame) if (!frame) {
{
return; return;
} }
@ -418,16 +392,12 @@ 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;
} }
} }
@ -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,7 +473,8 @@ 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;
@ -522,9 +487,9 @@ void CoreAV::invalidateGroupCallPeerSource(int group, int peer) {
*/ */
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,8 +590,7 @@ 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:
@ -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,11 +655,11 @@ 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))
@ -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,11 +700,11 @@ 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))
@ -763,8 +717,7 @@ void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, voi
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))

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,10 +77,8 @@ 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:
@ -97,13 +97,15 @@ signals:
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,
void* self);
static void videoFrameCallback(ToxAV* toxAV, uint32_t friendNum, uint16_t w, uint16_t h, 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, 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);

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;
} }
@ -88,8 +83,7 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
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,49 +274,46 @@ 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};
@ -360,56 +329,45 @@ 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->getFriendPublicKey(friendId).toString()); core->profile.saveAvatar(file->avatarData,
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);
} }
@ -513,10 +458,10 @@ 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;

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;
@ -44,33 +44,15 @@ private:
// 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,
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 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);
@ -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,
uint32_t kind,
uint64_t filesize,
const uint8_t* fname,
size_t fnameLen,
void* vCore); void* vCore);
static void onFileControlCallback(Tox* tox, uint32_t friendId, uint32_t fileId, static void onFileControlCallback(Tox* tox, uint32_t friendId, uint32_t fileId,
TOX_FILE_CONTROL control, void* core); TOX_FILE_CONTROL control, void* core);
static void onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId, static void 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);
static void onFileRecvChunkCallback(Tox *tox, static void onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t position,
uint32_t friendId, const uint8_t* data, size_t length, void* vCore);
uint32_t fileId, static void onConnectionStatusChanged(Core* core, uint32_t friendId, bool online);
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,8 +1,8 @@
#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
@ -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,16 +40,25 @@ 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

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())
{ {
} }

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,
// so we unsuscribe asynchronously, it's fine if the webcam takes a couple milliseconds more
// to poweroff.
QtConcurrent::run([]() { CameraSource::getInstance().unsubscribe(); }); QtConcurrent::run([]() { CameraSource::getInstance().unsubscribe(); });
if (videoSource) if (videoSource) {
{
videoSource->setDeleteOnClose(true); videoSource->setDeleteOnClose(true);
videoSource = nullptr; videoSource = nullptr;
} }
@ -180,18 +192,18 @@ ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other) noexcept
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(
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"); "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,8 +211,7 @@ ToxGroupCall::~ToxGroupCall()
{ {
Audio& audio = Audio::getInstance(); Audio& audio = Audio::getInstance();
for(quint32 v : peers) for (quint32 v : peers) {
{
audio.unsubscribeOutput(v); audio.unsubscribeOutput(v);
} }
} }

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"
@ -21,11 +21,15 @@ protected:
ToxCall() = default; ToxCall() = default;
explicit ToxCall(uint32_t CallId); explicit ToxCall(uint32_t CallId);
~ToxCall(); ~ToxCall();
public: public:
ToxCall(const ToxCall& other) = delete; ToxCall(const ToxCall& other) = delete;
ToxCall(ToxCall&& other) noexcept; ToxCall(ToxCall&& other) noexcept;
inline operator int() {return callId;} inline operator int()
{
return callId;
}
ToxCall& operator=(const ToxCall& other) = delete; ToxCall& operator=(const ToxCall& other) = delete;
ToxCall& operator=(ToxCall&& other) noexcept; ToxCall& operator=(ToxCall&& other) noexcept;
@ -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>{};
} }
@ -209,8 +201,7 @@ std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password,
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,8 +24,8 @@
#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
@ -60,7 +60,8 @@ const QRegularExpression ToxId::ToxIdRegEx(QString("(^|\\s)[A-Fa-f0-9]{%1}($|\\s
*/ */
ToxId::ToxId() ToxId::ToxId()
: toxId() : toxId()
{} {
}
/** /**
* @brief The copy constructor. * @brief The copy constructor.
@ -68,7 +69,8 @@ ToxId::ToxId()
*/ */
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,16 +84,11 @@ 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
} }
} }
@ -131,17 +128,11 @@ 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())) {
else if (rawId.length() == TOX_ADDRESS_SIZE
&& isToxId(rawId.toHex().toUpper()))
{
toxId = QByteArray(rawId); // construct from full toxid toxId = QByteArray(rawId); // construct from full toxid
} } else {
else
{
toxId = QByteArray(); // invalid id 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
{ {

View File

@ -15,7 +15,8 @@
*/ */
ToxPk::ToxPk() ToxPk::ToxPk()
: key() : key()
{} {
}
/** /**
* @brief The copy constructor. * @brief The copy constructor.
@ -23,7 +24,8 @@ ToxPk::ToxPk()
*/ */
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);
} }

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;

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,17 +18,19 @@
*/ */
#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);
@ -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();
} }

View File

@ -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

@ -35,10 +35,8 @@ GroupInvite::GroupInvite(int32_t friendID, uint8_t inviteType, const QByteArray&
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,9 +20,9 @@
#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
{ {

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,8 +48,7 @@ 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);
} }
} }

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();
} }
@ -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;
} }
@ -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;
} }
@ -229,15 +208,14 @@ bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout/*=-1*/)
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()
@ -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();

View File

@ -21,13 +21,13 @@
#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&)>;
@ -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;

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
@ -63,17 +64,15 @@ 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;
@ -96,19 +95,15 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QSt
#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);
@ -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();
@ -174,8 +172,7 @@ int main(int argc, char *argv[])
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)
@ -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);

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;
@ -178,15 +186,13 @@ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion()
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();
@ -200,8 +206,7 @@ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion()
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;
} }
@ -212,8 +217,7 @@ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion()
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;
} }
@ -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);
@ -303,15 +301,13 @@ QByteArray AutoUpdater::getUpdateFlist()
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();
@ -363,10 +359,12 @@ 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,
@ -379,15 +377,13 @@ AutoUpdater::UpdateFile AutoUpdater::getUpdateFile(UpdateFileMeta fileMeta,
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();
@ -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,8 +433,7 @@ 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;
@ -447,8 +441,7 @@ bool AutoUpdater::downloadUpdate()
// 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,24 +452,20 @@ 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;
@ -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;
} }
@ -566,8 +553,7 @@ 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;
@ -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();
@ -607,13 +592,12 @@ void AutoUpdater::installLocalUpdate()
// 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 +=
"\n\n"
+ QObject::tr("Version %1, %2")
.arg(newVersion.versionString,
QDateTime::fromMSecsSinceEpoch(newVersion.timestamp * 1000).toString()); 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);
} }
}; };
@ -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

@ -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
@ -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();
} }
@ -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();
} }
@ -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();
} }
@ -138,9 +134,12 @@ QByteArray Toxme::prepareEncryptedJson(QString url, int action, QString payload)
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();
} }
@ -201,8 +200,7 @@ Toxme::ExecCode Toxme::extractError(QString json)
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;
@ -245,10 +243,14 @@ QString Toxme::createAddress(ExecCode &code, QString server, ToxId id, QString a
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";
@ -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();
} }
@ -303,7 +303,8 @@ 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,

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)
{ {
@ -55,8 +55,7 @@ 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,10 +89,9 @@ 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);
@ -106,7 +101,9 @@ bool handleToxURI(const QString &toxURI)
return false; return false;
} }
ToxURIDialog *dialog = new ToxURIDialog(0, toxaddr, QObject::tr("%1 here! Tox me maybe?", ToxURIDialog* dialog = new ToxURIDialog(
0, toxaddr,
QObject::tr("%1 here! Tox me maybe?",
"Default message in Tox URI friend requests. Write something appropriate!") "Default message in Tox URI friend requests. Write something appropriate!")
.arg(Nexus::getCore()->getUsername())); .arg(Nexus::getCore()->getUsername()));
QObject::connect(dialog, &ToxURIDialog::finished, [=](int result) { QObject::connect(dialog, &ToxURIDialog::finished, [=](int result) {
@ -120,8 +117,8 @@ 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"));

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;
} }
@ -201,7 +201,8 @@ void Nexus::showMainGUI()
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,
Qt::BlockingQueuedConnection);
connect(core, &Core::badProxy, widget, &Widget::onBadProxyCore, Qt::BlockingQueuedConnection); connect(core, &Core::badProxy, widget, &Widget::onBadProxyCore, Qt::BlockingQueuedConnection);
connect(core, &Core::statusSet, widget, &Widget::onStatusSet); connect(core, &Core::statusSet, widget, &Widget::onStatusSet);
connect(core, &Core::usernameSet, widget, &Widget::setUsername); connect(core, &Core::usernameSet, widget, &Widget::setUsername);
@ -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

@ -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
@ -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";
} }
} }
@ -158,38 +150,34 @@ RawDatabase::~RawDatabase()
*/ */
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;
@ -252,8 +240,7 @@ bool RawDatabase::execNow(const RawDatabase::Query &statement)
*/ */
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;
} }
@ -295,8 +282,7 @@ void RawDatabase::execLater(const RawDatabase::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,14 +313,12 @@ 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));
@ -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');" "SELECT sqlcipher_export('encrypted');"
"DETACH DATABASE 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;
@ -409,8 +384,7 @@ bool RawDatabase::setPassword(const QString& password)
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;
} }
@ -427,14 +401,12 @@ bool RawDatabase::setPassword(const QString& password)
*/ */
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));
@ -463,16 +435,15 @@ 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;
} }
@ -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);
} }
}; };
@ -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,15 +548,13 @@ 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
@ -594,9 +565,10 @@ void RawDatabase::process()
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,19 +577,17 @@ 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) != SQLITE_OK) if (sqlite3_bind_blob(stmt, i + 1, blob.data(), blob.size(), SQLITE_STATIC)
{ != SQLITE_OK) {
qWarning() << "Failed to bind param" << curParam + i qWarning() << "Failed to bind param" << curParam + i << "to query"
<< "to query" << anonymizeQuery(query.query); << anonymizeQuery(query.query);
goto cleanupStatements; goto cleanupStatements;
} }
} }
@ -626,18 +596,15 @@ void RawDatabase::process()
} }
// 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;
} }
} }
@ -676,8 +642,7 @@ void RawDatabase::process()
// 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();
@ -714,22 +679,15 @@ QString RawDatabase::anonymizeQuery(const QByteArray& query)
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,13 +41,14 @@
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 peers (id INTEGER PRIMARY KEY, public_key TEXT NOT NULL "
"UNIQUE);"
"CREATE TABLE IF NOT EXISTS aliases (id INTEGER PRIMARY KEY, owner INTEGER," "CREATE TABLE IF NOT EXISTS aliases (id INTEGER PRIMARY KEY, owner INTEGER,"
"display_name BLOB NOT NULL, UNIQUE(owner, display_name));" "display_name BLOB NOT NULL, UNIQUE(owner, display_name));"
"CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, " "CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, "
@ -56,16 +57,15 @@ History::History(std::shared_ptr<RawDatabase> db)
"CREATE TABLE IF NOT EXISTS faux_offline_pending (id INTEGER PRIMARY KEY);"); "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,8 +88,7 @@ bool History::isValid()
*/ */
void History::eraseHistory() void History::eraseHistory()
{ {
if (!isValid()) if (!isValid()) {
{
return; return;
} }
@ -106,20 +105,17 @@ 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 "
@ -128,14 +124,12 @@ void History::removeFriendHistory(const QString& friendPk)
"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;").arg(id); "VACUUM;")
.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,76 +144,71 @@ 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, '"
+ friendPk + "');")
.arg(peerId)); .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, '"
+ sender + "');")
.arg(senderId)}; .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 +=
RawDatabase::Query(QString(
"INSERT INTO history (timestamp, chat_id, message, sender_alias) "
"VALUES (%1, %2, ?, (" "VALUES (%1, %2, ?, ("
" CASE WHEN changes() IS 0 THEN (" " CASE WHEN changes() IS 0 THEN ("
" SELECT id FROM aliases WHERE owner=%3 AND display_name=?)" " SELECT id FROM aliases WHERE owner=%3 AND display_name=?)"
" ELSE last_insert_rowid() END" " ELSE last_insert_rowid() END"
"));") "));")
.arg(time.toMSecsSinceEpoch()).arg(peerId).arg(senderId), .arg(time.toMSecsSinceEpoch())
.arg(peerId)
.arg(senderId),
{message.toUtf8(), dispName.toUtf8()}, insertIdCallback); {message.toUtf8(), dispName.toUtf8()}, insertIdCallback);
if (!isSent) if (!isSent) {
{ queries += RawDatabase::Query{"INSERT INTO faux_offline_pending (id) VALUES ("
queries += RawDatabase::Query{
"INSERT INTO faux_offline_pending (id) VALUES ("
" last_insert_rowid()" " last_insert_rowid()"
");"}; ");"};
} }
@ -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,19 +245,16 @@ 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(),
@ -283,8 +267,8 @@ QList<History::HistMessage> History::getChatHistory(const QString& friendPk,
}; };
// 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>
@ -37,10 +37,15 @@ 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;
@ -39,14 +39,16 @@ public:
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,14 +77,13 @@ 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;
} }
@ -95,58 +96,48 @@ Profile* Profile::loadProfile(QString name, const QString& password)
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,30 +160,26 @@ 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();
@ -239,8 +224,7 @@ QVector<QString> Profile::getFilesByExt(QString extension)
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);
} }
@ -308,47 +290,38 @@ QByteArray Profile::loadToxSave()
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";
} }
} }
@ -384,17 +357,14 @@ void Profile::saveToxSave(QByteArray data)
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";
} }
/** /**
@ -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()
@ -639,8 +602,7 @@ 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--;
} }
@ -683,20 +642,17 @@ QVector<QString> Profile::remove()
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
@ -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

@ -31,8 +31,7 @@ QString dataToString(QByteArray data)
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)
@ -66,8 +61,7 @@ int dataToVInt(const QByteArray& data)
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;
@ -81,8 +75,7 @@ size_t dataToVUint(const QByteArray& data)
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;
@ -108,8 +101,7 @@ 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;
@ -124,8 +116,7 @@ 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;

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,92 +46,80 @@ 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,

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
@ -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;
@ -101,8 +100,11 @@ QDataStream& readStream(QDataStream& dataStream, QByteArray& data)
} }
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}
{ {
} }
@ -111,12 +113,9 @@ 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);
} }
@ -130,19 +129,13 @@ void SettingsSerializer::endGroup()
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;
@ -153,20 +146,14 @@ 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();
@ -188,12 +175,9 @@ void SettingsSerializer::setArrayIndex(int 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());
@ -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,8 +480,7 @@ 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)
@ -550,8 +488,7 @@ void SettingsSerializer::readIni()
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;
@ -601,14 +535,12 @@ 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,10 +20,10 @@
#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
{ {
@ -60,9 +60,22 @@ private:
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;

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;
} }
} }
@ -136,31 +133,25 @@ QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& pa
{ {
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,8 +23,7 @@
#define PLATFORM_AUTORUN_H #define PLATFORM_AUTORUN_H
namespace Platform namespace Platform {
{
bool setAutorun(bool on); bool setAutorun(bool on);
bool getAutorun(); bool getAutorun();
} }

View File

@ -19,19 +19,22 @@
#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);

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,18 +31,23 @@
* 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 \"" + return toTString("\"" + QApplication::applicationFilePath().replace('/', '\\') + "\" -p \""
Settings::getInstance().getCurrentProfile() + "\""); + Settings::getInstance().getCurrentProfile() + "\"");
} }
inline tstring currentRegistryKeyName() inline tstring currentRegistryKeyName()
@ -55,19 +60,19 @@ 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,7 +83,8 @@ 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();
@ -88,7 +94,8 @@ bool Platform::getAutorun()
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);

View File

@ -19,13 +19,12 @@
#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(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
@ -37,14 +36,13 @@ namespace Platform
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() + "\"";
} }
} }
@ -52,10 +50,8 @@ 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();
} }

View File

@ -19,17 +19,16 @@
#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);

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,
@ -51,17 +51,17 @@ QVector<QPair<QString,QString>> DirectShow::getDeviceList()
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;
@ -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;
@ -188,8 +188,7 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
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;
@ -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;
@ -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)

View File

@ -21,17 +21,16 @@
#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);
} }

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,
@ -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{};
@ -129,15 +130,13 @@ 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) {
@ -154,9 +153,9 @@ QVector<VideoMode> v4l2::getDeviceModes(QString devName)
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));
@ -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,17 +19,16 @@
#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);
@ -37,4 +36,3 @@ namespace v4l2
} }
#endif // V4L2_H #endif // V4L2_H

View File

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

View File

@ -19,8 +19,8 @@
#include <QtCore/qsystemdetection.h> #include <QtCore/qsystemdetection.h>
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
#include <windows.h>
#include "src/platform/capslock.h" #include "src/platform/capslock.h"
#include <windows.h>
bool Platform::capsLockEnabled() bool Platform::capsLockEnabled()
{ {

View File

@ -19,8 +19,8 @@
#include <QtCore/qsystemdetection.h> #include <QtCore/qsystemdetection.h>
#if defined(Q_OS_UNIX) && !defined(__APPLE__) && !defined(__MACH__) #if defined(Q_OS_UNIX) && !defined(__APPLE__) && !defined(__MACH__)
#include <X11/XKBlib.h>
#include "src/platform/capslock.h" #include "src/platform/capslock.h"
#include <X11/XKBlib.h>
#undef KeyPress #undef KeyPress
#undef KeyRelease #undef KeyRelease
#undef FocusIn #undef FocusIn
@ -30,8 +30,7 @@ bool Platform::capsLockEnabled()
{ {
Display* d = XOpenDisplay((char*)0); Display* d = XOpenDisplay((char*)0);
bool caps_state = false; bool caps_state = false;
if (d) if (d) {
{
unsigned n; unsigned n;
XkbGetIndicatorState(d, XkbUseCoreKbd, &n); XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
caps_state = (n & 0x01) == 1; caps_state = (n & 0x01) == 1;

View File

@ -20,33 +20,32 @@
#include "install_osx.h" #include "install_osx.h"
#include <QApplication> #include <QApplication>
#include <QMessageBox>
#include <QDebug> #include <QDebug>
#include <QProcess>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QMessageBox>
#include <QProcess>
#include <QStandardPaths> #include <QStandardPaths>
#include <unistd.h> #include <unistd.h>
void osx::moveToAppFolder() void osx::moveToAppFolder()
{ {
if (qApp->applicationDirPath() != "/Applications/qtox.app/Contents/MacOS") if (qApp->applicationDirPath() != "/Applications/qtox.app/Contents/MacOS") {
{
qDebug() << "OS X: Not in Applications folder"; qDebug() << "OS X: Not in Applications folder";
QMessageBox AskInstall; QMessageBox AskInstall;
AskInstall.setIcon(QMessageBox::Question); AskInstall.setIcon(QMessageBox::Question);
AskInstall.setWindowModality(Qt::ApplicationModal); AskInstall.setWindowModality(Qt::ApplicationModal);
AskInstall.setText("Move to Applications folder?"); AskInstall.setText("Move to Applications folder?");
AskInstall.setInformativeText("I can move myself to the Applications folder, keeping your downloads folder less cluttered.\r\n"); AskInstall.setInformativeText("I can move myself to the Applications folder, keeping your "
"downloads folder less cluttered.\r\n");
AskInstall.setStandardButtons(QMessageBox::Yes | QMessageBox::No); AskInstall.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
AskInstall.setDefaultButton(QMessageBox::Yes); AskInstall.setDefaultButton(QMessageBox::Yes);
int AskInstallAttempt = AskInstall.exec(); // Actually ask the user int AskInstallAttempt = AskInstall.exec(); // Actually ask the user
if (AskInstallAttempt == QMessageBox::Yes) if (AskInstallAttempt == QMessageBox::Yes) {
{
QProcess* sudoprocess = new QProcess; QProcess* sudoprocess = new QProcess;
QProcess* qtoxprocess = new QProcess; QProcess* qtoxprocess = new QProcess;
@ -84,34 +83,39 @@ void osx::moveToAppFolder()
} }
} }
} }
// migrateProfiles() is compatabilty code that can be removed down the line when the time seems right. // migrateProfiles() is compatabilty code that can be removed down the line when the time seems
// right.
void osx::migrateProfiles() void osx::migrateProfiles()
{ {
QString oldPath = QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QDir::separator() + QString oldPath = QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
"Library" + QDir::separator() + "Preferences" + QDir::separator() + "tox"); + QDir::separator() + "Library" + QDir::separator()
+ "Preferences" + QDir::separator() + "tox");
QFileInfo checkDir(oldPath); QFileInfo checkDir(oldPath);
QString newPath = QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QDir::separator() QString newPath = QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
+ "Library" + QDir::separator() + "Application Support" + QDir::separator() + "Tox"); + QDir::separator() + "Library" + QDir::separator()
+ "Application Support" + QDir::separator() + "Tox");
QDir dir; QDir dir;
if (!checkDir.exists() || !checkDir.isDir()) if (!checkDir.exists() || !checkDir.isDir()) {
{
qDebug() << "OS X: Old settings directory not detected"; qDebug() << "OS X: Old settings directory not detected";
return; return;
} }
qDebug() << "OS X: Old settings directory detected migrating to default"; qDebug() << "OS X: Old settings directory detected migrating to default";
if (!dir.rename(oldPath, newPath)) if (!dir.rename(oldPath, newPath)) {
{ qDebug() << "OS X: Profile migration failed. ~/Library/Application Support/Tox already "
qDebug() << "OS X: Profile migration failed. ~/Library/Application Support/Tox already exists. Using alternate migration method."; "exists. Using alternate migration method.";
QString OSXMigrater = "../Resources/OSX-Migrater.sh"; QString OSXMigrater = "../Resources/OSX-Migrater.sh";
QProcess::execute(OSXMigrater); QProcess::execute(OSXMigrater);
QMessageBox MigrateProfile; QMessageBox MigrateProfile;
MigrateProfile.setIcon(QMessageBox::Information); MigrateProfile.setIcon(QMessageBox::Information);
MigrateProfile.setWindowModality(Qt::ApplicationModal); MigrateProfile.setWindowModality(Qt::ApplicationModal);
MigrateProfile.setText("Alternate profile migration method used."); MigrateProfile.setText("Alternate profile migration method used.");
MigrateProfile.setInformativeText("It has been detected that your profiles \nwhere migrated to the new settings directory; \nusing the alternate migration method. \n\nA backup can be found in your: \n/Users/[USER]/.Tox-Backup[DATE-TIME] \n\nJust in case. \r\n"); MigrateProfile.setInformativeText(
"It has been detected that your profiles \nwhere migrated to the new settings "
"directory; \nusing the alternate migration method. \n\nA backup can be found in your: "
"\n/Users/[USER]/.Tox-Backup[DATE-TIME] \n\nJust in case. \r\n");
MigrateProfile.exec(); MigrateProfile.exec();
} }
} }

View File

@ -25,9 +25,9 @@
#error "This file is only meant to be compiled for Mac OSX targets" #error "This file is only meant to be compiled for Mac OSX targets"
#endif #endif
namespace osx namespace osx {
{ static constexpr int EXIT_UPDATE_MACX =
static constexpr int EXIT_UPDATE_MACX = 218; // We track our state using unique exit codes when debugging 218; // We track our state using unique exit codes when debugging
static constexpr int EXIT_UPDATE_MACX_FAIL = 216; static constexpr int EXIT_UPDATE_MACX_FAIL = 216;
void moveToAppFolder(); void moveToAppFolder();

View File

@ -28,14 +28,10 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/* BOOLEAN:INT,INT (closures.def:1) */ /* BOOLEAN:INT,INT (closures.def:1) */
extern void g_cclosure_user_marshal_BOOLEAN__INT_INT (GClosure *closure, extern void g_cclosure_user_marshal_BOOLEAN__INT_INT(GClosure* closure, GValue* return_value,
GValue *return_value, guint n_param_values, const GValue* param_values,
guint n_param_values, gpointer invocation_hint, gpointer marshal_data);
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
G_END_DECLS G_END_DECLS
#endif /* __g_cclosure_user_marshal_MARSHAL_H__ */ #endif /* __g_cclosure_user_marshal_MARSHAL_H__ */

View File

@ -25,7 +25,6 @@
#include "statusnotifier.h" #include "statusnotifier.h"
GType status_notifier_error_get_type(void); GType status_notifier_error_get_type(void);
#define TYPE_STATUS_NOTIFIER_ERROR (status_notifier_error_get_type()) #define TYPE_STATUS_NOTIFIER_ERROR (status_notifier_error_get_type())
GType status_notifier_state_get_type(void); GType status_notifier_state_get_type(void);
@ -41,6 +40,3 @@ GType status_notifier_scroll_orientation_get_type (void);
G_END_DECLS G_END_DECLS
#endif /* __STATUS_NOTIFIER_ENUMS_H__ */ #endif /* __STATUS_NOTIFIER_ENUMS_H__ */

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