#!/usr/bin/env python3
"""
Generates MTS_IO_MccMncTable.cpp file by pulling latest MCC/MNC values
from http://mcc-mnc.com or the csv file.
Original Source Idea: https://github.com/musalbas/mcc-mnc-table
"""
########################################################################################################################
import re
import urllib.request
import urllib.parse
import datetime
import csv
import argparse
from typing import Iterable, Generator, Optional, TextIO
from collections import namedtuple
########################################################################################################################
PREAMBLE_TEMPLATE = """\
/*
* Copyright (C) 2015 by Multi-Tech Systems
*
* This file is part of libmts-io.
*
* libmts-io is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* libmts-io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libmts-io. If not, see .
*
*/
/*!
\\file MTS_IO_MccMncTable.cpp
\\brief Auto-Generated MCC-MNC Lookup Table
\\date {today}
\\author sgodinez
An Auto-Generated MCC-MNC Lookup Table
*/
"""
GENERAL_CODE = """\
#include
#include
#include
using namespace MTS::IO;
MTS::AutoPtr MccMncTable::m_apLock(new MTS::Lock());
MccMncTable* MccMncTable::m_pInstance = NULL;
MccMncTable* MccMncTable::getInstance() {
if(m_pInstance == NULL) {
m_apLock->lock();
if (m_pInstance == NULL) {
m_pInstance = new MccMncTable();
}
m_apLock->unlock();
}
return m_pInstance;
}
MccMncTable::MccMncTable() {
createTable();
}
Json::Value MccMncTable::lookup(const std::string& sMcc, const std::string& sMnc) {
uint32_t iMcc, iMnc;
std::string sNormalizedMnc = sMnc;
printTrace("[MCCMNC] MCCx[%s] MNCx[%s]", sMcc.c_str(), sMnc.c_str());
if (sMnc.length() == 2) {
sNormalizedMnc += 'f';
}
if (!MTS::Text::parseHex(iMcc, sMcc)) { return Json::Value::null; }
if (!MTS::Text::parseHex(iMnc, sNormalizedMnc)) { return Json::Value::null; }
printTrace("[MCCMNC] MCC0X[%d] MNC0X[%d]", iMcc, iMnc);
if (m_mTable.count(iMcc)) {
if(m_mTable[iMcc].count(iMnc)) {
std::vector vJson = MTS::Text::split(m_mTable[iMcc][iMnc], ',');
Json::Value j;
j["iso"] = vJson[0];
j["country"] = vJson[1];
j["code"] = vJson[2];
j["carrier"] = vJson[3];
j["carrierCode"] = vJson[4];
return j;
}
}
return Json::Value::null;
}
"""
########################################################################################################################
MccMncElement = namedtuple(
'MccMncData',
field_names=('mcc', 'mcc_int', 'mnc', 'mnc_int', 'iso', 'country', 'country_code', 'carrier', 'carrier_code')
)
########################################################################################################################
def init_argparse() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description='Generate MCC/MNC table file from Website or CSV')
parser.add_argument('-w', '--website', action='store_true')
parser.add_argument('-c', '--csv', type=str)
parser.add_argument('-t', '--target', type=str, default='-')
return parser
def print_cpp_preamble(*, target: Optional[TextIO] = None) -> None:
print(PREAMBLE_TEMPLATE.format(today=datetime.date.today()), file=target)
def print_cpp_general_code(*, target: Optional[TextIO] = None) -> None:
print(GENERAL_CODE, file=target)
def format_mcc_mnc_line(el: MccMncElement) -> str:
if el.mnc.upper() != "N/A":
return ' m_mTable[{mcc_int}][{mnc_int}] = "{iso},{country},{country_code},{carrier},{carrier_code}";'.format(
mcc_int=el.mcc_int,
mnc_int=el.mnc_int,
iso=el.iso,
country=el.country,
country_code=el.country_code,
carrier=el.carrier,
carrier_code=el.carrier_code
)
else:
# TODO: The Country and Country Code values were swapped in the original implementation due to a bug.
# Left as is for compatibility reasons.
return ' //MCC({mcc}) MNC(N/A) ISO({iso}) Country Code({country}) Country({country_code}) Carrier({carrier}) Carrier Code({carrier_code})'.format(
mcc=el.mcc,
iso=el.iso,
country=el.country,
country_code=el.country_code,
carrier=el.carrier,
carrier_code=el.carrier_code
)
def print_cpp_mcc_mnc_create_table(source: Iterable[MccMncElement], *, target: Optional[TextIO] = None) -> None:
print("void MccMncTable::createTable() {", file=target)
print(" std::string sData;", file=target)
for el in source:
print(format_mcc_mnc_line(el), file=target)
print("}", file=target)
print("", file=target)
def print_cpp(source: Iterable[MccMncElement], *, target: Optional[TextIO] = None) -> None:
print_cpp_preamble(target=target)
print_cpp_general_code(target=target)
print_cpp_mcc_mnc_create_table(source, target=target)
def mcc_to_mcc_int(src: str) -> str:
if src.upper() == "N/A":
return src
hash_ = int(src, 16)
return "{:d}".format(hash_)
def mnc_to_mnc_int(src: str) -> str:
if src.upper() == "N/A":
return src
src_norm = src
if len(src) == 2:
src_norm += 'f'
hash_ = int(src_norm, 16)
return "{:d}".format(hash_)
def mcc_mnc_from_website(url: str) -> Generator[MccMncElement, None, None]:
td_re = re.compile('([^<]*) | ' * 6)
html_bytes = urllib.request.urlopen(url).read() # type: bytes
html = html_bytes.decode(encoding='utf-8')
tbody_start = False
for line in html.split('\n'):
if '' in line:
tbody_start = True
elif '' in line:
break
elif tbody_start:
td_search = td_re.search(line)
mcc = td_search.group(1).strip().replace(',', '')
mnc = td_search.group(2).strip().replace(',', '')
iso = td_search.group(3).strip().replace(',', '')
country = td_search.group(4).strip().replace(',', '')
country_code = td_search.group(5).strip().replace(',', '')
carrier = td_search.group(6).strip().replace(',', '')
mcc_int = mcc_to_mcc_int(mcc)
mnc_int = mnc_to_mnc_int(mnc)
yield MccMncElement(
mcc=mcc,
mcc_int=mcc_int,
mnc=mnc,
mnc_int=mnc_int,
iso=iso,
country=country,
country_code=country_code,
carrier=carrier,
carrier_code="" # Multitech-specific, not populated from this source
)
def mcc_mnc_from_csv(path: str) -> Generator[MccMncElement, None, None]:
with open(path) as f:
csv_reader = csv.DictReader(f)
for row in csv_reader:
mcc = row['MCC']
mnc = row['MNC']
iso = row['ISO']
country = row['Country']
country_code = row['Country Code']
carrier = row['Carrier']
carrier_code = row['Carrier Code']
mcc_int = mcc_to_mcc_int(mcc)
mnc_int = mnc_to_mnc_int(mnc)
yield MccMncElement(
mcc=mcc,
mcc_int=mcc_int,
mnc=mnc,
mnc_int=mnc_int,
iso=iso,
country=country,
country_code=country_code,
carrier=carrier,
carrier_code=carrier_code
)
########################################################################################################################
def main() -> int:
parser = init_argparse()
args = parser.parse_args()
if (args.csv is not None) and args.website:
parser.error('Only one source can be used at a time.')
return 1
if args.csv is None:
source = mcc_mnc_from_website('http://mcc-mnc.com/')
else:
source = mcc_mnc_from_csv(args.csv)
if args.target == '-':
# Print to stdout
print_cpp(source, target=None)
else:
# Print to file
with open(args.target, 'w') as f:
print_cpp(source, target=f)
return 0
########################################################################################################################
if __name__ == "__main__":
ret = main()
exit(ret)