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 ntv_ret = vconf_get_int("db/private/email-service/slot_size", &(slot_size));
121 if (0 == ntv_ret && slot_size > 0) {
122 m_slot_size = slot_size;
125 PlatformResult ret = DBus::SyncProxy::create(DBus::kDBusPathNetworkStatus,
126 DBus::kDBusIfaceNetworkStatus, &m_proxy_sync);
127 CHECK_ERROR(ret, "create sync proxy failed");
129 LoggerE("Sync proxy is null");
130 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Sync proxy is null");
132 m_proxy_sync->signalSubscribe();
134 ret = DBus::LoadBodyProxy::create(DBus::kDBusPathNetworkStatus, DBus::kDBusIfaceNetworkStatus,
136 CHECK_ERROR(ret, "create load body proxy failed");
137 if (!m_proxy_load_body) {
138 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Load body proxy is null");
140 m_proxy_load_body->signalSubscribe();
142 // ret = DBus::LoadAttachmentProxy::create(DBus::Proxy::DBUS_PATH_NETWORK_STATUS,
143 // DBus::Proxy::DBUS_IFACE_NETWORK_STATUS,
144 // &m_proxy_load_attachment);
145 // CHECK_ERROR(ret, "create load attachment proxy failed");
146 // if (!m_proxy_load_attachment) {
147 // LoggerE("Load attachment proxy is null");
148 // return PlatformResult(ErrorCode::UNKNOWN_ERR, "Load attachment proxy is null");
150 // m_proxy_load_attachment->signalSubscribe();
152 ret = DBus::MessageProxy::create(*this, &m_proxy_messageStorage);
153 CHECK_ERROR(ret, "create message proxy failed");
154 if (!m_proxy_messageStorage) {
155 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Message proxy is null");
157 m_proxy_messageStorage->signalSubscribe();
159 ret = DBus::SendProxy::create(*this, &m_proxy_send);
160 CHECK_ERROR(ret, "create send proxy failed");
162 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Send proxy is null");
164 m_proxy_send->signalSubscribe();
166 m_is_initialized = true;
169 return PlatformResult(ErrorCode::NO_ERROR);
172 EmailManager::~EmailManager() {
176 PlatformResult EmailManager::addDraftMessagePlatform(int account_id,
177 std::shared_ptr<Message> message) {
179 return addMessagePlatform(account_id, message, EMAIL_MAILBOX_TYPE_DRAFT);
182 PlatformResult EmailManager::addOutboxMessagePlatform(int account_id,
183 std::shared_ptr<Message> message) {
185 return addMessagePlatform(account_id, message, EMAIL_MAILBOX_TYPE_OUTBOX);
188 PlatformResult EmailManager::addMessagePlatform(int account_id, std::shared_ptr<Message> message,
189 email_mailbox_type_e mailbox_type) {
191 email_mail_data_t* mail_data = NULL;
192 email_mail_data_t* mail_data_final = NULL;
193 email_mailbox_t* mailbox_data = NULL;
194 email_account_t* account = NULL;
197 if (mail_data_final) {
198 email_free_mail_data(&mail_data_final, 1);
201 email_free_mail_data(&mail_data, 1);
204 email_free_mailbox(&mailbox_data, 1);
207 email_free_account(&account, 1);
211 int err = EMAIL_ERROR_NONE;
213 PlatformResult ret = Message::convertPlatformEmail(message, &mail_data);
214 if (ret.IsError()) return ret;
216 mail_data->account_id = account_id;
218 // Adding "from" email address
219 err = email_get_account(account_id, GET_FULL_DATA_WITHOUT_PASSWORD, &account);
220 if (EMAIL_ERROR_NONE != err) {
221 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Cannot retrieve email account information",
222 ("email_get_account error: %d (%s)", err, get_error_message(err)));
224 LoggerE("FROM %s", account->user_email_address);
225 std::stringstream ss;
226 ss << "<" << account->user_email_address << ">";
227 std::string address_from;
229 mail_data->full_address_from = strdup(address_from.c_str());
230 LoggerE("FROM %s", mail_data->full_address_from);
232 // Setting mailbox id
233 err = email_get_mailbox_by_mailbox_type(account_id, mailbox_type, &mailbox_data);
234 if (EMAIL_ERROR_NONE != err) {
235 return LogAndCreateResult(
236 ErrorCode::UNKNOWN_ERR, "Cannot retrieve draft mailbox",
237 ("email_get_mailbox_by_mailbox_type error: %d (%s)", err, get_error_message(err)));
239 LoggerD("email_get_mailbox_by_mailbox_type success.\n");
240 mail_data->mailbox_id = mailbox_data->mailbox_id;
241 mail_data->mailbox_type = mailbox_data->mailbox_type;
244 mail_data->report_status = EMAIL_MAIL_REPORT_NONE;
245 mail_data->save_status = EMAIL_MAIL_STATUS_SAVED;
246 mail_data->flags_draft_field = 1;
248 // adding email without attachments
249 err = email_add_mail(mail_data, NULL, 0, NULL, 0);
250 if (EMAIL_ERROR_NONE != err) {
251 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
252 ("email_add_mail error: %d (%s)", err, get_error_message(err)));
254 LoggerD("email_add_mail success.\n");
257 LoggerD("saved mail without attachments id = [%d]\n", mail_data->mail_id);
259 message->setId(mail_data->mail_id);
260 message->setMessageStatus(MessageStatus::STATUS_DRAFT);
262 if (message->getHasAttachment()) {
263 ret = Message::addEmailAttachments(message);
270 * If the message is first in its thread (its mail_id == thread_id)
271 * and it has an attachment, there is some delay between addEmailAttachments
272 * return and the corresponding update of message's record in the internal
275 * The internal DB is queried for mail_data_final below, but due to
276 * the mentioned delay, it may return mail_data_final, that is not
277 * up-to-date, i.e with mail_id != thread_id).
278 * If such situation occurs, up to MAX_RETRIES retries of calling
279 * email_get_mail_data are performed.
280 * If returned mail_data_final is up-to-date before reaching retries limit,
281 * the returned value is used to set mail properties.
282 * If returned mail_data_final is still not up-to-date, thread_id of the message
283 * is set manually to mail_id.
286 if (isFirstInThread(mail_data)) {
288 const int MAX_RETRIES = 5;
289 for (; retry < MAX_RETRIES; ++retry) {
290 err = email_get_mail_data(message->getId(), &mail_data_final);
292 if (EMAIL_ERROR_NONE != err) {
294 * TODO: in the case of email_get_mail_data failure,
295 * the message should be removed from the databse
297 return LogAndCreateResult(
298 ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
299 ("email_get_mail_data error: %d (%s)", err, get_error_message(err)));
302 if (isFirstInThread(mail_data) && isFirstInThread(mail_data_final)) {
303 LoggerD("Message adding process finished after %d retries. mail_id == thread_id", retry);
307 int free_error = email_free_mail_data(&mail_data_final, 1);
308 if (EMAIL_ERROR_NONE != free_error) {
309 LoggerW("email_free_mail_data error: %d, %s", free_error, get_error_message(free_error));
311 LoggerD("Retry number %d failed", retry);
312 std::this_thread::sleep_for(100ms);
315 if (MAX_RETRIES == retry) {
317 "Message adding process not finished after %d retries. Setting proper conversationId "
320 mail_data_final->thread_id = mail_data_final->mail_id;
323 err = email_get_mail_data(message->getId(), &mail_data_final);
324 if (EMAIL_ERROR_NONE != err) {
325 return LogAndCreateResult(
326 ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
327 ("email_get_mail_data error: %d (%s)", err, get_error_message(err)));
331 ret = message->updateEmailMessage(*mail_data_final);
336 return PlatformResult(ErrorCode::NO_ERROR);
339 static gboolean addDraftMessageCompleteCB(void* data) {
341 MessageCallbackUserData* callback = static_cast<MessageCallbackUserData*>(data);
343 LoggerE("Callback is null");
347 if (callback->IsError()) {
348 LoggerD("Calling error callback");
349 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_FAILED);
351 LoggerD("Calling success callback");
353 picojson::object args;
354 args[JSON_DATA_MESSAGE] = MessagingUtil::messageToJson(callback->getMessage());
355 callback->SetSuccess(picojson::value(args));
366 void EmailManager::addDraftMessage(MessageCallbackUserData* callback) {
370 LoggerE("Callback is null");
374 std::lock_guard<std::mutex> lock(m_mutex);
375 std::shared_ptr<Message> message = callback->getMessage();
376 PlatformResult ret = addDraftMessagePlatform(callback->getAccountId(), message);
378 callback->SetError(ret);
382 if (!g_idle_add(addDraftMessageCompleteCB, static_cast<void*>(callback))) {
383 LoggerE("g_idle addition failed");
389 //**** sending email ****
390 static gboolean sendEmailCompleteCB(void* user_data) {
393 MessageRecipientsCallbackData* callback = static_cast<MessageRecipientsCallbackData*>(user_data);
395 LoggerE("Callback is null");
399 if (callback->IsError()) {
400 LoggerD("Calling error callback");
401 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_FAILED);
403 LoggerD("Calling success callback");
405 std::vector<picojson::value> recipients;
406 auto addToRecipients = [&recipients](std::string& e) -> void {
407 recipients.push_back(picojson::value(e));
410 auto toVect = callback->getMessage()->getTO();
411 std::for_each(toVect.begin(), toVect.end(), addToRecipients);
413 auto ccVect = callback->getMessage()->getCC();
414 std::for_each(ccVect.begin(), ccVect.end(), addToRecipients);
416 auto bccVect = callback->getMessage()->getBCC();
417 std::for_each(bccVect.begin(), bccVect.end(), addToRecipients);
419 picojson::object data;
420 data[JSON_DATA_RECIPIENTS] = picojson::value(recipients);
421 data[JSON_DATA_MESSAGE] = MessagingUtil::messageToJson(callback->getMessage());
423 callback->SetSuccess(picojson::value(data));
424 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_SENT);
435 PlatformResult EmailManager::sendMessage(MessageRecipientsCallbackData* callback) {
439 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Callback is null");
442 int err = EMAIL_ERROR_NONE;
443 email_mail_data_t* mail_data = NULL;
445 PlatformResult platform_result(ErrorCode::NO_ERROR);
447 std::shared_ptr<Message> message = callback->getMessage();
450 if (!(message->is_id_set())) {
451 platform_result = addOutboxMessagePlatform(callback->getAccountId(), message);
454 if (platform_result) {
455 err = email_get_mail_data(message->getId(), &mail_data);
456 if (EMAIL_ERROR_NONE != err) {
458 LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to get platform email structure",
459 ("email_get_mail_data %d (%s)", err, get_error_message(err)));
461 LoggerD("email_get_mail_data success.\n");
464 mail_data->save_status = EMAIL_MAIL_STATUS_SENDING;
467 err = email_send_mail(mail_data->mail_id, &req_id);
469 if (EMAIL_ERROR_NONE != err) {
471 LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to send message",
472 ("email_send_mail error: %d (%s)", err, get_error_message(err)));
474 LoggerD("req_id: %d", req_id);
475 callback->getMessage()->setMessageStatus(MessageStatus::STATUS_SENDING);
476 m_sendRequests[req_id] = callback;
481 LoggerE("Message is null");
482 platform_result = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Message is null");
485 if (!platform_result) {
486 LoggerE("Message send failed");
488 callback->SetError(platform_result);
490 if (!g_idle_add(sendEmailCompleteCB, static_cast<void*>(callback))) {
491 LoggerE("g_idle addition failed");
498 err = email_free_mail_data(&mail_data, 1);
500 if (EMAIL_ERROR_NONE != err) {
501 LoggerE("Failed to free mail data memory");
505 return platform_result;
508 void EmailManager::sendStatusCallback(int mail_id, email_noti_on_network_event status,
512 std::lock_guard<std::mutex> lock(m_mutex);
513 // find first request for this mail_id
514 SendReqMapIterator it = getSendRequest(mail_id);
515 if (it != m_sendRequests.end()) {
516 LoggerD("Found request");
517 MessageRecipientsCallbackData* callback = it->second;
518 m_sendRequests.erase(it);
520 if (NOTI_SEND_FAIL == status) {
521 LoggerD("Failed to send message, set proper error");
522 switch (error_code) {
523 case EMAIL_ERROR_NO_SIM_INSERTED:
524 case EMAIL_ERROR_SOCKET_FAILURE:
525 case EMAIL_ERROR_CONNECTION_FAILURE:
526 case EMAIL_ERROR_CONNECTION_BROKEN:
527 case EMAIL_ERROR_NO_SUCH_HOST:
528 case EMAIL_ERROR_NETWORK_NOT_AVAILABLE:
529 case EMAIL_ERROR_INVALID_STREAM:
530 case EMAIL_ERROR_NO_RESPONSE: {
531 callback->SetError(LogAndCreateResult(
532 ErrorCode::NETWORK_ERR, "Failed to send message",
533 ("Network error: %d (%s)", error_code, get_error_message(error_code))));
537 callback->SetError(LogAndCreateResult(
538 ErrorCode::UNKNOWN_ERR, "Failed to send message",
539 ("Unknown error: %d (%s)", error_code, get_error_message(error_code))));
542 } else if (NOTI_SEND_FINISH == status) {
543 LoggerD("Message sent successfully");
546 if (!g_idle_add(sendEmailCompleteCB, static_cast<void*>(callback))) {
547 LoggerE("g_idle addition failed");
552 LoggerW("No matching request found");
556 email_mail_data_t* EmailManager::loadMessage(int msg_id) {
558 email_mail_data_t* mail_data = NULL;
559 int err = email_get_mail_data(msg_id, &mail_data);
560 if (EMAIL_ERROR_NONE != err) {
561 LoggerE("email_get_mail_data failed. [%d] (%s)", err, get_error_message(err));
563 LoggerD("email_get_mail_data success.");
568 EmailManager::SendReqMapIterator EmailManager::getSendRequest(int mail_id) {
570 for (auto it = m_sendRequests.begin(); it != m_sendRequests.end(); it++) {
571 if (it->second->getMessage()->getId() == mail_id) {
575 return m_sendRequests.end();
578 void EmailManager::freeMessage(email_mail_data_t* mail_data) {
584 int err = email_free_mail_data(&mail_data, 1);
585 if (EMAIL_ERROR_NONE != err) {
586 LoggerE("Could not free mail data!");
590 void EmailManager::loadMessageBody(MessageBodyCallbackData* callback) {
593 LoggerE("Callback is null");
597 if (!callback->getMessage()) {
598 LoggerE("Callback's message is null");
602 m_proxy_load_body->addCallback(callback);
604 const int mailId = callback->getMessage()->getId();
607 int err = email_download_body(mailId, 0, &op_handle);
608 if (EMAIL_ERROR_NONE != err) {
609 LoggerE("Email download body failed, %d (%s)", err, get_error_message(err));
610 m_proxy_load_body->removeCallback(callback);
613 callback->setOperationHandle(op_handle);
616 PlatformResult EmailManager::loadMessageAttachment(MessageAttachmentCallbackData* callback) {
620 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Callback is null");
623 if (!callback->getMessageAttachment()) {
624 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR,
625 "Callback's message attachment is null");
628 std::shared_ptr<MessageAttachment> msgAttachment = callback->getMessageAttachment();
629 LoggerD("attachmentId: [%d] mailId: [%d]", msgAttachment->getId(), msgAttachment->getMessageId());
631 email_mail_data_t* mail_data = EmailManager::loadMessage(msgAttachment->getMessageId());
634 EmailManager::freeMessage(mail_data);
638 return LogAndCreateResult(
639 ErrorCode::UNKNOWN_ERR, "Couldn't load message.",
640 ("Couldn't get email_mail_data_t for messageId: %d", msgAttachment->getMessageId()));
643 AttachmentPtrVector attachments;
644 auto platform_result = Message::convertEmailToMessageAttachment(*mail_data, &attachments);
646 if (!platform_result) {
647 return platform_result;
650 LoggerD("Mail: [%d] contains: [%zu] attachments", msgAttachment->getMessageId(),
653 auto it = attachments.begin();
654 int attachmentIndex = -1;
655 for (int i = 0; it != attachments.end(); ++i, ++it) {
656 if ((*it)->getId() == msgAttachment->getId()) {
662 if (attachmentIndex < 0) {
663 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Couldn't find attachment.",
664 ("Attachment with id: %d not found", msgAttachment->getId()));
667 LoggerD("Attachment with id: [%d] is located at index: [%d]", msgAttachment->getId(),
671 const int nth = attachmentIndex + 1; // in documentation: the minimum number is "1"
672 callback->setNth(nth);
674 int err = email_download_attachment(msgAttachment->getMessageId(), nth, &op_handle);
676 if (EMAIL_ERROR_NONE != err) {
677 return LogAndCreateResult(
678 ErrorCode::UNKNOWN_ERR, "Failed to load attachment.",
679 ("Download email attachment failed with error: %d (%s)", err, get_error_message(err)));
681 LoggerD("email_download_attachment returned handle: [%d]", op_handle);
682 callback->setOperationHandle(op_handle);
683 m_proxy_load_attachment->addCallback(callback);
684 return PlatformResult(ErrorCode::NO_ERROR);
688 //#################################### sync: ###################################
690 void EmailManager::sync(void* data) {
693 SyncCallbackData* callback = static_cast<SyncCallbackData*>(data);
696 LoggerE("Callback is null");
700 long op_id = callback->getOpId();
701 m_proxy_sync->addCallback(op_id, callback);
703 int err = EMAIL_ERROR_NONE;
704 int limit = callback->getLimit();
706 int account_id = callback->getAccountId();
709 slot_size = m_slot_size;
714 err = email_set_mail_slot_size(0, 0, slot_size);
716 if (EMAIL_ERROR_NONE != err) {
717 LoggerE("Email set slot size failed, %d (%s)", err, get_error_message(err));
718 m_proxy_sync->removeCallback(op_id);
723 err = email_sync_header(account_id, 0, &op_handle);
725 if (EMAIL_ERROR_NONE != err) {
726 LoggerE("Email sync header failed, %d (%s)", err, get_error_message(err));
727 m_proxy_sync->removeCallback(op_id);
729 callback->setOperationHandle(op_handle);
733 //#################################### ^sync ###################################
735 //################################## syncFolder: ###############################
737 void EmailManager::syncFolder(SyncFolderCallbackData* callback) {
741 LoggerE("Callback is null");
745 const long op_id = callback->getOpId();
746 m_proxy_sync->addCallback(op_id, callback);
748 if (!callback->getMessageFolder()) {
749 LoggerE("Callback's messageFolder is null");
750 m_proxy_sync->removeCallback(op_id);
754 email_mailbox_t* mailbox = NULL;
756 const std::string folder_id_str = callback->getMessageFolder()->getId();
758 std::istringstream(folder_id_str) >> folder_id;
760 int err = email_get_mailbox_by_mailbox_id(folder_id, &mailbox);
762 if (EMAIL_ERROR_NONE != err || NULL == mailbox) {
763 LoggerE("Couldn't get mailbox, error code: %d (%s)", err, get_error_message(err));
764 m_proxy_sync->removeCallback(op_id);
768 const int limit = callback->getLimit();
772 slot_size = m_slot_size;
777 err = email_set_mail_slot_size(0, 0, slot_size);
778 if (EMAIL_ERROR_NONE != err) {
779 LoggerE("Email set slot size failed, %d (%s)", err, get_error_message(err));
782 const int account_id = callback->getAccountId();
784 err = email_sync_header(account_id, mailbox->mailbox_id, &op_handle);
786 if (EMAIL_ERROR_NONE != err) {
787 LoggerE("Email sync header failed, %d (%s)", err, get_error_message(err));
788 m_proxy_sync->removeCallback(op_id);
790 callback->setOperationHandle(op_handle);
794 if (NULL != mailbox) {
795 err = email_free_mailbox(&mailbox, 1);
796 if (EMAIL_ERROR_NONE != err) {
797 LoggerE("Failed to email_free_mailbox - err: %d (%s)", err, get_error_message(err));
803 //#################################### ^syncFolder #############################
805 //################################## stopSync: #################################
807 void EmailManager::stopSync(long op_id) {
810 SyncCallbackData* callback = static_cast<SyncCallbackData*>(m_proxy_sync->getCallback(op_id));
813 LoggerE("Callback is null");
817 int err = email_cancel_job(callback->getAccountId(), callback->getOperationHandle(),
818 EMAIL_CANCELED_BY_USER);
820 if (EMAIL_ERROR_NONE != err) {
821 LoggerE("Email cancel job failed, %d (%s)", err, get_error_message(err));
824 callback->SetError(LogAndCreateResult(ErrorCode::ABORT_ERR, "Sync aborted by user"));
827 m_proxy_sync->removeCallback(op_id);
830 //################################## ^stopSync #################################
832 void EmailManager::RemoveSyncCallback(long op_id) {
834 m_proxy_sync->removeCallback(op_id);
837 void EmailManager::RemoveCallbacksByQueue(const PostQueue& q) {
840 for (auto it = m_sendRequests.begin(); it != m_sendRequests.end();) {
841 if (it->second->HasQueue(q)) {
843 m_sendRequests.erase(it++);
850 void removeEmailCompleteCB(MessagesCallbackUserData* callback) {
853 LoggerE("Callback is null");
857 if (callback->IsError()) {
858 LoggerD("Calling error callback");
861 LoggerD("Calling success callback");
863 callback->SetSuccess();
872 EmailManager::DeleteReqVector::iterator EmailManager::getDeleteRequest(
873 const std::vector<int>& ids) {
875 for (auto idIt = ids.begin(); idIt != ids.end(); ++idIt) {
876 for (auto reqIt = m_deleteRequests.begin(); reqIt != m_deleteRequests.end(); ++reqIt) {
877 MessagePtrVector msgs = reqIt->callback->getMessages();
878 for (auto msgIt = msgs.begin(); msgIt != msgs.end(); ++msgIt) {
879 if ((*msgIt)->getId() == *idIt) {
885 return m_deleteRequests.end();
888 void EmailManager::removeStatusCallback(const std::vector<int>& ids,
889 email_noti_on_storage_event status) {
891 std::lock_guard<std::mutex> lock(m_mutex);
892 DeleteReqVector::iterator it = getDeleteRequest(ids);
893 if (it != m_deleteRequests.end()) {
894 LoggerD("Found request");
895 if (NOTI_MAIL_DELETE_FINISH == status) {
896 LoggerD("Successfully removed %zu mails", ids.size());
897 it->messagesDeleted += ids.size();
899 MessagesCallbackUserData* callback = it->callback;
900 if (NOTI_MAIL_DELETE_FAIL == status) {
901 callback->SetError(LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Messages remove failed"));
903 // if one of mails failed, call error callback
904 // if all mails are deleted, call success.
905 // >= is used in case of duplicated dbus messages
906 if (NOTI_MAIL_DELETE_FAIL == status ||
907 static_cast<unsigned int>(it->messagesDeleted) >= it->callback->getMessages().size()) {
908 LoggerD("Calling callback");
909 m_deleteRequests.erase(it);
911 removeEmailCompleteCB(callback);
913 LoggerD("Not all messages are removed, waiting for next callback");
916 LoggerD("Request not found, ignoring");
920 PlatformResult EmailManager::RemoveMessagesPlatform(MessagesCallbackUserData* callback) {
923 email_mail_data_t* mail = NULL;
925 std::lock_guard<std::mutex> lock(m_mutex);
926 std::vector<std::shared_ptr<Message>> messages = callback->getMessages();
927 MessageType type = callback->getMessageServiceType();
928 for (auto it = messages.begin(); it != messages.end(); ++it) {
929 if ((*it)->getType() != type) {
930 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Error while deleting email",
931 ("Invalid message type %d", (*it)->getType()));
934 for (auto it = messages.begin(); it != messages.end(); ++it) {
935 error = email_get_mail_data((*it)->getId(), &mail);
936 if (EMAIL_ERROR_NONE != error) {
937 return LogAndCreateResult(
938 ErrorCode::UNKNOWN_ERR, "Error while deleting mail",
939 ("Couldn't retrieve mail data: %d (%s)", error, get_error_message(error)));
942 // This task (_EMAIL_API_DELETE_MAIL) is for async
943 error = email_delete_mail(mail->mailbox_id, &mail->mail_id, 1, 0);
944 if (EMAIL_ERROR_NONE != error) {
945 email_free_mail_data(&mail, 1);
946 return LogAndCreateResult(
947 ErrorCode::UNKNOWN_ERR, "Error while deleting mail",
948 ("email_delete_mail error: %d (%s)", error, get_error_message(error)));
950 email_free_mail_data(&mail, 1);
952 // store delete request and wait for dbus response
954 request.callback = callback;
955 request.messagesDeleted = 0;
956 m_deleteRequests.push_back(request);
958 return PlatformResult(ErrorCode::NO_ERROR);
961 void EmailManager::removeMessages(MessagesCallbackUserData* callback) {
965 LoggerE("Callback is null");
969 PlatformResult ret = RemoveMessagesPlatform(callback);
971 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
972 callback->SetError(ret);
973 removeEmailCompleteCB(callback);
977 PlatformResult EmailManager::UpdateMessagesPlatform(MessagesCallbackUserData* callback) {
980 email_mail_data_t* mail = NULL;
983 email_free_mail_data(&mail, 1);
988 std::lock_guard<std::mutex> lock(m_mutex);
989 std::vector<std::shared_ptr<Message>> messages = callback->getMessages();
990 std::list<std::shared_ptr<Message>> firstInThreadAndHasAttachment;
991 MessageType type = callback->getMessageServiceType();
992 for (auto it = messages.begin(); it != messages.end(); ++it) {
993 if ((*it)->getType() != type) {
994 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Error while updating message",
995 ("Invalid message type: %d", (*it)->getType()));
998 for (auto it = messages.begin(); it != messages.end(); ++it) {
999 PlatformResult ret = Message::convertPlatformEmail((*it), &mail);
1000 if (ret.IsError()) return ret;
1002 if ((*it)->getHasAttachment()) {
1003 if (isFirstInThread(it->get())) {
1004 // Messages with attachments, that are first in their threads are added
1005 // to the container, to be processed later in this function
1006 firstInThreadAndHasAttachment.push_back(*it);
1008 // Update of mail on server using function email_update_mail() is not possible.
1009 // Attachment is updated only locally (can't be later loaded from server),
1010 // so use of workaround is needed:
1012 // 2. delete old mail
1014 // adding message again after changes
1015 PlatformResult ret = addDraftMessagePlatform(mail->account_id, (*it));
1016 if (ret.IsError()) {
1019 LoggerD("mail added - new id = [%d]\n", (*it)->getId());
1021 // storing old message id
1022 (*it)->setOldId(mail->mail_id);
1023 // deleting old mail
1024 error = email_delete_mail(mail->mailbox_id, &mail->mail_id, 1, 1);
1025 LoggerD("mail deleted = [%d]\n", mail->mail_id);
1026 if (EMAIL_ERROR_NONE != error) {
1027 return LogAndCreateResult(
1028 ErrorCode::UNKNOWN_ERR, "Error while deleting old mail on update",
1029 ("email_delete_mail error: %d (%s)", error, get_error_message(error)));
1032 LoggerD("There are no attachments, updating only email data.");
1033 error = email_update_mail(mail, NULL, 0, NULL, 0);
1034 if (EMAIL_ERROR_NONE != error) {
1035 return LogAndCreateResult(
1036 ErrorCode::UNKNOWN_ERR, "Error while updating mail",
1037 ("email_update_mail error: %d (%s)", error, get_error_message(error)));
1042 if (firstInThreadAndHasAttachment.size() == 0) {
1043 return PlatformResult(ErrorCode::NO_ERROR);
1047 * If a message with attachment, that is first in its thread was updated,
1048 * the update process consists of adding a new message - updated version
1049 * of the original and removing the original.
1051 * After the original mail is deleted, the thread_id of the updated version
1052 * changes - it is now identical to the mail_id of the new message.
1053 * The change should be reflected in the updated message, sent to the JS
1054 * layer. The code below sets proper thread_id for such messages and unsets
1055 * inResponseTo fields.
1057 * A few retries with sleeps between them are performed, to ensure, that after
1058 * this function returns, messages returned to the JS layer and
1059 * corresponding records in the mail DB have the same thread_ids. Due to a
1060 * delay between calling email_delete_mail function and the update of records
1061 * in the database, some time is needed.
1063 email_mail_data_t* mail_data{nullptr};
1065 const int MAX_RETRIES = 5;
1066 for (; retry < MAX_RETRIES; ++retry) {
1067 std::this_thread::sleep_for(100ms);
1068 auto it = firstInThreadAndHasAttachment.begin();
1069 while (it != firstInThreadAndHasAttachment.end()) {
1070 error = email_get_mail_data((*it)->getId(), &mail_data);
1071 if (EMAIL_ERROR_NONE != error) {
1072 return LogAndCreateResult(
1073 ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox",
1074 ("email_get_mail_data error: %d (%s)", error, get_error_message(error)));
1077 if (isFirstInThread(mail_data)) {
1078 (*it)->setConversationId(mail_data->thread_id);
1079 (*it)->unsetInResponseTo();
1080 it = firstInThreadAndHasAttachment.erase(it);
1085 int free_error = email_free_mail_data(&mail_data, 1);
1086 if (EMAIL_ERROR_NONE != free_error) {
1087 LoggerW("email_free_mail_data error: %d, %s", free_error, get_error_message(free_error));
1091 if (firstInThreadAndHasAttachment.size() == 0) {
1092 LoggerD("Message updating process finished after %d retries", retry);
1096 LoggerD("Message update retry number %d finished", retry);
1099 if (MAX_RETRIES == retry) {
1100 LoggerW("Message updating process not finished after %d retries.", retry);
1101 for (auto& message : firstInThreadAndHasAttachment) {
1102 LoggerD("Setting proper conversationId manually, message id: %d", message->getId());
1103 message->setConversationId(message->getId());
1104 message->unsetInResponseTo();
1108 return PlatformResult(ErrorCode::NO_ERROR);
1111 void EmailManager::updateMessages(MessagesCallbackUserData* callback) {
1115 LoggerE("Callback is null");
1119 PlatformResult ret = UpdateMessagesPlatform(callback);
1120 if (ret.IsError()) {
1121 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1122 callback->SetError(ret);
1125 if (callback->IsError()) {
1126 LoggerD("Calling error callback");
1128 LoggerD("Calling success callback");
1130 picojson::array array;
1131 auto messages = callback->getMessages();
1132 size_t messages_size = messages.size();
1133 for (size_t i = 0; i < messages_size; ++i) {
1134 array.push_back(MessagingUtil::messageToJson(messages[i]));
1137 callback->SetSuccess(picojson::value(array));
1146 PlatformResult EmailManager::FindMessagesPlatform(FindMsgCallbackUserData* callback) {
1148 email_mail_data_t* mailList = NULL;
1149 int mailListCount = 0;
1152 if (mailListCount > 0 && mailList != NULL) {
1153 if (EMAIL_ERROR_NONE != email_free_mail_data(&mailList, mailListCount)) {
1154 LoggerW("Failed to free mailList");
1159 std::lock_guard<std::mutex> lock(m_mutex);
1160 std::pair<int, email_mail_data_t*> emails;
1161 PlatformResult ret = MessagingDatabaseManager::getInstance().findEmails(callback, &emails);
1162 if (ret.IsError()) return ret;
1163 mailListCount = emails.first;
1164 LoggerD("Found %d mails", mailListCount);
1166 mailList = emails.second;
1167 email_mail_data_t* nth_email = mailList;
1169 for (int i = 0; i < mailListCount; ++i) {
1170 std::shared_ptr<Message> email;
1171 ret = Message::convertPlatformEmailToObject(*nth_email, &email);
1172 if (ret.IsError()) return ret;
1173 callback->addMessage(email);
1176 return PlatformResult(ErrorCode::NO_ERROR);
1179 void EmailManager::findMessages(FindMsgCallbackUserData* callback) {
1183 LoggerE("Callback is null");
1187 PlatformResult ret = FindMessagesPlatform(callback);
1188 if (ret.IsError()) {
1189 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1190 callback->SetError(ret);
1194 LoggerD("callback: %p error: %d messages.size() = %zu", callback, callback->IsError(),
1195 callback->getMessages().size());
1197 if (callback->IsError()) {
1198 LoggerD("Calling error callback");
1201 LoggerD("Calling success callback");
1202 std::vector<picojson::value> response;
1203 auto messages = callback->getMessages();
1204 std::for_each(messages.begin(), messages.end(), [&response](MessagePtr& message) {
1205 response.push_back(MessagingUtil::messageToJson(message));
1208 callback->SetSuccess(picojson::value(response));
1217 PlatformResult EmailManager::FindConversationsPlatform(ConversationCallbackData* callback) {
1220 std::lock_guard<std::mutex> lock(m_mutex);
1221 std::vector<std::shared_ptr<MessageConversation>> conversationsInfo;
1222 PlatformResult ret =
1223 MessagingDatabaseManager::getInstance().findEmailConversations(*callback, &conversationsInfo);
1224 if (ret.IsError()) return ret;
1226 LoggerD("Found %zu conversations", conversationsInfo.size());
1228 for (const auto& conversation : conversationsInfo) {
1229 callback->addConversation(conversation);
1232 return PlatformResult(ErrorCode::NO_ERROR);
1235 void EmailManager::findConversations(ConversationCallbackData* callback) {
1239 LoggerE("Callback is null");
1243 PlatformResult ret = FindConversationsPlatform(callback);
1244 if (ret.IsError()) {
1245 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1246 callback->SetError(ret);
1250 LoggerD("callback: %p error:%d conversations.size()=%zu", callback, callback->IsError(),
1251 callback->getConversations().size());
1253 if (callback->IsError()) {
1254 LoggerD("Calling error callback");
1256 LoggerD("Calling success callback");
1258 std::vector<picojson::value> response;
1259 auto messages = callback->getConversations();
1260 std::for_each(messages.begin(), messages.end(),
1261 [&response](std::shared_ptr<MessageConversation>& conversation) {
1262 response.push_back(MessagingUtil::conversationToJson(conversation));
1265 callback->SetSuccess(picojson::value(response));
1274 long EmailManager::getUniqueOpId() {
1276 // mutex is created only on first call (first call added to constructor
1277 // to initialize mutex correctly)
1278 static std::mutex op_id_mutex;
1279 std::lock_guard<std::mutex> lock(op_id_mutex);
1280 static long op_id = 0;
1284 PlatformResult EmailManager::FindFoldersPlatform(FoldersCallbackData* callback) {
1286 int ret = EMAIL_ERROR_UNKNOWN;
1287 int account_id = ACCOUNT_ID_NOT_INITIALIZED;
1288 email_mailbox_t* mailboxes = NULL;
1289 email_mailbox_t* nth_mailbox = NULL;
1290 int mailboxes_count;
1293 if (mailboxes != NULL) {
1294 if (EMAIL_ERROR_NONE != email_free_mailbox(&mailboxes, mailboxes_count)) {
1295 LoggerW("Free mailboxes failed: %d", ret);
1300 std::lock_guard<std::mutex> lock(m_mutex);
1302 tizen::AbstractFilterPtr filter = callback->getFilter();
1304 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Filter not provided");
1307 for (FilterIterator it(filter); false == it.isEnd(); it++) {
1308 if (FIS_COMPOSITE_START == it.getState()) {
1309 CompositeFilterPtr cf = castToCompositeFilter((*it));
1310 if (cf && INTERSECTION != cf->getType()) {
1311 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "Invalid Filter Type",
1312 ("Invalid Filter type: %d", cf->getType()));
1314 } else if (FIS_ATTRIBUTE_FILTER == it.getState()) {
1315 AttributeFilterPtr attrf = castToAttributeFilter((*it));
1317 const std::string attr_name = attrf->getAttributeName();
1318 if (FIND_FOLDERS_ATTRIBUTE_ACCOUNTID_NAME == attr_name) {
1319 account_id = static_cast<int>(attrf->getMatchValue()->toLong());
1321 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "The attribute name is invalid",
1322 ("The attribute name: %s is invalid", attr_name.c_str()));
1328 LoggerD("Listing folders for account ID: %d", account_id);
1329 if (account_id > 0) {
1330 ret = email_get_mailbox_list(account_id, -1, &mailboxes, &mailboxes_count);
1331 if (EMAIL_ERROR_NONE != ret || !mailboxes) {
1332 return LogAndCreateResult(
1333 ErrorCode::UNKNOWN_ERR, "Platform error, cannot get folders",
1334 ("email_get_mailbox_list error: %d (%s)", ret, get_error_message(ret)));
1337 if (mailboxes_count <= 0) {
1338 LoggerD("Empty mailboxes");
1340 LoggerD("Founded mailboxes: %d", mailboxes_count);
1342 nth_mailbox = mailboxes;
1343 for (int i = 0; i < mailboxes_count; ++i) {
1344 std::shared_ptr<MessageFolder> fd;
1345 fd = std::make_shared<MessageFolder>(*nth_mailbox);
1346 callback->addFolder(fd);
1351 return PlatformResult(ErrorCode::NO_ERROR);
1354 void EmailManager::findFolders(FoldersCallbackData* callback) {
1358 LoggerE("Callback is null");
1362 PlatformResult ret = FindFoldersPlatform(callback);
1363 if (ret.IsError()) {
1364 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1365 callback->SetError(ret);
1369 LoggerD("callback: %p error:%d folders.size()=%zu", callback, callback->IsError(),
1370 callback->getFolders().size());
1372 if (callback->IsError()) {
1373 LoggerD("Calling error callback");
1375 LoggerD("Calling success callback");
1377 std::vector<picojson::value> response;
1378 auto folders = callback->getFolders();
1379 std::for_each(folders.begin(), folders.end(),
1380 [&response](std::shared_ptr<MessageFolder>& folder) {
1381 response.push_back(MessagingUtil::folderToJson(folder));
1384 callback->SetSuccess(picojson::value(response));
1393 PlatformResult EmailManager::RemoveConversationsPlatform(ConversationCallbackData* callback) {
1396 std::lock_guard<std::mutex> lock(m_mutex);
1397 std::vector<std::shared_ptr<MessageConversation>> conversations = callback->getConversations();
1398 MessageType type = callback->getMessageServiceType();
1401 for (auto it = conversations.begin(); it != conversations.end(); ++it) {
1402 if ((*it)->getType() != type) {
1403 return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR,
1404 "Error while deleting email conversation",
1405 ("Invalid message type %d", (*it)->getType()));
1409 for (auto it = conversations.begin(); it != conversations.end(); ++it) {
1410 thread_id = (*it)->getConversationId();
1411 error = email_delete_thread(thread_id, false);
1412 if (EMAIL_ERROR_NONE != error) {
1413 return LogAndCreateResult(
1414 ErrorCode::TYPE_MISMATCH_ERR, "Error while deleting email conversation",
1415 ("Couldn't delete email conversation data %d (%s)", error, get_error_message(error)));
1418 // for now, there is no way to recognize deleting email thread job is completed.
1419 // so use polling to wait the thread is removed.
1420 email_mail_data_t* thread_info = NULL;
1422 struct timespec sleep_time = {0, 300L * 1000L * 1000L};
1423 nanosleep(&sleep_time, nullptr);
1424 LoggerD("Waiting to delete this email thread...");
1425 error = email_get_thread_information_by_thread_id(thread_id, &thread_info);
1427 if (thread_info != NULL) {
1431 } while (error != EMAIL_ERROR_MAIL_NOT_FOUND);
1433 return PlatformResult(ErrorCode::NO_ERROR);
1436 void EmailManager::removeConversations(ConversationCallbackData* callback) {
1440 LoggerE("Callback is null");
1444 PlatformResult ret = RemoveConversationsPlatform(callback);
1445 if (ret.IsError()) {
1446 LoggerE("%d (%s)", static_cast<int>(ret.error_code()), (ret.message()).c_str());
1447 callback->SetError(ret);
1450 if (callback->IsError()) {
1451 LoggerD("Calling error callback");
1453 LoggerD("Calling success callback");
1455 callback->SetSuccess();
1464 std::string EmailManager::getMessageStatus(int id) {
1467 email_mail_data_t* mail = nullptr;
1468 MessageStatus status = MessageStatus::STATUS_UNDEFINED;
1470 int ret = email_get_mail_data(id, &mail);
1471 if (EMAIL_ERROR_NONE != ret || !mail) {
1472 LoggerD("Failed to get data %d (%s)", ret, get_error_message(ret));
1476 switch (mail->save_status) {
1477 case EMAIL_MAIL_STATUS_SENT:
1478 status = MessageStatus::STATUS_SENT;
1480 case EMAIL_MAIL_STATUS_SENDING:
1481 status = MessageStatus::STATUS_SENDING;
1483 case EMAIL_MAIL_STATUS_SAVED:
1484 status = MessageStatus::STATUS_DRAFT;
1486 case EMAIL_MAIL_STATUS_SEND_FAILURE:
1487 status = MessageStatus::STATUS_FAILED;
1490 status = MessageStatus::STATUS_UNDEFINED;
1494 ret = email_free_mail_data(&mail, 1);
1495 if (EMAIL_ERROR_NONE != ret) {
1496 LoggerD("Failed to free mail data %d (%s)", ret, get_error_message(ret));
1499 return MessagingUtil::messageStatusToString(status);