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 "common/logger.h"
18 #include "common/platform_exception.h"
20 #include "MsgCommon/AbstractFilter.h"
22 #include "message_conversation.h"
23 #include "messaging_util.h"
25 #define MAX_THREAD_DATA_LEN 128
27 using common::ErrorCode;
28 using common::PlatformResult;
32 using extension::messaging::AnyPtr;
33 using common::AttributeMatchFlag;
34 using namespace extension::messaging::FilterUtils;
36 bool ContainsAllRequiredRecipients(AnyPtr match_value, std::vector<std::string> recipients,
37 const AttributeMatchFlag match_flag) {
39 if (!match_value->getValue().is<picojson::array>()) {
40 LoggerE("AttributeFilter value for TO attribute must be an array of strings.");
44 const auto array = match_value->getValue().get<picojson::array>();
45 for (const auto& value : array) {
46 if (!value.is<std::string>()) {
47 LoggerE("AttributeFilter value for TO attribute must be an array of strings.");
51 if (!isAnyStringMatching(value.get<std::string>(), recipients, match_flag)) {
52 LoggerD("AttributeFilter TO match failed on %s", value.get<std::string>().c_str());
65 using namespace tizen;
68 MessageConversation::MessageConversation()
69 : m_conversation_id(-1),
70 m_conversation_type(UNDEFINED),
75 m_last_message_id(-1) {
76 ScopeLogger("Message Conversation constructor.");
79 MessageConversation::MessageConversation(const email_mail_data_t& mail_data)
80 : m_conversation_id(mail_data.thread_id),
81 m_conversation_type(MessageType::EMAIL),
82 m_timestamp(mail_data.date_time),
83 m_count(mail_data.thread_item_count),
85 m_preview(MessagingUtil::SanitizeUtf8String(mail_data.preview_text)),
86 m_conversation_subject(MessagingUtil::SanitizeUtf8String(mail_data.subject)),
87 m_is_read(mail_data.flags_seen_field),
88 m_from(MessagingUtil::extractSingleEmailAddress(mail_data.full_address_from)),
89 m_to(Message::getEmailRecipientsFromStruct(mail_data.full_address_to)),
90 m_cc(Message::getEmailRecipientsFromStruct(mail_data.full_address_cc)),
91 m_bcc(Message::getEmailRecipientsFromStruct(mail_data.full_address_bcc)),
92 m_last_message_id(mail_data.mail_id) {
94 auto result = initializeUnreadMessagesCount();
95 if (result.IsError()) {
96 LoggerE("Could not initialize unreadMessages. conversation id: %d", m_conversation_id);
100 MessageConversation::~MessageConversation() {
101 ScopeLogger("Message Conversation destructor.");
103 // *** attributes getters
104 int MessageConversation::getConversationId() const {
105 return m_conversation_id;
108 MessageType MessageConversation::getType() const {
109 return m_conversation_type;
112 std::string MessageConversation::getTypeString() const {
113 return MessagingUtil::messageTypeToString(getType());
116 time_t MessageConversation::getTimestamp() const {
120 unsigned long MessageConversation::getMessageCount() const {
124 unsigned long MessageConversation::getUnreadMessages() const {
125 return m_unread_messages;
128 std::string MessageConversation::getPreview() const {
132 std::string MessageConversation::getSubject() const {
133 return m_conversation_subject;
136 bool MessageConversation::getIsRead() const {
140 std::string MessageConversation::getFrom() const {
144 std::vector<std::string> MessageConversation::getTo() const {
148 std::vector<std::string> MessageConversation::getCC() const {
152 std::vector<std::string> MessageConversation::getBCC() const {
156 int MessageConversation::getLastMessageId() const {
157 return m_last_message_id;
160 PlatformResult MessageConversation::convertMsgConversationToObject(
161 unsigned int threadId, msg_handle_t handle, std::shared_ptr<MessageConversation>* result) {
163 std::shared_ptr<MessageConversation> conversation(new MessageConversation());
165 msg_struct_t msgInfo = NULL;
166 msg_struct_t sendOpt = NULL;
168 msg_struct_t msg_thread = NULL;
170 msg_struct_list_s convViewList;
171 msg_list_handle_t addr_list = NULL;
172 msg_struct_t addr_info = NULL;
177 unsigned int lastMsgIndex = 0;
178 char msgData[MAX_THREAD_DATA_LEN] = {
182 msgInfo = msg_create_struct(MSG_STRUCT_MESSAGE_INFO);
183 std::unique_ptr<std::remove_pointer<msg_struct_t*>::type, int (*)(msg_struct_t*)> msg_info_ptr(
184 &msgInfo, &msg_release_struct);
185 // automatically release the memory
186 sendOpt = msg_create_struct(MSG_STRUCT_SENDOPT);
187 std::unique_ptr<std::remove_pointer<msg_struct_t*>::type, int (*)(msg_struct_t*)> send_opt_ptr(
188 &sendOpt, &msg_release_struct);
189 // automatically release the memory
191 conversation->m_conversation_id = threadId;
193 msg_thread = msg_create_struct(MSG_STRUCT_THREAD_INFO);
194 std::unique_ptr<std::remove_pointer<msg_struct_t*>::type, int (*)(msg_struct_t*)> msg_thread_ptr(
195 &msg_thread, &msg_release_struct);
196 // automatically release the memory
197 msg_error_t err = msg_get_thread(handle, conversation->m_conversation_id, msg_thread);
198 if (err != MSG_SUCCESS) {
199 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to retrieve thread.",
200 ("msg_get_thread error: %d (%s)", err, get_error_message(err)));
202 msg_get_int_value(msg_thread, MSG_THREAD_MSG_TYPE_INT, &tempInt);
205 case MSG_TYPE_SMS_CB:
206 case MSG_TYPE_SMS_JAVACB:
207 case MSG_TYPE_SMS_WAPPUSH:
208 case MSG_TYPE_SMS_MWI:
209 case MSG_TYPE_SMS_SYNCML:
210 case MSG_TYPE_SMS_REJECT:
211 conversation->m_conversation_type = SMS;
214 case MSG_TYPE_MMS_JAVA:
215 case MSG_TYPE_MMS_NOTI:
216 conversation->m_conversation_type = MMS;
220 msg_get_int_value(msg_thread, MSG_THREAD_MSG_TIME_INT, &tempInt);
221 conversation->m_timestamp = tempInt;
223 msg_get_int_value(msg_thread, MSG_THREAD_UNREAD_COUNT_INT, &tempInt);
224 conversation->m_unread_messages = tempInt;
226 msg_get_str_value(msg_thread, MSG_THREAD_MSG_DATA_STR, msgData, MAX_THREAD_DATA_LEN);
228 conversation->setPreview(msgData);
230 err = msg_get_conversation_view_list(handle, conversation->m_conversation_id, &convViewList);
231 std::unique_ptr<std::remove_pointer<msg_struct_list_s*>::type, int (*)(msg_struct_list_s*)>
232 conv_view_list_ptr(&convViewList, &msg_release_list_struct);
233 // automatically release the memory
234 if (err != MSG_SUCCESS) {
235 return LogAndCreateResult(
236 ErrorCode::UNKNOWN_ERR, "Get conversation(msg) view list fail.",
237 ("msg_get_conversation_view_list error: %d (%s)", err, get_error_message(err)));
240 lastMsgIndex = convViewList.nCount - 1;
241 conversation->m_count = convViewList.nCount;
243 msg_get_bool_value(convViewList.msg_struct_info[lastMsgIndex], MSG_CONV_MSG_READ_BOOL, &tempBool);
244 conversation->m_is_read = tempBool;
246 msg_get_int_value(convViewList.msg_struct_info[lastMsgIndex], MSG_CONV_MSG_ID_INT, &tempInt);
247 conversation->m_last_message_id = tempInt;
249 err = msg_get_message(handle, conversation->m_last_message_id, msgInfo, sendOpt);
250 if (err != MSG_SUCCESS) {
251 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "get message fail.",
252 ("msg_get_message error: %d (%s)", err, get_error_message(err)));
255 msg_get_int_value(convViewList.msg_struct_info[lastMsgIndex], MSG_CONV_MSG_DIRECTION_INT,
258 msg_get_list_handle(msgInfo, MSG_MESSAGE_ADDR_LIST_HND, (void**)&addr_list);
259 nToCnt = msg_list_length(addr_list);
261 if (MSG_DIRECTION_TYPE_MT == tempInt) {
262 if (nToCnt > 0 && nToCnt < MAX_TO_ADDRESS_CNT) {
263 char strNumber[MAX_ADDRESS_VAL_LEN] = {
266 addr_info = (msg_struct_t)msg_list_nth_data(addr_list, nToCnt - 1);
267 msg_get_str_value(addr_info, MSG_ADDRESS_INFO_ADDRESS_VALUE_STR, strNumber,
268 MAX_ADDRESS_VAL_LEN);
270 if (strNumber[0] != '\0') {
271 conversation->m_from = strNumber;
273 LoggerD("address is null ");
276 LoggerD("address count index fail");
279 if (nToCnt > 0 && nToCnt < MAX_TO_ADDRESS_CNT) {
280 for (int index = 0; index < nToCnt; index++) {
281 addr_info = (msg_struct_t)msg_list_nth_data(addr_list, index);
282 char strNumber[MAX_ADDRESS_VAL_LEN] = {
285 msg_get_str_value(addr_info, MSG_ADDRESS_INFO_ADDRESS_VALUE_STR, strNumber,
286 MAX_ADDRESS_VAL_LEN);
288 conversation->m_to.push_back(strNumber);
291 LoggerD("address fetch fail");
295 char strTemp[MAX_SUBJECT_LEN] = {0};
296 msg_get_str_value(msgInfo, MSG_MESSAGE_SUBJECT_STR, strTemp, MAX_SUBJECT_LEN);
298 conversation->setSubject(strTemp);
300 *result = conversation;
301 return PlatformResult(ErrorCode::NO_ERROR);
304 PlatformResult MessageConversation::convertEmailConversationToObject(
305 unsigned int threadId, std::shared_ptr<MessageConversation>* result) {
307 std::shared_ptr<MessageConversation> conversation(new MessageConversation());
309 email_mail_list_item_t* resultMail = NULL;
311 int ntv_ret = email_get_thread_information_ex(threadId, &resultMail);
312 if (ntv_ret != EMAIL_ERROR_NONE) {
314 if (resultMail->eas_data) {
315 free(resultMail->eas_data);
320 return LogAndCreateResult(
321 ErrorCode::UNKNOWN_ERR, "Couldn't get conversation.",
322 ("email_get_thread_information_ex error: %d (%s)", ntv_ret, get_error_message(ntv_ret)));
325 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Get email data fail.", ("Data is null"));
328 email_mail_data_t* mailData = NULL;
330 ntv_ret = email_get_mail_data(resultMail->mail_id, &mailData);
331 if (ntv_ret != EMAIL_ERROR_NONE) {
333 return LogAndCreateResult(
334 ErrorCode::UNKNOWN_ERR, "Get email data fail.",
335 ("email_get_mail_data error: %d (%s)", ntv_ret, get_error_message(ntv_ret)));
340 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Get email data fail.");
345 conversation->m_unread_messages = 0;
346 email_mail_list_item_t* mailList = NULL;
349 email_get_mail_list(mailData->account_id, 0, threadId, 0, resultMail->thread_item_count,
350 EMAIL_SORT_DATETIME_HIGH, &mailList, &count);
351 if (ntv_ret != EMAIL_ERROR_NONE) {
352 email_free_mail_data(&mailData, 1);
354 return LogAndCreateResult(
355 ErrorCode::UNKNOWN_ERR, "Get email data list fail.",
356 ("email_get_mail_list error: %d (%s)", ntv_ret, get_error_message(ntv_ret)));
359 for (index = 0; index < count; index++) {
360 if (mailList[index].flags_seen_field) {
361 conversation->m_unread_messages++;
364 conversation->m_count = resultMail->thread_item_count;
366 conversation->m_conversation_id = threadId;
368 conversation->m_conversation_type = MessageType::EMAIL;
370 conversation->m_timestamp = resultMail->date_time;
372 if (resultMail->preview_text[0] != '\0') {
373 conversation->setPreview(resultMail->preview_text);
376 if (resultMail->subject[0] != '\0') {
377 conversation->setSubject(resultMail->subject);
380 conversation->m_is_read = (bool)resultMail->flags_seen_field;
382 if (resultMail->full_address_from[0] != '\0') {
383 conversation->m_from =
384 MessagingUtil::extractSingleEmailAddress(resultMail->full_address_from);
387 conversation->m_to = Message::getEmailRecipientsFromStruct(mailData->full_address_to);
388 conversation->m_cc = Message::getEmailRecipientsFromStruct(mailData->full_address_cc);
389 conversation->m_bcc = Message::getEmailRecipientsFromStruct(mailData->full_address_bcc);
391 conversation->m_last_message_id = resultMail->mail_id;
393 if (mailData != NULL) {
394 email_free_mail_data(&mailData, 1);
398 if (resultMail != NULL) {
402 *result = conversation;
403 return PlatformResult(ErrorCode::NO_ERROR);
406 void MessageConversation::setConversationId(int id) {
407 m_conversation_id = id;
410 void MessageConversation::setType(MessageType type) {
411 m_conversation_type = type;
414 void MessageConversation::setTimestamp(time_t timestamp) {
415 m_timestamp = timestamp;
418 void MessageConversation::setMessageCount(int count) {
422 void MessageConversation::setUnreadMessages(int unread_messages) {
423 m_unread_messages = unread_messages;
426 void MessageConversation::setPreview(std::string preview) {
427 m_preview = MessagingUtil::SanitizeUtf8String(preview);
430 void MessageConversation::setSubject(std::string conversation_subject) {
431 m_conversation_subject = MessagingUtil::SanitizeUtf8String(conversation_subject);
434 void MessageConversation::setIsRead(bool is_read) {
438 void MessageConversation::setFrom(std::string from) {
442 void MessageConversation::setTo(std::vector<std::string>& to) {
446 void MessageConversation::setCC(std::vector<std::string>& cc) {
450 void MessageConversation::setBCC(std::vector<std::string>& bcc) {
454 void MessageConversation::setLastMessageId(int last_message_id) {
455 m_last_message_id = last_message_id;
460 * Attribute | Attribute filter| Attribute range filter
461 * | supported | supported
462 * ----------------+-----------------+------------------------
465 * timestamp | No | Yes
466 * messageCount | Yes | Yes
467 * unreadMessages | Yes | Yes
475 * lastMessageId | Yes | No
477 bool MessageConversation::isMatchingAttribute(const std::string& attribute_name,
478 const common::AttributeMatchFlag match_flag,
479 AnyPtr match_value) const {
481 LoggerD("attribute_name: (%s), match_flag: (%s), match_value: (%s)", attribute_name.c_str(),
482 common::AttributeMatchFlagToString(match_flag).c_str(), match_value->toString().c_str());
484 if (common::AttributeMatchFlag::kExists == match_flag) {
485 return m_message_conversation_filterable_attributes.find(attribute_name) !=
486 m_message_conversation_filterable_attributes.end();
489 /* Some attributes are converted to strings before passing to isMatching,
490 because they are strings in the JS Web API */
491 if (MESSAGE_CONVERSATION_ATTRIBUTE_ID == attribute_name) {
492 return FilterUtils::isMatching(*match_value, std::to_string(getConversationId()), match_flag);
493 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_TYPE == attribute_name) {
494 return FilterUtils::isMatching(*match_value, getTypeString(), match_flag);
495 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_MESSAGE_COUNT == attribute_name) {
496 return FilterUtils::isMatching(*match_value, getMessageCount(), match_flag);
497 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_UNREAD_MESSAGES == attribute_name) {
498 return FilterUtils::isMatching(*match_value, getUnreadMessages(), match_flag);
499 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_PREVIEW == attribute_name) {
500 return FilterUtils::isMatching(*match_value, getPreview(), match_flag);
501 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_SUBJECT == attribute_name) {
502 return FilterUtils::isMatching(*match_value, getSubject(), match_flag);
503 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_IS_READ == attribute_name) {
504 return FilterUtils::isMatching(*match_value, getIsRead(), match_flag);
505 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_FROM == attribute_name) {
506 return FilterUtils::isMatching(*match_value, getFrom(), match_flag);
507 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_TO == attribute_name) {
508 if (match_value->getValue().is<picojson::array>()) {
509 return ContainsAllRequiredRecipients(match_value, getTo(), match_flag);
510 } else if (match_value->getValue().is<std::string>()) {
511 return FilterUtils::isAnyStringMatching(match_value->toString(), getTo(), match_flag);
513 LoggerE("Unsupported type of AttributeFilter value for TO attribute.");
516 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_CC == attribute_name) {
517 return FilterUtils::isAnyStringMatching(match_value->toString(), getCC(), match_flag);
518 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_BCC == attribute_name) {
519 return FilterUtils::isAnyStringMatching(match_value->toString(), getBCC(), match_flag);
520 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_LAST_MESSAGE_ID == attribute_name) {
521 return FilterUtils::isMatching(*match_value, std::to_string(getLastMessageId()), match_flag);
524 LoggerD("attribute: %s is NOT SUPPORTED", attribute_name.c_str());
528 bool MessageConversation::isMatchingAttributeRange(const std::string& attribute_name,
529 AnyPtr initial_value, AnyPtr end_value) const {
530 ScopeLogger("attribute_name: (%s), initial_value: (%s), end_value: (%s)", attribute_name.c_str(),
531 initial_value->toString().c_str(), end_value->toString().c_str());
533 if (MESSAGE_CONVERSATION_ATTRIBUTE_TIMESTAMP == attribute_name) {
534 return FilterUtils::isTimeStampInRange(getTimestamp(), initial_value, end_value);
535 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_MESSAGE_COUNT == attribute_name) {
536 return initial_value->toULong() <= getMessageCount() &&
537 getMessageCount() <= end_value->toULong();
538 } else if (MESSAGE_CONVERSATION_ATTRIBUTE_UNREAD_MESSAGES == attribute_name) {
539 return initial_value->toULong() <= getUnreadMessages() &&
540 getUnreadMessages() <= end_value->toULong();
542 LoggerD("attribute:%s is NOT SUPPORTED", attribute_name.c_str());
547 common::PlatformResult MessageConversation::initializeUnreadMessagesCount() {
550 email_list_filter_t filter_list = {};
551 memset(&filter_list, 0, sizeof(email_list_filter_t));
553 filter_list.list_filter_item_type = EMAIL_LIST_FILTER_ITEM_RULE;
554 filter_list.list_filter_item.rule.rule_type = EMAIL_LIST_FILTER_RULE_EQUAL;
555 filter_list.list_filter_item.rule.target_attribute = EMAIL_MAIL_ATTRIBUTE_THREAD_ID;
556 filter_list.list_filter_item.rule.key_value.integer_type_value = m_conversation_id;
558 int total_messages = 0;
559 int unread_messages = 0;
560 auto result = email_count_mail(&filter_list, 1, &total_messages, &unread_messages);
561 if (EMAIL_ERROR_NONE != result) {
562 return LogAndCreateResult(
563 ErrorCode::UNKNOWN_ERR, "Error while getting data from database.",
564 ("email_count_mail error: %d (%s)", result, get_error_message(result)));
567 m_unread_messages = unread_messages;
569 return PlatformResult{ErrorCode::NO_ERROR};
574 bool defaultConversationComparator(const ConversationPtr&, const ConversationPtr&) {
581 const MessageConversation::MessageConversationComparatorMap
582 MessageConversation::m_message_conversation_comparators = {
584 "id", [] (const ConversationPtr& a, const ConversationPtr& b) {
585 return std::to_string(a->m_conversation_id) < std::to_string(b->m_conversation_id);
589 "timestamp", [] (const ConversationPtr& a, const ConversationPtr& b) {
590 return a->m_timestamp < b->m_timestamp;
594 "messageCount", [] (const ConversationPtr& a, const ConversationPtr& b) {
595 return a->m_count < b->m_count;
599 "unreadMessages", [] (const ConversationPtr& a, const ConversationPtr& b) {
600 return a->m_unread_messages < b->m_unread_messages;
604 "preview", [] (const ConversationPtr& a, const ConversationPtr& b) {
605 return a->m_preview < b->m_preview;
609 "subject", [] (const ConversationPtr& a, const ConversationPtr& b) {
610 return a->m_conversation_subject < b->m_conversation_subject;
614 "isRead", [] (const ConversationPtr& a, const ConversationPtr& b) {
615 return a->m_is_read < b->m_is_read;
619 "from", [] (const ConversationPtr& a, const ConversationPtr& b) {
620 return a->m_from < b->m_from;
624 "to", [] (const ConversationPtr& a, const ConversationPtr& b) {
625 return a->m_to < b->m_to;
629 "cc", [] (const ConversationPtr& a, const ConversationPtr& b) {
630 return a->m_cc < b->m_cc;
634 "bcc", [] (const ConversationPtr& a, const ConversationPtr& b) {
635 return a->m_bcc < b->m_bcc;
639 "lastMessageId", [] (const ConversationPtr& a, const ConversationPtr& b) {
640 return std::to_string(a->m_last_message_id) < std::to_string(b->m_last_message_id);
644 /* Every record has EMAIL type - no element is "greater than" any other. */
645 "type", defaultConversationComparator
650 const std::set<std::string> MessageConversation::m_message_conversation_filterable_attributes = {
651 MESSAGE_CONVERSATION_ATTRIBUTE_ID,
652 MESSAGE_CONVERSATION_ATTRIBUTE_TYPE,
653 MESSAGE_CONVERSATION_ATTRIBUTE_TIMESTAMP,
654 MESSAGE_CONVERSATION_ATTRIBUTE_MESSAGE_COUNT,
655 MESSAGE_CONVERSATION_ATTRIBUTE_UNREAD_MESSAGES,
656 MESSAGE_CONVERSATION_ATTRIBUTE_PREVIEW,
657 MESSAGE_CONVERSATION_ATTRIBUTE_SUBJECT,
658 MESSAGE_CONVERSATION_ATTRIBUTE_IS_READ,
659 MESSAGE_CONVERSATION_ATTRIBUTE_FROM,
660 MESSAGE_CONVERSATION_ATTRIBUTE_TO,
661 MESSAGE_CONVERSATION_ATTRIBUTE_CC,
662 MESSAGE_CONVERSATION_ATTRIBUTE_BCC,
663 MESSAGE_CONVERSATION_ATTRIBUTE_LAST_MESSAGE_ID};
665 MessageConversation::MessageConversationComparator MessageConversation::getComparator(
666 const std::string& attribute) {
667 auto comparator_it = m_message_conversation_comparators.find(attribute);
668 if (std::end(m_message_conversation_comparators) != comparator_it) {
669 return comparator_it->second;
672 return defaultConversationComparator;