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

1922 lines
63 KiB
C++
Raw Normal View History

2014-06-25 04:11:11 +08:00
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the COPYING file for more details.
*/
#include "core.h"
2014-09-30 02:28:59 +08:00
#include "misc/cdata.h"
#include "misc/cstring.h"
#include "misc/settings.h"
#include "widget/widget.h"
#include "historykeeper.h"
#include "src/audio.h"
2014-06-25 04:11:11 +08:00
2014-09-11 21:44:34 +08:00
#include <tox/tox.h>
#include <tox/toxencryptsave.h>
2014-09-11 21:44:34 +08:00
2014-08-02 04:24:02 +08:00
#include <ctime>
#include <functional>
2014-08-02 04:24:02 +08:00
2014-06-25 04:11:11 +08:00
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QSaveFile>
#include <QStandardPaths>
#include <QThread>
2014-09-11 21:44:34 +08:00
#include <QTimer>
#include <QCoreApplication>
#include <QDateTime>
2014-09-21 18:30:10 +08:00
#include <QList>
2014-09-24 22:51:16 +08:00
#include <QBuffer>
2014-10-01 19:23:00 +08:00
#include <QMessageBox>
#include <QMutexLocker>
2014-06-25 04:11:11 +08:00
2014-06-29 01:09:16 +08:00
const QString Core::CONFIG_FILE_NAME = "data";
const QString Core::TOX_EXT = ".tox";
2014-06-26 04:43:28 +08:00
QList<ToxFile> Core::fileSendQueue;
QList<ToxFile> Core::fileRecvQueue;
2014-11-13 19:18:04 +08:00
QHash<int, ToxGroupCall> Core::groupCalls;
2014-11-13 20:11:23 +08:00
QThread* Core::coreThread{nullptr};
2014-06-25 04:11:11 +08:00
2014-12-29 01:26:35 +08:00
#define MAX_GROUP_MESSAGE_LEN 1024
2014-11-13 20:11:23 +08:00
Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
tox(nullptr), camera(cam), loadPath(loadPath), ready{false}
2014-06-25 04:11:11 +08:00
{
qDebug() << "Core: loading Tox from" << loadPath;
2014-11-13 20:11:23 +08:00
coreThread = CoreThread;
Audio::getInstance();
2014-11-13 20:11:23 +08:00
2014-07-12 18:50:40 +08:00
videobuf = new uint8_t[videobufsize];
for (int i = 0; i < ptCounter; i++)
pwsaltedkeys[i] = nullptr;
2014-06-25 04:11:11 +08:00
toxTimer = new QTimer(this);
toxTimer->setSingleShot(true);
connect(toxTimer, &QTimer::timeout, this, &Core::process);
//connect(fileTimer, &QTimer::timeout, this, &Core::fileHeartbeat);
connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::process);
connect(this, SIGNAL(fileTransferFinished(ToxFile)), this, SLOT(onFileTransferFinished(ToxFile)));
for (int i=0; i<TOXAV_MAX_CALLS;i++)
{
calls[i].active = false;
calls[i].alSource = 0;
calls[i].sendAudioTimer = new QTimer();
calls[i].sendVideoTimer = new QTimer();
calls[i].sendAudioTimer->moveToThread(coreThread);
calls[i].sendVideoTimer->moveToThread(coreThread);
connect(calls[i].sendVideoTimer, &QTimer::timeout, [this,i](){sendCallVideo(i);});
}
2014-08-30 20:40:41 +08:00
// OpenAL init
QString outDevDescr = Settings::getInstance().getOutDev();
Audio::openOutput(outDevDescr);
QString inDevDescr = Settings::getInstance().getInDev();
Audio::openInput(inDevDescr);
2014-06-25 04:11:11 +08:00
}
Core::~Core()
{
qDebug() << "Deleting Core";
clearPassword(Core::ptMain);
clearPassword(Core::ptHistory);
2015-01-24 22:31:11 +08:00
toxTimer->stop();
coreThread->exit(0);
2015-01-25 02:48:23 +08:00
while (coreThread->isRunning())
{
qApp->processEvents();
coreThread->wait(500);
}
2015-01-24 22:31:11 +08:00
if (tox)
{
toxav_kill(toxav);
toxav = nullptr;
2014-06-25 04:11:11 +08:00
tox_kill(tox);
tox = nullptr;
2014-06-25 04:11:11 +08:00
}
2014-07-12 18:50:40 +08:00
if (videobuf)
{
delete[] videobuf;
videobuf=nullptr;
}
2014-08-30 20:40:41 +08:00
Audio::closeInput();
Audio::closeOutput();
2014-06-25 04:11:11 +08:00
}
2014-09-11 21:44:34 +08:00
Core* Core::getInstance()
{
return Widget::getInstance()->getCore();
}
void Core::make_tox()
{
2014-07-02 06:47:06 +08:00
// IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be disabled in options.
bool enableIPv6 = Settings::getInstance().getEnableIPv6();
2014-10-06 04:49:44 +08:00
bool forceTCP = Settings::getInstance().getForceTCP();
2014-12-28 20:32:25 +08:00
ProxyType proxyType = Settings::getInstance().getProxyType();
2014-10-07 10:09:15 +08:00
2014-07-02 06:47:06 +08:00
if (enableIPv6)
qDebug() << "Core starting with IPv6 enabled";
else
qWarning() << "Core starting with IPv6 disabled. LAN discovery may not work properly.";
2014-08-17 20:57:54 +08:00
Tox_Options toxOptions;
toxOptions.ipv6enabled = enableIPv6;
2014-10-06 04:49:44 +08:00
toxOptions.udp_disabled = forceTCP;
2014-10-07 10:09:15 +08:00
// No proxy by default
2014-12-28 20:32:25 +08:00
toxOptions.proxy_type = TOX_PROXY_NONE;
2014-08-17 20:57:54 +08:00
toxOptions.proxy_address[0] = 0;
toxOptions.proxy_port = 0;
2014-12-28 20:32:25 +08:00
if (proxyType != ProxyType::ptNone)
2014-10-06 04:49:44 +08:00
{
2014-10-07 10:09:15 +08:00
QString proxyAddr = Settings::getInstance().getProxyAddr();
int proxyPort = Settings::getInstance().getProxyPort();
if (proxyAddr.length() > 255)
{
qWarning() << "Core: proxy address" << proxyAddr << "is too long";
}
else if (proxyAddr != "" && proxyPort > 0)
{
qDebug() << "Core: using proxy" << proxyAddr << ":" << proxyPort;
2014-12-28 20:32:25 +08:00
// protection against changings in TOX_PROXY_TYPE enum
if (proxyType == ProxyType::ptSOCKS5)
toxOptions.proxy_type = TOX_PROXY_SOCKS5;
else if (proxyType == ProxyType::ptHTTP)
toxOptions.proxy_type = TOX_PROXY_HTTP;
2014-10-07 10:09:15 +08:00
uint16_t sz = CString::fromString(proxyAddr, (unsigned char*)toxOptions.proxy_address);
toxOptions.proxy_address[sz] = 0;
toxOptions.proxy_port = proxyPort;
}
2014-10-06 04:49:44 +08:00
}
2014-08-17 20:57:54 +08:00
tox = tox_new(&toxOptions);
if (tox == nullptr)
{
if (enableIPv6) // Fallback to IPv4
{
2014-08-17 20:57:54 +08:00
toxOptions.ipv6enabled = false;
tox = tox_new(&toxOptions);
if (tox == nullptr)
{
2014-12-28 20:32:25 +08:00
if (toxOptions.proxy_type != TOX_PROXY_NONE)
2014-10-06 04:49:44 +08:00
{
//QMessageBox::critical(Widget::getInstance(), tr("Proxy failure", "popup title"),
//tr("toxcore failed to start with your proxy settings. qTox cannot run; please modify your "
//"settings and restart.", "popup text"));
qCritical() << "Core: bad proxy! no toxcore!";
emit badProxy();
}
else
{
qCritical() << "Tox core failed to start";
emit failedToStart();
}
return;
}
else
qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery may not work properly.";
}
2014-12-28 20:32:25 +08:00
else if (toxOptions.proxy_type != TOX_PROXY_NONE)
2014-10-06 04:49:44 +08:00
{
emit badProxy();
return;
}
else
{
qCritical() << "Tox core failed to start";
emit failedToStart();
return;
}
}
toxav = toxav_new(tox, TOXAV_MAX_CALLS);
if (toxav == nullptr)
{
qCritical() << "Toxav core failed to start";
emit failedToStart();
return;
}
}
void Core::start()
{
make_tox();
qsrand(time(nullptr));
if (loadPath != "")
{
if (!loadConfiguration(loadPath)) // loadPath is meaningless after this
{
2014-10-25 02:55:54 +08:00
qCritical() << "Core: loadConfiguration failed, exiting now";
emit failedToStart();
tox_kill(tox);
tox = nullptr;
return;
}
loadPath = "";
2014-10-01 19:23:00 +08:00
}
else // new ID
{
setStatusMessage(tr("Toxing on qTox")); // this also solves the not updating issue
setUsername(tr("qTox User"));
QMetaObject::invokeMethod(Widget::getInstance(), "onSettingsClicked"); // update ui with new profile
}
tox_callback_friend_request(tox, onFriendRequest, this);
tox_callback_friend_message(tox, onFriendMessage, this);
tox_callback_friend_action(tox, onAction, this);
tox_callback_name_change(tox, onFriendNameChange, this);
tox_callback_typing_change(tox, onFriendTypingChange, this);
tox_callback_status_message(tox, onStatusMessageChanged, this);
tox_callback_user_status(tox, onUserStatusChanged, this);
tox_callback_connection_status(tox, onConnectionStatusChanged, this);
tox_callback_group_invite(tox, onGroupInvite, this);
tox_callback_group_message(tox, onGroupMessage, this);
tox_callback_group_namelist_change(tox, onGroupNamelistChange, this);
2014-11-06 08:22:50 +08:00
tox_callback_group_title(tox, onGroupTitleChange, this);
2014-10-30 16:40:47 +08:00
tox_callback_group_action(tox, onGroupAction, this);
tox_callback_file_send_request(tox, onFileSendRequestCallback, this);
tox_callback_file_control(tox, onFileControlCallback, this);
tox_callback_file_data(tox, onFileDataCallback, this);
2014-09-24 22:51:16 +08:00
tox_callback_avatar_info(tox, onAvatarInfoCallback, this);
tox_callback_avatar_data(tox, onAvatarDataCallback, this);
2014-11-01 22:19:11 +08:00
tox_callback_read_receipt(tox, onReadReceiptCallback, this);
toxav_register_callstate_callback(toxav, onAvInvite, av_OnInvite, this);
toxav_register_callstate_callback(toxav, onAvStart, av_OnStart, this);
toxav_register_callstate_callback(toxav, onAvCancel, av_OnCancel, this);
toxav_register_callstate_callback(toxav, onAvReject, av_OnReject, this);
toxav_register_callstate_callback(toxav, onAvEnd, av_OnEnd, this);
toxav_register_callstate_callback(toxav, onAvRinging, av_OnRinging, this);
2014-11-18 06:17:54 +08:00
toxav_register_callstate_callback(toxav, onAvMediaChange, av_OnPeerCSChange, this);
toxav_register_callstate_callback(toxav, onAvMediaChange, av_OnSelfCSChange, this);
toxav_register_callstate_callback(toxav, onAvRequestTimeout, av_OnRequestTimeout, this);
toxav_register_callstate_callback(toxav, onAvPeerTimeout, av_OnPeerTimeout, this);
2014-06-27 07:17:10 +08:00
2014-11-30 23:20:32 +08:00
toxav_register_audio_callback(toxav, playCallAudio, this);
toxav_register_video_callback(toxav, playCallVideo, this);
2014-07-09 15:55:25 +08:00
2014-09-28 06:19:30 +08:00
QPixmap pic = Settings::getInstance().getSavedAvatar(getSelfId().toString());
2014-09-24 22:51:16 +08:00
if (!pic.isNull() && !pic.size().isEmpty())
{
QByteArray data;
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
2014-09-24 23:28:38 +08:00
pic.save(&buffer, "PNG");
2014-09-24 22:51:16 +08:00
buffer.close();
setAvatar(TOX_AVATAR_FORMAT_PNG, data);
2014-09-24 22:51:16 +08:00
}
else
2014-09-28 06:19:30 +08:00
qDebug() << "Core: Error loading self avatar";
ready = true;
process(); // starts its own timer
}
/* Using the now commented out statements in checkConnection(), I watched how
* many ticks disconnects-after-initial-connect lasted. Out of roughly 15 trials,
* 5 disconnected; 4 were DCd for less than 20 ticks, while the 5th was ~50 ticks.
* So I set the tolerance here at 25, and initial DCs should be very rare now.
* This should be able to go to 50 or 100 without affecting legitimate disconnects'
2014-10-09 16:59:35 +08:00
* downtime, but lets be conservative for now. Edit: now ~~40~~ 30.
*/
2014-10-09 16:59:35 +08:00
#define CORE_DISCONNECT_TOLERANCE 30
void Core::process()
{
if (!tox)
return;
static int tolerance = CORE_DISCONNECT_TOLERANCE;
tox_do(tox);
2014-11-18 06:17:54 +08:00
toxav_do(toxav);
2014-09-29 07:21:08 +08:00
#ifdef DEBUG
//we want to see the debug messages immediately
fflush(stdout);
#endif
2014-09-29 07:21:08 +08:00
if (checkConnection())
tolerance = CORE_DISCONNECT_TOLERANCE;
else if (!(--tolerance))
2014-09-29 07:21:08 +08:00
{
bootstrapDht();
2014-09-29 07:21:08 +08:00
}
2014-11-18 06:17:54 +08:00
toxTimer->start(qMin(tox_do_interval(tox), toxav_do_interval(toxav)));
}
bool Core::checkConnection()
{
static bool isConnected = false;
//static int count = 0;
bool toxConnected = tox_isconnected(tox);
if (toxConnected && !isConnected) {
qDebug() << "Core: Connected to DHT";
emit connected();
isConnected = true;
//if (count) qDebug() << "Core: disconnect count:" << count;
//count = 0;
} else if (!toxConnected && isConnected) {
qDebug() << "Core: Disconnected to DHT";
emit disconnected();
isConnected = false;
//count++;
} //else if (!toxConnected) count++;
return isConnected;
}
void Core::bootstrapDht()
{
const Settings& s = Settings::getInstance();
QList<Settings::DhtServer> dhtServerList = s.getDhtServerList();
int listSize = dhtServerList.size();
if (listSize == 0)
{
qDebug() << "Settings: no bootstrap list?!?";
return;
}
static int j = qrand() % listSize;
2015-01-13 23:55:06 +08:00
qDebug() << "Core: Bootstrapping to the DHT ...";
int i=0;
2014-10-01 07:50:12 +08:00
while (i < 2) // i think the more we bootstrap, the more we jitter because the more we overwrite nodes
{
const Settings::DhtServer& dhtServer = dhtServerList[j % listSize];
if (tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(),
dhtServer.port, CUserId(dhtServer.userId).data()) == 1)
2015-01-13 23:55:06 +08:00
qDebug() << QString("Core: Bootstrapping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data()
+QString(", port ")+QString().setNum(dhtServer.port);
else
2015-01-13 23:55:06 +08:00
qDebug() << "Core: Error bootstrapping from "+dhtServer.name;
j++;
i++;
}
}
2014-06-25 04:11:11 +08:00
void Core::onFriendRequest(Tox*/* tox*/, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core)
{
emit static_cast<Core*>(core)->friendRequestReceived(CUserId::toString(cUserId), CString::toString(cMessage, cMessageSize));
}
2014-07-03 15:22:12 +08:00
void Core::onFriendMessage(Tox*/* tox*/, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->friendMessageReceived(friendId, CString::toString(cMessage, cMessageSize), false);
2014-06-25 04:11:11 +08:00
}
2014-07-03 15:22:12 +08:00
void Core::onFriendNameChange(Tox*/* tox*/, int friendId, const uint8_t* cName, uint16_t cNameSize, void* core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->friendUsernameChanged(friendId, CString::toString(cName, cNameSize));
}
void Core::onFriendTypingChange(Tox*/* tox*/, int friendId, uint8_t isTyping, void *core)
{
emit static_cast<Core*>(core)->friendTypingChanged(friendId, isTyping ? true : false);
}
2014-07-03 15:22:12 +08:00
void Core::onStatusMessageChanged(Tox*/* tox*/, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->friendStatusMessageChanged(friendId, CString::toString(cMessage, cMessageSize));
}
void Core::onUserStatusChanged(Tox*/* tox*/, int friendId, uint8_t userstatus, void* core)
{
Status status;
switch (userstatus) {
case TOX_USERSTATUS_NONE:
status = Status::Online;
break;
case TOX_USERSTATUS_AWAY:
status = Status::Away;
break;
case TOX_USERSTATUS_BUSY:
status = Status::Busy;
break;
default:
status = Status::Online;
break;
}
2014-09-24 23:28:38 +08:00
if (status == Status::Online || status == Status::Away)
tox_request_avatar_info(static_cast<Core*>(core)->tox, friendId);
2014-09-24 23:28:38 +08:00
2014-06-25 04:11:11 +08:00
emit static_cast<Core*>(core)->friendStatusChanged(friendId, status);
}
void Core::onConnectionStatusChanged(Tox*/* tox*/, int friendId, uint8_t status, void* core)
{
Status friendStatus = status ? Status::Online : Status::Offline;
emit static_cast<Core*>(core)->friendStatusChanged(friendId, friendStatus);
if (friendStatus == Status::Offline) {
static_cast<Core*>(core)->checkLastOnline(friendId);
for (ToxFile& f : fileSendQueue)
{
if (f.friendId == friendId && f.status == ToxFile::TRANSMITTING)
{
f.status = ToxFile::BROKEN;
emit static_cast<Core*>(core)->fileTransferBrokenUnbroken(f, true);
}
}
for (ToxFile& f : fileRecvQueue)
{
if (f.friendId == friendId && f.status == ToxFile::TRANSMITTING)
{
f.status = ToxFile::BROKEN;
emit static_cast<Core*>(core)->fileTransferBrokenUnbroken(f, true);
}
}
} else {
for (ToxFile& f : fileRecvQueue)
{
if (f.friendId == friendId && f.status == ToxFile::BROKEN)
{
2014-09-30 00:36:42 +08:00
qDebug() << QString("Core::onConnectionStatusChanged: %1: resuming broken filetransfer from position: %2").arg(f.file->fileName()).arg(f.bytesSent);
tox_file_send_control(static_cast<Core*>(core)->tox, friendId, 1, f.fileNum, TOX_FILECONTROL_RESUME_BROKEN, reinterpret_cast<const uint8_t*>(&f.bytesSent), sizeof(uint64_t));
emit static_cast<Core*>(core)->fileTransferBrokenUnbroken(f, false);
}
}
2014-06-25 04:11:11 +08:00
}
}
2014-07-03 15:22:12 +08:00
void Core::onAction(Tox*/* tox*/, int friendId, const uint8_t *cMessage, uint16_t cMessageSize, void *core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->friendMessageReceived(friendId, CString::toString(cMessage, cMessageSize), true);
2014-06-25 04:11:11 +08:00
}
2014-10-30 16:40:47 +08:00
void Core::onGroupAction(Tox*, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void* _core)
{
Core* core = static_cast<Core*>(_core);
2014-11-23 23:55:49 +08:00
emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(action, length), true);
2014-10-30 16:40:47 +08:00
}
void Core::onGroupInvite(Tox*, int friendnumber, uint8_t type, const uint8_t *data, uint16_t length,void *core)
2014-06-25 04:11:11 +08:00
{
QByteArray pk((char*)data, length);
if (type == TOX_GROUPCHAT_TYPE_TEXT)
{
qDebug() << QString("Core: Text group invite by %1").arg(friendnumber);
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber,type,pk);
}
else if (type == TOX_GROUPCHAT_TYPE_AV)
{
qDebug() << QString("Core: AV group invite by %1").arg(friendnumber);
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber,type,pk);
}
else
{
qWarning() << "Core: Group invite with unknown type "<<type;
}
2014-06-25 04:11:11 +08:00
}
void Core::onGroupMessage(Tox*, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *_core)
2014-06-25 04:11:11 +08:00
{
Core* core = static_cast<Core*>(_core);
2014-11-23 23:55:49 +08:00
emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(message, length), false);
2014-06-25 04:11:11 +08:00
}
void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t change, void *core)
{
qDebug() << QString("Core: Group namelist change %1:%2 %3").arg(groupnumber).arg(peernumber).arg(change);
emit static_cast<Core*>(core)->groupNamelistChanged(groupnumber, peernumber, change);
}
2014-11-06 08:22:50 +08:00
void Core::onGroupTitleChange(Tox*, int groupnumber, int peernumber, const uint8_t* title, uint8_t len, void* _core)
{
qDebug() << "Core: group" << groupnumber << "title changed by" << peernumber;
Core* core = static_cast<Core*>(_core);
QString author;
2014-11-06 10:50:32 +08:00
if (peernumber >= 0)
2014-11-06 08:22:50 +08:00
author = core->getGroupPeerName(groupnumber, peernumber);
emit core->groupTitleChanged(groupnumber, author, CString::toString(title, len));
}
void Core::onFileSendRequestCallback(Tox*, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
2014-07-03 15:22:12 +08:00
const uint8_t *filename, uint16_t filename_length, void *core)
2014-06-26 04:43:28 +08:00
{
qDebug() << QString("Core: Received file request %1 with friend %2").arg(filenumber).arg(friendnumber);
2014-07-01 19:33:59 +08:00
ToxFile file{filenumber, friendnumber,
CString::toString(filename,filename_length).toUtf8(), "", ToxFile::RECEIVING};
file.filesize = filesize;
fileRecvQueue.append(file);
emit static_cast<Core*>(core)->fileReceiveRequested(fileRecvQueue.last());
2014-06-26 04:43:28 +08:00
}
2014-08-05 09:00:17 +08:00
void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
2014-09-30 00:36:42 +08:00
uint8_t control_type, const uint8_t* data, uint16_t length, void *core)
2014-06-26 04:43:28 +08:00
{
2014-06-26 06:30:24 +08:00
ToxFile* file{nullptr};
if (receive_send == 1)
2014-06-26 06:30:24 +08:00
{
for (ToxFile& f : fileSendQueue)
2014-06-26 06:30:24 +08:00
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
}
}
else
{
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
2014-06-26 06:30:24 +08:00
}
}
if (!file)
{
qWarning("Core::onFileControlCallback: No such file in queue");
return;
}
2014-08-05 09:00:17 +08:00
if (receive_send == 1 && control_type == TOX_FILECONTROL_ACCEPT)
2014-06-26 04:43:28 +08:00
{
file->status = ToxFile::TRANSMITTING;
emit static_cast<Core*>(core)->fileTransferAccepted(*file);
2014-06-26 04:43:28 +08:00
qDebug() << "Core: File control callback, file accepted";
file->sendTimer = new QTimer(static_cast<Core*>(core));
connect(file->sendTimer, &QTimer::timeout, std::bind(sendAllFileData,static_cast<Core*>(core), file));
file->sendTimer->setSingleShot(true);
file->sendTimer->start(TOX_FILE_INTERVAL);
2014-06-26 04:43:28 +08:00
}
2014-06-26 06:30:24 +08:00
else if (receive_send == 1 && control_type == TOX_FILECONTROL_KILL)
{
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 cancelled by friend %2")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
2014-08-31 19:07:31 +08:00
// Wait for sendAllFileData to return before deleting the ToxFile, we MUST ensure this or we'll use after free
2014-08-31 19:57:59 +08:00
if (file->sendTimer)
2014-08-31 19:07:31 +08:00
{
QThread::msleep(1);
qApp->processEvents();
2014-08-31 19:57:59 +08:00
if (file->sendTimer)
{
delete file->sendTimer;
file->sendTimer = nullptr;
}
2014-08-31 19:07:31 +08:00
}
2014-08-05 09:00:17 +08:00
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
}
else if (receive_send == 1 && control_type == TOX_FILECONTROL_FINISHED)
{
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 to friend %2 is complete")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferFinished(*file);
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
}
else if (receive_send == 0 && control_type == TOX_FILECONTROL_KILL)
{
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 cancelled by friend %2")
.arg(file->fileNum).arg(file->friendId);
2014-06-26 06:30:24 +08:00
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::RECEIVING);
2014-08-05 09:00:17 +08:00
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
}
else if (receive_send == 0 && control_type == TOX_FILECONTROL_FINISHED)
{
qDebug() << QString("Core::onFileControlCallback: Reception of file %1 from %2 finished")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferFinished(*file);
2014-08-05 09:00:17 +08:00
// confirm receive is complete
2014-08-29 18:52:09 +08:00
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
2014-08-29 02:20:28 +08:00
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
2014-06-26 06:30:24 +08:00
}
else if (receive_send == 0 && control_type == TOX_FILECONTROL_ACCEPT)
{
if (file->status == ToxFile::BROKEN)
{
emit static_cast<Core*>(core)->fileTransferBrokenUnbroken(*file, false);
file->status = ToxFile::TRANSMITTING;
}
emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, false);
}
else if ((receive_send == 0 || receive_send == 1) && control_type == TOX_FILECONTROL_PAUSE)
{
emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, true);
}
else if (receive_send == 1 && control_type == TOX_FILECONTROL_RESUME_BROKEN)
{
2014-09-30 00:36:42 +08:00
if (length != sizeof(uint64_t))
return;
qDebug() << "Core::onFileControlCallback: TOX_FILECONTROL_RESUME_BROKEN";
2014-09-30 00:36:42 +08:00
uint64_t resumePos = *reinterpret_cast<const uint64_t*>(data);
if (resumePos >= (unsigned)file->filesize)
{
qWarning() << "Core::onFileControlCallback: invalid resume position";
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0); // don't sure about it
return;
}
file->status = ToxFile::TRANSMITTING;
emit static_cast<Core*>(core)->fileTransferBrokenUnbroken(*file, false);
file->bytesSent = resumePos;
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
2014-06-26 04:43:28 +08:00
else
{
qDebug() << QString("Core: File control callback, receive_send=%1, control_type=%2")
.arg(receive_send).arg(control_type);
}
}
2014-07-03 15:22:12 +08:00
void Core::onFileDataCallback(Tox*, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *core)
2014-06-26 04:43:28 +08:00
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::onFileDataCallback: No such file in queue");
return;
}
2014-07-01 19:33:59 +08:00
file->file->write((char*)data,length);
file->bytesSent += length;
2014-07-13 00:40:01 +08:00
//qDebug() << QString("Core::onFileDataCallback: received %1/%2 bytes").arg(file->bytesSent).arg(file->filesize);
emit static_cast<Core*>(core)->fileTransferInfo(file->friendId, file->fileNum,
file->filesize, file->bytesSent, ToxFile::RECEIVING);
2014-06-26 04:43:28 +08:00
}
2014-09-24 23:28:38 +08:00
void Core::onAvatarInfoCallback(Tox*, int32_t friendnumber, uint8_t format,
uint8_t* hash, void* _core)
2014-09-24 22:51:16 +08:00
{
Core* core = static_cast<Core*>(_core);
2014-09-25 00:22:09 +08:00
2014-09-27 03:56:51 +08:00
if (format == TOX_AVATAR_FORMAT_NONE)
{
//qDebug() << "Core: Got null avatar info from" << core->getFriendUsername(friendnumber);
emit core->friendAvatarRemoved(friendnumber);
QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".png"));
QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".hash"));
}
2014-09-27 03:23:20 +08:00
else
{
QByteArray oldHash = Settings::getInstance().getAvatarHash(core->getFriendAddress(friendnumber));
2014-09-30 05:36:23 +08:00
if (QByteArray((char*)hash, TOX_HASH_LENGTH) != oldHash)
// comparison failed miserably if I didn't convert hash to QByteArray
{
qDebug() << "Core: Got new avatar info from" << core->getFriendUsername(friendnumber);
tox_request_avatar_data(core->tox, friendnumber);
}
//else
// qDebug() << "Core: Got same avatar info from" << core->getFriendUsername(friendnumber);
}
2014-09-24 22:51:16 +08:00
}
2014-09-24 23:28:38 +08:00
void Core::onAvatarDataCallback(Tox*, int32_t friendnumber, uint8_t,
uint8_t *hash, uint8_t *data, uint32_t datalen, void *core)
2014-09-24 22:51:16 +08:00
{
2014-09-24 23:28:38 +08:00
QPixmap pic;
pic.loadFromData((uchar*)data, datalen);
2014-09-30 05:36:23 +08:00
if (!pic.isNull())
2014-09-24 23:28:38 +08:00
{
qDebug() << "Core: Got avatar data from" << static_cast<Core*>(core)->getFriendUsername(friendnumber);
2014-09-28 06:19:30 +08:00
Settings::getInstance().saveAvatar(pic, static_cast<Core*>(core)->getFriendAddress(friendnumber));
Settings::getInstance().saveAvatarHash(QByteArray((char*)hash, TOX_HASH_LENGTH), static_cast<Core*>(core)->getFriendAddress(friendnumber));
2014-09-24 23:28:38 +08:00
emit static_cast<Core*>(core)->friendAvatarChanged(friendnumber, pic);
}
2014-09-24 22:51:16 +08:00
}
2014-11-01 22:19:11 +08:00
void Core::onReadReceiptCallback(Tox*, int32_t friendnumber, uint32_t receipt, void *core)
{
emit static_cast<Core*>(core)->receiptRecieved(friendnumber, receipt);
}
2014-06-25 04:11:11 +08:00
void Core::acceptFriendRequest(const QString& userId)
{
int friendId = tox_add_friend_norequest(tox, CUserId(userId).data());
if (friendId == -1) {
emit failedToAddFriend(userId);
} else {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit friendAdded(friendId, userId);
}
}
void Core::requestFriendship(const QString& friendAddress, const QString& message)
{
const QString userId = friendAddress.mid(0, TOX_CLIENT_ID_SIZE * 2);
if (hasFriendWithAddress(friendAddress))
2014-11-10 08:04:35 +08:00
{
emit failedToAddFriend(userId, QString(tr("Friend is already added")));
}
2014-11-10 08:04:35 +08:00
else
{
qDebug() << "Core: requesting friendship of "+friendAddress;
CString cMessage(message);
int friendId = tox_add_friend(tox, CFriendAddress(friendAddress).data(), cMessage.data(), cMessage.size());
2014-11-10 08:04:35 +08:00
if (friendId < 0)
{
emit failedToAddFriend(userId);
2014-11-10 08:04:35 +08:00
}
else
{
// Update our friendAddresses
Settings::getInstance().updateFriendAdress(friendAddress);
emit friendAdded(friendId, userId);
}
2014-06-25 04:11:11 +08:00
}
saveConfiguration();
2014-06-25 04:11:11 +08:00
}
2014-11-09 20:32:19 +08:00
int Core::sendMessage(int friendId, const QString& message)
2014-06-25 04:11:11 +08:00
{
2014-11-09 20:32:19 +08:00
QMutexLocker ml(&messageSendMutex);
CString cMessage(message);
int receipt = tox_send_message(tox, friendId, cMessage.data(), cMessage.size());
emit messageSentResult(friendId, message, receipt);
return receipt;
2014-06-25 04:11:11 +08:00
}
2014-11-09 20:32:19 +08:00
int Core::sendAction(int friendId, const QString &action)
2014-06-25 04:11:11 +08:00
{
2014-11-09 20:32:19 +08:00
QMutexLocker ml(&messageSendMutex);
2014-06-25 04:11:11 +08:00
CString cMessage(action);
2014-11-09 20:32:19 +08:00
int receipt = tox_send_action(tox, friendId, cMessage.data(), cMessage.size());
emit messageSentResult(friendId, action, receipt);
return receipt;
2014-06-25 04:11:11 +08:00
}
void Core::sendTyping(int friendId, bool typing)
{
int ret = tox_set_user_is_typing(tox, friendId, typing);
if (ret == -1)
emit failedToSetTyping(typing);
}
void Core::sendGroupMessage(int groupId, const QString& message)
{
2014-12-29 01:26:35 +08:00
QList<CString> cMessages = splitMessage(message, MAX_GROUP_MESSAGE_LEN);
2014-06-25 04:11:11 +08:00
2014-09-21 18:30:10 +08:00
for (auto &cMsg :cMessages)
{
int ret = tox_group_message_send(tox, groupId, cMsg.data(), cMsg.size());
2014-11-01 21:00:43 +08:00
if (ret == -1)
emit groupSentResult(groupId, message, ret);
}
}
void Core::sendGroupAction(int groupId, const QString& message)
{
2014-12-29 01:26:35 +08:00
QList<CString> cMessages = splitMessage(message, MAX_GROUP_MESSAGE_LEN);
2014-11-01 21:00:43 +08:00
for (auto &cMsg :cMessages)
{
int ret = tox_group_action_send(tox, groupId, cMsg.data(), cMsg.size());
if (ret == -1)
emit groupSentResult(groupId, message, ret);
2014-09-21 18:30:10 +08:00
}
2014-06-25 04:11:11 +08:00
}
2014-11-06 08:22:50 +08:00
void Core::changeGroupTitle(int groupId, const QString& title)
{
CString cTitle(title);
int err = tox_group_set_title(tox, groupId, cTitle.data(), cTitle.size());
if (!err)
2014-11-06 10:50:32 +08:00
emit groupTitleChanged(groupId, getUsername(), title);
2014-11-06 08:22:50 +08:00
}
2014-07-01 19:33:59 +08:00
void Core::sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize)
2014-06-26 04:43:28 +08:00
{
QMutexLocker mlocker(&fileSendMutex);
2014-06-26 04:43:28 +08:00
QByteArray fileName = Filename.toUtf8();
2014-07-01 19:33:59 +08:00
int fileNum = tox_new_file_sender(tox, friendId, filesize, (uint8_t*)fileName.data(), fileName.size());
2014-06-26 04:43:28 +08:00
if (fileNum == -1)
{
qWarning() << "Core::sendFile: Can't create the Tox file sender";
2014-09-24 00:52:06 +08:00
emit fileSendFailed(friendId, Filename);
2014-06-26 04:43:28 +08:00
return;
}
qDebug() << QString("Core::sendFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId);
2014-07-01 19:33:59 +08:00
ToxFile file{fileNum, friendId, fileName, FilePath, ToxFile::SENDING};
file.filesize = filesize;
if (!file.open(false))
{
qWarning() << QString("Core::sendFile: Can't open file, error: %1").arg(file.file->errorString());
}
2014-07-01 19:33:59 +08:00
fileSendQueue.append(file);
emit fileSendStarted(fileSendQueue.last());
}
2014-06-26 04:43:28 +08:00
void Core::pauseResumeFileSend(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
2014-08-31 19:07:31 +08:00
qWarning("Core::pauseResumeFileSend: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit fileTransferPaused(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_PAUSE, nullptr, 0);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
else
qWarning() << "Core::pauseResumeFileSend: File is stopped";
}
2014-06-26 06:30:24 +08:00
void Core::pauseResumeFileRecv(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileRecv: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit fileTransferPaused(file->friendId, file->fileNum, ToxFile::RECEIVING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_PAUSE, nullptr, 0);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
else
qWarning() << "Core::pauseResumeFileRecv: File is stopped or broken";
2014-06-26 04:43:28 +08:00
}
void Core::cancelFileSend(int friendId, int fileNum)
2014-06-26 07:48:20 +08:00
{
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileSend: No such file in queue");
return;
}
2014-06-26 07:48:20 +08:00
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
2014-06-26 07:48:20 +08:00
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
while (file->sendTimer) QThread::msleep(1); // Wait until sendAllFileData returns before deleting
removeFileFromQueue(true, friendId, fileNum);
}
void Core::cancelFileRecv(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileRecv: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::RECEIVING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, friendId, fileNum);
}
void Core::rejectFileRecvRequest(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::rejectFileRecvRequest: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(false, friendId, fileNum);
}
void Core::acceptFileRecvRequest(int friendId, int fileNum, QString path)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::acceptFileRecvRequest: No such file in queue");
return;
}
file->setFilePath(path);
if (!file->open(true))
{
qWarning() << "Core::acceptFileRecvRequest: Unable to open file";
return;
}
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
2014-06-26 07:48:20 +08:00
}
void Core::removeFriend(int friendId, bool fake)
2014-06-25 04:11:11 +08:00
{
if (!tox || fake)
return;
2014-06-25 04:11:11 +08:00
if (tox_del_friend(tox, friendId) == -1) {
emit failedToRemoveFriend(friendId);
} else {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit friendRemoved(friendId);
}
}
void Core::removeGroup(int groupId, bool fake)
2014-06-25 04:11:11 +08:00
{
if (!tox || fake)
2014-10-09 16:59:35 +08:00
return;
2014-06-25 04:11:11 +08:00
tox_del_groupchat(tox, groupId);
2014-11-17 00:45:06 +08:00
2014-11-17 02:11:37 +08:00
if (groupCalls[groupId].active)
leaveGroupCall(groupId);
2014-06-25 04:11:11 +08:00
}
QString Core::getUsername() const
{
QString sname;
int size = tox_get_self_name_size(tox);
uint8_t* name = new uint8_t[size];
if (tox_get_self_name(tox, name) == size)
sname = CString::toString(name, size);
delete[] name;
return sname;
}
2014-06-25 04:11:11 +08:00
void Core::setUsername(const QString& username)
{
CString cUsername(username);
if (tox_set_name(tox, cUsername.data(), cUsername.size()) == -1) {
emit failedToSetUsername(username);
} else {
emit usernameSet(username);
saveConfiguration();
2014-06-25 04:11:11 +08:00
}
}
2014-09-24 22:51:16 +08:00
void Core::setAvatar(uint8_t format, const QByteArray& data)
{
if (tox_set_avatar(tox, format, (uint8_t*)data.constData(), data.size()) != 0)
{
qWarning() << "Core: Failed to set self avatar";
return;
}
QPixmap pic;
pic.loadFromData(data);
2014-09-28 06:19:30 +08:00
Settings::getInstance().saveAvatar(pic, getSelfId().toString());
2014-09-24 22:51:16 +08:00
emit selfAvatarChanged(pic);
2014-09-28 06:19:30 +08:00
// Broadcast our new avatar!
// according to tox.h, we need not broadcast this ourselves, but initial testing indicated elsewise
const uint32_t friendCount = tox_count_friendlist(tox);;
for (unsigned i=0; i<friendCount; i++)
tox_send_avatar_info(tox, i);
2014-09-24 22:51:16 +08:00
}
ToxID Core::getSelfId() const
2014-09-11 19:40:27 +08:00
{
uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox, friendAddress);
2014-09-12 21:41:41 +08:00
return ToxID::fromString(CFriendAddress::toString(friendAddress));
2014-09-11 19:40:27 +08:00
}
QString Core::getIDString() const
{
return getSelfId().toString().left(12);
// 12 is the smallest multiple of four such that
// 16^n > 10^10 (which is roughly the planet's population)
}
QString Core::getStatusMessage() const
{
QString sname;
int size = tox_get_self_status_message_size(tox);
uint8_t* name = new uint8_t[size];
if (tox_get_self_status_message(tox, name, size) == size)
sname = CString::toString(name, size);
delete[] name;
return sname;
}
2014-06-25 04:11:11 +08:00
void Core::setStatusMessage(const QString& message)
{
CString cMessage(message);
if (tox_set_status_message(tox, cMessage.data(), cMessage.size()) == -1) {
emit failedToSetStatusMessage(message);
} else {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit statusMessageSet(message);
}
}
void Core::setStatus(Status status)
{
TOX_USERSTATUS userstatus;
switch (status) {
case Status::Online:
userstatus = TOX_USERSTATUS_NONE;
break;
case Status::Away:
userstatus = TOX_USERSTATUS_AWAY;
break;
case Status::Busy:
userstatus = TOX_USERSTATUS_BUSY;
break;
default:
userstatus = TOX_USERSTATUS_INVALID;
break;
}
if (tox_set_user_status(tox, userstatus) == 0) {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit statusSet(status);
} else {
emit failedToSetStatus(status);
}
}
void Core::onFileTransferFinished(ToxFile file)
{
if (file.direction == file.SENDING)
emit fileUploadFinished(file.filePath);
else
emit fileDownloadFinished(file.filePath);
}
QString Core::sanitize(QString name)
2014-06-25 04:11:11 +08:00
{
2014-09-13 03:38:14 +08:00
// these are pretty much Windows banned filename characters
QList<QChar> banned = {'/', '\\', ':', '<', '>', '"', '|', '?', '*'};
for (QChar c : banned)
name.replace(c, '_');
// also remove leading and trailing periods
if (name[0] == '.')
name[0] = '_';
if (name.endsWith('.'))
name[name.length()-1] = '_';
return name;
}
bool Core::loadConfiguration(QString path)
2014-09-10 21:36:11 +08:00
{
// setting the profile is now the responsibility of the caller
2014-06-25 04:11:11 +08:00
QFile configurationFile(path);
qDebug() << "Core::loadConfiguration: reading from " << path;
2014-06-25 04:11:11 +08:00
if (!configurationFile.exists()) {
qWarning() << "The Tox configuration file was not found";
2014-10-01 19:23:00 +08:00
return true;
2014-06-25 04:11:11 +08:00
}
if (!configurationFile.open(QIODevice::ReadOnly)) {
qCritical() << "File " << path << " cannot be opened";
2014-10-01 19:23:00 +08:00
return true;
2014-06-25 04:11:11 +08:00
}
qint64 fileSize = configurationFile.size();
if (fileSize > 0) {
QByteArray data = configurationFile.readAll();
2014-10-01 19:23:00 +08:00
int error = tox_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size());
if (error < 0)
{
qWarning() << "Core: tox_load failed with error "<< error;
2014-10-01 19:23:00 +08:00
}
else if (error == 1) // Encrypted data save
{
2014-10-24 21:56:27 +08:00
if (!Settings::getInstance().getEncryptTox())
Widget::getInstance()->showWarningMsgBox(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
uint8_t salt[tox_pass_salt_length()];
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
do
2014-10-01 19:23:00 +08:00
{
while (!pwsaltedkeys[ptMain])
2014-10-12 18:24:57 +08:00
{
emit blockingGetPassword(tr("Tox datafile decryption password"), ptMain, salt);
if (!pwsaltedkeys[ptMain])
Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password."));
2014-10-12 18:24:57 +08:00
}
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
if (error != 0)
2014-10-12 18:24:57 +08:00
{
QMessageBox msgb;
msgb.moveToThread(qApp->thread());
QPushButton *tryAgain = msgb.addButton(tr("Try Again"), QMessageBox::AcceptRole);
QPushButton *cancel = msgb.addButton(tr("Change profile"), QMessageBox::RejectRole);
QPushButton *wipe = msgb.addButton(tr("Reinit current profile"), QMessageBox::ActionRole);
msgb.setDefaultButton(tryAgain);
2014-10-19 22:59:58 +08:00
msgb.setWindowTitle(tr("Password error"));
msgb.setText(tr("Wrong password has been entered"));
// msgb.setInformativeText(tr(""));
msgb.exec();
if (msgb.clickedButton() == tryAgain)
clearPassword(ptMain);
else if (msgb.clickedButton() == cancel)
{
configurationFile.close();
return false;
}
else if (msgb.clickedButton() == wipe)
{
clearPassword(ptMain);
Settings::getInstance().setEncryptTox(false);
error = 0;
}
}
else
Settings::getInstance().setEncryptTox(true);
} while (error != 0);
}
}
2014-06-25 04:11:11 +08:00
configurationFile.close();
// set GUI with user and statusmsg
QString name = getUsername();
2014-10-18 09:15:26 +08:00
if (!name.isEmpty())
emit usernameSet(name);
QString msg = getStatusMessage();
2014-10-18 09:15:26 +08:00
if (!msg.isEmpty())
emit statusMessageSet(msg);
2014-10-18 09:15:26 +08:00
QString id = getSelfId().toString();
if (!id.isEmpty())
emit idSet(id);
// tox core is already decrypted
if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs())
{
bool error = true;
// get salt
QFile file(HistoryKeeper::getHistoryPath());
file.open(QIODevice::ReadOnly);
QByteArray data = file.read(tox_pass_encryption_extra_length());
file.close();
uint8_t salt[tox_pass_salt_length()];
int err = tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
if (err)
{ // maybe we should handle this better
qWarning() << "Core: history db isn't encrypted, but encryption is set!! No history loaded...";
}
else
{
do
{
while (!pwsaltedkeys[ptHistory])
{
emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, salt);
if (!pwsaltedkeys[ptHistory])
Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password."));
}
if (!HistoryKeeper::checkPassword())
{
if (QMessageBox::Ok == Widget::getInstance()->showWarningMsgBox(tr("Encrypted log"),
tr("Your history is encrypted with different password.\nDo you want to try another password?"),
QMessageBox::Ok | QMessageBox::Cancel))
{
error = true;
clearPassword(ptHistory);
}
else
{
error = false;
clearPassword(ptHistory);
Widget::getInstance()->showWarningMsgBox(tr("History"), tr("Due to incorret password history will be disabled."));
Settings::getInstance().setEncryptLogs(false);
Settings::getInstance().setEnableLogging(false);
}
} else {
error = false;
}
} while (error);
}
}
2014-06-25 04:11:11 +08:00
loadFriends();
2014-10-01 19:23:00 +08:00
return true;
2014-06-25 04:11:11 +08:00
}
void Core::saveConfiguration()
2014-06-25 04:11:11 +08:00
{
2015-01-24 22:31:11 +08:00
if (QThread::currentThread() != coreThread)
return (void) QMetaObject::invokeMethod(this, "saveConfiguration");
QString dir = Settings::getSettingsDirPath();
QDir directory(dir);
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
qCritical() << "Error while creating directory " << dir;
2014-07-01 03:26:37 +08:00
return;
}
QString profile = Settings::getInstance().getCurrentProfile();
2014-10-16 00:18:32 +08:00
if (profile == "")
{ // no profile active; this should only happen on startup, if at all
profile = sanitize(getUsername());
2014-10-16 00:18:32 +08:00
if (profile == "") // happens on creation of a new Tox ID
profile = getIDString();
2014-10-16 00:18:32 +08:00
Settings::getInstance().setCurrentProfile(profile);
}
2014-10-16 00:18:32 +08:00
QString path = directory.filePath(profile + TOX_EXT);
saveConfiguration(path);
}
2014-07-01 03:26:37 +08:00
void Core::saveConfiguration(const QString& path)
{
2015-01-24 22:31:11 +08:00
if (QThread::currentThread() != coreThread)
return (void) QMetaObject::invokeMethod(this, "saveConfiguration", Q_ARG(const QString&, path));
if (!tox)
{
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
return;
2014-06-25 04:11:11 +08:00
}
Settings::getInstance().save();
2014-06-25 04:11:11 +08:00
QSaveFile configurationFile(path);
2015-01-24 22:31:11 +08:00
if (!configurationFile.open(QIODevice::WriteOnly))
{
2014-06-25 04:11:11 +08:00
qCritical() << "File " << path << " cannot be opened";
return;
}
qDebug() << "Core: writing tox_save to " << path;
2015-01-24 22:31:11 +08:00
uint32_t fileSize;
bool encrypt = Settings::getInstance().getEncryptTox();
if (encrypt)
2014-10-12 15:31:48 +08:00
fileSize = tox_encrypted_size(tox);
else
fileSize = tox_size(tox);
2015-01-24 22:31:11 +08:00
if (fileSize > 0 && fileSize <= INT32_MAX)
{
2014-06-25 04:11:11 +08:00
uint8_t *data = new uint8_t[fileSize];
if (encrypt)
2014-10-12 15:31:48 +08:00
{
if (!pwsaltedkeys[ptMain])
2014-10-12 15:31:48 +08:00
{
Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Will be saved without encryption!"));
2014-10-19 22:59:58 +08:00
tox_save(tox, data);
}
else
{
int ret = tox_encrypted_key_save(tox, data, pwsaltedkeys[ptMain]);
2014-10-19 22:59:58 +08:00
if (ret == -1)
{
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!";
return;
}
2014-10-12 15:31:48 +08:00
}
2014-10-19 22:59:58 +08:00
}
else
2015-01-24 22:31:11 +08:00
{
tox_save(tox, data);
2015-01-24 22:31:11 +08:00
}
2014-10-12 15:31:48 +08:00
2014-06-25 04:11:11 +08:00
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
configurationFile.commit();
delete[] data;
}
}
void Core::switchConfiguration(const QString& profile)
{
if (profile.isEmpty())
qDebug() << "Core: creating new Id";
else
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
2014-10-16 00:18:32 +08:00
saveConfiguration();
ready = false;
clearPassword(ptMain);
clearPassword(ptHistory);
toxTimer->stop();
2014-11-02 19:24:14 +08:00
Widget::getInstance()->setEnabledThreadsafe(false);
if (tox) {
toxav_kill(toxav);
toxav = nullptr;
tox_kill(tox);
tox = nullptr;
}
2014-10-09 16:59:35 +08:00
emit selfAvatarChanged(QPixmap(":/img/contact_dark.png"));
emit blockingClearContacts(); // we need this to block, but signals are required for thread safety
if (profile.isEmpty())
loadPath = "";
else
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
// the new profile needs to be set before resetting the settings, so that
// we don't load the old profile's profile.ini
Settings::getInstance().setCurrentProfile(profile);
Settings::getInstance().save(false); // save new profile, but don't write old profile info to newprofile.ini
Settings::resetInstance();
HistoryKeeper::getInstance()->resetInstance();
2014-10-09 16:59:35 +08:00
start();
2014-11-02 19:24:14 +08:00
Widget::getInstance()->setEnabledThreadsafe(true);
}
2014-06-25 04:11:11 +08:00
void Core::loadFriends()
{
const uint32_t friendCount = tox_count_friendlist(tox);
if (friendCount > 0) {
// assuming there are not that many friends to fill up the whole stack
int32_t *ids = new int32_t[friendCount];
tox_get_friendlist(tox, ids, friendCount);
uint8_t clientId[TOX_CLIENT_ID_SIZE];
for (int32_t i = 0; i < static_cast<int32_t>(friendCount); ++i) {
if (tox_get_client_id(tox, ids[i], clientId) == 0) {
emit friendAdded(ids[i], CUserId::toString(clientId));
2014-10-09 16:59:35 +08:00
2014-06-25 04:11:11 +08:00
const int nameSize = tox_get_name_size(tox, ids[i]);
if (nameSize > 0) {
uint8_t *name = new uint8_t[nameSize];
if (tox_get_name(tox, ids[i], name) == nameSize) {
2014-09-29 01:57:27 +08:00
emit friendUsernameChanged(ids[i], CString::toString(name, nameSize));
2014-06-25 04:11:11 +08:00
}
delete[] name;
}
const int statusMessageSize = tox_get_status_message_size(tox, ids[i]);
if (statusMessageSize > 0) {
uint8_t *statusMessage = new uint8_t[statusMessageSize];
if (tox_get_status_message(tox, ids[i], statusMessage, statusMessageSize) == statusMessageSize) {
2014-09-29 01:57:27 +08:00
emit friendStatusMessageChanged(ids[i], CString::toString(statusMessage, statusMessageSize));
2014-06-25 04:11:11 +08:00
}
delete[] statusMessage;
}
checkLastOnline(ids[i]);
}
}
delete[] ids;
}
}
void Core::checkLastOnline(int friendId) {
const uint64_t lastOnline = tox_get_last_online(tox, friendId);
if (lastOnline > 0) {
emit friendLastSeenChanged(friendId, QDateTime::fromTime_t(lastOnline));
}
}
int Core::getGroupNumberPeers(int groupId) const
{
return tox_group_number_peers(tox, groupId);
}
QString Core::getGroupPeerName(int groupId, int peerId) const
{
QString name;
uint8_t nameArray[TOX_MAX_NAME_LENGTH];
int length = tox_group_peername(tox, groupId, peerId, nameArray);
if (length == -1)
{
qWarning() << "Core::getGroupPeerName: Unknown error";
return name;
}
name = CString::toString(nameArray, length);
return name;
}
2014-11-23 23:55:49 +08:00
ToxID Core::getGroupPeerToxID(int groupId, int peerId) const
{
ToxID peerToxID;
uint8_t rawID[TOX_CLIENT_ID_SIZE];
int res = tox_group_peer_pubkey(tox, groupId, peerId, rawID);
if (res == -1)
{
qWarning() << "Core::getGroupPeerToxID: Unknown error";
return peerToxID;
}
peerToxID = ToxID::fromString(CUserId::toString(rawID));
return peerToxID;
}
2014-06-25 04:11:11 +08:00
QList<QString> Core::getGroupPeerNames(int groupId) const
{
QList<QString> names;
int nPeers = getGroupNumberPeers(groupId);
if (nPeers == -1)
{
qWarning() << "Core::getGroupPeerNames: Unable to get number of peers";
return names;
}
uint8_t namesArray[nPeers][TOX_MAX_NAME_LENGTH];
uint16_t* lengths = new uint16_t[nPeers];
int result = tox_group_get_names(tox, groupId, namesArray, lengths, nPeers);
if (result != nPeers)
{
qWarning() << "Core::getGroupPeerNames: Unexpected result";
return names;
}
for (int i=0; i<nPeers; i++)
names.push_back(CString::toString(namesArray[i], lengths[i]));
return names;
}
int Core::joinGroupchat(int32_t friendnumber, uint8_t type, const uint8_t* friend_group_public_key,uint16_t length) const
2014-06-25 04:11:11 +08:00
{
if (type == TOX_GROUPCHAT_TYPE_TEXT)
{
qDebug() << QString("Trying to join text groupchat invite sent by friend %1").arg(friendnumber);
return tox_join_groupchat(tox, friendnumber, friend_group_public_key,length);
}
else if (type == TOX_GROUPCHAT_TYPE_AV)
{
qDebug() << QString("Trying to join AV groupchat invite sent by friend %1").arg(friendnumber);
return toxav_join_av_groupchat(tox, friendnumber, friend_group_public_key, length,
&Audio::playGroupAudioQueued, const_cast<Core*>(this));
}
else
{
qWarning() << "Core::joinGroupchat: Unknown groupchat type "<<type;
return -1;
}
2014-06-25 04:11:11 +08:00
}
void Core::quitGroupChat(int groupId) const
{
tox_del_groupchat(tox, groupId);
}
void Core::removeFileFromQueue(bool sendQueue, int friendId, int fileId)
{
bool found = false;
if (sendQueue)
{
for (int i=0; i<fileSendQueue.size();)
{
if (fileSendQueue[i].friendId == friendId && fileSendQueue[i].fileNum == fileId)
{
found = true;
fileSendQueue[i].file->close();
delete fileSendQueue[i].file;
fileSendQueue.removeAt(i);
continue;
}
i++;
}
}
else
{
for (int i=0; i<fileRecvQueue.size();)
{
if (fileRecvQueue[i].friendId == friendId && fileRecvQueue[i].fileNum == fileId)
{
found = true;
fileRecvQueue[i].file->close();
delete fileRecvQueue[i].file;
fileRecvQueue.removeAt(i);
continue;
}
i++;
}
}
if (!found)
qWarning() << "Core::removeFileFromQueue: No such file in queue";
}
void Core::sendAllFileData(Core *core, ToxFile* file)
{
if (file->status == ToxFile::PAUSED)
{
2014-08-31 19:13:46 +08:00
file->sendTimer->start(5+TOX_FILE_INTERVAL);
return;
}
else if (file->status == ToxFile::STOPPED)
2014-06-27 09:06:56 +08:00
{
qWarning("Core::sendAllFileData: File is stopped");
file->sendTimer->disconnect();
delete file->sendTimer;
file->sendTimer = nullptr;
2014-06-27 09:06:56 +08:00
return;
}
emit core->fileTransferInfo(file->friendId, file->fileNum, file->filesize, file->bytesSent, ToxFile::SENDING);
// qApp->processEvents();
long long chunkSize = tox_file_data_size(core->tox, file->friendId);
if (chunkSize == -1)
{
qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send");
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, file->friendId, file->fileNum);
return;
}
//qDebug() << "chunkSize: " << chunkSize;
chunkSize = std::min(chunkSize, file->filesize);
uint8_t* data = new uint8_t[chunkSize];
file->file->seek(file->bytesSent);
int readSize = file->file->read((char*)data, chunkSize);
if (readSize == -1)
2014-06-27 09:06:56 +08:00
{
qWarning() << QString("Core::sendAllFileData: Error reading from file: %1").arg(file->file->errorString());
delete[] data;
2014-08-29 18:52:09 +08:00
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, file->friendId, file->fileNum);
2014-06-27 09:06:56 +08:00
return;
}
else if (readSize == 0)
2014-06-28 03:59:25 +08:00
{
qWarning() << QString("Core::sendAllFileData: Nothing to read from file: %1").arg(file->file->errorString());
delete[] data;
2014-08-29 18:52:09 +08:00
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, file->friendId, file->fileNum);
2014-06-28 03:59:25 +08:00
return;
}
if (tox_file_send_data(core->tox, file->friendId, file->fileNum, data, readSize) == -1)
2014-06-30 05:38:48 +08:00
{
//qWarning("Core::fileHeartbeat: Error sending data chunk");
//core->process();
delete[] data;
//QThread::msleep(1);
file->sendTimer->start(1+TOX_FILE_INTERVAL);
2014-06-30 05:38:48 +08:00
return;
}
delete[] data;
file->bytesSent += readSize;
//qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file->bytesSent).arg(file->fileData.size());
if (file->bytesSent < file->filesize)
2014-06-28 03:59:25 +08:00
{
file->sendTimer->start(TOX_FILE_INTERVAL);
return;
}
else
2014-06-30 05:38:48 +08:00
{
2014-08-29 18:52:09 +08:00
//qDebug("Core: File transfer finished");
file->sendTimer->disconnect();
delete file->sendTimer;
file->sendTimer = nullptr;
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
2014-08-29 18:52:09 +08:00
//emit core->fileTransferFinished(*file);
2014-06-30 05:38:48 +08:00
}
}
void Core::groupInviteFriend(int friendId, int groupId)
{
tox_invite_friend(tox, friendId, groupId);
}
2014-06-30 05:38:48 +08:00
void Core::createGroup(uint8_t type)
2014-06-30 05:38:48 +08:00
{
if (type == TOX_GROUPCHAT_TYPE_TEXT)
2014-11-13 19:18:04 +08:00
{
emit emptyGroupCreated(tox_add_groupchat(tox));
2014-11-13 19:18:04 +08:00
}
else if (type == TOX_GROUPCHAT_TYPE_AV)
2014-11-13 19:18:04 +08:00
{
emit emptyGroupCreated(toxav_add_av_groupchat(tox, &Audio::playGroupAudioQueued, this));
2014-11-13 19:18:04 +08:00
}
else
2014-11-13 19:18:04 +08:00
{
qWarning() << "Core::createGroup: Unknown type "<<type;
2014-11-13 19:18:04 +08:00
}
2014-06-30 05:38:48 +08:00
}
bool Core::hasFriendWithAddress(const QString &addr) const
{
// Valid length check
if (addr.length() != (TOX_FRIEND_ADDRESS_SIZE * 2))
2014-11-10 08:04:35 +08:00
{
return false;
}
QString pubkey = addr.left(TOX_CLIENT_ID_SIZE * 2);
return hasFriendWithPublicKey(pubkey);
}
bool Core::hasFriendWithPublicKey(const QString &pubkey) const
{
// Valid length check
if (pubkey.length() != (TOX_CLIENT_ID_SIZE * 2))
2014-11-10 08:04:35 +08:00
{
return false;
}
bool found = false;
const uint32_t friendCount = tox_count_friendlist(tox);
2014-11-10 08:04:35 +08:00
if (friendCount > 0)
{
int32_t *ids = new int32_t[friendCount];
tox_get_friendlist(tox, ids, friendCount);
2014-11-10 08:04:35 +08:00
for (int32_t i = 0; i < static_cast<int32_t>(friendCount); ++i)
{
// getFriendAddress may return either id (public key) or address
QString addrOrId = getFriendAddress(ids[i]);
// Set true if found
if (addrOrId.toUpper().startsWith(pubkey.toUpper()))
2014-11-10 08:04:35 +08:00
{
found = true;
break;
}
}
delete[] ids;
}
return found;
}
2014-09-01 04:26:45 +08:00
QString Core::getFriendAddress(int friendNumber) const
{
2014-09-01 04:26:45 +08:00
// If we don't know the full address of the client, return just the id, otherwise get the full address
uint8_t rawid[TOX_CLIENT_ID_SIZE];
tox_get_client_id(tox, friendNumber, rawid);
QByteArray data((char*)rawid,TOX_CLIENT_ID_SIZE);
QString id = data.toHex().toUpper();
QString addr = Settings::getInstance().getFriendAdress(id);
if (addr.size() > id.size())
return addr;
2014-09-01 04:26:45 +08:00
return id;
}
2014-09-21 18:30:10 +08:00
QString Core::getFriendUsername(int friendnumber) const
{
uint8_t name[TOX_MAX_NAME_LENGTH];
tox_get_name(tox, friendnumber, name);
return CString::toString(name, tox_get_name_size(tox, friendnumber));
}
2014-12-29 01:26:35 +08:00
QList<CString> Core::splitMessage(const QString &message, int maxLen)
2014-09-21 18:30:10 +08:00
{
QList<CString> splittedMsgs;
QByteArray ba_message(message.toUtf8());
2014-12-29 01:26:35 +08:00
while (ba_message.size() > maxLen)
2014-09-21 18:30:10 +08:00
{
2014-12-29 01:26:35 +08:00
int splitPos = ba_message.lastIndexOf(' ', maxLen - 1);
2014-09-21 18:30:10 +08:00
if (splitPos <= 0)
{
2014-12-29 01:26:35 +08:00
splitPos = maxLen;
2014-09-21 18:30:10 +08:00
if (ba_message[splitPos] & 0x80)
{
do {
splitPos--;
} while (!(ba_message[splitPos] & 0x40));
}
splitPos--;
}
splittedMsgs.push_back(CString(ba_message.left(splitPos + 1)));
ba_message = ba_message.mid(splitPos + 1);
}
splittedMsgs.push_back(CString(ba_message));
return splittedMsgs;
}
void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
{
2014-10-12 07:58:10 +08:00
if (password.isEmpty())
{
clearPassword(passtype);
2014-10-12 07:58:10 +08:00
return;
}
if (!pwsaltedkeys[passtype])
pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()];
2014-10-12 07:58:10 +08:00
CString str(password);
if (salt)
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype]);
else
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]);
password.clear();
}
void Core::clearPassword(PasswordType passtype)
2014-10-12 07:58:10 +08:00
{
if (pwsaltedkeys[passtype])
2014-10-12 07:58:10 +08:00
{
delete[] pwsaltedkeys[passtype];
pwsaltedkeys[passtype] = nullptr;
2014-10-12 07:58:10 +08:00
}
}
QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
2014-10-12 07:58:10 +08:00
{
if (!pwsaltedkeys[passtype])
2014-10-12 15:31:48 +08:00
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)
2014-10-12 07:58:10 +08:00
{
qWarning() << "Core::encryptData: encryption failed";
return QByteArray();
}
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + tox_pass_encryption_extra_length());
2014-10-12 07:58:10 +08:00
}
QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype)
2014-10-12 18:24:57 +08:00
{
if (!pwsaltedkeys[passtype])
2014-10-12 18:24:57 +08:00
return QByteArray();
int sz = data.size() - tox_pass_encryption_extra_length();
uint8_t decrypted[sz];
if (tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], decrypted) != sz)
2014-10-12 18:24:57 +08:00
{
qWarning() << "Core::decryptData: decryption failed";
return QByteArray();
}
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
}
bool Core::isPasswordSet(PasswordType passtype)
{
if (pwsaltedkeys[passtype])
return true;
return false;
}
QString Core::getPeerName(const ToxID& id) const
{
QString name;
CUserId cid(id.toString());
int friendId = tox_get_friend_number(tox, (uint8_t*)cid.data());
if (friendId < 0)
{
qWarning() << "Core::getPeerName: No such peer "+id.toString();
return name;
}
2014-11-03 06:37:54 +08:00
const int nameSize = tox_get_name_size(tox, friendId);
if (nameSize <= 0)
{
//qDebug() << "Core::getPeerName: Can't get name of friend "+QString().setNum(friendId)+" ("+id.toString()+")";
return name;
}
2014-11-03 06:37:54 +08:00
uint8_t* cname = new uint8_t[nameSize<TOX_MAX_NAME_LENGTH ? TOX_MAX_NAME_LENGTH : nameSize];
if (tox_get_name(tox, friendId, cname) != nameSize)
{
qWarning() << "Core::getPeerName: Can't get name of friend "+QString().setNum(friendId)+" ("+id.toString()+")";
delete[] cname;
return name;
}
name = name.fromLocal8Bit((char*)cname, nameSize);
2014-11-03 06:37:54 +08:00
delete[] cname;
return name;
}
bool Core::isReady()
{
return ready;
}
2014-11-16 00:10:04 +08:00
void Core::setNospam(uint32_t nospam)
{
uint8_t *nspm = reinterpret_cast<uint8_t*>(&nospam);
std::reverse(nspm, nspm + 4);
tox_set_nospam(tox, nospam);
}
void Core::resetCallSources()
{
for (ToxGroupCall& call : groupCalls)
call.alSources.clear();
for (ToxCall& call : calls)
{
if (call.active && call.alSource)
{
ALuint tmp = call.alSource;
call.alSource = 0;
alDeleteSources(1, &tmp);
alGenSources(1, &call.alSource);
}
}
}