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

Merge of the new API compatible qTox branch

Merge branch 'back_in_the_game'
This commit is contained in:
tux3 2015-04-26 17:47:53 +02:00
commit 2fed6724b2
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
73 changed files with 3963 additions and 3430 deletions

View File

@ -409,10 +409,6 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
SOURCES += \
src/audio.cpp \
src/core.cpp \
src/coreav.cpp \
src/coreencryption.cpp \
src/corestructs.cpp \
src/historykeeper.cpp \
src/main.cpp \
src/nexus.cpp \
@ -428,14 +424,22 @@ SOURCES += \
src/video/videoframe.cpp \
src/widget/gui.cpp \
src/toxme.cpp \
src/misc/qrwidget.cpp
src/misc/qrwidget.cpp \
src/core/core.cpp \
src/core/coreav.cpp \
src/core/coreencryption.cpp \
src/core/corefile.cpp \
src/core/corestructs.cpp \
src/profilelocker.cpp \
src/avatarbroadcaster.cpp
HEADERS += \
src/audio.h \
src/core.h \
src/corestructs.h \
src/coredefines.h \
src/coreav.h \
src/core/core.h \
src/core/coreav.h \
src/core/coredefines.h \
src/core/corefile.h \
src/core/corestructs.h \
src/historykeeper.h \
src/nexus.h \
src/misc/cdata.h \
@ -448,6 +452,9 @@ HEADERS += \
src/video/cameraworker.h \
src/video/videoframe.h \
src/video/videosource.h \
src/video/netvideosource.h \
src/widget/gui.h \
src/toxme.h \
src/misc/qrwidget.h
src/misc/qrwidget.h \
src/profilelocker.h \
src/avatarbroadcaster.h

View File

@ -2,7 +2,9 @@
if which apt-get; then
sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev \
libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev qttools5-dev-tools qtchooser libxss-dev libqt5svg5* libqrencode-dev
libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev \
qttools5-dev-tools qtchooser libxss-dev libqt5svg5* libqrencode-dev \
libqt5opengl5-dev
elif which pacman; then
sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss qt5-svg qrencode
elif which yum; then

View File

@ -24,7 +24,7 @@
#define FIX_SND_PCM_PREPARE_BUG 0
#include "audio.h"
#include "src/core.h"
#include "src/core/core.h"
#include <QDebug>
#include <QThread>
@ -72,6 +72,7 @@ Audio::~Audio()
audioThread->wait();
if (audioThread->isRunning())
audioThread->terminate();
delete audioThread;
delete audioInLock;
delete audioOutLock;
@ -123,6 +124,7 @@ void Audio::openInput(const QString& inDevDescr)
alInDev = nullptr;
if (tmp)
alcCaptureCloseDevice(tmp);
int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
if (inDevDescr.isEmpty())
alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag,
@ -163,6 +165,7 @@ void Audio::openOutput(const QString& outDevDescr)
alOutDev = alcOpenDevice(nullptr);
else
alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str());
if (!alOutDev)
{
qWarning() << "Audio: Cannot open output audio device";
@ -171,8 +174,10 @@ void Audio::openOutput(const QString& outDevDescr)
{
if (alContext && alcMakeContextCurrent(nullptr) == ALC_TRUE)
alcDestroyContext(alContext);
if (tmp)
alcCloseDevice(tmp);
alContext=alcCreateContext(alOutDev,nullptr);
if (!alcMakeContextCurrent(alContext))
{
@ -180,7 +185,9 @@ void Audio::openOutput(const QString& outDevDescr)
alcCloseDevice(alOutDev);
}
else
{
alGenSources(1, &alMainSource);
}
qDebug() << "Audio: Opening audio output "<<outDevDescr;
@ -217,13 +224,9 @@ void Audio::closeOutput()
if (alOutDev)
{
if (alcCloseDevice(alOutDev) == ALC_TRUE)
{
alOutDev = nullptr;
}
else
{
qWarning() << "Audio: Failed to close output";
}
}
}

41
src/avatarbroadcaster.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "avatarbroadcaster.h"
#include "src/core/core.h"
#include <QObject>
#include <QDebug>
QByteArray AvatarBroadcaster::avatarData;
QMap<uint32_t, bool> AvatarBroadcaster::friendsSentTo;
static QMetaObject::Connection autoBroadcastConn;
static auto autoBroadcast = [](uint32_t friendId, Status)
{
AvatarBroadcaster::sendAvatarTo(friendId);
};
void AvatarBroadcaster::setAvatar(QByteArray data)
{
avatarData = data;
friendsSentTo.clear();
QVector<uint32_t> friends = Core::getInstance()->getFriendList();
for (uint32_t friendId : friends)
sendAvatarTo(friendId);
}
void AvatarBroadcaster::sendAvatarTo(uint32_t friendId)
{
if (friendsSentTo.contains(friendId) && friendsSentTo[friendId])
return;
if (!Core::getInstance()->isFriendOnline(friendId))
return;
qDebug() << "AvatarBroadcaster: Sending avatar to "<<friendId;
Core::getInstance()->sendAvatarFile(friendId, avatarData);
friendsSentTo[friendId] = true;
}
void AvatarBroadcaster::enableAutoBroadcast(bool state)
{
QObject::disconnect(autoBroadcastConn);
if (state)
autoBroadcastConn = QObject::connect(Core::getInstance(), &Core::friendStatusChanged, autoBroadcast);
}

28
src/avatarbroadcaster.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef AVATARBROADCASTER_H
#define AVATARBROADCASTER_H
#include <QByteArray>
#include <QMap>
/// Takes care of broadcasting avatar changes to our friends in a smart way
/// Cache a copy of our current avatar and friends who have received it
/// so we don't spam avatar transfers to a friend who already has it.
class AvatarBroadcaster
{
private:
AvatarBroadcaster()=delete;
public:
/// Set our current avatar
static void setAvatar(QByteArray data);
/// Send our current avatar to this friend, if not already sent
static void sendAvatarTo(uint32_t friendId);
/// If true, we automatically broadcast our avatar to friends when they come online
static void enableAutoBroadcast(bool state = true);
private:
static QByteArray avatarData;
static QMap<uint32_t, bool> friendsSentTo;
};
#endif // AVATARBROADCASTER_H

View File

@ -244,17 +244,17 @@ void ChatLine::moveBy(qreal deltaY)
bbox.moveTop(bbox.top() + deltaY);
}
bool ChatLine::lessThanBSRectTop(const ChatLine::Ptr lhs, const qreal rhs)
bool ChatLine::lessThanBSRectTop(const ChatLine::Ptr& lhs, const qreal& rhs)
{
return lhs->sceneBoundingRect().top() < rhs;
}
bool ChatLine::lessThanBSRectBottom(const ChatLine::Ptr lhs, const qreal rhs)
bool ChatLine::lessThanBSRectBottom(const ChatLine::Ptr& lhs, const qreal& rhs)
{
return lhs->sceneBoundingRect().bottom() < rhs;
}
bool ChatLine::lessThanRowIndex(const ChatLine::Ptr lhs, const ChatLine::Ptr rhs)
bool ChatLine::lessThanRowIndex(const ChatLine::Ptr& lhs, const ChatLine::Ptr& rhs)
{
return lhs->getRow() < rhs->getRow();
}

View File

@ -82,9 +82,9 @@ public:
bool isOverSelection(QPointF scenePos);
//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);
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);
protected:
friend class ChatLog;

View File

@ -177,33 +177,17 @@ void ChatLog::mousePressEvent(QMouseEvent* ev)
{
QGraphicsView::mousePressEvent(ev);
QPointF scenePos = mapToScene(ev->pos());
if(ev->button() == Qt::LeftButton)
{
clickPos = ev->pos();
clearSelection();
}
if(ev->button() == Qt::RightButton)
{
if(!isOverSelection(scenePos))
clearSelection();
}
}
void ChatLog::mouseReleaseEvent(QMouseEvent* ev)
{
QGraphicsView::mouseReleaseEvent(ev);
QPointF scenePos = mapToScene(ev->pos());
if(ev->button() == Qt::RightButton)
{
if(!isOverSelection(scenePos))
clearSelection();
}
selectionScrollDir = NoDirection;
}

View File

@ -67,7 +67,7 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
// Note: Eliding cannot be enabled for RichText items. (QTBUG-17207)
msg->addColumn(new Text(senderText, isMe ? Style::getFont(Style::BigBold) : Style::getFont(Style::Big), true, sender, type == ACTION ? actionColor : Qt::black), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text(text, Style::getFont(Style::Big), false, type == ACTION ? QString("*%1 %2*").arg(sender, rawMessage) : rawMessage), ColumnFormat(1.0, ColumnFormat::VariableSize));
msg->addColumn(new Text(text, Style::getFont(Style::Big), 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())

View File

@ -18,7 +18,7 @@
#define CHATMESSAGE_H
#include "chatline.h"
#include "src/corestructs.h"
#include "src/core/corestructs.h"
#include <QDateTime>
class QGraphicsScene;

View File

@ -18,7 +18,7 @@
#include "ui_filetransferwidget.h"
#include "src/nexus.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/style.h"
#include "src/widget/widget.h"
@ -77,6 +77,8 @@ FileTransferWidget::FileTransferWidget(QWidget *parent, ToxFile file)
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);
setupButtons();
@ -87,7 +89,9 @@ FileTransferWidget::FileTransferWidget(QWidget *parent, ToxFile file)
ui->progressLabel->setText(tr("Waiting to send...", "file transfer widget"));
}
else
{
ui->progressLabel->setText(tr("Accept to receive this file", "file transfer widget"));
}
setFixedHeight(78);
}
@ -114,7 +118,7 @@ void FileTransferWidget::autoAcceptTransfer(const QString &path)
//Do not automatically accept the file-transfer if the path is not writable.
//The user can still accept it manually.
if (Nexus::isFilePathWritable(filepath))
if (Nexus::tryRemoveFile(filepath))
Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath);
else
qDebug() << "Warning: Cannot write to " << filepath;
@ -126,7 +130,7 @@ void FileTransferWidget::acceptTransfer(const QString &filepath)
return;
//test if writable
if(!Nexus::isFilePathWritable(filepath))
if(!Nexus::tryRemoveFile(filepath))
{
QMessageBox::warning(0,
tr("Location not writable","Title of permissions popup"),
@ -186,6 +190,7 @@ void FileTransferWidget::paintEvent(QPaintEvent *)
// draw background
if(drawButtonAreaNeeded())
painter.setClipRect(QRect(0,0,width()-buttonFieldWidth,height()));
painter.setBrush(QBrush(backgroundColor));
painter.drawRoundRect(geometry(), r * ratio, r);
@ -222,7 +227,10 @@ void FileTransferWidget::onFileTransferInfo(ToxFile file)
// ETA, speed
qreal deltaSecs = dt / 1000.0;
qint64 deltaBytes = qMax(file.bytesSent - lastBytesSent, qint64(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;
qreal bytesPerSec = static_cast<int>(static_cast<qreal>(deltaBytes) / deltaSecs);
// calculate mean
@ -306,6 +314,26 @@ void FileTransferWidget::onFileTransferPaused(ToxFile file)
setupButtons();
}
void FileTransferWidget::onFileTransferResumed(ToxFile file)
{
if(fileInfo != file)
return;
fileInfo = file;
ui->etaLabel->setText("");
ui->progressLabel->setText(tr("Resuming...", "file transfer widget"));
// reset mean
meanIndex = 0;
for(size_t i=0; i<TRANSFER_ROLLING_AVG_COUNT; ++i)
meanData[i] = 0.0;
setBackgroundColor(Style::getColor(Style::LightGrey), false);
setupButtons();
}
void FileTransferWidget::onFileTransferFinished(ToxFile file)
{
if(fileInfo != file)
@ -320,10 +348,12 @@ void FileTransferWidget::onFileTransferFinished(ToxFile file)
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg"));
ui->topButton->setObjectName("ok");
ui->topButton->setToolTip(tr("Open file."));
ui->topButton->show();
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/dir.svg"));
ui->bottomButton->setObjectName("dir");
ui->bottomButton->setToolTip(tr("Open file directory."));
ui->bottomButton->show();
// preview
@ -333,6 +363,21 @@ void FileTransferWidget::onFileTransferFinished(ToxFile file)
disconnect(Core::getInstance(), 0, this, 0);
}
void FileTransferWidget::fileTransferRemotePausedUnpaused(ToxFile file, bool paused)
{
if (paused)
onFileTransferPaused(file);
else
onFileTransferResumed(file);
}
void FileTransferWidget::fileTransferBrokenUnbroken(ToxFile file, bool broken)
{
/// TODO: Handle broken transfer differently once we have resuming code
if (broken)
onFileTransferCancelled(file);
}
QString FileTransferWidget::getHumanReadableSize(qint64 size)
{
static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"};
@ -360,9 +405,11 @@ void FileTransferWidget::setupButtons()
case ToxFile::TRANSMITTING:
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg"));
ui->topButton->setObjectName("cancel");
ui->topButton->setToolTip(tr("Cancel transfer"));
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg"));
ui->bottomButton->setObjectName("pause");
ui->bottomButton->setToolTip(tr("Pause transfer"));
setButtonColor(Style::getColor(Style::Green));
@ -370,9 +417,11 @@ void FileTransferWidget::setupButtons()
case ToxFile::PAUSED:
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg"));
ui->topButton->setObjectName("cancel");
ui->topButton->setToolTip(tr("Cancel transfer"));
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/arrow_white.svg"));
ui->bottomButton->setObjectName("resume");
ui->bottomButton->setToolTip(tr("Resume transfer"));
setButtonColor(Style::getColor(Style::LightGrey));
@ -381,16 +430,19 @@ void FileTransferWidget::setupButtons()
case ToxFile::BROKEN: //TODO: ?
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg"));
ui->topButton->setObjectName("cancel");
ui->topButton->setToolTip(tr("Cancel transfer"));
if(fileInfo.direction == ToxFile::SENDING)
{
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg"));
ui->bottomButton->setObjectName("pause");
ui->bottomButton->setToolTip(tr("Pause transfer"));
}
else
{
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg"));
ui->bottomButton->setObjectName("accept");
ui->bottomButton->setToolTip(tr("Accept transfer"));
}
break;
}

View File

@ -20,8 +20,8 @@
#include <QWidget>
#include <QTime>
#include "../chatlinecontent.h"
#include "../../corestructs.h"
#include "src/chatlog/chatlinecontent.h"
#include "src/core/corestructs.h"
namespace Ui {
@ -45,7 +45,10 @@ protected slots:
void onFileTransferAccepted(ToxFile file);
void onFileTransferCancelled(ToxFile file);
void onFileTransferPaused(ToxFile file);
void onFileTransferResumed(ToxFile file);
void onFileTransferFinished(ToxFile file);
void fileTransferRemotePausedUnpaused(ToxFile file, bool paused);
void fileTransferBrokenUnbroken(ToxFile file, bool broken);
protected:
QString getHumanReadableSize(qint64 size);
@ -69,7 +72,7 @@ private:
Ui::FileTransferWidget *ui;
ToxFile fileInfo;
QTime lastTick;
qint64 lastBytesSent = 0;
quint64 lastBytesSent = 0;
QVariantAnimation* backgroundColorAnimation = nullptr;
QVariantAnimation* buttonColorAnimation = nullptr;
QColor backgroundColor;

File diff suppressed because it is too large Load Diff

1344
src/core/core.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
#include <QMutex>
#include <tox/tox.h>
#include <tox/toxencryptsave.h>
#include "corestructs.h"
#include "coreav.h"
@ -56,15 +57,17 @@ public:
QString getPeerName(const ToxID& id) const;
QVector<uint32_t> getFriendList() const; ///< Returns the list of friendIds in our friendlist, an empty list on error
int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure
QString getGroupPeerName(int groupId, int peerId) const; ///< Get the name of a peer of a group
ToxID getGroupPeerToxID(int groupId, int peerId) const; ///< Get the ToxID of a peer of a group
QList<QString> getGroupPeerNames(int groupId) const; ///< Get the names of the peers of a group
QString getFriendAddress(int friendNumber) const; ///< Get the full address if known, or Tox ID of a friend
QString getFriendUsername(int friendNumber) const; ///< Get the username of a friend
QString getFriendAddress(uint32_t friendId) const; ///< Get the full address if known, or Tox ID of a friend
QString getFriendUsername(uint32_t friendId) const; ///< Get the username of a friend
bool isFriendOnline(uint32_t friendId) const; ///< Check if a friend is online. Unknown friends are considered offline.
bool hasFriendWithAddress(const QString &addr) const; ///< Check if we have a friend by address
bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key
int joinGroupchat(int32_t friendNumber, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite
int joinGroupchat(int32_t friendId, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite
void quitGroupChat(int groupId) const; ///< Quit a groupchat
QString getIDString() const; ///< Get the 12 first characters of our Tox ID
@ -93,43 +96,46 @@ public slots:
void acceptFriendRequest(const QString& userId);
void requestFriendship(const QString& friendAddress, const QString& message);
void groupInviteFriend(int friendId, int groupId);
void groupInviteFriend(uint32_t friendId, int groupId);
void createGroup(uint8_t type = TOX_GROUPCHAT_TYPE_AV);
void removeFriend(int friendId, bool fake = false);
void removeFriend(uint32_t friendId, bool fake = false);
void removeGroup(int groupId, bool fake = false);
void setStatus(Status status);
void setUsername(const QString& username);
void setStatusMessage(const QString& message);
void setAvatar(uint8_t format, const QByteArray& data);
void setAvatar(const QByteArray& data);
int sendMessage(int friendId, const QString& message);
int sendMessage(uint32_t friendId, const QString& message);
void sendGroupMessage(int groupId, const QString& message);
void sendGroupAction(int groupId, const QString& message);
void changeGroupTitle(int groupId, const QString& title);
int sendAction(int friendId, const QString& action);
void sendTyping(int friendId, bool typing);
int sendAction(uint32_t friendId, const QString& action);
void sendTyping(uint32_t friendId, bool typing);
void sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize);
void cancelFileSend(int friendId, int fileNum);
void cancelFileRecv(int friendId, int fileNum);
void rejectFileRecvRequest(int friendId, int fileNum);
void acceptFileRecvRequest(int friendId, int fileNum, QString path);
void pauseResumeFileSend(int friendId, int fileNum);
void pauseResumeFileRecv(int friendId, int fileNum);
void sendFile(uint32_t friendId, QString Filename, QString FilePath, long long filesize);
void sendAvatarFile(uint32_t friendId, const QByteArray& data);
void cancelFileSend(uint32_t friendId, uint32_t fileNum);
void cancelFileRecv(uint32_t friendId, uint32_t fileNum);
void rejectFileRecvRequest(uint32_t friendId, uint32_t fileNum);
void acceptFileRecvRequest(uint32_t friendId, uint32_t fileNum, QString path);
void pauseResumeFileSend(uint32_t friendId, uint32_t fileNum);
void pauseResumeFileRecv(uint32_t friendId, uint32_t fileNum);
void answerCall(int callId);
void rejectCall(int callId);
void hangupCall(int callId);
void startCall(int friendId, bool video=false);
void cancelCall(int callId, int friendId);
void startCall(uint32_t friendId, bool video=false);
void cancelCall(int callId, uint32_t friendId);
void micMuteToggle(int callId);
void volMuteToggle(int callId);
void setNospam(uint32_t nospam);
bool isGroupAvEnabled(int groupId); ///< True for AV groups, false for text-only groups
static void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
static void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call. Call from the GUI thread.
static void disableGroupCallMic(int groupId);
@ -151,23 +157,23 @@ signals:
void blockingClearContacts();
void friendRequestReceived(const QString& userId, const QString& message);
void friendMessageReceived(int friendId, const QString& message, bool isAction);
void friendMessageReceived(uint32_t friendId, const QString& message, bool isAction);
void friendAdded(int friendId, const QString& userId);
void friendAdded(uint32_t friendId, const QString& userId);
void friendStatusChanged(int friendId, Status status);
void friendStatusMessageChanged(int friendId, const QString& message);
void friendUsernameChanged(int friendId, const QString& username);
void friendTypingChanged(int friendId, bool isTyping);
void friendAvatarChanged(int friendId, const QPixmap& pic);
void friendAvatarRemoved(int friendId);
void friendStatusChanged(uint32_t friendId, Status status);
void friendStatusMessageChanged(uint32_t friendId, const QString& message);
void friendUsernameChanged(uint32_t friendId, const QString& username);
void friendTypingChanged(uint32_t friendId, bool isTyping);
void friendAvatarChanged(uint32_t friendId, const QPixmap& pic);
void friendAvatarRemoved(uint32_t friendId);
void friendRemoved(int friendId);
void friendRemoved(uint32_t friendId);
void friendLastSeenChanged(int friendId, const QDateTime& dateTime);
void friendLastSeenChanged(uint32_t friendId, const QDateTime& dateTime);
void emptyGroupCreated(int groupnumber);
void groupInviteReceived(int friendnumber, uint8_t type, QByteArray publicKey);
void groupInviteReceived(uint32_t friendId, uint8_t type, QByteArray publicKey);
void groupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
void groupTitleChanged(int groupnumber, const QString& author, const QString& title);
@ -179,14 +185,14 @@ signals:
void idSet(const QString& id);
void selfAvatarChanged(const QPixmap& pic);
void messageSentResult(int friendId, const QString& message, int messageId);
void messageSentResult(uint32_t friendId, const QString& message, int messageId);
void groupSentResult(int groupId, const QString& message, int result);
void actionSentResult(int friendId, const QString& action, int success);
void actionSentResult(uint32_t friendId, const QString& action, int success);
void receiptRecieved(int friedId, int receipt);
void failedToAddFriend(const QString& userId, const QString& errorInfo = QString());
void failedToRemoveFriend(int friendId);
void failedToRemoveFriend(uint32_t friendId);
void failedToSetUsername(const QString& username);
void failedToSetStatusMessage(const QString& message);
void failedToSetStatus(Status status);
@ -207,45 +213,45 @@ signals:
void fileTransferRemotePausedUnpaused(ToxFile file, bool paused);
void fileTransferBrokenUnbroken(ToxFile file, bool broken);
void fileSendFailed(int FriendId, const QString& fname);
void fileSendFailed(uint32_t friendId, const QString& fname);
void avInvite(int friendId, int callIndex, bool video);
void avStart(int friendId, int callIndex, bool video);
void avCancel(int friendId, int callIndex);
void avEnd(int friendId, int callIndex);
void avRinging(int friendId, int callIndex, bool video);
void avStarting(int friendId, int callIndex, bool video);
void avEnding(int friendId, int callIndex);
void avRequestTimeout(int friendId, int callIndex);
void avPeerTimeout(int friendId, int callIndex);
void avMediaChange(int friendId, int callIndex, bool videoEnabled);
void avCallFailed(int friendId);
void avRejected(int friendId, int callIndex);
void avInvite(uint32_t friendId, int callIndex, bool video);
void avStart(uint32_t friendId, int callIndex, bool video);
void avCancel(uint32_t friendId, int callIndex);
void avEnd(uint32_t friendId, int callIndex);
void avRinging(uint32_t friendId, int callIndex, bool video);
void avStarting(uint32_t friendId, int callIndex, bool video);
void avEnding(uint32_t friendId, int callIndex);
void avRequestTimeout(uint32_t friendId, int callIndex);
void avPeerTimeout(uint32_t friendId, int callIndex);
void avMediaChange(uint32_t friendId, int callIndex, bool videoEnabled);
void avCallFailed(uint32_t friendId);
void avRejected(uint32_t friendId, int callIndex);
void videoFrameReceived(vpx_image* frame);
private:
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onFriendMessage(Tox* tox, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onFriendNameChange(Tox* tox, int friendId, const uint8_t* cName, uint16_t cNameSize, void* core);
static void onFriendTypingChange(Tox* tox, int friendId, uint8_t isTyping, void* core);
static void onStatusMessageChanged(Tox* tox, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onUserStatusChanged(Tox* tox, int friendId, uint8_t userstatus, void* core);
static void onConnectionStatusChanged(Tox* tox, int friendId, uint8_t status, void* core);
static void onAction(Tox* tox, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onGroupAction(Tox* tox, int groupnumber, int peernumber, const uint8_t * action, uint16_t length, void* core);
static void onGroupInvite(Tox *tox, int friendnumber, uint8_t type, const uint8_t *data, uint16_t length,void *userdata);
static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber, const uint8_t * message, uint16_t length, void *userdata);
static void onGroupNamelistChange(Tox *tox, int groupnumber, int peernumber, uint8_t change, void *userdata);
static void onGroupTitleChange(Tox*, int groupnumber, int peernumber, const uint8_t* title, uint8_t len, void* _core);
static void onFileSendRequestCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
const uint8_t *filename, uint16_t filename_length, void *userdata);
static void onFileControlCallback(Tox *tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, const uint8_t *data, uint16_t length, void *core);
static void onFileDataCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
static void onAvatarInfoCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata);
static void onAvatarDataCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata);
static void onReadReceiptCallback(Tox *tox, int32_t friendnumber, uint32_t receipt, void *core);
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, 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 onGroupAction(Tox* tox, int groupnumber, int peernumber, const uint8_t * action,
uint16_t length, void* core);
static void onGroupInvite(Tox *tox, int32_t friendId, uint8_t type, const uint8_t *data,
uint16_t length, void *userdata);
static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber,
const uint8_t * message, uint16_t length, void *userdata);
static void onGroupNamelistChange(Tox *tox, int groupId, int peerId, uint8_t change, void *core);
static void onGroupTitleChange(Tox*, int groupnumber, int peernumber,
const uint8_t* title, uint8_t len, void* _core);
static void onReadReceiptCallback(Tox *tox, uint32_t friendId, uint32_t receipt, void *core);
static void onAvInvite(void* toxav, int32_t call_index, void* core);
static void onAvStart(void* toxav, int32_t call_index, void* core);
@ -259,32 +265,28 @@ private:
static void sendGroupCallAudio(int groupId, ToxAv* toxav);
static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled);
static void prepareCall(uint32_t friendId, int callId, ToxAv *toxav, bool videoEnabled);
static void cleanupCall(int callId);
static void playCallAudio(void *toxav, int32_t callId, const int16_t *data, uint16_t samples, void *user_data); // Callback
static void playCallAudio(void *toxav, int32_t callId, const int16_t *data,
uint16_t samples, void *user_data); // Callback
static void sendCallAudio(int callId, ToxAv* toxav);
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
unsigned channels, int sampleRate);
static void playCallVideo(void *toxav, int32_t callId, const vpx_image_t* img, void *user_data);
void sendCallVideo(int callId);
bool checkConnection();
bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise
QByteArray loadToxSave(QString path);
bool loadEncryptedSave(QByteArray& data);
void checkEncryptedHistory();
void make_tox();
void make_tox(QByteArray savedata);
void loadFriends();
static void sendAllFileData(Core* core, ToxFile* file);
static void removeFileFromQueue(bool sendQueue, int friendId, int fileId);
void checkLastOnline(int friendId);
void checkLastOnline(uint32_t friendId);
void deadifyTox();
private slots:
void onFileTransferFinished(ToxFile file);
private:
Tox* tox;
ToxAv* toxav;
@ -293,23 +295,22 @@ private:
QString loadPath; // meaningless after start() is called
QList<DhtServer> dhtServerList;
int dhtServerId;
static QList<ToxFile> fileSendQueue, fileRecvQueue;
static ToxCall calls[TOXAV_MAX_CALLS];
#ifdef QTOX_FILTER_AUDIO
static AudioFilterer * filterer[TOXAV_MAX_CALLS];
#endif
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
QMutex fileSendMutex, messageSendMutex;
QMutex messageSendMutex;
bool ready;
uint8_t* pwsaltedkeys[PasswordType::ptCounter] = {nullptr}; // use the pw's hash as the "pw"
TOX_PASS_KEY* pwsaltedkeys[PasswordType::ptCounter] = {nullptr}; // use the pw's hash as the "pw"
// Hack for reloading current profile if switching to an encrypted one fails.
// Testing the passwords before killing the current profile is perfectly doable,
// however it would require major refactoring;
// the Core class as a whole also requires major refactoring (especially to support multiple IDs at once),
// so I'm punting on this until then, when it would get fixed anyways
uint8_t* backupkeys[PasswordType::ptCounter] = {nullptr};
TOX_PASS_KEY* backupkeys[PasswordType::ptCounter] = {nullptr};
QString* backupProfile = nullptr;
void saveCurrentInformation();
QString loadOldInformation();
@ -320,6 +321,7 @@ private:
static QThread *coreThread;
friend class Audio; ///< Audio can access our calls directly to reduce latency
friend class CoreFile; ///< CoreFile can access tox* and emit our signals
};
#endif // CORE_HPP

