Commit 8dfc664d authored by Andreas Traczyk's avatar Andreas Traczyk

database: migrate to per account database

Gitlab: #407
Change-Id: I834cf0d216dfd9e6badab8d7aab951b8875c1bd6
parent 2697394b
......@@ -280,7 +280,7 @@ SET( libringclient_LIB_SRCS
src/conversationmodel.cpp
src/database.cpp
src/authority/daemon.cpp
src/authority/databasehelper.cpp
src/authority/storagehelper.cpp
src/lrc.cpp
src/newaccountmodel.cpp
src/peerdiscoverymodel.cpp
......
......@@ -131,7 +131,7 @@ struct Info
std::chrono::steady_clock::time_point startTime;
Status status = Status::INVALID;
Type type = Type::INVALID;
std::string peer;
std::string peerUri;
bool isOutgoing;
bool audioMuted = false;
bool videoMuted = false;
......
......@@ -33,6 +33,13 @@ namespace api
namespace contact
{
/**
* @var profileInfo
* @var registeredName
* @var isTrusted
* @var isPresent
* @var isBanned
*/
struct Info
{
profile::Info profileInfo;
......
......@@ -57,7 +57,7 @@ public:
const account::Info& owner;
ContactModel(const account::Info& owner,
Database& database,
Database& db,
const CallbacksHandler& callbacksHandler,
const BehaviorController& behaviorController);
~ContactModel();
......@@ -85,18 +85,6 @@ public:
* @return list of banned contacts uris as string
*/
const std::list<std::string>& getBannedContacts() const;
/**
* @param uri
* @param isAccount
* @return empty string if no contact, else the uri in db
*/
const std::string getProfileId(const std::string &ur, bool isAccount = false) const;
/**
* @deprecated use getProfileId
* @param contactUri
* @return empty string if no contact, else the uri in db
*/
const std::string getContactProfileId(const std::string& contactUri) const;
/**
* @return all contacts for this account.
*/
......
......@@ -36,8 +36,8 @@ enum class Type {
TEXT,
CALL,
CONTACT,
OUTGOING_DATA_TRANSFER,
INCOMING_DATA_TRANSFER
DATA_TRANSFER,
COUNT__
};
static inline const std::string
......@@ -50,11 +50,10 @@ to_string(const Type& type)
return "CALL";
case Type::CONTACT:
return "CONTACT";
case Type::OUTGOING_DATA_TRANSFER:
return "OUTGOING_DATA_TRANSFER";
case Type::INCOMING_DATA_TRANSFER:
return "INCOMING_DATA_TRANSFER";
case Type::DATA_TRANSFER:
return "DATA_TRANSFER";
case Type::INVALID:
case Type::COUNT__:
default:
return "INVALID";
}
......@@ -69,24 +68,19 @@ to_type(const std::string& type)
return interaction::Type::CALL;
else if (type == "CONTACT")
return interaction::Type::CONTACT;
else if (type == "OUTGOING_DATA_TRANSFER")
return interaction::Type::OUTGOING_DATA_TRANSFER;
else if (type == "INCOMING_DATA_TRANSFER")
return interaction::Type::INCOMING_DATA_TRANSFER;
else if (type == "DATA_TRANSFER")
return interaction::Type::DATA_TRANSFER;
else
return interaction::Type::INVALID;
}
enum class Status {
INVALID,
UNKNOWN,
SENDING,
FAILED,
SUCCEED,
READ,
UNREAD,
TRANSFER_CREATED, /*[jn] mettre à jour les fonctions de conversion */
FAILURE,
SUCCESS,
TRANSFER_CREATED,
TRANSFER_ACCEPTED,
TRANSFER_CANCELED,
TRANSFER_ERROR,
......@@ -95,7 +89,8 @@ enum class Status {
TRANSFER_AWAITING_PEER,
TRANSFER_AWAITING_HOST,
TRANSFER_TIMEOUT_EXPIRED,
TRANSFER_FINISHED
TRANSFER_FINISHED,
COUNT__
};
static inline const std::string
......@@ -106,14 +101,10 @@ to_string(const Status& status)
return "UNKNOWN";
case Status::SENDING:
return "SENDING";
case Status::FAILED:
return "FAILED";
case Status::SUCCEED:
return "SUCCEED";
case Status::READ:
return "READ";
case Status::UNREAD:
return "UNREAD";
case Status::FAILURE:
return "FAILURE";
case Status::SUCCESS:
return "SUCCESS";
case Status::TRANSFER_CREATED:
return "TRANSFER_CREATED";
case Status::TRANSFER_ACCEPTED:
......@@ -135,6 +126,7 @@ to_string(const Status& status)
case Status::TRANSFER_FINISHED:
return "TRANSFER_FINISHED";
case Status::INVALID:
case Status::COUNT__:
default:
return "INVALID";
}
......@@ -144,56 +136,60 @@ static inline Status
to_status(const std::string& status)
{
if (status == "UNKNOWN")
return interaction::Status::UNKNOWN;
return Status::UNKNOWN;
else if (status == "SENDING")
return interaction::Status::SENDING;
else if (status == "FAILED")
return interaction::Status::FAILED;
else if (status == "SUCCEED")
return interaction::Status::SUCCEED;
else if (status == "READ")
return interaction::Status::READ;
else if (status == "UNREAD")
return interaction::Status::UNREAD;
return Status::SENDING;
else if (status == "FAILURE")
return Status::FAILURE;
else if (status == "SUCCESS")
return Status::SUCCESS;
else if (status == "TRANSFER_CREATED")
return interaction::Status::TRANSFER_CREATED;
return Status::TRANSFER_CREATED;
else if (status == "TRANSFER_ACCEPTED")
return interaction::Status::TRANSFER_ACCEPTED;
return Status::TRANSFER_ACCEPTED;
else if (status == "TRANSFER_CANCELED")
return interaction::Status::TRANSFER_CANCELED;
return Status::TRANSFER_CANCELED;
else if (status == "TRANSFER_ERROR")
return interaction::Status::TRANSFER_ERROR;
return Status::TRANSFER_ERROR;
else if (status == "TRANSFER_UNJOINABLE_PEER")
return interaction::Status::TRANSFER_UNJOINABLE_PEER;
return Status::TRANSFER_UNJOINABLE_PEER;
else if (status == "TRANSFER_ONGOING")
return interaction::Status::TRANSFER_ONGOING;
return Status::TRANSFER_ONGOING;
else if (status == "TRANSFER_AWAITING_HOST")
return interaction::Status::TRANSFER_AWAITING_HOST;
return Status::TRANSFER_AWAITING_HOST;
else if (status == "TRANSFER_AWAITING_PEER")
return interaction::Status::TRANSFER_AWAITING_PEER;
return Status::TRANSFER_AWAITING_PEER;
else if (status == "TRANSFER_TIMEOUT_EXPIRED")
return interaction::Status::TRANSFER_TIMEOUT_EXPIRED;
return Status::TRANSFER_TIMEOUT_EXPIRED;
else if (status == "TRANSFER_FINISHED")
return interaction::Status::TRANSFER_FINISHED;
return Status::TRANSFER_FINISHED;
else
return interaction::Status::INVALID;
return Status::INVALID;
}
/**
* @var authorUri
* @var body
* @var timestamp
* @var duration
* @var type
* @var status
* @var isRead
*/
struct Info
{
std::string authorUri;
std::string body;
std::time_t timestamp = 0;
std::time_t duration = 0;
Type type = Type::INVALID;
Status status = Status::INVALID;
bool isRead = false;
};
static inline bool isOutgoing(const Info& interaction) {
return (interaction.status != lrc::api::interaction::Status::READ
&& interaction.status != lrc::api::interaction::Status::UNREAD
&& interaction.type != lrc::api::interaction::Type::INCOMING_DATA_TRANSFER)
|| interaction.type == lrc::api::interaction::Type::OUTGOING_DATA_TRANSFER;
return interaction.authorUri.empty();
}
} // namespace interaction
......
......@@ -41,7 +41,15 @@ class AVModel;
class LIB_EXPORT Lrc {
public:
Lrc();
/**
* Construct an Lrc object and optionally invoke callbacks
* to control ui informing the user of a possibly lengthy
* migration process.
* @param willMigrateCb
* @param didMigrateCb
*/
Lrc(MigrationCb willMigrateCb = {},
MigrationCb didMigrateCb = {});
~Lrc();
/**
* get a reference on account model.
......@@ -77,6 +85,10 @@ public:
* Can communicate with the daemon via dbus
*/
static bool dbusIsValid();
/**
* Connect to debugMessageReceived signal
*/
void subscribeToDebugReceived();
/**
* Helper: get call list from daemon
......
......@@ -53,12 +53,11 @@ class BehaviorController;
class LIB_EXPORT NewAccountModel : public QObject {
Q_OBJECT
public:
using AccountInfoMap = std::map<std::string, account::Info>;
NewAccountModel(Lrc& lrc,
Database& database,
const CallbacksHandler& callbackHandler,
const api::BehaviorController& behaviorController);
const api::BehaviorController& behaviorController,
MigrationCb& willMigrateCb,
MigrationCb& didMigrateCb);
~NewAccountModel();
/**
......@@ -173,13 +172,13 @@ public:
* Set an account to the first position
*/
void setTopAccount(const std::string& accountId);
/**
* Build the vCard for an account
* Get the vCard for an account
* @param id
* @return vcard of the account
*/
std::string accountVCard(const std::string& accountId, bool compressImage = true) const;
std::string compressedAvatar(const std::string& img) const;
Q_SIGNALS:
/**
......
......@@ -72,11 +72,12 @@ public:
/**
* Create a new call with a contact
* @param url of the contact to call
* @param uri of the contact to call
* @param isAudioOnly, set to false by default
* @return the call uid created. Empty string is returned if call couldn't be created.
*/
std::string createCall(const std::string& url, bool isAudioOnly = false);
std::string createCall(const std::string& uri, bool isAudioOnly = false);
/**
* Get the call from its call id
* @param uid
......@@ -84,6 +85,7 @@ public:
* @throw out_of_range exception if not found
*/
const call::Info& getCall(const std::string& uid) const;
/**
* Get the call from the peer uri
* @param uri
......@@ -92,6 +94,7 @@ public:
* @throw out_of_range exception if not found
*/
const call::Info& getCallFromURI(const std::string& uri, bool notOver = false) const;
/**
* Get conference from a peer uri
* @param uri
......@@ -99,11 +102,13 @@ public:
* @throw out_of_range exception if not found
*/
const call::Info& getConferenceFromURI(const std::string& uri) const;
/**
* @param callId to test
* @return true if callId is presend else false.
*/
bool hasCall(const std::string& callId) const;
/**
* Send a text message to a SIP call
* @param callId
......@@ -116,64 +121,76 @@ public:
* @param callId
*/
void accept(const std::string& callId) const;
/**
* Hang up a call
* @param callId
*/
void hangUp(const std::string& callId) const;
/**
* Refuse a call
* @param callId
*/
void refuse(const std::string& callId) const;
/**
* Toggle audio record on a call
* @param callId
*/
void toggleAudioRecord(const std::string& callId) const;
/**
* Play DTMF in a call
* @param callId
* @param value to play
*/
void playDTMF(const std::string& callId, const std::string& value) const;
/**
* Toggle pause on a call
* @param callId
*/
void togglePause(const std::string& callId) const;
/**
* Toggle a media on a call
* @param callId
* @param media {AUDIO, VIDEO}
*/
void toggleMedia(const std::string& callId, const NewCallModel::Media media) const;
/**
* Not implemented yet
*/
void setQuality(const std::string& callId, const double quality) const;
/**
* Blind transfer. Directly transfer a call to a sip number
* @param callId: the call to transfer
* @param to: the sip number (for example: "sip:1412")
*/
void transfer(const std::string& callId, const std::string& to) const;
/**
* Perform an attended. Transfer a call to another call
* @param callIdSrc: the call to transfer
* @param callIdDest: the destination's call
*/
void transferToCall(const std::string& callIdSrc, const std::string& callIdDest) const;
/**
* Create a conference from 2 calls.
* @param callIdA uid of the call A
* @param callIdB uid of the call B
*/
void joinCalls(const std::string& callIdA, const std::string& callIdB) const;
/**
* Not implemented yet
*/
void removeParticipant(const std::string& callId, const std::string& participant) const;
/**
* @param callId
* @return a human readable call duration (M:ss)
......
......@@ -35,7 +35,8 @@ enum class Type {
RING,
SIP,
PENDING,
TEMPORARY
TEMPORARY,
COUNT__
};
static inline const std::string
......@@ -51,6 +52,7 @@ to_string(const Type& type)
case Type::TEMPORARY:
return "TEMPORARY";
case Type::INVALID:
case Type::COUNT__:
default:
return "INVALID";
}
......@@ -71,6 +73,12 @@ to_type(const std::string& type)
return Type::INVALID;
}
/**
* @var uri
* @var avatar
* @var alias
* @var type
*/
struct Info
{
std::string uri = "";
......
/****************************************************************************
* Copyright (C) 2017-2019 Savoir-faire Linux Inc. *
* Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com> *
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> *
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "databasehelper.h"
#include "api/profile.h"
#include "api/datatransfer.h"
#include <account_const.h>
#include <datatransfer_interface.h>
namespace lrc
{
namespace authority
{
namespace database
{
std::string
getProfileId(Database& db,
const std::string& accountId,
const std::string& isAccount,
const std::string& uri)
{
auto accountProfiles = db.select("profile_id", "profiles_accounts",
"account_id=:account_id AND is_account=:is_account",
{{":account_id", accountId},
{":is_account", isAccount}}).payloads;
if (accountProfiles.empty() && (isAccount == "true")) {
return "";
}
if (isAccount == "true") return accountProfiles[0];
// we may have many contacts profiles for one account id,
// and need to check uri in addition to account id
auto profiles = db.select("id", "profiles", "uri=:uri", {{":uri", uri}}).payloads;
if (profiles.empty()) return "";
std::sort(accountProfiles.begin(), accountProfiles.end());
std::sort(profiles.begin(), profiles.end());
std::vector<std::string> common;
std::set_intersection(accountProfiles.begin(), accountProfiles.end(),
profiles.begin(), profiles.end(),
std::back_inserter(common));
//if profile exists but not linked with account id,
// update profiles_accounts. Except empty uri for SIP accounts
if(common.empty()) {
if(!uri.empty()) {
db.insertInto("profiles_accounts",
{{":profile_id", "profile_id"}, {":account_id", "account_id"},
{":is_account", "is_account"}},
{{":profile_id", profiles[0]}, {":account_id", accountId},
{":is_account", isAccount}});
}
return profiles[0];
}
return common[0];
}
std::string
getOrInsertProfile(Database& db,
const std::string& contactUri,
const std::string& accountId,
bool isAccount,
const std::string& type,
const std::string& alias,
const std::string& avatar)
{
// Check if profile is already present.
const std::string isAccountStr = isAccount ? "true" : "false";
auto profileAlreadyExists = getProfileId(db, accountId, isAccountStr, contactUri);
if (profileAlreadyExists.empty()) {
// Doesn't exists, add profile to the database
auto row = db.insertInto("profiles",
{{":uri", "uri"}, {":alias", "alias"}, {":photo", "photo"}, {":type", "type"},
{":status", "status"}},
{{":uri", contactUri}, {":alias", alias}, {":photo", avatar}, {":type", type},
{":status", "TRUSTED"}});
if (row == -1) {
qDebug() << "contact not added to the database";
return "";
}
// link profile id to account id
auto profiles = db.select("profile_id", "profiles_accounts",
"profile_id=:profile_id AND \
account_id=:account_id AND \
is_account=:is_account",
{{":profile_id", std::to_string(row)},
{":account_id", accountId},
{":is_account", isAccountStr}})
.payloads;
if (profiles.empty()) {
db.insertInto("profiles_accounts",
{{":profile_id", "profile_id"},
{":account_id", "account_id"},
{":is_account", "is_account"}},
{{":profile_id", std::to_string(row)},
{":account_id", accountId},
{":is_account", isAccountStr}});
}
return std::to_string(row);
} else {
// Exists, update and retrieve it.
if (!avatar.empty() && !alias.empty()) {
db.update("profiles",
"alias=:alias, photo=:photo",
{{":alias", alias}, {":photo", avatar}},
"id=:id", {{":id", profileAlreadyExists}});
} else if (!avatar.empty()) {
db.update("profiles",
"photo=:photo",
{{":photo", avatar}},
"id=:id", {{":id", profileAlreadyExists}});
}
return profileAlreadyExists;
}
}
std::vector<std::string>
getConversationsForProfile(Database& db, const std::string& profileId)
{
return db.select("id",
"conversations",
"participant_id=:participant_id",
{{":participant_id", profileId}}).payloads;
}
std::vector<std::string>
getPeerParticipantsForConversation(Database& db, const std::string& profileId, const std::string& conversationId)
{
return db.select("participant_id",
"conversations",
"id=:id AND participant_id!=:participant_id",
{{":id", conversationId}, {":participant_id", profileId}}).payloads;
}
std::string
getAvatarForProfileId(Database& db, const std::string& profileId)
{
auto returnFromDb = db.select("photo",
"profiles",
"id=:id",
{{":id", profileId}});
if (returnFromDb.nbrOfCols == 1 && returnFromDb.payloads.size() >= 1) {
auto payloads = returnFromDb.payloads;
return payloads[0];
}
return "";