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

Make Nexus own and start the LoginScreen

And start implementing some of the required methods to make Core, LoginScreen and Nexus use Profile
This commit is contained in:
tux3 2015-06-04 01:30:17 +02:00
parent 7d6167d90c
commit 032c561e62
13 changed files with 258 additions and 55 deletions

View File

@ -24,6 +24,7 @@
#include "src/audio.h"
#include "src/profilelocker.h"
#include "src/avatarbroadcaster.h"
#include "src/profile.h"
#include "corefile.h"
#include <tox/tox.h>
@ -52,11 +53,9 @@ QThread* Core::coreThread{nullptr};
#define MAX_GROUP_MESSAGE_LEN 1024
Core::Core(QThread *CoreThread, QString loadPath) :
tox(nullptr), toxav(nullptr), loadPath(loadPath), ready{false}
Core::Core(QThread *CoreThread, Profile& profile) :
tox(nullptr), toxav(nullptr), profile(profile), ready{false}
{
qDebug() << "loading Tox from" << loadPath;
coreThread = CoreThread;
Audio::getInstance();
@ -239,7 +238,7 @@ void Core::start()
{
qDebug() << "Starting up";
QByteArray savedata = loadToxSave(loadPath);
QByteArray savedata = profile.loadToxSave();
make_tox(savedata);
@ -888,7 +887,7 @@ QString Core::sanitize(QString name)
QByteArray Core::loadToxSave(QString path)
{
QByteArray data;
loadPath = ""; // if not empty upon return, then user forgot a password and is switching
//loadPath = ""; // if not empty upon return, then user forgot a password and is switching
// If we can't get a lock, then another instance is already using that profile
while (!ProfileLocker::lock(QFileInfo(path).baseName()))
@ -1019,10 +1018,10 @@ void Core::switchConfiguration(const QString& _profile)
emit selfAvatarChanged(QPixmap(":/img/contact_dark.svg"));
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);
//if (profile.isEmpty())
//loadPath = "";
//else
// loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
Settings::getInstance().switchProfile(profile);
HistoryKeeper::resetInstance();

View File

@ -29,6 +29,7 @@
#include "coredefines.h"
#include "toxid.h"
class Profile;
template <typename T> class QList;
class QTimer;
class QString;
@ -45,7 +46,7 @@ class Core : public QObject
public:
enum PasswordType {ptMain = 0, ptHistory, ptCounter};
explicit Core(QThread* coreThread, QString initialLoadPath);
explicit Core(QThread* coreThread, Profile& profile);
static Core* getInstance(); ///< Returns the global widget's Core instance
~Core();
@ -292,7 +293,7 @@ private:
Tox* tox;
ToxAv* toxav;
QTimer *toxTimer, *fileTimer; //, *saveTimer;
QString loadPath; // meaningless after start() is called
Profile& profile;
int dhtServerId;
static ToxCall calls[TOXAV_MAX_CALLS];
#ifdef QTOX_FILTER_AUDIO

View File

@ -110,7 +110,7 @@ QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
if (!tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
pwsaltedkeys[passtype], encrypted, nullptr))
{
qWarning() << "encryptData: encryption failed";
qWarning() << "Encryption failed";
return QByteArray();
}
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
@ -126,7 +126,7 @@ QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype)
if (!tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
pwsaltedkeys[passtype], decrypted, nullptr))
{
qWarning() << "decryptData: decryption failed";
qWarning() << "Decryption failed";
return QByteArray();
}
return QByteArray(reinterpret_cast<char*>(decrypted), sz);

View File

@ -12,6 +12,7 @@
See the COPYING file for more details.
*/
#include "toxme.h"
#include "widget/widget.h"
#include "misc/settings.h"
#include "src/nexus.h"
@ -20,6 +21,7 @@
#include "src/widget/toxsave.h"
#include "src/autoupdate.h"
#include "src/profilelocker.h"
#include "src/widget/loginscreen.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QDateTime>
@ -31,9 +33,6 @@
#include <QProcess>
#include <sodium.h>
#include "toxme.h"
#include <unistd.h>
#define EXIT_UPDATE_MACX 218 //We track our state using unique exit codes when debugging
@ -292,7 +291,6 @@ int main(int argc, char *argv[])
Nexus::getInstance().start();
// Run
a.setQuitOnLastWindowClosed(false);
int errorcode = a.exec();
#ifdef LOG_TO_FILE