View File

@ -15,12 +15,12 @@
*/
#include "core.h"
#include "video/camera.h"
#include "audio.h"
#include "src/video/camera.h"
#include "src/audio.h"
#ifdef QTOX_FILTER_AUDIO
#include "audiofilterer.h"
#include "src/audiofilterer.h"
#endif
#include "misc/settings.h"
#include "src/misc/settings.h"
#include <QDebug>
#include <QTimer>
@ -34,12 +34,14 @@ uint8_t* Core::videobuf;
bool Core::anyActiveCalls()
{
for (auto& call : calls)
{
if (call.active)
return true;
}
return false;
}
void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled)
void Core::prepareCall(uint32_t friendId, int32_t callId, ToxAv* toxav, bool videoEnabled)
{
qDebug() << QString("Core: preparing call %1").arg(callId);
calls[callId].callId = callId;
@ -93,6 +95,7 @@ void Core::onAvMediaChange(void* toxav, int32_t callId, void* core)
int friendId;
if (toxav_get_peer_csettings((ToxAv*)toxav, callId, 0, &settings) < 0)
goto fail;
friendId = toxav_get_peer_id((ToxAv*)toxav, callId, 0);
if (friendId < 0)
goto fail;
@ -120,7 +123,7 @@ fail: // Centralized error handling
return;
}
void Core::answerCall(int callId)
void Core::answerCall(int32_t callId)
{
int friendId = toxav_get_peer_id(toxav, callId, 0);
if (friendId < 0)
@ -152,23 +155,23 @@ void Core::answerCall(int callId)
delete transSettings;
}
void Core::hangupCall(int callId)
void Core::hangupCall(int32_t callId)
{
qDebug() << QString("Core: hanging up call %1").arg(callId);
calls[callId].active = false;
toxav_hangup(toxav, callId);
}
void Core::rejectCall(int callId)
void Core::rejectCall(int32_t callId)
{
qDebug() << QString("Core: rejecting call %1").arg(callId);
calls[callId].active = false;
toxav_reject(toxav, callId, nullptr);
}
void Core::startCall(int friendId, bool video)
void Core::startCall(uint32_t friendId, bool video)
{
int callId;
int32_t callId;
ToxAvCSettings cSettings = av_DefaultSettings;
cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
@ -204,14 +207,14 @@ void Core::startCall(int friendId, bool video)
}
}
void Core::cancelCall(int callId, int friendId)
void Core::cancelCall(int32_t callId, uint32_t friendId)
{
qDebug() << QString("Core: Cancelling call with %1").arg(friendId);
calls[callId].active = false;
toxav_cancel(toxav, callId, friendId, nullptr);
}
void Core::cleanupCall(int callId)
void Core::cleanupCall(int32_t callId)
{
qDebug() << QString("Core: cleaning up call %1").arg(callId);
calls[callId].active = false;
@ -220,6 +223,7 @@ void Core::cleanupCall(int callId)
calls[callId].sendVideoTimer->stop();
if (calls[callId].videoEnabled)
Camera::getInstance()->unsubscribe();
Audio::unsuscribeInput();
toxav_kill_transmission(Core::getInstance()->toxav, callId);
}
@ -239,7 +243,7 @@ void Core::playCallAudio(void* toxav, int32_t callId, const int16_t *data, uint1
playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate);
}
void Core::sendCallAudio(int callId, ToxAv* toxav)
void Core::sendCallAudio(int32_t callId, ToxAv* toxav)
{
if (!calls[callId].active)
return;
@ -286,9 +290,7 @@ void Core::sendCallAudio(int callId, ToxAv* toxav)
}
if ((r = toxav_send_audio(toxav, callId, dest, r)) < 0)
{
qDebug() << "Core: toxav_send_audio error";
}
}
calls[callId].sendAudioTimer->start();
}
@ -303,7 +305,7 @@ void Core::playCallVideo(void*, int32_t callId, const vpx_image_t* img, void *us
calls[callId].videoSource.pushVPXFrame(img);
}
void Core::sendCallVideo(int callId)
void Core::sendCallVideo(int32_t callId)
{
if (!calls[callId].active || !calls[callId].videoEnabled)
return;
@ -333,15 +335,13 @@ void Core::sendCallVideo(int callId)
calls[callId].sendVideoTimer->start();
}
void Core::micMuteToggle(int callId)
void Core::micMuteToggle(int32_t callId)
{
if (calls[callId].active)
{
calls[callId].muteMic = !calls[callId].muteMic;
}
}
void Core::volMuteToggle(int callId)
void Core::volMuteToggle(int32_t callId)
{
if (calls[callId].active)
{

View File

@ -3,7 +3,7 @@
#include <QHash>
#include <tox/toxav.h>
#include "video/netvideosource.h"
#include "src/video/netvideosource.h"
#if defined(__APPLE__) && defined(__MACH__)
#include <OpenAL/al.h>
@ -19,8 +19,8 @@ struct ToxCall
{
ToxAvCSettings codecSettings;
QTimer *sendAudioTimer, *sendVideoTimer;
int callId;
int friendId;
int32_t callId;
uint32_t friendId;
bool videoEnabled;
bool active;
bool muteMic;

View File

@ -20,17 +20,18 @@
#include "core.h"
#include "src/widget/gui.h"
#include "src/misc/settings.h"
#include "src/misc/cstring.h"
#include "src/historykeeper.h"
#include <tox/tox.h>
#include <tox/toxencryptsave.h>
#include "src/misc/settings.h"
#include "misc/cstring.h"
#include "historykeeper.h"
#include <QApplication>
#include <QDebug>
#include <QSaveFile>
#include <QFile>
#include <QThread>
#include <algorithm>
#include <cassert>
void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
{
@ -38,31 +39,30 @@ void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
if (password.isEmpty())
return;
pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()];
pwsaltedkeys[passtype] = new TOX_PASS_KEY;
CString str(password);
if (salt)
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype]);
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype], nullptr);
else
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]);
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype], nullptr);
password.clear();
}
#include <algorithm>
void Core::useOtherPassword(PasswordType type)
{
clearPassword(type);
pwsaltedkeys[type] = new uint8_t[tox_pass_key_length()];
pwsaltedkeys[type] = new TOX_PASS_KEY;
PasswordType other = (type == ptMain) ? ptHistory : ptMain;
std::copy(pwsaltedkeys[other], pwsaltedkeys[other]+tox_pass_key_length(), pwsaltedkeys[type]);
std::copy(pwsaltedkeys[other], pwsaltedkeys[other]+1, pwsaltedkeys[type]);
}
void Core::clearPassword(PasswordType passtype)
{
delete[] pwsaltedkeys[passtype];
delete pwsaltedkeys[passtype];
pwsaltedkeys[passtype] = nullptr;
}
@ -71,13 +71,13 @@ void Core::saveCurrentInformation()
{
if (pwsaltedkeys[ptMain])
{
backupkeys[ptMain] = new uint8_t[tox_pass_key_length()];
std::copy(pwsaltedkeys[ptMain], pwsaltedkeys[ptMain]+tox_pass_key_length(), backupkeys[ptMain]);
backupkeys[ptMain] = new TOX_PASS_KEY;
std::copy(pwsaltedkeys[ptMain], pwsaltedkeys[ptMain]+1, backupkeys[ptMain]);
}
if (pwsaltedkeys[ptHistory])
{
backupkeys[ptHistory] = new uint8_t[tox_pass_key_length()];
std::copy(pwsaltedkeys[ptHistory], pwsaltedkeys[ptHistory]+tox_pass_key_length(), backupkeys[ptHistory]);
backupkeys[ptHistory] = new TOX_PASS_KEY;
std::copy(pwsaltedkeys[ptHistory], pwsaltedkeys[ptHistory]+1, backupkeys[ptHistory]);
}
backupProfile = new QString(Settings::getInstance().getCurrentProfile());
}
@ -107,23 +107,26 @@ QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
{
if (!pwsaltedkeys[passtype])
return QByteArray();
uint8_t encrypted[data.size() + tox_pass_encryption_extra_length()];
if (tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], encrypted) == -1)
uint8_t encrypted[data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
if (!tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
pwsaltedkeys[passtype], encrypted, nullptr))
{
qWarning() << "Core::encryptData: encryption failed";
return QByteArray();
}
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + tox_pass_encryption_extra_length());
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
}
QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype)
{
if (!pwsaltedkeys[passtype])
return QByteArray();
int sz = data.size() - tox_pass_encryption_extra_length();
int sz = data.size() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t decrypted[sz];
int decr_size = tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], decrypted);
if (decr_size != sz)
if (!tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
pwsaltedkeys[passtype], decrypted, nullptr))
{
qWarning() << "Core::decryptData: decryption failed";
return QByteArray();
@ -147,18 +150,17 @@ QByteArray Core::getSaltFromFile(QString filename)
qWarning() << "Core: file" << filename << "doesn't exist";
return QByteArray();
}
QByteArray data = file.read(tox_pass_encryption_extra_length());
QByteArray data = file.read(TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
file.close();
uint8_t *salt = new uint8_t[tox_pass_salt_length()];
int err = tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
if (err)
uint8_t *salt = new uint8_t[TOX_PASS_SALT_LENGTH];
if (!tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt))
{
qWarning() << "Core: can't get salt from" << filename << "header";
return QByteArray();
}
QByteArray res = QByteArray::fromRawData(reinterpret_cast<const char*>(salt), tox_pass_salt_length());
QByteArray res(reinterpret_cast<const char*>(salt), TOX_PASS_SALT_LENGTH);
delete[] salt;
return res;
}
@ -168,6 +170,7 @@ bool Core::loadEncryptedSave(QByteArray& data)
if (!Settings::getInstance().getEncryptTox())
GUI::showWarning(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
size_t fileSize = data.size();
int error = -1;
QString a(tr("Please enter the password for the %1 profile.", "used in load() when no pw is already set").arg(Settings::getInstance().getCurrentProfile()));
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
@ -175,18 +178,23 @@ bool Core::loadEncryptedSave(QByteArray& data)
if (pwsaltedkeys[ptMain]) // password set, try it
{
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
if (!error)
QByteArray newData(fileSize-TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0);
if (tox_pass_key_decrypt((uint8_t*)data.data(), fileSize, pwsaltedkeys[ptMain],
(uint8_t*)newData.data(), nullptr))
{
data = newData;
Settings::getInstance().setEncryptTox(true);
return true;
}
dialogtxt = tr("The profile password failed. Please try another?", "used only when pw set before load() doesn't work");
}
else
{
dialogtxt = a;
}
uint8_t salt[tox_pass_salt_length()];
uint8_t salt[TOX_PASS_SALT_LENGTH];
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
do
@ -199,9 +207,16 @@ bool Core::loadEncryptedSave(QByteArray& data)
return false;
}
else
{
setPassword(pw, ptMain, salt);
}
QByteArray newData(fileSize-TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0);
error = !tox_pass_key_decrypt((uint8_t*)data.data(), data.size(), pwsaltedkeys[ptMain],
(uint8_t*)newData.data(), nullptr);
if (!error)
data = newData;
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
dialogtxt = a + "\n" + b;
} while (error != 0);
@ -233,10 +248,14 @@ void Core::checkEncryptedHistory()
{
if (!exists || HistoryKeeper::checkPassword())
return;
dialogtxt = tr("The chat history password failed. Please try another?", "used only when pw set before load() doesn't work");
}
else
{
dialogtxt = a;
}
dialogtxt += "\n" + c;
if (pwsaltedkeys[ptMain])
@ -283,20 +302,19 @@ void Core::saveConfiguration(const QString& path)
}
QSaveFile configurationFile(path);
if (!configurationFile.open(QIODevice::WriteOnly)) {
if (!configurationFile.open(QIODevice::WriteOnly))
{
qCritical() << "File " << path << " cannot be opened";
return;
}
qDebug() << "Core: writing tox_save to " << path;
uint32_t fileSize; bool encrypt = Settings::getInstance().getEncryptTox();
if (encrypt)
fileSize = tox_encrypted_size(tox);
else
fileSize = tox_size(tox);
uint32_t fileSize = tox_get_savedata_size(tox);
bool encrypt = Settings::getInstance().getEncryptTox();
if (fileSize > 0 && fileSize <= std::numeric_limits<int32_t>::max()) {
if (fileSize > 0 && fileSize <= std::numeric_limits<int32_t>::max())
{
uint8_t *data = new uint8_t[fileSize];
if (encrypt)
@ -306,20 +324,32 @@ void Core::saveConfiguration(const QString& path)
// probably zero chance event
GUI::showWarning(tr("NO Password"), tr("Local file encryption is enabled, but there is no password! It will be disabled."));
Settings::getInstance().setEncryptTox(false);
tox_save(tox, data);
tox_get_savedata(tox, data);
}
else
{
int ret = tox_encrypted_key_save(tox, data, pwsaltedkeys[ptMain]);
if (ret == -1)
tox_get_savedata(tox, data);
uint8_t* newData = new uint8_t[fileSize+TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
if (tox_pass_key_encrypt(data, fileSize, pwsaltedkeys[ptMain], newData, nullptr))
{
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!";
delete[] data;
data = newData;
fileSize+=TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
}
else
{
delete[] newData;
delete[] data;
qCritical() << "Core::saveConfiguration(QString): Encryption failed, couldn't save";
configurationFile.cancelWriting();
return;
}
}
}
else
tox_save(tox, data);
{
tox_get_savedata(tox, data);
}
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
configurationFile.commit();

415
src/core/corefile.cpp Normal file
View File

@ -0,0 +1,415 @@
#include "core.h"
#include "corefile.h"
#include "corestructs.h"
#include "src/misc/cstring.h"
#include "src/misc/settings.h"
#include <QDebug>
#include <QFile>
#include <QThread>
#include <QDir>
#include <memory>
QMutex CoreFile::fileSendMutex;
QHash<uint64_t, ToxFile> CoreFile::fileMap;
using namespace std;
void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& data)
{
QMutexLocker mlocker(&fileSendMutex);
uint8_t filename[TOX_HASH_LENGTH];
tox_hash(filename, (uint8_t*)data.data(), data.size());
uint64_t filesize = data.size();
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, filesize,
nullptr, filename, TOX_HASH_LENGTH, nullptr);
if (fileNum == UINT32_MAX)
{
qWarning() << "CoreFile::sendAvatarFile: Can't create the Tox file sender";
return;
}
//qDebug() << QString("CoreFile::sendAvatarFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId);
ToxFile file{fileNum, friendId, "", "", ToxFile::SENDING};
file.filesize = filesize;
file.fileName = QByteArray((char*)filename, TOX_HASH_LENGTH);
file.fileKind = TOX_FILE_KIND_AVATAR;
file.avatarData = data;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr);
addFile(friendId, fileNum, file);
}
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 == UINT32_MAX)
{
qWarning() << "CoreFile::sendFile: Can't create the Tox file sender";
emit core->fileSendFailed(friendId, Filename);
return;
}
qDebug() << QString("CoreFile::sendFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId);
ToxFile file{fileNum, friendId, fileName, FilePath, ToxFile::SENDING};
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))
{
qWarning() << QString("CoreFile::sendFile: Can't open file, error: %1").arg(file.file->errorString());
}
addFile(friendId, fileNum, file);
emit core->fileSendStarted(file);
}
void CoreFile::pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::pauseResumeFileSend: No such file in queue");
return;
}
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)
{
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
else
qWarning() << "CoreFile::pauseResumeFileSend: File is stopped";
}
void CoreFile::pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::cancelFileRecv: No such file in queue");
return;
}
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)
{
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
else
qWarning() << "CoreFile::pauseResumeFileRecv: File is stopped or broken";
}
void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::cancelFileSend: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
while (file->sendTimer) QThread::msleep(1); // Wait until sendAllFileData returns before deleting
removeFile(friendId, fileId);
}
void CoreFile::cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::cancelFileRecv: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
}
void CoreFile::rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::rejectFileRecvRequest: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
}
void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::acceptFileRecvRequest: No such file in queue");
return;
}
file->setFilePath(path);
if (!file->open(true))
{
qWarning() << "CoreFile::acceptFileRecvRequest: Unable to open file";
return;
}
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
ToxFile* CoreFile::findFile(uint32_t friendId, uint32_t fileId)
{
uint64_t key = ((uint64_t)friendId<<32) + (uint64_t)fileId;
if (!fileMap.contains(key))
{
qWarning() << "CoreFile::findFile: File transfer with ID "<<friendId<<':'<<fileId<<" doesn't exist";
return nullptr;
}
else
return &fileMap[key];
}
void CoreFile::addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file)
{
uint64_t key = ((uint64_t)friendId<<32) + (uint64_t)fileId;
if (fileMap.contains(key))
qWarning() << "CoreFile::addFile: Overwriting existing file transfer with same ID "<<friendId<<':'<<fileId;
fileMap.insert(key, file);
}
void CoreFile::removeFile(uint32_t friendId, uint32_t fileId)
{
uint64_t key = ((uint64_t)friendId<<32) + (uint64_t)fileId;
if (!fileMap.contains(key))
{
qWarning() << "CoreFile::removeFile: No such file in queue";
return;
}
fileMap[key].file->close();
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 *_core)
{
Core* core = static_cast<Core*>(_core);
qDebug() << QString("CoreFile: Received file request %1:%2 kind %3")
.arg(friendId).arg(fileId).arg(kind);
if (kind == TOX_FILE_KIND_AVATAR)
{
QString friendAddr = core->getFriendAddress(friendId);
if (!filesize)
{
// Avatars of size 0 means explicitely no avatar
emit core->friendAvatarRemoved(friendId);
QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+friendAddr.left(64)+".png"));
QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+friendAddr.left(64)+".hash"));
return;
}
else if (Settings::getInstance().getAvatarHash(friendAddr) == QByteArray((char*)fname, fnameLen))
{
// If it's an avatar but we already have it cached, cancel
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return;
}
else
{
// It's an avatar and we don't have it, autoaccept the transfer
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr);
}
}
ToxFile file{fileId, friendId,
CString::toString(fname,fnameLen).toUtf8(), "", ToxFile::RECEIVING};
file.filesize = filesize;
file.fileKind = kind;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileId, (uint8_t*)file.resumeFileId.data(), nullptr);
addFile(friendId, fileId, file);
if (kind != TOX_FILE_KIND_AVATAR)
emit core->fileReceiveRequested(file);
}
void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId,
TOX_FILE_CONTROL control, void *core)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::onFileControlCallback: No such file in queue");
return;
}
if (control == TOX_FILE_CONTROL_CANCEL)
{
qDebug() << "CoreFile::onFileControlCallback: Received cancel for file "<<friendId<<":"<<fileId;
emit static_cast<Core*>(core)->fileTransferCancelled(*file);
removeFile(friendId, fileId);
}
else if (control == TOX_FILE_CONTROL_PAUSE)
{
qDebug() << "CoreFile::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)
{
qDebug() << "CoreFile::onFileControlCallback: Received pause 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;
}
}
void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
uint64_t pos, size_t length, void* core)
{
//qDebug() << "File data req of "<<length<<" at "<<pos<<" for file "<<friendId<<':'<<fileId;
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::onFileDataCallback: No such file in queue");
return;
}
// If we reached EOF, ack and cleanup the transfer
if (!length)
{
//qDebug("CoreFile::onFileDataCallback: File sending completed");
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit static_cast<Core*>(core)->fileTransferFinished(*file);
removeFile(friendId, fileId);
return;
}
unique_ptr<uint8_t[]> data(new uint8_t[length]);
int64_t nread;
if (file->fileKind == TOX_FILE_KIND_AVATAR)
{
QByteArray chunk = file->avatarData.mid(pos, length);
nread = chunk.size();
memcpy(data.get(), chunk.data(), nread);
}
else
{
file->file->seek(pos);
nread = file->file->read((char*)data.get(), length);
if (nread <= 0)
{
qWarning("CoreFile::onFileDataCallback: Failed to read from file");
emit static_cast<Core*>(core)->fileTransferCancelled(*file);
tox_file_send_chunk(tox, friendId, fileId, pos, nullptr, 0, nullptr);
removeFile(friendId, fileId);
return;
}
file->bytesSent += length;
}
if (!tox_file_send_chunk(tox, friendId, fileId, pos, data.get(), nread, nullptr))
{
qWarning("CoreFile::onFileDataCallback: Failed to send data chunk");
return;
}
if (file->fileKind != TOX_FILE_KIND_AVATAR)
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 *core)
{
//qDebug() << QString("CoreFile: Received chunk for %1:%2 pos %3 size %4")
// .arg(friendId).arg(fileId).arg(position).arg(length);
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::onFileRecvChunkCallback: No such file in queue");
tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return;
}
if (file->bytesSent != position)
{
/// TODO: Allow ooo receiving for non-stream transfers, with very careful checking
qWarning("CoreFile::onFileRecvChunkCallback: Received a chunk out-of-order, aborting transfer");
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit static_cast<Core*>(core)->fileTransferCancelled(*file);
tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
return;
}
if (!length)
{
if (file->fileKind == TOX_FILE_KIND_AVATAR)
{
QPixmap pic;
pic.loadFromData(file->avatarData);
if (!pic.isNull())
{
qDebug() << "Core: Got avatar data from" << static_cast<Core*>(core)->getFriendUsername(friendId);
Settings::getInstance().saveAvatar(pic, static_cast<Core*>(core)->getFriendAddress(friendId));
Settings::getInstance().saveAvatarHash(file->fileName, static_cast<Core*>(core)->getFriendAddress(friendId));
emit static_cast<Core*>(core)->friendAvatarChanged(friendId, pic);
}
}
else
{
emit static_cast<Core*>(core)->fileTransferFinished(*file);
}
removeFile(friendId, fileId);
return;
}
if (file->fileKind == TOX_FILE_KIND_AVATAR)
file->avatarData.append((char*)data, length);
else
file->file->write((char*)data,length);
file->bytesSent += length;
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit static_cast<Core*>(core)->fileTransferInfo(*file);
}
void CoreFile::onConnectionStatusChanged(Core* core, uint32_t friendId, bool online)
{
/// TODO: Actually resume broken file transfers
/// We need to:
/// - 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)
ToxFile::FileStatus status = online ? ToxFile::TRANSMITTING : ToxFile::BROKEN;
for (uint64_t key : fileMap.keys())
{
if (key>>32 != friendId)
continue;
fileMap[key].status = status;
emit core->fileTransferBrokenUnbroken(fileMap[key], !online);
}
}

