#!/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)