diff --git a/include/linphone/utils/general.h b/include/linphone/utils/general.h index 71f444cd7..fea6e3ff0 100644 --- a/include/linphone/utils/general.h +++ b/include/linphone/utils/general.h @@ -117,9 +117,11 @@ constexpr T *getPublicHelper (Object *object, const ObjectPrivate *) { #define L_DECLARE_PUBLIC(CLASS) \ inline CLASS *getPublic () { \ + L_ASSERT(mPublic); \ return getPublicHelper(mPublic, this); \ } \ inline const CLASS *getPublic () const { \ + L_ASSERT(mPublic); \ return getPublicHelper(mPublic, this); \ } \ friend class CLASS; diff --git a/src/content/content.cpp b/src/content/content.cpp index 3d18fbe59..b9581de73 100644 --- a/src/content/content.cpp +++ b/src/content/content.cpp @@ -72,9 +72,11 @@ Content &Content::operator= (Content &&src) { return *this; } -bool Content::operator==(const Content& content) const { +bool Content::operator== (const Content &content) const { L_D(); - return d->contentType == content.getContentType() && d->body == content.getBody() && d->contentDisposition == content.getContentDisposition(); + return d->contentType == content.getContentType() && + d->body == content.getBody() && + d->contentDisposition == content.getContentDisposition(); } const ContentType &Content::getContentType () const { diff --git a/src/content/content.h b/src/content/content.h index 665293dbc..57f78787f 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -39,7 +39,7 @@ public: Content &operator= (const Content &src); Content &operator= (Content &&src); - bool operator==(const Content& content) const; + bool operator== (const Content &content) const; const ContentType &getContentType () const; void setContentType (const ContentType &contentType); diff --git a/src/db/abstract/abstract-db.cpp b/src/db/abstract/abstract-db.cpp index acc03fa84..731f53bed 100644 --- a/src/db/abstract/abstract-db.cpp +++ b/src/db/abstract/abstract-db.cpp @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifdef SOCI_ENABLED + #include +#endif // ifdef SOCI_ENABLED + #include "abstract-db-p.h" #include "db/provider/db-session-provider.h" #include "logger/logger.h" @@ -86,17 +90,27 @@ string AbstractDb::primaryKeyAutoIncrementStr (const string &type) const { return ""; } -string AbstractDb::insertOrIgnoreStr () const { +long AbstractDb::getLastInsertId () const { L_D(); - switch (d->backend) { - case Mysql: - return "INSERT IGNORE INTO "; + string sql; + switch(d->backend) { case Sqlite3: - return "INSERT OR IGNORE INTO "; + sql = "SELECT last_insert_rowid()"; + break; + default: + lWarning() << "Unsupported backend."; + return -1; } - return ""; + long result = 0; + + #ifdef SOCI_ENABLED + soci::session *session = d->dbSession.getBackendSession(); + *session << sql, soci::into(result); + #endif // ifdef SOCI_ENABLED + + return result; } LINPHONE_END_NAMESPACE diff --git a/src/db/abstract/abstract-db.h b/src/db/abstract/abstract-db.h index 153dfea79..f8250f37b 100644 --- a/src/db/abstract/abstract-db.h +++ b/src/db/abstract/abstract-db.h @@ -52,7 +52,8 @@ protected: virtual void init (); std::string primaryKeyAutoIncrementStr (const std::string &type = "INT") const; - std::string insertOrIgnoreStr () const; + + long getLastInsertId () const; private: L_DECLARE_PRIVATE(AbstractDb); diff --git a/src/db/events-db.cpp b/src/db/events-db.cpp index 00d1d3304..ae51560a1 100644 --- a/src/db/events-db.cpp +++ b/src/db/events-db.cpp @@ -41,7 +41,38 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE -class EventsDbPrivate : public AbstractDbPrivate {}; +struct MessageEventReferences { +#ifdef SOCI_ENABLED + long eventId; + long localSipAddressId; + long remoteSipAddressId; + long chatRoomId; + long contentTypeId; +#endif +}; + +class EventsDbPrivate : public AbstractDbPrivate { +#ifdef SOCI_ENABLED +public: + long insertSipAddress (const string &sipAddress); + long insertContentType (const string &contentType); + long insertEvent (EventLog::Type type, const tm &date); + long insertChatRoom (long sipAddressId, const tm &date); + long insertMessageEvent ( + const MessageEventReferences &references, + ChatMessage::State state, + ChatMessage::Direction direction, + const string &imdnMessageId, + bool isSecured, + const string *text = nullptr + ); + + void importLegacyMessages (const soci::rowset &messages); +#endif + +private: + L_DECLARE_PUBLIC(EventsDb); +}; // ----------------------------------------------------------------------------- @@ -78,16 +109,6 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {} ); } -// ----------------------------------------------------------------------------- - - struct MessageEventReferences { - long eventId; - long localSipAddressId; - long remoteSipAddressId; - long chatRoomId; - long contentTypeId; - }; - // ----------------------------------------------------------------------------- static string buildSqlEventFilter (const list &filters, EventsDb::FilterMask mask) { @@ -111,7 +132,7 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {} sql += " WHERE "; } else sql += " OR "; - sql += " event_type_id = "; + sql += " type = "; sql += mapEventFilterToSql(filter); } @@ -120,73 +141,148 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {} // ----------------------------------------------------------------------------- - static inline long insertSipAddress (soci::session &session, const string &sipAddress) { + long EventsDbPrivate::insertSipAddress (const string &sipAddress) { + L_Q(); + soci::session *session = dbSession.getBackendSession(); + long id; - session << "SELECT id FROM sip_address WHERE value = :sipAddress", soci::use(sipAddress), soci::into(id); - if (session.got_data()) + *session << "SELECT id FROM sip_address WHERE value = :sipAddress", soci::use(sipAddress), soci::into(id); + if (session->got_data()) return id; - session << "INSERT INTO sip_address (value) VALUES (:sipAddress)", soci::use(sipAddress); - session.get_last_insert_id("sip_address", id); - return id; + *session << "INSERT INTO sip_address (value) VALUES (:sipAddress)", soci::use(sipAddress); + return q->getLastInsertId(); } - static inline long insertContentType (soci::session &session, const string &contentType) { + long EventsDbPrivate::insertContentType (const string &contentType) { + L_Q(); + soci::session *session = dbSession.getBackendSession(); + long id; - session << "SELECT id FROM content_type WHERE value = :contentType", soci::use(contentType), soci::into(id); - if (session.got_data()) + *session << "SELECT id FROM content_type WHERE value = :contentType", soci::use(contentType), soci::into(id); + if (session->got_data()) return id; - session << "INSERT INTO content_type (value) VALUES (:contentType)", soci::use(contentType); - session.get_last_insert_id("content_type", id); - return id; + *session << "INSERT INTO content_type (value) VALUES (:contentType)", soci::use(contentType); + return q->getLastInsertId(); } - static inline long insertEvent (soci::session &session, EventLog::Type type, const tm &date) { - session << "INSERT INTO event (event_type_id, date) VALUES (:eventTypeId, :date)", + long EventsDbPrivate::insertEvent (EventLog::Type type, const tm &date) { + L_Q(); + soci::session *session = dbSession.getBackendSession(); + + *session << "INSERT INTO event (type, date) VALUES (:type, :date)", soci::use(static_cast(type)), soci::use(date); - long id; - session.get_last_insert_id("event", id); - return id; + return q->getLastInsertId(); } - static inline long insertChatRoom (soci::session &session, long sipAddressId, const tm &date) { + long EventsDbPrivate::insertChatRoom (long sipAddressId, const tm &date) { + soci::session *session = dbSession.getBackendSession(); + long id; - session << "SELECT peer_sip_address_id FROM chat_room WHERE peer_sip_address_id = :sipAddressId", + *session << "SELECT peer_sip_address_id FROM chat_room WHERE peer_sip_address_id = :sipAddressId", soci::use(sipAddressId), soci::into(id); - if (!session.got_data()) - session << "INSERT INTO chat_room (peer_sip_address_id, creation_date, last_update_date, subject) VALUES" + if (!session->got_data()) + *session << "INSERT INTO chat_room (peer_sip_address_id, creation_date, last_update_date, subject) VALUES" " (:sipAddressId, :creationDate, :lastUpdateDate, '')", soci::use(sipAddressId), soci::use(date), soci::use(date); else - session << "UPDATE chat_room SET last_update_date = :lastUpdateDate WHERE peer_sip_address_id = :sipAddressId", + *session << "UPDATE chat_room SET last_update_date = :lastUpdateDate WHERE peer_sip_address_id = :sipAddressId", soci::use(date), soci::use(sipAddressId); + return sipAddressId; } - static inline long insertMessageEvent ( - soci::session &session, + long EventsDbPrivate::insertMessageEvent ( const MessageEventReferences &references, ChatMessage::State state, ChatMessage::Direction direction, const string &imdnMessageId, bool isSecured, - const string *text = nullptr + const string *text ) { - soci::indicator textIndicator = text ? soci::i_ok : soci::i_null; + L_Q(); + soci::session *session = dbSession.getBackendSession(); - session << "INSERT INTO message_event (" - " event_id, chat_room_id, local_sip_address_id, remote_sip_address_id, content_type_id," + soci::indicator textIndicator = text ? soci::i_ok : soci::i_null; + *session << "INSERT INTO message_event (" + " event_id, chat_room_id, local_sip_address_id, remote_sip_address_id," " state, direction, imdn_message_id, is_secured, text" ") VALUES (" - " :eventId, :chatRoomId, :localSipaddressId, :remoteSipaddressId, :contentTypeId," + " :eventId, :chatRoomId, :localSipaddressId, :remoteSipaddressId," " :state, :direction, :imdnMessageId, :isSecured, :text" ")", soci::use(references.eventId), soci::use(references.chatRoomId), soci::use(references.localSipAddressId), - soci::use(references.remoteSipAddressId), soci::use(references.contentTypeId), - soci::use(static_cast(state)), soci::use(static_cast(direction)), soci::use(imdnMessageId), - soci::use(isSecured ? 1 : 0), soci::use(text ? *text : string(), textIndicator); - long id; - return session.get_last_insert_id("message_event", id); - return id; + soci::use(references.remoteSipAddressId), soci::use(static_cast(state)), + soci::use(static_cast(direction)), soci::use(imdnMessageId), soci::use(isSecured ? 1 : 0), + soci::use(text ? *text : string(), textIndicator); + return q->getLastInsertId(); + } + +// ----------------------------------------------------------------------------- + + template + static T getValueFromLegacyMessage (const soci::row &message, int index, bool &isNull) { + isNull = false; + + try { + return message.get(index); + } catch (const exception &) { + isNull = true; + } + + return T(); + } + + void EventsDbPrivate::importLegacyMessages (const soci::rowset &messages) { + soci::session *session = dbSession.getBackendSession(); + + soci::transaction tr(*session); + + for (const auto &message : messages) { + const int direction = message.get(3) + 1; + if (direction != 1 && direction != 2) { + lWarning() << "Unable to import legacy message with invalid direction."; + return; + } + + const int state = message.get(7, static_cast(ChatMessage::State::Displayed)); + + const tm date = Utils::getLongAsTm(message.get(9, 0)); + + const bool noUrl = false; + const string url = getValueFromLegacyMessage(message, 8, const_cast(noUrl)); + + const string contentType = message.get( + 13, + message.get(11, -1) != -1 + ? "application/vnd.gsma.rcs-ft-http+xml" + : (noUrl ? "text/plain" : "message/external-body") + ); + + const bool noText = false; + const string text = getValueFromLegacyMessage(message, 4, const_cast(noText)); + + struct MessageEventReferences references; + references.eventId = insertEvent(EventLog::Type::ChatMessage, date); + references.localSipAddressId = insertSipAddress(message.get(1)); + references.remoteSipAddressId = insertSipAddress(message.get(2)); + references.chatRoomId = insertChatRoom(references.remoteSipAddressId, date); + references.contentTypeId = insertContentType(contentType); + + insertMessageEvent ( + references, + static_cast(state), + static_cast(direction), + message.get(12, ""), + !!message.get(14, 0), + noText ? nullptr : &text + ); + + const bool noAppData = false; + const string appData = getValueFromLegacyMessage(message, 10, const_cast(noAppData)); + (void)appData; + } + + tr.commit(); } // ----------------------------------------------------------------------------- @@ -210,7 +306,7 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {} *session << "CREATE TABLE IF NOT EXISTS event (" " id" + primaryKeyAutoIncrementStr() + "," - " event_type_id TINYINT UNSIGNED NOT NULL," + " type TINYINT UNSIGNED NOT NULL," " date DATE NOT NULL" ")"; @@ -238,10 +334,8 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {} " id" + primaryKeyAutoIncrementStr() + "," " event_id INT UNSIGNED NOT NULL," " chat_room_id INT UNSIGNED NOT NULL," - " local_sip_address_id INT UNSIGNED NOT NULL," " remote_sip_address_id INT UNSIGNED NOT NULL," - " content_type_id INT UNSIGNED NOT NULL," // See: https://tools.ietf.org/html/rfc5438#section-6.3 @@ -468,79 +562,14 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {} // ----------------------------------------------------------------------------- - template - static T getValueFromLegacyMessage (const soci::row &message, int index, bool &isNull) { - isNull = false; - - try { - return message.get(index); - } catch (const exception &) { - isNull = true; - } - - return T(); - } - - static void importLegacyMessages ( - soci::session *session, - const string &insertOrIgnoreStr, - const soci::rowset &messages - ) { - soci::transaction tr(*session); - - for (const auto &message : messages) { - const int direction = message.get(3) + 1; - if (direction != 1 && direction != 2) { - lWarning() << "Unable to import legacy message with invalid direction."; - return; - } - - const int state = message.get(7, static_cast(ChatMessage::State::Displayed)); - - const tm date = Utils::getLongAsTm(message.get(9, 0)); - - const bool noUrl = false; - const string url = getValueFromLegacyMessage(message, 8, const_cast(noUrl)); - - const string contentType = message.get( - 13, - message.get(11, -1) != -1 - ? "application/vnd.gsma.rcs-ft-http+xml" - : (noUrl ? "text/plain" : "message/external-body") - ); - - const bool noText = false; - const string text = getValueFromLegacyMessage(message, 4, const_cast(noText)); - - struct MessageEventReferences references; - references.eventId = insertEvent(*session, EventLog::Type::ChatMessage, date); - references.localSipAddressId = insertSipAddress(*session, message.get(1)); - references.remoteSipAddressId = insertSipAddress(*session, message.get(2)); - references.chatRoomId = insertChatRoom(*session, references.remoteSipAddressId, date); - references.contentTypeId = insertContentType(*session, contentType); - - insertMessageEvent ( - *session, - references, - static_cast(state), - static_cast(direction), - message.get(12, ""), - !!message.get(14, 0), - noText ? nullptr : &text - ); - - const bool noAppData = false; - const string appData = getValueFromLegacyMessage(message, 10, const_cast(noAppData)); - (void)text; - (void)appData; - } - - tr.commit(); - } - bool EventsDb::import (Backend, const string ¶meters) { L_D(); + if (!isConnected()) { + lWarning() << "Unable to import data. Not connected."; + return 0; + } + // Backend is useless, it's sqlite3. (Only available legacy backend.) const string uri = "sqlite3://" + parameters; DbSession inDbSession = DbSessionProvider::getInstance()->getSession(uri); @@ -550,14 +579,13 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {} return false; } - soci::session *outSession = d->dbSession.getBackendSession(); soci::session *inSession = inDbSession.getBackendSession(); // Import messages. try { - soci::rowset messages = (inSession->prepare << "SELECT * FROM history ORDER BY id DESC"); + soci::rowset messages = (inSession->prepare << "SELECT * FROM history"); try { - importLegacyMessages(outSession, insertOrIgnoreStr(), messages); + d->importLegacyMessages(messages); } catch (const exception &e) { lInfo() << "Failed to import legacy messages from: `" << uri << "`. (" << e.what() << ")"; return false; diff --git a/src/object/object-p.h b/src/object/object-p.h index 2623ea0f0..c03afac98 100644 --- a/src/object/object-p.h +++ b/src/object/object-p.h @@ -36,7 +36,7 @@ public: virtual ~ObjectPrivate () = default; protected: - Object *mPublic; + Object *mPublic = nullptr; private: std::unordered_map properties; diff --git a/src/object/object.cpp b/src/object/object.cpp index 3f127b04b..7c62c0c42 100644 --- a/src/object/object.cpp +++ b/src/object/object.cpp @@ -30,12 +30,14 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE +Object::Object (ObjectPrivate &p) : mPrivate(&p) { + mPrivate->mPublic = this; +} + Object::~Object () { delete mPrivate; } -Object::Object (ObjectPrivate &p) : mPrivate(&p) {} - shared_ptr Object::getSharedFromThis () { return const_pointer_cast(static_cast(this)->getSharedFromThis()); }