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

View File

@ -29,6 +29,7 @@
#include "coredefines.h" #include "coredefines.h"
#include "toxid.h" #include "toxid.h"
class Profile;
template <typename T> class QList; template <typename T> class QList;
class QTimer; class QTimer;
class QString; class QString;
@ -45,7 +46,7 @@ class Core : public QObject
public: public:
enum PasswordType {ptMain = 0, ptHistory, ptCounter}; 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 static Core* getInstance(); ///< Returns the global widget's Core instance
~Core(); ~Core();
@ -292,7 +293,7 @@ private:
Tox* tox; Tox* tox;
ToxAv* toxav; ToxAv* toxav;
QTimer *toxTimer, *fileTimer; //, *saveTimer; QTimer *toxTimer, *fileTimer; //, *saveTimer;
QString loadPath; // meaningless after start() is called Profile& profile;
int dhtServerId; int dhtServerId;
static ToxCall calls[TOXAV_MAX_CALLS]; static ToxCall calls[TOXAV_MAX_CALLS];
#ifdef QTOX_FILTER_AUDIO #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(), if (!tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
pwsaltedkeys[passtype], encrypted, nullptr)) pwsaltedkeys[passtype], encrypted, nullptr))
{ {
qWarning() << "encryptData: encryption failed"; qWarning() << "Encryption failed";
return QByteArray(); 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);
@ -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(), if (!tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
pwsaltedkeys[passtype], decrypted, nullptr)) pwsaltedkeys[passtype], decrypted, nullptr))
{ {
qWarning() << "decryptData: decryption failed"; qWarning() << "Decryption failed";
return QByteArray(); return QByteArray();
} }
return QByteArray(reinterpret_cast<char*>(decrypted), sz); return QByteArray(reinterpret_cast<char*>(decrypted), sz);

View File

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

View File

