#include "SMSRecord.hpp" #include "Common/Query.hpp" #include "ContactRecord.hpp" #include "ThreadRecord.hpp" #include "queries/messages/sms/QuerySMSAdd.hpp" #include "queries/messages/sms/QuerySMSGet.hpp" #include "queries/messages/sms/QuerySMSGetByContactID.hpp" #include "queries/messages/sms/QuerySMSGetByID.hpp" #include "queries/messages/sms/QuerySMSGetByText.hpp" #include "queries/messages/sms/QuerySMSGetCount.hpp" #include "queries/messages/sms/QuerySMSRemove.hpp" #include "queries/messages/sms/QuerySMSUpdate.hpp" #include "queries/messages/sms/QuerySMSSearchByType.hpp" #include "queries/messages/sms/QuerySMSGetByThreadID.hpp" #include "queries/messages/sms/QuerySMSGetCountByThreadID.hpp" #include "queries/messages/sms/QuerySMSGetLastByThreadID.hpp" #include #include #include SMSRecord::SMSRecord(const SMSTableRow &w, const utils::PhoneNumber::View &num) : date(w.date), dateSent(w.dateSent), errorCode(w.errorCode), body(w.body), type(w.type), threadID(w.threadID), contactID(w.contactID), number(num) { this->ID = w.ID; } SMSRecordInterface::SMSRecordInterface(SmsDB *smsDb, ContactsDB *contactsDb) : smsDB(smsDb), contactsDB(contactsDb) {} bool SMSRecordInterface::Add(const SMSRecord &rec) { ContactRecordInterface contactInterface(contactsDB); auto contactMatch = contactInterface.MatchByNumber(rec.number, ContactRecordInterface::CreateTempContact::True); if (!contactMatch.has_value()) { LOG_ERROR("Cannot find contact, for number %s", rec.number.getFormatted().c_str()); return false; } auto contactID = contactMatch->contactId; auto numberID = contactMatch->numberId; // Search for a thread with specified contactID ThreadRecordInterface threadInterface(smsDB, contactsDB); auto threadRec = threadInterface.GetLimitOffsetByField(0, 1, ThreadRecordField::NumberID, std::to_string(numberID).c_str()); // Thread not found, create one if (threadRec->size() == 0) { ThreadRecord re; re.contactID = contactID; re.numberID = numberID; if (!threadInterface.Add(re)) { LOG_ERROR("Cannot create new thread"); return false; } threadRec = threadInterface.GetLimitOffsetByField(0, 1, ThreadRecordField::NumberID, std::to_string(numberID).c_str()); if (threadRec->size() == 0) { LOG_ERROR("Thread not found"); return false; } } auto thread = (*threadRec)[0]; // Create SMS if (!smsDB->sms.add(SMSTableRow{{.ID = DB_ID_NONE}, // the entry is not yet in the DB .threadID = thread.ID, .contactID = contactID, .date = rec.date, .dateSent = rec.dateSent, .errorCode = rec.errorCode, .body = rec.body, .type = rec.type })) { LOG_ERROR("Cannot add sms"); return false; } // TODO: error check // Update thread UpdateThreadSummary(thread, rec); thread.msgCount++; if (rec.type == SMSType::INBOX) { thread.unreadMsgCount++; LOG_DEBUG("unreadMsgCount = %" PRIu32 " for thread = %" PRIu32, thread.unreadMsgCount, thread.ID); } if (!threadInterface.Update(thread)) { LOG_ERROR("Cannot update thread"); return false; } return true; } uint32_t SMSRecordInterface::GetCount() { return smsDB->sms.count(); } uint32_t SMSRecordInterface::GetLastID(void) { return smsDB->getLastInsertRowId(); } std::unique_ptr> SMSRecordInterface::GetLimitOffsetByField(uint32_t offset, uint32_t limit, SMSRecordField field, const char *str) { auto records = std::make_unique>(); std::vector smses; switch (field) { case SMSRecordField ::ContactID: smses = smsDB->sms.getLimitOffsetByField(offset, limit, SMSTableFields::ContactID, str); break; case SMSRecordField ::ThreadID: smses = smsDB->sms.getLimitOffsetByField(offset, limit, SMSTableFields::ThreadID, str); break; default: LOG_ERROR("SMS thread get - wrong selection: %d", static_cast(field)); return records; } LOG_INFO("Get: %u SMS by selection: %d", static_cast(smses.size()), static_cast(field)); ContactRecordInterface contactInterface(contactsDB); for (const auto &w : smses) { auto contactRec = contactInterface.GetByIdWithTemporary(w.contactID); if (contactRec.numbers.size() != 0) { // TODO: or numberUser? or other number? records->push_back({w, contactRec.numbers[0].number}); } } return records; } std::unique_ptr> SMSRecordInterface::GetLimitOffset(uint32_t offset, uint32_t limit) { auto smses = smsDB->sms.getLimitOffset(offset, limit); auto records = std::make_unique>(); ContactRecordInterface contactInterface(contactsDB); for (const auto &w : smses) { auto contactRec = contactInterface.GetByIdWithTemporary(w.contactID); if (contactRec.numbers.size() != 0) { // TODO: or numberUser? or other number records->push_back({w, contactRec.numbers[0].number}); } } return records; } bool SMSRecordInterface::Update(const SMSRecord &recUpdated) { auto recCurrent = GetByID(recUpdated.ID); if (!recCurrent.isValid()) { // if updating non-existent entry return false; } smsDB->sms.update(SMSTableRow{{.ID = recCurrent.ID}, .threadID = recCurrent.threadID, .contactID = recCurrent.contactID, .date = recUpdated.date, .dateSent = recUpdated.dateSent, .errorCode = recUpdated.errorCode, .body = recUpdated.body, .type = recUpdated.type}); // Update messages read count if necessary ThreadRecordInterface threadInterface(smsDB, contactsDB); auto thread = threadInterface.GetByID(recCurrent.threadID); // update thread details with the latest sms from given thread auto latest_vec = GetLimitOffsetByField(0, 1, SMSRecordField ::ThreadID, std::to_string(thread.ID).c_str()); if (latest_vec->size() == 0) { return false; } auto recLatestInThread = (*latest_vec)[0]; // is updated sms is the latest we need to update the summary if (recUpdated.ID == recLatestInThread.ID) { // latest is visible, so update thread details UpdateThreadSummary(thread, recLatestInThread); threadInterface.Update(thread); } return true; } void SMSRecordInterface::UpdateThreadSummary(ThreadRecord &threadToUpdate, const SMSRecord &rec) { threadToUpdate.snippet = rec.body.substr(0, rec.body.length() >= snippetLength ? snippetLength : rec.body.length()); threadToUpdate.date = rec.date; threadToUpdate.type = rec.type; } bool SMSRecordInterface::RemoveByID(uint32_t id) { auto sms = smsDB->sms.getById(id); if (!sms.isValid()) { return false; } ThreadRecordInterface threadInterface(smsDB, contactsDB); auto threadRec = threadInterface.GetByID(sms.threadID); // If thread not found if (!threadRec.isValid()) { return smsDB->sms.removeById(id); } // If thread contains only one message remove it if (threadRec.msgCount <= 1) { threadInterface.RemoveByID(sms.threadID); } else { // update thread details if deleted SMS is the latest sms from the thread auto twoLatest = GetLimitOffsetByField(0, 2, SMSRecordField ::ThreadID, std::to_string(threadRec.ID).c_str()); if (twoLatest->size() == 0) { LOG_ERROR("Cannot fetch no SMSes even though thread's msgCount says so (id %lu)", static_cast(sms.threadID)); threadInterface.RemoveByID(sms.threadID); return false; // not handled (do no change window) } else { // check if there is need to change thread summary if (sms.ID == (*twoLatest)[0].ID) { // there is the need if (twoLatest->size() < 2) { LOG_ERROR("Cannot fetch another sms from a thread even though thread's msgCount says so (id %lu)", static_cast(sms.threadID)); smsDB->sms.removeById(id); // remove sms which triggered this function threadInterface.RemoveByID(sms.threadID); // and dangling thread as well return true; // handled (current window will be changed) } else { auto newLatest = (*twoLatest)[1]; UpdateThreadSummary(threadRec, newLatest); } } threadRec.msgCount--; threadInterface.Update(threadRec); // if deleting the newest sms, refresh thread details with next sms in the column } } // Remove SMS return smsDB->sms.removeById(id); } bool SMSRecordInterface::RemoveByField(SMSRecordField field, const char *str) { switch (field) { case SMSRecordField ::ContactID: return smsDB->sms.removeByField(SMSTableFields::ContactID, str); case SMSRecordField ::ThreadID: return smsDB->sms.removeByField(SMSTableFields::ThreadID, str); case SMSRecordField ::Number: return false; default: return false; } } SMSRecord SMSRecordInterface::GetByID(uint32_t id) { auto sms = smsDB->sms.getById(id); ContactRecordInterface contactInterface(contactsDB); auto contactRec = contactInterface.GetByID(sms.contactID); auto number = contactRec.numbers.size() != 0 ? contactRec.numbers[0].number : utils::PhoneNumber::View(); return SMSRecord{sms, number}; } std::unique_ptr SMSRecordInterface::runQuery(std::shared_ptr query) { if (typeid(*query) == typeid(db::query::SMSSearchByType)) { return runQueryImpl(static_cast(query.get())); } else if (typeid(*query) == typeid(db::query::SMSGetByID)) { return getByIDQuery(query); } else if (typeid(*query) == typeid(db::query::SMSGetByContactID)) { return getByContactIDQuery(query); } else if (typeid(*query) == typeid(db::query::SMSGetByThreadID)) { return getByThreadIDQuery(query); } else if (typeid(*query) == typeid(db::query::SMSGetCountByThreadID)) { return getCountByThreadIDQuery(query); } else if (typeid(*query) == typeid(db::query::SMSGetByText)) { return getByTextQuery(query); } else if (typeid(*query) == typeid(db::query::SMSGetLastByThreadID)) { return getLastByThreadIDQuery(query); } else if (typeid(*query) == typeid(db::query::SMSGetCount)) { return getCountQuery(query); } else if (typeid(*query) == typeid(db::query::SMSAdd)) { return addQuery(query); } else if (typeid(*query) == typeid(db::query::SMSRemove)) { return removeQuery(query); } else if (typeid(*query) == typeid(db::query::SMSUpdate)) { return updateQuery(query); } else if (typeid(*query) == typeid(db::query::SMSGet)) { return getQuery(query); } return nullptr; } std::unique_ptr SMSRecordInterface::runQueryImpl( const db::query::SMSSearchByType *query) { auto db_result = smsDB->sms.getManyByType(query->type, query->starting_postion, query->depth); auto records = std::make_unique>(); ContactRecordInterface contactInterface(contactsDB); for (const auto &w : db_result.second) { auto contactRec = contactInterface.GetByIdWithTemporary(w.contactID); if (contactRec.numbers.size() != 0) { records->push_back({w, contactRec.numbers[0].number}); } } return std::make_unique(db_result.first, *records); } std::unique_ptr SMSRecordInterface::getByIDQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto sms = smsDB->sms.getById(localQuery->id); SMSRecord record; record.body = sms.body; record.contactID = sms.contactID; record.date = sms.date; record.dateSent = sms.dateSent; record.errorCode = sms.errorCode; record.threadID = sms.threadID; record.type = sms.type; record.ID = sms.ID; auto response = std::make_unique(record); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::getByContactIDQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto smsVector = smsDB->sms.getByContactId(localQuery->contactId); std::vector recordVector; for (auto sms : smsVector) { SMSRecord record; record.body = sms.body; record.contactID = sms.contactID; record.date = sms.date; record.dateSent = sms.dateSent; record.errorCode = sms.errorCode; record.threadID = sms.threadID; record.type = sms.type; record.ID = sms.ID; recordVector.emplace_back(record); } auto response = std::make_unique(recordVector); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::getByTextQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto smsVector = smsDB->sms.getByText(localQuery->text); std::vector recordVector; for (auto sms : smsVector) { SMSRecord record; record.body = sms.body; record.contactID = sms.contactID; record.date = sms.date; record.dateSent = sms.dateSent; record.errorCode = sms.errorCode; record.threadID = sms.threadID; record.type = sms.type; record.ID = sms.ID; recordVector.emplace_back(record); } auto response = std::make_unique(recordVector); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::getCountQuery(std::shared_ptr query) { auto response = std::make_unique(smsDB->sms.count()); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::getCountByThreadIDQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto response = std::make_unique( smsDB->sms.countByFieldId("thread_id", localQuery->threadId)); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::addQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto record = localQuery->record; const auto ret = Add(record); if (ret) { record.ID = GetLastID(); } auto response = std::make_unique(record, ret); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::removeQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto ret = RemoveByID(localQuery->id); auto response = std::make_unique(ret); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::updateQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto ret = Update(localQuery->record); auto response = std::make_unique(ret); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::getQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto smsVector = smsDB->sms.getLimitOffset(localQuery->getOffset(), localQuery->getLimit()); std::vector recordVector; for (auto sms : smsVector) { SMSRecord record; record.body = sms.body; record.contactID = sms.contactID; record.date = sms.date; record.dateSent = sms.dateSent; record.errorCode = sms.errorCode; record.threadID = sms.threadID; record.type = sms.type; record.ID = sms.ID; recordVector.emplace_back(record); } auto response = std::make_unique(recordVector); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::getByThreadIDQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); auto smsVector = smsDB->sms.getByThreadId(localQuery->threadId, localQuery->offset, localQuery->limit); std::vector recordVector; for (auto sms : smsVector) { SMSRecord record; record.body = sms.body; record.contactID = sms.contactID; record.date = sms.date; record.dateSent = sms.dateSent; record.errorCode = sms.errorCode; record.threadID = sms.threadID; record.type = sms.type; record.ID = sms.ID; recordVector.emplace_back(record); } auto response = std::make_unique(recordVector); response->setRequestQuery(query); return response; } std::unique_ptr SMSRecordInterface::getLastByThreadIDQuery(std::shared_ptr query) { const auto localQuery = static_cast(query.get()); const auto &result = smsDB->sms.getByThreadId(localQuery->threadId, 0, 0); std::optional lastRecord; if (!result.empty()) { const auto &sms = result.back(); SMSRecord record; record.body = sms.body; record.contactID = sms.contactID; record.date = sms.date; record.dateSent = sms.dateSent; record.errorCode = sms.errorCode; record.threadID = sms.threadID; record.type = sms.type; record.ID = sms.ID; lastRecord = record; } auto response = std::make_unique(lastRecord); response->setRequestQuery(query); return response; }