2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 //#include <JSWebAPIErrorFactory.h>
18 //#include <JSWebAPIError.h>
25 #include "common/logger.h"
26 #include "common/platform_exception.h"
27 #include "common/scope_exit.h"
28 //#include <GlobalContextManager.h>
30 #include "MsgCommon/AbstractFilter.h"
32 #include <email-api-account.h>
33 #include <email-api-mail.h>
34 #include <email-api-mailbox.h>
35 #include <email-api-network.h>
37 #include "email_manager.h"
39 #include "message_conversation.h"
40 #include "message_service.h"
41 #include "messaging_util.h"
42 //#include "MessageCallbackUserData.h"
43 //#include "MessagesCallbackUserData.h"
44 #include "conversation_callback_data.h"
45 #include "find_msg_callback_user_data.h"
46 #include "message_email.h"
47 #include "messaging_database_manager.h"
49 //#include "JSMessage.h"
50 //#include "JSMessageConversation.h"
51 //#include "JSMessageFolder.h"
53 #include <email-api.h>
56 //#include "DBus/SyncProxy.h"
57 #include "DBus/LoadBodyProxy.h"
58 //#include "DBus/LoadAttachmentProxy.h"
61 #include "MsgCommon/FilterIterator.h"
63 #include "common/scope_exit.h"
64 #include "messaging/DBus/DBusTypes.h"
66 using namespace common;
67 using namespace extension::tizen;
68 using namespace std::chrono_literals;
74 const int ACCOUNT_ID_NOT_INITIALIZED = -1;
75 const std::string FIND_FOLDERS_ATTRIBUTE_ACCOUNTID_NAME = "serviceId";
77 bool isFirstInThread(const Message* message) {
79 return message->getId() == message->getConversationId();
82 bool isFirstInThread(const email_mail_data_t* mail_data) {
84 return mail_data->mail_id == mail_data->thread_id;
86 } // anonymous namespace
88 EmailManager::EmailManager() : m_slot_size(-1), m_is_initialized(false) {
92 #define CHECK_ERROR(ret, message) \
93 if (ret.IsError()) { \
98 PlatformResult EmailManager::InitializeEmailService() {
101 if (!m_is_initialized) {
104 int ntv_ret = email_service_begin();
105 if (ntv_ret != EMAIL_ERROR_NONE) {
106 return LogAndCreateResult(
107 ErrorCode::UNKNOWN_ERR, "Email service failed to begin",
108 ("email_service_begin error: %d (%s)", ntv_ret, get_error_message(ntv_ret)));
111 ntv_ret = email_open_db();
112 if (ntv_ret != EMAIL_ERROR_NONE) {
113 return LogAndCreateResult(
114 ErrorCode::UNKNOWN_ERR, "Email DB failed to open",
115 ("email_open_db error: %d (%s)", ntv_ret, get_error_message(ntv_ret)));
119 vconf_get_int("db/private/email-service/slot_size", &(slot_size));
121 m_slot_size = slot_size;
124 PlatformResult ret = DBus::SyncProxy::create(DBus::kDBusPathNetworkStatus,
125 DBus::kDBusIfaceNetworkStatus, &m_proxy_sync);
126 CHECK_ERROR(ret, "create sync proxy failed");
128 LoggerE("Sync proxy is null");
129 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Sync proxy is null");
131 m_proxy_sync->signalSubscribe();
133 ret = DBus::LoadBodyProxy::create(DBus::kDBusPathNetworkStatus, DBus::kDBusIfaceNetworkStatus,
135 CHECK_ERROR(ret, "create load body proxy failed");
136 if (!m_proxy_load_body) {
137 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Load body proxy is null");
139 m_proxy_load_body->signalSubscribe();
141 // ret = DBus::LoadAttachmentProxy::create(DBus::Proxy::DBUS_PATH_NETWORK_STATUS,
142 // DBus::Proxy::DBUS_IFACE_NETWORK_STATUS,
143 // &m_proxy_load_attachment);
144 // CHECK_ERROR(ret, "create load attachment proxy failed");
145 // if (!m_proxy_load_attachment) {
146 // LoggerE("Load attachment proxy is null");
147 // return PlatformResult(ErrorCode::UNKNOWN_ERR, "Load attachment proxy is null");
149 // m_proxy_load_attachment->signalSubscribe();
151 ret = DBus::MessageProxy::create(*this, &m_proxy_messageStorage);
152 CHECK_ERROR(ret, "create message proxy failed");
153 if (!m_proxy_messageStorage) {
154 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Message proxy is null");
156 m_proxy_messageStorage->signalSubscribe();
158 ret = DBus::SendProxy::create(*this, &m_proxy_send);
159 CHECK_ERROR(ret, "create send proxy failed");
161 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Send proxy is null");
163 m_proxy_send->signalSubscribe();
165 m_is_initialized = true;
168 return PlatformResult(ErrorCode::NO_ERROR);
171 EmailManager::~EmailManager() {
175 PlatformResult EmailManager::addDraftMessagePlatform(int account_id,
176 std::shared_ptr<Message> message) {
178 return addMessagePlatform(account_id, message, EMAIL_MAILBOX_TYPE_DRAFT);
181 PlatformResult EmailManager::addOutboxMessagePlatform(int account_id,
182 std::shared_ptr<Message> message) {
184 return addMessagePlatform(account_id, message, EMAIL_MAILBOX_TYPE_OUTBOX);
187 PlatformResult EmailManager::addMessagePlatform(int account_id, std::shared_ptr<Message> message,
188 email_mailbox_type_e mailbox_type) {
190 email_mail_data_t* mail_data = NULL;
191 email_mail_data_t* mail_data_final = NULL;
192 email_mailbox_t* mailbox_data = NULL;
193 email_account_t* account = NULL;
196 if (mail_data_final) {
197 email_free_mail_data(&mail_data_final, 1);
200 email_free_mail_data(&mail_data, 1);
203 email_free_mailbox(&mailbox_data, 1);
206 email_free_account(&account, 1);
210 int err = EMAIL_ERROR_NONE;
212 PlatformResult ret = Message::convertPlatformEmail(message, &mail_data);
213 if (ret.IsError()) return ret;
215 mail_data->account_id = account_id;
217 // Adding "from" email address
218 err = email_get_account(account_id, GET_FULL_DATA_WITHOUT_PASSWORD, &account);
219 if (EMAIL_ERROR_NONE != err) {
220 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Cannot retrieve email account information",
221 ("email_get_account error: %d (%s)", err, get_error_message(err)));
223 LoggerE("FROM %s", account->user_email_address);
224 std::stringstream ss;
225 ss << "<" << account->user_email_address << ">";
226 std::string address_from;
228 mail_data->full_address_from = strdup(address_from.c_str());
229 LoggerE("FROM %s", mail_data->full_address_from);
231 // Setting mailbox id
232 err = email_get_mailbox_by_mailbox_type(account_id, mailbox_type, &mailbox_data);
233 if (EMAIL_ERROR_NONE != err) {
234 return LogAndCreateResult(
235 ErrorCode::UNKNOWN_ERR, "Cannot retrieve draft mailbox",
236 ("email_get_mailbox_by_mailbox_type error: %d (%s)", err, get_error_message(err)));
238 LoggerD("email_get_mailbox_by_mailbox_type success.\n");
239 mail_data->mailbox_id = mailbox_data->mailbox_id;
240 mail_data->mailbox_type = mailbox_data->mailbox_type;
243 mail_data->report_status = EMAIL_MAIL_REPORT_NONE;
244 mail_data->save_status = EMAIL_MAIL_STATUS_SAVED;
245 mail_data->flags_draft_field = 1;
247 // adding email without attachments
248 err = email_add_mail(mail_data, NULL, 0, NULL, 0);
249 if (EMAIL_ERROR_NONE != err) {
250 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
251 ("email_add_mail error: %d (%s)", err, get_error_message(err)));
253 LoggerD("email_add_mail success.\n");
256 LoggerD("saved mail without attachments id = [%d]\n", mail_data->mail_id);
258 message->setId(mail_data->mail_id);
259 message->setMessageStatus(MessageStatus::STATUS_DRAFT);
261 if (message->getHasAttachment()) {
262 ret = Message::addEmailAttachments(message);
269 * If the message is first in its thread (its mail_id == thread_id)
270 * and it has an attachment, there is some delay between addEmailAttachments
271 * return and the corresponding update of message's record in the internal
274 * The internal DB is queried for mail_data_final below, but due to
275 * the mentioned delay, it may return mail_data_final, that is not
276 * up-to-date, i.e with mail_id != thread_id).
277 * If such situation occurs, up to MAX_RETRIES retries of calling
278 * email_get_mail_data are performed.
279 * If returned mail_data_final is up-to-date before reaching retries limit,
280 * the returned value is used to set mail properties.
281 * If returned mail_data_final is still not up-to-date, thread_id of the message
282 * is set manually to mail_id.
285 if (isFirstInThread(mail_data)) {
287 const int MAX_RETRIES = 5;
288 for (; retry < MAX_RETRIES; ++retry) {
289 err = email_get_mail_data(message->getId(), &mail_data_final);
291 if (EMAIL_ERROR_NONE != err) {
293 * TODO: in the case of email_get_mail_data failure,
294 * the message should be removed from the databse
296 return LogAndCreateResult(
297 ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
298 ("email_get_mail_data error: %d (%s)", err, get_error_message(err)));
301 if (isFirstInThread(mail_data) && isFirstInThread(mail_data_final)) {
302 LoggerD("Message adding process finished after %d retries. mail_id == thread_id", retry);
306 int free_error = email_free_mail_data(&mail_data_final, 1);
307 if (EMAIL_ERROR_NONE != free_error) {
308 LoggerW("email_free_mail_data error: %d, %s", free_error, get_error_message(free_error));
310 LoggerD("Retry number %d failed", retry);
311 std::this_thread::sleep_for(100ms);
314 if (MAX_RETRIES == retry) {
316 "Message adding process not finished after %d retries. Setting proper conversationId "
319 mail_data_final->thread_id = mail_data_final->mail_id;
322 err = email_get_mail_data(message->getId(), &mail_data_final);
323 if (EMAIL_ERROR_NONE != err) {
324 return LogAndCreateResult(
325 ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
326 ("email_get_mail_data error: %d (%s)", err, get_error_message(err)));
330 ret = message->updateEmailMessage(*mail_data_final);
335 return PlatformResult(ErrorCode::NO_ERROR);
338 static gboolean addDraftMessageCompleteCB(void* data) {
340 MessageCallbackUserData* callback = static_cast<MessageCallbackUserData*>(data);
342 LoggerE("Callback is null");
346 if (callback->IsError()) {
347 LoggerD("Calling error callback");
348 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_FAILED);
350 LoggerD("Calling success callback");
352 picojson::object args;
353 args[JSON_DATA_MESSAGE] = MessagingUtil::messageToJson(callback->getMessage());
354 callback->SetSuccess(picojson::value(args));
365 void EmailManager::addDraftMessage(MessageCallbackUserData* callback) {
369 LoggerE("Callback is null");
373 std::lock_guard<std::mutex> lock(m_mutex);
374 std::shared_ptr<Message> message = callback->getMessage();
375 PlatformResult ret = addDraftMessagePlatform(callback->getAccountId(), message);
377 callback->SetError(ret);
381 if (!g_idle_add(addDraftMessageCompleteCB, static_cast<void*>(callback))) {
382 LoggerE("g_idle addition failed");
388 //**** sending email ****
389 static gboolean sendEmailCompleteCB(void* user_data) {
392 MessageRecipientsCallbackData* callback = static_cast<MessageRecipientsCallbackData*>(user_data);
394 LoggerE("Callback is null");
398 if (callback->IsError()) {
399 LoggerD("Calling error callback");
400 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_FAILED);
402 LoggerD("Calling success callback");
404 std::vector<picojson::value> recipients;
405 auto addToRecipients = [&recipients](std::string& e) -> void {
406 recipients.push_back(picojson::value(e));
409 auto toVect = callback->getMessage()->getTO();
410 std::for_each(toVect.begin(), toVect.end(), addToRecipients);
412 auto ccVect = callback->getMessage()->getCC();
413 std::for_each(ccVect.begin(), ccVect.end(), addToRecipients);
415 auto bccVect = callback->getMessage()->getBCC();
416 std::for_each(bccVect.begin(), bccVect.end(), addToRecipients);
418 picojson::object data;
419 data[JSON_DATA_RECIPIENTS] = picojson::value(recipients);
420 data[JSON_DATA_MESSAGE] = MessagingUtil::messageToJson(callback->getMessage());
422 callback->SetSuccess(picojson::value(data));
423 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_SENT);
434 PlatformResult EmailManager::sendMessage(MessageRecipientsCallbackData* callback) {
438 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Callback is null");
441 int err = EMAIL_ERROR_NONE;
442 email_mail_data_t* mail_data = NULL;
444 PlatformResult platform_result(ErrorCode::NO_ERROR);
446 std::shared_ptr<Message> message = callback->getMessage();
449 if (!(message->is_id_set())) {
450 platform_result = addOutboxMessagePlatform(callback->getAccountId(), message);
453 if (platform_result) {
454 err = email_get_mail_data(message->getId(), &mail_data);
455 if (EMAIL_ERROR_NONE != err) {
457 LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to get platform email structure",
458 ("email_get_mail_data %d (%s)", err, get_error_message(err)));
460 LoggerD("email_get_mail_data success.\n");
463 mail_data->save_status = EMAIL_MAIL_STATUS_SENDING;
466 err = email_send_mail(mail_data->mail_id, &req_id);
468 if (EMAIL_ERROR_NONE != err) {
470 LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to send message",
471 ("email_send_mail error: %d (%s)", err, get_error_message(err)));
473 LoggerD("req_id: %d", req_id);
474 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_SENDING);
475 m_sendRequests[req_id] = callback;
480 LoggerE("Message is null");
481 platform_result = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Message is null");
484 if (!platform_result) {
485 LoggerE("Message send failed");
487 callback->SetError(platform_result);
489 if (!g_idle_add(sendEmailCompleteCB, static_cast<void*>(callback))) {
490 LoggerE("g_idle addition failed");
497 err = email_free_mail_data(&mail_data, 1);
499 if (EMAIL_ERROR_NONE != err) {
500 LoggerE("Failed to free mail data memory");
504 return platform_result;
507 void EmailManager::sendStatusCallback(int mail_id, email_noti_on_network_event status,
511 std::lock_guard<std::mutex> lock(m_mutex);
512 // find first request for this mail_id
513 SendReqMapIterator it = getSendRequest(mail_id);
514 if (it != m_sendRequests.end()) {
515 LoggerD("Found request");
516 MessageRecipientsCallbackData* callback = it->second;
517 m_sendRequests.erase(it);
519 if (NOTI_SEND_FAIL == status) {
520 LoggerD("Failed to send message, set proper error");
521 switch (error_code) {
522 case EMAIL_ERROR_NO_SIM_INSERTED:
523 case EMAIL_ERROR_SOCKET_FAILURE:
524 case EMAIL_ERROR_CONNECTION_FAILURE:
525 case EMAIL_ERROR_CONNECTION_BROKEN:
526 case EMAIL_ERROR_NO_SUCH_HOST:
527 case EMAIL_ERROR_NETWORK_NOT_AVAILABLE:
528 case EMAIL_ERROR_INVALID_STREAM:
529 case EMAIL_ERROR_NO_RESPONSE: {
530 callback->SetError(LogAndCreateResult(
531 ErrorCode::NETWORK_ERR, "Failed to send message",
532 ("Network error: %d (%s)", error_code, get_error_message(error_code))));
536 callback->SetError(LogAndCreateResult(
537 ErrorCode::UNKNOWN_ERR, "Failed to send message",
538 ("Unknown error: %d (%s)", error_code, get_error_message(error_code))));
541 } else if (NOTI_SEND_FINISH == status) {
542 LoggerD("Message sent successfully");
545 if (!g_idle_add(sendEmailCompleteCB, static_cast<void*>(callback))) {
546 LoggerE("g_idle addition failed");
551 LoggerW("No matching request found");
555 email_mail_data_t* EmailManager::loadMessage(int msg_id) {
557 email_mail_data_t* mail_data = NULL;
558 int err = email_get_mail_data(msg_id, &mail_data);
559 if (EMAIL_ERROR_NONE != err) {
560 LoggerE("email_get_mail_data failed. [%d] (%s)", err, get_error_message(err));
562 LoggerD("email_get_mail_data success.");
567 EmailManager::SendReqMapIterator EmailManager::getSendRequest(int mail_id) {
569 for (auto it = m_sendRequests.begin(); it != m_sendRequests.end(); it++) {
570 if (it->second->getMessage()->getId() == mail_id) {
574 return m_sendRequests.end();
577 void EmailManager::freeMessage(email_mail_data_t* mail_data) {
583 int err = email_free_mail_data(&mail_data, 1);
584 if (EMAIL_ERROR_NONE != err) {
585 LoggerE("Could not free mail data!");
589 void EmailManager::loadMessageBody(MessageBodyCallbackData* callback) {
592 LoggerE("Callback is null");
596 if (!callback->getMessage()) {
597 LoggerE("Callback's message is null");
601 m_proxy_load_body->addCallback(callback);
603 const int mailId = callback->getMessage()->getId();
606 int err = email_download_body(mailId, 0, &op_handle);
607 if (EMAIL_ERROR_NONE != err) {
608 LoggerE("Email download body failed, %d (%s)", err, get_error_message(err));
609 m_proxy_load_body->removeCallback(callback);
612 callback->setOperationHandle(op_handle);
615 PlatformResult EmailManager::loadMessageAttachment(MessageAttachmentCallbackData* callback) {
619 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Callback is null");
622 if (!callback->getMessageAttachment()) {
623 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR,
624 "Callback's message attachment is null");
627 std::shared_ptr<MessageAttachment> msgAttachment = callback->getMessageAttachment();
628 LoggerD("attachmentId: [%d] mailId: [%d]", msgAttachment->getId(), msgAttachment->getMessageId());
630 email_mail_data_t* mail_data = EmailManager::loadMessage(msgAttachment->getMessageId());
633 EmailManager::freeMessage(mail_data);
637 return LogAndCreateResult(
638 ErrorCode::UNKNOWN_ERR, "Couldn't load message.",
639 ("Couldn't get email_mail_data_t for messageId: %d", msgAttachment->getMessageId()));
642 AttachmentPtrVector attachments;
643 auto platform_result = Message::convertEmailToMessageAttachment(*mail_data, &attachments);
645 if (!platform_result) {
646 return platform_result;
649 LoggerD("Mail: [%d] contains: [%zu] attachments", msgAttachment->getMessageId(),
652 auto it = attachments.begin();
653 int attachmentIndex = -1;
654 for (int i = 0; it != attachments.end(); ++i, ++it) {
655 if ((*it)->getId() == msgAttachment->getId()) {
661 if (attachmentIndex < 0) {
662 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Couldn't find attachment.",
663 ("Attachment with id: %d not found", msgAttachment->getId()));
666 LoggerD("Attachment with id: [%d] is located at index: [%d]", msgAttachment->getId(),
670 const int nth = attachmentIndex + 1; // in documentation: the minimum number is "1"
671 callback->setNth(nth);
673 int err = email_download_attachment(msgAttachment->getMessageId(), nth, &op_handle);
675 if (EMAIL_ERROR_NONE != err) {
676 return LogAndCreateResult(
677 ErrorCode::UNKNOWN_ERR, "Failed to load attachment.",
678 ("Download email attachment failed with error: %d (%s)", err, get_error_message(err)));
680 LoggerD("email_download_attachment returned handle: [%d]", op_handle);
681 callback->setOperationHandle(op_handle);
682 m_proxy_load_attachment->addCallback(callback);
683 return PlatformResult(ErrorCode::NO_ERROR);
687 //#################################### sync: ###################################
689 void EmailManager::sync(void* data) {
692 SyncCallbackData* callback = static_cast<SyncCallbackData*>(data);
695 LoggerE("Callback is null");
699 long op_id = callback->getOpId();
700 m_proxy_sync->addCallback(op_id, callback);
702 int err = EMAIL_ERROR_NONE;
703 int limit = callback->getLimit();
705 int account_id = callback->getAccountId();
708 slot_size = m_slot_size;
713 err = email_set_mail_slot_size(0, 0, slot_size);
715 if (EMAIL_ERROR_NONE != err) {
716 LoggerE("Email set slot size failed, %d (%s)", err, get_error_message(err));
717 m_proxy_sync->removeCallback(op_id);
722 err = email_sync_header(account_id, 0, &op_handle);
724 if (EMAIL_ERROR_NONE != err) {
725 LoggerE("Email sync header failed, %d (%s)", err, get_error_message(err));
726 m_proxy_sync->removeCallback(op_id);
728 callback->setOperationHandle(op_handle);
732 //#################################### ^sync ###################################
734 //################################## syncFolder: ###############################
736 void EmailManager::syncFolder(SyncFolderCallbackData* callback) {
740 LoggerE("Callback is null");
744 const long op_id = callback->getOpId();
745 m_proxy_sync->addCallback(op_id, callback);
747 if (!callback->getMessageFolder()) {
748 LoggerE("Callback's messageFolder is null");
749 m_proxy_sync->removeCallback(op_id);
753 email_mailbox_t* mailbox = NULL;
755 const std::string folder_id_str = callback->getMessageFolder()->getId();
757 std::istringstream(folder_id_str) >> folder_id;
759 int err = email_get_mailbox_by_mailbox_id(folder_id, &mailbox);
761 if (EMAIL_ERROR_NONE != err || NULL == mailbox) {
762 LoggerE("Couldn't get mailbox, error code: %d (%s)", err, get_error_message(err));
763 m_proxy_sync->removeCallback(op_id);
767 const int limit = callback->getLimit();
771 slot_size = m_slot_size;
776 err = email_set_mail_slot_size(0, 0, slot_size);
777 if (EMAIL_ERROR_NONE != err) {
778 LoggerE("Email set slot size failed, %d (%s)", err, get_error_message(err));
781 const int account_id = callback->getAccountId();
783 err = email_sync_header(account_id, mailbox->mailbox_id, &op_handle);
785 if (EMAIL_ERROR_NONE != err) {
786 LoggerE("Email sync header failed, %d (%s)", err, get_error_message(err));
787 m_proxy_sync->removeCallback(op_id);
789 callback->setOperationHandle(op_handle);
793 if (NULL != mailbox) {
794 err = email_free_mailbox(&mailbox, 1);
795 if (EMAIL_ERROR_NONE != err) {
796 LoggerE("Failed to email_free_mailbox - err: %d (%s)", err, get_error_message(err));
802 //#################################### ^syncFolder #############################
804 //################################## stopSync: #################################
806 void EmailManager::stopSync(long op_id) {
809 SyncCallbackData* callback = static_cast<SyncCallbackData*>(m_proxy_sync->getCallback(op_id));
812 LoggerE("Callback is null");
816 int err = email_cancel_job(callback->getAccountId(), callback->getOperationHandle(),
817 EMAIL_CANCELED_BY_USER);
819 if (EMAIL_ERROR_NONE != err) {
820 LoggerE("Email cancel job failed, %d (%s)", err, get_error_message(err));
823 callback->SetError(LogAndCreateResult(ErrorCode::ABORT_ERR, "Sync aborted by user"));
826 m_proxy_sync->removeCallback(op_id);
829 //################################## ^stopSync #################################
831 void EmailManager::RemoveSyncCallback(long op_id) {
833 m_proxy_sync->removeCallback(op_id);
836 void EmailManager::RemoveCallbacksByQueue(const PostQueue& q) {
839 for (auto it = m_sendRequests.begin(); it != m_sendRequests.end();) {
840 if (it->second->HasQueue(q)) {
842 m_sendRequests.erase(it++);
849 void removeEmailCompleteCB(MessagesCallbackUserData* callback) {
852 LoggerE("Callback is null");
856 if (callback->IsError()) {
857 LoggerD("Calling error callback");
860 LoggerD("Calling success callback");
862 callback->SetSuccess();
871 EmailManager::DeleteReqVector::iterator EmailManager::getDeleteRequest(
872 const std::vector<int>& ids) {
874 for (auto idIt = ids.begin(); idIt != ids.end(); ++idIt) {
875 for (auto reqIt = m_deleteRequests.begin(); reqIt != m_deleteRequests.end(); ++reqIt) {
876 MessagePtrVector msgs = reqIt->callback->getMessages();
877 for (auto msgIt = msgs.begin(); msgIt != msgs.end(); ++msgIt) {
878 if ((*msgIt)->getId() == *idIt) {
884 return m_deleteRequests.end();
887 void EmailManager::removeStatusCallback(const std::vector<int>& ids,
888 email_noti_on_storage_event status) {
890 std::lock_guard<std::mutex> lock(m_mutex);
891 DeleteReqVector::iterator it = getDeleteRequest(ids);
892 if (it != m_deleteRequests.end()) {
893 LoggerD("Found request");
894 if (NOTI_MAIL_DELETE_FINISH == status) {
895 LoggerD("Successfully removed %zu mails", ids.size());
896 it->messagesDeleted += ids.size();
898 MessagesCallbackUserData* callback = it->callback;
899 if (NOTI_MAIL_DELETE_FAIL == status) {
900 callback->SetError(LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Messages remove failed"));
902 // if one of mails failed, call error callback
903 // if all mails are deleted, call success.
904 // >= is used in case of duplicated dbus messages
905 if (NOTI_MAIL_DELETE_FAIL == status ||
906 static_cast<unsigned int>(it->messagesDeleted) >= it->callback->getMessages().size()) {
907 LoggerD("Calling callback");
908 m_deleteRequests.erase(it);
910 removeEmailCompleteCB(callback);
912 LoggerD("Not all messages are removed, waiting for next callback");
915 LoggerD("Request not found, ignoring");
919 PlatformResult EmailManager::RemoveMessagesPlatform(MessagesCallbackUserData* callback) {
922 email_mail_data_t* mail = NULL;
924 std::lock_guard<std::mutex> lock(m_mutex);
925 std::vector<std::shared_ptr<Message>> messages = callback->getMessages();
926 MessageType type = callback->getMessageServiceType();
927 for (auto it = messages.begin(); it != messages.end(); ++it) {
928 if ((*it)->getType() != type) {
929 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Error while deleting email",
930 ("Invalid message type %d", (*it)->getType()));
933 for (auto it = messages.begin(); it != messages.end(); ++it) {
934 error = email_get_mail_data((*it)->getId(), &mail);
935 if (EMAIL_ERROR_NONE != error) {
936 return LogAndCreateResult(
937 ErrorCode::UNKNOWN_ERR, "Error while deleting mail",
938 ("Couldn't retrieve mail data: %d (%s)", error, get_error_message(error)));
941 // This task (_EMAIL_API_DELETE_MAIL) is for async
942 error = email_delete_mail(mail->mailbox_id, &mail->mail_id, 1, 0);
943 if (EMAIL_ERROR_NONE != error) {
944 email_free_mail_data(&mail, 1);
945 return LogAndCreateResult(
946 ErrorCode::UNKNOWN_ERR, "Error while deleting mail",
947 ("email_delete_mail error: %d (%s)", error, get_error_message(error)));
949 email_free_mail_data(&mail, 1);
951 // store delete request and wait for dbus response
953 request.callback = callback;
954 request.messagesDeleted = 0;
955 m_deleteRequests.push_back(request);
957 return PlatformResult(ErrorCode::NO_ERROR);
960 void EmailManager::removeMessages(MessagesCallbackUserData* callback) {
964 LoggerE("Callback is null");
968 PlatformResult ret = RemoveMessagesPlatform(callback);
970 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
971 callback->SetError(ret);
972 removeEmailCompleteCB(callback);
976 PlatformResult EmailManager::UpdateMessagesPlatform(MessagesCallbackUserData* callback) {
979 email_mail_data_t* mail = NULL;
982 email_free_mail_data(&mail, 1);
987 std::lock_guard<std::mutex> lock(m_mutex);
988 std::vector<std::shared_ptr<Message>> messages = callback->getMessages();
989 std::list<std::shared_ptr<Message>> firstInThreadAndHasAttachment;
990 MessageType type = callback->getMessageServiceType();
991 for (auto it = messages.begin(); it != messages.end(); ++it) {
992 if ((*it)->getType() != type) {
993 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Error while updating message",
994 ("Invalid message type: %d", (*it)->getType()));
997 for (auto it = messages.begin(); it != messages.end(); ++it) {
998 PlatformResult ret = Message::convertPlatformEmail((*it), &mail);
999 if (ret.IsError()) return ret;
1001 if ((*it)->getHasAttachment()) {
1002 if (isFirstInThread(it->get())) {
1003 // Messages with attachments, that are first in their threads are added
1004 // to the container, to be processed later in this function
1005 firstInThreadAndHasAttachment.push_back(*it);
1007 // Update of mail on server using function email_update_mail() is not possible.
1008 // Attachment is updated only locally (can't be later loaded from server),
1009 // so use of workaround is needed:
1011 // 2. delete old mail
1013 // adding message again after changes
1014 PlatformResult ret = addDraftMessagePlatform(mail->account_id, (*it));
1015 if (ret.IsError()) {
1018 LoggerD("mail added - new id = [%d]\n", (*it)->getId());
1020 // storing old message id
1021 (*it)->setOldId(mail->mail_id);
1022 // deleting old mail
1023 error = email_delete_mail(mail->mailbox_id, &mail->mail_id, 1, 1);
1024 LoggerD("mail deleted = [%d]\n", mail->mail_id);
1025 if (EMAIL_ERROR_NONE != error) {
1026 return LogAndCreateResult(
1027 ErrorCode::UNKNOWN_ERR, "Error while deleting old mail on update",
1028 ("email_delete_mail error: %d (%s)", error, get_error_message(error)));
1031 LoggerD("There are no attachments, updating only email data.");
1032 error = email_update_mail(mail, NULL, 0, NULL, 0);
1033 if (EMAIL_ERROR_NONE != error) {
1034 return LogAndCreateResult(
1035 ErrorCode::UNKNOWN_ERR, "Error while updating mail",
1036 ("email_update_mail error: %d (%s)", error, get_error_message(error)));
1041 if (firstInThreadAndHasAttachment.size() == 0) {
1042 return PlatformResult(ErrorCode::NO_ERROR);
1046 * If a message with attachment, that is first in its thread was updated,
1047 * the update process consists of adding a new message - updated version
1048 * of the original and removing the original.
1050 * After the original mail is deleted, the thread_id of the updated version
1051 * changes - it is now identical to the mail_id of the new message.
1052 * The change should be reflected in the updated message, sent to the JS
1053 * layer. The code below sets proper thread_id for such messages and unsets
1054 * inResponseTo fields.
1056 * A few retries with sleeps between them are performed, to ensure, that after
1057 * this function returns, messages returned to the JS layer and
1058 * corresponding records in the mail DB have the same thread_ids. Due to a
1059 * delay between calling email_delete_mail function and the update of records
1060 * in the database, some time is needed.
1062 email_mail_data_t* mail_data{nullptr};
1064 const int MAX_RETRIES = 5;
1065 for (; retry < MAX_RETRIES; ++retry) {
1066 std::this_thread::sleep_for(100ms);
1067 auto it = firstInThreadAndHasAttachment.begin();
1068 while (it != firstInThreadAndHasAttachment.end()) {
1069 error = email_get_mail_data((*it)->getId(), &mail_data);
1070 if (EMAIL_ERROR_NONE != error) {
1071 return LogAndCreateResult(
1072 ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
1073 ("email_get_mail_data error: %d (%s)", error, get_error_message(error)));
1076 if (isFirstInThread(mail_data)) {
1077 (*it)->setConversationId(mail_data->thread_id);
1078 (*it)->unsetInResponseTo();
1079 it = firstInThreadAndHasAttachment.erase(it);
1084 int free_error = email_free_mail_data(&mail_data, 1);
1085 if (EMAIL_ERROR_NONE != free_error) {
1086 LoggerW("email_free_mail_data error: %d, %s", free_error, get_error_message(free_error));
1090 if (firstInThreadAndHasAttachment.size() == 0) {
1091 LoggerD("Message updating process finished after %d retries", retry);
1095 LoggerD("Message update retry number %d finished", retry);
1098 if (MAX_RETRIES == retry) {
1099 LoggerW("Message updating process not finished after %d retries.", retry);
1100 for (auto& message : firstInThreadAndHasAttachment) {
1101 LoggerD("Setting proper conversationId manually, message id: %d", message->getId());
1102 message->setConversationId(message->getId());
1103 message->unsetInResponseTo();
1107 return PlatformResult(ErrorCode::NO_ERROR);
1110 void EmailManager::updateMessages(MessagesCallbackUserData* callback) {
1114 LoggerE("Callback is null");
1118 PlatformResult ret = UpdateMessagesPlatform(callback);
1119 if (ret.IsError()) {
1120 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1121 callback->SetError(ret);
1124 if (callback->IsError()) {
1125 LoggerD("Calling error callback");
1127 LoggerD("Calling success callback");
1129 picojson::array array;
1130 auto messages = callback->getMessages();
1131 size_t messages_size = messages.size();
1132 for (size_t i = 0; i < messages_size; ++i) {
1133 array.push_back(MessagingUtil::messageToJson(messages[i]));
1136 callback->SetSuccess(picojson::value(array));
1145 PlatformResult EmailManager::FindMessagesPlatform(FindMsgCallbackUserData* callback) {
1147 email_mail_data_t* mailList = NULL;
1148 int mailListCount = 0;
1151 if (mailListCount > 0 && mailList != NULL) {
1152 if (EMAIL_ERROR_NONE != email_free_mail_data(&mailList, mailListCount)) {
1153 LoggerW("Failed to free mailList");
1158 std::lock_guard<std::mutex> lock(m_mutex);
1159 std::pair<int, email_mail_data_t*> emails;
1160 PlatformResult ret = MessagingDatabaseManager::getInstance().findEmails(callback, &emails);
1161 if (ret.IsError()) return ret;
1162 mailListCount = emails.first;
1163 LoggerD("Found %d mails", mailListCount);
1165 mailList = emails.second;
1166 email_mail_data_t* nth_email = mailList;
1168 for (int i = 0; i < mailListCount; ++i) {
1169 std::shared_ptr<Message> email;
1170 ret = Message::convertPlatformEmailToObject(*nth_email, &email);
1171 if (ret.IsError()) return ret;
1172 callback->addMessage(email);
1175 return PlatformResult(ErrorCode::NO_ERROR);
1178 void EmailManager::findMessages(FindMsgCallbackUserData* callback) {
1182 LoggerE("Callback is null");
1186 PlatformResult ret = FindMessagesPlatform(callback);
1187 if (ret.IsError()) {
1188 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1189 callback->SetError(ret);
1193 LoggerD("callback: %p error: %d messages.size() = %zu", callback, callback->IsError(),
1194 callback->getMessages().size());
1196 if (callback->IsError()) {
1197 LoggerD("Calling error callback");
1200 LoggerD("Calling success callback");
1201 std::vector<picojson::value> response;
1202 auto messages = callback->getMessages();
1203 std::for_each(messages.begin(), messages.end(), [&response](MessagePtr& message) {
1204 response.push_back(MessagingUtil::messageToJson(message));
1207 callback->SetSuccess(picojson::value(response));
1216 PlatformResult EmailManager::FindConversationsPlatform(ConversationCallbackData* callback) {
1219 std::lock_guard<std::mutex> lock(m_mutex);
1220 std::vector<std::shared_ptr<MessageConversation>> conversationsInfo;
1221 PlatformResult ret =
1222 MessagingDatabaseManager::getInstance().findEmailConversations(*callback, &conversationsInfo);
1223 if (ret.IsError()) return ret;
1225 LoggerD("Found %zu conversations", conversationsInfo.size());
1227 for (const auto& conversation : conversationsInfo) {
1228 callback->addConversation(conversation);
1231 return PlatformResult(ErrorCode::NO_ERROR);
1234 void EmailManager::findConversations(ConversationCallbackData* callback) {
1238 LoggerE("Callback is null");
1242 PlatformResult ret = FindConversationsPlatform(callback);
1243 if (ret.IsError()) {
1244 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1245 callback->SetError(ret);
1249 LoggerD("callback: %p error:%d conversations.size()=%zu", callback, callback->IsError(),
1250 callback->getConversations().size());
1252 if (callback->IsError()) {
1253 LoggerD("Calling error callback");
1255 LoggerD("Calling success callback");
1257 std::vector<picojson::value> response;
1258 auto messages = callback->getConversations();
1259 std::for_each(messages.begin(), messages.end(),
1260 [&response](std::shared_ptr<MessageConversation>& conversation) {
1261 response.push_back(MessagingUtil::conversationToJson(conversation));
1264 callback->SetSuccess(picojson::value(response));
1273 long EmailManager::getUniqueOpId() {
1275 // mutex is created only on first call (first call added to constructor
1276 // to initialize mutex correctly)
1277 static std::mutex op_id_mutex;
1278 std::lock_guard<std::mutex> lock(op_id_mutex);
1279 static long op_id = 0;
1283 PlatformResult EmailManager::FindFoldersPlatform(FoldersCallbackData* callback) {
1285 int ret = EMAIL_ERROR_UNKNOWN;
1286 int account_id = ACCOUNT_ID_NOT_INITIALIZED;
1287 email_mailbox_t* mailboxes = NULL;
1288 email_mailbox_t* nth_mailbox = NULL;
1289 int mailboxes_count;
1292 if (mailboxes != NULL) {
1293 if (EMAIL_ERROR_NONE != email_free_mailbox(&mailboxes, mailboxes_count)) {
1294 LoggerW("Free mailboxes failed: %d", ret);
1299 std::lock_guard<std::mutex> lock(m_mutex);
1301 tizen::AbstractFilterPtr filter = callback->getFilter();
1303 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Filter not provided");
1306 for (FilterIterator it(filter); false == it.isEnd(); it++) {
1307 if (FIS_COMPOSITE_START == it.getState()) {
1308 CompositeFilterPtr cf = castToCompositeFilter((*it));
1309 if (cf && INTERSECTION != cf->getType()) {
1310 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Invalid Filter Type",
1311 ("Invalid Filter type: %d", cf->getType()));
1313 } else if (FIS_ATTRIBUTE_FILTER == it.getState()) {
1314 AttributeFilterPtr attrf = castToAttributeFilter((*it));
1316 const std::string attr_name = attrf->getAttributeName();
1317 if (FIND_FOLDERS_ATTRIBUTE_ACCOUNTID_NAME == attr_name) {
1318 account_id = static_cast<int>(attrf->getMatchValue()->toLong());
1320 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "The attribute name is invalid",
1321 ("The attribute name: %s is invalid", attr_name.c_str()));
1327 LoggerD("Listing folders for account ID: %d", account_id);
1328 if (account_id > 0) {
1329 ret = email_get_mailbox_list(account_id, -1, &mailboxes, &mailboxes_count);
1330 if (EMAIL_ERROR_NONE != ret || !mailboxes) {
1331 return LogAndCreateResult(
1332 ErrorCode::UNKNOWN_ERR, "Platform error, cannot get folders",
1333 ("email_get_mailbox_list error: %d (%s)", ret, get_error_message(ret)));
1336 if (mailboxes_count <= 0) {
1337 LoggerD("Empty mailboxes");
1339 LoggerD("Founded mailboxes: %d", mailboxes_count);
1341 nth_mailbox = mailboxes;
1342 for (int i = 0; i < mailboxes_count; ++i) {
1343 std::shared_ptr<MessageFolder> fd;
1344 fd = std::make_shared<MessageFolder>(*nth_mailbox);
1345 callback->addFolder(fd);
1350 return PlatformResult(ErrorCode::NO_ERROR);
1353 void EmailManager::findFolders(FoldersCallbackData* callback) {
1357 LoggerE("Callback is null");
1361 PlatformResult ret = FindFoldersPlatform(callback);
1362 if (ret.IsError()) {
1363 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1364 callback->SetError(ret);
1368 LoggerD("callback: %p error:%d folders.size()=%zu", callback, callback->IsError(),
1369 callback->getFolders().size());
1371 if (callback->IsError()) {
1372 LoggerD("Calling error callback");
1374 LoggerD("Calling success callback");
1376 std::vector<picojson::value> response;
1377 auto folders = callback->getFolders();
1378 std::for_each(folders.begin(), folders.end(),
1379 [&response](std::shared_ptr<MessageFolder>& folder) {
1380 response.push_back(MessagingUtil::folderToJson(folder));
1383 callback->SetSuccess(picojson::value(response));
1392 PlatformResult EmailManager::RemoveConversationsPlatform(ConversationCallbackData* callback) {
1395 std::lock_guard<std::mutex> lock(m_mutex);
1396 std::vector<std::shared_ptr<MessageConversation>> conversations = callback->getConversations();
1397 MessageType type = callback->getMessageServiceType();
1400 for (auto it = conversations.begin(); it != conversations.end(); ++it) {
1401 if ((*it)->getType() != type) {
1402 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR,
1403 "Error while deleting email conversation",
1404 ("Invalid message type %d", (*it)->getType()));
1408 for (auto it = conversations.begin(); it != conversations.end(); ++it) {
1409 thread_id = (*it)->getConversationId();
1410 error = email_delete_thread(thread_id, false);
1411 if (EMAIL_ERROR_NONE != error) {
1412 return LogAndCreateResult(
1413 ErrorCode::TYPE_MISMATCH_ERR, "Error while deleting email conversation",
1414 ("Couldn't delete email conversation data %d (%s)", error, get_error_message(error)));
1417 // for now, there is no way to recognize deleting email thread job is completed.
1418 // so use polling to wait the thread is removed.
1419 email_mail_data_t* thread_info = NULL;
1421 struct timespec sleep_time = {0, 300L * 1000L * 1000L};
1422 nanosleep(&sleep_time, nullptr);
1423 LoggerD("Waiting to delete this email thread...");
1424 error = email_get_thread_information_by_thread_id(thread_id, &thread_info);
1426 if (thread_info != NULL) {
1430 } while (error != EMAIL_ERROR_MAIL_NOT_FOUND);
1432 return PlatformResult(ErrorCode::NO_ERROR);
1435 void EmailManager::removeConversations(ConversationCallbackData* callback) {
1439 LoggerE("Callback is null");
1443 PlatformResult ret = RemoveConversationsPlatform(callback);
1444 if (ret.IsError()) {
1445 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1446 callback->SetError(ret);
1449 if (callback->IsError()) {
1450 LoggerD("Calling error callback");
1452 LoggerD("Calling success callback");
1454 callback->SetSuccess();
1463 std::string EmailManager::getMessageStatus(int id) {
1466 email_mail_data_t* mail = nullptr;
1467 MessageStatus status = MessageStatus::STATUS_UNDEFINED;
1469 int ret = email_get_mail_data(id, &mail);
1470 if (EMAIL_ERROR_NONE != ret || !mail) {
1471 LoggerD("Failed to get data %d (%s)", ret, get_error_message(ret));
1475 switch (mail->save_status) {
1476 case EMAIL_MAIL_STATUS_SENT:
1477 status = MessageStatus::STATUS_SENT;
1479 case EMAIL_MAIL_STATUS_SENDING:
1480 status = MessageStatus::STATUS_SENDING;
1482 case EMAIL_MAIL_STATUS_SAVED:
1483 status = MessageStatus::STATUS_DRAFT;
1485 case EMAIL_MAIL_STATUS_SEND_FAILURE:
1486 status = MessageStatus::STATUS_FAILED;
1489 status = MessageStatus::STATUS_UNDEFINED;
1493 ret = email_free_mail_data(&mail, 1);
1494 if (EMAIL_ERROR_NONE != ret) {
1495 LoggerD("Failed to free mail data %d (%s)", ret, get_error_message(ret));
1498 return MessagingUtil::messageStatusToString(status);