| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cromo/sms_cache.h" |
| |
| #include <mm/mm-modem.h> // for MM_ERROR_MODEM_GSM_INVALIDINDEX |
| |
| #include <memory> |
| |
| #include <base/logging.h> |
| |
| namespace cromo { |
| |
| void SmsCache::AddToCache(SmsMessageFragment* fragment) { |
| const int index = fragment->index(); |
| |
| std::map<int, int>::const_iterator it = fragments_.find(index); |
| if (it != fragments_.end()) { |
| messages_.erase(it->second); |
| fragments_.erase(index); |
| // TODO(njw): Need to check for and remove from multipart object |
| // as well (though this whole clause is kind of a big |
| // shouldn't-happen) |
| } |
| |
| SmsMessage* message; |
| if (fragment->part_count() == 1) { |
| message = new SmsMessage(fragment); |
| messages_[index] = message; |
| } else { |
| std::map<uint16_t, int>::const_iterator multi_it; |
| multi_it = multiparts_.find(fragment->part_reference()); |
| if (multi_it == multiparts_.end()) { |
| message = new SmsMessage(fragment); |
| messages_[index] = message; |
| multiparts_[fragment->part_reference()] = index; |
| } else { |
| message = messages_[multi_it->second]; |
| message->AddFragment(fragment); |
| } |
| } |
| fragments_[index] = message->index(); |
| } |
| |
| SmsMessage* SmsCache::GetFromCache(int index) { |
| std::map<int, SmsMessage*>::const_iterator it = messages_.find(index); |
| if (it != messages_.end()) |
| return it->second; |
| else |
| return nullptr; |
| } |
| |
| int SmsCache::GetCanonicalIndex(int index) { |
| std::map<int, int>::const_iterator it = fragments_.find(index); |
| if (it != fragments_.end()) |
| return it->second; |
| else |
| return -1; |
| } |
| |
| void SmsCache::RemoveFromCache(int index) { |
| std::map<int, SmsMessage*>::const_iterator it = messages_.find(index); |
| if (it == messages_.end()) |
| return; |
| |
| const SmsMessage* sms = it->second; |
| if (sms->part_count() > 1) |
| multiparts_.erase(sms->part_reference()); |
| |
| std::unique_ptr<std::vector<int>> parts(sms->MessageIndexList()); |
| for (std::vector<int>::const_iterator it = parts->begin(); |
| it != parts->end(); |
| ++it) { |
| fragments_.erase(*it); |
| } |
| |
| messages_.erase(index); |
| delete sms; |
| } |
| |
| void SmsCache::ClearCache() { |
| for (std::map<int, SmsMessage*>::const_iterator message_it = |
| messages_.begin(); |
| message_it != messages_.end(); |
| ++message_it) { |
| delete message_it->second; |
| } |
| messages_.clear(); |
| multiparts_.clear(); |
| fragments_.clear(); |
| } |
| |
| SmsCache::~SmsCache() { |
| ClearCache(); |
| } |
| |
| SmsMessage* SmsCache::SmsReceived(int index, |
| DBus::Error& error, // NOLINT - refs. |
| SmsModemOperations* impl) { |
| SmsMessageFragment* frag = impl->GetSms(index, error); |
| if (error.is_set()) |
| return nullptr; |
| |
| AddToCache(frag); |
| return GetFromCache(GetCanonicalIndex(index)); |
| } |
| |
| static void SmsToPropertyMap(SmsMessage* sms, |
| utilities::DBusPropertyMap* result) { |
| CHECK(result); |
| (*result)["number"].writer().append_string(sms->sender_address().c_str()); |
| (*result)["smsc"].writer().append_string(sms->smsc_address().c_str()); |
| (*result)["text"].writer().append_string(sms->GetMessageText().c_str()); |
| (*result)["timestamp"].writer().append_string(sms->timestamp().c_str()); |
| (*result)["index"].writer().append_uint32(sms->index()); |
| } |
| |
| utilities::DBusPropertyMap* SmsCache::Get(int index, |
| DBus::Error& error, // NOLINT - refs. |
| SmsModemOperations* impl) { |
| int cindex = GetCanonicalIndex(index); |
| |
| if (cindex == -1) { |
| SmsMessageFragment* frag = impl->GetSms(index, error); |
| if (error.is_set()) |
| return nullptr; |
| AddToCache(frag); |
| cindex = GetCanonicalIndex(index); |
| } |
| |
| SmsMessage* sms = GetFromCache(cindex); |
| if (!sms->IsComplete()) { |
| LOG(WARNING) << "Message at index " << index << " was not complete."; |
| error.set("Message is incomplete", "GetSMS"); |
| return nullptr; |
| } |
| |
| utilities::DBusPropertyMap* map = new utilities::DBusPropertyMap(); |
| SmsToPropertyMap(sms, map); |
| return map; |
| } |
| |
| static const char kErrorInvalidIndex[] = |
| "org.freedesktop.ModemManager.Modem.Gsm." |
| MM_ERROR_MODEM_GSM_INVALIDINDEX; |
| |
| void SmsCache::Delete(int index, DBus::Error& error, // NOLINT - refs. |
| SmsModemOperations* impl) { |
| const SmsMessage* sms = GetFromCache(index); |
| if (!sms) { |
| if (GetCanonicalIndex(index) == -1) { |
| // We don't know anything about this index number. Pass the |
| // delete operation through. |
| impl->DeleteSms(index, error); |
| } else { |
| // We know about this index number but it's not valid to delete |
| // the middle of multipart messages. |
| error.set(kErrorInvalidIndex, "DeleteSMS"); |
| return; |
| } |
| } else { |
| std::unique_ptr<std::vector<int>> slots(sms->MessageIndexList()); |
| // There's some difficulty in handling errors vs. cache |
| // consistency here. If we call RemoveFromCache() unconditionally, |
| // and then have an error deleting a message, then the cache will |
| // not know about some fragments. The alternative would be to add |
| // some API to delete single elements as we go, which is OK for |
| // consistency, but we'd have to figure out what to do with the |
| // 'key' index of multipart messages. Ensure we delete it last? |
| // Wicked complicated. |
| RemoveFromCache(index); |
| for (std::vector<int>::const_iterator it = slots->begin(); |
| it != slots->end(); |
| ++it) { |
| DBus::Error tmperror; |
| impl->DeleteSms(*it, tmperror); |
| } |
| } |
| } |
| |
| std::vector<utilities::DBusPropertyMap>* SmsCache::List( |
| DBus::Error& error, // NOLINT - refs. |
| SmsModemOperations* impl) { |
| // Reset the cache |
| ClearCache(); |
| |
| // Refill the cache |
| std::unique_ptr<std::vector<int>> indexlist(impl->ListSms(error)); |
| if (!error.is_set()) { |
| for (std::vector<int>::const_iterator it = indexlist->begin(); |
| it != indexlist->end(); |
| ++it) { |
| DBus::Error get_sms_error; |
| SmsMessageFragment* frag = impl->GetSms(*it, get_sms_error); |
| // If GetSms fails, continue to the next SMS message fragment. |
| // |error| should only be set once, so report only the first error. |
| if (get_sms_error.is_set()) { |
| if (!error.is_set()) { |
| error.set(get_sms_error.name(), get_sms_error.message()); |
| } |
| continue; |
| } |
| AddToCache(frag); |
| } |
| } |
| |
| // Iterate over the cache and return complete messages |
| std::vector<utilities::DBusPropertyMap>* result = |
| new std::vector<utilities::DBusPropertyMap>(); |
| for (std::map<int, SmsMessage*>::const_iterator sms_it = messages_.begin(); |
| sms_it != messages_.end(); |
| ++sms_it) { |
| SmsMessage* sms = sms_it->second; |
| if (sms->IsComplete()) { |
| utilities::DBusPropertyMap map; |
| // Copy an empty map into the vector and then operate on it, |
| // rather than operating on a map and copying the contents. |
| result->push_back(map); |
| SmsToPropertyMap(sms, &result->back()); |
| } |
| } |
| return result; |
| } |
| |
| } // namespace cromo |