pycrate/pycrate_mobile/TS23040_SMS.py

1128 lines
38 KiB
Python
Raw Normal View History

# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
2018-02-09 21:23:26 +00:00
# * Version : 0.3
# *
# * Copyright 2017. Benoit Michau. ANSSI.
# *
# * This library 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.1 of the License, or (at your option) any later version.
# *
# * This library 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 this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
# *
# *--------------------------------------------------------
# * File Name : pycrate_mobile/TS23040_SMS.py
# * Created : 2017-10-23
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
__all__ = [
'SMS_TP',
'TP_OA',
'TP_DA',
'TP_PID',
'TP_DCS',
'TP_SCTS',
'TP_VP',
'TP_VPe',
'TP_DT',
'TP_RA',
'TP_UDH',
'TP_UD',
'TP_PI',
'SMS_DELIVER',
'SMS_DELIVER_REPORT_RP_ERROR',
'SMS_DELIVER_REPORT_RP_ACK',
'SMS_SUBMIT',
'SMS_SUBMIT_REPORT_RP_ERROR',
'SMS_SUBMIT_REPORT_RP_ACK',
'SMS_STATUS_REPORT',
'SMS_COMMAND'
]
#------------------------------------------------------------------------------#
# 3GPP TS 23.040: Technical realization of the Short Message Service (SMS)
# release 13 (d20)
#------------------------------------------------------------------------------#
from time import struct_time
from math import ceil
from pycrate_core.utils import *
from pycrate_core.elt import *
from pycrate_core.base import *
from .TS24008_IE import BufBCD, _BCDType_dict, _NumPlan_dict
from .TS24007 import *
from .TS23038 import *
_str_reserved = 'reserved'
class SMS_TP(Envelope):
"""parent class for all SMS TP messages
"""
pass
#------------------------------------------------------------------------------#
# Address fields
# TS 23.040, section 9.1.2.5
#------------------------------------------------------------------------------#
class _TPAddress(Envelope):
_GEN = (
Uint8('Len'), # WNG: number of digits in Num
Uint('Ext', val=1, bl=1),
Uint('Type', val=1, bl=3, dic=_BCDType_dict),
Uint('NumberingPlan', val=1, bl=4, dic=_NumPlan_dict),
BufBCD('Num')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(lambda: len(self['Num'].decode()))
self[4].set_blauto(lambda: 8*int(ceil(self[0]()/2.0)))
#------------------------------------------------------------------------------#
# TPMessageTypeIndicator (TPMTI)
# TS 23.040, section 9.2.3.1
#------------------------------------------------------------------------------#
# coded on 2 bits
_TP_MTI_MT_dict = {
0 : 'SMS-DELIVER',
1 : 'SMS-SUBMIT-REPORT',
2 : 'SMS-STATUS-REPORT',
3 : 'Reserved',
}
_TP_MTI_MO_dict = {
0 : 'SMS-DELIVER-REPORT',
1 : 'SMS-SUBMIT',
2 : 'SMS-COMMAND',
3 : 'Reserved',
}
#------------------------------------------------------------------------------#
# TPMoreMessagestoSend (TPMMS)
# TS 23.040, section 9.2.3.2
#------------------------------------------------------------------------------#
# coded on 1 bit
_TP_MMS_dict = {
0 : 'More messages are waiting for the MS in this SC',
1 : 'No more messages are waiting for the MS in this SC',
}
#------------------------------------------------------------------------------#
# TPValidityPeriodFormat (TPVPF)
# TS 23.040, section 9.2.3.3
#------------------------------------------------------------------------------#
# coded on 2 bits
_TP_VPF_dict = {
0 : 'TP VP field not present',
1 : 'TP-VP field present - enhanced format',
2 : 'TP VP field present - relative format',
3 : 'TP VP field present - absolute format',
}
#------------------------------------------------------------------------------#
# TPStatusReportIndication (TPSRI)
# TS 23.040, section 9.2.3.4
#------------------------------------------------------------------------------#
# coded on 1 bit
_TP_SRI_dict = {
0 : 'A status report shall not be returned',
1 : 'A status report shall be returned',
}
#------------------------------------------------------------------------------#
# TPStatusReportRequest (TPSRR)
# TS 23.040, section 9.2.3.5
#------------------------------------------------------------------------------#
# coded on 1 bit
_TP_SRR_dict = {
0 : 'A status report is not requested',
1 : 'A status report is requested',
}
#------------------------------------------------------------------------------#
# TPOriginatingAddress (TPOA)
# TS 23.040, section 9.2.3.7
#------------------------------------------------------------------------------#
class TP_OA(_TPAddress):
pass
#------------------------------------------------------------------------------#
# TPDestinationAddress (TPDA)
# TS 23.040, section 9.2.3.8
#------------------------------------------------------------------------------#
class TP_DA(_TPAddress):
pass
#------------------------------------------------------------------------------#
# TPProtocolIdentifier (TPPID)
# TS 23.040, section 9.2.3.9
#------------------------------------------------------------------------------#
_TP_PIDFmt_dict = {
0 : 'telematic indication',
1 : 'no telematic indication',
2 : 'Reserved',
3 : 'protocol for SC specific use',
}
_TP_PIDTelematic_dict = {
0 : 'no telematic interworking, but SME-to-SME protocol',
1 : 'telematic interworking',
}
_TP_PIDTeleserv_dict = {
0 : 'implicit - device type is specific to this SC, '\
'or can be concluded on the basis of the address',
1 : 'telex (or teletex reduced to telex format)',
2 : 'group 3 telefax',
3 : 'group 4 telefax',
4 : 'voice telephone (i.e. conversion to speech)',
5 : 'ERMES (European Radio Messaging System)',
6 : 'National Paging system (known to the SC)',
7 : 'Videotex (T.100 [20] /T.101 [21])',
8 : 'teletex, carrier unspecified',
9 : 'teletex, in PSPDN',
10 : 'teletex, in CSPDN',
11 : 'teletex, in analog PSTN',
12 : 'teletex, in digital ISDN',
13 : 'UCI (Universal Computer Interface, ETSI DE/PS 3 01 3)',
14 : '(reserved, 2 combinations)',
16 : 'a message handling facility (known to the SC)',
17 : 'any public X.400 based message handling system',
18 : 'Internet Electronic Mail',
19 : '(reserved, 5 combinations)',
24 : 'values specific to each SC, usage based on mutual agreement '\
'between the SME and the SC (7 combinations available for each SC)',
31 : 'A GSM/UMTS mobile station. The SC converts the SM from the received '\
'TP Data Coding Scheme to any data coding scheme supported by that MS',
}
_TP_PIDServ_dict = {
0 : 'Short Message Type 0',
1 : 'Replace Short Message Type 1',
2 : 'Replace Short Message Type 2',
3 : 'Replace Short Message Type 3',
4 : 'Replace Short Message Type 4',
5 : 'Replace Short Message Type 5',
6 : 'Replace Short Message Type 6',
7 : 'Replace Short Message Type 7',
8 : 'Reserved',
30 : 'Enhanced Message Service (Obsolete)',
31 : 'Return Call Message',
32 : 'Reserved',
60 : 'ANSI-136 R-DATA',
61 : 'ME Data download',
62 : 'ME De personalization Short Message',
63 : '(U)SIM Data download', # for USAT, TS 51.011
}
class _TP_PIDTelematic(Envelope):
_GEN = (
Uint('Telematic', bl=1, dic=_TP_PIDTelematic_dict),
Uint('Protocol', bl=5)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_dicauto(lambda: _TP_PIDTeleserv_dict if self[0]() == 1 else {})
class TP_PID(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Format', bl=2, dic=_TP_PIDFmt_dict),
_TP_PIDTelematic('Telematic'), # telematic if Format == 0
Uint('Protocol', bl=6) # Format != 0
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_transauto(lambda: self[0]() != 0)
self[2].set_transauto(lambda: self[0]() == 0)
self[2].set_dicauto(lambda: _TP_PIDServ_dict if self[0]() == 1 else {})
#------------------------------------------------------------------------------#
# TPDataCodingScheme (TPDCS)
# TS 23.040, section 9.2.3.10
#------------------------------------------------------------------------------#
# defined in TS 23.038
class TP_DCS(SMS_DCS):
pass
#------------------------------------------------------------------------------#
# TPServiceCentreTimeStamp (TPSCTS)
# TS 23.040, section 9.2.3.11
#------------------------------------------------------------------------------#
class _TP_SCTS_Comp(Envelope):
def set_val(self, vals):
if isinstance(vals, integer_types):
self.encode(vals)
else:
Envelope.set_val(self, vals)
def encode(self, val):
self.set_val( (val%10, val//10) )
def decode(self):
return self[0]()+10*self[1]()
def repr(self):
return '<%s : %i%i>' % (self._name, self[1](), self[0]())
__repr__ = repr
class _TP_SCTS_TZ(Envelope):
_Sign_dict = {0: '+', 1: '-'}
_GEN = (
Uint('TZ1', bl=4),
Uint('TZS', bl=1, dic=_Sign_dict),
Uint('TZ0', bl=3)
)
def set_val(self, vals):
if isinstance(vals, float):
self.encode(vals)
else:
Envelope.set_val(self, vals)
def encode(self, val):
if val < 0:
self[1].set_val(1)
val = -val
else:
self[1].set_val(0)
if val != 0:
quart = ceil((val*4) % 128)
self[0].set_val( quart%16 )
self[2].set_val( quart>>4 )
else:
self[0].set_val(0)
self[2].set_val(0)
def decode(self):
if self[1]() == 1:
return -0.25 * ((self[2]()<<4) + self[0]())
else:
return 0.25 * ((self[2]()<<4) + self[0]())
def repr(self):
return '<TZ: %s%.2f>' % (self._Sign_dict[self[1]()], 0.25 * (self[0]() + (self[2]()<<4)))
__repr__ = repr
class TP_SCTS(Envelope):
YEAR_BASE = 2000
_GEN = (
_TP_SCTS_Comp('Year', GEN=(Uint('Y1', bl=4), Uint('Y0', bl=4))),
_TP_SCTS_Comp('Mon', GEN=(Uint('M1', bl=4), Uint('M0', bl=4))),
_TP_SCTS_Comp('Day', GEN=(Uint('D1', bl=4), Uint('D0', bl=4))),
_TP_SCTS_Comp('Hour', GEN=(Uint('H1', bl=4), Uint('H0', bl=4))),
_TP_SCTS_Comp('Min', GEN=(Uint('M1', bl=4), Uint('M0', bl=4))),
_TP_SCTS_Comp('Sec', GEN=(Uint('S1', bl=4), Uint('S0', bl=4))),
_TP_SCTS_TZ('TZ')
)
def set_val(self, val):
if isinstance(val, (tuple, list)) and len(val) == 2:
self.encode(*val)
else:
Envelope.set_val(self, val)
def encode(self, ts, tz=0.0):
"""encode a Python struct_time and potential timezone shift as the value of TP_SCTS
"""
self['Year'].encode( ts.tm_year-self.YEAR_BASE )
self['Mon'].encode( ts.tm_mon )
self['Day'].encode( ts.tm_mday )
self['Hour'].encode( ts.tm_hour )
self['Min'].encode( ts.tm_min )
self['Sec'].encode( ts.tm_sec )
self['TZ'].encode( tz )
def decode(self):
"""decode the current TP_SCTS value into a Python struct_time and timezone shift
"""
ts = struct_time((self.YEAR_BASE+self['Year'].decode(), self['Mon'].decode(),
self['Day'].decode(), self['Hour'].decode(), self['Min'].decode(),
self['Sec'].decode(), 0, 0, 0))
return ts, self['TZ'].decode()
#------------------------------------------------------------------------------#
# TP-VP (Relative format)
# TS 23.040, section 9.2.3.12.1
#------------------------------------------------------------------------------#
_TP_VPRel_dict = {}
for i in range(0, 144):
_TP_VPRel_dict[i] = '%i min' % (5*(i+1))
for i in range(144, 168):
_TP_VPRel_dict[i] = '%.1f hr' % (12+(i-143)*0.5)
for i in range(168, 197):
_TP_VPRel_dict[i] = '%i day' % (i-166)
for i in range(197, 256):
_TP_VPRel_dict[i] = '%i week' % (i-192)
#------------------------------------------------------------------------------#
# TP-VP (Absolute format)
# TS 23.040, section 9.2.3.12.2
#------------------------------------------------------------------------------#
class TP_VP(TP_SCTS):
pass
#------------------------------------------------------------------------------#
# TP-VP (Enhanced format)
# TS 23.040, section 9.2.3.12.3
#------------------------------------------------------------------------------#
# not sure it should have been called "enhanced"...
_TP_VPeFmt_dict = {
0 : 'no VP specified', # 6 spare bytes
1 : 'relative VP', # 5 spare bytes
2 : 'relative VP (0 < VP < 256 sec)', # 5 spare bytes
3 : 'relative VP (HHMMSS)', # 3 spare bytes
4 : _str_reserved, # 6 spare bytes
5 : _str_reserved,
6 : _str_reserved,
7 : _str_reserved
}
class _TP_VPe_HHMMSS(Envelope):
_GEN = (
_TP_SCTS_Comp('Hour', GEN=(Uint('H1', bl=4), Uint('H0', bl=4))),
_TP_SCTS_Comp('Min', GEN=(Uint('M1', bl=4), Uint('M0', bl=4))),
_TP_SCTS_Comp('Sec', GEN=(Uint('S1', bl=4), Uint('S0', bl=4)))
)
def set_val(self, val):
if isinstance(val, struct_time):
self.encode(val)
else:
Envelope.set_val(self, val)
def encode(self, ts):
"""encode a Python struct_time as the value of _TP_VPe_HHMMSS
"""
self['Hour'].encode( ts.tm_hour )
self['Min'].encode( ts.tm_min )
self['Sec'].encode( ts.tm_sec )
def decode(self):
"""decode the current _TP_VPe_HHMMSS value into a Python struct_time
"""
return struct_time((0, 0, 0, self['Hour'].decode(), self['Min'].decode(),
self['Sec'].decode(), 0, 0, 0))
class TP_VPe(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Ext', bl=1),
Uint('SingleShot', bl=1),
Uint(_str_reserved, bl=3),
Uint('VPFormat', bl=3, dic=_TP_VPeFmt_dict),
Uint8('VP'),
_TP_VPe_HHMMSS('TP'),
BufAuto('spare')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[4].set_transauto(lambda: self[3]() not in (1, 2))
self[4].set_dicauto(lambda: _TP_VPRel_dict if self[3]() == 1 else {})
self[5].set_transauto(lambda: self[3]() != 3)
self[6].set_blauto(self._set_spare_bl)
def _set_spare_bl(self):
fmt = self[3]()
if fmt in (1, 2):
return 40
elif fmt == 3:
return 24
else:
return 48
#------------------------------------------------------------------------------#
# TPDischargeTime (TPDT)
# TS 23.040, section 9.2.3.13
#------------------------------------------------------------------------------#
class TP_DT(TP_SCTS):
pass
#------------------------------------------------------------------------------#
# TPRecipientAddress (TPRA)
# TS 23.040, section 9.2.3.14
#------------------------------------------------------------------------------#
class TP_RA(_TPAddress):
pass
#------------------------------------------------------------------------------#
# TPStatus (TPST)
# TS 23.040, section 9.2.3.15
#------------------------------------------------------------------------------#
_TP_ST_dict = {
0 : 'Short message transaction completed - Short message received '\
'by the SME',
1 : 'Short message transaction completed - Short message forwarded '\
'by the SC to the SME but the SC is unable to confirm delivery',
2 : 'Short message transaction completed - Short message replaced '\
'by the SC',
3 : 'Short message transaction completed - reserved',
16 : 'Short message transaction completed - SC specific',
32 : 'Temporary error, SC still trying to transfer SM - Congestion',
33 : 'Temporary error, SC still trying to transfer SM - SME busy',
34 : 'Temporary error, SC still trying to transfer SM - No response '\
'from SME',
35 : 'Temporary error, SC still trying to transfer SM - Service rejected',
36 : 'Temporary error, SC still trying to transfer SM - '\
'Quality of service not available',
37 : 'Temporary error, SC still trying to transfer SM - Error in SME',
38 : 'Temporary error, SC still trying to transfer SM - reserved',
48 : 'Temporary error, SC still trying to transfer SM - SC specific',
64 : 'Permanent error, SC is not making any more transfer attempts - '\
'Remote procedure error',
65 : 'Permanent error, SC is not making any more transfer attempts - '\
'Incompatible destination',
66 : 'Permanent error, SC is not making any more transfer attempts - '\
'Connection rejected by SME',
67 : 'Permanent error, SC is not making any more transfer attempts - '\
'Not obtainable',
68 : 'Permanent error, SC is not making any more transfer attempts - '\
'Quality of service not available',
69 : 'Permanent error, SC is not making any more transfer attempts - '\
'No interworking available',
70 : 'Permanent error, SC is not making any more transfer attempts - '\
'SM Validity Period Expired',
71 : 'Permanent error, SC is not making any more transfer attempts - '\
'SM Deleted by originating SME',
72 : 'Permanent error, SC is not making any more transfer attempts - '\
'SM Deleted by SC Administration',
73 : 'Permanent error, SC is not making any more transfer attempts - '\
'SM does not exist',
74 : 'Permanent error, SC is not making any more transfer attempts - '\
'Reserved',
80 : 'Permanent error, SC is not making any more transfer attempts - '\
'SC specific',
96 : 'Temporary error, SC is not making any more transfer attempts - '\
'Congestion',
97 : 'Temporary error, SC is not making any more transfer attempts - '\
'SME busy',
98 : 'Temporary error, SC is not making any more transfer attempts - '\
'No response from SME',
99 : 'Temporary error, SC is not making any more transfer attempts - '\
'Service rejected',
100 : 'Temporary error, SC is not making any more transfer attempts - '\
'Quality of service not available',
101 : 'Temporary error, SC is not making any more transfer attempts - '\
'Error in SME',
102 : 'Temporary error, SC is not making any more transfer attempts - '\
'Reserved',
112 : 'Temporary error, SC is not making any more transfer attempts - '\
'Values specific to each SC',
}
#------------------------------------------------------------------------------#
# TPUserDataLength (TPUDL)
# TS 23.040, section 9.2.3.16
#------------------------------------------------------------------------------#
# number of septets (7-bit groups) in case of GSM 7-bit alphabet used in TP_UD
# number of bytes in case 8-bit or UCS2 alphabet in TP_UD
# in all cases, it includes the TP_UDH part if present
#------------------------------------------------------------------------------#
# TPReplyPath (TPRP)
# TS 23.040, section 9.2.3.17
#------------------------------------------------------------------------------#
_TP_RP_dict = {
0 : 'TP Reply Path parameter is not set in this SMS SUBMIT/DELIVER',
1 : 'TP Reply Path parameter is set in this SMS SUBMIT/DELIVER',
}
#------------------------------------------------------------------------------#
# TPCommandType (TPCT)
# TS 23.040, section 9.2.3.19
#------------------------------------------------------------------------------#
_TP_CT_dict = {
0 : 'Enquiry relating to previously submitted short message',
1 : 'Cancel Status Report Request relating to previously '\
'submitted short message',
2 : 'Delete previously submitted Short Message',
3 : 'Enable Status Report Request relating to previously '\
'submitted Short Message',
224 : 'SC specific',
}
#------------------------------------------------------------------------------#
# TPFailureCause (TPFCS)
# TS 23.040, section 9.2.3.22
#------------------------------------------------------------------------------#
_TP_FCS_dict = {
0x80 : 'TP-PID error : telematic interworking not supported',
0x81 : 'TP-PID error : short message Type 0 not supported',
0x82 : 'TP-PID error : cannot replace short message',
0x83 : 'TP-PID error : reserved',
0x8F : 'Unspecified TP-PID error',
0x90 : 'TP-DCS error : data coding scheme (alphabet) not supported',
0x91 : 'TP-DCS error : message class not supported',
0x92 : 'TP-DCS error : reserved',
0x9F : 'Unspecified TP-DCS error',
0xA0 : 'TP-Command Error : command cannot be actioned',
0xA1 : 'TP-Command Error : Command unsupported',
0xA2 : 'TP-Command Error : reserved',
0xAF : 'Unspecified TP-Command error',
0xB0 : 'TPDU not supported',
0xB1 : 'TPDU not supported : reserved',
0xC0 : 'SC busy',
0xC1 : 'No SC subscription',
0xC2 : 'SC system failure',
0xC3 : 'Invalid SME address',
0xC4 : 'Destination SME barred',
0xC5 : 'SM Rejected-Duplicate SM',
0xC6 : 'TP-VPF not supported',
0xC7 : 'TP-VP not supported',
0xD0 : '(U)SIM SMS storage full',
0xD1 : 'No SMS storage capability in (U)SIM',
0xD2 : 'Error in MS',
0xD3 : 'Memory Capacity Exceeded',
0xD4 : '(U)SIM Application Toolkit Busy',
0xD5 : '(U)SIM data download error',
0xE0 : 'Values specific to an application',
0xFF : 'Unspecified error cause',
}
#------------------------------------------------------------------------------#
# TPUser Data (TPUD)
# TS 23.040, section 9.2.3.24
#------------------------------------------------------------------------------#
_TP_UDHType_dict = {
0x0 : 'Concatenated short messages, 8-bit reference number',
0x1 : 'Special SMS Message Indication',
0x2 : 'Reserved',
0x3 : 'Value not used to avoid misinterpretation as <LF> character',
0x4 : 'Application port addressing scheme, 8 bit address',
0x5 : 'Application port addressing scheme, 16 bit address',
0x6 : 'SMSC Control Parameters',
0x7 : 'UDH Source Indicator',
0x8 : 'Concatenated short message, 16-bit reference number',
0x9 : 'Wireless Control Message Protocol',
0x0A : 'Text Formatting',
0x0B : 'Predefined Sound',
0x0C : 'User Defined Sound (iMelody max 128 bytes)',
0x0D : 'Predefined Animation',
0x0E : 'Large Animation (16*16 times 4 = 32*4 =128 bytes)',
0x0F : 'Small Animation (8*8 times 4 = 8*4 =32 bytes)',
0x10 : 'Large Picture (32*32 = 128 bytes)',
0x11 : 'Small Picture (16*16 = 32 bytes)',
0x12 : 'Variable Picture',
0x13 : 'User prompt indicator',
0x14 : 'Extended Object',
0x15 : 'Reused Extended Object',
0x16 : 'Compression Control',
0x17 : 'Object Distribution Indicator',
0x18 : 'Standard WVG object',
0x19 : 'Character Size WVG object',
0x1A : 'Extended Object Data Request Command',
0x20 : 'RFC 822 E-Mail Header',
0x21 : 'Hyperlink format element',
0x22 : 'Reply Address Element',
0x23 : 'Enhanced Voice Mail Information',
0x24 : 'National Language Single Shift',
0x25 : 'National Language Locking Shift'
}
for i in range(0x1B, 0x20):
_TP_UDHType_dict[i] = 'Reserved for future EMS features'
for i in range(0x70, 0x80):
_TP_UDHType_dict[i] = '(U)SIM Toolkit Security Headers'
for i in range(0x80, 0xA0):
_TP_UDHType_dict[i] = 'SME to SME specific use'
for i in range(0xC0, 0xE0):
_TP_UDHType_dict[i] = 'SC specific use'
class _TP_UDH_IE(Envelope):
_GEN = (
Uint8('T', dic=_TP_UDHType_dict),
Uint8('L'),
Buf('V', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(self[2].get_len)
self[2].set_blauto(lambda: 8*self[1].get_val())
class TP_UDH(Envelope):
_GEN = (
Uint8('UDHL'),
Sequence('UDH', GEN=_TP_UDH_IE('UDHIE')),
Uint('fill', rep=REPR_BIN), # WNG: not sure this will be aligned in the correct way
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(self[1].get_len)
self[2].set_blauto(self._set_fill_bl)
def _set_fill_bl(self):
try:
dcs = self.get_env().get_env()['TP_DCS']
except:
return 0
else:
grp, cs = dcs['Group'](), dcs['Charset']()
if grp in (0, 1, 4, 5, 15) and cs == 0:
# DCS_7B
return (7 - (self[1].get_bl()%7)) % 7
elif grp in (12, 13) and cs in (0, 2):
# DCS_7B
return (7 - (self[1].get_bl()%7)) % 7
else:
return 0
def _from_char(self, char):
if not self.get_trans():
self[0]._from_char(char)
clen = char._len_bit
char._len_bit = char._cur + 8*self[0]()
self[1]._from_char(char)
char._len_bit = clen
self[2]._from_char(char)
class BufUD(Buf):
# default encoding, required in SMS REPORT without TP-DCS
DEFAULT_DCS = DCS_7B
# indicator for GSM 7b encoding length
_ENC_BL = 0
def set_val(self, val):
if isinstance(val, bytes_types):
Buf.set_val(self, val)
else:
self.encode(val)
def get_dcs(self):
try:
dcs = self.get_env().get_env()['TP_DCS']
except:
return self.DEFAULT_DCS
else:
grp, cs = dcs['Group'](), dcs['Charset']()
if grp in (0, 1, 4, 5, 15):
if cs == 0:
return DCS_7B
elif cs == 2:
return DCS_UCS
elif grp in (12, 13) and cs in (0, 2):
return DCS_7B
elif grp == 14 and cs in (0, 2):
return DCS_UCS
return DCS_8B
def encode(self, val):
dcs = self.get_dcs()
if dcs == DCS_7B:
enc, cnt = encode_7b(val)
self.set_val(enc)
self._ENC_BL = 7*cnt
elif dcs == DCS_UCS:
2017-11-26 10:53:54 +00:00
self.set_val(val.encode('utf-16-be'))
self._ENC_BL = 0
else:
self.set_val(val)
self._ENC_BL = 0
def decode(self):
dcs = self.get_dcs()
if dcs == DCS_7B:
try:
udhbl = self.get_env()['UDH'].get_bl()
except:
udhbl = 0
if (udhbl + self._ENC_BL) % 8 == 1:
return decode_7b(self.get_val())[:-1]
else:
return decode_7b(self.get_val())
elif dcs == DCS_UCS:
2017-11-26 10:53:54 +00:00
return str(self.get_val(), 'utf-16-be')
else:
return self.get_val()
def repr(self):
dcs = self.get_dcs()
if dcs == DCS_7B:
return '<%s : %s>' % (self._name, self.decode())
elif dcs == DCS_UCS:
return '<%s : %s>' % (self._name, self.decode())
else:
return Buf.repr(self)
__repr__ = repr
class TP_UD(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint8('UDL'),
TP_UDH('UDH'),
BufUD('UD')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(self._set_udl)
self[1].set_transauto(lambda: True if self.get_udhi() == 0 else False)
def _set_udl(self):
if self.get_dcs() == DCS_7B:
# count number of septets:
# UDH should be a round number of septet (thanks to the fill bits)
# UD should have a non-null ENC_BL after encoding the text
return (self[1].get_bl() + self[2]._ENC_BL) // 7
else:
return self[1].get_len() + self[2].get_len()
def _from_char(self, char):
dcs = self.get_dcs()
self[0]._from_char(char)
ccur, clen, charnum = char._cur, char._len_bit, self[0]()
self[1]._from_char(char)
if dcs == DCS_7B:
char._len_bit = ccur + 7*charnum
lastbit, self[2]._ENC_BL = char._len_bit%8, (char._len_bit-char._cur)
if lastbit:
char._len_bit += 8-lastbit
self[2]._from_char(char)
else:
char._len_bit = ccur + 8*charnum
self[2]._ENC_BL = 0
self[2]._from_char(char)
char._len_bit = clen
def get_udhi(self):
try:
udhi = self.get_env()['TP_UDHI']
except:
return 0
else:
return udhi()
def get_dcs(self):
try:
dcs = self.get_env()['TP_DCS']
except:
return self.DEFAULT_DCS
else:
grp, cs = dcs['Group'](), dcs['Charset']()
if grp in (0, 1, 4, 5, 15):
if cs == 0:
return DCS_7B
elif cs == 2:
return DCS_UCS
elif grp in (12, 13) and cs in (0, 2):
return DCS_7B
elif grp == 14 and cs in (0, 2):
return DCS_UCS
return DCS_8B
#------------------------------------------------------------------------------#
# TPStatusReportQualifier (TPSRQ)
# TS 23.040, section 9.2.3.26
#------------------------------------------------------------------------------#
_TP_SRQ_dict = {
0 : 'The SMSSTATUSREPORT is the result of a SMSSUBMIT',
1 : 'The SMSSTATUSREPORT is the result of an SMSCOMMAND'
}
#------------------------------------------------------------------------------#
# TPParameterIndicator (TPPI)
# TS 23.040, section 9.2.3.27
#------------------------------------------------------------------------------#
class TP_PI(Envelope):
_GEN = (
Uint('Ext', bl=1),
Uint('reserved', bl=4),
Uint('TP_UDL', bl=1),
Uint('TP_DCS', bl=1),
Uint('TP_PID', bl=1)
)
#------------------------------------------------------------------------------#
# SMSDELIVER type
# TS 23.040, section 9.2.2.1
#------------------------------------------------------------------------------#
# Net -> UE
class SMS_DELIVER(SMS_TP):
_GEN = (
Uint('TP_SRI', bl=1, dic=_TP_SRI_dict),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('TP_RP', bl=1, dic=_TP_RP_dict),
Uint('TP_LP', desc='Loop Prevention', bl=1),
Uint('spare', bl=1),
Uint('TP_MMS', desc='no More Message to Send', bl=1),
Uint('TP_MTI', bl=2, dic=_TP_MTI_MT_dict),
TP_OA(desc='Originating Address'),
TP_PID(),
TP_DCS(),
TP_SCTS(),
TP_UD()
)
#------------------------------------------------------------------------------#
# SMSDELIVERREPORT for RPERROR
# TS 23.040, section 9.2.2.1a (i)
#------------------------------------------------------------------------------#
# UE -> Net
class SMS_DELIVER_REPORT_RP_ERROR(SMS_TP):
ENV_SEL_TRANS = False
_GEN = (
Uint('spare', bl=1),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('spare', bl=4),
Uint('TP_MTI', bl=2, dic=_TP_MTI_MO_dict),
Uint8('TP_FCS', dic=_TP_FCS_dict),
TP_PI(),
TP_PID(),
TP_DCS(),
TP_UD()
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self['TP_PDI'].set_transauto(lambda: False if self['TP_PI']['TP_PID']() else True)
self['TP_DCS'].set_transauto(lambda: False if self['TP_PI']['TP_DCS']() else True)
self['TP_UD'].set_transauto(lambda: False if self['TP_PI']['TP_UDL']() else True)
#------------------------------------------------------------------------------#
# SMSDELIVERREPORT for RPACK
# TS 23.040, section 9.2.2.1a (ii)
#------------------------------------------------------------------------------#
# UE -> Net
class SMS_DELIVER_REPORT_RP_ACK(SMS_TP):
ENV_SEL_TRANS = False
_GEN = (
Uint('spare', bl=1),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('spare', bl=4),
Uint('TP_MTI', bl=2, dic=_TP_MTI_MO_dict),
TP_PI(),
TP_PID(),
TP_DCS(),
TP_UD()
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self['TP_PID'].set_transauto(lambda: False if self['TP_PI']['TP_PID']() else True)
self['TP_DCS'].set_transauto(lambda: False if self['TP_PI']['TP_DCS']() else True)
self['TP_UD'].set_transauto(lambda: False if self['TP_PI']['TP_UDL']() else True)
#------------------------------------------------------------------------------#
# SMSSUBMIT type
# TS 23.040, section 9.2.2.2
#------------------------------------------------------------------------------#
# UE -> Net
class SMS_SUBMIT(SMS_TP):
ENV_SEL_TRANS = False
_GEN = (
Uint('TP_SRR', bl=1, dic=_TP_SRR_dict),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('TP_RP', bl=1, dic=_TP_RP_dict),
Uint('TP_VPF', bl=2, dic=_TP_VPF_dict),
Uint('TP_RD', desc='Reject Duplicates', bl=1),
Uint('TP_MTI', val=1, bl=2, dic=_TP_MTI_MT_dict),
Uint8('TP_MR', desc='Message Reference'),
TP_DA(desc='Destination Address'),
TP_PID(),
TP_DCS(),
# TP_VP: None / Uint8(TP_VPRel) / TP_VP() / TP_VPe(), depends on TP_VPF
Uint8('TP_VP', dic=_TP_VPRel_dict),
TP_VP(),
TP_VPe(),
TP_UD()
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[10].set_transauto(lambda: False if self[3]() == 2 else True)
self[11].set_transauto(lambda: False if self[3]() == 3 else True)
self[12].set_transauto(lambda: False if self[3]() == 1 else True)
#------------------------------------------------------------------------------#
# SMSSUBMITREPORT for RPERROR
# TS 23.040, section 9.2.2.2a (i)
#------------------------------------------------------------------------------#
# Net -> UE
class SMS_SUBMIT_REPORT_RP_ERROR(SMS_TP):
ENV_SEL_TRANS = False
_GEN = (
Uint('spare', bl=1),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('spare', bl=4),
Uint('TP_MTI', val=1, bl=2, dic=_TP_MTI_MT_dict),
Uint8('TP_FCS', dic=_TP_FCS_dict),
TP_PI(),
TP_SCTS(),
TP_PID(),
TP_DCS(),
TP_UD()
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self['TP_PID'].set_transauto(lambda: False if self['TP_PI']['TP_PID']() else True)
self['TP_DCS'].set_transauto(lambda: False if self['TP_PI']['TP_DCS']() else True)
self['TP_UD'].set_transauto(lambda: False if self['TP_PI']['TP_UDL']() else True)
#------------------------------------------------------------------------------#
# SMSSUBMITREPORT for RPACK
# TS 23.040, section 9.2.2.2a (ii)
#------------------------------------------------------------------------------#
# Net -> UE
class SMS_SUBMIT_REPORT_RP_ACK(SMS_TP):
ENV_SEL_TRANS = False
_GEN = (
Uint('spare', bl=1),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('spare', bl=4),
Uint('TP_MTI', val=1, bl=2, dic=_TP_MTI_MO_dict),
TP_PI(),
TP_SCTS(),
TP_PID(),
TP_DCS(),
TP_UD()
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self['TP_PID'].set_transauto(lambda: False if self['TP_PI']['TP_PID']() else True)
self['TP_DCS'].set_transauto(lambda: False if self['TP_PI']['TP_DCS']() else True)
self['TP_UD'].set_transauto(lambda: False if self['TP_PI']['TP_UDL']() else True)
#------------------------------------------------------------------------------#
# SMSSTATUSREPORT type
# TS 23.040, section 9.2.2.3
#------------------------------------------------------------------------------#
# Net -> UE
class SMS_STATUS_REPORT(SMS_TP):
ENV_SEL_TRANS = False
_GEN = (
Uint('spare', bl=1),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('TP_SRQ', bl=1, dic=_TP_SRQ_dict),
Uint('TP_LP', desc='Loop Prevention', bl=1),
Uint('spare', bl=1),
Uint('TP_MMS', desc='no More Message to Send', bl=1),
Uint('TP_MTI', val=2, bl=2, dic=_TP_MTI_MT_dict),
Uint8('TP_MR', desc='Message Reference'),
TP_RA(desc='Recipient Address'),
TP_SCTS(),
TP_DT(),
Uint8('TP_ST', dic=_TP_ST_dict),
TP_PI(),
TP_PID(),
TP_DCS(),
TP_UD()
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self['TP_PID'].set_transauto(lambda: False if self['TP_PI']['TP_PID']() else True)
self['TP_DCS'].set_transauto(lambda: False if self['TP_PI']['TP_DCS']() else True)
self['TP_UD'].set_transauto(lambda: False if self['TP_PI']['TP_UDL']() else True)
#------------------------------------------------------------------------------#
# SMSCOMMAND type
# TS 23.040, section 9.2.2.4
#------------------------------------------------------------------------------#
# UE -> Net
class SMS_COMMAND(SMS_TP):
_GEN = (
Uint('spare', bl=1),
Uint('TP_UDHI', desc='UDH Indicator', bl=1),
Uint('TP_SRR', bl=1, dic=_TP_SRR_dict),
Uint('spare', bl=3),
Uint('TP_MTI', val=2, bl=2, dic=_TP_MTI_MO_dict),
Uint8('TP_MR', desc='Message Reference'),
TP_PID(),
Uint8('TP_CT', dic=_TP_CT_dict),
Uint8('TP_MN', desc='Message Number'),
TP_DA(desc='Destination Address'),
Uint8('TP_CDL'),
Buf('TP_CD', val=b'', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[10].set_valauto(self[11].get_len)
self[11].set_blauto(lambda: 8*self[10]())