diff --git a/src/api/newaccountmodel.h b/src/api/newaccountmodel.h index 6b7246c5ed5232102845ad41a7f051527dafa932..ad3437f6d45e0bd46ef1e48303e7101c47ed68ab 100644 --- a/src/api/newaccountmodel.h +++ b/src/api/newaccountmodel.h @@ -121,6 +121,13 @@ public: * @param enable */ void enableAccount(const std::string& accountId, bool enabled); + /** + * Change the avatar of an account + * @param accountId + * @param avatar + * @throws out_of_range exception if account is not found + */ + void setAvatar(const std::string& accountId, const std::string& avatar); Q_SIGNALS: /** diff --git a/src/authority/databasehelper.cpp b/src/authority/databasehelper.cpp index 412b15f75fe34991cc173901949d3a8d3a6f4739..7687aa8d3f2306cf96da07b71f81a62c1a003063 100644 --- a/src/authority/databasehelper.cpp +++ b/src/authority/databasehelper.cpp @@ -113,6 +113,16 @@ getAvatarForProfileId(Database& db, const std::string& profileId) return ""; } +void +setAvatarForProfileId(Database& db, const std::string& profileId, const std::string& avatar) +{ + db.update("profiles", + "photo=:photo", + {{":photo", avatar}}, + "id=:id", + {{":id", profileId}}); +} + api::contact::Info buildContactFromProfileId(Database& db, const std::string& profileId) { diff --git a/src/authority/databasehelper.h b/src/authority/databasehelper.h index 31725a1f7508ceca50236f84809df75436910f81..61f8d77cfd6effbd0f82bde8558381fb85f92890 100644 --- a/src/authority/databasehelper.h +++ b/src/authority/databasehelper.h @@ -88,6 +88,13 @@ std::vector<std::string> getPeerParticipantsForConversation(Database& db, */ std::string getAvatarForProfileId(Database& db, const std::string& profileId); +/** + * @param db + * @param profileId + * @param avatar + */ +void setAvatarForProfileId(Database& db, const std::string& profileId, const std::string& avatar); + api::contact::Info buildContactFromProfileId(Database& db, const std::string& profileId); /** diff --git a/src/call.cpp b/src/call.cpp index 923f53f31ef337969f13dd4e28098b7b59bc1346..603cd6a2ac04afda5113d676406d502a851002b6 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -21,13 +21,16 @@ #include "call.h" //Std include -#include <time.h> +#include <fstream> #include <memory> +#include <string> +#include <time.h> //Qt #include <QtCore/QFile> #include <QtCore/QTimer> #include <QtCore/QDateTime> +#include <QtCore/QStandardPaths> //DRing #include <account_const.h> @@ -36,6 +39,7 @@ //Ring library #include "dbus/callmanager.h" +#include "dbus/configurationmanager.h" #include "collectioninterface.h" #include "person.h" @@ -61,6 +65,9 @@ #include "personmodel.h" #include "private/contactmethod_p.h" +#include "database.h" +#include "authority/databasehelper.h" + #include "media/audio.h" #include "media/video.h" #include "media/text.h" @@ -1684,7 +1691,28 @@ void CallPrivate::sendProfile() * like this is not. Therefore we use the proprietary PROFILE_VCF MIME. */ auto t = mediaFactory<Media::Text>(Media::Media::Direction::OUT); - auto vCard = profile->person()->toVCard(); + + MapStringString details = ConfigurationManager::instance().getAccountDetails(m_Account->id()); + using namespace DRing::Account; + std::string uri = ""; + if (m_Account->protocol() == Account::Protocol::RING + && details[ConfProperties::USERNAME].contains("ring:")) { + uri = details[ConfProperties::USERNAME].toStdString().substr(std::string("ring:").size()); + } else { + uri = details[ConfProperties::USERNAME].toStdString(); + } + + // NOTE: Some clients still use the old LRC. So, we should be able to use the database or old VCard files for now. + // TODO: migrate sendProfile to newcallmodel.cpp as soon as possible. + std::ifstream dbfile(lrc::DATABASE_PATH.toStdString()); + std::string photo = ""; + if (dbfile.good()) { + lrc::Database db; + auto accountProfileId = lrc::authority::database::getOrInsertProfile(db, uri); + // Retrieve avatar from database + photo = lrc::authority::database::getAvatarForProfileId(db, accountProfileId); + } + auto vCard = profile->person()->toVCard({}, photo); qsrand(time(nullptr)); const auto& key = QString::number(qrand()); diff --git a/src/database.cpp b/src/database.cpp index 34b00984c3d817b45b6531d506b68ef1e144998f..fba93d5c694032b7d93f5c4c465364a1312f4567 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -30,7 +30,6 @@ #include <QtSql/QSqlDatabase> #include <QtSql/QSqlError> #include <QtSql/QSqlRecord> -#include <QtCore/QStandardPaths> #include <QtCore/QVariant> #include <QDir> @@ -52,9 +51,6 @@ namespace lrc using namespace api; -static constexpr auto VERSION = "1"; -static constexpr auto NAME = "ring.db"; - Database::Database() : QObject() { @@ -70,11 +66,7 @@ Database::Database() // initalize the database. db_ = QSqlDatabase::addDatabase("QSQLITE"); -#ifdef ENABLE_TEST - db_.setDatabaseName(QString("/tmp/") + QString(NAME)); -#else - db_.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + NAME); -#endif + db_.setDatabaseName(DATABASE_PATH); // open the database. if (not db_.open()) { diff --git a/src/database.h b/src/database.h index 3a14a769d3c071abe3d67cbb76c9337ec6395c3a..7163e29cce810a5cdeea3bc8356675669521b3fd 100644 --- a/src/database.h +++ b/src/database.h @@ -26,11 +26,22 @@ // Qt #include <qobject.h> +#include <QtCore/QDir> #include <QtSql/QSqlQuery> +#include <QtCore/QStandardPaths> namespace lrc { +static constexpr auto VERSION = "1"; +static constexpr auto NAME = "ring.db"; + +#ifdef ENABLE_TEST + static const QString DATABASE_PATH = QDir("/tmp/").filePath(NAME); +#else + static const QString DATABASE_PATH = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)).filePath(NAME); +#endif + /** * @brief Class that communicates with the database. * @note not thread safe. diff --git a/src/newaccountmodel.cpp b/src/newaccountmodel.cpp index da7bf6b14fdcede852fee37c01ba9cca21caabfa..ca39e3ccd5c7264db38ac40f8b9f9338168ce2da 100644 --- a/src/newaccountmodel.cpp +++ b/src/newaccountmodel.cpp @@ -170,6 +170,20 @@ NewAccountModel::enableAccount(const std::string& accountId, bool enabled) accountInfo->second.enabled = enabled; } +void +NewAccountModel::setAvatar(const std::string& accountId, const std::string& avatar) +{ + auto accountInfo = pimpl_->accounts.find(accountId); + if (accountInfo == pimpl_->accounts.end()) { + throw std::out_of_range("NewAccountModel::setAvatar, can't find " + accountId); + } + accountInfo->second.profileInfo.avatar = avatar; + auto accountProfileId = authority::database::getOrInsertProfile(pimpl_->database, accountInfo->second.profileInfo.uri); + if (!accountProfileId.empty()) { + authority::database::setAvatarForProfileId(pimpl_->database, accountProfileId, avatar); + } +} + bool NewAccountModel::exportToFile(const std::string& accountId, const std::string& path) const { diff --git a/src/person.cpp b/src/person.cpp index 2703cf7f3b520af7353d799b843ca95e802915f4..286a3044a691e7baa0b0649f2d11a8eefed3a206 100644 --- a/src/person.cpp +++ b/src/person.cpp @@ -724,7 +724,7 @@ void Person::addCustomField(const QString& key, const QString& value) d_ptr->m_lCustomAttributes.insert(key, value); } -const QByteArray Person::toVCard(QList<Account*> accounts) const +const QByteArray Person::toVCard(QList<Account*> accounts, const std::string& avatar) const { //serializing here VCardUtils maker; @@ -759,7 +759,12 @@ const QByteArray Person::toVCard(QList<Account*> accounts) const maker.addProperty(VCardUtils::Property::X_RINGACCOUNT, acc->id()); } - maker.addPhoto(GlobalInstances::pixmapManipulator().toByteArray(photo())); + if (avatar.empty()) { + maker.addPhoto(GlobalInstances::pixmapManipulator().toByteArray(photo())); + } else { + maker.addPhoto(QByteArray(avatar.c_str()), false); + } + return maker.endVCard(); } diff --git a/src/person.h b/src/person.h index 4907837fc14af3a52ac54a0da02df41ee852a7bd..7e502f46e4a3ef8647256d32bf2ab6c3c5eb4d32 100644 --- a/src/person.h +++ b/src/person.h @@ -113,7 +113,7 @@ public: //Mutator Q_INVOKABLE void addAddress(const Address& addr); Q_INVOKABLE void addCustomField(const QString& key, const QString& value); - Q_INVOKABLE const QByteArray toVCard(QList<Account*> accounts = {}) const; + Q_INVOKABLE const QByteArray toVCard(QList<Account*> accounts = {}, const std::string& avatar = "") const; protected: //The D-Pointer can be shared if a PlaceHolderPerson is merged with a real one diff --git a/src/private/vcardutils.cpp b/src/private/vcardutils.cpp index af73a2c35eab857e48488d7479a7e7d9f22b0f12..28c957b320b1b9e33e74103cd28b95f76a3b0bd0 100644 --- a/src/private/vcardutils.cpp +++ b/src/private/vcardutils.cpp @@ -279,13 +279,14 @@ void VCardUtils::addContactMethod(const QString& type, const QString& num) addProperty(prop, num); } -void VCardUtils::addPhoto(const QByteArray img) +void VCardUtils::addPhoto(const QByteArray img, bool convertToBase64) { - m_vCard << (QString::fromUtf8(Property::PHOTO) + - QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) + - "ENCODING=BASE64" + - QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) + - "TYPE=PNG:" + img.toBase64().trimmed()); + auto b64Img = convertToBase64 ? img.toBase64().trimmed() : img.trimmed(); + m_vCard << (QString::fromUtf8(Property::PHOTO) + + QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) + + "ENCODING=BASE64" + + QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) + + "TYPE=PNG:" + b64Img); } const QByteArray VCardUtils::endVCard() diff --git a/src/private/vcardutils.h b/src/private/vcardutils.h index 57e60a3ac2f93de426564e8d21d3a84163979787..028e1a2b0d903ef17e81a0b505f0c49b5701af0f 100644 --- a/src/private/vcardutils.h +++ b/src/private/vcardutils.h @@ -73,7 +73,7 @@ public: void addEmail(const QString& type, const QString& num); void addAddress(const Person::Address& addr); void addContactMethod(const QString& type, const QString& num); - void addPhoto(const QByteArray img); + void addPhoto(const QByteArray img, bool convertToBase64 = true); const QByteArray endVCard(); //Loading