57
src/core/corefile.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef COREFILE_H
#define COREFILE_H
#include <cstdint>
#include <cstddef>
#include <memory>
#include <tox/tox.h>
#include "corestructs.h"
#include <QString>
#include <QMutex>
#include <QHash>
struct Tox;
class Core;
/// Implements Core's file transfer callbacks
/// Avoids polluting core.h with private internal callbacks
class CoreFile
{
friend class Core;
private:
CoreFile()=delete;
private:
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 addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file);
static void removeFile(uint32_t friendId, uint32_t fileId);
private:
static void onFileReceiveCallback(Tox*, uint32_t friendnumber, uint32_t fileId, uint32_t kind,
uint64_t filesize, const uint8_t *fname, size_t fnameLen, void *core);
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 *core);
static void onConnectionStatusChanged(Core* core, uint32_t friendId, bool online);
private:
static QMutex fileSendMutex;
static QHash<uint64_t, ToxFile> fileMap;
/// TODO: Replace the two queues by a hash map uint64_t -> unique_ptr<ToxFile>
};
#endif // COREFILE_H

View File

@ -1,14 +1,15 @@
#include "src/corestructs.h"
#include "src/core.h"
#include "src/core/corestructs.h"
#include "src/core/core.h"
#include <tox/tox.h>
#include <QFile>
#include <QRegularExpression>
#define TOX_ID_LENGTH 2*TOX_FRIEND_ADDRESS_SIZE
#define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE
ToxFile::ToxFile(int FileNum, int FriendId, QByteArray FileName, QString FilePath, FileDirection Direction)
: fileNum(FileNum), friendId(FriendId), fileName{FileName}, filePath{FilePath}, file{new QFile(filePath)},
bytesSent{0}, filesize{0}, status{STOPPED}, direction{Direction}, sendTimer{nullptr}
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}, sendTimer{nullptr}
{
}
@ -78,5 +79,5 @@ void ToxID::clear()
bool ToxID::isToxId(const QString& value)
{
const QRegularExpression hexRegExp("^[A-Fa-f0-9]+$");
return value.length() == TOX_ID_LENGTH && value.contains(hexRegExp);
return value.length() == TOX_HEX_ID_LENGTH && value.contains(hexRegExp);
}

View File

@ -5,6 +5,7 @@
// They should include this file directly instead to reduce compilation times
#include <QString>
#include <memory>
class QFile;
class QTimer;
@ -58,7 +59,7 @@ struct ToxFile
};
ToxFile()=default;
ToxFile(int FileNum, int FriendId, QByteArray FileName, QString FilePath, FileDirection Direction);
ToxFile(uint32_t FileNum, uint32_t FriendId, QByteArray FileName, QString FilePath, FileDirection Direction);
~ToxFile(){}
bool operator==(const ToxFile& other) const;
@ -67,16 +68,19 @@ struct ToxFile
void setFilePath(QString path);
bool open(bool write);
int fileNum;
int friendId;
uint8_t fileKind; ///< Data file (default) or avatar
uint32_t fileNum;
uint32_t friendId;
QByteArray fileName;
QString filePath;
QFile* file;
qint64 bytesSent;
qint64 filesize;
std::shared_ptr<QFile> file;
quint64 bytesSent;
quint64 filesize;
FileStatus status;
FileDirection direction;
QTimer* sendTimer;
QByteArray avatarData;
QByteArray resumeFileId;
};
#endif // CORESTRUCTS_H

View File

@ -19,10 +19,10 @@
#include "widget/friendwidget.h"
#include "widget/form/chatform.h"
#include "widget/gui.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/settings.h"
Friend::Friend(int FriendId, const ToxID &UserId)
Friend::Friend(uint32_t FriendId, const ToxID &UserId)
: userName{Core::getInstance()->getPeerName(UserId)},
userID{UserId}, friendId{FriendId}
{
@ -35,11 +35,6 @@ Friend::Friend(int FriendId, const ToxID &UserId)
widget = new FriendWidget(friendId, getDisplayedName());
chatForm = new ChatForm(this);
if (Settings::getInstance().getEnableLogging())
{
chatForm->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
widget->historyLoaded = true;
}
}
Friend::~Friend()
@ -48,6 +43,15 @@ Friend::~Friend()
delete widget;
}
void Friend::loadHistory()
{
if (Settings::getInstance().getEnableLogging())
{
chatForm->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
widget->historyLoaded = true;
}
}
void Friend::setName(QString name)
{
userName = name;
@ -87,6 +91,7 @@ QString Friend::getDisplayedName() const
{
if (userAlias.size() == 0)
return userName;
return userAlias;
}
@ -95,7 +100,7 @@ const ToxID &Friend::getToxID() const
return userID;
}
int Friend::getFriendID() const
uint32_t Friend::getFriendID() const
{
return friendId;
}

View File