@ -1,12 +1,16 @@
#include "nexus.h" #include "nexus.h"
#include "profile.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "misc/settings.h" #include "misc/settings.h"
#include "video/camerasource.h" #include "video/camerasource.h"
#include "widget/gui.h" #include "widget/gui.h"
#include "widget/loginscreen.h"
#include <QThread> #include <QThread>
#include <QDebug> #include <QDebug>
#include <QImageReader> #include <QImageReader>
#include <QFile> #include <QFile>
#include <QApplication>
#include <cassert>
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
#include <src/widget/androidgui.h> #include <src/widget/androidgui.h>
@ -18,18 +22,14 @@ static Nexus* nexus{nullptr};
Nexus::Nexus(QObject *parent) : Nexus::Nexus(QObject *parent) :
QObject(parent), QObject(parent),
core{nullptr}, profile{nullptr},
coreThread{nullptr},
widget{nullptr}, widget{nullptr},
androidgui{nullptr}, androidgui{nullptr}
started{false}
{ {
} }
Nexus::~Nexus() Nexus::~Nexus()
{ {
delete core;
delete coreThread;
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
delete androidgui; delete androidgui;
#else #else
@ -39,9 +39,6 @@ Nexus::~Nexus()
void Nexus::start() void Nexus::start()
{ {
if (started)
return;
qDebug() << "Starting up"; qDebug() << "Starting up";
// Setup the environment // Setup the environment
@ -59,19 +56,30 @@ void Nexus::start()
qRegisterMetaType<Core::PasswordType>("Core::PasswordType"); qRegisterMetaType<Core::PasswordType>("Core::PasswordType");
qRegisterMetaType<std::shared_ptr<VideoFrame>>("std::shared_ptr<VideoFrame>"); 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 // Create GUI
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
widget = Widget::getInstance(); widget = Widget::getInstance();
#endif #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 // Start GUI
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
androidgui = new AndroidGUI; androidgui = new AndroidGUI;
@ -88,6 +96,7 @@ void Nexus::start()
GUI::setEnabled(false); GUI::setEnabled(false);
// Connections // Connections
Core* core = profile->getCore();
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
connect(core, &Core::connected, androidgui, &AndroidGUI::onConnected); connect(core, &Core::connected, androidgui, &AndroidGUI::onConnected);
connect(core, &Core::disconnected, androidgui, &AndroidGUI::onDisconnected); connect(core, &Core::disconnected, androidgui, &AndroidGUI::onDisconnected);
@ -138,10 +147,7 @@ void Nexus::start()
connect(widget, &Widget::changeProfile, core, &Core::switchConfiguration); connect(widget, &Widget::changeProfile, core, &Core::switchConfiguration);
#endif #endif
// Start Core profile->startCore();
coreThread->start();
started = true;
} }
Nexus& Nexus::getInstance() Nexus& Nexus::getInstance()
@ -160,7 +166,20 @@ void Nexus::destroyInstance()
Core* Nexus::getCore() 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() AndroidGUI* Nexus::getAndroidGUI()

View File

@ -3,10 +3,11 @@
#include <QObject> #include <QObject>
class QThread;
class Core;
class Widget; class Widget;
class AndroidGUI; class AndroidGUI;
class Profile;
class LoginScreen;
class Core;
/// This class is in charge of connecting various systems together /// This class is in charge of connecting various systems together
/// and forwarding signals appropriately to the right objects /// and forwarding signals appropriately to the right objects
@ -15,11 +16,17 @@ class Nexus : public QObject
{ {
Q_OBJECT Q_OBJECT
public: 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 Nexus& getInstance();
static void destroyInstance(); static void destroyInstance();
static Core* getCore(); ///< Will return 0 if not started 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 AndroidGUI* getAndroidGUI(); ///< Will return 0 if not started
static Widget* getDesktopGUI(); ///< Will return 0 if not started static Widget* getDesktopGUI(); ///< Will return 0 if not started
static QString getSupportedImageFilter(); static QString getSupportedImageFilter();
@ -30,11 +37,10 @@ private:
~Nexus(); ~Nexus();
private: private:
Core* core; Profile* profile;
QThread* coreThread;
Widget* widget; Widget* widget;
AndroidGUI* androidgui; AndroidGUI* androidgui;
bool started; LoginScreen* loginScreen;
}; };
#endif // NEXUS_H #endif // NEXUS_H

View File

@ -1,19 +1,48 @@
#include "profile.h" #include "profile.h"
#include "profilelocker.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/core/core.h"
#include <cassert> #include <cassert>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QThread>
#include <QObject>
#include <QDebug>
QVector<QString> Profile::profiles; 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() Profile::~Profile()
{ {
delete core;
delete coreThread;
assert(ProfileLocker::getCurLockName() == name);
ProfileLocker::unlock();
} }
QVector<QString> Profile::getFilesByExt(QString extension) QVector<QString> Profile::getFilesByExt(QString extension)
@ -52,3 +81,86 @@ QVector<QString> Profile::getProfiles()
{ {
return profiles; 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 <QVector>
#include <QString> #include <QString>
#include <QByteArray>
class Core;
class QThread;
/// Manages user profiles /// Manages user profiles
class Profile class Profile
{ {
public: 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(); ~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 /// Scan for profile, automatically importing them if needed
/// NOT thread-safe /// NOT thread-safe
static void scanProfiles(); static void scanProfiles();
static QVector<QString> getProfiles(); static QVector<QString> getProfiles();
/// Checks whether a profile is encrypted. Return false on error.
static bool isProfileEncrypted(QString name);
private: private:
Profile(QString name, QString password);
/// Lists all the files in the config dir with a given extension /// Lists all the files in the config dir with a given extension
/// Pass the raw extension, e.g. "jpeg" not ".jpeg". /// Pass the raw extension, e.g. "jpeg" not ".jpeg".
static QVector<QString> getFilesByExt(QString extension); static QVector<QString> getFilesByExt(QString extension);
@ -25,7 +39,13 @@ private:
static void importProfile(QString name); static void importProfile(QString name);
private: private:
Core* core;
QThread* coreThread;
QString name, password;
static QVector<QString> profiles; 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 #endif // PROFILE_H

View File

@ -97,3 +97,16 @@ void ProfileLocker::deathByBrokenLock()
qCritical() << "Lock is *BROKEN*, exiting immediately"; qCritical() << "Lock is *BROKEN*, exiting immediately";
abort(); 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); static bool lock(QString profile);
/// Releases the lock on the current profile /// Releases the lock on the current profile
static void unlock(); 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 /// Releases all locks on all profiles
/// DO NOT call unless all we're the only qTox instance /// DO NOT call unless all we're the only qTox instance
/// and we don't hold any lock yet. /// 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->loginPgbtn, &QPushButton::clicked, this, &LoginScreen::onLoginPageClicked);
connect(ui->createAccountButton, &QPushButton::clicked, this, &LoginScreen::onCreateNewProfile); connect(ui->createAccountButton, &QPushButton::clicked, this, &LoginScreen::onCreateNewProfile);
connect(ui->loginButton, &QPushButton::clicked, this, &LoginScreen::onLogin); 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(); QVector<QString> profiles = Profile::getProfiles();
for (QString profile : profiles) for (QString profile : profiles)
ui->loginUsernames->addItem(profile); ui->loginUsernames->addItem(profile);
@ -23,11 +40,6 @@ LoginScreen::LoginScreen(QWidget *parent) :
ui->stackedWidget->setCurrentIndex(1); ui->stackedWidget->setCurrentIndex(1);
} }
LoginScreen::~LoginScreen()
{
delete ui;
}
void LoginScreen::onNewProfilePageClicked() void LoginScreen::onNewProfilePageClicked()
{ {
ui->stackedWidget->setCurrentIndex(0); 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() void LoginScreen::onLogin()
{ {

View File

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

View File

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