From 17dcb8956d5640bfe82422a36f4b482ad17fcfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Voltattorni=20Casta=C3=B1o?= Date: Wed, 10 Jan 2018 10:31:51 -0400 Subject: [PATCH 1/7] Added as and ns types to the validity check, polished the bit value set code --- ISO8583/ISO8583.py | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/ISO8583/ISO8583.py b/ISO8583/ISO8583.py index fa19eaf..db7472b 100644 --- a/ISO8583/ISO8583.py +++ b/ISO8583/ISO8583.py @@ -27,6 +27,7 @@ else: from ISOErrors import * import struct +import re class ISO8583: @@ -1012,14 +1013,15 @@ def getValuesArray(self): ################################################################################################ - def __raiseValueTypeError(self, bit): + def __raiseValueTypeError(self, bit, value): """ Raise a type error exception @param: bit -> bit that caused the error @raises: InvalidValueType -> exception with message according to type error """ raise InvalidValueType( - 'Error: value of type %s has invalid type' % (self.getBitType(bit)) + 'Error: Bit %s of type %s and value %s has invalid type' %\ + (bit, self.getBitType(bit), value) ) ################################################################################################ @@ -1038,13 +1040,19 @@ def __checkBitTypeValidity(self, bit, value): if bitType == 'a': if not all(x.isspace() or x.isalpha() for x in value): - self.__raiseValueTypeError(bit) + self.__raiseValueTypeError(bit, value) elif bitType == 'n': if not value.isdecimal(): - self.__raiseValueTypeError(bit) + self.__raiseValueTypeError(bit, value) elif bitType == 'an': if not all(x.isspace() or x.isalnum() for x in value): - self.__raiseValueTypeError(bit) + self.__raiseValueTypeError(bit, value) + elif bitType == 'as': + if not all(not x.isdecimal() for x in value): + self.__raiseValueTypeError(bit, value) + elif bitType == 'ns': + if not all(not x.isalpha() for x in value): + self.__raiseValueTypeError(bit, value) # No exceptions raised, return return True @@ -1081,8 +1089,13 @@ def __getBitFromStr(self, strWithoutMtiBitmap): print('This bit is larger thant the specification.') # raise ValueToLarge("This bit is larger than the especification!") - self.BITMAP_VALUES[cont] = strWithoutMtiBitmap[offset:offset + 2] + strWithoutMtiBitmap[ - offset + 2:offset + 2 + valueSize] + value = strWithoutMtiBitmap[offset:offset + 2] + \ + strWithoutMtiBitmap[offset + 2:\ + offset + 2 + valueSize] + + self.__checkBitTypeValidity(cont, value) + + self.BITMAP_VALUES[cont] = value if self.DEBUG is True: print('\tSetting bit %s value %s' % @@ -1102,8 +1115,14 @@ def __getBitFromStr(self, strWithoutMtiBitmap): if valueSize > self.getBitLimit(cont): raise ValueToLarge( "This bit is larger than the especification!") - self.BITMAP_VALUES[cont] = strWithoutMtiBitmap[offset:offset + 3] + strWithoutMtiBitmap[ - offset + 3:offset + 3 + valueSize] + + value = strWithoutMtiBitmap[offset:offset + 3] + \ + strWithoutMtiBitmap[offset + 3:\ + offset + 3 + valueSize] + + self.__checkBitTypeValidity(cont, value) + + self.BITMAP_VALUES[cont] = value if self.DEBUG is True: print('\tSetting bit %s value %s' % From c44bef9dfe190a9b27171c23f8b6913f520ad865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Voltattorni=20Casta=C3=B1o?= Date: Wed, 10 Jan 2018 18:12:28 -0400 Subject: [PATCH 2/7] Added support for type A_or_N, used in bits 49, 50 and 51, added check for types in L* types in the function setBit --- ISO8583/ISO8583.py | 82 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/ISO8583/ISO8583.py b/ISO8583/ISO8583.py index db7472b..28ef1d0 100644 --- a/ISO8583/ISO8583.py +++ b/ISO8583/ISO8583.py @@ -91,7 +91,7 @@ class ISO8583: # Y = large str representation # Z = type of the bit (B, N, A, AN, ANS, LL, LLL) # W = size of the information that N need to has - # K = type os values a, an, n, ansb, b + # K = type os values a, an, n, ansb, b, a_or_n _BITS_VALUE_TYPE[1] = ['BME', 'Bit Map Extended', 'B', 16, 'b'] _BITS_VALUE_TYPE[2] = ['2', 'Primary account number (PAN)', 'LL', 19, 'n'] _BITS_VALUE_TYPE[3] = ['3', 'Precessing code', 'N', 6, 'n'] @@ -150,10 +150,10 @@ class ISO8583: _BITS_VALUE_TYPE[46] = ['46', 'Amounts fees', 'LLL', 999, 'an'] _BITS_VALUE_TYPE[47] = ['47', 'Additional data national', 'LLL', 999, 'an'] _BITS_VALUE_TYPE[48] = ['48', 'Additional data private', 'LLL', 999, 'an'] - _BITS_VALUE_TYPE[49] = ['49', 'Verification data', 'A', 3, 'a'] - _BITS_VALUE_TYPE[50] = ['50', 'Currency code, settlement', 'AN', 3, 'an'] + _BITS_VALUE_TYPE[49] = ['49', 'Verification data', 'A_or_N', 3, 'a_or_n'] + _BITS_VALUE_TYPE[50] = ['50', 'Currency code, settlement', 'A_or_N', 3, 'a_or_n'] _BITS_VALUE_TYPE[51] = [ - '51', 'Currency code, cardholder billing', 'A', 3, 'a'] + '51', 'Currency code, cardholder billing', 'A_or_N', 3, 'a_or_n'] _BITS_VALUE_TYPE[52] = [ '52', 'Personal identification number (PIN) data', 'B', 16, 'b'] _BITS_VALUE_TYPE[53] = [ @@ -452,6 +452,9 @@ def setBit(self, bit, value): if self.getBitType(bit) == 'B': self.__setBitTypeB(bit, value) + if self.getBitType(bit) == 'A_or_N': + self.__setBitTypeA_or_N(bit, value) + # Continuation bit? if bit > 64: # need to set bit 1 of first "bit" in bitmap @@ -670,6 +673,8 @@ def __setBitTypeLL(self, bit, value): size = "%s" % len(value) + self.__checkBitTypeValidity(bit, value) + self.BITMAP_VALUES[bit] = "%s%s" % (size.zfill(2), value) ################################################################################################ @@ -698,6 +703,8 @@ def __setBitTypeLLL(self, bit, value): size = "%s" % len(value) + self.__checkBitTypeValidity(bit, value) + self.BITMAP_VALUES[bit] = "%s%s" % (size.zfill(3), value) ################################################################################################ @@ -728,6 +735,35 @@ def __setBitTypeN(self, bit, value): ################################################################################################ + ################################################################################################ + # Set of type A_or_N, + def __setBitTypeA_or_N(self, bit, value): + """Method that set a bit with value in form A_or_N + It complete the size of the bit with a default value + Example: pack.setBit(49,'20') -> Bit 49 is a A_or_N type, so this bit, + in ASCII form need to has size = 3 (ISO especification) so the + value 20 size = 2 need to receive "1" more number. + In this case, will be "0" in the left. In the package, + the bit will be sent like '020' + @param: bit -> bit to be setted + @param: value -> value to be setted + @raise: ValueToLarge Exception + It's a internal method, so don't call! + """ + + value = "%s" % value + + if len(value) > self.getBitLimit(bit): + value = value[0:self.getBitLimit(bit)] + raise ValueToLarge('Error: value up to size! Bit[%s] of type %s limit size = %s' % ( + bit, self.getBitType(bit), self.getBitLimit(bit))) + + self.__checkBitTypeValidity(bit, value) + + self.BITMAP_VALUES[bit] = value.zfill(self.getBitLimit(bit)) + + ################################################################################################ + ################################################################################################ # Set of type A def __setBitTypeA(self, bit, value): @@ -1053,6 +1089,11 @@ def __checkBitTypeValidity(self, bit, value): elif bitType == 'ns': if not all(not x.isalpha() for x in value): self.__raiseValueTypeError(bit, value) + elif bitType == 'a_or_n': + # It has to be either alpha or numeric. + if not ( (all(x.isspace() or x.isalpha() for x in value)) or\ + (value.isdecimal()) ): + self.__raiseValueTypeError(bit, value) # No exceptions raised, return return True @@ -1138,7 +1179,7 @@ def __getBitFromStr(self, strWithoutMtiBitmap): # offset += valueSize + 4 if bitType == 'N' or bitType == 'A' or bitType == 'ANS' or \ - bitType == 'B' or bitType == 'AN': + bitType == 'B' or bitType == 'AN' or bitType == 'A_or_N': value = strWithoutMtiBitmap[ offset:self.getBitLimit(cont) + offset @@ -1299,6 +1340,37 @@ def getBit(self, bit): else: raise BitNotSet("Bit number %s was not set!" % bit) + + ################################################################################################ + + ################################################################################################ + # Method that returns the value of the bit + def getBitValue(self, bit): + """Return the value of the bit without + @param: bit -> the number of the bit that you want the value + @raise: BitInexistent Exception, BitNotSet Exception + """ + + # Get the raw bit + value = self.getBit(bit) + return_value = None + + # Get the bit's type + bit_type = self.getBitType(bit) + + if bit_type == 'L': + return_value = value[1:] + elif bit_type == 'LL': + return_value = value[2:] + elif bit_type == 'LLL': + return_value = value[3:] + elif bit_type in ['N', 'B', 'A', 'AN', 'ANS']: + return_value = value + + return return_value + + + ################################################################################################ ################################################################################################ From e4560461dc35428e996d43b13059021d75b43c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Voltattorni=20Casta=C3=B1o?= Date: Wed, 1 May 2019 16:23:30 -0400 Subject: [PATCH 3/7] Add missing merge changes --- ISO8583/ISO8583.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ISO8583/ISO8583.py b/ISO8583/ISO8583.py index fd1b830..0f8c5a0 100644 --- a/ISO8583/ISO8583.py +++ b/ISO8583/ISO8583.py @@ -1982,7 +1982,7 @@ def getNetworkISO(self, bigEndian=True): @raise: InvalidMTI Exception """ - netIso = "" + netIso = "".encode() asciiIso = self.getRawIso() if bigEndian: @@ -1994,7 +1994,7 @@ def getNetworkISO(self, bigEndian=True): if self.DEBUG is True: print('Pack Little-endian') - netIso += asciiIso.encode('ascii') + netIso += asciiIso return netIso @@ -2032,8 +2032,18 @@ def setNetworkISO(self, iso, bigEndian=True): @raise: InvalidIso8583 Exception """ - if len(iso) < 24: - raise InvalidIso8583('This is not a valid iso!!Invalid Size') + if self.MTI_format == 'A' or self.MTI_format == 'E': + mti_len = 4 + else: + mti_len = 2 + + if self.BITMAP_format == 'A' or self.BITMAP_format == 'E': + bitmap_min_size = 16 + else: + bitmap_min_size = 8 + + if len(iso) < (mti_len + bitmap_min_size + 4): + raise InvalidIso8583('This is not a valid iso!! Invalid Size') size = iso[0:2] if bigEndian: @@ -2047,7 +2057,7 @@ def setNetworkISO(self, iso, bigEndian=True): if len(iso) != (size[0] + 2): raise InvalidIso8583( - 'This is not a valid iso!!The ISO8583 ASCII(%s) is less than the size %s!' % (len(iso[2:]), size[0])) + 'This is not a valid iso!! The ISO8583 ASCII(%s) is less than the size %s!' % (len(iso[2:]), size[0])) self.setIsoContent(iso[2:]) From c96a2c9e5a837c82235f0a2d8cd5f3c9e6609f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Voltattorni=20Casta=C3=B1o?= Date: Thu, 2 May 2019 14:57:42 -0400 Subject: [PATCH 4/7] Add bit type validity check The function that validates type would not work with the bitmap as a byte sequence rather than a string, so encoding for ascii is needed. The bit 35 was changed from n to ans, since the track 2 data contains special characters. --- ISO8583/ISO8583.py | 90 ++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/ISO8583/ISO8583.py b/ISO8583/ISO8583.py index 0f8c5a0..dea775e 100644 --- a/ISO8583/ISO8583.py +++ b/ISO8583/ISO8583.py @@ -18,7 +18,7 @@ """ __author__ = 'Igor Vitorio Custodio ' -__version__ = '1.4' +__version__ = '1.5' __licence__ = 'GPL V3' import sys @@ -88,11 +88,11 @@ class ISO8583: # ISO8583 constants _BITS_VALUE_TYPE = {} # Every _BITS_VALUE_TYPE has: - # _BITS_VALUE_TYPE[N] = [ X,Y, Z, W,K] + # _BITS_VALUE_TYPE[N] = [X, Y, Z, V, W, K, L] # N = bitnumber # X = smallStr representation of the bit meaning # Y = large str representation - # Z = type of the bit (B, N, A, AN, ANS, LL, LLL, LLLLLL) + # Z = type of the bit (B, N, A, AN, ANS, A_or_N, LL, LLL, LLLLLL) # V = format of indicator length indicator LL, LLL, etc (-, A[scii], B[CD]) # W = size of the information that N need to has # K = type of values a, an, n, ans, b, a_or_n @@ -138,7 +138,7 @@ class ISO8583: _BITS_VALUE_TYPE[33] = [ '33', 'Forwarding institution identification code', 'LL', 'A', 11, 'n', 'A'] _BITS_VALUE_TYPE[34] = ['34', 'Primary Account Number, extended', 'LL', 'A', 28, 'n', 'A'] - _BITS_VALUE_TYPE[35] = ['35', 'Track 2 data', 'LL', 'A', 37, 'n', 'A'] + _BITS_VALUE_TYPE[35] = ['35', 'Track 2 data', 'LL', 'A', 37, 'ans', 'A'] _BITS_VALUE_TYPE[36] = ['36', 'Track 3 data', 'LLL', 'A', 104, 'n', 'A'] _BITS_VALUE_TYPE[37] = ['37', 'Retrieval reference number', 'N', '-', 12, 'an', 'A'] _BITS_VALUE_TYPE[38] = ['38', 'Approval code', 'N', '-', 6, 'an', 'A'] @@ -1399,27 +1399,33 @@ def __checkBitTypeValidity(self, bit, value): """ bitType = self.getBitValueType(bit) - - if bitType == 'a': - if not all(x.isspace() or x.isalpha() for x in value): - self.__raiseValueTypeError(bit, value) - elif bitType == 'n': - if not value.isdecimal(): - self.__raiseValueTypeError(bit, value) - elif bitType == 'an': - if not all(x.isspace() or x.isalnum() for x in value): - self.__raiseValueTypeError(bit, value) - elif bitType == 'as': - if not all(not x.isdecimal() for x in value): - self.__raiseValueTypeError(bit, value) - elif bitType == 'ns': - if not all(not x.isalpha() for x in value): - self.__raiseValueTypeError(bit, value) - elif bitType == 'a_or_n': - # It has to be either alpha or numeric. - if not ( (all(x.isspace() or x.isalpha() for x in value)) or\ - (value.isdecimal()) ): - self.__raiseValueTypeError(bit, value) + bitFormat = self.getBitFormat(bit) + + if bitFormat == 'A': + if type(value) == bytes: + value = value.decode('ascii') + + # ASCII format, able to decode and use str methods to validate + if bitType == 'a': + if not all(x.isspace() or x.isalpha() for x in value): + self.__raiseValueTypeError(bit, value) + elif bitType == 'n': + if not value.isdecimal(): + self.__raiseValueTypeError(bit, value) + elif bitType == 'an': + if not all(x.isspace() or x.isalnum() for x in value): + self.__raiseValueTypeError(bit, value) + elif bitType == 'as': + if not all(not x.isdecimal() for x in value): + self.__raiseValueTypeError(bit, value) + elif bitType == 'ns': + if not all(not x.isalpha() for x in value): + self.__raiseValueTypeError(bit, value) + elif bitType == 'a_or_n': + # It has to be either alpha or numeric. + if not ( (all(x.isspace() or x.isalpha() for x in value)) or + (value.isdecimal()) ): + self.__raiseValueTypeError(bit, value) # No exceptions raised, return return True @@ -1432,6 +1438,8 @@ def __getBitFromStr(self, strWithoutMtiBitmap): """Method that receive a string (ASCII) without MTI and Bitmaps (first and second), understand it and remove the bits values @param: str -> with all bits presents whithout MTI and bitmap It's a internal method, so don't call! + @raises: InvalidValueType -> exception with message according to + type error """ if self.DEBUG is True: @@ -1474,8 +1482,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): else: # ASCII and EBCDIC have the same length modvalueSize = valueSize - self.BITMAP_VALUES[cont] = strWithoutMtiBitmap[offset:offset+lenoffset] + strWithoutMtiBitmap[ - offset+lenoffset:offset+lenoffset+ modvalueSize] + bit_value = (strWithoutMtiBitmap[offset:offset+lenoffset] + + strWithoutMtiBitmap[offset+lenoffset:offset+ + lenoffset+modvalueSize] + ) + self.__checkBitTypeValidity(cont, bit_value) + self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: print('\tSetting bit %s value %s' % @@ -1509,8 +1521,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): else: modvalueSize = valueSize - self.BITMAP_VALUES[cont] = strWithoutMtiBitmap[offset:offset+lenoffset] + strWithoutMtiBitmap[ - offset+lenoffset:offset+lenoffset+modvalueSize] + bit_value = (strWithoutMtiBitmap[offset:offset+lenoffset] + + strWithoutMtiBitmap[offset+lenoffset:offset+ + lenoffset+modvalueSize] + ) + self.__checkBitTypeValidity(cont, bit_value) + self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: print('\tSetting bit %s value %s' % @@ -1544,8 +1560,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): else: modvalueSize = valueSize - self.BITMAP_VALUES[cont] = strWithoutMtiBitmap[offset:offset+lenoffset] + strWithoutMtiBitmap[ - offset+lenoffset:offset+lenoffset+modvalueSize] + bit_value = (strWithoutMtiBitmap[offset:offset+lenoffset] + + strWithoutMtiBitmap[offset+lenoffset:offset+ + lenoffset+modvalueSize] + ) + self.__checkBitTypeValidity(cont, bit_value) + self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: print('\tSetting bit %s value %s' % @@ -1570,10 +1590,10 @@ def __getBitFromStr(self, strWithoutMtiBitmap): else: modvalueSize = origvalueSize - value = strWithoutMtiBitmap[offset:modvalueSize + offset] + bit_value = strWithoutMtiBitmap[offset:modvalueSize + offset] - #self.__checkBitTypeValidity(cont, value) - self.BITMAP_VALUES[cont] = value + self.__checkBitTypeValidity(cont, bit_value) + self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: print('\tSetting bit %s value %s' % @@ -1892,7 +1912,7 @@ def getBit(self, bit): def getBitValue(self, bit): """Return the value of the bit without @param: bit -> the number of the bit that you want the value - @raise: BitInexistent Exception, BitNotSet Exception + @raise: BitNonexistent Exception, BitNotSet Exception """ # Get the raw bit From bb711fd1c77af9b821ec964a4e7aac6e30f49db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Voltattorni=20Casta=C3=B1o?= Date: Mon, 6 May 2019 10:36:30 -0400 Subject: [PATCH 5/7] Add length check to variable bits The bits of type LL, LLL and LLLLLL needed validation on their sizes according to the size value given by the first digits --- ISO8583/ISO8583.py | 82 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/ISO8583/ISO8583.py b/ISO8583/ISO8583.py index dea775e..5303bbb 100644 --- a/ISO8583/ISO8583.py +++ b/ISO8583/ISO8583.py @@ -817,7 +817,7 @@ def __getBitsFromBitmap(self): ################################################################################################ # Method that receive a ISO8583 ASCII package in the network form and parse it. - def __formatValue(self,bit,value): + def __formatValue(self, bit, value): """Method that formats a value to the appropriate data for the bit or LL or LLL bits @param: bit -> bit to be setted @param: value -> value to be setted @@ -869,7 +869,8 @@ def __setBitTypeLL(self, bit, value): data = self.__formatValue(bit, value) lenform = self.getBitLenForm(bit) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) + self.__check_bit_data_length(bit, value) if lenform == 'A': self.BITMAP_VALUES[bit] = size.zfill(2).encode() + data @@ -909,7 +910,8 @@ def __setBitTypeLLL(self, bit, value): data = self.__formatValue(bit, value) lenform = self.getBitLenForm(bit) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) + self.__check_bit_data_length(bit, value) if lenform == 'A': self.BITMAP_VALUES[bit] = size.zfill(3).encode() + data @@ -948,7 +950,8 @@ def __setBitTypeLLLLLL(self, bit, value): data = self.__formatValue(bit, value) lenform = self.getBitLenForm(bit) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) + self.__check_bit_data_length(bit, value) if lenform == 'A': self.BITMAP_VALUES[bit] = size.zfill(6).encode() + data @@ -981,7 +984,7 @@ def __setBitTypeN(self, bit, value): raise ValueTooLarge('Error: value up to size! Bit[%s] of type %s limit size = %s' % ( bit, self.getBitType(bit), self.getBitLimit(bit))) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) data_form = self.getBitFormat(bit) @@ -1018,7 +1021,7 @@ def __setBitTypeA_or_N(self, bit, value): raise ValueTooLarge('Error: value up to size! Bit[%s] of type %s limit size = %s' % ( bit, self.getBitType(bit), self.getBitLimit(bit))) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) data_form = self.getBitFormat(bit) @@ -1052,7 +1055,7 @@ def __setBitTypeA(self, bit, value): raise ValueTooLarge('Error: value up to size! Bit[%s] of type %s limit size = %s' % ( bit, self.getBitType(bit), self.getBitLimit(bit))) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) data_form = self.getBitFormat(bit) @@ -1083,7 +1086,7 @@ def __setBitTypeAN(self, bit, value): raise ValueTooLarge('Error: value up to size! Bit[%s] of type %s limit size = %s' % ( bit, self.getBitType(bit), self.getBitLimit(bit))) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) data_form = self.getBitFormat(bit) @@ -1146,7 +1149,7 @@ def __setBitTypeANS(self, bit, value): raise ValueTooLarge('Error: value up to size! Bit[%s] of type %s limit size = %s' % ( bit, self.getBitType(bit), self.getBitLimit(bit))) - self.__checkBitTypeValidity(bit, value) + self.__check_bit_type_validity(bit, value) data_form = self.getBitFormat(bit) @@ -1390,12 +1393,12 @@ def __raiseValueTypeError(self, bit, value=None): ################################################################################################ - def __checkBitTypeValidity(self, bit, value): + def __check_bit_type_validity(self, bit, value): """ Verify that a bit's value has the correct type - @param: bit -> bit to be validated - @param: value -> bit's value as a string - @raises: InvalidValueType -> exception with message according to - type error + :param int bit: bit to be validated + :param str or byte value: bit's value + :raises InvalidValueType: exception with message according to + type error """ bitType = self.getBitValueType(bit) @@ -1430,6 +1433,43 @@ def __checkBitTypeValidity(self, bit, value): # No exceptions raised, return return True + def __check_bit_data_length(self, bit, value): + """ + Verify the data size of the data in the LL values, the data should + match the size given by the first bits. + :param int bit: The bit to validate + :param str value: The value of the bit + :raises ValueTooLarge: when the value does not match the size + """ + bit_type = self.getBitType(bit) + len_type = self.getBitLenForm(bit) # Should be 'A' + data_type = self.getBitFormat(bit) # Should be 'A' + + index = None + + if (len_type == 'A') and (data_type == 'A'): + # Able to parse the length and data as str + if bit_type == 'LL': + index = 2 + elif bit_type == 'LLL': + index = 3 + elif bit_type == 'LLLLLL': + index = 6 + + if index: + # Verify length of the incoming value + data_length = int(value[:index]) + if len(value[index:]) != data_length: + raise ValueTooLarge( + 'Error: Bit {bit} has a header size of {size} ' + 'but a data size of {data_size}'.format( + bit=bit, + size=data_length, + data_size=len(value[index:]))) + + return True + + ################################################################################################ ################################################################################################ @@ -1486,7 +1526,9 @@ def __getBitFromStr(self, strWithoutMtiBitmap): strWithoutMtiBitmap[offset+lenoffset:offset+ lenoffset+modvalueSize] ) - self.__checkBitTypeValidity(cont, bit_value) + self.__check_bit_type_validity(cont, bit_value) + self.__check_bit_data_length(cont, bit_value) + self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: @@ -1525,7 +1567,9 @@ def __getBitFromStr(self, strWithoutMtiBitmap): strWithoutMtiBitmap[offset+lenoffset:offset+ lenoffset+modvalueSize] ) - self.__checkBitTypeValidity(cont, bit_value) + self.__check_bit_type_validity(cont, bit_value) + self.__check_bit_data_length(cont, bit_value) + self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: @@ -1564,7 +1608,9 @@ def __getBitFromStr(self, strWithoutMtiBitmap): strWithoutMtiBitmap[offset+lenoffset:offset+ lenoffset+modvalueSize] ) - self.__checkBitTypeValidity(cont, bit_value) + self.__check_bit_type_validity(cont, bit_value) + self.__check_bit_data_length(cont, bit_value) + self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: @@ -1592,7 +1638,7 @@ def __getBitFromStr(self, strWithoutMtiBitmap): bit_value = strWithoutMtiBitmap[offset:modvalueSize + offset] - self.__checkBitTypeValidity(cont, bit_value) + self.__check_bit_type_validity(cont, bit_value) self.BITMAP_VALUES[cont] = bit_value if self.DEBUG is True: From a380f913aabac58a8ba861a54b250595a35decd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Voltattorni=20Casta=C3=B1o?= Date: Mon, 6 May 2019 14:13:09 -0400 Subject: [PATCH 6/7] Fix error on data types Fix error when concatenating data types. --- ISO8583/ISO8583.py | 79 +++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/ISO8583/ISO8583.py b/ISO8583/ISO8583.py index 5303bbb..be65442 100644 --- a/ISO8583/ISO8583.py +++ b/ISO8583/ISO8583.py @@ -870,7 +870,7 @@ def __setBitTypeLL(self, bit, value): lenform = self.getBitLenForm(bit) self.__check_bit_type_validity(bit, value) - self.__check_bit_data_length(bit, value) + self.__check_bit_data_length(bit, value, size) if lenform == 'A': self.BITMAP_VALUES[bit] = size.zfill(2).encode() + data @@ -911,7 +911,7 @@ def __setBitTypeLLL(self, bit, value): lenform = self.getBitLenForm(bit) self.__check_bit_type_validity(bit, value) - self.__check_bit_data_length(bit, value) + self.__check_bit_data_length(bit, value, size) if lenform == 'A': self.BITMAP_VALUES[bit] = size.zfill(3).encode() + data @@ -951,7 +951,7 @@ def __setBitTypeLLLLLL(self, bit, value): lenform = self.getBitLenForm(bit) self.__check_bit_type_validity(bit, value) - self.__check_bit_data_length(bit, value) + self.__check_bit_data_length(bit, value, size) if lenform == 'A': self.BITMAP_VALUES[bit] = size.zfill(6).encode() + data @@ -1433,39 +1433,23 @@ def __check_bit_type_validity(self, bit, value): # No exceptions raised, return return True - def __check_bit_data_length(self, bit, value): + def __check_bit_data_length(self, bit, value, size): """ Verify the data size of the data in the LL values, the data should match the size given by the first bits. :param int bit: The bit to validate :param str value: The value of the bit + :param int size: The size of the field to set :raises ValueTooLarge: when the value does not match the size """ - bit_type = self.getBitType(bit) - len_type = self.getBitLenForm(bit) # Should be 'A' - data_type = self.getBitFormat(bit) # Should be 'A' - - index = None - - if (len_type == 'A') and (data_type == 'A'): - # Able to parse the length and data as str - if bit_type == 'LL': - index = 2 - elif bit_type == 'LLL': - index = 3 - elif bit_type == 'LLLLLL': - index = 6 - - if index: - # Verify length of the incoming value - data_length = int(value[:index]) - if len(value[index:]) != data_length: - raise ValueTooLarge( - 'Error: Bit {bit} has a header size of {size} ' - 'but a data size of {data_size}'.format( - bit=bit, - size=data_length, - data_size=len(value[index:]))) + size = int(size) + if len(value) != size: + raise ValueTooLarge( + 'Error: Bit {bit} has a header size of {size} ' + 'but a data size of {data_size}'.format( + bit=bit, + size=size, + data_size=len(value))) return True @@ -1522,12 +1506,13 @@ def __getBitFromStr(self, strWithoutMtiBitmap): else: # ASCII and EBCDIC have the same length modvalueSize = valueSize - bit_value = (strWithoutMtiBitmap[offset:offset+lenoffset] + - strWithoutMtiBitmap[offset+lenoffset:offset+ - lenoffset+modvalueSize] - ) - self.__check_bit_type_validity(cont, bit_value) - self.__check_bit_data_length(cont, bit_value) + data_size = strWithoutMtiBitmap[offset:offset+lenoffset] + data_value = strWithoutMtiBitmap[ + offset + lenoffset:offset + lenoffset + + modvalueSize] + bit_value = data_size + data_value + self.__check_bit_type_validity(cont, data_value) + self.__check_bit_data_length(cont, data_value, data_size) self.BITMAP_VALUES[cont] = bit_value @@ -1563,12 +1548,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): else: modvalueSize = valueSize - bit_value = (strWithoutMtiBitmap[offset:offset+lenoffset] + - strWithoutMtiBitmap[offset+lenoffset:offset+ - lenoffset+modvalueSize] - ) - self.__check_bit_type_validity(cont, bit_value) - self.__check_bit_data_length(cont, bit_value) + data_size = strWithoutMtiBitmap[offset:offset + lenoffset] + data_value = strWithoutMtiBitmap[ + offset + lenoffset:offset + lenoffset + modvalueSize] + bit_value = data_size + data_value + self.__check_bit_type_validity(cont, data_value) + self.__check_bit_data_length(cont, data_value, data_size) self.BITMAP_VALUES[cont] = bit_value @@ -1604,12 +1589,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): else: modvalueSize = valueSize - bit_value = (strWithoutMtiBitmap[offset:offset+lenoffset] + - strWithoutMtiBitmap[offset+lenoffset:offset+ - lenoffset+modvalueSize] - ) - self.__check_bit_type_validity(cont, bit_value) - self.__check_bit_data_length(cont, bit_value) + data_size = strWithoutMtiBitmap[offset:offset + lenoffset] + data_value = strWithoutMtiBitmap[ + offset + lenoffset:offset + lenoffset + modvalueSize] + bit_value = data_size + data_value + self.__check_bit_type_validity(cont, data_value) + self.__check_bit_data_length(cont, data_value, data_size) self.BITMAP_VALUES[cont] = bit_value From 97cf8ebdb9158d2c45520522f6a9cb8467d2f9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Voltattorni=20Casta=C3=B1o?= Date: Thu, 19 Sep 2019 15:43:20 -0400 Subject: [PATCH 7/7] Correct bit 126 The bit 126 had an invalid size, 6 instead of 999. Also added some formatting to ValueTooLarge raising, and updated setup.py to include ebcdic --- ISO8583/ISO8583.py | 22 +++++++++++++++++----- setup.py | 1 + 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ISO8583/ISO8583.py b/ISO8583/ISO8583.py index be65442..98fb339 100644 --- a/ISO8583/ISO8583.py +++ b/ISO8583/ISO8583.py @@ -267,7 +267,7 @@ class ISO8583: _BITS_VALUE_TYPE[124] = ['124', 'Info Text', 'LLL', 'A', 255, 'ans', 'A'] _BITS_VALUE_TYPE[125] = [ '125', 'Network management information', 'LL', 'A', 50, 'ans', 'A'] - _BITS_VALUE_TYPE[126] = ['126', 'Issuer trace id', 'LL', 'A', 6, 'ans', 'A'] + _BITS_VALUE_TYPE[126] = ['126', 'Issuer trace id', 'LLL', 'A', 999, 'ans', 'A'] _BITS_VALUE_TYPE[127] = ['127', 'Reserved for private use', 'LLL', 'A', 999, 'ans', 'A'] _BITS_VALUE_TYPE[128] = ['128', 'Message authentication code (MAC) field', 'B', '-', 16, 'b', 'A'] @@ -1498,8 +1498,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): print('Size of the message in LL = %s' % valueSize) if valueSize > self.getBitLimit(cont): - print('This bit is larger than the specification.') - # raise ValueTooLarge("This bit is larger than the specification!") + print( + "The bit's {} value is larger than " + "the specification.".format(cont)) + raise ValueTooLarge( + "The bit's {} value is larger than " + "the specification.".format(cont)) if self.getBitFormat(cont) == 'P': modvalueSize = self.__getPackedLen(valueSize) @@ -1540,8 +1544,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): print('Size of the message in LLL = %s' % valueSize) if valueSize > self.getBitLimit(cont): + print( + "The bit's {} value is larger than " + "the specification.".format(cont)) raise ValueTooLarge( - "This bit is larger than the specification!") + "The bit's {} value is larger than " + "the specification.".format(cont)) if self.getBitFormat(cont) == 'P': modvalueSize = self.__getPackedLen(valueSize) @@ -1581,8 +1589,12 @@ def __getBitFromStr(self, strWithoutMtiBitmap): print('Size of the message in LLLLLL = %s' % valueSize) if valueSize > self.getBitLimit(cont): + print( + "The bit's {} value is larger than " + "the specification.".format(cont)) raise ValueTooLarge( - "This bit is larger than the specification!") + "The bit's {} value is larger than " + "the specification.".format(cont)) if self.getBitFormat(cont) == 'P': modvalueSize = self.__getPackedLen(valueSize) diff --git a/setup.py b/setup.py index d1be970..e7fb26e 100755 --- a/setup.py +++ b/setup.py @@ -10,4 +10,5 @@ url='https://github.com/ducminhgd/python-ISOMessage8583', packages=['ISO8583'], keywords='ISO8583' + install_requires=['ebcdic==1.1.1'] )