@ -19,7 +19,7 @@
#include <QObject>
#include <QString>
#include "corestructs.h"
#include "src/core/corestructs.h"
struct FriendWidget;
class ChatForm;
@ -28,11 +28,14 @@ class Friend : public QObject
{
Q_OBJECT
public:
Friend(int FriendId, const ToxID &UserId);
Friend(uint32_t FriendId, const ToxID &UserId);
Friend(const Friend& other)=delete;
~Friend();
Friend& operator=(const Friend& other)=delete;
/// Loads the friend's chat history if enabled
void loadHistory();
void setName(QString name);
void setAlias(QString name);
QString getDisplayedName() const;
@ -43,7 +46,7 @@ public:
int getEventFlag() const;
const ToxID &getToxID() const;
int getFriendID() const;
uint32_t getFriendID() const;
void setStatus(Status s);
Status getStatus() const;
@ -57,7 +60,7 @@ signals:
private:
QString userAlias, userName;
ToxID userID;
int friendId;
uint32_t friendId;
int hasNewEvents;
Status friendStatus;

View File

@ -34,6 +34,10 @@ Friend* FriendList::addFriend(int friendId, const ToxID& userId)
friendList[friendId] = newfriend;
tox2id[userId.publicKey] = friendId;
// Must be done AFTER adding to the friendlist
// or we won't find the friend and history will have blank names
newfriend->loadHistory();
return newfriend;
}

View File

@ -19,7 +19,7 @@
template <class T> class QList;
template <class A, class B> class QHash;
struct Friend;
class Friend;
class QString;
struct ToxID;

View File

@ -19,7 +19,7 @@
#include "widget/form/groupchatform.h"
#include "friendlist.h"
#include "friend.h"
#include "core.h"
#include "src/core/core.h"
#include "widget/gui.h"
#include <QDebug>
#include <QTimer>
@ -102,6 +102,7 @@ void Group::regeneratePeerList()
ToxID id = Core::getInstance()->getGroupPeerToxID(groupId, i);
if (id.isMine())
selfPeerNum = i;
QString toxid = id.publicKey;
toxids[toxid] = peers[i];
Friend *f = FriendList::findFriend(id);
@ -177,9 +178,7 @@ QString Group::resolveToxID(const ToxID &id) const
auto it = toxids.find(key);
if (it != toxids.end())
{
return *it;
}
return QString();
}

View File

@ -23,7 +23,7 @@
#define RETRY_PEER_INFO_INTERVAL 500
struct Friend;
class Friend;
class GroupWidget;
class GroupChatForm;
struct ToxID;

View File

@ -16,7 +16,7 @@
#include "historykeeper.h"
#include "misc/settings.h"
#include "core.h"
#include "src/core/core.h"
#include <QSqlError>
#include <QFile>
@ -56,7 +56,9 @@ HistoryKeeper *HistoryKeeper::getInstance()
historyInstance = new HistoryKeeper(dbIntf);
return historyInstance;
} else {
}
else
{
path = getHistoryPath();
}
}
@ -111,9 +113,7 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
QSqlQuery ret = db->exec("SELECT seq FROM sqlite_sequence WHERE name=\"sent_status\";");
int idCur = 0;
if (ret.first())
{
idCur = ret.value(0).toInt();
}
if (idCur != idMax)
{
@ -145,6 +145,7 @@ qint64 HistoryKeeper::addChatEntry(const QString& chat, const QString& message,
db->exec("BEGIN TRANSACTION;");
for (auto &it : cmds)
db->exec(it);
db->exec("COMMIT TRANSACTION;");
messageID++;
@ -167,7 +168,9 @@ QList<HistoryKeeper::HistMessage> HistoryKeeper::getChatHistory(HistoryKeeper::C
dbAnswer = db->exec(QString("SELECT history.id, timestamp, user_id, message, status FROM history LEFT JOIN sent_status ON history.id = sent_status.id ") +
QString("INNER JOIN aliases ON history.sender = aliases.id AND timestamp BETWEEN %1 AND %2 AND chat_id = %3;")
.arg(time64_from).arg(time64_to).arg(chat_id));
} else {
}
else
{
// no groupchats yet
}
@ -372,7 +375,8 @@ void HistoryKeeper::setSyncType(Db::syncType sType)
{
QString syncCmd;
switch (sType) {
switch (sType)
{
case Db::syncType::stFull:
syncCmd = "FULL";
break;
@ -413,5 +417,6 @@ QList<HistoryKeeper::HistMessage> HistoryKeeper::exportMessagesDeleteFile(int en
qDebug() << "HistoryKeeper: count" << msgs.size() << "messages exported";
if (!removeHistory(encrypted))
qWarning() << "HistoryKeeper: couldn't delete old log file!";
return msgs;
}

View File

@ -134,7 +134,14 @@ bool IPC::isCurrentOwner()
{
if (globalMemory.lock())
{
bool isOwner = ((*(uint64_t*)globalMemory.data()) == globalId);
void* data = globalMemory.data();
if (!data)
{
qWarning() << "IPC: isCurrentOwner failed to access the memory, returning false";
globalMemory.unlock();
return false;
}
bool isOwner = ((*(uint64_t*)data) == globalId);
globalMemory.unlock();
return isOwner;
}

View File

@ -21,6 +21,7 @@
#include "src/widget/toxuri.h"
#include "src/widget/toxsave.h"
#include "src/autoupdate.h"
#include "src/profilelocker.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QDateTime>
@ -82,6 +83,8 @@ int main(int argc, char *argv[])
a.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
#endif
qsrand(time(0));
// Process arguments
QCommandLineParser parser;
parser.setApplicationDescription("qTox, version: " + QString(GIT_VERSION) + "\nBuilt: " + __TIME__ + " " + __DATE__);
@ -213,6 +216,13 @@ int main(int argc, char *argv[])
ipc.registerEventHandler("save", &toxSaveEventHandler);
ipc.registerEventHandler("activate", &toxActivateEventHandler);
// If we're the IPC owner and we just started, then
// either we're the only running instance or any other instance
// is already so frozen it lost ownership.
// It's safe to remove any potential stale locks in this situation.
if (ipc.isCurrentOwner())
ProfileLocker::clearAllLocks();
if (parser.positionalArguments().size() > 0)
{
QString firstParam(parser.positionalArguments()[0]);
@ -254,7 +264,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
}
else if (!ipc.isCurrentOwner())
else if (!ipc.isCurrentOwner() && !parser.isSet("p"))
{
uint32_t dest = 0;
if (parser.isSet("p"))

View File

@ -72,7 +72,7 @@ QString CUserId::toString(const uint8_t* cUserId)
// CFriendAddress
const uint16_t CFriendAddress::SIZE{TOX_FRIEND_ADDRESS_SIZE};
const uint16_t CFriendAddress::SIZE{TOX_ADDRESS_SIZE};
CFriendAddress::CFriendAddress(const QString &friendAddress) :
CData(friendAddress, SIZE)

View File

@ -16,7 +16,7 @@
#include "encrypteddb.h"
#include "src/misc/settings.h"
#include "src/core.h"
#include "src/core/core.h"
#include <tox/toxencryptsave.h>
@ -25,7 +25,7 @@
#include <QSqlError>
qint64 EncryptedDb::encryptedChunkSize = 4096;
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - tox_pass_encryption_extra_length();
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
PlainDb(":memory:", initList), fileName(fname)
@ -41,7 +41,9 @@ EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
encrFile.close();
}
else
{
chunkPosition = 0;
}
encrFile.setFileName(fileName);
@ -90,7 +92,9 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf)
if (buf.size() > 0)
{
fileContent += buf;
} else {
}
else
{
qWarning() << "EncryptedDb::pullFileContent: Encrypted history log is corrupted: can't decrypt, will be deleted";
buf = QByteArray();
return false;
@ -108,9 +112,8 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf)
PlainDb::exec("BEGIN TRANSACTION;");
for (auto line : sqlCmds)
{
QSqlQuery r = PlainDb::exec(line);
}
PlainDb::exec("COMMIT TRANSACTION;");
dbFile.close();
@ -144,9 +147,8 @@ void EncryptedDb::appendToEncrypted(const QString &sql)
QByteArray encr = Core::getInstance()->encryptData(buffer, Core::ptHistory);
if (encr.size() > 0)
{
encrFile.write(encr);
}
encrFile.flush();
}
@ -161,10 +163,10 @@ bool EncryptedDb::check(const QString &fname)
QByteArray encrChunk = file.read(encryptedChunkSize);
QByteArray buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
if (buf.size() == 0)
{
state = false;
}
} else {
}
else
{
file.close();
file.open(QIODevice::WriteOnly);
}

View File

@ -154,18 +154,18 @@ QByteArray rangedSingleToData(float value, float min, float max, int numberOfBit
numberOfBits -= 8;
if (numberOfBits <= 8)
{
data += (unsigned char)source>>8;
data += (unsigned char)(source>>8);
return data;
}
data += (unsigned char)source>>8;
data += (unsigned char)(source>>8);
numberOfBits -= 8;
if (numberOfBits <= 8)
{
data += (unsigned char)source>>16;
data += (unsigned char)(source>>16);
return data;
}
data += (unsigned char)source>>16;
data += (unsigned char)source>>24;
data += (unsigned char)(source>>16);
data += (unsigned char)(source>>24);
return data;
}

View File

@ -16,10 +16,11 @@
#include "settings.h"
#include "smileypack.h"
#include "src/corestructs.h"
#include "src/core/corestructs.h"
#include "src/misc/db/plaindb.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/widget/gui.h"
#include "src/profilelocker.h"
#ifdef QTOX_PLATFORM_EXT
#include "src/platform/autorun.h"
#endif
@ -54,6 +55,7 @@ Settings& Settings::getInstance()
{
if (!settings)
settings = new Settings();
return *settings;
}
@ -66,9 +68,23 @@ void Settings::switchProfile(const QString& profile)
// If this instance is not main instance previous save did not happen therefore
// we manually set profile again and load profile settings
setCurrentProfile(profile);
loaded = false;
load();
}
QString Settings::genRandomProfileName()
{
QDir dir(getSettingsDirPath());
QString basename = "imported_";
QString randname;
do {
randname = QString().setNum(qrand()*qrand()*qrand(), 16);
randname.truncate(6);
randname = basename + randname;
} while (QFile(dir.filePath(randname)).exists());
return randname;
}
QString Settings::detectProfile()
{
QDir dir(getSettingsDirPath());
@ -83,15 +99,27 @@ QString Settings::detectProfile()
path = dir.filePath(Core::CONFIG_FILE_NAME);
QFile file(path);
if (file.exists())
return path;
{
profile = genRandomProfileName();
setCurrentProfile(profile);
file.rename(profile + Core::TOX_EXT);
return profile;
}
else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data
return path;
{
profile = genRandomProfileName();
setCurrentProfile(profile);
QFile(path).rename(profile + Core::TOX_EXT);
return profile;
}
else
#endif
{
profile = askProfiles();
if (profile.isEmpty())
{
return "";
}
else
{
switchProfile(profile);
@ -100,7 +128,9 @@ QString Settings::detectProfile()
}
}
else
{
return path;
}
}
QList<QString> Settings::searchProfiles()
@ -111,6 +141,7 @@ QList<QString> Settings::searchProfiles()
dir.setNameFilters(QStringList("*.tox"));
for (QFileInfo file : dir.entryInfoList())
out += file.completeBaseName();
return out;
}
@ -153,7 +184,9 @@ void Settings::load()
ps.endGroup();
}
else
{
makeToxPortable = false;
}
QDir dir(getSettingsDirPath());
QString filePath = dir.filePath(FILENAME);
@ -177,7 +210,8 @@ void Settings::load()
useCustomDhtList = true;
qDebug() << "Using custom bootstrap nodes list";
int serverListSize = s.beginReadArray("dhtServerList");
for (int i = 0; i < serverListSize; i ++) {
for (int i = 0; i < serverListSize; i ++)
{
s.setArrayIndex(i);
DhtServer server;
server.name = s.value("name").toString();
@ -189,7 +223,9 @@ void Settings::load()
s.endArray();
}
else
{
useCustomDhtList=false;
}
s.endGroup();
s.beginGroup("General");
@ -203,8 +239,11 @@ void Settings::load()
setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt());
proxyAddr = s.value("proxyAddr", "").toString();
proxyPort = s.value("proxyPort", 0).toInt();
currentProfile = s.value("currentProfile", "").toString();
currentProfileId = makeProfileId(currentProfile);
if (currentProfile.isEmpty())
{
currentProfile = s.value("currentProfile", "").toString();
currentProfileId = makeProfileId(currentProfile);
}
autoAwayTime = s.value("autoAwayTime", 10).toInt();
checkUpdates = s.value("checkUpdates", false).toBool();
showWindow = s.value("showWindow", true).toBool();
@ -227,9 +266,9 @@ void Settings::load()
s.beginGroup("Widgets");
QList<QString> objectNames = s.childKeys();
for (const QString& name : objectNames) {
for (const QString& name : objectNames)
widgetSettings[name] = s.value(name).toByteArray();
}
s.endGroup();
s.beginGroup("GUI");
@ -282,7 +321,8 @@ void Settings::load()
QSettings rcs(":/conf/settings.ini", QSettings::IniFormat);
rcs.beginGroup("DHT Server");
int serverListSize = rcs.beginReadArray("dhtServerList");
for (int i = 0; i < serverListSize; i ++) {
for (int i = 0; i < serverListSize; i ++)
{
rcs.setArrayIndex(i);
DhtServer server;
server.name = rcs.value("name").toString();
@ -297,7 +337,6 @@ void Settings::load()
loaded = true;
if (!currentProfile.isEmpty()) // new profile in Core::switchConfiguration
{
// load from a profile specific friend data list if possible
QString tmp = dir.filePath(currentProfile + ".ini");
@ -357,7 +396,8 @@ void Settings::saveGlobal(QString path)
s.beginGroup("DHT Server");
s.setValue("useCustomList", useCustomDhtList);
s.beginWriteArray("dhtServerList", dhtServerList.size());
for (int i = 0; i < dhtServerList.size(); i ++) {
for (int i = 0; i < dhtServerList.size(); i ++)
{
s.setArrayIndex(i);
s.setValue("name", dhtServerList[i].name);
s.setValue("userId", dhtServerList[i].userId);
@ -398,9 +438,9 @@ void Settings::saveGlobal(QString path)
s.beginGroup("Widgets");
const QList<QString> widgetNames = widgetSettings.keys();
for (const QString& name : widgetNames) {
for (const QString& name : widgetNames)
s.setValue(name, widgetSettings.value(name));
}
s.endGroup();
s.beginGroup("GUI");
@ -505,12 +545,15 @@ QPixmap Settings::getSavedAvatar(const QString &ownerId)
QString filePath = dir.filePath("avatar_"+ownerId.left(64));
if (!QFileInfo(filePath).exists()) // try without truncation, for old self avatars
filePath = dir.filePath("avatar_"+ownerId);
pic.load(filePath);
saveAvatar(pic, ownerId);
QFile::remove(filePath);
}
else
{
pic.load(filePath);
}
return pic;
}
@ -530,6 +573,7 @@ void Settings::saveAvatarHash(const QByteArray& hash, const QString& ownerId)
QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash"));
if (!file.open(QIODevice::WriteOnly))
return;
file.write(hash);
file.close();
}
@ -541,6 +585,7 @@ QByteArray Settings::getAvatarHash(const QString& ownerId)
QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash"));
if (!file.open(QIODevice::ReadOnly))
return QByteArray();
QByteArray out = file.readAll();
file.close();
return out;
@ -840,6 +885,7 @@ void Settings::setAutoAwayTime(int newValue)
{
if (newValue < 0)
newValue = 10;
autoAwayTime = newValue;
}
@ -849,9 +895,7 @@ QString Settings::getAutoAcceptDir(const ToxID& id) const
auto it = friendLst.find(key);
if (it != friendLst.end())
{
return it->autoAcceptDir;
}
return QString();
}
@ -1110,9 +1154,7 @@ QString Settings::getFriendAdress(const QString &publicKey) const
QString key = ToxID::fromString(publicKey).publicKey;
auto it = friendLst.find(key);
if (it != friendLst.end())
{
return it->addr;
}
return QString();
}
@ -1124,7 +1166,9 @@ void Settings::updateFriendAdress(const QString &newAddr)
if (it != friendLst.end())
{
it->addr = newAddr;
} else {
}
else
{
friendProp fp;
fp.addr = newAddr;
fp.alias = "";
@ -1138,9 +1182,7 @@ QString Settings::getFriendAlias(const ToxID &id) const
QString key = id.publicKey;
auto it = friendLst.find(key);
if (it != friendLst.end())
{
return it->alias;
}
return QString();
}
@ -1152,7 +1194,9 @@ void Settings::setFriendAlias(const ToxID &id, const QString &alias)
if (it != friendLst.end())
{
it->alias = alias;
} else {
}
else
{
friendProp fp;
fp.addr = key;
fp.alias = alias;

View File

@ -256,6 +256,9 @@ public:
void save(QString path, bool writePersonal = true);
void load();
private:
static QString genRandomProfileName();
private:
static Settings* settings;

View File

@ -1,5 +1,5 @@
#include "nexus.h"
#include "core.h"
#include "src/core/core.h"
#include "misc/settings.h"
#include "video/camera.h"
#include "widget/gui.h"
@ -40,6 +40,7 @@ void Nexus::start()
{
if (started)
return;
qDebug() << "Nexus: Starting up";
// Setup the environment
@ -47,6 +48,7 @@ void Nexus::start()
qRegisterMetaType<vpx_image>("vpx_image");
qRegisterMetaType<uint8_t>("uint8_t");
qRegisterMetaType<uint16_t>("uint16_t");
qRegisterMetaType<uint32_t>("uint32_t");
qRegisterMetaType<const int16_t*>("const int16_t*");
qRegisterMetaType<int32_t>("int32_t");
qRegisterMetaType<int64_t>("int64_t");
@ -77,6 +79,12 @@ void Nexus::start()
#endif
GUI::getInstance();
// Zetok protection
// There are small instants on startup during which no
// profile is loaded but the GUI could still receive events,
// e.g. between two modal windows. Disable the GUI to prevent that.
GUI::setEnabled(false);
// Connections
#ifdef Q_OS_ANDROID
connect(core, &Core::connected, androidgui, &AndroidGUI::onConnected);
@ -119,8 +127,8 @@ void Nexus::start()
connect(core, &Core::blockingClearContacts, widget, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
connect(core, &Core::friendTypingChanged, widget, &Widget::onFriendTypingChanged);
connect(core, SIGNAL(messageSentResult(int,QString,int)), widget, SLOT(onMessageSendResult(int,QString,int)));
connect(core, SIGNAL(groupSentResult(int,QString,int)), widget, SLOT(onGroupSendResult(int,QString,int)));
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);
@ -138,6 +146,7 @@ Nexus& Nexus::getInstance()
{
if (!nexus)
nexus = new Nexus;
return *nexus;
}
@ -167,10 +176,11 @@ QString Nexus::getSupportedImageFilter()
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));
}
bool Nexus::isFilePathWritable(const QString& filepath)
bool Nexus::tryRemoveFile(const QString& filepath)
{
QFile tmp(filepath);
bool writable = tmp.open(QIODevice::WriteOnly);

View File

@ -23,7 +23,7 @@ public:
static AndroidGUI* getAndroidGUI(); ///< Will return 0 if not started
static Widget* getDesktopGUI(); ///< Will return 0 if not started
static QString getSupportedImageFilter();
static bool isFilePathWritable(const QString& filepath); // WARNING: Tests by brute force, i.e. removes the file in question
static bool tryRemoveFile(const QString& filepath); ///< Dangerous way to find out if a path is writable
private:
explicit Nexus(QObject *parent = 0);

View File

@ -18,7 +18,7 @@
#include "src/friend.h"
#include "src/historykeeper.h"
#include "src/misc/settings.h"
#include "src/core.h"
#include "src/core/core.h"
#include <QMutexLocker>
#include <QTimer>
@ -91,6 +91,7 @@ void OfflineMsgEngine::deliverOfflineMsgs()
rec = Core::getInstance()->sendAction(f->getFriendID(), messageText);
else
rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText);
registerReceipt(rec, iter.key(), iter.value().msg);
}
}

