blob: b941cb3999116a24ad57c7ba763a26d1b0cd3a8a [file] [log] [blame]
// 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