Flatpak migration and account security

Flatpak:
- Rewrite chat message deprecated logs : no more ensuring that this file must exists
- If a message history file exists in the application folder, let the SDK migrate data and remove the file
- Copy recursively all data from Flatpak installation, if files doesn't exist
- Split 2 migrations : old GTK and Flatpak

Account:
- Show logs only if the current proxy has been authentifiate for this session
- Update in realtime logs when this proxy has been registered
- Ensure to not refresh logs if the account has already been registered
This commit is contained in:
Julien Wadel 2020-06-10 22:20:19 +02:00
parent 0ac9f08b9f
commit 12f717b853
8 changed files with 123 additions and 46 deletions

View file

@ -240,7 +240,7 @@ string Paths::getLogsDirPath () {
}
string Paths::getMessageHistoryFilePath () {
return getWritableFilePath(getAppMessageHistoryFilePath());
return getReadableFilePath(getAppMessageHistoryFilePath());// No need to ensure that the file exists as this DB is deprecated
}
string Paths::getPackageDataDirPath () {
@ -300,10 +300,19 @@ static void migrateConfigurationFile (const QString &oldPath, const QString &new
qWarning() << "Failed migration of" << oldPath << "to" << newPath;
}
}
void Paths::migrate () {
void migrateFlatpakVersionFiles(){
#ifdef Q_OS_LINUX
// Copy all files
QString flatpakPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)+"/.var/app/com.belledonnecommunications.linphone/data/linphone";
if( QDir().exists(flatpakPath)){
qInfo() << "Migrating data from Flatpak.";
Utils::copyDir(flatpakPath, QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
}
#endif
}
void migrateGTKVersionFiles(){
QString newPath = getAppConfigFilePath();
QString oldBaseDir = QSysInfo::productType() == QLatin1String("windows")
QString oldBaseDir = QSysInfo::productType() == QLatin1String("windows")
? QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)
: QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString oldPath = oldBaseDir + "/.linphonerc";
@ -329,3 +338,7 @@ void Paths::migrate () {
if (!filePathExists(newPath) && filePathExists(oldPath))
migrateFile(oldPath, newPath);
}
void Paths::migrate () {
migrateFlatpakVersionFiles(); // First, check Flatpak version as it is the earlier version
migrateGTKVersionFiles();// Then check old version for migration
}

View file

@ -22,6 +22,7 @@
#include "components/core/CoreManager.hpp"
#include "components/settings/AccountSettingsModel.hpp"
#include "components/settings/SettingsModel.hpp"
#include "components/sip-addresses/SipAddressesModel.hpp"
#include "utils/LinphoneUtils.hpp"
#include "utils/Utils.hpp"
@ -55,9 +56,9 @@ private:
linphone::AccountCreator::Status status,
const string &
) override {
if (status == linphone::AccountCreator::Status::AccountCreated)
if (status == linphone::AccountCreator::Status::AccountCreated){
emit mAssistant->createStatusChanged(QString(""));
else {
}else {
if (status == linphone::AccountCreator::Status::RequestFailed)
emit mAssistant->createStatusChanged(tr("requestFailed"));
else if (status == linphone::AccountCreator::Status::ServerError)
@ -74,6 +75,7 @@ private:
) override {
if (status == linphone::AccountCreator::Status::AccountExist || status == linphone::AccountCreator::Status::AccountExistWithAlias) {
createProxyConfig(creator);
CoreManager::getInstance()->getSipAddressesModel()->reset();
emit mAssistant->loginStatusChanged(QString(""));
} else {
if (status == linphone::AccountCreator::Status::RequestFailed)
@ -94,7 +96,7 @@ private:
) {
if (creator->getEmail().empty())
createProxyConfig(creator);
CoreManager::getInstance()->getSipAddressesModel()->reset();
emit mAssistant->activateStatusChanged(QString(""));
} else {
if (status == linphone::AccountCreator::Status::RequestFailed)
@ -111,6 +113,7 @@ private:
) override {
if (status == linphone::AccountCreator::Status::AccountActivated) {
createProxyConfig(creator);
CoreManager::getInstance()->getSipAddressesModel()->reset();
emit mAssistant->activateStatusChanged(QString(""));
} else {
if (status == linphone::AccountCreator::Status::RequestFailed)
@ -126,6 +129,7 @@ private:
const string &
) override {
if (status == linphone::AccountCreator::Status::RequestOk) {
CoreManager::getInstance()->getSipAddressesModel()->reset();
emit mAssistant->recoverStatusChanged(QString(""));
} else {
if (status == linphone::AccountCreator::Status::RequestFailed)

View file

@ -23,6 +23,7 @@
#include <QSysInfo>
#include <QtConcurrent>
#include <QTimer>
#include <QFile>
#include "config.h"
@ -210,7 +211,10 @@ void CoreManager::cleanLogs () const {
void CoreManager::setDatabasesPaths () {
SET_DATABASE_PATH(Friends, Paths::getFriendsListFilePath());
SET_DATABASE_PATH(CallLogs, Paths::getCallHistoryFilePath());
SET_DATABASE_PATH(Chat, Paths::getMessageHistoryFilePath());
if(QFile::exists(Utils::coreStringToAppString(Paths::getMessageHistoryFilePath()))){
SET_DATABASE_PATH(Chat, Paths::getMessageHistoryFilePath());// Setting the message database let SDK to migrate data
QFile::remove(Utils::coreStringToAppString(Paths::getMessageHistoryFilePath()));
}
}
#undef SET_DATABASE_PATH

View file

@ -71,10 +71,17 @@ SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(pare
QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &SipAddressesModel::handleCallStateChanged);
QObject::connect(coreHandlers, &CoreHandlers::presenceReceived, this, &SipAddressesModel::handlePresenceReceived);
QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &SipAddressesModel::handleIsComposingChanged);
QObject::connect(coreHandlers, &CoreHandlers::registrationStateChanged, this, &SipAddressesModel::handleRegistrationStateChanged);
}
// -----------------------------------------------------------------------------
void SipAddressesModel::reset(){
mPeerAddressToSipAddressEntry.clear();
mRefs.clear();
resetInternalData();
initSipAddresses();
emit sipAddressReset();
}
int SipAddressesModel::rowCount (const QModelIndex &) const {
return mRefs.count();
}
@ -433,7 +440,15 @@ void SipAddressesModel::handleIsComposingChanged (const shared_ptr<linphone::Cha
Q_ASSERT(row != -1);
emit dataChanged(index(row, 0), index(row, 0));
}
void SipAddressesModel::handleRegistrationStateChanged( const std::shared_ptr<linphone::ProxyConfig> &proxyConfig, linphone::RegistrationState state){
QString address = Utils::coreStringToAppString(proxyConfig->getIdentityAddress()->asStringUriOnly());
if(!mRegistredProxies.contains(address) && state == linphone::RegistrationState::Ok)// This is a new state.
reset();
else if( state == linphone::RegistrationState::Cleared){
mRegistredProxies.removeAll(address);
reset();
}
}
// -----------------------------------------------------------------------------
void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, ContactModel *contact) {
@ -538,16 +553,25 @@ void SipAddressesModel::removeContactOfSipAddress (const QString &sipAddress) {
void SipAddressesModel::initSipAddresses () {
QElapsedTimer timer;
timer.start();
auto proxies = CoreManager::getInstance()->getCore()->getProxyConfigList();
foreach( auto proxy, proxies){
QString address = Utils::coreStringToAppString(proxy->getIdentityAddress()->asStringUriOnly());
if(proxy->getState() == linphone::RegistrationState::Ok && !mRegistredProxies.contains(address)){
mRegistredProxies.push_back(address);
}
}
initSipAddressesFromChat();
initSipAddressesFromCalls();
initSipAddressesFromChat(mRegistredProxies);
initSipAddressesFromCalls(mRegistredProxies);
initRefs();
initSipAddressesFromContacts();
qInfo() << "Sip addresses model initialized in:" << timer.elapsed() << "ms.";
}
void SipAddressesModel::initSipAddressesFromChat () {
void SipAddressesModel::initSipAddressesFromChat (const QStringList &pRegistredProxies) {
for (const auto &chatRoom : CoreManager::getInstance()->getCore()->getChatRooms()) {
list<shared_ptr<linphone::ChatMessage>> history(chatRoom->getHistory(1));
if (history.empty())
@ -556,52 +580,55 @@ void SipAddressesModel::initSipAddressesFromChat () {
QString peerAddress(Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()));
QString localAddress(Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()));
getSipAddressEntry(peerAddress)->localAddressToConferenceEntry[localAddress] = {
chatRoom->getUnreadMessagesCount(),
CoreManager::getInstance()->getMissedCallCount(peerAddress, localAddress),
false,
QDateTime::fromMSecsSinceEpoch(history.back()->getTime() * 1000)
};
if(pRegistredProxies.contains(localAddress)){
getSipAddressEntry(peerAddress)->localAddressToConferenceEntry[localAddress] = {
chatRoom->getUnreadMessagesCount(),
CoreManager::getInstance()->getMissedCallCount(peerAddress, localAddress),
false,
QDateTime::fromMSecsSinceEpoch(history.back()->getTime() * 1000)
};
}
}
}
void SipAddressesModel::initSipAddressesFromCalls () {
void SipAddressesModel::initSipAddressesFromCalls (const QStringList &pRegistredProxies) {
using ConferenceId = QPair<QString, QString>;
QSet<ConferenceId> conferenceDone;
for (const auto &callLog : CoreManager::getInstance()->getCore()->getCallLogs()) {
const QString peerAddress(Utils::coreStringToAppString(callLog->getRemoteAddress()->asStringUriOnly()));
const QString localAddress(Utils::coreStringToAppString(callLog->getLocalAddress()->asStringUriOnly()));
switch (callLog->getStatus()) {
case linphone::Call::Status::Aborted:
case linphone::Call::Status::EarlyAborted:
if(pRegistredProxies.contains(localAddress)){
switch (callLog->getStatus()) {
case linphone::Call::Status::Aborted:
case linphone::Call::Status::EarlyAborted:
return; // Ignore aborted calls.
case linphone::Call::Status::AcceptedElsewhere:
case linphone::Call::Status::DeclinedElsewhere:
case linphone::Call::Status::AcceptedElsewhere:
case linphone::Call::Status::DeclinedElsewhere:
return; // Ignore accepted calls on other device.
case linphone::Call::Status::Success:
case linphone::Call::Status::Declined:
case linphone::Call::Status::Success:
case linphone::Call::Status::Declined:
case linphone::Call::Status::Missed:
case linphone::Call::Status::Missed:
break;
}
}
ConferenceId conferenceId{ peerAddress, localAddress };
if (conferenceDone.contains(conferenceId))
continue; // Already used.
conferenceDone << conferenceId;
ConferenceId conferenceId{ peerAddress, localAddress };
if (conferenceDone.contains(conferenceId))
continue; // Already used.
conferenceDone << conferenceId;
// The duration can be wrong if status is not success.
QDateTime timestamp(callLog->getStatus() == linphone::Call::Status::Success
? QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000)
: QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000));
QDateTime timestamp(callLog->getStatus() == linphone::Call::Status::Success
? QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000)
: QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000));
auto &localToConferenceEntry = getSipAddressEntry(peerAddress)->localAddressToConferenceEntry;
auto it = localToConferenceEntry.find(localAddress);
if (it == localToConferenceEntry.end())
localToConferenceEntry[localAddress] = { 0,0, false, move(timestamp) };
else if (it->timestamp.isNull() || timestamp > it->timestamp)
it->timestamp = move(timestamp);
auto &localToConferenceEntry = getSipAddressEntry(peerAddress)->localAddressToConferenceEntry;
auto it = localToConferenceEntry.find(localAddress);
if (it == localToConferenceEntry.end())
localToConferenceEntry[localAddress] = { 0,0, false, move(timestamp) };
else if (it->timestamp.isNull() || timestamp > it->timestamp)
it->timestamp = move(timestamp);
}
}
}

View file

@ -52,6 +52,8 @@ public:
};
SipAddressesModel (QObject *parent = Q_NULLPTR);
void reset();
int rowCount (const QModelIndex &index = QModelIndex()) const override;
@ -78,6 +80,8 @@ public:
Q_INVOKABLE static QString cleanSipAddress (const QString &sipAddress);
// ---------------------------------------------------------------------------
signals:
void sipAddressReset();// The model has been reset
private:
bool removeRow (int row, const QModelIndex &parent = QModelIndex());
@ -104,6 +108,8 @@ private:
void handleMessageSent (const std::shared_ptr<linphone::ChatMessage> &message);
void handleIsComposingChanged (const std::shared_ptr<linphone::ChatRoom> &chatRoom);
void handleRegistrationStateChanged( const std::shared_ptr<linphone::ProxyConfig> &proxyConfig, linphone::RegistrationState state);
// ---------------------------------------------------------------------------
@ -125,8 +131,8 @@ private:
void initSipAddresses ();
void initSipAddressesFromChat ();
void initSipAddressesFromCalls ();
void initSipAddressesFromChat (const QStringList &pRegistredProxies);// Read chat logs and keep only for registred proxies
void initSipAddressesFromCalls (const QStringList &pRegistredProxies);// Read call logs and keep only for registred proxies
void initSipAddressesFromContacts ();
void initRefs ();
@ -143,7 +149,7 @@ private:
it = mPeerAddressToSipAddressEntry.insert(peerAddress, { peerAddress, nullptr, Presence::Offline, {} });
return &(*it);
}
QStringList mRegistredProxies;// Storing registred proxies is used to avoid loosing logs when disconnected
QHash<QString, SipAddressEntry> mPeerAddressToSipAddressEntry;
QList<const SipAddressEntry *> mRefs;

View file

@ -33,6 +33,9 @@ TimelineModel::TimelineModel (QObject *parent) : QSortFilterProxyModel(parent) {
QObject::connect(accountSettingsModel, &AccountSettingsModel::accountSettingsUpdated, this, [this]() {
handleLocalAddressChanged(CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddressAsStringUriOnly());
});
QObject::connect(coreManager->getSipAddressesModel(), &SipAddressesModel::sipAddressReset, this, [this]() {
invalidate();// Invalidate and reload GUI if the model has been reset
});
mLocalAddress = accountSettingsModel->getUsedSipAddressAsStringUriOnly();
setSourceModel(coreManager->getSipAddressesModel());

View file

@ -20,6 +20,8 @@
#include <QFileInfo>
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include "Utils.hpp"
@ -355,3 +357,20 @@ QString Utils::getCountryName(const QLocale::Country& p_country)
countryName = QLocale::countryToString(p_country);
return countryName;
}
// Copy a folder recursively without erasing old file
void Utils::copyDir(QString from, QString to) {
QDir dir;
dir.setPath(from);
from += QDir::separator();
to += QDir::separator();
foreach (QString copyFile, dir.entryList(QDir::Files)) {// Copy each files
QString toFile = to + copyFile;
if (!QFile::exists(toFile))
QFile::copy(from+copyFile, toFile);
}
foreach (QString nextDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {// Copy folder
QString toDir = to + nextDir;
QDir().mkpath(toDir);// no need to check if dir exists
copyDir(from + nextDir, toDir);//Go up
}
}

View file

@ -98,6 +98,7 @@ namespace Utils {
return connection;
}
QString getCountryName(const QLocale::Country& country);
void copyDir(QString from, QString to);// Copy a folder recursively without erasing old file
}
#endif // UTILS_H_