View File

@ -24,7 +24,7 @@
#include <QMap>
#include "src/chatlog/chatmessage.h"
struct Friend;
class Friend;
class QTimer;
class OfflineMsgEngine : public QObject

99
src/profilelocker.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "profilelocker.h"
#include "src/misc/settings.h"
#include <QDir>
#include <QDebug>
using namespace std;
unique_ptr<QLockFile> ProfileLocker::lockfile;
QString ProfileLocker::curLockName;
QString ProfileLocker::lockPathFromName(const QString& name)
{
return Settings::getInstance().getSettingsDirPath()+'/'+name+".lock";
}
bool ProfileLocker::isLockable(QString profile)
{
// If we already have the lock, it's definitely lockable
if (lockfile && curLockName == profile)
return true;
QLockFile newLock(lockPathFromName(profile));
return newLock.tryLock();
}
bool ProfileLocker::lock(QString profile)
{
if (lockfile && curLockName == profile)
return true;
QLockFile* newLock = new QLockFile(lockPathFromName(profile));
if (!newLock->tryLock())
{
delete newLock;
return false;
}
unlock();
lockfile.reset(newLock);
curLockName = profile;
return true;
}
void ProfileLocker::unlock()
{
if (!lockfile)
return;
lockfile->unlock();
delete lockfile.release();
lockfile = nullptr;
curLockName.clear();
}
void ProfileLocker::clearAllLocks()
{
qDebug() << "ProfileLocker::clearAllLocks: Wiping out all lock files";
if (lockfile)
unlock();
QDir dir(Settings::getInstance().getSettingsDirPath());
dir.setFilter(QDir::Files);
dir.setNameFilters({"*.lock"});
QFileInfoList files = dir.entryInfoList();
for (QFileInfo fileInfo : files)
{
QFile file(fileInfo.absoluteFilePath());
file.remove();
}
}
void ProfileLocker::assertLock()
{
if (!lockfile)
{
qCritical() << "ProfileLocker::assertLock: We don't seem to own any lock!";
deathByBrokenLock();
}
if (!QFile(lockPathFromName(curLockName)).exists())
{
QString tmp = curLockName;
unlock();
if (lock(tmp))
{
qCritical() << "ProfileLocker::assertLock: Lock file was lost, but could be restored";
}
else
{
qCritical() << "ProfileLocker::assertLock: Lock file was lost, and could *NOT* be restored";
deathByBrokenLock();
}
}
}
void ProfileLocker::deathByBrokenLock()
{
qCritical() << "ProfileLocker: Lock is *BROKEN*, exiting immediately";
abort();
}

46
src/profilelocker.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef PROFILELOCKER_H
#define PROFILELOCKER_H
#include <QLockFile>
#include <memory>
/// Locks a Tox profile so that multiple instances can not use the same profile.
/// Only one lock can be acquired at the same time, which means
/// that there is little need for manually unlocking.
/// The current lock will expire if you exit or acquire a new one.
class ProfileLocker
{
private:
ProfileLocker()=delete;
public:
/// Checks if a profile is currently locked by *another* instance
/// If we own the lock, we consider it lockable
/// There is no guarantee that the result will still be valid by the
/// time it is returned, this is provided on a best effort basis
static bool isLockable(QString profile);
/// Tries to acquire the lock on a profile, will not block
/// Returns true if we already own the lock
static bool lock(QString profile);
/// Releases the lock on the current profile
static void unlock();
/// Releases all locks on all profiles
/// DO NOT call unless all we're the only qTox instance
/// and we don't hold any lock yet.
static void clearAllLocks();
/// Check that we actually own the lock
/// In case the file was deleted on disk, restore it
/// If we can't get a lock, exit qTox immediately
/// If we never had a lock in the firt place, exit immediately
static void assertLock();
private:
static QString lockPathFromName(const QString& name);
static void deathByBrokenLock(); ///< Print an error then exit immediately
private:
static std::unique_ptr<QLockFile> lockfile;
static QString curLockName;
};
#endif // PROFILELOCKER_H

View File

