1
0
Fork 0
mirror of https://github.com/deltachat/deltachat-core.git synced 2025-10-05 19:42:04 +02:00
This commit is contained in:
B. Petersen 2016-07-26 23:54:00 +02:00
parent 2c77b608ee
commit 09f95f5b5c
12 changed files with 517 additions and 426 deletions

View file

@ -29,6 +29,9 @@
#include <stdlib.h>
#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;
}

View file

@ -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 ();
};

View file

@ -29,6 +29,7 @@
#include <stdlib.h>
#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
}

View file

@ -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 ();
};

View file

@ -30,7 +30,7 @@
#define __MRIMAP_H__
#include "mrloginparam.h"
class MrLoginParam;
class MrMailbox;

View file

@ -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
}

View file

@ -31,6 +31,8 @@
#include <sqlite3.h>
#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

View file

@ -36,10 +36,14 @@
#include <libetpan/libetpan.h> // 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;
};

View file

@ -29,20 +29,77 @@
#include <stdlib.h>
#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
}

View file

@ -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 ();
};

View file

@ -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
}

View file

@ -34,10 +34,10 @@
#include <libetpan.h>
#include <pthread.h>
#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