diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 075ef9bb5..f8c10c78b 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -23,9 +23,9 @@ #include #include "core/logger/QtLogger.hpp" +#include "core/login/LoginPage.hpp" #include "core/singleapplication/singleapplication.h" #include "tool/Constants.hpp" -#include "view/Page/LoginPage.hpp" App::App(int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { @@ -35,14 +35,18 @@ App::App(int &argc, char *argv[]) mLinphoneThread->start(); } +App *App::getInstance() { + return dynamic_cast(QApplication::instance()); +} + //----------------------------------------------------------- // Initializations //----------------------------------------------------------- void App::init() { // Core. Manage the logger so it must be instantiate at first. - mCoreModel = QSharedPointer::create("", mLinphoneThread); - + auto coreModel = CoreModel::create("", mLinphoneThread); + connect(mLinphoneThread, &QThread::started, coreModel.get(), &CoreModel::start); // Console Commands createCommandParser(); mParser->parse(this->arguments()); @@ -54,7 +58,6 @@ void App::init() { if (mParser->isSet("verbose")) QtLogger::enableVerbose(true); if (mParser->isSet("qt-logs-only")) QtLogger::enableQtOnly(true); - connect(mLinphoneThread, &QThread::started, mCoreModel.get(), &CoreModel::start); // QML mEngine = new QQmlApplicationEngine(this); mEngine->addImportPath(":/"); @@ -78,6 +81,13 @@ void App::initCppInterfaces() { } //------------------------------------------------------------ + +void App::clean() { + mLinphoneThread->exit(); + mLinphoneThread->wait(); + delete mLinphoneThread; +} + void App::createCommandParser() { if (!mParser) delete mParser; diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index c8f3fdaef..63f6c26d6 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -29,18 +29,35 @@ class App : public SingleApplication { public: App(int &argc, char *argv[]); + static App* getInstance(); + +// App::postModelAsync() => run lambda in model thread and continue. +// App::postModelSync() => run lambda in current thread and block connection. + template + static auto postModelAsync(Func&& callable, Args&& ...args) { + QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, args...); + } + template + static auto postModelAsync(Func&& callable) { + QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable); + } + template + static auto postModelSync(Func&& callable) { + QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable + , QThread::currentThread() != CoreModel::getInstance()->thread() ? Qt::BlockingQueuedConnection : Qt::DirectConnection); + } + void clean(); void init(); void initCppInterfaces(); void onLoggerInitialized(); QQmlApplicationEngine *mEngine = nullptr; - Thread *mLinphoneThread = nullptr; - QSharedPointer mCoreModel; private: void createCommandParser(); QCommandLineParser *mParser = nullptr; + Thread *mLinphoneThread = nullptr; }; diff --git a/Linphone/core/CMakeLists.txt b/Linphone/core/CMakeLists.txt index 4861aa859..9043627d3 100644 --- a/Linphone/core/CMakeLists.txt +++ b/Linphone/core/CMakeLists.txt @@ -1,6 +1,7 @@ list(APPEND _LINPHONEAPP_SOURCES core/App.cpp core/logger/QtLogger.cpp + core/login/LoginPage.cpp core/path/Paths.cpp core/setting/Settings.cpp core/thread/Thread.cpp diff --git a/Linphone/core/logger/QtLogger.hpp b/Linphone/core/logger/QtLogger.hpp index 0d7afe7c7..a37e10e53 100644 --- a/Linphone/core/logger/QtLogger.hpp +++ b/Linphone/core/logger/QtLogger.hpp @@ -18,8 +18,8 @@ * along with this program. If not, see . */ -#ifndef LOGGER_STATIC_MODEL_H_ -#define LOGGER_STATIC_MODEL_H_ +#ifndef QT_LOGGER_H_ +#define QT_LOGGER_H_ #include #include diff --git a/Linphone/core/login/LoginPage.cpp b/Linphone/core/login/LoginPage.cpp new file mode 100644 index 000000000..8fd9e26ad --- /dev/null +++ b/Linphone/core/login/LoginPage.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "LoginPage.hpp" +#include + +#include "core/App.hpp" + +#include "model/account/AccountManager.hpp" + +LoginPage::LoginPage(QObject *parent) : QObject(parent) { +} + +bool LoginPage::isLogged() const { + // View thread + return mIsLogged; +} + +void LoginPage::setIsLogged(bool status) { + // Should be view thread only because of object updates. + if (mIsLogged != status) { + mIsLogged = status; + emit isLoggedChanged(); + } +} + +void LoginPage::login(const QString &username, const QString &password) { + App::postModelAsync([=]() { + // Create on Model thread. + AccountManager *accountManager = new AccountManager(); + connect(accountManager, &AccountManager::logged, this, [accountManager, this](bool isLoggued) mutable { + // View thread + setIsLogged(isLoggued); + accountManager->deleteLater(); + }); + accountManager->login(username, password); + }); +} diff --git a/Linphone/view/Page/LoginPage.hpp b/Linphone/core/login/LoginPage.hpp similarity index 85% rename from Linphone/view/Page/LoginPage.hpp rename to Linphone/core/login/LoginPage.hpp index 36ffc114b..77ec6d812 100644 --- a/Linphone/view/Page/LoginPage.hpp +++ b/Linphone/core/login/LoginPage.hpp @@ -27,9 +27,14 @@ public: LoginPage(QObject *parent = nullptr); Q_PROPERTY(bool isLogged READ isLogged NOTIFY isLoggedChanged) + + Q_INVOKABLE void login(const QString& username, const QString& password); - bool isLogged(); + bool isLogged() const; + void setIsLogged(bool status); signals: void isLoggedChanged(); -}; \ No newline at end of file +private: + bool mIsLogged = false; +}; diff --git a/Linphone/core/thread/Thread.cpp b/Linphone/core/thread/Thread.cpp index 6734cc2c9..c58a9db1b 100644 --- a/Linphone/core/thread/Thread.cpp +++ b/Linphone/core/thread/Thread.cpp @@ -27,6 +27,6 @@ void Thread::run() { int toExit = false; while (!toExit) { int result = exec(); - if (result < 0) toExit = true; + if (result <= 0) toExit = true; } -} \ No newline at end of file +} diff --git a/Linphone/data/assistant/create-app-sip-account.rc b/Linphone/data/assistant/create-app-sip-account.rc new file mode 100644 index 000000000..a23f4995a --- /dev/null +++ b/Linphone/data/assistant/create-app-sip-account.rc @@ -0,0 +1,52 @@ + + +
+ 10000000 +
+
+ 1 + 0 + 1 + 120 + sip:voip-metrics@sip.linphone.org;transport=tls + 1 + 180 + 600 + sip:?@sip.linphone.org + <sip:sip.linphone.org;transport=tls> + 1 + default_nat_policy_values + sip.linphone.org + message-expires=2419200 + sip:conference-factory@sip.linphone.org + sip:videoconference-factory@sip.linphone.org + 1 + 1 + https://lime.linphone.org/lime-server/lime-server.php +
+
+ stun.linphone.org + stun,ice +
+
+ https://www.linphone.org:444/lft.php +
+
+ sips:rls@sip.linphone.org +
+
+ sip.linphone.org + SHA-256 + -1 + 1 + -1 + 64 + 1 + ^[a-z0-9+_.\-]*$ + https://subscribe.linphone.org:444/wizard.php +
+
+ 1 + https://subscribe.linphone.org/api/ +
+
diff --git a/Linphone/data/assistant/use-app-sip-account.rc b/Linphone/data/assistant/use-app-sip-account.rc new file mode 100644 index 000000000..4e6917299 --- /dev/null +++ b/Linphone/data/assistant/use-app-sip-account.rc @@ -0,0 +1,52 @@ + + +
+ 10000000 +
+
+ 1 + 0 + 1 + 120 + sip:voip-metrics@sip.linphone.org;transport=tls + 1 + 180 + 600 + sip:?@sip.linphone.org + <sip:sip.linphone.org;transport=tls> + 1 + default_nat_policy_values + sip.linphone.org + message-expires=2419200 + sip:conference-factory@sip.linphone.org + sip:videoconference-factory@sip.linphone.org + 1 + 1 + https://lime.linphone.org/lime-server/lime-server.php +
+
+ stun.linphone.org + stun,ice +
+
+ https://www.linphone.org:444/lft.php +
+
+ sips:rls@sip.linphone.org +
+
+ sip.linphone.org + SHA-256 + -1 + 1 + -1 + 64 + 1 + ^[a-z0-9+_.\-]*$ + https://subscribe.linphone.org:444/wizard.php +
+
+ 1 + https://subscribe.linphone.org/api/ +
+
diff --git a/Linphone/data/assistant/use-other-sip-account.rc b/Linphone/data/assistant/use-other-sip-account.rc new file mode 100644 index 000000000..c5413129e --- /dev/null +++ b/Linphone/data/assistant/use-other-sip-account.rc @@ -0,0 +1,43 @@ + + +
+ 10000000 +
+
+ 0 + 0 + 0 + + 0 + 0 + 3600 + + + + 1 + + + + + + +
+
+ + +
+
+ + +
+
+ + MD5 + -1 + 0 + -1 + 128 + 1 + ^[a-zA-Z0-9+_.\-]*$ +
+
diff --git a/Linphone/main.cpp b/Linphone/main.cpp index 53d9109e8..33a415004 100644 --- a/Linphone/main.cpp +++ b/Linphone/main.cpp @@ -20,5 +20,6 @@ int main(int argc, char *argv[]) { } int result = app.exec(); + app.clean(); return result; } diff --git a/Linphone/model/CMakeLists.txt b/Linphone/model/CMakeLists.txt index 1cae29160..b3911e600 100644 --- a/Linphone/model/CMakeLists.txt +++ b/Linphone/model/CMakeLists.txt @@ -1,4 +1,7 @@ list(APPEND _LINPHONEAPP_SOURCES + model/account/AccountListener.cpp + model/account/AccountManager.cpp + model/core/CoreModel.cpp model/core/CoreListener.cpp diff --git a/Linphone/view/Page/LoginPage.cpp b/Linphone/model/account/AccountListener.cpp similarity index 64% rename from Linphone/view/Page/LoginPage.cpp rename to Linphone/model/account/AccountListener.cpp index af79d48c7..f9060499a 100644 --- a/Linphone/view/Page/LoginPage.cpp +++ b/Linphone/model/account/AccountListener.cpp @@ -18,17 +18,15 @@ * along with this program. If not, see . */ -#include "LoginPage.hpp" -#include +#include "AccountListener.hpp" -LoginPage::LoginPage(QObject *parent) : QObject(parent) { +#include + +AccountListener::AccountListener(QObject *parent) : QObject(parent) { } -bool LoginPage::isLogged() { - static bool testLog = false; - QTimer::singleShot(2000, [&]() mutable { - testLog = true; - emit isLoggedChanged(); - }); - return testLog; -} \ No newline at end of file +void AccountListener::onRegistrationStateChanged(const std::shared_ptr &account, + linphone::RegistrationState state, + const std::string &message) { + emit registrationStateChanged(account, state, message); +} diff --git a/Linphone/model/account/AccountListener.hpp b/Linphone/model/account/AccountListener.hpp new file mode 100644 index 000000000..21ac829d5 --- /dev/null +++ b/Linphone/model/account/AccountListener.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ACCOUNT_LISTENER_H_ +#define ACCOUNT_LISTENER_H_ + +#include +#include + +class AccountListener : public QObject, public linphone::AccountListener { + Q_OBJECT +public: + AccountListener(QObject *parent = nullptr); + + virtual void onRegistrationStateChanged(const std::shared_ptr &account, + linphone::RegistrationState state, + const std::string &message) override; + +signals: + void registrationStateChanged(const std::shared_ptr & account, linphone::RegistrationState state, const std::string & message); +}; + +#endif diff --git a/Linphone/model/account/AccountManager.cpp b/Linphone/model/account/AccountManager.cpp new file mode 100644 index 000000000..8a56d1c73 --- /dev/null +++ b/Linphone/model/account/AccountManager.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "AccountManager.hpp" + +#include + +#include "core/path/Paths.hpp" +#include "model/core/CoreModel.hpp" +#include "tool/Utils.hpp" + +AccountManager::AccountManager(QObject *parent) : QObject(parent) { +} + +std::shared_ptr AccountManager::createAccount(const QString &assistantFile) { + auto core = CoreModel::getInstance()->getCore(); + QString assistantPath = Paths::getAssistantConfigDirPath() + assistantFile; + qInfo() << QStringLiteral("[AccountManager] Set config on assistant: `%1`.").arg(assistantPath); + core->getConfig()->loadFromXmlFile(Utils::appStringToCoreString(assistantPath)); + return core->createAccount(core->createAccountParams()); +} + +bool AccountManager::login(QString username, QString password) { + auto core = CoreModel::getInstance()->getCore(); + auto factory = linphone::Factory::get(); + auto account = createAccount("use-app-sip-account.rc"); + auto params = account->getParams()->clone(); + // Sip address. + auto identity = params->getIdentityAddress()->clone(); + if (mAccountListener) return false; + + identity->setUsername(Utils::appStringToCoreString(username)); + if (params->setIdentityAddress(identity)) { + qWarning() << QStringLiteral("[AccountManager] Unable to set identity address: `%1`.") + .arg(Utils::coreStringToAppString(identity->asStringUriOnly())); + return false; + } + core->addAuthInfo(factory->createAuthInfo(Utils::appStringToCoreString(username), // Username. + "", // User ID. + Utils::appStringToCoreString(password), // Password. + "", // HA1. + "", // Realm. + identity->getDomain() // Domain. + )); + + mAccountListener = std::make_shared(this); + account->addListener(mAccountListener); + connect(mAccountListener.get(), &AccountListener::registrationStateChanged, this, + &AccountManager::onRegistrationStateChanged); + core->addAccount(account); + return true; +} + +void AccountManager::onRegistrationStateChanged(const std::shared_ptr &account, + linphone::RegistrationState state, + const std::string &message) { + auto core = CoreModel::getInstance()->getCore(); + switch (state) { + case linphone::RegistrationState::Failed: + core->removeAccount(account); + account->removeListener(mAccountListener); + mAccountListener = nullptr; + emit logged(false); + break; + case linphone::RegistrationState::Ok: + account->removeListener(mAccountListener); + mAccountListener = nullptr; + emit logged(true); + break; + default: { + } + } +} diff --git a/Linphone/model/account/AccountManager.hpp b/Linphone/model/account/AccountManager.hpp new file mode 100644 index 000000000..d41af1b3b --- /dev/null +++ b/Linphone/model/account/AccountManager.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ACCOUNT_MANAGER_H_ +#define ACCOUNT_MANAGER_H_ + +#include +#include + +#include "AccountListener.hpp" + +class AccountManager: public QObject { +Q_OBJECT +public: + AccountManager(QObject *parent = nullptr); + + bool login(QString username, QString password); + + std::shared_ptr createAccount(const QString& assistantFile); + + void onRegistrationStateChanged(const std::shared_ptr & account, linphone::RegistrationState state, const std::string & message); +signals: + void logged(bool isLoggued); +private: + std::shared_ptr mAccountListener; +}; + +#endif diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index 22a2596be..d2b450869 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -27,21 +27,39 @@ #include #include +#include "core/App.hpp" #include "core/path/Paths.hpp" #include "tool/Utils.hpp" // ============================================================================= -CoreModel::CoreModel(const QString &configPath, QObject *parent) : QObject(parent) { +QSharedPointer CoreModel::gCoreModel; + +CoreModel::CoreModel(const QString &configPath, QThread *parent) : QObject() { + connect(parent, &QThread::finished, this, [this]() { + // Model thread + if (mCore && mCore->getGlobalState() == linphone::GlobalState::On) mCore->stop(); + gCoreModel = nullptr; + }); mConfigPath = configPath; mLogger = std::make_shared(this); mLogger->init(); + moveToThread(parent); } CoreModel::~CoreModel() { } +QSharedPointer CoreModel::create(const QString &configPath, QThread *parent) { + auto model = QSharedPointer::create(configPath, parent); + gCoreModel = model; + return model; +} + void CoreModel::start() { + mIterateTimer = new QTimer(this); + mIterateTimer->setInterval(30); + connect(mIterateTimer, &QTimer::timeout, [this]() { mCore->iterate(); }); setPathBeforeCreation(); mCore = linphone::Factory::get()->createCore(Utils::appStringToCoreString(Paths::getConfigFilePath(mConfigPath)), @@ -49,12 +67,12 @@ void CoreModel::start() { setPathsAfterCreation(); mCore->start(); setPathAfterStart(); - mCore->enableAutoIterate(true); + mIterateTimer->start(); } // ----------------------------------------------------------------------------- -CoreModel *CoreModel::getInstance() { - return nullptr; +QSharedPointer CoreModel::getInstance() { + return gCoreModel; } std::shared_ptr CoreModel::getCore() { diff --git a/Linphone/model/core/CoreModel.hpp b/Linphone/model/core/CoreModel.hpp index 6534e66ae..f67ffd7d8 100644 --- a/Linphone/model/core/CoreModel.hpp +++ b/Linphone/model/core/CoreModel.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "model/logger/LoggerModel.hpp" @@ -34,15 +35,14 @@ class CoreModel : public QObject { Q_OBJECT public: - CoreModel(const QString &configPath, QObject *parent); + CoreModel(const QString &configPath, QThread * parent); ~CoreModel(); + static QSharedPointer create(const QString &configPath, QThread * parent); + static QSharedPointer getInstance(); std::shared_ptr getCore(); void start(); - - static CoreModel *getInstance(); - void setConfigPath(QString path); @@ -55,11 +55,13 @@ signals: void loggerInitialized(); private: QString mConfigPath; + QTimer *mIterateTimer = nullptr; void setPathBeforeCreation(); void setPathsAfterCreation(); void setPathAfterStart(); + static QSharedPointer gCoreModel; }; #endif diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 8a0d116d6..5f72435e5 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -7,9 +7,4 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Page/LoginPage.qml ) -list(APPEND _LINPHONEAPP_SOURCES - view/Page/LoginPage.cpp -) - set(_LINPHONEAPP_QML_FILES ${_LINPHONEAPP_QML_FILES} PARENT_SCOPE) -set(_LINPHONEAPP_SOURCES ${_LINPHONEAPP_SOURCES} PARENT_SCOPE) diff --git a/Linphone/view/Page/LoginPage.qml b/Linphone/view/Page/LoginPage.qml index 08b85cb35..ca09627a5 100644 --- a/Linphone/view/Page/LoginPage.qml +++ b/Linphone/view/Page/LoginPage.qml @@ -13,7 +13,7 @@ Item{ RowLayout{ Button{ text: 'Sign In' - onClicked: console.log("Click!") + onClicked: LoginPageCpp.login("myusername", "passy") } Button{ text: 'Sign Out' @@ -22,4 +22,4 @@ Item{ } } } - \ No newline at end of file +