@ -24,7 +24,7 @@
#include <tox/tox.h>
#include <tox/toxdns.h>
#define TOX_ID_LENGTH 2*TOX_FRIEND_ADDRESS_SIZE
#define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE
const ToxDNS::tox3_server ToxDNS::pinnedServers[]
{
@ -58,35 +58,45 @@ QByteArray ToxDNS::fetchLastTextRecord(const QString& record, bool silent)
qApp->processEvents();
QThread::msleep(100);
}
if (timeout >= 30) {
if (timeout >= 30)
{
dns.abort();
if (!silent)
showWarning(tr("The connection timed out","The DNS gives the Tox ID associated to toxme.se addresses"));
return result;
}
if (dns.error() == QDnsLookup::NotFoundError) {
if (dns.error() == QDnsLookup::NotFoundError)
{
if (!silent)
showWarning(tr("This address does not exist","The DNS gives the Tox ID associated to toxme.se addresses"));
return result;
}
else if (dns.error() != QDnsLookup::NoError) {
else if (dns.error() != QDnsLookup::NoError)
{
if (!silent)
showWarning(tr("Error while looking up DNS","The DNS gives the Tox ID associated to toxme.se addresses"));
return result;
}
const QList<QDnsTextRecord> textRecords = dns.textRecords();
if (textRecords.isEmpty()) {
if (textRecords.isEmpty())
{
if (!silent)
showWarning(tr("No text record found", "Error with the DNS"));
return result;
}
const QList<QByteArray> textRecordValues = textRecords.last().values();
if (textRecordValues.length() != 1) {
if (textRecordValues.length() != 1)
{
if (!silent)
showWarning(tr("Unexpected number of values in text record", "Error with the DNS"));
return result;
}
@ -104,7 +114,8 @@ QString ToxDNS::queryTox1(const QString& record, bool silent)
// Check toxdns protocol version
int verx = entry.indexOf("v=");
if (verx) {
if (verx)
{
verx += 2;
int verend = entry.indexOf(';', verx);
if (verend)
@ -114,6 +125,7 @@ QString ToxDNS::queryTox1(const QString& record, bool silent)
{
if (!silent)
showWarning(tr("The version of Tox DNS used by this server is not supported", "Error with the DNS"));
return toxId;
}
}
@ -121,23 +133,29 @@ QString ToxDNS::queryTox1(const QString& record, bool silent)
// Get the tox id
int idx = entry.indexOf("id=");
if (idx < 0) {
if (idx < 0)
{
if (!silent)
showWarning(tr("The DNS lookup does not contain any Tox ID", "Error with the DNS"));
return toxId;
}
idx += 3;
if (entry.length() < idx + static_cast<int>(TOX_ID_LENGTH)) {
if (entry.length() < idx + static_cast<int>(TOX_HEX_ID_LENGTH))
{
if (!silent)
showWarning(tr("The DNS lookup does not contain a valid Tox ID", "Error with the DNS"));
return toxId;
}
toxId = entry.mid(idx, TOX_ID_LENGTH);
if (!ToxID::isToxId(toxId)) {
toxId = entry.mid(idx, TOX_HEX_ID_LENGTH);
if (!ToxID::isToxId(toxId))
{
if (!silent)
showWarning(tr("The DNS lookup does not contain a valid Tox ID", "Error with the DNS"));
return toxId;
}
@ -178,7 +196,8 @@ QString ToxDNS::queryTox3(const tox3_server& server, const QString &record, bool
// Check toxdns protocol version
verx = entry.indexOf("v=");
if (verx!=-1) {
if (verx!=-1)
{
verx += 2;
int verend = entry.indexOf(';', verx);
if (verend!=-1)
@ -194,14 +213,15 @@ QString ToxDNS::queryTox3(const tox3_server& server, const QString &record, bool
// Get and decrypt the tox id
idx = entry.indexOf("id=");
if (idx < 0) {
if (idx < 0)
{
qWarning() << "queryTox3: Server "<<server.name<<" returned an empty id, using tox1 as a fallback";
goto fallbackOnTox1;
}
idx += 3;
id = entry.mid(idx).toUtf8();
uint8_t toxId[TOX_FRIEND_ADDRESS_SIZE];
uint8_t toxId[TOX_ADDRESS_SIZE];
toxIdSize = tox_decrypt_dns3_TXT(tox_dns3, toxId, (uint8_t*)id.data(), id.size(), request_id);
if (toxIdSize < 0) // We can always fallback on tox1 if toxdns3 fails
{
@ -217,6 +237,7 @@ QString ToxDNS::queryTox3(const tox3_server& server, const QString &record, bool
fallbackOnTox1:
if (tox_dns3)
tox_dns3_kill(tox_dns3);
#if TOX1_SILENT_FALLBACK
toxIdStr = queryTox1(record, silent);
#elif TOX1_ASK_FALLBACK
@ -226,6 +247,7 @@ Should tox1 be used anyway?\n\
If unsure, press No, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
if (btn == QMessageBox::Yes)
queryTox1(record, silent);
#endif
return toxIdStr;
}
@ -234,12 +256,17 @@ ToxID ToxDNS::resolveToxAddress(const QString &address, bool silent)
{
ToxID toxId;
if (address.isEmpty()) {
if (address.isEmpty())
{
return toxId;
} else if (ToxID::isToxId(address)) {
}
else if (ToxID::isToxId(address))
{
toxId = ToxID::fromString(address);
return toxId;
} else {
}
else
{
// If we're querying one of our pinned server, do a tox3 request directly
QString servname = address.mid(address.indexOf('@')+1);
for (const ToxDNS::tox3_server& pin : ToxDNS::pinnedServers)
@ -272,6 +299,7 @@ Should tox1 be used anyway?\n\
If unsure, press No, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
if (btn == QMessageBox::Ok)
toxId = ToxID::fromString(queryTox1(address, silent));
#else
return toxId;
#endif

View File

@ -18,7 +18,7 @@
#ifndef QTOXDNS_H
#define QTOXDNS_H
#include "src/corestructs.h"
#include "src/core/corestructs.h"
#include <QDnsLookup>
#include <QObject>

View File

@ -1,5 +1,5 @@
#include "toxme.h"
#include "core.h"
#include "src/core/core.h"
#include <QtDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
@ -69,16 +69,19 @@ ToxID Toxme::lookup(QString address)
const int index = response.indexOf(pattern);
if (index == -1)
return id;
response = response.mid(index+pattern.size());
const int idStart = response.indexOf('"');
if (idStart == -1)
return id;
response = response.mid(idStart+1);
const int idEnd = response.indexOf('"');
if (idEnd == -1)
return id;
response.truncate(idEnd);
id = ToxID::fromString(response);
@ -93,17 +96,20 @@ int Toxme::extractError(QString json)
const int index = json.indexOf(pattern);
if (index == -1)
return INT_MIN;
json = json.mid(index+pattern.size());
const int end = json.indexOf('}');
if (end == -1)
return INT_MIN;
json.truncate(end);
bool ok;
int r = json.toInt(&ok);
if (!ok)
return INT_MIN;
return r;
}

View File

@ -4,7 +4,7 @@
#include <QString>
#include <QMutex>
#include <memory>
#include "corestructs.h"
#include "src/core/corestructs.h"
class QNetworkAccessManager;

View File

@ -37,12 +37,14 @@ CameraWorker::~CameraWorker()
void CameraWorker::onStart()
{
clock = new QTimer(this);
clock->setSingleShot(false);
clock->setInterval(1000/60);
connect(clock, &QTimer::timeout, this, &CameraWorker::doWork);
if (!clock)
{
clock = new QTimer(this);
clock->setSingleShot(false);
clock->setInterval(1000/60);
connect(clock, &QTimer::timeout, this, &CameraWorker::doWork);
}
emit started();
}

View File

@ -2,7 +2,7 @@
#include "ui_android.h"
#include "friendlistwidget.h"
#include "maskablepixmapwidget.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/friend.h"
#include "src/friendlist.h"
#include "src/group.h"

View File

@ -1,7 +1,7 @@
#ifndef ANDROIDGUI_H
#define ANDROIDGUI_H
#include "src/corestructs.h"
#include "src/core/corestructs.h"
#include <QWidget>
class MaskablePixmapWidget;

View File

@ -23,7 +23,7 @@
#include <tox/tox.h>
#include "ui_mainwindow.h"
#include "src/nexus.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/cdata.h"
#include "src/toxdns.h"
#include "src/misc/settings.h"
@ -87,16 +87,22 @@ void AddFriendForm::onSendTriggered()
{
QString id = toxId.text().trimmed();
if (id.isEmpty()) {
if (id.isEmpty())
{
GUI::showWarning(tr("Couldn't add friend"), tr("Please fill in a valid Tox ID","Tox ID of the friend you're sending a friend request to"));
} else if (ToxID::isToxId(id)) {
}
else if (ToxID::isToxId(id))
{
if (id.toUpper() == Core::getInstance()->getSelfId().toString().toUpper())
GUI::showWarning(tr("Couldn't add friend"), tr("You can't add yourself as a friend!","When trying to add your own Tox ID as friend"));
else
emit friendRequested(id, getMessage());
this->toxId.clear();
this->message.clear();
} else {
}
else
{
if (Settings::getInstance().getProxyType() != ProxyType::ptNone)
{
QMessageBox::StandardButton btn = QMessageBox::warning(main, "qTox", tr("qTox needs to use the Tox DNS, but can't do it through a proxy.\n\
@ -123,7 +129,8 @@ void AddFriendForm::setIdFromClipboard()
{
QClipboard* clipboard = QApplication::clipboard();
QString id = clipboard->text().trimmed();
if (Core::getInstance()->isReady() && !id.isEmpty() && ToxID::isToxId(id)) {
if (Core::getInstance()->isReady() && !id.isEmpty() && ToxID::isToxId(id))
{
if (!ToxID::fromString(id).isMine())
toxId.setText(id);
}

View File

@ -24,7 +24,7 @@
#include <QDragEnterEvent>
#include <QBitmap>
#include "chatform.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/friend.h"
#include "src/historykeeper.h"
#include "src/misc/style.h"
@ -58,7 +58,7 @@ ChatForm::ChatForm(Friend* chatFriend)
statusMessageLabel->setFont(Style::getFont(Style::Medium));
statusMessageLabel->setMinimumHeight(Style::getFont(Style::Medium).pixelSize());
statusMessageLabel->setTextFormat(Qt::PlainText);
callConfirm = nullptr;
offlineEngine = new OfflineMsgEngine(f);
@ -89,7 +89,10 @@ ChatForm::ChatForm(Friend* chatFriend)
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
connect(this, SIGNAL(chatAreaCleared()), getOfflineMsgEngine(), SLOT(removeAllReciepts()));
connect(&typingTimer, &QTimer::timeout, this, [=]{Core::getInstance()->sendTyping(f->getFriendID(), false);});
connect(&typingTimer, &QTimer::timeout, this, [=]{
Core::getInstance()->sendTyping(f->getFriendID(), false);
isTyping = false;
} );
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig) {
if (text != orig) emit aliasChanged(text);
} );
@ -116,6 +119,8 @@ void ChatForm::onSendTriggered()
if (msg.isEmpty())
return;
msgEdit->setLastMessage(msg); //set last message only when sending it
bool isAction = msg.startsWith("/me ");
if (isAction)
msg = msg = msg.right(msg.length() - 4);
@ -123,8 +128,6 @@ void ChatForm::onSendTriggered()
QList<CString> splittedMsg = Core::splitMessage(msg, TOX_MAX_MESSAGE_LENGTH);
QDateTime timestamp = QDateTime::currentDateTime();
msgEdit->setLastMessage(msg); //set last message only when sending it
bool status = !Settings::getInstance().getFauxOfflineMessaging();
for (CString& c_msg : splittedMsg)
@ -157,6 +160,7 @@ void ChatForm::onTextEditChanged()
{
if (isTyping)
Core::getInstance()->sendTyping(f->getFriendID(), false);
isTyping = false;
return;
}
@ -178,6 +182,7 @@ void ChatForm::onAttachClicked()
QStringList paths = QFileDialog::getOpenFileNames(0,tr("Send a file"));
if (paths.isEmpty())
return;
for (QString path : paths)
{
QFile file(path);
@ -254,7 +259,7 @@ void ChatForm::onFileRecvRequest(ToxFile file)
}
}
void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
void ChatForm::onAvInvite(uint32_t FriendId, int CallId, bool video)
{
if (FriendId != f->getFriendID())
return;
@ -269,6 +274,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
callConfirm = new CallConfirmWidget(videoButton);
if (isVisible())
callConfirm->show();
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
@ -283,6 +289,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
callConfirm = new CallConfirmWidget(callButton);
if (isVisible())
callConfirm->show();
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
@ -294,7 +301,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
}
callButton->style()->polish(callButton);
videoButton->style()->polish(videoButton);
insertChatMessage(ChatMessage::createChatInfoMessage(tr("%1 calling").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime()));
Widget* w = Widget::getInstance();
@ -306,7 +313,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
}
}
void ChatForm::onAvStart(int FriendId, int CallId, bool video)
void ChatForm::onAvStart(uint32_t FriendId, int CallId, bool video)
{
if (FriendId != f->getFriendID())
return;
@ -353,11 +360,11 @@ void ChatForm::onAvStart(int FriendId, int CallId, bool video)
this, SLOT(onMicMuteToggle()));
connect(volButton, SIGNAL(clicked()),
this, SLOT(onVolMuteToggle()));
startCounter();
}
void ChatForm::onAvCancel(int FriendId, int)
void ChatForm::onAvCancel(uint32_t FriendId, int)
{
if (FriendId != f->getFriendID())
return;
@ -371,11 +378,11 @@ void ChatForm::onAvCancel(int FriendId, int)
stopCounter();
netcam->hide();
addSystemInfoMessage(tr("%1 stopped calling").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime());
}
void ChatForm::onAvEnd(int FriendId, int)
void ChatForm::onAvEnd(uint32_t FriendId, int)
{
if (FriendId != f->getFriendID())
return;
@ -390,8 +397,8 @@ void ChatForm::onAvEnd(int FriendId, int)
netcam->hide();
}
void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
{
void ChatForm::onAvRinging(uint32_t FriendId, int CallId, bool video)
{
if (FriendId != f->getFriendID())
return;
@ -422,11 +429,11 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
connect(callButton, SIGNAL(clicked()),
this, SLOT(onCancelCallTriggered()));
}
addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime());
}
void ChatForm::onAvStarting(int FriendId, int CallId, bool video)
void ChatForm::onAvStarting(uint32_t FriendId, int CallId, bool video)
{
if (FriendId != f->getFriendID())
return;
@ -457,11 +464,11 @@ void ChatForm::onAvStarting(int FriendId, int CallId, bool video)
videoButton->setToolTip("");
connect(callButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
}
startCounter();
}
void ChatForm::onAvEnding(int FriendId, int)
void ChatForm::onAvEnding(uint32_t FriendId, int)
{
if (FriendId != f->getFriendID())
return;
@ -473,11 +480,11 @@ void ChatForm::onAvEnding(int FriendId, int)
enableCallButtons();
stopCounter();
netcam->hide();
}
void ChatForm::onAvRequestTimeout(int FriendId, int)
void ChatForm::onAvRequestTimeout(uint32_t FriendId, int)
{
if (FriendId != f->getFriendID())
return;
@ -489,11 +496,11 @@ void ChatForm::onAvRequestTimeout(int FriendId, int)
enableCallButtons();
stopCounter();
netcam->hide();
}
void ChatForm::onAvPeerTimeout(int FriendId, int)
void ChatForm::onAvPeerTimeout(uint32_t FriendId, int)
{
if (FriendId != f->getFriendID())
return;
@ -502,14 +509,14 @@ void ChatForm::onAvPeerTimeout(int FriendId, int)
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
stopCounter();
netcam->hide();
}
void ChatForm::onAvRejected(int FriendId, int)
void ChatForm::onAvRejected(uint32_t FriendId, int)
{
if (FriendId != f->getFriendID())
return;
@ -520,13 +527,13 @@ void ChatForm::onAvRejected(int FriendId, int)
callConfirm = nullptr;
enableCallButtons();
insertChatMessage(ChatMessage::createChatInfoMessage(tr("Call rejected"), ChatMessage::INFO, QDateTime::currentDateTime()));
netcam->hide();
}
void ChatForm::onAvMediaChange(int FriendId, int CallId, bool video)
void ChatForm::onAvMediaChange(uint32_t FriendId, int CallId, bool video)
{
if (FriendId != f->getFriendID() || CallId != callId)
return;
@ -534,13 +541,9 @@ void ChatForm::onAvMediaChange(int FriendId, int CallId, bool video)
qDebug() << "onAvMediaChange";
if (video)
{
netcam->show(Core::getInstance()->getVideoSourceFromCall(CallId), f->getDisplayedName());
}
else
{
netcam->hide();
}
}
void ChatForm::onAnswerCallTriggered()
@ -564,10 +567,8 @@ void ChatForm::onHangupCallTriggered()
//Fixes an OS X bug with ending a call while in full screen
if(netcam->isFullScreen())
{
netcam->showNormal();
}
audioInputFlag = false;
audioOutputFlag = false;
emit hangupCall(callId);
@ -588,9 +589,8 @@ void ChatForm::onRejectCallTriggered()
audioInputFlag = false;
audioOutputFlag = false;
emit rejectCall(callId);
enableCallButtons();
enableCallButtons();
}
void ChatForm::onCallTriggered()
@ -601,7 +601,7 @@ void ChatForm::onCallTriggered()
audioOutputFlag = true;
callButton->disconnect();
videoButton->disconnect();
emit startCall(f->getFriendID());
emit startCall(f->getFriendID(), false);
}
void ChatForm::onVideoCallTriggered()
@ -612,10 +612,10 @@ void ChatForm::onVideoCallTriggered()
audioOutputFlag = true;
callButton->disconnect();
videoButton->disconnect();
emit startVideoCall(f->getFriendID(), true);
emit startCall(f->getFriendID(), true);
}
void ChatForm::onAvCallFailed(int FriendId)
void ChatForm::onAvCallFailed(uint32_t FriendId)
{
if (FriendId != f->getFriendID())
return;
@ -631,7 +631,7 @@ void ChatForm::onAvCallFailed(int FriendId)
void ChatForm::onCancelCallTriggered()
{
qDebug() << "onCancelCallTriggered";
enableCallButtons();
netcam->hide();
@ -641,19 +641,19 @@ void ChatForm::onCancelCallTriggered()
void ChatForm::enableCallButtons()
{
qDebug() << "enableCallButtons";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("grey");
micButton->style()->polish(micButton);
micButton->setToolTip("");
micButton->disconnect();
micButton->disconnect();
volButton->setObjectName("grey");
volButton->style()->polish(volButton);
volButton->setToolTip("");
volButton->disconnect();
callButton->setObjectName("grey");
callButton->style()->polish(callButton);
callButton->setToolTip("");
@ -662,7 +662,7 @@ void ChatForm::enableCallButtons()
videoButton->style()->polish(videoButton);
videoButton->setToolTip("");
videoButton->disconnect();
if(disableCallButtonsTimer == nullptr)
{
disableCallButtonsTimer = new QTimer();
@ -671,7 +671,7 @@ void ChatForm::enableCallButtons()
disableCallButtonsTimer->start(1500); // 1.5sec
qDebug() << "timer started!!";
}
}
void ChatForm::onEnableCallButtons()
@ -686,7 +686,7 @@ void ChatForm::onEnableCallButtons()
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
videoButton->setToolTip(tr("Start video call"));
connect(callButton, SIGNAL(clicked()),
this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()),
@ -737,7 +737,7 @@ void ChatForm::onVolMuteToggle()
}
}
void ChatForm::onFileSendFailed(int FriendId, const QString &fname)
void ChatForm::onFileSendFailed(uint32_t FriendId, const QString &fname)
{
if (FriendId != f->getFriendID())
return;
@ -745,7 +745,7 @@ void ChatForm::onFileSendFailed(int FriendId, const QString &fname)
addSystemInfoMessage(tr("Failed to send file \"%1\"").arg(fname), ChatMessage::ERROR, QDateTime::currentDateTime());
}
void ChatForm::onAvatarChange(int FriendId, const QPixmap &pic)
void ChatForm::onAvatarChange(uint32_t FriendId, const QPixmap &pic)
{
if (FriendId != f->getFriendID())
return;
@ -787,7 +787,7 @@ void ChatForm::dropEvent(QDropEvent *ev)
}
}
void ChatForm::onAvatarRemoved(int FriendId)
void ChatForm::onAvatarRemoved(uint32_t FriendId)
{
if (FriendId != f->getFriendID())
return;
@ -806,6 +806,7 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
{
if (earliestMessage < since)
return;
if (earliestMessage < now)
{
now = earliestMessage;
@ -826,7 +827,7 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
// Show the date every new day
QDateTime msgDateTime = it.timestamp.toLocalTime();
QDate msgDate = msgDateTime.date();
if (msgDate > lastDate)
{
lastDate = msgDate;
@ -862,7 +863,7 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
rec = Core::getInstance()->sendMessage(f->getFriendID(), msg->toString());
else
rec = Core::getInstance()->sendAction(f->getFriendID(), msg->toString());
getOfflineMsgEngine()->registerReceipt(rec, it.id, msg);
}
}
@ -912,7 +913,7 @@ void ChatForm::stopCounter()
callDurationTimer->stop();
callDuration->setText("");
callDuration->hide();
delete callDurationTimer;
callDurationTimer = nullptr;
}
@ -933,13 +934,13 @@ QString ChatForm::secondsToDHMS(quint32 duration)
duration /= 60;
int hours = (int) (duration % 24);
int days = (int) (duration / 24);
if (minutes == 0)
return cD + res.sprintf("%02ds", seconds);
if (hours == 0 && days == 0)
return cD + res.sprintf("%02dm %02ds", minutes, seconds);
if (days == 0)
return cD + res.sprintf("%02dh %02dm %02ds", hours, minutes, seconds);
//I assume no one will ever have call longer than ~30days

View File

@ -18,14 +18,14 @@
#define CHATFORM_H
#include "genericchatform.h"
#include "src/corestructs.h"
#include "src/core/corestructs.h"
#include <QSet>
#include <QLabel>
#include <QTimer>
#include <QElapsedTimer>
struct Friend;
class Friend;
class FileTransferInstance;
class NetCamView;
class QPixmap;
@ -50,12 +50,11 @@ public:
virtual void show(Ui::MainWindow &ui);
signals:
void sendFile(int32_t friendId, QString, QString, long long);
void startCall(int friendId);
void startVideoCall(int friendId, bool video);
void sendFile(uint32_t friendId, QString, QString, long long);
void startCall(uint32_t FriendId, bool video);
void answerCall(int callId);
void hangupCall(int callId);
void cancelCall(int callId, int friendId);
void cancelCall(int callId, uint32_t FriendId);
void rejectCall(int callId);
void micMuteToggle(int callId);
void volMuteToggle(int callId);
@ -64,22 +63,22 @@ signals:
public slots:
void startFileSend(ToxFile file);
void onFileRecvRequest(ToxFile file);
void onAvInvite(int FriendId, int CallId, bool video);
void onAvStart(int FriendId, int CallId, bool video);
void onAvCancel(int FriendId, int CallId);
void onAvEnd(int FriendId, int CallId);
void onAvRinging(int FriendId, int CallId, bool video);
void onAvStarting(int FriendId, int CallId, bool video);
void onAvEnding(int FriendId, int CallId);
void onAvRequestTimeout(int FriendId, int CallId);
void onAvPeerTimeout(int FriendId, int CallId);
void onAvMediaChange(int FriendId, int CallId, bool video);
void onAvCallFailed(int FriendId);
void onAvRejected(int FriendId, int CallId);
void onAvInvite(uint32_t FriendId, int CallId, bool video);
void onAvStart(uint32_t FriendId, int CallId, bool video);
void onAvCancel(uint32_t FriendId, int CallId);
void onAvEnd(uint32_t FriendId, int CallId);
void onAvRinging(uint32_t FriendId, int CallId, bool video);
void onAvStarting(uint32_t FriendId, int CallId, bool video);
void onAvEnding(uint32_t FriendId, int CallId);
void onAvRequestTimeout(uint32_t FriendId, int CallId);
void onAvPeerTimeout(uint32_t FriendId, int CallId);
void onAvMediaChange(uint32_t FriendId, int CallId, bool video);
void onAvCallFailed(uint32_t FriendId);
void onAvRejected(uint32_t FriendId, int CallId);
void onMicMuteToggle();
void onVolMuteToggle();
void onAvatarChange(int FriendId, const QPixmap& pic);
void onAvatarRemoved(int FriendId);
void onAvatarChange(uint32_t FriendId, const QPixmap& pic);
void onAvatarRemoved(uint32_t FriendId);
private slots:
void onSendTriggered();
@ -91,7 +90,7 @@ private slots:
void onHangupCallTriggered();
void onCancelCallTriggered();
void onRejectCallTriggered();
void onFileSendFailed(int FriendId, const QString &fname);
void onFileSendFailed(uint32_t FriendId, const QString &fname);
void onLoadHistory();
void onUpdateTime();
void onEnableCallButtons();

View File

@ -29,7 +29,7 @@
#include "src/misc/settings.h"
#include "src/widget/tool/chattextedit.h"
#include "src/widget/maskablepixmapwidget.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/grouplist.h"
#include "src/group.h"
#include "src/friendlist.h"
@ -366,7 +366,9 @@ QString GenericChatForm::resolveToxID(const ToxID &id)
if (f)
{
return f->getDisplayedName();
} else {
}
else
{
for (auto it : GroupList::getAllGroups())
{
QString res = it->resolveToxID(id);

View File

@ -21,7 +21,7 @@
#include <QPoint>
#include <QDateTime>
#include <QMenu>
#include "src/corestructs.h"
#include "src/core/corestructs.h"
#include "src/chatlog/chatmessage.h"
// Spacing in px inserted when the author of the last message changes
@ -60,8 +60,8 @@ public:
ChatLog* getChatLog() const;
signals:
void sendMessage(int, QString);
void sendAction(int, QString);
void sendMessage(uint32_t, QString);
void sendAction(uint32_t, QString);
void chatAreaCleared();
public slots:

View File

@ -21,7 +21,7 @@
#include "src/widget/tool/chattextedit.h"
#include "src/widget/croppinglabel.h"
#include "src/widget/maskablepixmapwidget.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/style.h"
#include <QPushButton>
#include <QMimeData>
@ -64,11 +64,10 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
namesListLayout = new FlowLayout(0,5,0);
QStringList names(group->getPeerList());
QLabel *l;
for (const QString& name : names)
{
l = new QLabel(name);
QLabel *l = new QLabel(name);
l->setTextFormat(Qt::PlainText);
namesListLayout->addWidget(l);
}
@ -110,10 +109,17 @@ void GroupChatForm::onSendTriggered()
emit sendAction(group->getGroupId(), msg);
}
else
{
emit sendMessage(group->getGroupId(), msg);
}
}
else
addSelfMessage(msg, msg.startsWith("/me "), QDateTime::currentDateTime(), true);
{
if (msg.startsWith("/me "))
addSelfMessage(msg.right(msg.length() - 4), true, QDateTime::currentDateTime(), true);
else
addSelfMessage(msg, false, QDateTime::currentDateTime(), true);
}
}
void GroupChatForm::onUserListChanged()
@ -150,6 +156,7 @@ void GroupChatForm::onUserListChanged()
QLabel* label = orderizer[names[i]];
if (i != nNames - 1)
label->setText(label->text() + ", ");
namesListLayout->addWidget(label);
}

View File

@ -14,7 +14,7 @@
See the COPYING file for more details.
*/
#include "src/core.h"
#include "src/core/core.h"
#include "src/nexus.h"
#include "ui_profileform.h"
#include "profileform.h"
@ -27,6 +27,7 @@
#include "src/widget/gui.h"
#include "src/historykeeper.h"
#include "src/misc/style.h"
#include "src/profilelocker.h"
#include <QLabel>
#include <QLineEdit>
#include <QGroupBox>
@ -41,6 +42,7 @@ void ProfileForm::refreshProfiles()
bodyUI->profiles->clear();
for (QString profile : Settings::getInstance().searchProfiles())
bodyUI->profiles->addItem(profile);
QString current = Settings::getInstance().getCurrentProfile();
if (current != "")
bodyUI->profiles->setCurrentText(current);
@ -183,6 +185,7 @@ void ProfileForm::setToxId(const QString& id)
qr = new QRWidget();
qr->setQRData("tox:"+id);
bodyUI->qrCode->setPixmap(QPixmap::fromImage(qr->getImage()->scaledToWidth(150)));
refreshProfiles();
}
void ProfileForm::onAvatarClicked()
@ -193,6 +196,7 @@ void ProfileForm::onAvatarClicked()
Nexus::getSupportedImageFilter());
if (filename.isEmpty())
return;
QFile file(filename);
file.open(QIODevice::ReadOnly);
if (!file.isOpen())
@ -214,22 +218,7 @@ void ProfileForm::onAvatarClicked()
pic.save(&buffer, "PNG");
buffer.close();
if (bytes.size() >= TOX_AVATAR_MAX_DATA_LENGTH)
{
pic = pic.scaled(64,64, Qt::KeepAspectRatio, Qt::SmoothTransformation);
bytes.clear();
buffer.open(QIODevice::WriteOnly);
pic.save(&buffer, "PNG");
buffer.close();
}
if (bytes.size() >= TOX_AVATAR_MAX_DATA_LENGTH)
{
GUI::showError(tr("Error"), tr("This image is too big"));
return;
}
Nexus::getCore()->setAvatar(TOX_AVATAR_FORMAT_PNG, bytes);
Nexus::getCore()->setAvatar(bytes);
}
void ProfileForm::onLoadClicked()
@ -259,6 +248,13 @@ void ProfileForm::onRenameClicked()
if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"),
tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)))
{
if (!ProfileLocker::lock(name))
{
GUI::showWarning(tr("Profile already exists", "rename failed title"),
tr("A profile named \"%1\" already exists and is in use.").arg(cur));
break;
}
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
@ -268,6 +264,7 @@ void ProfileForm::onRenameClicked()
Settings::getInstance().setCurrentProfile(name);
if (resetAutorun)
Settings::getInstance().setAutorun(true); // fixes -p flag in autostart command line
break;
}
} while (true);
@ -281,7 +278,7 @@ void ProfileForm::onExportClicked()
tr("Tox save file (*.tox)", "save dialog filter"));
if (!path.isEmpty())
{
if (!Nexus::isFilePathWritable(path))
if (!Nexus::tryRemoveFile(path))
{
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"));
return;
@ -389,7 +386,7 @@ void ProfileForm::on_saveQr_clicked()
tr("Save QrCode (*.png)", "save dialog filter"));
if (!path.isEmpty())
{
if (!Nexus::isFilePathWritable(path))
if (!Nexus::tryRemoveFile(path))
{
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"));
return;

View File

@ -21,7 +21,7 @@
#include <QLabel>
#include <QTimer>
#include <QVBoxLayout>
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/qrwidget.h"
class CroppingLabel;

View File

@ -20,7 +20,7 @@
#include "src/widget/widget.h"
#include "src/misc/settings.h"
#include "src/misc/smileypack.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/style.h"
#include <QMessageBox>
#include <QStyleFactory>
@ -52,6 +52,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6());
for (int i = 0; i < langs.size(); i++)
bodyUI->transComboBox->insertItem(i, langs[i]);
bodyUI->transComboBox->setCurrentIndex(locales.indexOf(Settings::getInstance().getTranslation()));
bodyUI->cbAutorun->setChecked(Settings::getInstance().getAutorun());
#if defined(__APPLE__) && defined(__MACH__)
@ -69,7 +70,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
bodyUI->minimizeToTray->setEnabled(showSystemTray);
bodyUI->lightTrayIcon->setChecked(Settings::getInstance().getLightTrayIcon());
bodyUI->lightTrayIcon->setEnabled(showSystemTray);
bodyUI->statusChanges->setChecked(Settings::getInstance().getStatusChangeNotificationEnabled());
bodyUI->useEmoticons->setChecked(Settings::getInstance().getUseEmoticons());
bodyUI->autoacceptFiles->setChecked(Settings::getInstance().getAutoSaveEnabled());
@ -83,9 +84,8 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
bodyUI->cbGroupchatPosition->setChecked(Settings::getInstance().getGroupchatPosition());
for (auto entry : SmileyPack::listSmileyPacks())
{
bodyUI->smileyPackBrowser->addItem(entry.first, entry.second);
}
bodyUI->smileyPackBrowser->setCurrentIndex(bodyUI->smileyPackBrowser->findData(Settings::getInstance().getSmileyPack()));
reloadSmiles();
bodyUI->smileyPackBrowser->setEnabled(bodyUI->useEmoticons->isChecked());
@ -99,6 +99,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
for (QString color : Style::themeColorNames)
bodyUI->themeColorCBox->addItem(color);
bodyUI->themeColorCBox->setCurrentIndex(Settings::getInstance().getThemeColor());
bodyUI->emoticonSize->setValue(Settings::getInstance().getEmojiFontPointSize());
@ -174,7 +175,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
connect(bodyUI->cbFauxOfflineMessaging, &QCheckBox::stateChanged, this, &GeneralForm::onFauxOfflineMessaging);
connect(bodyUI->cbCompactLayout, &QCheckBox::stateChanged, this, &GeneralForm::onCompactLayout);
connect(bodyUI->cbGroupchatPosition, &QCheckBox::stateChanged, this, &GeneralForm::onGroupchatPositionChanged);
// prevent stealing mouse whell scroll
// scrolling event won't be transmitted to comboboxes or qspinboxes when scrolling
// you can scroll through general settings without accidentially chaning theme/skin/icons etc.
@ -331,11 +332,9 @@ void GeneralForm::onProxyAddrEdited()
void GeneralForm::onProxyPortEdited(int port)
{
if (port > 0)
{
Settings::getInstance().setProxyPort(port);
} else {
else
Settings::getInstance().setProxyPort(-1);
}
}
void GeneralForm::onUseProxyUpdated()

View File

@ -19,7 +19,7 @@
#include "src/widget/form/settingswidget.h"
#include "src/misc/settings.h"
#include "src/historykeeper.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/widget/widget.h"
#include "src/widget/gui.h"
#include "src/widget/form/setpassworddialog.h"
@ -115,8 +115,10 @@ bool PrivacyForm::setChatLogsPassword()
else
{
if (GUI::askQuestion(tr("Old encrypted chat history", "popup title"), tr("There is currently an unused encrypted chat history, but the password you just entered doesn't match.\n\nIf you don't care about the old history, you may delete it and use the password you just entered.\nOtherwise, hit Cancel to try again.", "This happens when enabling encryption after previously \"Disabling History\""), tr("Delete"), tr("Cancel")))
{
if (GUI::askQuestion(tr("Old encrypted chat history", "popup title"), tr("Are you absolutely sure you want to lose the unused encrypted chat history?", "secondary popup"), tr("Delete"), tr("Cancel")))
haveEncHist = false; // logically this is really just a `break`, but conceptually this is more accurate
}
}
} while (haveEncHist);

View File

@ -19,7 +19,7 @@
was greatly simplified for use in qTox. */
#include "tabcompleter.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/group.h"
#include "src/widget/tool/chattextedit.h"
#include <QRegExp>
@ -54,8 +54,10 @@ void TabCompleter::buildCompletionList()
QRegExp regex(QString("^[-_\\[\\]{}|`^.\\\\]*").append(QRegExp::escape(tabAbbrev)), Qt::CaseInsensitive);
for (auto name : group->getPeerList())
{
if (regex.indexIn(name) > -1)
completionMap[name.toLower()] = name;
}
nextCompletion = completionMap.begin();
lastCompletionLength = tabAbbrev.length();
@ -64,12 +66,14 @@ void TabCompleter::buildCompletionList()
void TabCompleter::complete()
{
if (!enabled) {
if (!enabled)
{
buildCompletionList();
enabled = true;
}
if (nextCompletion != completionMap.end()) {
if (nextCompletion != completionMap.end())
{
// clear previous completion
auto cur = msgEdit->textCursor();
cur.setPosition(cur.selectionEnd());
@ -85,13 +89,16 @@ void TabCompleter::complete()
nextCompletion++;
// we're completing the first word of the line
if (msgEdit->textCursor().position() == lastCompletionLength) {
if (msgEdit->textCursor().position() == lastCompletionLength)
{
msgEdit->insertPlainText(nickSuffix);
lastCompletionLength += nickSuffix.length();
}
}
else { // we're at the end of the list -> start over again
if (!completionMap.isEmpty()) {
else
{ // we're at the end of the list -> start over again
if (!completionMap.isEmpty())
{
nextCompletion = completionMap.begin();
complete();
}

View File

@ -87,7 +87,7 @@ void FriendListWidget::onGroupchatPositionChanged(bool top)
}
}
void FriendListWidget::moveWidget(QWidget *w, Status s, int hasNewEvents)
void FriendListWidget::moveWidget(QWidget *w, Status s)
{
QVBoxLayout* l = getFriendLayout(s);
l->removeWidget(w);

View File

@ -19,7 +19,7 @@
#include <QWidget>
#include <QHash>
#include "src/corestructs.h"
#include "src/core/corestructs.h"
class QVBoxLayout;
class QGridLayout;
@ -37,7 +37,7 @@ signals:
public slots:
void onGroupchatPositionChanged(bool top);
void moveWidget(QWidget *w, Status s, int hasNewEvents);
void moveWidget(QWidget *w, Status s);
private:
QHash<int, QVBoxLayout*> layouts;

View File

@ -20,7 +20,7 @@
#include "groupwidget.h"
#include "src/friendlist.h"
#include "src/friend.h"
#include "src/core.h"
#include "src/core/core.h"
#include "form/chatform.h"
#include "maskablepixmapwidget.h"
#include "croppinglabel.h"

View File

@ -21,7 +21,7 @@
#include "form/groupchatform.h"
#include "maskablepixmapwidget.h"
#include "src/misc/style.h"
#include "src/core.h"
#include "src/core/core.h"
#include <QPalette>
#include <QMenu>
#include <QContextMenuEvent>
@ -60,7 +60,9 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
if (selectedItem)
{
if (selectedItem == quitGroup)
{
emit removeGroup(groupId);
}
else if (selectedItem == setTitle)
{
bool ok;
@ -77,7 +79,7 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
* element - title, the rest of the widget stays in the same CSS as it
* was on mouse over. Repainting whole widget fixes style problem.
*/
this->repaint();
this->repaint();
}
}
}

View File

@ -15,7 +15,7 @@
*/
#include "netcamview.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/widget/videosurface.h"
#include <QLabel>
#include <QHBoxLayout>

View File

@ -16,7 +16,7 @@
#include "toxsave.h"
#include "gui.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/settings.h"
#include <QCoreApplication>
#include <QDir>
@ -42,9 +42,7 @@ bool handleToxSave(const QString& path)
}
while (!core->isReady())
{
qApp->processEvents();
}
QFileInfo info(path);
if (!info.exists())

View File

@ -19,7 +19,7 @@
#include "src/toxdns.h"
#include "src/widget/tool/friendrequestdialog.h"
#include "src/nexus.h"
#include "src/core.h"
#include "src/core/core.h"
#include <QByteArray>
#include <QString>
#include <QMessageBox>
@ -51,9 +51,7 @@ bool handleToxURI(const QString &toxURI)
}
while (!core->isReady())
{
qApp->processEvents();
}
QString toxaddr;
if (toxURI.startsWith("tox://"))

View File

@ -16,7 +16,7 @@
#include "widget.h"
#include "ui_mainwindow.h"
#include "src/core.h"
#include "src/core/core.h"
#include "src/misc/settings.h"
#include "src/friend.h"
#include "src/friendlist.h"
@ -50,6 +50,7 @@
#include <QClipboard>
#include <QThread>
#include <QDialogButtonBox>
#include <QShortcut>
#include <QTimer>
#include <QStyleFactory>
#include <QTranslator>
@ -72,6 +73,7 @@ bool toxActivateEventHandler(const QByteArray&)
{
if (!Widget::getInstance()->isActiveWindow())
Widget::getInstance()->forceShow();
return true;
}
@ -132,17 +134,17 @@ void Widget::init()
ui->mainHead->layout()->setSpacing(0);
ui->tooliconsZone->setStyleSheet(Style::resolve("QPushButton{background-color:@themeDark;border:none;}QPushButton:hover{background-color:@themeMediumDark;border:none;}"));
if (QStyleFactory::keys().contains(Settings::getInstance().getStyle())
&& Settings::getInstance().getStyle() != "None")
{
ui->mainHead->setStyle(QStyleFactory::create(Settings::getInstance().getStyle()));
ui->mainContent->setStyle(QStyleFactory::create(Settings::getInstance().getStyle()));
}
ui->mainHead->setStyleSheet(Style::getStylesheet(":ui/settings/mainHead.css"));
ui->mainHead->setStyleSheet(Style::getStylesheet(":ui/settings/mainHead.css"));
ui->mainContent->setStyleSheet(Style::getStylesheet(":ui/settings/mainContent.css"));
ui->statusHead->setStyleSheet(Style::getStylesheet(":/ui/window/statusPanel.css"));
contactListWidget = new FriendListWidget(0, Settings::getInstance().getGroupchatPosition());
@ -171,7 +173,7 @@ void Widget::init()
Style::setThemeColor(Settings::getInstance().getThemeColor());
reloadTheme();
updateIcons();
filesForm = new FilesForm();
addFriendForm = new AddFriendForm;
profileForm = new ProfileForm();
@ -196,7 +198,11 @@ void Widget::init()
connect(timer, &QTimer::timeout, this, &Widget::onTryCreateTrayIcon);
connect(offlineMsgTimer, &QTimer::timeout, this, &Widget::processOfflineMsgs);
// keyboard shortcuts
new QShortcut(Qt::CTRL + Qt::Key_Q, this, SLOT(close()));
addFriendForm->show(*ui);
setWindowTitle(tr("Add friend"));
connect(settingsWidget, &SettingsWidget::groupchatPositionToggled, contactListWidget, &FriendListWidget::onGroupchatPositionChanged);
#if (AUTOUPDATE_ENABLED)
@ -265,7 +271,9 @@ void Widget::updateIcons()
QString status;
if (eventIcon)
{
status = "event";
}
else
{
status = ui->statusButton->property("status").toString();
@ -291,6 +299,7 @@ Widget::~Widget()
AutoUpdater::abortUpdates();
if (icon)
icon->hide();
hideMainForms();
delete profileForm;
delete settingsWidget;
@ -313,6 +322,7 @@ Widget* Widget::getInstance()
if (!instance)
instance = new Widget();
return instance;
}
@ -337,9 +347,7 @@ void Widget::changeEvent(QEvent *event)
if (event->type() == QEvent::WindowStateChange)
{
if (isMinimized() && Settings::getInstance().getMinimizeToTray())
{
this->hide();
}
}
}
@ -448,7 +456,7 @@ void Widget::confirmExecutableOpen(const QFileInfo file)
{
return;
}
// The user wants to run this file, so make it executable and run it
QFile(file.filePath()).setPermissions(file.permissions() | QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther);
QProcess::startDetached(file.filePath());
@ -531,13 +539,12 @@ void Widget::hideMainForms()
QLayoutItem* item;
while ((item = ui->mainHead->layout()->takeAt(0)) != 0)
item->widget()->hide();
while ((item = ui->mainContent->layout()->takeAt(0)) != 0)
item->widget()->hide();
if (activeChatroomWidget != nullptr)
{
activeChatroomWidget->setAsInactiveChatroom();
}
}
void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername)
@ -580,7 +587,7 @@ void Widget::addFriend(int friendId, const QString &userId)
//qDebug() << "Widget: Adding friend with id" << userId;
ToxID userToxId = ToxID::fromString(userId);
Friend* newfriend = FriendList::addFriend(friendId, userToxId);
contactListWidget->moveWidget(newfriend->getFriendWidget(),Status::Offline,0);
contactListWidget->moveWidget(newfriend->getFriendWidget(),Status::Offline);
Core* core = Nexus::getCore();
connect(newfriend, &Friend::displayedNameChanged, contactListWidget, &FriendListWidget::moveWidget);
@ -589,17 +596,16 @@ void Widget::addFriend(int friendId, const QString &userId)
connect(newfriend->getFriendWidget(), SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int)));
connect(newfriend->getFriendWidget(), SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int)));
connect(newfriend->getFriendWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newfriend->getChatForm(), SLOT(focusInput()));
connect(newfriend->getChatForm(), SIGNAL(sendMessage(int,QString)), core, SLOT(sendMessage(int,QString)));
connect(newfriend->getChatForm(), &GenericChatForm::sendMessage, core, &Core::sendMessage);
connect(newfriend->getChatForm(), &GenericChatForm::sendAction, core, &Core::sendAction);
connect(newfriend->getChatForm(), SIGNAL(sendFile(int32_t, QString, QString, long long)), core, SLOT(sendFile(int32_t, QString, QString, long long)));
connect(newfriend->getChatForm(), SIGNAL(answerCall(int)), core, SLOT(answerCall(int)));
connect(newfriend->getChatForm(), SIGNAL(hangupCall(int)), core, SLOT(hangupCall(int)));
connect(newfriend->getChatForm(), SIGNAL(rejectCall(int)), core, SLOT(rejectCall(int)));
connect(newfriend->getChatForm(), SIGNAL(startCall(int)), core, SLOT(startCall(int)));
connect(newfriend->getChatForm(), SIGNAL(startVideoCall(int,bool)), core, SLOT(startCall(int,bool)));
connect(newfriend->getChatForm(), SIGNAL(cancelCall(int,int)), core, SLOT(cancelCall(int,int)));
connect(newfriend->getChatForm(), SIGNAL(micMuteToggle(int)), core, SLOT(micMuteToggle(int)));
connect(newfriend->getChatForm(), SIGNAL(volMuteToggle(int)), core, SLOT(volMuteToggle(int)));
connect(newfriend->getChatForm(), &ChatForm::sendFile, core, &Core::sendFile);
connect(newfriend->getChatForm(), &ChatForm::answerCall, core, &Core::answerCall);
connect(newfriend->getChatForm(), &ChatForm::hangupCall, core, &Core::hangupCall);
connect(newfriend->getChatForm(), &ChatForm::rejectCall, core, &Core::rejectCall);
connect(newfriend->getChatForm(), &ChatForm::startCall, core, &Core::startCall);
connect(newfriend->getChatForm(), &ChatForm::cancelCall, core, &Core::cancelCall);
connect(newfriend->getChatForm(), &ChatForm::micMuteToggle, core, &Core::micMuteToggle);
connect(newfriend->getChatForm(), &ChatForm::volMuteToggle, core, &Core::volMuteToggle);
connect(newfriend->getChatForm(), &ChatForm::aliasChanged, newfriend->getFriendWidget(), &FriendWidget::setAlias);
connect(core, &Core::fileReceiveRequested, newfriend->getChatForm(), &ChatForm::onFileRecvRequest);
connect(core, &Core::avInvite, newfriend->getChatForm(), &ChatForm::onAvInvite);
@ -650,24 +656,21 @@ void Widget::onFriendStatusChanged(int friendId, Status status)
if (isActualChange)
{
if (f->getStatus() == Status::Offline)
{
contactListWidget->moveWidget(f->getFriendWidget(), Status::Online, f->getEventFlag());
}
contactListWidget->moveWidget(f->getFriendWidget(), Status::Online);
else if (status == Status::Offline)
{
contactListWidget->moveWidget(f->getFriendWidget(), Status::Offline, f->getEventFlag());
}
contactListWidget->moveWidget(f->getFriendWidget(), Status::Offline);
}
f->setStatus(status);
f->getFriendWidget()->updateStatusLight();
//won't print the message if there were no messages before
if (!f->getChatForm()->isEmpty()
&& Settings::getInstance().getStatusChangeNotificationEnabled())
{
QString fStatus = "";
switch(f->getStatus()){
switch(f->getStatus())
{
case Status::Away:
fStatus = tr("away", "contact status"); break;
case Status::Busy:
@ -717,9 +720,8 @@ void Widget::onChatroomWidgetClicked(GenericChatroomWidget *widget)
hideMainForms();
widget->setChatForm(*ui);
if (activeChatroomWidget != nullptr)
{
activeChatroomWidget->setAsInactiveChatroom();
}
activeChatroomWidget = widget;
widget->setAsActiveChatroom();
setWindowTitle(widget->getName());
@ -736,7 +738,7 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool
QDateTime timestamp = QDateTime::currentDateTime();
f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true);
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, isAction ? "/me " + message : message,
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, isAction ? "/me " + f->getDisplayedName() + " " + message : message,
f->getToxID().publicKey, timestamp, true);
f->setEventFlag(f->getFriendWidget() != activeChatroomWidget);
@ -758,6 +760,7 @@ void Widget::newMessageAlert(GenericChatroomWidget* chat)
bool inactiveWindow = isMinimized() || !isActiveWindow();
if (!inactiveWindow && activeChatroomWidget != nullptr && chat == activeChatroomWidget)
return;
if (ui->statusButton->property("status").toString() == "busy")
return;
@ -793,6 +796,7 @@ void Widget::playRingtone()
{
if (ui->statusButton->property("status").toString() == "busy")
return;
QApplication::alert(this);
static QFile sndFile1(":audio/ToxicIncomingCall.pcm"); // for whatever reason this plays slower/downshifted from what any other program plays the file as... but whatever
@ -933,7 +937,9 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha
// g->getChatForm()->addSystemInfoMessage(tr("%1 has left the chat").arg(name), "white", QDateTime::currentDateTime());
}
else if (change == TOX_CHAT_CHANGE_PEER_NAME) // core overwrites old name before telling us it changed...
{
g->updatePeer(peernumber,Nexus::getCore()->getGroupPeerName(groupnumber, peernumber));
}
}
void Widget::onGroupTitleChanged(int groupnumber, const QString& author, const QString& title)
@ -988,19 +994,20 @@ Group *Widget::createGroup(int groupId)
return g;
}
Core* core = Nexus::getCore();
QString groupName = QString("Groupchat #%1").arg(groupId);
Group* newgroup = GroupList::addGroup(groupId, groupName, true);
Group* newgroup = GroupList::addGroup(groupId, groupName, core->isGroupAvEnabled(groupId));
QLayout* layout = contactListWidget->getGroupLayout();
layout->addWidget(newgroup->getGroupWidget());
newgroup->getGroupWidget()->updateStatusLight();
Core* core = Nexus::getCore();
connect(settingsWidget, &SettingsWidget::compactToggled, newgroup->getGroupWidget(), &GenericChatroomWidget::onCompactChanged);
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
connect(newgroup->getGroupWidget(), SIGNAL(removeGroup(int)), this, SLOT(removeGroup(int)));
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newgroup->getChatForm(), SLOT(focusInput()));
connect(newgroup->getChatForm(), SIGNAL(sendMessage(int,QString)), core, SLOT(sendGroupMessage(int,QString)));
connect(newgroup->getChatForm(), SIGNAL(sendAction(int,QString)), core, SLOT(sendGroupAction(int,QString)));
connect(newgroup->getChatForm(), &GroupChatForm::sendMessage, core, &Core::sendGroupMessage);
connect(newgroup->getChatForm(), &GroupChatForm::sendAction, core, &Core::sendGroupAction);
connect(newgroup->getChatForm(), &GroupChatForm::groupTitleChanged, core, &Core::changeGroupTitle);
return newgroup;
}
@ -1065,7 +1072,9 @@ void Widget::onUserAwayCheck()
}
}
else if (autoAwayActive)
{
autoAwayActive = false;
}
#endif
}
@ -1108,10 +1117,14 @@ void Widget::onTryCreateTrayIcon()
setHidden(Settings::getInstance().getAutostartInTray());
}
else
{
show();
}
}
else if (!isVisible())
{
show();
}
}
else
{
@ -1139,7 +1152,7 @@ void Widget::setStatusBusy()
Nexus::getCore()->setStatus(Status::Busy);
}
void Widget::onMessageSendResult(int friendId, const QString& message, int messageId)
void Widget::onMessageSendResult(uint32_t friendId, const QString& message, int messageId)
{
Q_UNUSED(message)
Q_UNUSED(messageId)
@ -1164,6 +1177,7 @@ void Widget::onFriendTypingChanged(int friendId, bool isTyping)
Friend* f = FriendList::findFriend(friendId);
if (!f)
return;
f->getChatForm()->setFriendTyping(isTyping);
}
@ -1197,9 +1211,7 @@ void Widget::processOfflineMsgs()
{
QList<Friend*> frnds = FriendList::getAllFriends();
for (Friend *f : frnds)
{
f->getChatForm()->getOfflineMsgEngine()->deliverOfflineMsgs();
}
OfflineMsgEngine::globalMutex.unlock();
}
@ -1209,9 +1221,7 @@ void Widget::clearAllReceipts()
{
QList<Friend*> frnds = FriendList::getAllFriends();
for (Friend *f : frnds)
{
f->getChatForm()->getOfflineMsgEngine()->removeAllReciepts();
}
}
void Widget::reloadTheme()

