Add security on conference calls.

Fix group call by not using ConferenceScheduler.
Fix some QML errors.
This commit is contained in:
Julien Wadel 2025-07-22 15:15:51 +02:00
parent 956fbe1f15
commit fdab2ffbba
5 changed files with 70 additions and 23 deletions

View file

@ -35,15 +35,15 @@
#include <qqmlapplicationengine.h>
#include "app/App.hpp"
#include "components/calls/CallsListModel.hpp"
#include "components/conferenceScheduler/ConferenceScheduler.hpp"
#include "components/core/CoreManager.hpp"
#include "components/other/timeZone/TimeZoneModel.hpp"
#include "components/participant/ParticipantListModel.hpp"
#include "components/settings/SettingsModel.hpp"
#include "components/timeline/TimelineListModel.hpp"
#include "utils/Utils.hpp"
#include "utils/LinphoneEnums.hpp"
#include "utils/Utils.hpp"
// =============================================================================
@ -63,10 +63,14 @@ QSharedPointer<ConferenceInfoModel> ConferenceInfoModel::create(std::shared_ptr<
ConferenceInfoModel::ConferenceInfoModel (QObject * parent) : QObject(parent){
mConferenceInfo = linphone::Factory::get()->createConferenceInfo();
mIsScheduled = false;
if(CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled())
mConferenceInfo->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
initDateTime();
auto defaultAccount = CoreManager::getInstance()->getCore()->getDefaultAccount();
if(defaultAccount){
auto accountAddress = defaultAccount->getContactAddress();
std::shared_ptr<const linphone::Address> accountAddress = defaultAccount->getContactAddress();
if(!accountAddress)
accountAddress = defaultAccount->getParams()->getIdentityAddress();
if(accountAddress){
auto cleanedClonedAddress = accountAddress->clone();
cleanedClonedAddress->clean();
@ -163,7 +167,7 @@ QDateTime ConferenceInfoModel::getEndDateTimeUtc() const{
}
QString ConferenceInfoModel::getOrganizer() const{
return Utils::coreStringToAppString(mConferenceInfo->getOrganizer()->asString());
return Utils::coreStringToAppString(mConferenceInfo->getOrganizer() ? mConferenceInfo->getOrganizer()->asString() : "Self");
}
QString ConferenceInfoModel::getSubject() const{
@ -199,6 +203,11 @@ bool ConferenceInfoModel::isEnded() const{
return mIsEnded;
}
bool ConferenceInfoModel::isSecured() const{
return mConferenceInfo ? mConferenceInfo->getSecurityLevel() != linphone::Conference::SecurityLevel::None
: CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled();
}
bool ConferenceInfoModel::getIsEnded() const{
return getEndDateTimeUtc() < QDateTime::currentDateTimeUtc();
}
@ -323,6 +332,14 @@ void ConferenceInfoModel::setIsEnded(const bool& end){
}
}
void ConferenceInfoModel::setIsSecured(const bool& on){
if( on != isSecured()){
if(mConferenceInfo)
mConferenceInfo->setSecurityLevel(on ? linphone::Conference::SecurityLevel::EndToEnd : linphone::Conference::SecurityLevel::None);
emit isSecuredChanged();
}
}
void ConferenceInfoModel::setInviteMode(const int& mode){
if( mode != mInviteMode){
mInviteMode = mode;
@ -353,20 +370,46 @@ void ConferenceInfoModel::resetConferenceInfo() {
}
}
void ConferenceInfoModel::createConference(const int& securityLevel) {
void ConferenceInfoModel::createConference() {
CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false;
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
static std::shared_ptr<linphone::Conference> conference;
qInfo() << "Conference creation of " << getSubject() << " at " << securityLevel << " security, organized by " << getOrganizer() << " for " << getDateTimeSystem().toString();
qInfo() << "Conference creation of " << getSubject() << " at " << (int)mConferenceInfo->getSecurityLevel() << " security, organized by " << getOrganizer() << " for " << getDateTimeSystem().toString();
qInfo() << "Participants:";
for(auto p : mConferenceInfo->getParticipants())
qInfo() << "\t" << p->asString().c_str();
mConferenceScheduler = ConferenceScheduler::create();
mConferenceScheduler->mSendInvite = mInviteMode;
connect(mConferenceScheduler.get(), &ConferenceScheduler::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent);
connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onConferenceSchedulerStateChanged);
mConferenceScheduler->getConferenceScheduler()->setInfo(mConferenceInfo);
if(isScheduled()) {
mConferenceScheduler = ConferenceScheduler::create();
mConferenceScheduler->mSendInvite = mInviteMode;
connect(mConferenceScheduler.get(), &ConferenceScheduler::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent);
connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onConferenceSchedulerStateChanged);
mConferenceScheduler->getConferenceScheduler()->setInfo(mConferenceInfo);
}else {
// Since SDK 5.4, unscheduled conference must be created from a group call.
auto callListModel = CoreManager::getInstance()->getCallsListModel();
auto settingsModel = CoreManager::getInstance()->getSettingsModel();
bool videoEnabled = CoreManager::getInstance()->getSettingsModel()->getVideoConferenceEnabled() && settingsModel->getVideoConferenceEnabled();
auto parameters = core->createConferenceParams(nullptr);
if(!CoreManager::getInstance()->getSettingsModel()->getVideoConferenceEnabled()) {
parameters->enableVideo(false);
parameters->setConferenceFactoryAddress(nullptr);// Do a local conference
}else {
parameters->enableVideo(videoEnabled);
conference = core->createConferenceWithParams(parameters);
}
if(!conference) emit conferenceCreationFailed();
else {
auto callParameters = CoreManager::getInstance()->getCore()->createCallParams(nullptr);
callParameters->enableVideo(videoEnabled);
if(!conference->inviteParticipants(mConferenceInfo->getParticipants(), callParameters)) {
emit conferenceCreated();
}else{
emit conferenceCreationFailed();
}
}
}
}
void ConferenceInfoModel::cancelConference(){

View file

@ -50,6 +50,7 @@ public:
Q_PROPERTY(QString uri READ getUri NOTIFY uriChanged)
Q_PROPERTY(bool isScheduled READ isScheduled WRITE setIsScheduled NOTIFY isScheduledChanged)
Q_PROPERTY(bool isEnded READ isEnded WRITE setIsEnded NOTIFY isEndedChanged)
Q_PROPERTY(bool isSecured READ isSecured WRITE setIsSecured NOTIFY isSecuredChanged)
Q_PROPERTY(int inviteMode READ getInviteMode WRITE setInviteMode NOTIFY inviteModeChanged)
Q_PROPERTY(int participantCount READ getParticipantCount NOTIFY participantsChanged)
Q_PROPERTY(int allParticipantCount READ getAllParticipantCount NOTIFY participantsChanged)
@ -78,6 +79,7 @@ public:
QString getUri() const;
bool isScheduled() const;
bool isEnded() const;
bool isSecured() const;
bool getIsEnded() const;
int getInviteMode() const;
Q_INVOKABLE QVariantList getParticipants() const;
@ -96,6 +98,7 @@ public:
void setDescription(const QString& description);
void setIsScheduled(const bool& on);
void setIsEnded(const bool& end);
void setIsSecured(const bool& on);
void setInviteMode(const int& modes);
void setDateTime(const QDate& date, const QTime& time, TimeZoneModel * model);
@ -105,7 +108,7 @@ public:
// Tools
Q_INVOKABLE void resetConferenceInfo();// Recreate a new conference info from factory
Q_INVOKABLE void createConference(const int& securityLevel);
Q_INVOKABLE void createConference();
Q_INVOKABLE void cancelConference();
Q_INVOKABLE void deleteConferenceInfo();// Remove completly this conference info from DB
@ -124,6 +127,7 @@ signals:
void uriChanged();
void isScheduledChanged();
void isEndedChanged();
void isSecuredChanged();
void inviteModeChanged();
void conferenceInfoStateChanged();
void conferenceSchedulerStateChanged();
@ -140,6 +144,7 @@ private:
bool mIsScheduled = true;
bool mIsEnded = false;
bool mIsSecured = true;
QTimer mCheckEndTimer;
int mInviteMode = 0;
bool mRemoveRequested = false;// true if user has request its deletion from DB

View file

@ -154,13 +154,13 @@ Rectangle {
asynchronous: index > 20
active: true
sourceComponent: TimelineItem{
timelineModel: $modelData
timelineModel: !!$modelData ? $modelData : null
modelIndex: index
optionsTogglable: timeline.optionsTogglable
actions: timeline.actions
Connections{
target: $modelData
target: !!$modelData ? $modelData : null
onSelectedChanged:{
if(selected) {
view.currentIndex = index;

View file

@ -51,7 +51,6 @@ DialogPlus {
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 15
spacing:4
visible: false // TODO
Text {
Layout.fillWidth: true
//: 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured.
@ -80,14 +79,12 @@ DialogPlus {
anchors.verticalCenter: parent.verticalCenter
width:50
enabled:true
checked: !SettingsModel.standardChatEnabled && SettingsModel.secureChatEnabled
checked: conferenceInfoModel.isSecured
onClicked: {
var newCheck = checked
if(SettingsModel.standardChatEnabled && checked || SettingsModel.secureChatEnabled && !checked)
newCheck = !checked;
checked = newCheck;
conferenceInfoModel.isSecured = newCheck
}
indicatorStyle: SwitchStyle.aux
}
@ -141,7 +138,8 @@ DialogPlus {
conferenceInfoModel.setParticipants(selectedParticipants.participantListModel)
conferenceInfoModel.inviteMode = getInviteMode();
conferenceInfoModel.createConference(false && secureSwitch.checked) // TODO remove false when Encryption is ready to use
conferenceInfoModel.isSecured = secureSwitch.checked
conferenceInfoModel.createConference()
}
TooltipArea{
visible: AccountSettingsModel.conferenceUri == '' || subject.text == '' || selectedParticipants.count < conferenceManager.minParticipants

View file

@ -341,7 +341,8 @@ ColumnLayout {
conferenceInfoModel.setParticipants(conversation.chatRoomModel.participants)
conferenceInfoModel.inviteMode = 0;
conferenceInfoModel.createConference(false)// TODO activate it when secure video conference is implemented
conferenceInfoModel.isSecured = conversation.securityLevel
conferenceInfoModel.createConference()
}else{
Logic.openConferenceManager({chatRoomModel:conversation.chatRoomModel, autoCall:true})
}