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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,12 +20,12 @@
#include "chatmessage.h"
#include "chatlinecontentproxy.h"
#include "textformatter.h"
#include "content/text.h"
#include "content/timestamp.h"
#include "content/spinner.h"
#include "content/filetransferwidget.h"
#include "content/image.h"
#include "content/notificationicon.h"
#include "content/spinner.h"
#include "content/text.h"
#include "content/timestamp.h"
#include <QDebug>
@ -37,36 +37,35 @@
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);
QString text = rawMessage.toHtmlEscaped();
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())
text = SmileyPack::getInstance().smileyfied(text);
//quotes (green text)
// quotes (green text)
text = detectQuotes(detectAnchors(text), type);
//text styling
// text styling
Settings::StyleType styleType = Settings::getInstance().getStylePreference();
if (styleType != Settings::StyleType::NONE)
{
if (styleType != Settings::StyleType::NONE) {
TextFormatter tf = TextFormatter(text);
text = tf.applyStyling(styleType == Settings::StyleType::WITH_CHARS);
}
switch(type)
{
switch (type) {
case NORMAL:
text = wrapDiv(text, "msg");
break;
@ -86,9 +85,15 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
if (isMe)
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(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));
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(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())
msg->markAsSent(date);
@ -96,29 +101,39 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
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);
QString text = rawMessage.toHtmlEscaped();
QString img;
switch(type)
{
case INFO: img = ":/ui/chatArea/info.svg"; break;
case ERROR: img = ":/ui/chatArea/error.svg"; break;
case TYPING: img = ":/ui/chatArea/typing.svg"; break;
switch (type) {
case INFO:
img = ":/ui/chatArea/info.svg";
break;
case ERROR:
img = ":/ui/chatArea/error.svg";
break;
case TYPING:
img = ":/ui/chatArea/typing.svg";
break;
}
QFont baseFont = Settings::getInstance().getChatMessageFont();
msg->addColumn(new Image(QSize(18, 18), img), ColumnFormat(NAME_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));
msg->addColumn(new Image(QSize(18, 18), img),
ColumnFormat(NAME_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;
}
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);
@ -127,9 +142,12 @@ ChatMessage::Ptr ChatMessage::createFileTransferMessage(const QString& sender, T
if (isMe)
authorFont.setBold(true);
msg->addColumn(new Text(sender, authorFont, true), ColumnFormat(NAME_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));
msg->addColumn(new Text(sender, authorFont, true),
ColumnFormat(NAME_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;
}
@ -140,14 +158,17 @@ ChatMessage::Ptr ChatMessage::createTypingNotification()
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
// user received typing notifications constantly since contact came online.
// This causes "[user]..." to be displayed in place of user nick, as long
// 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 Text("[user]...", baseFont, false, ""), ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left));
msg->addColumn(new NotificationIcon(QSize(18, 18)),
ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text("[user]...", baseFont, false, ""),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left));
return msg;
}
@ -159,12 +180,13 @@ ChatMessage::Ptr ChatMessage::createBusyNotification()
baseFont.setPixelSize(baseFont.pixelSize() + 2);
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;
}
void ChatMessage::markAsSent(const QDateTime &time)
void ChatMessage::markAsSent(const QDateTime& time)
{
QFont baseFont = Settings::getInstance().getChatMessageFont();
@ -205,30 +227,29 @@ void ChatMessage::hideDate()
c->hide();
}
QString ChatMessage::detectAnchors(const QString &str)
QString ChatMessage::detectAnchors(const QString& str)
{
QString out = str;
// detect URIs
QRegExp exp("("
"(?:\\b)((www\\.)|(http[s]?|ftp)://)" // (protocol)://(printable - non-special character)
// http://ONEORMOREALHPA-DIGIT
"\\w+\\S+)" // any other character, lets domains and other
// ↓ link to a file, or samba share
// https://en.wikipedia.org/wiki/File_URI_scheme
"|(?:\\b)((file|smb)://)([\\S| ]*)"
"|(?:\\b)(tox:[a-zA-Z\\d]{76})" //link with full user address
"|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)" //@mail link
"|(?:\\b)(tox:\\S+@\\S+)"); // starts with `tox` then : and only alpha-digits till the end
// also accepts tox:agilob@net as simplified TOX ID
QRegExp exp(
"("
"(?:\\b)((www\\.)|(http[s]?|ftp)://)" // (protocol)://(printable - non-special character)
// http://ONEORMOREALHPA-DIGIT
"\\w+\\S+)" // any other character, lets domains and other
// ↓ link to a file, or samba share
// https://en.wikipedia.org/wiki/File_URI_scheme
"|(?:\\b)((file|smb)://)([\\S| ]*)"
"|(?:\\b)(tox:[a-zA-Z\\d]{76})" // link with full user address
"|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)" //@mail link
"|(?:\\b)(tox:\\S+@\\S+)"); // starts with `tox` then : and only alpha-digits till the end
// also accepts tox:agilob@net as simplified TOX ID
int offset = 0;
while ((offset = exp.indexIn(out, offset)) != -1)
{
while ((offset = exp.indexIn(out, offset)) != -1) {
QString url = exp.cap();
// 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();
continue;
}
@ -250,25 +271,20 @@ QString ChatMessage::detectQuotes(const QString& str, MessageType type)
// detect text quotes
QStringList messageLines = str.split("\n");
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
// quotes and action messages possible, since only first line can cause
// 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)
quotedText += "<span class=quote>" + messageLines[i] + "</span>";
else
quotedText += messageLines[i];
}
else
{
} else {
quotedText += messageLines[i];
}
if (i < messageLines.size() - 1)
{
if (i < messageLines.size() - 1) {
quotedText += '\n';
}
}
@ -276,7 +292,7 @@ QString ChatMessage::detectQuotes(const QString& str, MessageType type)
return quotedText;
}
QString ChatMessage::wrapDiv(const QString &str, const QString &div)
QString ChatMessage::wrapDiv(const QString& str, const QString& div)
{
return QString("<p class=%1>%2</p>").arg(div, /*QChar(0x200E) + */QString(str));
return QString("<p class=%1>%2</p>").arg(div, /*QChar(0x200E) + */ QString(str));
}

View File

@ -47,9 +47,13 @@ public:
ChatMessage();
static ChatMessage::Ptr createChatMessage(const QString& sender, const QString& rawMessage, MessageType type, bool isMe, 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 createChatMessage(const QString& sender, const QString& rawMessage,
MessageType type, bool isMe,
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 createBusyNotification();

View File

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

View File

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

View File

@ -27,17 +27,17 @@
class Image : public ChatLineContent
{
public:
Image(QSize size, const QString &filename);
Image(QSize size, const QString& filename);
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 qreal getAscent() const override;
private:
QSize size;
QPixmap pmap;
};
#endif // IMAGE_H

View File

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

View File

@ -34,7 +34,8 @@ public:
explicit NotificationIcon(QSize size);
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 qreal getAscent() const override;
@ -49,7 +50,6 @@ private:
qreal dotWidth = 0.2;
qreal alpha = 0.0;
};
#endif // NOTIFICATIONICON_H

View File

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

View File

@ -22,9 +22,9 @@
#include "../chatlinecontent.h"
#include <QTimer>
#include <QObject>
#include <QPixmap>
#include <QTimer>
class QVariantAnimation;
@ -35,7 +35,8 @@ public:
Spinner(const QString& img, QSize size, qreal speed);
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 visibilityChanged(bool visible) override;
virtual qreal getAscent() const override;
@ -50,7 +51,6 @@ private:
QTimer timer;
qreal alpha = 0.0;
QVariantAnimation* blendAnimation;
};
#endif // SPINNER_H

View File

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

View File

@ -31,7 +31,8 @@ class Text : public ChatLineContent
Q_OBJECT
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();
void setText(const QString& txt);
@ -53,8 +54,8 @@ public:
virtual void visibilityChanged(bool keepInMemory) final;
virtual qreal getAscent() const final;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) final override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) final override;
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event) final override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) final override;
void hoverMoveEvent(QGraphicsSceneHoverEvent* event) final override;
virtual QString getText() const final;
@ -90,7 +91,6 @@ private:
QFont defFont;
QString defStyleSheet;
QColor color;
};
#endif // TEXT_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -21,10 +21,10 @@
#ifndef COREAV_H
#define COREAV_H
#include <QObject>
#include <memory>
#include <atomic>
#include "src/core/toxcall.h"
#include <QObject>
#include <atomic>
#include <memory>
#include <tox/toxav.h>
class Friend;
@ -54,9 +54,11 @@ public:
bool isCallActive(const Friend* f) const;
bool isCallActive(const Group* g) 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);
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);
void invalidateCallSources();
@ -75,14 +77,12 @@ public:
void toggleMuteCallInput(const Friend* f);
void toggleMuteCallOutput(const Friend* f);
static void groupCallCallback(void* tox, int group, int peer,
const int16_t* data, unsigned samples,
uint8_t channels, unsigned sample_rate,
void* core);
static void groupCallCallback(void* tox, int group, int peer, const int16_t* data, unsigned samples,
uint8_t channels, unsigned sample_rate, void* core);
static void invalidateGroupCallPeerSource(int group, int peer);
public slots:
bool startCall(uint32_t friendNum, bool video=false);
bool startCall(uint32_t friendNum, bool video = false);
bool answerCall(uint32_t friendNum);
bool cancelCall(uint32_t friendNum);
void timeoutCall(uint32_t friendNum);
@ -95,17 +95,19 @@ signals:
void avEnd(uint32_t friendId);
private slots:
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 bitrateCallback(ToxAV *toxAV, uint32_t friendNum, uint32_t arate, uint32_t vrate, 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 bitrateCallback(ToxAV* toxAV, uint32_t friendNum, uint32_t arate, uint32_t vrate,
void* self);
void killTimerFromThread();
private:
void process();
static void audioFrameCallback(ToxAV *toxAV, uint32_t friendNum, const int16_t *pcm, 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,
const uint8_t *y, const uint8_t *u, const uint8_t *v,
static void audioFrameCallback(ToxAV* toxAV, uint32_t friendNum, const int16_t* pcm,
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,
const uint8_t* y, const uint8_t* u, const uint8_t* v,
int32_t ystride, int32_t ustride, int32_t vstride, void* self);
private:

View File

@ -22,12 +22,12 @@
#include "corefile.h"
#include "corestructs.h"
#include "src/core/cstring.h"
#include "src/persistence/settings.h"
#include "src/persistence/profile.h"
#include "src/persistence/settings.h"
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QThread>
#include <QDir>
#include <memory>
/**
@ -55,13 +55,10 @@ unsigned CoreFile::corefileIterationInterval()
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.
*/
constexpr unsigned fileInterval = 10,
idleInterval = 1000;
constexpr unsigned fileInterval = 10, idleInterval = 1000;
for (ToxFile& file : fileMap)
{
if (file.status == ToxFile::TRANSMITTING)
{
for (ToxFile& file : fileMap) {
if (file.status == ToxFile::TRANSMITTING) {
return fileInterval;
}
}
@ -72,10 +69,8 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
{
QMutexLocker mlocker(&fileSendMutex);
if (data.isEmpty())
{
tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, 0,
nullptr, nullptr, 0, nullptr);
if (data.isEmpty()) {
tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, 0, nullptr, nullptr, 0, nullptr);
return;
}
@ -86,10 +81,9 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
TOX_ERR_FILE_SEND error;
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:
break;
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);
}
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);
QByteArray fileName = filename.toUtf8();
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_DATA, filesize, nullptr,
(uint8_t*)fileName.data(), fileName.size(), nullptr);
if (fileNum == std::numeric_limits<uint32_t>::max())
{
(uint8_t*)fileName.data(), fileName.size(), nullptr);
if (fileNum == std::numeric_limits<uint32_t>::max()) {
qWarning() << "sendFile: Can't create the Tox file sender";
emit core->fileSendFailed(friendId, filename);
return;
@ -140,8 +134,7 @@ void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString
file.filesize = filesize;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
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());
}
@ -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)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("pauseResumeFileSend: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
if (file->status == ToxFile::TRANSMITTING) {
file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file);
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;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
else
{
} else {
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)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("cancelFileRecv: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
if (file->status == ToxFile::TRANSMITTING) {
file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file);
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;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
else
{
} else {
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)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("cancelFileSend: No such file in queue");
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)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("cancelFileRecv: No such file in queue");
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)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("rejectFileRecvRequest: No such file in queue");
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)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("acceptFileRecvRequest: No such file in queue");
return;
}
file->setFilePath(path);
if (!file->open(true))
{
if (!file->open(true)) {
qWarning() << "acceptFileRecvRequest: Unable to open file";
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)
{
uint64_t key = getFriendKey(friendId, fileId);
if (fileMap.contains(key))
{
if (fileMap.contains(key)) {
return &fileMap[key];
}
qWarning() << "findFile: File transfer with ID" << friendId << ':'
<< fileId << "doesn't exist";
qWarning() << "findFile: File transfer with ID" << friendId << ':' << fileId << "doesn't exist";
return nullptr;
}
@ -281,10 +255,9 @@ void CoreFile::addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file)
{
uint64_t key = getFriendKey(friendId, fileId);
if (fileMap.contains(key))
{
qWarning() << "addFile: Overwriting existing file transfer with same ID"
<< friendId << ':' << fileId;
if (fileMap.contains(key)) {
qWarning() << "addFile: Overwriting existing file transfer with same ID" << friendId << ':'
<< fileId;
}
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)
{
uint64_t key = getFriendKey(friendId, fileId);
if (!fileMap.contains(key))
{
if (!fileMap.contains(key)) {
qWarning() << "removeFile: No such file in queue";
return;
}
@ -302,52 +274,49 @@ void CoreFile::removeFile(uint32_t friendId, uint32_t fileId)
fileMap.remove(key);
}
void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId,
uint32_t kind, uint64_t filesize,
const uint8_t* fname, size_t fnameLen,
void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, uint32_t kind,
uint64_t filesize, const uint8_t* fname, size_t fnameLen,
void* vCore)
{
Core* core = static_cast<Core*>(vCore);
if (kind == TOX_FILE_KIND_AVATAR)
{
if (kind == TOX_FILE_KIND_AVATAR) {
// TODO: port this to ToxPk
QString friendAddr = core->getFriendPublicKey(friendId).toString();
if (!filesize)
{
if (!filesize) {
qDebug() << QString("Received empty avatar request %1:%2").arg(friendId).arg(fileId);
// Avatars of size 0 means explicitely no avatar
emit core->friendAvatarRemoved(friendId);
core->profile.removeAvatar(friendAddr);
return;
}
else
{
static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH, "TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!");
} else {
static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH,
"TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!");
uint8_t avatarHash[TOX_FILE_ID_LENGTH];
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
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);
return;
}
else
{
} else {
// 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);
}
}
}
else
{
qDebug() << QString("Received file request %1:%2 kind %3")
.arg(friendId).arg(fileId).arg(kind);
} else {
qDebug() << QString("Received file request %1:%2 kind %3").arg(friendId).arg(fileId).arg(kind);
}
ToxFile file{fileId, friendId, QByteArray((char*)fname,fnameLen), "", ToxFile::RECEIVING};
ToxFile file{fileId, friendId, QByteArray((char*)fname, fnameLen), "", ToxFile::RECEIVING};
file.filesize = filesize;
file.fileKind = kind;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
@ -357,59 +326,48 @@ void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId,
emit core->fileReceiveRequested(file);
}
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);
if (!file)
{
if (!file) {
qWarning("onFileControlCallback: No such file in queue");
return;
}
if (control == TOX_FILE_CONTROL_CANCEL)
{
if (control == TOX_FILE_CONTROL_CANCEL) {
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);
removeFile(friendId, fileId);
}
else if (control == TOX_FILE_CONTROL_PAUSE)
{
qDebug() << "onFileControlCallback: Received pause for file "<<friendId<<":"<<fileId;
} else if (control == TOX_FILE_CONTROL_PAUSE) {
qDebug() << "onFileControlCallback: Received pause for file " << friendId << ":" << fileId;
file->status = ToxFile::PAUSED;
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)
qDebug() << "Avatar transfer"<<fileId<<"to friend"<<friendId<<"accepted";
qDebug() << "Avatar transfer" << fileId << "to friend" << friendId << "accepted";
else
qDebug() << "onFileControlCallback: Received resume for file "<<friendId<<":"<<fileId;
qDebug() << "onFileControlCallback: Received resume for file " << friendId << ":" << fileId;
file->status = ToxFile::TRANSMITTING;
emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, false);
}
else
{
qWarning() << "Unhandled file control "<<control<<" for file "<<friendId<<':'<<fileId;
} else {
qWarning() << "Unhandled file control " << control << " for file " << friendId << ':' << fileId;
}
}
void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
uint64_t pos, size_t length, void* core)
void CoreFile::onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t pos,
size_t length, void* core)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("onFileDataCallback: No such file in queue");
return;
}
// If we reached EOF, ack and cleanup the transfer
if (!length)
{
if (file->fileKind != TOX_FILE_KIND_AVATAR)
{
if (!length) {
if (file->fileKind != TOX_FILE_KIND_AVATAR) {
emit static_cast<Core*>(core)->fileTransferFinished(*file);
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]);
int64_t nread;
if (file->fileKind == TOX_FILE_KIND_AVATAR)
{
if (file->fileKind == TOX_FILE_KIND_AVATAR) {
QByteArray chunk = file->avatarData.mid(pos, length);
nread = chunk.size();
memcpy(data.get(), chunk.data(), nread);
}
else
{
} else {
file->file->seek(pos);
nread = file->file->read((char*)data.get(), length);
if (nread <= 0)
{
if (nread <= 0) {
qWarning("onFileDataCallback: Failed to read from file");
emit static_cast<Core*>(core)->fileTransferCancelled(*file);
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;
}
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");
return;
}
@ -450,22 +403,18 @@ void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
emit static_cast<Core*>(core)->fileTransferInfo(*file);
}
void CoreFile::onFileRecvChunkCallback(Tox *tox, uint32_t friendId,
uint32_t fileId, uint64_t position,
const uint8_t* data, size_t length,
void* vCore)
void CoreFile::onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t position,
const uint8_t* data, size_t length, void* vCore)
{
Core* core = static_cast<Core*>(vCore);
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
if (!file) {
qWarning("onFileRecvChunkCallback: No such file in queue");
tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return;
}
if (file->bytesSent != position)
{
if (file->bytesSent != position) {
qWarning("onFileRecvChunkCallback: Received a chunk out-of-order, aborting transfer");
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit core->fileTransferCancelled(*file);
@ -474,21 +423,17 @@ void CoreFile::onFileRecvChunkCallback(Tox *tox, uint32_t friendId,
return;
}
if (!length)
{
if (file->fileKind == TOX_FILE_KIND_AVATAR)
{
if (!length) {
if (file->fileKind == TOX_FILE_KIND_AVATAR) {
QPixmap pic;
pic.loadFromData(file->avatarData);
if (!pic.isNull())
{
qDebug() << "Got"<<file->avatarData.size()<<"bytes of avatar data from" <<friendId;
core->profile.saveAvatar(file->avatarData, core->getFriendPublicKey(friendId).toString());
if (!pic.isNull()) {
qDebug() << "Got" << file->avatarData.size() << "bytes of avatar data from" << friendId;
core->profile.saveAvatar(file->avatarData,
core->getFriendPublicKey(friendId).toString());
emit core->friendAvatarChanged(friendId, pic);
}
}
else
{
} else {
emit core->fileTransferFinished(*file);
emit core->fileDownloadFinished(file->filePath);
}
@ -499,7 +444,7 @@ void CoreFile::onFileRecvChunkCallback(Tox *tox, uint32_t friendId,
if (file->fileKind == TOX_FILE_KIND_AVATAR)
file->avatarData.append((char*)data, length);
else
file->file->write((char*)data,length);
file->file->write((char*)data, length);
file->bytesSent += length;
if (file->fileKind != TOX_FILE_KIND_AVATAR)
@ -513,11 +458,11 @@ void CoreFile::onConnectionStatusChanged(Core* core, uint32_t friendId, bool onl
// - Start a new file transfer with the same 32byte file ID with toxcore
// - Seek to the correct position again
// - 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;
for (uint64_t key : fileMap.keys())
{
if (key>>32 != friendId)
for (uint64_t key : fileMap.keys()) {
if (key >> 32 != friendId)
continue;
fileMap[key].status = status;
emit core->fileTransferBrokenUnbroken(fileMap[key], !online);

View File

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

View File

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

View File

@ -7,7 +7,13 @@
class QFile;
class QTimer;
enum class Status : int {Online = 0, Away, Busy, Offline};
enum class Status : int
{
Online = 0,
Away,
Busy,
Offline
};
struct DhtServer
{
@ -37,8 +43,11 @@ struct ToxFile
};
ToxFile() = default;
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,
FileDirection Direction);
~ToxFile()
{
}
bool operator==(const ToxFile& other) const;
bool operator!=(const ToxFile& other) const;

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
#include "src/audio/audio.h"
#include "src/core/toxcall.h"
#include "src/audio/audio.h"
#include "src/core/coreav.h"
#include "src/persistence/settings.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!
*
* @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
* @brief True if our user asked for a video call, sending and recieving.
@ -30,17 +31,23 @@
using namespace std;
ToxCall::ToxCall(uint32_t CallId)
: callId{CallId}, alSource{0},
inactive{true}, muteMic{false}, muteVol{false}
: callId{CallId}
, alSource{0}
, inactive{true}
, muteMic{false}
, muteVol{false}
{
Audio& audio = Audio::getInstance();
audio.subscribeInput();
audio.subscribeOutput(alSource);
}
ToxCall::ToxCall(ToxCall&& other) noexcept
: audioInConn{other.audioInConn}, callId{other.callId}, alSource{other.alSource},
inactive{other.inactive}, muteMic{other.muteMic}, muteVol{other.muteVol}
ToxCall::ToxCall(ToxCall&& other) noexcept : audioInConn{other.audioInConn},
callId{other.callId},
alSource{other.alSource},
inactive{other.inactive},
muteMic{other.muteMic},
muteVol{other.muteVol}
{
other.audioInConn = QMetaObject::Connection();
other.callId = numeric_limits<decltype(callId)>::max();
@ -81,15 +88,13 @@ ToxCall& ToxCall::operator=(ToxCall&& other) noexcept
void ToxFriendCall::startTimeout()
{
if (!timeoutTimer)
{
if (!timeoutTimer) {
timeoutTimer = new QTimer();
// We might move, so we need copies of members. CoreAV won't move while we're alive
CoreAV* avCopy = av;
auto callIdCopy = callId;
QObject::connect(timeoutTimer, &QTimer::timeout, [avCopy, callIdCopy](){
avCopy->timeoutCall(callIdCopy);
});
QObject::connect(timeoutTimer, &QTimer::timeout,
[avCopy, callIdCopy]() { avCopy->timeoutCall(callIdCopy); });
}
if (!timeoutTimer->isActive())
@ -107,19 +112,21 @@ void ToxFriendCall::stopTimeout()
}
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
: ToxCall(FriendNum),
videoEnabled{VideoEnabled}, nullVideoBitrate{false}, videoSource{nullptr},
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)},
av{&av}, timeoutTimer{nullptr}
: ToxCall(FriendNum)
, videoEnabled{VideoEnabled}
, nullVideoBitrate{false}
, videoSource{nullptr}
, state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)}
, av{&av}
, timeoutTimer{nullptr}
{
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
[&av,FriendNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
{
av.sendCallAudio(FriendNum, pcm, samples, chans, rate);
});
[&av, FriendNum](const int16_t* pcm, size_t samples,
uint8_t chans, uint32_t rate) {
av.sendCallAudio(FriendNum, pcm, samples, chans, rate);
});
if (videoEnabled)
{
if (videoEnabled) {
videoSource = new CoreVideoSource;
CameraSource& source = CameraSource::getInstance();
@ -127,15 +134,20 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
source.open();
source.subscribe();
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
: ToxCall(move(other)),
videoEnabled{other.videoEnabled}, nullVideoBitrate{other.nullVideoBitrate},
videoSource{other.videoSource}, state{other.state},
av{other.av}, timeoutTimer{other.timeoutTimer}
videoEnabled{other.videoEnabled},
nullVideoBitrate{other.nullVideoBitrate},
videoSource{other.videoSource},
state{other.state},
av{other.av},
timeoutTimer{other.timeoutTimer}
{
other.videoEnabled = false;
other.videoSource = nullptr;
@ -147,14 +159,14 @@ ToxFriendCall::~ToxFriendCall()
if (timeoutTimer)
delete timeoutTimer;
if (videoEnabled)
{
if (videoEnabled) {
// 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,
// so we unsuscribe asynchronously, it's fine if the webcam takes a couple milliseconds more to poweroff.
QtConcurrent::run([](){CameraSource::getInstance().unsubscribe();});
if (videoSource)
{
// If the CameraSource thread calls toxav *_send_frame, we might deadlock the toxav and
// CameraSource locks,
// so we unsuscribe asynchronously, it's fine if the webcam takes a couple milliseconds more
// to poweroff.
QtConcurrent::run([]() { CameraSource::getInstance().unsubscribe(); });
if (videoSource) {
videoSource->setDeleteOnClose(true);
videoSource = nullptr;
}
@ -163,7 +175,7 @@ ToxFriendCall::~ToxFriendCall()
ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other) noexcept
{
ToxCall::operator =(move(other));
ToxCall::operator=(move(other));
videoEnabled = other.videoEnabled;
other.videoEnabled = false;
videoSource = other.videoSource;
@ -177,21 +189,21 @@ ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other) noexcept
return *this;
}
ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV &av)
ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV& av)
: ToxCall(static_cast<decltype(callId)>(GroupNum))
{
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");
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");
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
[&av,GroupNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
{
av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate);
});
[&av, GroupNum](const int16_t* pcm, size_t samples,
uint8_t chans, uint32_t rate) {
av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate);
});
}
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept
: ToxCall(move(other))
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept : ToxCall(move(other))
{
}
@ -199,15 +211,14 @@ ToxGroupCall::~ToxGroupCall()
{
Audio& audio = Audio::getInstance();
for(quint32 v : peers)
{
for (quint32 v : peers) {
audio.unsubscribeOutput(v);
}
}
ToxGroupCall &ToxGroupCall::operator=(ToxGroupCall &&other) noexcept
ToxGroupCall& ToxGroupCall::operator=(ToxGroupCall&& other) noexcept
{
ToxCall::operator =(move(other));
ToxCall::operator=(move(other));
return *this;
}

View File

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

View File

@ -20,10 +20,10 @@
#include "toxencrypt.h"
#include <tox/toxencryptsave.h>
#include <memory>
#include <QByteArray>
#include <QDebug>
#include <QString>
#include <memory>
// functions for nice debug output
static QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error);
@ -51,9 +51,10 @@ ToxEncrypt::~ToxEncrypt()
* @brief Constructs a ToxEncrypt object from a Tox_Pass_Key.
* @param key Derived key to use for encryption and decryption.
*/
ToxEncrypt::ToxEncrypt(Tox_Pass_Key* key) :
passKey{key}
{}
ToxEncrypt::ToxEncrypt(Tox_Pass_Key* key)
: passKey{key}
{
}
/**
* @brief Gets the minimum number of bytes needed for isEncrypted()
@ -72,8 +73,7 @@ int ToxEncrypt::getMinBytes()
*/
bool ToxEncrypt::isEncrypted(const QByteArray& ciphertext)
{
if (ciphertext.length() < TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
{
if (ciphertext.length() < TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
return false;
}
@ -89,8 +89,7 @@ bool ToxEncrypt::isEncrypted(const QByteArray& ciphertext)
*/
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.";
}
@ -103,8 +102,7 @@ QByteArray ToxEncrypt::encryptPass(const QString& password, const QByteArray& pl
static_cast<size_t>(pass.size()),
reinterpret_cast<uint8_t*>(ciphertext.data()), &error);
if (error != TOX_ERR_ENCRYPTION_OK)
{
if (error != TOX_ERR_ENCRYPTION_OK) {
qCritical() << getEncryptionError(error);
return QByteArray{};
}
@ -121,14 +119,12 @@ QByteArray ToxEncrypt::encryptPass(const QString& password, const QByteArray& pl
*/
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.";
return QByteArray{};
}
if (password.length() == 0)
{
if (password.length() == 0) {
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()),
static_cast<size_t>(ciphertext.size()),
reinterpret_cast<const uint8_t*>(pass.constData()),
static_cast<size_t>(pass.size()),
reinterpret_cast<uint8_t*>(plaintext.data()), &error);
static_cast<size_t>(pass.size()), reinterpret_cast<uint8_t*>(plaintext.data()),
&error);
if (error != TOX_ERR_DECRYPTION_OK)
{
if (error != TOX_ERR_DECRYPTION_OK) {
qWarning() << getDecryptionError(error);
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()),
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);
qCritical() << getKeyDerivationError(error);
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)
{
if (!isEncrypted(toxSave))
{
if (!isEncrypted(toxSave)) {
qWarning() << "The data was not encrypted using this module or it's corrupted.";
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];
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);
return std::unique_ptr<ToxEncrypt>{};
}
@ -207,10 +199,9 @@ std::unique_ptr<ToxEncrypt> ToxEncrypt::makeToxEncrypt(const QString& password,
QByteArray pass = password.toUtf8();
TOX_ERR_KEY_DERIVATION keyError;
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);
qWarning() << getKeyDerivationError(keyError);
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
{
if (!passKey)
{
if (!passKey) {
qCritical() << "The passKey is invalid.";
return QByteArray{};
}
QByteArray ciphertext(plaintext.length() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0x00);
TOX_ERR_ENCRYPTION error;
tox_pass_key_encrypt(passKey,
reinterpret_cast<const uint8_t*>(plaintext.constData()),
tox_pass_key_encrypt(passKey, reinterpret_cast<const uint8_t*>(plaintext.constData()),
static_cast<size_t>(plaintext.size()),
reinterpret_cast<uint8_t*>(ciphertext.data()), &error);
if (error != TOX_ERR_ENCRYPTION_OK)
{
if (error != TOX_ERR_ENCRYPTION_OK) {
qCritical() << getEncryptionError(error);
return QByteArray{};
}
@ -256,21 +244,18 @@ QByteArray ToxEncrypt::encrypt(const QByteArray& plaintext) 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.";
return QByteArray{};
}
QByteArray plaintext(ciphertext.length() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0x00);
TOX_ERR_DECRYPTION error;
tox_pass_key_decrypt(passKey,
reinterpret_cast<const uint8_t*>(ciphertext.constData()),
tox_pass_key_decrypt(passKey, reinterpret_cast<const uint8_t*>(ciphertext.constData()),
static_cast<size_t>(ciphertext.size()),
reinterpret_cast<uint8_t*>(plaintext.data()), &error);
if (error != TOX_ERR_DECRYPTION_OK)
{
if (error != TOX_ERR_DECRYPTION_OK) {
qWarning() << getDecryptionError(error);
return QByteArray{};
}
@ -285,14 +270,15 @@ QByteArray ToxEncrypt::decrypt(const QByteArray& ciphertext) const
*/
QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error)
{
switch(error)
{
switch (error) {
case TOX_ERR_KEY_DERIVATION_OK:
return QStringLiteral("The function returned successfully.");
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:
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:
return QStringLiteral("Unknown key derivation error.");
}
@ -305,14 +291,15 @@ QString getKeyDerivationError(TOX_ERR_KEY_DERIVATION error)
*/
QString getEncryptionError(TOX_ERR_ENCRYPTION error)
{
switch(error)
{
switch (error) {
case TOX_ERR_ENCRYPTION_OK:
return QStringLiteral("The function returned successfully.");
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:
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:
return QStringLiteral("The encryption itself failed.");
default:
@ -327,14 +314,15 @@ QString getEncryptionError(TOX_ERR_ENCRYPTION error)
*/
QString getDecryptionError(TOX_ERR_DECRYPTION error)
{
switch(error)
{
switch (error) {
case TOX_ERR_DECRYPTION_OK:
return QStringLiteral("The function returned successfully.");
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:
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:
return QStringLiteral("The input data is missing the magic number or is corrupted.");
default:
@ -349,12 +337,12 @@ QString getDecryptionError(TOX_ERR_DECRYPTION error)
*/
QString getSaltError(TOX_ERR_GET_SALT error)
{
switch(error)
{
switch (error) {
case TOX_ERR_GET_SALT_OK:
return QStringLiteral("The function returned successfully.");
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:
return QStringLiteral("The input data is missing the magic number or is corrupted.");
default:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,11 +18,11 @@
*/
#include "offlinemsgengine.h"
#include "src/friend.h"
#include "src/persistence/settings.h"
#include "src/core/core.h"
#include "src/friend.h"
#include "src/nexus.h"
#include "src/persistence/profile.h"
#include "src/persistence/settings.h"
#include <QMutexLocker>
#include <QTimer>
@ -38,9 +38,9 @@
const int OfflineMsgEngine::offlineTimeout = 20000;
QMutex OfflineMsgEngine::globalMutex;
OfflineMsgEngine::OfflineMsgEngine(Friend* frnd) :
mutex(QMutex::Recursive),
f(frnd)
OfflineMsgEngine::OfflineMsgEngine(Friend* frnd)
: mutex(QMutex::Recursive)
, f(frnd)
{
}
@ -54,12 +54,10 @@ void OfflineMsgEngine::dischargeReceipt(int receipt)
Profile* profile = Nexus::getProfile();
auto it = receipts.find(receipt);
if (it != receipts.end())
{
if (it != receipts.end()) {
int mID = it.value();
auto msgIt = undeliveredMsgs.find(mID);
if (msgIt != undeliveredMsgs.end())
{
if (msgIt != undeliveredMsgs.end()) {
if (profile->isHistoryEnabled())
profile->getHistory()->markAsSent(mID);
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);
@ -94,24 +93,19 @@ void OfflineMsgEngine::deliverOfflineMsgs()
removeAllReceipts();
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 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);
continue;
}
QString messageText = val.msg->toString();
int rec;
if (val.msg->isAction())
{
if (val.msg->isAction()) {
rec = Core::getInstance()->sendAction(f->getFriendId(), messageText);
}
else
{
} else {
rec = Core::getInstance()->sendMessage(f->getFriendId(), messageText);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,9 +22,14 @@
#include "src/widget/style.h"
#include <QBuffer>
#include <QBuffer>
#include <QCoreApplication>
#include <QCoreApplication>
#include <QDir>
#include <QDir>
#include <QDomDocument>
#include <QDomDocument>
#include <QDomElement>
#include <QDomElement>
#include <QFile>
#include <QFileInfo>
@ -32,11 +37,6 @@
#include <QFontInfo>
#include <QIcon>
#include <QPixmap>
#include <QDir>
#include <QCoreApplication>
#include <QDomDocument>
#include <QDomElement>
#include <QBuffer>
#include <QStandardPaths>
#include <QStringBuilder>
#include <QtConcurrent/QtConcurrentRun>
@ -73,8 +73,8 @@ SmileyPack::SmileyPack()
{
loadingMutex.lock();
QtConcurrent::run(this, &SmileyPack::load, Settings::getInstance().getSmileyPack());
connect(&Settings::getInstance(), &Settings::smileyPackChanged,
this, &SmileyPack::onSmileyPackChanged);
connect(&Settings::getInstance(), &Settings::smileyPackChanged, this,
&SmileyPack::onSmileyPackChanged);
}
/**
@ -93,7 +93,8 @@ QStringList SmileyPack::loadDefaultPaths()
// Workaround to fix https://bugreports.qt.io/browse/QTBUG-57522
setlocale(LC_ALL, "");
#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
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"
location = QStandardPaths::DataLocation;
#endif
for(auto qtoxPath : QStandardPaths::standardLocations(location))
{
for (auto qtoxPath : QStandardPaths::standardLocations(location)) {
qtoxPath += QDir::separator() + EMOTICONS_SUB_DIR;
if(!paths.contains(qtoxPath))
{
if (!paths.contains(qtoxPath)) {
paths << qtoxPath;
}
}
// system wide emoticons
for(auto genericPath : QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation))
{
for (auto genericPath : QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) {
genericPath += QDir::separator() + EMOTICONS_SUB_DIR;
if(!paths.contains(genericPath))
{
if (!paths.contains(genericPath)) {
paths << genericPath;
}
}
@ -134,33 +131,27 @@ QList<QPair<QString, QString>> SmileyPack::listSmileyPacks()
QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& paths)
{
QList<QPair<QString, QString> > smileyPacks;
QList<QPair<QString, QString>> smileyPacks;
for (QString path : paths)
{
if (path.leftRef(1) == "~")
{
for (QString path : paths) {
if (path.leftRef(1) == "~") {
path.replace(0, 1, QDir::homePath());
}
QDir dir(path);
if (!dir.exists())
{
if (!dir.exists()) {
continue;
}
for (const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
for (const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
dir.cd(subdirectory);
QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files);
// Does it contain a file called emoticons.xml?
if (entries.size() > 0)
{
if (entries.size() > 0) {
QString packageName = dir.dirName();
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);
}
}
@ -193,8 +184,7 @@ bool SmileyPack::load(const QString& filename)
// open emoticons.xml
QFile xmlFile(filename);
if (!xmlFile.open(QIODevice::ReadOnly))
{
if (!xmlFile.open(QIODevice::ReadOnly)) {
loadingMutex.unlock();
return false; // cannot open file
}
@ -220,32 +210,26 @@ bool SmileyPack::load(const QString& filename)
doc.setContent(xmlFile.readAll());
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();
QDomElement stringElement = emoticonElements.at(i).firstChildElement("string");
QStringList emoticonSet; // { ":)", ":-)" } etc.
while (!stringElement.isNull())
{
QString emoticon = stringElement.text()
.replace("<","&lt;").replace(">","&gt;");
while (!stringElement.isNull()) {
QString emoticon = stringElement.text().replace("<", "&lt;").replace(">", "&gt;");
filenameTable.insert(emoticon, file);
cacheSmiley(file); // preload all smileys
if (!getCachedSmiley(emoticon).isNull())
{
if (!getCachedSmiley(emoticon).isNull()) {
emoticonSet.push_back(emoticon);
}
stringElement = stringElement.nextSibling().toElement();
}
if (emoticonSet.size() > 0)
{
if (emoticonSet.size() > 0) {
emoticons.push_back(emoticonSet);
}
}
@ -264,11 +248,9 @@ QString SmileyPack::smileyfied(QString msg)
int index = msg.indexOf(exp);
// 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();
if (filenameTable.contains(key))
{
if (filenameTable.contains(key)) {
QString imgRichText = getAsRichText(key);
msg.replace(index, key.length(), imgRichText);
@ -309,15 +291,13 @@ void SmileyPack::cacheSmiley(const QString& name)
QIcon SmileyPack::getCachedSmiley(const QString& key)
{
// valid key?
if (!filenameTable.contains(key))
{
if (!filenameTable.contains(key)) {
return QPixmap();
}
// cache it if needed
QString file = filenameTable.value(key);
if (!iconCache.contains(file))
{
if (!iconCache.contains(file)) {
cacheSmiley(file);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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