View File

@ -25,7 +25,7 @@
#include "form/settingswidget.h"
#include "form/profileform.h"
#include "form/filesform.h"
#include "src/corestructs.h"
#include "src/core/corestructs.h"
#define PIXELS_TO_ACT 7
@ -35,7 +35,7 @@ class MainWindow;
class GenericChatroomWidget;
class Group;
struct Friend;
class Friend;
class QSplitter;
class VideoSurface;
class QMenu;
@ -104,6 +104,7 @@ public slots:
void onFriendUsernameChanged(int friendId, const QString& username);
void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
void onFriendRequestReceived(const QString& userId, const QString& message);
void onMessageSendResult(uint32_t friendId, const QString& message, int messageId);
void onReceiptRecieved(int friendId, int receipt);
void onEmptyGroupCreated(int groupId);
void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite);
@ -111,6 +112,7 @@ public slots:
void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title);
void onGroupPeerAudioPlaying(int groupnumber, int peernumber);
void onGroupSendResult(int groupId, const QString& message, int result);
void playRingtone();
void onFriendTypingChanged(int friendId, bool isTyping);
void nextContact();
@ -140,8 +142,6 @@ private slots:
void setStatusOnline();
void setStatusAway();
void setStatusBusy();
void onMessageSendResult(int friendId, const QString& message, int messageId);
void onGroupSendResult(int groupId, const QString& message, int result);
void onIconClick(QSystemTrayIcon::ActivationReason);
void onUserAwayCheck();
void onEventIconTick();

