Files
MuditaOS/module-utils/PhoneNumber.cpp
Marcin Smoczyński c47923d70a [EGD-3107] Call number validation
Use specialized class to to pass phone number between apps and services
instead of ambigious std::string. Introduced class (utils::PhoneNumber)
wraps calls to libphonenumber providing more convienient interface.
However using libphonenumber directly may be resource hungry in terms
of stack, heap and cpu usage, so helper class has been introduced to
pass information about phone numbers (utils::PhoneNumber::View). It is
designed to maintain integrity thus can be created from an instance of
utils::PhoneNumber only or moved/copied to.

Add new field to the calllog db holding e164 formatted number. Both
entered and e164 numbers will be needed to be able to match with
contacts correctly.

Add constexpr information about country codes (utils::country).
2020-05-05 00:51:53 +02:00

170 lines
4.7 KiB
C++

#include "PhoneNumber.hpp"
#include "country.hpp"
#include "log/log.hpp"
#include <phonenumbers/phonenumberutil.h>
#include <phonenumbers/asyoutypeformatter.h>
#include <stdexcept>
#include <string>
using namespace utils;
PhoneNumber::PhoneNumber(const std::string &phoneNumber, country::Id defaultCountryCode)
: valid(false), countryCode(defaultCountryCode), util(*phn_util::GetInstance()), rawInput(phoneNumber)
{
valid = util.ParseAndKeepRawInput(phoneNumber, country::getAlpha2Code(countryCode), &pbNumber) ==
errCode::NO_PARSING_ERROR;
}
bool PhoneNumber::isValid() const
{
return valid;
}
const std::string &PhoneNumber::get() const
{
/// raw input can also be returned directly from pbNumber::rawInput() but
/// unfortunately if number parsing failed raw input is not preserved so
/// additional field had to be added in order to be able to always return
/// raw input
return rawInput;
}
std::string PhoneNumber::getFormatted() const
{
if (!valid) {
return rawInput;
}
std::string formatted;
util.FormatInOriginalFormat(pbNumber, country::getAlpha2Code(countryCode), &formatted);
return formatted;
}
std::string PhoneNumber::toE164() const
{
if (!valid) {
throw std::runtime_error("Can't create E164 format from an invalid number.");
}
std::string e164number;
util.Format(pbNumber, formatType::E164, &e164number);
return e164number;
}
bool PhoneNumber::operator==(const PhoneNumber &right)
{
return util.IsNumberMatch(pbNumber, right.pbNumber) == phn_util::EXACT_MATCH;
}
bool PhoneNumber::operator==(const View &view)
{
if (isValid() != view.isValid()) {
return false;
}
return isValid() ? toE164() == view.getE164() : rawInput == view.getEntered();
}
PhoneNumber::numberType PhoneNumber::getType() const
{
return util.GetNumberType(pbNumber);
}
bool PhoneNumber::e164format(const std::string &number, std::string &e164, country::Id defaultCountryCode)
{
i18n::phonenumbers::PhoneNumber phNumber;
auto &util = *phn_util::GetInstance();
auto result = util.Parse(number, country::getAlpha2Code(defaultCountryCode), &phNumber);
if (result != errCode::NO_PARSING_ERROR) {
return false;
}
util.Format(phNumber, formatType::E164, &e164);
return true;
}
PhoneNumber::View PhoneNumber::makeView() const
{
return PhoneNumber::View(get(), getFormatted(), isValid() ? toE164() : "", isValid());
}
PhoneNumber::View PhoneNumber::validateNumber(const std::string &e164, const std::string &entered)
{
auto &util = *phn_util::GetInstance();
// parse E164 number
i18n::phonenumbers::PhoneNumber pbNumberE164;
if (util.Parse(e164, country::getAlpha2Code(country::Id::UNKNOWN), &pbNumberE164) != errCode::NO_PARSING_ERROR) {
return PhoneNumber::View(entered, entered, e164, false);
}
// get region code from E164 number
std::string regionCode;
util.GetRegionCodeForNumber(pbNumberE164, &regionCode);
if (regionCode == country::getAlpha2Code(country::Id::UNKNOWN)) {
return PhoneNumber::View(entered, entered, e164, false);
}
// use region code to parse number originally entered by the user
// keep raw input in order to be able to use original format in formatting
i18n::phonenumbers::PhoneNumber pbNumberEntered;
if (util.ParseAndKeepRawInput(entered, regionCode, &pbNumberEntered) != errCode::NO_PARSING_ERROR) {
return PhoneNumber::View(entered, entered, e164, false);
}
// check if numbers match
if (util.IsNumberMatch(pbNumberE164, pbNumberEntered) != phn_util::EXACT_MATCH) {
return PhoneNumber::View(entered, entered, e164, false);
}
// format number to generate full view of the number
std::string formatted;
util.FormatInOriginalFormat(pbNumberEntered, regionCode, &formatted);
return PhoneNumber::View(entered, formatted, e164, true);
}
void PhoneNumber::View::clear()
{
this->operator=(View());
}
PhoneNumber::View PhoneNumber::viewFromE164(const std::string &e164)
{
PhoneNumber number(e164, country::Id::UNKNOWN);
return number.makeView();
}
const std::string &PhoneNumber::View::getEntered() const
{
return entered;
}
const std::string &PhoneNumber::View::getE164() const
{
return e164;
}
const std::string &PhoneNumber::View::getFormatted() const
{
return formatted;
}
bool PhoneNumber::View::isValid() const
{
return valid;
}
PhoneNumber::View::View(const std::string &enteredNumber,
const std::string &formattedNumber,
const std::string &e164Number,
bool valid)
: entered(enteredNumber), formatted(formattedNumber), e164(e164Number), valid(valid)
{}