1# imsi.py - functions for handling International Mobile Subscriber Identity
2#           (IMSI) numbers
3#
4# Copyright (C) 2011-2015 Arthur de Jong
5#
6# This library is free software; you can redistribute it and/or
7# modify it under the terms of the GNU Lesser General Public
8# License as published by the Free Software Foundation; either
9# version 2.1 of the License, or (at your option) any later version.
10#
11# This library is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14# Lesser General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with this library; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19# 02110-1301 USA
20
21"""IMSI (International Mobile Subscriber Identity).
22
23The IMSI (International Mobile Subscriber Identity) is used to identify
24mobile phone users (the SIM).
25
26>>> validate('429011234567890')
27'429011234567890'
28>>> validate('439011234567890')  # unknown MCC
29Traceback (most recent call last):
30    ...
31InvalidComponent: ...
32>>> split('429011234567890')
33('429', '01', '1234567890')
34>>> split('310150123456789')
35('310', '150', '123456789')
36>>> info('460001234567890')['mcc']
37'460'
38>>> str(info('460001234567890')['country'])
39'China'
40"""
41
42from stdnum.exceptions import *
43from stdnum.util import clean, isdigits
44
45
46def compact(number):
47    """Convert the IMSI number to the minimal representation. This strips the
48    number of any valid separators and removes surrounding whitespace."""
49    return clean(number, ' -').strip().upper()
50
51
52def split(number):
53    """Split the specified IMSI into a Mobile Country Code (MCC), a Mobile
54    Network Code (MNC), a Mobile Station Identification Number (MSIN)."""
55    from stdnum import numdb
56    # clean up number
57    number = compact(number)
58    # split the number
59    return tuple(numdb.get('imsi').split(number))
60
61
62def validate(number):
63    """Check if the number provided is a valid IMSI."""
64    number = compact(number)
65    if not isdigits(number):
66        raise InvalidFormat()
67    if len(number) not in (14, 15):
68        raise InvalidLength()
69    if len(split(number)) < 2:
70        raise InvalidComponent()  # unknown MCC
71    return number
72
73
74def info(number):
75    """Return a dictionary of data about the supplied number."""
76    from stdnum import numdb
77    # clean up number
78    number = compact(number)
79    # split the number
80    info = dict(number=number)
81    mcc_info, mnc_info, msin_info = numdb.get('imsi').info(number)
82    info['mcc'] = mcc_info[0]
83    info.update(mcc_info[1])
84    info['mnc'] = mnc_info[0]
85    info.update(mnc_info[1])
86    info['msin'] = msin_info[0]
87    info.update(msin_info[1])
88    return info
89
90
91def is_valid(number):
92    """Check if the number provided is a valid IMSI."""
93    try:
94        return bool(validate(number))
95    except ValidationError:
96        return False
97