2166
translations/de.ts vendored

File diff suppressed because it is too large Load Diff

152
translations/it.ts vendored
View File

@ -116,62 +116,62 @@ qualità video elevate, questo può causare problemi con le chiamate video.</tra
<context>
<name>AddFriendForm</name>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="36"/>
<location filename="../src/widget/form/addfriendform.cpp" line="37"/>
<source>Add Friends</source>
<translation>Aggiungi Contatto</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="39"/>
<location filename="../src/widget/form/addfriendform.cpp" line="40"/>
<source>Tox ID</source>
<comment>Tox ID of the person you&apos;re sending a friend request to</comment>
<translation>Tox ID</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="40"/>
<location filename="../src/widget/form/addfriendform.cpp" line="41"/>
<source>Message</source>
<comment>The message you send in friend requests</comment>
<translation>Messaggio</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="41"/>
<location filename="../src/widget/form/addfriendform.cpp" line="42"/>
<source>Send friend request</source>
<translation>Invia richiesta d&apos;amicizia</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="80"/>
<location filename="../src/widget/form/addfriendform.cpp" line="83"/>
<source>%1 here! Tox me maybe?</source>
<comment>Default message in friend requests if the field is left blank. Write something appropriate!</comment>
<translation>Ciao, sono %1.
Permettimi di aggiungerti alla mia lista contatti.</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="88"/>
<location filename="../src/widget/form/addfriendform.cpp" line="91"/>
<source>Please fill in a valid Tox ID</source>
<comment>Tox ID of the friend you&apos;re sending a friend request to</comment>
<translation>Inserisci un Tox ID valido</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="88"/>
<location filename="../src/widget/form/addfriendform.cpp" line="91"/>
<location filename="../src/widget/form/addfriendform.cpp" line="109"/>
<location filename="../src/widget/form/addfriendform.cpp" line="94"/>
<location filename="../src/widget/form/addfriendform.cpp" line="112"/>
<source>Couldn&apos;t add friend</source>
<translation>Impossibile aggiungere il contatto</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="91"/>
<location filename="../src/widget/form/addfriendform.cpp" line="94"/>
<source>You can&apos;t add yourself as a friend!</source>
<comment>When trying to add your own Tox ID as friend</comment>
<translation>Non puoi aggiungere te stesso come contatto!</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="99"/>
<location filename="../src/widget/form/addfriendform.cpp" line="102"/>
<source>qTox needs to use the Tox DNS, but can&apos;t do it through a proxy.
Ignore the proxy and connect to the Internet directly?</source>
<translation>qTox deve usare Tox DNS, ma non può farlo attraverso un proxy.
Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox?</translation>
</message>
<message>
<location filename="../src/widget/form/addfriendform.cpp" line="109"/>
<location filename="../src/widget/form/addfriendform.cpp" line="112"/>
<source>This Tox ID does not exist</source>
<comment>DNS error</comment>
<translation>Questo Tox ID non esiste</translation>
@ -413,12 +413,12 @@ Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox?</tr
<translation>Invio del file &quot;%1&quot; fallito</translation>
</message>
<message>
<location filename="../src/widget/form/chatform.cpp" line="909"/>
<location filename="../src/widget/form/chatform.cpp" line="910"/>
<source>Call with %1 ended. %2</source>
<translation>Chiamata con %1 terminata. %2</translation>
</message>
<message>
<location filename="../src/widget/form/chatform.cpp" line="928"/>
<location filename="../src/widget/form/chatform.cpp" line="929"/>
<source>Call duration: </source>
<translation>Durata chiamata: </translation>
</message>
@ -760,30 +760,30 @@ Soprannome:</translation>
<context>
<name>GeneralForm</name>
<message>
<location filename="../src/widget/form/settings/generalform.cpp" line="39"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="42"/>
<source>General</source>
<translation>Generale</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalform.cpp" line="89"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="94"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="93"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="98"/>
<source>None</source>
<translation>Nessuno</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalform.cpp" line="270"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="295"/>
<source>Choose an auto accept directory</source>
<comment>popup title</comment>
<translation>Scegli dove salvare i files accettati automaticamente</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalform.cpp" line="328"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="353"/>
<source>Call active</source>
<comment>popup title</comment>
<translation>Chiamata in corso</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalform.cpp" line="329"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="354"/>
<source>You can&apos;t disconnect while a call is active!</source>
<comment>popup text</comment>
<translation>Non puoi disconnetterti mentre c&apos;è una chiamata in corso!</translation>
@ -929,62 +929,55 @@ nella traybar invece che nella taskbar.</translation>
<source>Play sound</source>
<translation>Riproduci suono</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="376"/>
<source>Messages you are trying to send to your friends when they are not online
will be sent to them when they will appear online to you.</source>
<comment>toolTip for Faux offline messaging setting</comment>
<translation>I messaggi che invii ai contatti offline saranno inviati quando appaiono online.</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="390"/>
<source>Compact contact list</source>
<translation>Usa lista contatti compatta</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="430"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="440"/>
<source>Smiley Pack:</source>
<extracomment>Text on smiley pack label</extracomment>
<translation>Emoticons:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="504"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="514"/>
<source>Emoticon size:</source>
<translation>Dimensione:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="536"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="546"/>
<source>Style:</source>
<translation>Stile:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="553"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="563"/>
<source>Theme color:</source>
<translation>Colore tema:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="570"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="580"/>
<source>Timestamp format:</source>
<translation>Formato data/ora:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="592"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="612"/>
<source>Connection Settings</source>
<translation>Impostazioni Connessione</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="616"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="636"/>
<source>Enable IPv6 (recommended)</source>
<extracomment>Text on a checkbox to enable IPv6</extracomment>
<translation>Abilita IPv6 (consigliato)</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="650"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="670"/>
<source>Proxy type:</source>
<translation>Proxy:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="657"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="677"/>
<source>Address:</source>
<extracomment>Text on proxy addr label</extracomment>
<translation>Indirizzo:</translation>
@ -1046,6 +1039,13 @@ will be sent to them when they will appear online to you.</source>
<comment>toolTip for Focus window setting</comment>
<translation>Dai il focus alla finestra di qTox quando arriva un nuovo messaggio.</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="376"/>
<source>Messages you are trying to send to your friends when they are not online
will be sent to them when they appear online to you.</source>
<comment>toolTip for Faux offline messaging setting</comment>
<translation>I messaggi che invii ai contatti offline, saranno spediti quando appaiono online.</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="387"/>
<source>Your contact list will be shown in compact mode.</source>
@ -1053,55 +1053,71 @@ will be sent to them when they will appear online to you.</source>
<translation>La lista contatti sarà visualizzata in modo compatto.</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="406"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="397"/>
<source>If checked, groupchats will be placed at the top of the friends list, otherwise, they&apos;ll be placed below online friends.</source>
<comment>toolTip for groupchat positioning</comment>
<translation>Le chat di gruppo saranno posizionate all&apos;inizio della lista contatti, altrimenti saranno posizionate sotto ai contatti online.</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="400"/>
<source>Place groupchats at top of friend list</source>
<translation>Posiziona le chat di gruppo in cima alla lista contatti</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="416"/>
<source>Theme</source>
<translation>Impostazioni Tema</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="412"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="422"/>
<source>Use emoticons</source>
<translation>Usa emoticons</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="520"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="530"/>
<source> px</source>
<translation> px</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="606"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="597"/>
<source>Date format:</source>
<translation>Formato data:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="626"/>
<source>Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary.</source>
<extracomment>force tcp checkbox tooltip</extracomment>
<translation>Disabilitando questo sarà possibile usare qTox con Tor. Tuttavia verrà aggiunto carico alla rete Tox, quindi disabilitare solo se necessario.</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="609"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="629"/>
<source>Enable UDP (recommended)</source>
<extracomment>Text on checkbox to disable UDP</extracomment>
<translation>Abilita UDP (consigliato)</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="681"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="701"/>
<source>None</source>
<translation>Nessuno</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="686"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="706"/>
<source>SOCKS5</source>
<translation>SOCKS 5</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="691"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="711"/>
<source>HTTP</source>
<translation>HTTP</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="667"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="687"/>
<source>Port</source>
<extracomment>Text on proxy port label</extracomment>
<translation>Porta</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="701"/>
<location filename="../src/widget/form/settings/generalsettings.ui" line="721"/>
<source>Reconnect</source>
<comment>reconnect button</comment>
<translation>Riconnetti</translation>
@ -2084,131 +2100,131 @@ Se non sei sicuro, scegli &quot;No&quot;, così le informazioni inviate al serve
<context>
<name>Widget</name>
<message>
<location filename="../src/widget/widget.cpp" line="1063"/>
<location filename="../src/widget/widget.cpp" line="1092"/>
<source>&amp;Quit</source>
<translation>&amp;Esci</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="106"/>
<location filename="../src/widget/widget.cpp" line="107"/>
<source>Online</source>
<comment>Button to set your status to &apos;Online&apos;</comment>
<translation>Online</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="109"/>
<location filename="../src/widget/widget.cpp" line="110"/>
<source>Away</source>
<comment>Button to set your status to &apos;Away&apos;</comment>
<translation>Assente</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="112"/>
<location filename="../src/widget/widget.cpp" line="113"/>
<source>Busy</source>
<comment>Button to set your status to &apos;Busy&apos;</comment>
<translation>Occupato</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="364"/>
<location filename="../src/widget/widget.cpp" line="383"/>
<source>Toxcore failed to start, the application will terminate after you close this message.</source>
<translation>Impossibile avviare Toxcore.\nqTox terminerà dopo che avrai chiuso questo messaggio.</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="373"/>
<location filename="../src/widget/widget.cpp" line="392"/>
<source>toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart.</source>
<comment>popup text</comment>
<translation>Impossibile avviare Toxcore con le tue impostazione proxy.\nqTox non può funzionare correttamente, per favore modifica le impostazioni e riavvia il programma.</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="405"/>
<location filename="../src/widget/widget.cpp" line="424"/>
<source>Add friend</source>
<translation>Aggiungi contatto</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="418"/>
<location filename="../src/widget/widget.cpp" line="437"/>
<source>File transfers</source>
<translation>Files trasferiti</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="428"/>
<location filename="../src/widget/widget.cpp" line="447"/>
<source>Executable file</source>
<comment>popup title</comment>
<translation>File eseguibile</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="428"/>
<location filename="../src/widget/widget.cpp" line="447"/>
<source>You have asked qTox to open an executable file. Executable files can potentially damage your computer. Are you sure want to open this file?</source>
<comment>popup text</comment>
<translation>Hai chiesto a qTox di aprire un file eseguibile. I files eseguibili possono danneggiare il tuo computer. Sei sicuro di voler aprire questo file?</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="498"/>
<location filename="../src/widget/widget.cpp" line="517"/>
<source>Settings</source>
<translation>Impostazioni</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="506"/>
<location filename="../src/widget/widget.cpp" line="525"/>
<source>Profile</source>
<translation>Profilo</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="615"/>
<location filename="../src/widget/widget.cpp" line="634"/>
<source>Couldn&apos;t request friendship</source>
<translation>Impossibile inviare la richiesta d&apos;amicizia</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="643"/>
<location filename="../src/widget/widget.cpp" line="672"/>
<source>away</source>
<comment>contact status</comment>
<translation>assente</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="645"/>
<location filename="../src/widget/widget.cpp" line="674"/>
<source>busy</source>
<comment>contact status</comment>
<translation>occupato</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="647"/>
<location filename="../src/widget/widget.cpp" line="676"/>
<source>offline</source>
<comment>contact status</comment>
<translation>offline</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="651"/>
<location filename="../src/widget/widget.cpp" line="680"/>
<source>online</source>
<comment>contact status</comment>
<translation>online</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="654"/>
<location filename="../src/widget/widget.cpp" line="683"/>
<source>%1 is now %2</source>
<comment>e.g. &quot;Dubslow is now online&quot;</comment>
<translation>%1 è ora %2</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="837"/>
<location filename="../src/widget/widget.cpp" line="866"/>
<source>Group invite</source>
<comment>popup title</comment>
<translation>Invito chat di gruppo</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="837"/>
<location filename="../src/widget/widget.cpp" line="866"/>
<source>%1 has invited you to a groupchat. Would you like to join?</source>
<comment>popup text</comment>
<translation>%1 ti ha invitato in una chat di gruppo. Vuoi partecipare?</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="892"/>
<location filename="../src/widget/widget.cpp" line="921"/>
<source>&lt;Unknown&gt;</source>
<comment>Placeholder when we don&apos;t know someone&apos;s name in a group chat</comment>
<translation>&lt;Sconosciuto&gt;</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="918"/>
<location filename="../src/widget/widget.cpp" line="947"/>
<source>%1 has set the title to %2</source>
<translation>%1 ha impostato il titolo in %2</translation>
</message>
<message>
<location filename="../src/widget/widget.cpp" line="1130"/>
<location filename="../src/widget/widget.cpp" line="1159"/>
<source>Message failed to send</source>
<translation>Impossibile inviare il messaggio</translation>
</message>

View File

@ -84,108 +84,132 @@ QScrollArea
QScrollArea > QWidget > QWidget
{
background: transparent;
background: transparent;
}
QScrollBar:vertical {
QScrollArea::corner
{
background: white;
border: none;
}
QScrollBar:vertical
{
background: transparent;
width: 12px;
margin-top: 2px;
margin-bottom: 2px;
}
QScrollBar::handle:vertical {
QScrollBar::handle:vertical
{
background: #d1d1d1;
min-height: 20px;
border-radius: 3px;
margin-left: 2px;
}
QScrollBar::handle:vertical:hover {
QScrollBar::handle:vertical:hover
{
background: #e3e3e3;
}
QScrollBar::handle:vertical:pressed {
QScrollBar::handle:vertical:pressed
{
background: #b1b1b1;
}
QScrollBar::add-line:vertical {
background: url(":ui/chatArea/scrollBarDownArrow.svg") center;
QScrollBar::add-line:vertical
{
background: white;
height: 0px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
background: url(":ui/chatArea/scrollBarUpArrow.svg") center;
QScrollBar::sub-line:vertical
{
background: white;
height: 0px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar:QScrollBar::down-arrow:vertical {
QScrollBar:QScrollBar::down-arrow:vertical
{
width: 10;
height: 10px;
background: white;
}
QScrollBar:QScrollBar::up-arrow:vertical {
QScrollBar:QScrollBar::up-arrow:vertical
{
width: 10px;
height: 10px;
background: white;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
{
background: none;
}
QScrollBar:horizontal {
QScrollBar:horizontal
{
background: white;
height: 10px;
margin: 0 2px 0 2px;
}
QScrollBar::handle:horizontal {
QScrollBar::handle:horizontal
{
background: #d1d1d1;
min-width: 20px;
border-radius: 2px;
}
QScrollBar::handle:horizontal:hover {
QScrollBar::handle:horizontal:hover
{
background: #e3e3e3;
}
QScrollBar::handle:horizontal:pressed {
QScrollBar::handle:horizontal:pressed
{
background: #b1b1b1;
}
QScrollBar::add-line:horizontal {
background: url(":ui/chatArea/scrollBarRightArrow.svg") center;
QScrollBar::add-line:horizontal
{
background: white;
width: 0px;
subcontrol-position: right;
subcontrol-origin: margin;
}
QScrollBar::sub-line:horizontal {
background: url(":ui/chatArea/scrollBarLeftArrow.svg") center;
QScrollBar::sub-line:horizontal
{
background: white;
width: 0px;
subcontrol-position: left;
subcontrol-origin: margin;
}
QScrollBar:QScrollBar::down-arrow:horizontal {
QScrollBar:QScrollBar::down-arrow:horizontal
{
width: 10;
height: 10px;
background: white;
}
QScrollBar:QScrollBar::up-arrow:horizontal {
QScrollBar:QScrollBar::up-arrow:horizontal
{
width: 10px;
height: 10px;
background: white;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
{
background: none;
}