From 09f95f5b5c511a01ca9fce373795adc01e5c8d6f Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Tue, 26 Jul 2016 23:54:00 +0200 Subject: [PATCH] Cleanup. --- src/mrchat.cpp | 281 ++++++++++++++++++++++++++++++++-- src/mrchat.h | 46 ++++-- src/mrcontact.cpp | 84 +++++++++- src/mrcontact.h | 16 +- src/mrimap.h | 2 +- src/mrimfparser.cpp | 6 +- src/mrmailbox.cpp | 42 ++++- src/mrmailbox.h | 15 +- src/mrmsg.cpp | 67 +++++++- src/mrmsg.h | 11 +- src/mrsqlite3.cpp | 363 +------------------------------------------- src/mrsqlite3.h | 10 +- 12 files changed, 517 insertions(+), 426 deletions(-) diff --git a/src/mrchat.cpp b/src/mrchat.cpp index 5c344e7f..6e75863b 100644 --- a/src/mrchat.cpp +++ b/src/mrchat.cpp @@ -29,6 +29,9 @@ #include #include "mrmailbox.h" #include "mrchat.h" +#include "mrtools.h" +#include "mrmsg.h" +#include "mrcontact.h" MrChat::MrChat(MrMailbox* mailbox) @@ -36,19 +39,234 @@ MrChat::MrChat(MrMailbox* mailbox) m_mailbox = mailbox; m_type = MR_CHAT_UNDEFINED; m_name = NULL; - m_last_timestamp = 0; - m_last_msg_type = MR_MSG_UNDEFINED; - m_last_msg = NULL; + m_lastMsg = NULL; } MrChat::~MrChat() { - free(m_name); - free(m_last_msg); + Empty(); } +void MrChat::Empty() +{ + if( m_name ) { + free(m_name); + m_name = NULL; + } + + if( m_lastMsg ) { + delete m_lastMsg; + m_lastMsg = NULL; + } +} + + +bool MrChat::SetFromStmt(sqlite3_stmt* row) +{ + Empty(); + + m_id = sqlite3_column_int (row, 0); // the columns are defined in MR_GET_CHATS_PREFIX + m_type = (MrChatType) sqlite3_column_int (row, 1); + m_name = save_strdup((char*)sqlite3_column_text (row, 2)); + m_lastMsg = new MrMsg(m_mailbox, + (time_t)sqlite3_column_int64(row, 3), + (MrMsgType)sqlite3_column_int (row, 4), + (char*)sqlite3_column_text (row, 5)); + + if( m_name == NULL || m_lastMsg == NULL || m_lastMsg->m_msg == NULL ) { + return false; + } + + return true; +} + + +bool MrChat::LoadFromDb(const char* name, uint32_t id) +{ + bool success = false; + char* q = NULL; + sqlite3_stmt* stmt = NULL; + + Empty(); + + if( name ) { + q = sqlite3_mprintf(MR_GET_CHATS_PREFIX " WHERE name=%Q;", name); + } + else { + q = sqlite3_mprintf(MR_GET_CHATS_PREFIX " WHERE id=%i;", id); + } + + stmt = m_mailbox->m_sql.sqlite3_prepare_v2_(q); + + if( sqlite3_step(stmt) != SQLITE_ROW ) { + goto LoadFromDb_Cleanup; + } + + if( !SetFromStmt(stmt) ) { + goto LoadFromDb_Cleanup; + } + + // success + success = true; + + // cleanup +LoadFromDb_Cleanup: + if( q ) { + sqlite3_free(q); + } + + if( stmt ) { + sqlite3_finalize(stmt); + } + + return success; +} + + +/******************************************************************************* + * Static funcions + ******************************************************************************/ + + +size_t MrChat::GetChatCnt(MrMailbox* mailbox) // static function +{ + if( mailbox->m_sql.m_cobj==NULL ) { + return 0; // no database, no chats - this is no error (needed eg. for information) + } + + sqlite3_stmt* s = mailbox->m_sql.m_pd[SELECT_COUNT_FROM_chats]; + sqlite3_reset (s); + if( sqlite3_step(s) != SQLITE_ROW ) { + MrLogSqliteError(mailbox->m_sql.m_cobj); + MrLogError("MrSqlite3::GetChatCnt() failed."); + return 0; // error + } + + return sqlite3_column_int(s, 0); // success +} + + +uint32_t MrChat::ChatExists(MrMailbox* mailbox, MrChatType type, uint32_t contact_id) // static function +{ + uint32_t chat_id = 0; + + if( type == MR_CHAT_NORMAL ) + { + char* q=sqlite3_mprintf("SELECT id FROM chats INNER JOIN chats_contacts ON id=chat_id WHERE type=%i AND contact_id=%i", type, contact_id); + + sqlite3_stmt* stmt = mailbox->m_sql.sqlite3_prepare_v2_(q); + if( stmt ) { + int r = sqlite3_step(stmt); + if( r == SQLITE_ROW ) { + chat_id = sqlite3_column_int(stmt, 0); + } + sqlite3_finalize(stmt); + } + else { + MrLogSqliteError(mailbox->m_sql.m_cobj); + MrLogError("MrSqlite3::ChatExists() failed."); + } + + sqlite3_free(q); + } + + return chat_id; +} + + +uint32_t MrChat::CreateChatRecord(MrMailbox* mailbox, uint32_t contact_id) // static function +{ + uint32_t chat_id = 0; + MrContact* contact = NULL; + char* chat_name, *q = NULL; + sqlite3_stmt* stmt = NULL; + + if( (chat_id=ChatExists(mailbox, MR_CHAT_NORMAL, contact_id)) != 0 ) { + return chat_id; // soon success + } + + // get fine chat name + contact = new MrContact(mailbox); + if( !contact->LoadFromDb(contact_id) ) { + goto CreateNormalChat_Cleanup; + } + + chat_name = (contact->m_name&&contact->m_name[0])? contact->m_name : contact->m_email; + + // create chat record + q = sqlite3_mprintf("INSERT INTO chats (type, name) VALUES(%i, %Q)", MR_CHAT_NORMAL, chat_name); + stmt = mailbox->m_sql.sqlite3_prepare_v2_(q); + if( stmt == NULL) { + goto CreateNormalChat_Cleanup; + } + + if( sqlite3_step(stmt) != SQLITE_DONE ) { + goto CreateNormalChat_Cleanup; + } + + chat_id = sqlite3_last_insert_rowid(mailbox->m_sql.m_cobj); + + sqlite3_free(q); + q = NULL; + sqlite3_finalize(stmt); + stmt = NULL; + + // add contact IDs to the new chat record + q = sqlite3_mprintf("INSERT INTO chats_contacts (chat_id, contact_id) VALUES(%i, %i)", chat_id, contact_id); + stmt = mailbox->m_sql.sqlite3_prepare_v2_(q); + + if( sqlite3_step(stmt) != SQLITE_DONE ) { + goto CreateNormalChat_Cleanup; + } + + // add already existing messages to the chat record + + sqlite3_free(q); + q = NULL; + sqlite3_finalize(stmt); + stmt = NULL; + + q = sqlite3_mprintf("UPDATE msg SET chat_id=%i WHERE chat_id=0 AND from_id=%i;", chat_id, contact_id); + stmt = mailbox->m_sql.sqlite3_prepare_v2_(q); + + if( sqlite3_step(stmt) != SQLITE_DONE ) { + goto CreateNormalChat_Cleanup; + } + + // cleanup +CreateNormalChat_Cleanup: + if( q ) { + sqlite3_free(q); + } + + if( stmt ) { + sqlite3_finalize(stmt); + } + + if( contact ) { + delete contact; + } + return chat_id; +} + + +uint32_t MrChat::FindOutChatId(MrMailbox* mailbox, carray* contact_ids_from, carray* contact_ids_to) // static function +{ + if( carray_count(contact_ids_from)==1 ) { + return ChatExists(mailbox, MR_CHAT_NORMAL, (uint32_t)(uintptr_t)carray_get(contact_ids_from, 0)); + } + + return 0; +} + + +/******************************************************************************* + * Send Messages + ******************************************************************************/ + + void MrChat::SendMsg(const char* text) { } @@ -59,13 +277,22 @@ void MrChat::SendMsg(const char* text) ******************************************************************************/ -MrChatList::MrChatList() +MrChatList::MrChatList(MrMailbox* mailbox) { + m_mailbox = mailbox; m_chats = carray_new(128); } MrChatList::~MrChatList() +{ + Empty(); + carray_free(m_chats); + m_chats = NULL; +} + + +void MrChatList::Empty() { if( m_chats ) { @@ -78,8 +305,44 @@ MrChatList::~MrChatList() delete chat; } } - - carray_free(m_chats); - m_chats = NULL; } } + + +bool MrChatList::LoadFromDb() +{ + bool success = false; + char* q = NULL; + sqlite3_stmt* stmt = NULL; + + Empty(); + + // select example with left join and minimum: http://stackoverflow.com/questions/7588142/mysql-left-join-min + q = sqlite3_mprintf(MR_GET_CHATS_PREFIX " ORDER BY timestamp;"); + stmt = m_mailbox->m_sql.sqlite3_prepare_v2_(q); + if( stmt==NULL ) { + goto GetChatList_Cleanup; + } + + while( sqlite3_step(stmt) == SQLITE_ROW ) { + MrChat* chat = new MrChat(m_mailbox); + if( chat->SetFromStmt(stmt) ) { + carray_add(m_chats, (void*)chat, NULL); + } + } + + // success + success = true; + + // cleanup +GetChatList_Cleanup: + if( q ) { + sqlite3_free(q); + } + + if( stmt ) { + sqlite3_finalize(stmt); + } + + return success; +} diff --git a/src/mrchat.h b/src/mrchat.h index e31f7bcc..8642fdfb 100644 --- a/src/mrchat.h +++ b/src/mrchat.h @@ -31,7 +31,7 @@ #define __MRCHAT_H__ -#include "mrmsg.h" +class MrMsg; class MrMailbox; @@ -47,33 +47,49 @@ enum MrChatType class MrChat { public: - MrChat (MrMailbox*); - ~MrChat (); + MrChat (MrMailbox*); + ~MrChat (); + bool LoadFromDb (const char* name, uint32_t id); - // the data should be read only and are valid until the object is Release()'d. + static size_t GetChatCnt (MrMailbox*); + static uint32_t ChatExists (MrMailbox*, MrChatType, uint32_t contact_id); // returns chat_id or 0 + static uint32_t CreateChatRecord (MrMailbox*, uint32_t contact_id); + static uint32_t FindOutChatId (MrMailbox*, carray* contact_ids_from, carray* contact_ids_to); + + // the data should be read only and are valid until the object is delete'd. // unset strings are set to NULL. - int m_id; - MrChatType m_type; - char* m_name; - time_t m_last_timestamp; - MrMsgType m_last_msg_type; - char* m_last_msg; + int m_id; + MrChatType m_type; + char* m_name; + MrMsg* m_lastMsg; // send a message - void SendMsg (const char* text); + void SendMsg (const char* text); private: // the mailbox, the chat belongs to - MrMailbox* m_mailbox; + #define MR_GET_CHATS_PREFIX "SELECT c.id, c.type, c.name, m.timestamp, m.type, m.msg FROM chats c LEFT JOIN msg m ON (c.id=m.chat_id AND m.timestamp=(SELECT MIN(timestamp) FROM msg WHERE chat_id=c.id)) " + bool SetFromStmt (sqlite3_stmt* row); + void Empty (); + MrMailbox* m_mailbox; + + friend class MrChatList; }; class MrChatList { public: - MrChatList (); - ~MrChatList (); - carray* m_chats; // contains MrChat objects + MrChatList (MrMailbox*); + ~MrChatList (); + bool LoadFromDb (); + + // data + carray* m_chats; // contains MrChat objects + +private: + MrMailbox* m_mailbox; + void Empty (); }; diff --git a/src/mrcontact.cpp b/src/mrcontact.cpp index 564db603..ef728ae2 100644 --- a/src/mrcontact.cpp +++ b/src/mrcontact.cpp @@ -29,6 +29,7 @@ #include #include "mrmailbox.h" #include "mrcontact.h" +#include "mrtools.h" MrContact::MrContact(MrMailbox* mailbox) @@ -41,7 +42,86 @@ MrContact::MrContact(MrMailbox* mailbox) MrContact::~MrContact() { - free(m_name); - free(m_email); + Empty(); } + +void MrContact::Empty() +{ + if( m_name ) { + free(m_name); + m_name = NULL; + } + + if( m_email ) { + free(m_email); + m_email = NULL; + } +} + + +bool MrContact::LoadFromDb(uint32_t contact_id) +{ + bool success = false; + char* q; + sqlite3_stmt* stmt; + + Empty(); + + q=sqlite3_mprintf("SELECT id, name, email FROM contacts WHERE id=%i;", contact_id); + stmt = m_mailbox->m_sql.sqlite3_prepare_v2_(q); + if( stmt == NULL ) { + goto LoadFromDb_Cleanup; + } + + if( sqlite3_step(stmt) != SQLITE_ROW ) { + goto LoadFromDb_Cleanup; + } + + m_id = contact_id; + m_name = save_strdup((char*)sqlite3_column_text(stmt, 1)); + m_email = save_strdup((char*)sqlite3_column_text(stmt, 2)); + if( m_name == NULL || m_email == NULL ) { + goto LoadFromDb_Cleanup; // out of memory, should not happen + } + + // success + success = true; + + // cleanup +LoadFromDb_Cleanup: + if( q ) { + sqlite3_free(q); + } + + if( stmt ) { + sqlite3_finalize(stmt); + } + + return success; +} + + +/******************************************************************************* + * Static funcions + ******************************************************************************/ + + +size_t MrContact::GetContactCnt(MrMailbox* mailbox) // static function +{ + if( mailbox->m_sql.m_cobj==NULL ) { + return 0; // no database, no contacts - this is no error (needed eg. for information) + } + + sqlite3_stmt* s = mailbox->m_sql.m_pd[SELECT_COUNT_FROM_contacts]; + sqlite3_reset (s); + if( sqlite3_step(s) != SQLITE_ROW ) { + MrLogSqliteError(mailbox->m_sql.m_cobj); + MrLogError("MrSqlite3::GetContactCnt() failed."); + return 0; // error + } + + return sqlite3_column_int(s, 0); // success +} + + diff --git a/src/mrcontact.h b/src/mrcontact.h index a5d64df1..446c6444 100644 --- a/src/mrcontact.h +++ b/src/mrcontact.h @@ -40,18 +40,22 @@ class MrMailbox; class MrContact { public: - MrContact (MrMailbox*); - ~MrContact (); + MrContact (MrMailbox*); + ~MrContact (); + bool LoadFromDb (uint32_t id); + + static size_t GetContactCnt (MrMailbox*); // the data should be read only and are valid until the object is Release()'d. // unset strings are set to NULL. - uint32_t m_id; - char* m_name; // != NULL, however, may be empty - char* m_email; // != NULL + uint32_t m_id; + char* m_name; // != NULL, however, may be empty + char* m_email; // != NULL private: // the mailbox, the contact belongs to - MrMailbox* m_mailbox; + MrMailbox* m_mailbox; + void Empty (); }; diff --git a/src/mrimap.h b/src/mrimap.h index 452f9152..38f6ab72 100644 --- a/src/mrimap.h +++ b/src/mrimap.h @@ -30,7 +30,7 @@ #define __MRIMAP_H__ -#include "mrloginparam.h" +class MrLoginParam; class MrMailbox; diff --git a/src/mrimfparser.cpp b/src/mrimfparser.cpp index 37731927..524d88e7 100644 --- a/src/mrimfparser.cpp +++ b/src/mrimfparser.cpp @@ -319,12 +319,12 @@ int32_t MrImfParser::Imf2Msg(const char* imf_raw, size_t imf_len) // (of course, the user can add other chats manually) if( !comes_from_extern && carray_count(contact_ids_to)==1 ) { - chat_id = m_mailbox->m_sql.CreateChatRecord((uint32_t)(uintptr_t)carray_get(contact_ids_to, 0)); + chat_id = MrChat::CreateChatRecord(m_mailbox, (uint32_t)(uintptr_t)carray_get(contact_ids_to, 0)); } if( chat_id == 0 ) { - chat_id = m_mailbox->m_sql.FindOutChatId(contact_ids_from, contact_ids_to); + chat_id = MrChat::FindOutChatId(m_mailbox, contact_ids_from, contact_ids_to); } } @@ -341,7 +341,7 @@ int32_t MrImfParser::Imf2Msg(const char* imf_raw, size_t imf_len) } } - if( m_mailbox->m_sql.MessageIdExists(message_id) ) { + if( MrMsg::MessageIdExists(m_mailbox, message_id) ) { goto Imf2Msg_Done; // success - the message is already added to our database } diff --git a/src/mrmailbox.cpp b/src/mrmailbox.cpp index 00bf71c4..636e99e6 100644 --- a/src/mrmailbox.cpp +++ b/src/mrmailbox.cpp @@ -31,6 +31,8 @@ #include #include "mrmailbox.h" #include "mrimfparser.h" +#include "mrcontact.h" +#include "mrmsg.h" /******************************************************************************* @@ -163,11 +165,25 @@ void MrMailbox::ReceiveImf(const char* imf, size_t imf_len) ******************************************************************************/ +size_t MrMailbox::GetChatCnt() +{ + MrSqlite3Locker l(m_sql); + return MrChat::GetChatCnt(this); +} + + MrChatList* MrMailbox::GetChats() { MrSqlite3Locker locker(m_sql); - return m_sql.GetChatList(); + MrChatList* obj = new MrChatList(this); + if( obj->LoadFromDb() ) { + return obj; + } + else { + delete obj; + return NULL; + } } @@ -175,7 +191,14 @@ MrChat* MrMailbox::GetChat(const char* name) { MrSqlite3Locker locker(m_sql); - return m_sql.GetSingleChat(name, 0); + MrChat* obj = new MrChat(this); + if( obj->LoadFromDb(name, 0) ) { + return obj; + } + else { + delete obj; + return NULL; + } } @@ -183,7 +206,14 @@ MrChat* MrMailbox::GetChat(uint32_t id) { MrSqlite3Locker locker(m_sql); - return m_sql.GetSingleChat(NULL, id); + MrChat* obj = new MrChat(this); + if( obj->LoadFromDb(NULL, id) ) { + return obj; + } + else { + delete obj; + return NULL; + } } @@ -233,9 +263,9 @@ char* MrMailbox::GetInfo() debug_dir = m_sql.GetConfig("debug_dir", NULL); - contacts = m_sql.GetContactCnt(); - chats = m_sql.GetChatCnt(); - messages = m_sql.GetMsgCnt(); + contacts = MrContact::GetContactCnt(this); + chats = MrChat::GetChatCnt(this); + messages = MrMsg::GetMsgCnt(this); } // create info diff --git a/src/mrmailbox.h b/src/mrmailbox.h index 2bf8165d..fb7c293f 100644 --- a/src/mrmailbox.h +++ b/src/mrmailbox.h @@ -36,10 +36,14 @@ #include // defines uint16_t etc. #include "mrsqlite3.h" -#include "mrchat.h" -#include "mrcontact.h" #include "mrimap.h" #include "mrerror.h" +#include "mrloginparam.h" + + +class MrChat; +class MrChatList; +class MrContact; #define MR_VERSION_MAJOR 0 @@ -72,7 +76,7 @@ public: MrContact* GetContact (size_t i); // iterate chats - size_t GetChatCnt () { MrSqlite3Locker l(m_sql); return m_sql.GetChatCnt(); } + size_t GetChatCnt (); MrChatList* GetChats (); // the result must be delete'd MrChat* GetChat (const char* name); // the result must be delete'd MrChat* GetChat (uint32_t id); // the result must be delete'd @@ -86,9 +90,11 @@ public: char* GetDbFile (); // the returned string must be free()'d, returns NULL on errors or if no database is open char* GetInfo (); // multi-line output; the returned string must be free()'d, returns NULL on errors + // data, should be treated as read-only + MrSqlite3 m_sql; + private: // private stuff - MrSqlite3 m_sql; MrLoginParam m_loginParam; MrImap m_imap; @@ -97,7 +103,6 @@ private: void ReceiveImf (const char* imf, size_t imf_len); friend class MrImap; - friend class MrImfParser; }; diff --git a/src/mrmsg.cpp b/src/mrmsg.cpp index eff7cf68..f4795ffa 100644 --- a/src/mrmsg.cpp +++ b/src/mrmsg.cpp @@ -29,20 +29,77 @@ #include #include "mrmailbox.h" #include "mrmsg.h" +#include "mrtools.h" MrMsg::MrMsg(MrMailbox* mailbox) { - m_mailbox = mailbox; - m_type = MR_MSG_UNDEFINED; - m_msg = NULL; - m_time = 0; + m_mailbox = mailbox; + m_timestamp = 0; + m_type = MR_MSG_UNDEFINED; + m_msg = NULL; +} + + +MrMsg::MrMsg(MrMailbox* mailbox, time_t timestamp, MrMsgType type, char* msg) +{ + m_mailbox = mailbox; + m_timestamp = timestamp; + m_type = type; + m_msg = save_strdup(msg); +} + + +void MrMsg::Empty() +{ + if( m_msg ) { + free(m_msg); + m_msg = NULL; + } } MrMsg::~MrMsg() { - free(m_msg); + Empty(); +} + + +/******************************************************************************* + * Static functions + ******************************************************************************/ + + +size_t MrMsg::GetMsgCnt(MrMailbox* mailbox) // static function +{ + if( mailbox->m_sql.m_cobj==NULL ) { + return 0; // no database, no messages - this is no error (needed eg. for information) + } + + sqlite3_stmt* s = mailbox->m_sql.m_pd[SELECT_COUNT_FROM_msg]; + sqlite3_reset (s); + if( sqlite3_step(s) != SQLITE_ROW ) { + MrLogSqliteError(mailbox->m_sql.m_cobj); + MrLogError("MrSqlite3::GetMsgCnt() failed."); + return 0; // error + } + + return sqlite3_column_int(s, 0); // success +} + + +bool MrMsg::MessageIdExists(MrMailbox* mailbox, const char* message_id) // static function +{ + // check, if the given Message-ID exists in the database (if not, the message is normally downloaded from the server and parsed, + // so, we should even keep unuseful messages in the database (we can leave the other fields empty to safe space) + sqlite3_stmt* s = mailbox->m_sql.m_pd[SELECT_id_FROM_msg_m]; + sqlite3_reset (s); + sqlite3_bind_text(s, 1, message_id, -1, SQLITE_STATIC); + if( sqlite3_step(s) != SQLITE_ROW ) { + return false; // record does not exist + } + + return true; // record does exist } diff --git a/src/mrmsg.h b/src/mrmsg.h index 8c8f5c4b..f9dd16de 100644 --- a/src/mrmsg.h +++ b/src/mrmsg.h @@ -51,18 +51,23 @@ enum MrMsgType class MrMsg { public: - MrMsg (MrMailbox*); - ~MrMsg (); + MrMsg (MrMailbox*); + MrMsg (MrMailbox*, time_t timestamp, MrMsgType, char*); + ~MrMsg (); + + static size_t GetMsgCnt (MrMailbox*); + static bool MessageIdExists(MrMailbox*, const char* message_id); // the data should be read only and are valid until the object is Release()'d. // unset strings are set to NULL. + time_t m_timestamp; // unix time the message was sended MrMsgType m_type; char* m_msg; // meaning dedpends on m_type - time_t m_time; // unix time the message was sended private: // the mailbox, the message belongs to MrMailbox* m_mailbox; + void Empty (); }; diff --git a/src/mrsqlite3.cpp b/src/mrsqlite3.cpp index 2d5874dd..d8f1fe50 100644 --- a/src/mrsqlite3.cpp +++ b/src/mrsqlite3.cpp @@ -31,6 +31,7 @@ #include "mrsqlite3.h" #include "mrerror.h" #include "mrtools.h" +#include "mrcontact.h" MrSqlite3::MrSqlite3(MrMailbox* mailbox) @@ -148,12 +149,6 @@ Open_Error: void MrSqlite3::Close() { - #define SQLITE3_FINALIZE_(a) \ - if((a)) { \ - sqlite3_finalize((a)); \ - (a) = NULL; \ - } - if( m_cobj ) { for( int i = 0; i < PREDEFINED_CNT; i++ ) { @@ -375,359 +370,3 @@ bool MrSqlite3::SetConfigInt(const char* key, int32_t value) return ret; } - -/******************************************************************************* - * Handle contacts - ******************************************************************************/ - - -size_t MrSqlite3::GetContactCnt() -{ - if( m_cobj==NULL ) { - return 0; // no database, no contacts - this is no error (needed eg. for information) - } - - sqlite3_reset (m_pd[SELECT_COUNT_FROM_contacts]); - if( sqlite3_step(m_pd[SELECT_COUNT_FROM_contacts]) != SQLITE_ROW ) { - MrLogSqliteError(m_cobj); - MrLogError("MrSqlite3::GetContactCnt() failed."); - return 0; // error - } - - return sqlite3_column_int(m_pd[SELECT_COUNT_FROM_contacts], 0); // success -} - - -MrContact* MrSqlite3::GetContact(uint32_t contact_id) -{ - bool success = false; - MrContact* contact = NULL; - char* q; - sqlite3_stmt* stmt; - - contact = new MrContact(m_mailbox); - if( contact == NULL ) { - goto GetContact_Cleanup; - } - - q=sqlite3_mprintf("SELECT id, name, email FROM contacts WHERE id=%i;", contact_id); - stmt = sqlite3_prepare_v2_(q); - if( stmt == NULL ) { - goto GetContact_Cleanup; - } - - if( sqlite3_step(stmt) != SQLITE_ROW ) { - goto GetContact_Cleanup; - } - - contact->m_id = contact_id; - contact->m_name = save_strdup((char*)sqlite3_column_text(stmt, 1)); - contact->m_email = save_strdup((char*)sqlite3_column_text(stmt, 2)); - if( contact->m_name == NULL || contact->m_email == NULL ) { - goto GetContact_Cleanup; // out of memory, should not happen - } - - // success - success = true; - - // cleanup -GetContact_Cleanup: - if( q ) { - sqlite3_free(q); - } - - if( stmt ) { - sqlite3_finalize(stmt); - } - - if( success ) { - return contact; - } - else { - delete contact; - return NULL; - } - -} - - -/******************************************************************************* - * Handle chats - ******************************************************************************/ - - -size_t MrSqlite3::GetChatCnt() -{ - if( m_cobj==NULL ) { - return 0; // no database, no chats - this is no error (needed eg. for information) - } - - sqlite3_reset (m_pd[SELECT_COUNT_FROM_chats]); - if( sqlite3_step(m_pd[SELECT_COUNT_FROM_chats]) != SQLITE_ROW ) { - MrLogSqliteError(m_cobj); - MrLogError("MrSqlite3::GetChatCnt() failed."); - return 0; // error - } - - return sqlite3_column_int(m_pd[SELECT_COUNT_FROM_chats], 0); // success -} - - -uint32_t MrSqlite3::ChatExists(MrChatType type, uint32_t contact_id) -{ - uint32_t chat_id = 0; - - if( type == MR_CHAT_NORMAL ) - { - char* q=sqlite3_mprintf("SELECT id FROM chats INNER JOIN chats_contacts ON id=chat_id WHERE type=%i AND contact_id=%i", type, contact_id); - - sqlite3_stmt* stmt = sqlite3_prepare_v2_(q); - if( stmt ) { - int r = sqlite3_step(stmt); - if( r == SQLITE_ROW ) { - chat_id = sqlite3_column_int(stmt, 0); - } - sqlite3_finalize(stmt); - } - else { - MrLogSqliteError(m_cobj); - MrLogError("MrSqlite3::ChatExists() failed."); - } - - sqlite3_free(q); - } - - return chat_id; -} - - -uint32_t MrSqlite3::CreateChatRecord(uint32_t contact_id) -{ - uint32_t chat_id = 0; - MrContact* contact = NULL; - char* chat_name, *q = NULL; - sqlite3_stmt* stmt = NULL; - - if( (chat_id=ChatExists(MR_CHAT_NORMAL, contact_id)) != 0 ) { - return chat_id; // soon success - } - - // get fine chat name - contact = GetContact(contact_id); - if( contact == NULL ) { - goto CreateNormalChat_Cleanup; - } - - chat_name = (contact->m_name&&contact->m_name[0])? contact->m_name : contact->m_email; - - // create chat record - q = sqlite3_mprintf("INSERT INTO chats (type, name) VALUES(%i, %Q)", MR_CHAT_NORMAL, chat_name); - stmt = sqlite3_prepare_v2_(q); - if( stmt == NULL) { - goto CreateNormalChat_Cleanup; - } - - if( sqlite3_step(stmt) != SQLITE_DONE ) { - goto CreateNormalChat_Cleanup; - } - - chat_id = sqlite3_last_insert_rowid(m_cobj); - - sqlite3_free(q); - q = NULL; - sqlite3_finalize(stmt); - stmt = NULL; - - // add contact IDs to the new chat record - q = sqlite3_mprintf("INSERT INTO chats_contacts (chat_id, contact_id) VALUES(%i, %i)", chat_id, contact_id); - stmt = sqlite3_prepare_v2_(q); - - if( sqlite3_step(stmt) != SQLITE_DONE ) { - goto CreateNormalChat_Cleanup; - } - - // add already existing messages to the chat record - - sqlite3_free(q); - q = NULL; - sqlite3_finalize(stmt); - stmt = NULL; - - q = sqlite3_mprintf("UPDATE msg SET chat_id=%i WHERE chat_id=0 AND from_id=%i;", chat_id, contact_id); - stmt = sqlite3_prepare_v2_(q); - - if( sqlite3_step(stmt) != SQLITE_DONE ) { - goto CreateNormalChat_Cleanup; - } - - // cleanup -CreateNormalChat_Cleanup: - if( q ) { - sqlite3_free(q); - } - - if( stmt ) { - sqlite3_finalize(stmt); - } - - if( contact ) { - delete contact; - } - return chat_id; -} - - -uint32_t MrSqlite3::FindOutChatId(carray* contact_ids_from, carray* contact_ids_to) -{ - if( carray_count(contact_ids_from)==1 ) { - return ChatExists(MR_CHAT_NORMAL, (uint32_t)(uintptr_t)carray_get(contact_ids_from, 0)); - } - - return 0; -} - - -MrChatList* MrSqlite3::GetChatList() -{ - #define GET_CHATS_PREFIX "SELECT c.id, c.type, c.name, m.timestamp, m.type, m.msg FROM chats c LEFT JOIN msg m ON (c.id=m.chat_id AND m.timestamp=(SELECT MIN(timestamp) FROM msg WHERE chat_id=c.id)) " - MrChatList* chatlist = NULL; - MrChat* chat; - bool success = false; - char* q = NULL; - sqlite3_stmt* stmt = NULL; - - if( (chatlist=new MrChatList()) == NULL ) { - goto GetChatList_Cleanup; - } - - // select example with left join and minimum: http://stackoverflow.com/questions/7588142/mysql-left-join-min - q = sqlite3_mprintf(GET_CHATS_PREFIX " ORDER BY timestamp;"); - stmt = sqlite3_prepare_v2_(q); - - while( sqlite3_step(stmt) == SQLITE_ROW ) { - chat = new MrChat(m_mailbox); - chat->m_id = sqlite3_column_int (stmt, 0); - chat->m_type = (MrChatType) sqlite3_column_int (stmt, 1); - chat->m_name = save_strdup((char*)sqlite3_column_text (stmt, 2)); - chat->m_last_timestamp = sqlite3_column_int64(stmt, 3); - chat->m_last_msg_type = (MrMsgType) sqlite3_column_int (stmt, 4); - chat->m_last_msg = save_strdup((char*)sqlite3_column_text (stmt, 5)); - if( chat->m_name && chat->m_last_msg ) { - carray_add(chatlist->m_chats, (void*)chat, NULL); - } - } - - // success - success = true; - - // cleanup -GetChatList_Cleanup: - if( q ) { - sqlite3_free(q); - } - - if( stmt ) { - sqlite3_finalize(stmt); - } - - if( success ) { - return chatlist; - } - else { - delete chatlist; - return NULL; - } -} - - -MrChat* MrSqlite3::GetSingleChat(const char* name, uint32_t id) -{ - MrChat* chat = NULL; - bool success = false; - char* q = NULL; - sqlite3_stmt* stmt = NULL; - - if( (chat=new MrChat(m_mailbox)) == NULL ) { - goto GetSingleChat_Cleanup; - } - - if( name ) { - q = sqlite3_mprintf(GET_CHATS_PREFIX " WHERE name=%Q;", name); - } - else { - q = sqlite3_mprintf(GET_CHATS_PREFIX " WHERE id=%i;", id); - } - - stmt = sqlite3_prepare_v2_(q); - - if( sqlite3_step(stmt) != SQLITE_ROW ) { - goto GetSingleChat_Cleanup; - } - - chat->m_id = sqlite3_column_int (stmt, 0); - chat->m_type = (MrChatType) sqlite3_column_int (stmt, 1); - chat->m_name = save_strdup((char*)sqlite3_column_text (stmt, 2)); - chat->m_last_timestamp = sqlite3_column_int64(stmt, 3); - chat->m_last_msg_type = (MrMsgType) sqlite3_column_int (stmt, 4); - chat->m_last_msg = save_strdup((char*)sqlite3_column_text (stmt, 5)); - if( chat->m_name==NULL || chat->m_last_msg==NULL ) { - goto GetSingleChat_Cleanup; - } - - // success - success = true; - - // cleanup -GetSingleChat_Cleanup: - if( q ) { - sqlite3_free(q); - } - - if( stmt ) { - sqlite3_finalize(stmt); - } - - if( success ) { - return chat; - } - else { - delete chat; - return NULL; - } -} - - -/******************************************************************************* - * Handle messages - ******************************************************************************/ - - -size_t MrSqlite3::GetMsgCnt() -{ - if( m_cobj==NULL ) { - return 0; // no database, no messages - this is no error (needed eg. for information) - } - - sqlite3_reset (m_pd[SELECT_COUNT_FROM_msg]); - if( sqlite3_step(m_pd[SELECT_COUNT_FROM_msg]) != SQLITE_ROW ) { - MrLogSqliteError(m_cobj); - MrLogError("MrSqlite3::GetMsgCnt() failed."); - return 0; // error - } - - return sqlite3_column_int(m_pd[SELECT_COUNT_FROM_msg], 0); // success -} - - -bool MrSqlite3::MessageIdExists(const char* message_id) -{ - // check, if the given Message-ID exists in the database (if not, the message is normally downloaded from the server and parsed, - // so, we should even keep unuseful messages in the database (we can leave the other fields empty to safe space) - sqlite3_reset (m_pd[SELECT_id_FROM_msg_m]); - sqlite3_bind_text(m_pd[SELECT_id_FROM_msg_m], 1, message_id, -1, SQLITE_STATIC); - if( sqlite3_step(m_pd[SELECT_id_FROM_msg_m]) != SQLITE_ROW ) { - return false; // record does not exist - } - - return true; // record does exist -} diff --git a/src/mrsqlite3.h b/src/mrsqlite3.h index 404a9b0a..8e604848 100644 --- a/src/mrsqlite3.h +++ b/src/mrsqlite3.h @@ -34,10 +34,10 @@ #include #include #include "mrchat.h" -#include "mrcontact.h" class MrMailbox; +class MrContact; // predefined statements @@ -83,14 +83,6 @@ public: size_t GetContactCnt (); MrContact* GetContact (uint32_t contact_id); - // handle chats - size_t GetChatCnt (); - uint32_t ChatExists (MrChatType, uint32_t contact_id); // returns chat_id or 0 - uint32_t CreateChatRecord (uint32_t contact_id); - uint32_t FindOutChatId (carray* contact_ids_from, carray* contact_ids_to); - MrChatList* GetChatList (); - MrChat* GetSingleChat (const char* name, uint32_t id); - // handle messages size_t GetMsgCnt (); // total number of messages, just for statistics, normally not needed for the program flow bool MessageIdExists (const char* message_id); // check existance of a Message-ID