blob: 9e6e28b67a83a0e16a43baf0369f99b378dc95f1 [file] [log] [blame]
// Copyright (c) 2013 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.
// Implements shill::CellularOperatorInfo. See cellular_operator_info.h for
// details.
#include "shill/cellular_operator_info.h"
#include <base/stl_util.h>
#include <base/string_number_conversions.h>
#include <base/string_split.h>
#include <base/string_util.h>
#include "shill/logging.h"
using base::FilePath;
using std::map;
using std::string;
using std::vector;
namespace shill {
namespace {
typedef vector<const CellularOperatorInfo::CellularOperator *>
ConstOperatorVector;
string FormattedMCCMNC(const string &mccmnc) {
return "[MCCMNC=" + mccmnc + "]";
}
string FormattedSID(const string &sid) {
return "[SID=" + sid + "]";
}
template <class Collection>
const typename Collection::value_type::second_type *FindOrNull(
const Collection &collection,
const typename Collection::value_type::first_type &key) {
typename Collection::const_iterator it = collection.find(key);
if (it == collection.end())
return NULL;
return &it->second;
}
typedef CellularOperatorInfo COI;
bool ParseNameLine(const string &value, COI::LocalizedName *name) {
vector<string> fields;
base::SplitString(value, ',', &fields);
if (fields.size() != 2) {
LOG(ERROR) << "Badly formed \"name\" entry.";
return false;
}
name->language = fields[0];
name->name = fields[1];
return true;
}
// Advances the file reader to the next line that is neither a line comment
// nor empty and returns it in |line|. If the end of file is reached, returns
// false.
bool AdvanceToNextValidLine(FileReader &file_reader, string *line) {
while (file_reader.ReadLine(line)) {
TrimWhitespaceASCII(*line, TRIM_ALL, line);
// Skip line comments.
if ((*line)[0] == '#' || line->empty())
continue;
// If line consists of a single null character, skip
if (line->length() == 1 && (*line)[0] == '\0')
continue;
return true;
}
return false;
}
// Splits |line| into two strings, separated with the |key_value_delimiter|.
// Returns |false| if line does not contain |key_value_delimiter|, otherwise
// returns the first substring in |key| and the second substring in |value|,
// and returns true.
bool ParseKeyValue(const string &line,
char key_value_delimiter,
std::string *key,
std::string *value) {
const size_t length = line.length();
for (size_t i = 0; i < length; ++i) {
if (line[i] == key_value_delimiter) {
key->assign(line, 0, i);
value->assign(line, i + 1, length-i);
return true;
}
}
LOG(ERROR) << "Badly formed line: " << line;
return false;
}
} // namespace
class CellularOperatorInfoImpl {
public:
CellularOperatorInfoImpl() {
key_to_handler_["provider"] = new ProviderHandler(this);
key_to_handler_["mccmnc"] = new MccmncHandler(this);
key_to_handler_["name"] = new NameHandler(this);
key_to_handler_["apn"] = new ApnHandler(this);
key_to_handler_["sid"] = new SidHandler(this);
key_to_handler_["olp"] = new OlpHandler(this);
key_to_handler_["identifier"] = new IdentifierHandler(this);
key_to_handler_["activation-code"] = new ActivationCodeHandler(this);
key_to_handler_["country"] = new CountryHandler(this);
}
~CellularOperatorInfoImpl() {
STLDeleteContainerPairSecondPointers(key_to_handler_.begin(),
key_to_handler_.end());
}
private:
struct ParserState {
ParserState() : provider(NULL), apn(NULL), parsing_apn(false) {}
void Clear() {
country.clear();
provider = NULL;
apn = NULL;
parsing_apn = false;
}
std::string country;
COI::CellularOperator *provider;
COI::MobileAPN *apn;
bool parsing_apn;
};
class KeyHandler {
public:
KeyHandler(CellularOperatorInfoImpl *info_impl) : info_impl_(info_impl) {}
virtual ~KeyHandler() {}
virtual bool HandleKey(const string &value) = 0;
protected:
CellularOperatorInfoImpl *info_impl() const { return info_impl_; }
private:
CellularOperatorInfoImpl *info_impl_;
DISALLOW_COPY_AND_ASSIGN(KeyHandler);
};
class CountryHandler : public KeyHandler {
public:
CountryHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleCountry(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(CountryHandler);
};
class NameHandler : public KeyHandler {
public:
NameHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleNameKey(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(NameHandler);
};
class ProviderHandler : public KeyHandler {
public:
ProviderHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleProvider(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(ProviderHandler);
};
class MccmncHandler : public KeyHandler {
public:
MccmncHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleMCCMNC(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(MccmncHandler);
};
class IdentifierHandler : public KeyHandler {
public:
IdentifierHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleIdentifier(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(IdentifierHandler);
};
class ActivationCodeHandler : public KeyHandler {
public:
ActivationCodeHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleActivationCode(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(ActivationCodeHandler);
};
class ApnHandler : public KeyHandler {
public:
ApnHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleAPN(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(ApnHandler);
};
class SidHandler : public KeyHandler {
public:
SidHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleSID(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(SidHandler);
};
class OlpHandler : public KeyHandler {
public:
OlpHandler(CellularOperatorInfoImpl *info_impl)
: KeyHandler(info_impl) {}
bool HandleKey(const string &value) {
return info_impl()->HandleOLP(value);
}
private:
DISALLOW_COPY_AND_ASSIGN(OlpHandler);
};
bool HandleFirstLine(FileReader &reader) {
// Read until the first line that is not a line comment.
string line;
if (!AdvanceToNextValidLine(reader, &line))
return false;
string key, value;
if (!ParseKeyValue(line, ':', &key, &value))
return false;
if (key != "serviceproviders") {
LOG(ERROR) << "File does not begin with \"serviceproviders\" "
<< "entry.";
return false;
}
if (value != "3.0") {
LOG(ERROR) << "Unrecognized serviceproviders format.";
return false;
}
return true;
}
bool HandleCountry(const string &value) {
state_.country = value;
return true;
}
bool HandleNameKey(const string &value) {
if (state_.parsing_apn)
return HandleAPNName(value);
return HandleName(value);
}
bool HandleProvider(const string &value) {
state_.parsing_apn = false;
vector<string> fields;
base::SplitString(value, ',', &fields);
if (fields.size() != 4) {
LOG(ERROR) << "Badly formed \"provider\" entry.";
return false;
}
int is_primary = 0;
if (!base::StringToInt(fields[2], &is_primary)) {
LOG(ERROR) << "Badly formed value for \"is_primary\": " << fields[2];
return false;
}
int requires_roaming = 0;
if (!base::StringToInt(fields[3], &requires_roaming)) {
LOG(ERROR) << "Badly formed value for \"requires_roaming\": "
<< fields[3];
return false;
}
state_.provider = new COI::CellularOperator();
state_.provider->is_primary_ = is_primary != 0;
state_.provider->requires_roaming_ = requires_roaming != 0;
state_.provider->country_ = state_.country;
operators_.push_back(state_.provider);
return true;
}
bool HandleMCCMNC(const string &value) {
if (value.empty()) {
LOG(ERROR) << "Empty \"mccmnc\" value.";
return false;
}
if (!state_.provider) {
LOG(ERROR) << "Found \"mccmnc\" entry without \"provider\".";
return false;
}
vector<string> mccmnc_list;
base::SplitString(value, ',', &mccmnc_list);
if ((mccmnc_list.size() % 2) != 0) {
LOG(ERROR) << "Badly formatted \"mccmnc\" entry. "
<< "Expected even number of elements.";
return false;
}
for (size_t i = 0; i < mccmnc_list.size(); i += 2) {
const string &mccmnc = mccmnc_list[i];
if (!mccmnc.empty()) {
state_.provider->mccmnc_list_.push_back(mccmnc);
mccmnc_to_operator_[mccmnc] = state_.provider;
int index = -1;
if (base::StringToInt(mccmnc_list[i + 1], &index))
state_.provider->mccmnc_to_olp_idx_[mccmnc] = index;
}
}
return true;
}
bool HandleIdentifier(const string &value) {
if (!state_.provider) {
LOG(ERROR) << "Found \"identifier\" entry without \"provider\".";
return false;
}
state_.provider->identifier_ = value;
return true;
}
bool HandleActivationCode(const string &value) {
if (!state_.provider) {
LOG(ERROR) << "Found \"activation-code\" entry without \"provider\".";
return false;
}
state_.provider->activation_code_ = value;
return true;
}
bool HandleAPN(const string &value) {
if (!state_.provider) {
LOG(ERROR) << "Found \"apn\" entry without \"provider\".";
return false;
}
vector<string> fields;
base::SplitString(value, ',', &fields);
if (fields.size() != 4) {
LOG(ERROR) << "Badly formed \"apn\" entry.";
return false;
}
state_.apn = new COI::MobileAPN();
state_.apn->apn = fields[1];
state_.apn->username = fields[2];
state_.apn->password = fields[3];
state_.provider->apn_list_.push_back(state_.apn);
state_.parsing_apn = true;
return true;
}
bool HandleAPNName(const string &value) {
if (!(state_.parsing_apn && state_.apn)) {
LOG(ERROR) << "APN not being parsed.";
return false;
}
COI::LocalizedName name;
if (!ParseNameLine(value, &name))
return false;
state_.apn->name_list.push_back(name);
return true;
}
bool HandleName(const string &value) {
if (!state_.provider) {
LOG(ERROR) << "Found \"name\" entry without \"provider\".";
return false;
}
COI::LocalizedName name;
if (!ParseNameLine(value, &name))
return false;
if (state_.provider->name_list_.size() == 0) {
ConstOperatorVector &matching_operators =
name_to_operators_[name.name];
matching_operators.push_back(state_.provider);
}
state_.provider->name_list_.push_back(name);
return true;
}
bool HandleSID(const string &value) {
if (value.empty()) {
LOG(ERROR) << "Empty \"sid\" value.";
return false;
}
if (!state_.provider) {
LOG(ERROR) << "Found \"sid\" entry without \"provider\".";
return false;
}
vector<string> sid_list;
base::SplitString(value, ',', &sid_list);
if ((sid_list.size() % 2) != 0) {
LOG(ERROR) << "Badly formatted \"sid\" entry. "
<< "Expected even number of elements. ";
return false;
}
for (size_t i = 0; i < sid_list.size(); i += 2) {
const string &sid = sid_list[i];
if (!sid.empty()) {
state_.provider->sid_list_.push_back(sid);
sid_to_operator_[sid] = state_.provider;
int index = -1;
if (base::StringToInt(sid_list[i + 1], &index))
state_.provider->sid_to_olp_idx_[sid] = index;
}
}
return true;
}
bool HandleOLP(const string &value) {
if (!state_.provider) {
LOG(ERROR) << "Found \"olp\" entry without \"provider\"";
return false;
}
vector<string> fields;
base::SplitString(value, ',', &fields);
if (fields.size() != 3) {
LOG(ERROR) << "Badly formed \"apn\" entry.";
return false;
}
CellularService::OLP *olp = new CellularService::OLP();
olp->SetMethod(fields[0]);
olp->SetURL(fields[1]);
olp->SetPostData(fields[2]);
state_.provider->olp_list_.push_back(olp);
return true;
}
void ClearOperators() {
operators_.clear();
mccmnc_to_operator_.clear();
sid_to_operator_.clear();
name_to_operators_.clear();
}
bool HandleKeyValue(const string &key, const string &value) {
KeyHandler * const *handler = FindOrNull(key_to_handler_, key);
if (!handler) {
LOG(ERROR) << "Invalid key \"" << key << "\".";
return false;
}
return (*handler)->HandleKey(value);
}
bool Load(const FilePath &info_file_path) {
// Clear any previus operators.
ClearOperators();
FileReader file_reader;
if (!file_reader.Open(info_file_path)) {
LOG(ERROR) << "Could not open operator info file.";
return false;
}
// See data/cellular_operator_info for the format of file contents.
state_.Clear();
if (!HandleFirstLine(file_reader))
return false;
string line;
while (true) {
if (!AdvanceToNextValidLine(file_reader, &line))
break;
string key, value;
if (!ParseKeyValue(line, ':', &key, &value)) {
ClearOperators();
return false;
}
if (!HandleKeyValue(key, value)) {
LOG(ERROR) << "Failed to parse \"" << key << "\" entry.";
ClearOperators();
return false;
}
}
return true;
}
private:
friend class CellularOperatorInfo;
ParserState state_;
ScopedVector<COI::CellularOperator> operators_;
map<string, COI::CellularOperator *> mccmnc_to_operator_;
map<string, COI::CellularOperator *> sid_to_operator_;
map<string, ConstOperatorVector> name_to_operators_;
map<string, KeyHandler *> key_to_handler_;
DISALLOW_COPY_AND_ASSIGN(CellularOperatorInfoImpl);
};
COI::LocalizedName::LocalizedName() {}
COI::LocalizedName::LocalizedName(string name,
string language)
: name(name),
language(language) {}
CellularOperatorInfo::CellularOperator::CellularOperator()
: is_primary_(false),
requires_roaming_(false) {}
CellularOperatorInfo::CellularOperatorInfo()
: impl_(new CellularOperatorInfoImpl()) {}
CellularOperatorInfo::~CellularOperatorInfo() {}
const ScopedVector<CellularOperatorInfo::CellularOperator> &
CellularOperatorInfo::operators() const {
return impl_->operators_;
}
const CellularOperatorInfo::CellularOperator *
CellularOperatorInfo::GetCellularOperatorByMCCMNC(const string &mccmnc) const {
SLOG(Cellular, 2) << __func__ << "(" << FormattedMCCMNC(mccmnc) << ")";
CellularOperator * const *provider =
FindOrNull(impl_->mccmnc_to_operator_, mccmnc);
if (!provider) {
LOG(ERROR) << "Operator with " << FormattedMCCMNC(mccmnc)
<< " not found.";
return NULL;
}
return *provider;
}
const CellularOperatorInfo::CellularOperator *
CellularOperatorInfo::GetCellularOperatorBySID(const string &sid) const {
SLOG(Cellular, 2) << __func__ << "(" << FormattedSID(sid) << ")";
CellularOperator * const *provider = FindOrNull(impl_->sid_to_operator_, sid);
if (!provider) {
LOG(ERROR) << "Operator with " << FormattedSID(sid) << " not found.";
return NULL;
}
return *provider;
}
const ConstOperatorVector *CellularOperatorInfo::GetCellularOperators(
const string &name) const {
SLOG(Cellular, 2) << __func__ << "(" << name << ")";
const ConstOperatorVector *providers =
FindOrNull(impl_->name_to_operators_, name);
if (!providers) {
LOG(ERROR) << "Given name \"" << name << "\" did not match any operators.";
return NULL;
}
return providers;
}
const CellularService::OLP *
CellularOperatorInfo::GetOLPByMCCMNC(const string &mccmnc) const {
SLOG(Cellular, 2) << __func__ << "(" << FormattedMCCMNC(mccmnc) << ")";
const CellularOperator *provider = GetCellularOperatorByMCCMNC(mccmnc);
if (!provider)
return NULL;
const uint32 *indexptr = FindOrNull(provider->mccmnc_to_olp_idx_, mccmnc);
if (!indexptr)
return NULL;
uint32 index = *indexptr;
if (index >= provider->olp_list_.size()) {
LOG(ERROR) << "Invalid OLP index found for "
<< FormattedMCCMNC(mccmnc) << ".";
return NULL;
}
return provider->olp_list_[index];
}
const CellularService::OLP *
CellularOperatorInfo::GetOLPBySID(const string &sid) const {
SLOG(Cellular, 2) << __func__ << "(" << FormattedSID(sid) << ")";
const CellularOperator *provider = GetCellularOperatorBySID(sid);
if (!provider)
return NULL;
const uint32 *indexptr = FindOrNull(provider->sid_to_olp_idx_, sid);
if (!indexptr)
return NULL;
uint32 index = *indexptr;
if (index >= provider->olp_list_.size()) {
LOG(ERROR) << "Invalid OLP index found for " << FormattedSID(sid) << ".";
return NULL;
}
return provider->olp_list_[index];
}
bool CellularOperatorInfo::Load(const FilePath &info_file_path) {
SLOG(Cellular, 2) << __func__ << "(" << info_file_path.value() << ")";
return impl_->Load(info_file_path);
}
} // namespace shill