View File

@ -1,12 +1,16 @@
#include "nexus.h"
#include "profile.h"
#include "src/core/core.h"
#include "misc/settings.h"
#include "video/camerasource.h"
#include "widget/gui.h"
#include "widget/loginscreen.h"
#include <QThread>
#include <QDebug>
#include <QImageReader>
#include <QFile>
#include <QApplication>
#include <cassert>
#ifdef Q_OS_ANDROID
#include <src/widget/androidgui.h>
@ -18,18 +22,14 @@ static Nexus* nexus{nullptr};
Nexus::Nexus(QObject *parent) :
QObject(parent),
core{nullptr},
coreThread{nullptr},
profile{nullptr},
widget{nullptr},
androidgui{nullptr},
started{false}
androidgui{nullptr}
{
}
Nexus::~Nexus()
{
delete core;
delete coreThread;
#ifdef Q_OS_ANDROID
delete androidgui;
#else
@ -39,9 +39,6 @@ Nexus::~Nexus()
void Nexus::start()
{
if (started)
return;
qDebug() << "Starting up";
// Setup the environment
@ -59,19 +56,30 @@ void Nexus::start()
qRegisterMetaType<Core::PasswordType>("Core::PasswordType");
qRegisterMetaType<std::shared_ptr<VideoFrame>>("std::shared_ptr<VideoFrame>");
// Create and show login screen
loginScreen = new LoginScreen();
showLogin();
}
void Nexus::showLogin()
{
((QApplication*)qApp)->setQuitOnLastWindowClosed(true);
loginScreen->reset();
loginScreen->show();
}
void Nexus::showMainGUI()
{
assert(profile);
((QApplication*)qApp)->setQuitOnLastWindowClosed(false);
loginScreen->close();
// Create GUI
#ifndef Q_OS_ANDROID
widget = Widget::getInstance();
#endif
// Create Core
QString profilePath = Settings::getInstance().detectProfile();
coreThread = new QThread(this);
coreThread->setObjectName("qTox Core");
core = new Core(coreThread, profilePath);
core->moveToThread(coreThread);
connect(coreThread, &QThread::started, core, &Core::start);
// Start GUI
#ifdef Q_OS_ANDROID
androidgui = new AndroidGUI;
@ -88,6 +96,7 @@ void Nexus::start()
GUI::setEnabled(false);
// Connections
Core* core = profile->getCore();
#ifdef Q_OS_ANDROID
connect(core, &Core::connected, androidgui, &AndroidGUI::onConnected);
connect(core, &Core::disconnected, androidgui, &AndroidGUI::onDisconnected);
@ -138,10 +147,7 @@ void Nexus::start()
connect(widget, &Widget::changeProfile, core, &Core::switchConfiguration);
#endif
// Start Core
coreThread->start();
started = true;
profile->startCore();
}
Nexus& Nexus::getInstance()
@ -160,7 +166,20 @@ void Nexus::destroyInstance()
Core* Nexus::getCore()
{
return getInstance().core;
Nexus& nexus = getInstance();
if (!nexus.profile)
return nullptr;
return nexus.profile->getCore();
}
Profile* Nexus::getProfile()
{
return getInstance().profile;
}
void Nexus::setProfile(Profile* profile)
{
getInstance().profile = profile;
}
AndroidGUI* Nexus::getAndroidGUI()

View File

@ -3,10 +3,11 @@
#include <QObject>
class QThread;
class Core;
class Widget;
class AndroidGUI;
class Profile;
class LoginScreen;
class Core;
/// This class is in charge of connecting various systems together
/// and forwarding signals appropriately to the right objects
@ -15,11 +16,17 @@ class Nexus : public QObject
{
Q_OBJECT
public:
void start(); ///< Will initialise the systems (GUI, Core, ...)
void start(); ///< Sets up invariants and calls showLogin
void showLogin(); ///< Shows the login screen
/// Hides the login screen and shows the GUI for the given profile.
/// Will delete the current GUI, if it exists.
void showMainGUI();
static Nexus& getInstance();
static void destroyInstance();
static Core* getCore(); ///< Will return 0 if not started
static Profile* getProfile(); ///< Will return 0 if not started
static void setProfile(Profile* profile); ///< Delete the current profile, if any, and replaces it
static AndroidGUI* getAndroidGUI(); ///< Will return 0 if not started
static Widget* getDesktopGUI(); ///< Will return 0 if not started
static QString getSupportedImageFilter();
@ -30,11 +37,10 @@ private:
~Nexus();
private:
Core* core;
QThread* coreThread;
Profile* profile;
Widget* widget;
AndroidGUI* androidgui;
bool started;
LoginScreen* loginScreen;
};
#endif // NEXUS_H

View File

@ -1,19 +1,48 @@
#include "profile.h"
#include "profilelocker.h"
#include "src/misc/settings.h"
#include "src/core/core.h"
#include <cassert>
#include <QDir>
#include <QFileInfo>
#include <QThread>
#include <QObject>
#include <QDebug>
QVector<QString> Profile::profiles;
Profile::Profile()
Profile::Profile(QString name, QString password)
: name{name}, password{password}
{
coreThread = new QThread();
coreThread->setObjectName("qTox Core");
core = new Core(coreThread, *this);
core->moveToThread(coreThread);
QObject::connect(coreThread, &QThread::started, core, &Core::start);
}
Profile* Profile::loadProfile(QString name, QString password)
{
if (ProfileLocker::hasLock())
{
}
if (!ProfileLocker::lock(name))
{
qWarning() << "Failed to lock profile "<<name;
return nullptr;
}
return new Profile(name, password);
}
Profile::~Profile()
{
delete core;
delete coreThread;
assert(ProfileLocker::getCurLockName() == name);
ProfileLocker::unlock();
}
QVector<QString> Profile::getFilesByExt(QString extension)
@ -52,3 +81,86 @@ QVector<QString> Profile::getProfiles()
{
return profiles;
}
Core* Profile::getCore()
{
return core;
}
void Profile::startCore()
{
coreThread->start();
}
QByteArray Profile::loadToxSave()
{
QByteArray data;
QString path = Settings::getSettingsDirPath() + QDir::separator() + name;
QFile saveFile(path);
qint64 fileSize;
qDebug() << "Loading tox save "<<path;
if (!saveFile.exists())
{
qWarning() << "The tox save file "<<path<<" was not found";
goto fail;
}
if (!saveFile.open(QIODevice::ReadOnly))
{
qCritical() << "The tox save file " << path << " couldn't' be opened";
goto fail;
}
fileSize = saveFile.size();
if (fileSize <= 0)
{
qWarning() << "The tox save file"<<path<<" is empty!";
goto fail;
}
data = saveFile.readAll();
if (tox_is_data_encrypted((uint8_t*)data.data()))
{
if (password.isEmpty())
{
qCritical() << "The tox save file is encrypted, but we don't have a password!";
goto fail;
}
uint8_t salt[TOX_PASS_SALT_LENGTH];
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
core->setPassword(password, Core::ptMain, salt);
data = core->decryptData(data, Core::ptMain);
if (data.isEmpty())
qCritical() << "Failed to decrypt the tox save file";
}
else
{
if (!password.isEmpty())
qWarning() << "We have a password, but the tox save file is not encrypted";
}
fail:
saveFile.close();
return data;
}
bool Profile::isProfileEncrypted(QString name)
{
uint8_t data[encryptHeaderSize] = {0};
QString path = Settings::getSettingsDirPath() + QDir::separator() + name + ".tox";
QFile saveFile(path);
if (!saveFile.open(QIODevice::ReadOnly))
{
qWarning() << "Couldn't open tox save "<<path;
return false;
}
saveFile.read((char*)data, encryptHeaderSize);
saveFile.close();
return tox_is_data_encrypted(data);
}

View File

@ -3,20 +3,34 @@
#include <QVector>
#include <QString>
#include <QByteArray>
class Core;
class QThread;
/// Manages user profiles
class Profile
{
public:
Profile();
/// Locks and loads an existing profile and create the associate Core* instance
/// Returns a nullptr on error, for example if the profile is already in use
Profile* loadProfile(QString name, QString password);
~Profile();
Core* getCore();
void startCore(); ///< Starts the Core thread
QByteArray loadToxSave(); ///< Loads the profile's .tox save from file, unencrypted
/// Scan for profile, automatically importing them if needed
/// NOT thread-safe
static void scanProfiles();
static QVector<QString> getProfiles();
/// Checks whether a profile is encrypted. Return false on error.
static bool isProfileEncrypted(QString name);
private:
Profile(QString name, QString password);
/// Lists all the files in the config dir with a given extension
/// Pass the raw extension, e.g. "jpeg" not ".jpeg".
static QVector<QString> getFilesByExt(QString extension);
@ -25,7 +39,13 @@ private:
static void importProfile(QString name);
private:
Core* core;
QThread* coreThread;
QString name, password;
static QVector<QString> profiles;
/// How much data we need to read to check if the file is encrypted
/// Must be >= TOX_ENC_SAVE_MAGIC_LENGTH (8), which isn't publicly defined
static constexpr int encryptHeaderSize = 8;
};
#endif // PROFILE_H

View File

@ -97,3 +97,16 @@ void ProfileLocker::deathByBrokenLock()
qCritical() << "Lock is *BROKEN*, exiting immediately";
abort();
}
bool ProfileLocker::hasLock()
{
return lockfile.operator bool();
}
QString ProfileLocker::getCurLockName()
{
if (lockfile)
return curLockName;
else
return QString();
}

