Build fixes for new toolchain
[platform/core/api/webapi-plugins.git] / src / messaging / message_conversation.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "common/logger.h"
18 #include "common/platform_exception.h"
19
20 #include "MsgCommon/AbstractFilter.h"
21 #include "message.h"
22 #include "message_conversation.h"
23 #include "messaging_util.h"
24
25 #define MAX_THREAD_DATA_LEN 128
26
27 using common::ErrorCode;
28 using common::PlatformResult;
29
30 namespace {
31
32 using extension::messaging::AnyPtr;
33 using common::AttributeMatchFlag;
34 using namespace extension::messaging::FilterUtils;
35
36 bool ContainsAllRequiredRecipients(AnyPtr match_value, std::vector<std::string> recipients,
37                                    const AttributeMatchFlag match_flag) {
38   ScopeLogger();
39   if (!match_value->getValue().is<picojson::array>()) {
40     LoggerE("AttributeFilter value for TO attribute must be an array of strings.");
41     return false;
42   }
43
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.");
48       return false;
49     }
50
51     if (!isAnyStringMatching(value.get<std::string>(), recipients, match_flag)) {
52       LoggerD("AttributeFilter TO match failed on %s", value.get<std::string>().c_str());
53       return false;
54     }
55   }
56
57   return true;
58 }
59
60 }  // namespace
61
62 namespace extension {
63 namespace messaging {
64
65 using namespace tizen;
66
67 // *** constructor
68 MessageConversation::MessageConversation()
69     : m_conversation_id(-1),
70       m_conversation_type(UNDEFINED),
71       m_timestamp(-1),
72       m_count(0),
73       m_unread_messages(0),
74       m_is_read(false),
75       m_last_message_id(-1) {
76   ScopeLogger("Message Conversation constructor.");
77 }
78
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),
84       m_unread_messages(0),
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) {
93   ScopeLogger();
94   auto result = initializeUnreadMessagesCount();
95   if (result.IsError()) {
96     LoggerE("Could not initialize unreadMessages. conversation id: %d", m_conversation_id);
97   }
98 }
99
100 MessageConversation::~MessageConversation() {
101   ScopeLogger("Message Conversation destructor.");
102 }
103 // *** attributes getters
104 int MessageConversation::getConversationId() const {
105   return m_conversation_id;
106 }
107
108 MessageType MessageConversation::getType() const {
109   return m_conversation_type;
110 }
111
112 std::string MessageConversation::getTypeString() const {
113   return MessagingUtil::messageTypeToString(getType());
114 }
115
116 time_t MessageConversation::getTimestamp() const {
117   return m_timestamp;
118 }
119
120 unsigned long MessageConversation::getMessageCount() const {
121   return m_count;
122 }
123
124 unsigned long MessageConversation::getUnreadMessages() const {
125   return m_unread_messages;
126 }
127
128 std::string MessageConversation::getPreview() const {
129   return m_preview;
130 }
131
132 std::string MessageConversation::getSubject() const {
133   return m_conversation_subject;
134 }
135
136 bool MessageConversation::getIsRead() const {
137   return m_is_read;
138 }
139
140 std::string MessageConversation::getFrom() const {
141   return m_from;
142 }
143
144 std::vector<std::string> MessageConversation::getTo() const {
145   return m_to;
146 }
147
148 std::vector<std::string> MessageConversation::getCC() const {
149   return m_cc;
150 }
151
152 std::vector<std::string> MessageConversation::getBCC() const {
153   return m_bcc;
154 }
155
156 int MessageConversation::getLastMessageId() const {
157   return m_last_message_id;
158 }
159
160 PlatformResult MessageConversation::convertMsgConversationToObject(
161     unsigned int threadId, msg_handle_t handle, std::shared_ptr<MessageConversation>* result) {
162   ScopeLogger();
163   std::shared_ptr<MessageConversation> conversation(new MessageConversation());
164
165   msg_struct_t msgInfo = NULL;
166   msg_struct_t sendOpt = NULL;
167
168   msg_struct_t msg_thread = NULL;
169
170   msg_struct_list_s convViewList;
171   msg_list_handle_t addr_list = NULL;
172   msg_struct_t addr_info = NULL;
173
174   int tempInt;
175   bool tempBool;
176   int nToCnt;
177   unsigned int lastMsgIndex = 0;
178   char msgData[MAX_THREAD_DATA_LEN] = {
179       0,
180   };
181
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
190
191   conversation->m_conversation_id = threadId;
192
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)));
201   }
202   msg_get_int_value(msg_thread, MSG_THREAD_MSG_TYPE_INT, &tempInt);
203   switch (tempInt) {
204     case MSG_TYPE_SMS:
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;
212       break;
213     case MSG_TYPE_MMS:
214     case MSG_TYPE_MMS_JAVA:
215     case MSG_TYPE_MMS_NOTI:
216       conversation->m_conversation_type = MMS;
217       break;
218   }
219
220   msg_get_int_value(msg_thread, MSG_THREAD_MSG_TIME_INT, &tempInt);
221   conversation->m_timestamp = tempInt;
222
223   msg_get_int_value(msg_thread, MSG_THREAD_UNREAD_COUNT_INT, &tempInt);
224   conversation->m_unread_messages = tempInt;
225
226   msg_get_str_value(msg_thread, MSG_THREAD_MSG_DATA_STR, msgData, MAX_THREAD_DATA_LEN);
227
228   conversation->setPreview(msgData);
229
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)));
238   }
239
240   lastMsgIndex = convViewList.nCount - 1;
241   conversation->m_count = convViewList.nCount;
242
243   msg_get_bool_value(convViewList.msg_struct_info[lastMsgIndex], MSG_CONV_MSG_READ_BOOL, &tempBool);
244   conversation->m_is_read = tempBool;
245
246   msg_get_int_value(convViewList.msg_struct_info[lastMsgIndex], MSG_CONV_MSG_ID_INT, &tempInt);
247   conversation->m_last_message_id = tempInt;
248
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)));
253   }
254
255   msg_get_int_value(convViewList.msg_struct_info[lastMsgIndex], MSG_CONV_MSG_DIRECTION_INT,
256                     &tempInt);
257
258   msg_get_list_handle(msgInfo, MSG_MESSAGE_ADDR_LIST_HND, (void**)&addr_list);
259   nToCnt = msg_list_length(addr_list);
260
261   if (MSG_DIRECTION_TYPE_MT == tempInt) {
262     if (nToCnt > 0 && nToCnt < MAX_TO_ADDRESS_CNT) {
263       char strNumber[MAX_ADDRESS_VAL_LEN] = {
264           0,
265       };
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);
269
270       if (strNumber[0] != '\0') {
271         conversation->m_from = strNumber;
272       } else {
273         LoggerD("address is null ");
274       }
275     } else {
276       LoggerD("address count index fail");
277     }
278   } else {
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] = {
283             0,
284         };
285         msg_get_str_value(addr_info, MSG_ADDRESS_INFO_ADDRESS_VALUE_STR, strNumber,
286                           MAX_ADDRESS_VAL_LEN);
287
288         conversation->m_to.push_back(strNumber);
289       }
290     } else {
291       LoggerD("address fetch fail");
292     }
293   }
294
295   char strTemp[MAX_SUBJECT_LEN] = {0};
296   msg_get_str_value(msgInfo, MSG_MESSAGE_SUBJECT_STR, strTemp, MAX_SUBJECT_LEN);
297
298   conversation->setSubject(strTemp);
299
300   *result = conversation;
301   return PlatformResult(ErrorCode::NO_ERROR);
302 }
303
304 PlatformResult MessageConversation::convertEmailConversationToObject(
305     unsigned int threadId, std::shared_ptr<MessageConversation>* result) {
306   ScopeLogger();
307   std::shared_ptr<MessageConversation> conversation(new MessageConversation());
308
309   email_mail_list_item_t* resultMail = NULL;
310
311   int ntv_ret = email_get_thread_information_ex(threadId, &resultMail);
312   if (ntv_ret != EMAIL_ERROR_NONE) {
313     if (resultMail) {
314       if (resultMail->eas_data) {
315         free(resultMail->eas_data);
316       }
317       free(resultMail);
318     }
319
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)));
323   } else {
324     if (!resultMail) {
325       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Get email data fail.", ("Data is null"));
326     }
327
328     email_mail_data_t* mailData = NULL;
329
330     ntv_ret = email_get_mail_data(resultMail->mail_id, &mailData);
331     if (ntv_ret != EMAIL_ERROR_NONE) {
332       free(resultMail);
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)));
336     }
337
338     if (!mailData) {
339       free(resultMail);
340       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Get email data fail.");
341     }
342
343     int index = 0;
344     int count = 0;
345     conversation->m_unread_messages = 0;
346     email_mail_list_item_t* mailList = NULL;
347
348     ntv_ret =
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);
353       free(resultMail);
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)));
357     }
358
359     for (index = 0; index < count; index++) {
360       if (mailList[index].flags_seen_field) {
361         conversation->m_unread_messages++;
362       }
363     }
364     conversation->m_count = resultMail->thread_item_count;
365
366     conversation->m_conversation_id = threadId;
367
368     conversation->m_conversation_type = MessageType::EMAIL;
369
370     conversation->m_timestamp = resultMail->date_time;
371
372     if (resultMail->preview_text[0] != '\0') {
373       conversation->setPreview(resultMail->preview_text);
374     }
375
376     if (resultMail->subject[0] != '\0') {
377       conversation->setSubject(resultMail->subject);
378     }
379
380     conversation->m_is_read = (bool)resultMail->flags_seen_field;
381
382     if (resultMail->full_address_from[0] != '\0') {
383       conversation->m_from =
384           MessagingUtil::extractSingleEmailAddress(resultMail->full_address_from);
385     }
386
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);
390
391     conversation->m_last_message_id = resultMail->mail_id;
392
393     if (mailData != NULL) {
394       email_free_mail_data(&mailData, 1);
395     }
396   }
397
398   if (resultMail != NULL) {
399     free(resultMail);
400   }
401
402   *result = conversation;
403   return PlatformResult(ErrorCode::NO_ERROR);
404 }
405
406 void MessageConversation::setConversationId(int id) {
407   m_conversation_id = id;
408 }
409
410 void MessageConversation::setType(MessageType type) {
411   m_conversation_type = type;
412 }
413
414 void MessageConversation::setTimestamp(time_t timestamp) {
415   m_timestamp = timestamp;
416 }
417
418 void MessageConversation::setMessageCount(int count) {
419   m_count = count;
420 }
421
422 void MessageConversation::setUnreadMessages(int unread_messages) {
423   m_unread_messages = unread_messages;
424 }
425
426 void MessageConversation::setPreview(std::string preview) {
427   m_preview = MessagingUtil::SanitizeUtf8String(preview);
428 }
429
430 void MessageConversation::setSubject(std::string conversation_subject) {
431   m_conversation_subject = MessagingUtil::SanitizeUtf8String(conversation_subject);
432 }
433
434 void MessageConversation::setIsRead(bool is_read) {
435   m_is_read = is_read;
436 }
437
438 void MessageConversation::setFrom(std::string from) {
439   m_from = from;
440 }
441
442 void MessageConversation::setTo(std::vector<std::string>& to) {
443   m_to = to;
444 }
445
446 void MessageConversation::setCC(std::vector<std::string>& cc) {
447   m_cc = cc;
448 }
449
450 void MessageConversation::setBCC(std::vector<std::string>& bcc) {
451   m_bcc = bcc;
452 }
453
454 void MessageConversation::setLastMessageId(int last_message_id) {
455   m_last_message_id = last_message_id;
456 }
457
458 /**
459  *
460  *  Attribute      | Attribute filter| Attribute range filter
461  *                 | supported       | supported
462  * ----------------+-----------------+------------------------
463  * id              | Yes             | No
464  * type            | Yes             | No
465  * timestamp       | No              | Yes
466  * messageCount    | Yes             | Yes
467  * unreadMessages  | Yes             | Yes
468  * preview         | Yes             | No
469  * subject         | Yes             | No
470  * isRead          | Yes             | No
471  * from            | Yes             | No
472  * to              | Yes             | No
473  * cc              | Yes             | No
474  * bcc             | Yes             | No
475  * lastMessageId   | Yes             | No
476  */
477 bool MessageConversation::isMatchingAttribute(const std::string& attribute_name,
478                                               const common::AttributeMatchFlag match_flag,
479                                               AnyPtr match_value) const {
480   ScopeLogger();
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());
483
484   if (common::AttributeMatchFlag::kExists == match_flag) {
485     return m_message_conversation_filterable_attributes.find(attribute_name) !=
486            m_message_conversation_filterable_attributes.end();
487   }
488
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);
512     } else {
513       LoggerE("Unsupported type of AttributeFilter value for TO attribute.");
514       return false;
515     }
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);
522   }
523
524   LoggerD("attribute: %s is NOT SUPPORTED", attribute_name.c_str());
525   return false;
526 }
527
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());
532
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();
541   } else {
542     LoggerD("attribute:%s is NOT SUPPORTED", attribute_name.c_str());
543   }
544   return false;
545 }
546
547 common::PlatformResult MessageConversation::initializeUnreadMessagesCount() {
548   ScopeLogger();
549
550   email_list_filter_t filter_list = {};
551   memset(&filter_list, 0, sizeof(email_list_filter_t));
552
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;
557
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)));
565   }
566
567   m_unread_messages = unread_messages;
568
569   return PlatformResult{ErrorCode::NO_ERROR};
570 }
571
572 namespace {
573
574 bool defaultConversationComparator(const ConversationPtr&, const ConversationPtr&) {
575   return false;
576 }
577
578 }  // namespace
579
580 // clang-format off
581 const MessageConversation::MessageConversationComparatorMap
582   MessageConversation::m_message_conversation_comparators = {
583     {
584       "id", [] (const ConversationPtr& a, const ConversationPtr& b) {
585           return std::to_string(a->m_conversation_id) < std::to_string(b->m_conversation_id);
586       }
587     },
588     {
589       "timestamp", [] (const ConversationPtr& a, const ConversationPtr& b) {
590         return a->m_timestamp < b->m_timestamp;
591       }
592     },
593     {
594       "messageCount", [] (const ConversationPtr& a, const ConversationPtr& b) {
595         return a->m_count < b->m_count;
596       }
597     },
598     {
599       "unreadMessages", [] (const ConversationPtr& a, const ConversationPtr& b) {
600         return a->m_unread_messages < b->m_unread_messages;
601       }
602     },
603     {
604       "preview", [] (const ConversationPtr& a, const ConversationPtr& b) {
605         return a->m_preview < b->m_preview;
606       }
607     },
608     {
609       "subject", [] (const ConversationPtr& a, const ConversationPtr& b) {
610         return a->m_conversation_subject < b->m_conversation_subject;
611       }
612     },
613     {
614       "isRead", [] (const ConversationPtr& a, const ConversationPtr& b) {
615         return a->m_is_read < b->m_is_read;
616       }
617     },
618     {
619       "from", [] (const ConversationPtr& a, const ConversationPtr& b) {
620         return a->m_from < b->m_from;
621       }
622     },
623     {
624       "to", [] (const ConversationPtr& a, const ConversationPtr& b) {
625         return a->m_to < b->m_to;
626       }
627     },
628     {
629       "cc", [] (const ConversationPtr& a, const ConversationPtr& b) {
630         return a->m_cc < b->m_cc;
631       }
632     },
633     {
634       "bcc", [] (const ConversationPtr& a, const ConversationPtr& b) {
635         return a->m_bcc < b->m_bcc;
636       }
637     },
638     {
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);
641       }
642     },
643     {
644       /* Every record has EMAIL type - no element is "greater than" any other. */
645       "type", defaultConversationComparator
646     }
647 };
648 // clang-format on
649
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};
664
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;
670   }
671
672   return defaultConversationComparator;
673 }
674
675 }  // messaging
676 }  // extension