View File

@ -24,6 +24,10 @@ public:
static bool lock(QString profile);
/// Releases the lock on the current profile
static void unlock();
/// Returns true if we're currently holding a lock
static bool hasLock();
/// Return the name of the currently loaded profile, a null string if there is none
static QString getCurLockName();
/// 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.

View File

@ -12,7 +12,24 @@ LoginScreen::LoginScreen(QWidget *parent) :
connect(ui->loginPgbtn, &QPushButton::clicked, this, &LoginScreen::onLoginPageClicked);
connect(ui->createAccountButton, &QPushButton::clicked, this, &LoginScreen::onCreateNewProfile);
connect(ui->loginButton, &QPushButton::clicked, this, &LoginScreen::onLogin);
connect(ui->loginUsernames, &QComboBox::currentTextChanged, this, &LoginScreen::onLoginUsernameSelected);
reset();
}
LoginScreen::~LoginScreen()
{
delete ui;
}
void LoginScreen::reset()
{
ui->newUsername->clear();
ui->newPass->clear();
ui->loginPassword->clear();
ui->loginUsernames->clear();
Profile::scanProfiles();
QVector<QString> profiles = Profile::getProfiles();
for (QString profile : profiles)
ui->loginUsernames->addItem(profile);
@ -23,11 +40,6 @@ LoginScreen::LoginScreen(QWidget *parent) :
ui->stackedWidget->setCurrentIndex(1);
}
LoginScreen::~LoginScreen()
{
delete ui;
}
void LoginScreen::onNewProfilePageClicked()
{
ui->stackedWidget->setCurrentIndex(0);
@ -43,6 +55,23 @@ void LoginScreen::onCreateNewProfile()
}
void LoginScreen::onLoginUsernameSelected(const QString &name)
{
if (name.isEmpty())
return;
if (Profile::isProfileEncrypted(name))
{
ui->loginPasswordLabel->show();
ui->loginPassword->show();
}
else
{
ui->loginPasswordLabel->hide();
ui->loginPassword->hide();
}
}
void LoginScreen::onLogin()
{

View File

@ -14,8 +14,10 @@ class LoginScreen : public QWidget
public:
explicit LoginScreen(QWidget *parent = 0);
~LoginScreen();
void reset(); ///< Resets the UI, clears all fields
private slots:
void onLoginUsernameSelected(const QString& name);
// Buttons to change page
void onNewProfilePageClicked();
void onLoginPageClicked();

View File

@ -709,7 +709,7 @@ margin-bottom:5px;</string>
<widget class="QComboBox" name="loginUsernames"/>
</item>
<item>
<widget class="QLabel" name="label_4">
<widget class="QLabel" name="loginPasswordLabel">
<property name="text">
<string>Password:</string>
</property>