def determine_coding(self, unicode_str): if type(unicode_str) != unicode: raise SMSException('Input is not unicode') try: try: _test0338 = unicode_str.encode('gsm03.38') sms_log.debug('GSM03.38 OK "%s" -> "%s"' % (unicode_str, _test0338.decode('gsm03.38'))) return '0' except ValueError as ex: sms_log.debug( 'Encoding to GSM03.38 default alphabet not possible. %s' % sys.exc_info()[1]) _test0338s = gsm0338.Codec(single_shift_decode_map=gsm0338. SINGLE_SHIFT_CHARACTER_SET_SPANISH) _test = _test0338s.encode(unicode_str)[0] sms_log.debug('GSM03.38 Spanish Shift OK "%s" -> "%s"' % (unicode_str, _test0338s.decode(_test)[0])) return '2' except Exception as ex: template = "exception of type {0}. Arguments:\n{1!r}" print template.format(type(ex).__name__, ex.args) sms_log.debug('Using GSM03.38 Spanish Shift not possible. %s' % sys.exc_info()[1]) return '2'
def parseAddress(tpdu): '''Parse the TP-Address-Length, TP-Type-Of-Address and TP-Address from the start of the given TPDU and return the TOA, address and remaining TPDU. ''' pl = tpdu.int() toa = tpdu.int() # XXX todo parse this if 0 and toa not in (0x91, 0x81): raise ValueError('expected toa of 81/91, not 0x%02x' % toa) address = tpdu.octets(pl / 2 + pl % 2) if (toa & 0x70) == 0x50: # GSM-coded address - decode to ASCII and strip any crap address = unpack7bit(address, 0) c = gsm0338.Codec() address = decode_ascii_safe(c.decode(address, 'replace')[0], False) address = address.encode('ascii') # some phones put bits in the lower nybble of the TOA when # sending alphanumeric addresses; this is wrong wrong wrongedy # wrong "for Type-of-number = 101 bits 3,2,1,0 are reserved and # shall be transmitted as 0000" from the spec. toa &= 0xF0 else: address = unpackPhoneNumber(address) # prefix of "00" *usually* means "+" in eurospeak if address.startswith('00'): #if address.startswith(b'\x00'): address = address[2:] return pl, toa, address
def test_encode_spanish_single_shift(): unicode_spanish_single_shift = u'ç\u000A^{}\[~]|ÁÍÓÚá€íóú' gsm_spanish_single_shift = b'\x1B\x09\x0A\x1B\x14\x1B\x28\x1B\x29' \ b'\x1B\x2F\x1B\x3C\x1B\x3D\x1B\x3E\x1B\x40' \ b'\x1B\x41\x1B\x49\x1B\x4F\x1B\x55\x1B\x61' \ b'\x1B\x65\x1B\x69\x1B\x6F\x1B\x75' codec = gsm0338.Codec( single_shift_decode_map=gsm0338.SINGLE_SHIFT_CHARACTER_SET_SPANISH) assert codec.encode(unicode_spanish_single_shift) == \ (gsm_spanish_single_shift, len(unicode_spanish_single_shift))
def check_decode0338(self, text): try: return text.decode('gsm03.38') except Exception as ex: sms_log.error(str(ex)) try: gsm_shift_codec = gsm0338.Codec( single_shift_decode_map=gsm0338. SINGLE_SHIFT_CHARACTER_SET_SPANISH) return gsm_shift_codec.decode(text)[0] except Exception as ex: sms_log.error(str(ex))
def determineAddress(address): '''Determine the TP-Address-Length, TP-Type-of-Address and TP-(Originating|Destination)-Address values for the supplied address string. ''' if re.match('^\d+$', address): # phone number # Type-of-Address == 91 (international number) tp_al = len(address) tp_toa = 0x91 packed = packPhoneNumber(address) else: # Type-of-Address == D0 (alphanumeric) c = gsm0338.Codec() l, packed = pack7bit(c.encode(address, 'replace')) tp_al = len(packed) * 2 tp_toa = 0xD0 return tp_al, tp_toa, packed
def generate_mwi_dcs(type, active, message): '''Given a MWI type, active flag and unicode message generate a DCS for an SMS PDU. Return the (possibly modified) message and the tp_dcs to use. ''' dcs = dict(voicemail=0, fax=1, email=2, other=3)[type] if active == 'active': dcs |= 0x08 if not message: return message, dcs | 0xC0 message, x = attempt_encoding(message) try: gsm0338.Codec().encode(message) # code with GSM 0338 return message, dcs | 0xD0 except UnicodeError: # code with UCS2 return message, dcs | 0xE0
def smpp_to_sms_data_coding(smpp_dcs, content): '''Attempt to convert the SMPP data coding scheme (SMPP v34) to a useful SMS PDU (GSM 03.38) data coding scheme. The top nybble of the data coding scheme is the same for both specifications; it's just the lower nybble that the fuckers couldn't agree on. Since the SMS PDU spec dictates what's actually transmitted to the handset it trumps the SMPP one. Fortunately for non-trivial messages (ie. top nybble != 0) the SMPP spec says "see the GSM spec" so we just pass those through. We currently cannot handle messages in JIS (0208 or 0212) or KS C 5601. ''' top = smpp_dcs & 0xf0 if top: return smpp_dcs, content bottom = smpp_dcs & 0xf # default alphabet or ASCII; pass on if bottom in (0, 1): return 0, content # raw binary - 0000 0100 # note I've included "pictogram encoding" and "music codes" as "raw # data" if bottom == (2, 4, 9, 10): return 4, content # UCS2 - 0000 1000 if bottom == 8: return 8, content # one of the ISO charsets (iso-8859-1, iso-8859-5, iso-8859-8) or just # give the hell up charset = SMPP_ISO_CHARSETS.get(bottom, 'ascii') content, b = attempt_encoding(content.decode(charset, 'ignore')) content = gsm0338.Codec().encode(content) return 0, content
def guess_dcs(message): '''Given a plain-text message try to guess an appropriate DCS. ''' # figure encoding and add TP-DCS, TP-UDL and TP-UD (enforcing the 140 # octet maximum length of TP-UD) c = gsm0338.Codec() try: # GSM-0338 (7-bit) length = len(c.encode(message)) # TP-User-Data-Length -- number of septets (characters) if length > 160: raise ValueError('7-bit message too long (%d>160 chars)' % length) return 0 except UnicodeError: # UCS2 (well, UTF-16) big-endian length = len(message.encode('utf_16_be')) if length > 140: raise ValueError('UCS-2 message too long (%d>140 chars)' % length) return 8
def receive(self, request, source, destination, charset, coding, text, btext, dr, dcs): if btext == '': btext = text t = binascii.hexlify(btext) api_log.info( '%s - [POST] %s Data: source:"%s" destination:"%s" charset:"%s"' % (request.getHost().host, self.path, source, destination, charset)) api_log.debug( 'Data: source:"%s" destination:"%s" charset:"%s" coding: "%s" content: %s HexofBin: %s DR: %s DCS: %s' % (source, destination, charset, coding, text.decode(charset, 'replace'), t, dr, dcs)) # Kannel sends us GSM0338 but sets charset param to UTF-8 if coding == '0': try: gsm_shift_codec = gsm0338.Codec( single_shift_decode_map=gsm0338. SINGLE_SHIFT_CHARACTER_SET_SPANISH) text = gsm_shift_codec.decode(btext)[0] api_log.info('SMS Decoded from GSM 03.38') api_log.debug('Decoded text:"%s"' % text) except: # Catch Everything, try to not actually LOSE messages! e = sys.exc_info()[0] api_log.debug('Caught Exception: %s %s' % (e, sys.exc_info()[1])) data = { 'status': 'failed', 'error': str(e) + ' ' + str(sys.exc_info()[1]) } text = btext # Kannel can have problems if we send back UTF-16BE, so let's standardise here: if coding == '2' and charset == 'UTF-16BE': try: text = btext.decode('utf-16be') api_log.info('SMS decoded as UTF-16BE') api_log.debug('Decoded text: "%s"' % text) except: # Catch Everything, try to not actually LOSE messages! e = sys.exc_info()[0] api_log.debug('Caught Exception: %s %s' % (e, sys.exc_info()[1])) # Some phones are sending multi part messages with different charsets. # Kannel concatenates and sends as UTF-16BE coding 2 try: api_log.info('Trying multi part trick') a = btext[:134] b = btext[134:] text = a.decode('utf-16be') + b.decode('utf8') except: api_log.debug('Caught Exception: %s %s' % (e, sys.exc_info()[1])) text = btext try: sms = SMS() sms.receive(source, destination, text, charset, coding) data = {'status': 'success', 'error': ''} except SMSException as e: data = {'status': 'failed', 'error': str(e)} api_log.info(data) return data
def build_msgs(smsq): ret = [] csms = {} for sms in smsq: charset = 'utf-8' utext = '' """ 0 id INTEGER PRIMARY KEY AUTOINCREMENT 1 created TIMESTAMP NOT NULL 2 sent TIMESTAMP 3 deliver_attempts INTEGER NOT NULL DEFAULT 0 4 valid_until TIMESTAMP 5 reply_path_req INTEGER NOT NULL 6 status_rep_req INTEGER NOT NULL (7 is_report INTEGER NOT NULL) (8 msg_ref INTEGER NOT NULL) 7 9 protocol_id INTEGER NOT NULL 8 10 data_coding_scheme INTEGER NOT NULL 9 11 ud_hdr_ind INTEGER NOT NULL 10 12 src_addr TEXT NOT NULL 11 13 src_ton INTEGER NOT NULL 12 14 src_npi INTEGER NOT NULL 13 15 dest_addr TEXT NOT NULL 14 16 dest_ton INTEGER NOT NULL 15 17 dest_npi INTEGER NOT NULL 16 18 user_data BLOB 17 19 header BLOB 18 20 text TEXT """ if db_revision == '5': is_report = sms[7] coding = sms[10] udhdr = sms[11] src = sms[12] dest = sms[15] userdata = sms[18] header = sms[19] text = sms[20] elif db_revision == '4': is_report = 0 coding = sms[8] udhdr = sms[9] src = sms[10] dest = sms[13] coding = sms[8] userdata = sms[16] header = sms[17] text = sms[18] else: print "Unknown DB Revision" exit() log.debug("Message ID: \033[93m" + str(sms[0]) + '\033[0m') log.debug("Valid Until: " + str(sms[4])) log.debug("Coding is \033[32m%s \033[0m" % str(coding)) log.debug("UD HDR Indicator: " + str(sms[9])) log.debug("User Data: " + binascii.hexlify(userdata)) dtext = _dbd_decode_bin(userdata) if udhdr == 64: h = dtext[:7] #for i in [0, 1, 2, 3, 4, 5, 6]: # log.debug( ord(h[i]) ) #udh=parse_udh(h) udhdr = 1 if (udhdr == 1 and ord(dtext[0]) == 5): log.debug("UDH Detected") h = dtext[:6] msg = dtext[6:] udh = parse_udh(h) log.debug("CSMS Reference: " + str(udh['csms_ref'])) log.debug("Part No #" + str(udh['part_num']) + " of " + str(udh['parts'])) log.debug("Part Coding:" + str(coding)) not_decoded = 0 if coding == 0: msg = str( unpackSeptets(msg, None, 0, 6).lstrip(chr(0)).rstrip(chr(0))) try: msgpart = unicode(msg, 'gsm03.38') charset = 'GSM03.38' except: log.debug("Multipart decode failed for gsm03.38") else: try: msgpart = unicode(msg, 'utf-8') charset = 'UTF-8' except UnicodeDecodeError: log.debug("Multipart decode failed for UTF-8") try: msgpart = unicode(msg, 'utf-16be') charset = 'UTF-16BE' except: # Can't decode this segment on its own, # probably because truncated utf16 data. msgpart = msg charset = 'utf-16be' not_decoded = 1 if udh['part_num'] > 1 and csms[udh['csms_ref']][ 'not_decoded'][udh['part_num'] - 1] == 1: # try adding it to the last part.. csms[udh['csms_ref']]['text'][udh['part_num'] - 1] += msg msgpart = '' if not_decoded == 1: log.debug("Message Part Not Decoded on its own") else: log.debug("Message Part:" + msgpart.encode('utf-8', 'replace')) try: if not udh['csms_ref'] in csms or csms[ udh['csms_ref']] == None: csms[udh['csms_ref']] = {} csms[udh['csms_ref']]['ids'] = {} csms[udh['csms_ref']]['text'] = {} csms[udh['csms_ref']]['not_decoded'] = {} csms[udh['csms_ref']]['parts'] = udh['parts'] csms[udh['csms_ref']]['ids'][udh['part_num']] = sms[0] csms[udh['csms_ref']]['text'][udh['part_num']] = msgpart csms[udh['csms_ref']]['not_decoded'][ udh['part_num']] = not_decoded if csms[udh['csms_ref']]['parts'] and udh['part_num'] == csms[ udh['csms_ref']]['parts']: log.debug("Found Last Part of CSMS %s" % str(udh['csms_ref'])) utext = '' mid = '' for i in range(0, csms[udh['csms_ref']]['parts']): try: if csms[udh['csms_ref']]['not_decoded'][i + 1] == 1: text += csms[udh['csms_ref']]['text'][i + 1] else: utext += csms[udh['csms_ref']]['text'][i + 1] mid += str( csms[udh['csms_ref']]['ids'][i + 1]) + ', ' except KeyError: log.error( "Missing part %s of Multipart SMS %s for id %s" % ((i + 1), udh['csms_ref'], sms[0])) pass #if csms[udh['csms_ref']]['not_decoded'][i] == 1: # utext=utext+text.decode(charset) mid = mid.rstrip(', ') csms[udh['csms_ref']] = None #utext = unicode(utext.encode('utf-8')) else: continue except Exception as ex: print ex else: mid = str(sms[0]) if coding == 0: # unpackSeptets returns bytearray text7 = unpackSeptets(dtext).rstrip(chr(0)) log.debug("User Data Octets unpacked: " + binascii.hexlify(text7)) # gsm_codec_s = gsm0338.Codec() gsm_codec = gsm0338.Codec(single_shift_decode_map=gsm0338. SINGLE_SHIFT_CHARACTER_SET_SPANISH) utext = gsm_codec.decode(str(text7))[0] charset = 'gsm03.38' elif (coding == 8 or coding == 4) and is_report < 1: # I don't have any indicator of what the actual charset is. log.debug("Lost") try: if re.match(r'[\x00-\x0f]', dtext): utext = unicode(dtext, 'utf-16be') charset = 'UTF-16BE' else: utext = unicode(dtext, 'utf-8') charset = 'UTF-8' except UnicodeDecodeError as e: try: utext = unicode(dtext, 'utf-16be') except Exception as e: print e charset = 'UTF-16BE' log.debug("Coding value is 4/8, Charset Determined %s", charset) else: utext = unicode(dtext, charset, 'replace') charset = 'utf-8' log.debug("User Data dbd_decoded: " + binascii.hexlify(utext.encode(charset, 'replace'))) if header: log.debug("Header field: " + binascii.hexlify(header)) log.debug("DB text field '%s' (Length:%s)" % (binascii.hexlify(text), str(len(text)))) r = {} r['sms'] = sms r['mid'] = mid r['src'] = src r['dest'] = dest r['coding'] = coding r['charset'] = charset r['text'] = utext ret.append(r) if 'options' in globals() and options.debug_stop: cs(locals()) return ret
def codec(): return gsm0338.Codec()
def determineUD(user_data, tp_dcs, user_data_headers): '''Figure the TP-User-Data content and generate the PDU parameters tp_udhi, tp_dcs, tp_udl and tp_ud. ''' if user_data_headers: tp_udhi = 1 h = '' for ie, val in user_data_headers: h += chr(ie) + chr(len(val)) + ''.join(map(chr, val)) tp_ud = chr(len(h)) + h else: tp_udhi = 0 tp_ud = '' top_nybble = tp_dcs & 0xF0 if top_nybble == 0xC0: # Message Waiting Indication Group: Discard Message GSM codec = 'gsm' elif top_nybble == 0xD0: # Message Waiting Indication Group: Store Message GSM codec = 'gsm' elif top_nybble == 0xE0: # Message Waiting Indication Group: Store Message UCS2 codec = 'ucs2' elif top_nybble == 0xF0: # Data coding / message class codec = ['gsm', None][(tp_dcs & 0x04) >> 2] else: # either General Data Coding indication (0x0X .. 0x3X) # or Automatic Deletion (0x4X .. 0xbX) # (note that the book says Automatic Deletion but the GSM spec # says Reserved ... either way I'll just pass through) try: codec = ['gsm', None, 'ucs2'][(tp_dcs & 0x0C) >> 2] except IndexError: raise ValueError('bad tp_dcs value (reserved alphabet)') if codec == 'gsm': # GSM-0338 default alphabet c = gsm0338.Codec() # play it safe and force encoding with replace encoded = c.encode(user_data, 'replace') # TP-User-Data 7bit packed GSM-0338 encoded funky sh!t l, user_data = pack7bit(encoded, len(tp_ud)) tp_udl = l + len(tp_ud) elif codec == 'ucs2': # UCS2 user_data = user_data.encode('utf_16_be', 'replace') length = len(user_data) if length > 140: raise ValueError('UCS-2 message too long (%d>140 chars)' % length) tp_udl = len(user_data) + len(tp_ud) else: # 8-bit data tp_udl = len(user_data) + len(tp_ud) tp_ud += user_data return tp_udhi, tp_dcs, tp_udl, tp_ud
def parseUD(tp_dcs, tp_ud, tp_udhi, tp_udl): '''Parse user data (ie. the message) out of the tp_ud data string. ''' # pull out user-data headers if any if tp_udhi: data, headerlen, user_data_headers = SMS_GENERIC.parseUDH(tp_ud) else: data = tp_ud headerlen = 0 user_data_headers = [] # GSM 03.38, section 4 if (tp_dcs & 0xc0) == 0: if tp_dcs & 0x20: raise PDUDecodeError('compressed data not supported: ' 'tp_dcs 0x%02x (%s)' % (tp_dcs, describe_tp_dcs(tp_dcs))) data = decompress_user_data(data) try: charset = {0x00: '7bit', 0x04: '8bit', 0x08: 'utf-16'}[tp_dcs & 0x0c] except KeyError: raise PDUDecodeError('invalid DCS : tp_dcs 0x%02x ' '(specifies unknown charset)' % tp_dcs) elif (tp_dcs & 0xf0) in (0xc0, 0xd0): # MWI, Default Alphabet charset = '7bit' elif (tp_dcs & 0xf0) == 0xe0: # MWI, USC2 charset = 'utf-16' elif (tp_dcs & 0xf0) == 0xf0: charset = {0x00: '7bit', 0x04: '8bit'}[tp_dcs & 0x04] else: raise PDUDecodeError('unhandled tp_dcs 0x%02x (%s)' % (tp_dcs, describe_tp_dcs(tp_dcs))) # figure the number of characters (or octets, for data) that are # expected based on the tp_udl minus however many header octets # we've seen actual_udl = tp_udl - headerlen # now decode the user data if charset == '7bit': # basic 7 bit coding - 03.38 S6.2.1 data = unpack7bit(data, headerlen) c = gsm0338.Codec() user_data, length = c.decode(data) user_data = user_data[:actual_udl] elif charset == '8bit': # 8 bit coding is "user defined". S6.2.2 user_data = unpack8bit(data) user_data = user_data[:actual_udl] elif charset == 'utf-16': # UTF-16 aka UCS2, S6.2.3 try: user_data = unpackUCS2(data) user_data = user_data[:actual_udl] except UnicodeDecodeError as e: raise PDUDecodeError('PDU corrupted: %s' % e) else: raise PDUDecodeError('tp_dcs of 0x%02x (%s), charset %s' % (tp_dcs, describe_tp_dcs(tp_dcs), charset)) return user_data, user_data_headers
def attempt_encoding(u, limit=160): '''Given the input unicode string attempt to encode it for SMS delivery. This means taking some arbitrary input text and fitting it, encoded, within the 160 septet limit (overridable) of an SMS packet. Some characters in the input may not be encodable at all, some may encode to multiple characters in the SMS packet. The rules for handling the input text are: 1. attempt to encode with GSM-0338, 2. remove typographical characters (eg, curly quotes), 3. attempt to represent without accents, 4. attempt to replace common characters that encode to double-width characters, and 5. give up and use UTF-16. Using UTF-16 is a last resort since it halves the message length. Returns two things: the potentially-translated and truncated string and the string containing any excess characters. ''' # Attempt to encode with GSM-0338 + translations gsm = gsm0338.Codec() l = [] e = [] s = '' for c in u: # replace all control codes if ord(c) < 0x20 and c not in '\r\n': c = u'?' try: t = gsm.encode(c) except UnicodeError: translated = remove_typography(c) translated = remove_accent(translated) if c == translated: # no translation possible; can't encode in GSM break c = translated try: t = gsm.encode(c) except UnicodeError: # translated but we still can't GSM encode break s += t if len(s) > limit: e.append(c) else: l.append(c) else: if e: # one last thing to try.... s = u''.join(l) + u''.join(e) s = replace_gsm_doubles(s) if len(s) <= 160: return (s, u'') return (u''.join(l), u''.join(e)) # encode using UTF-16 l = [] e = list(u) while e: c = e.pop(0) t = u''.join(l) + c if len(t.encode('utf16')) > 140: break l.append(c) return (u''.join(l), u''.join(e))
def rx_deliver_sm(pdu): global smpp_messages if not isinstance(pdu, smpplib.command.DeliverSM): mid = pdu.sequence log.debug('PDU Seq. #%s is not a DeliverSM' % pdu.sequence) return _udhi = pdu.esm_class & smpplib.consts.SMPP_GSMFEAT_UDHI log.info("--> RX SMS ref(%s) DataCoding (%s), TON(%s), UHDI(%s)" % (pdu.user_message_reference, pdu.data_coding, pdu.dest_addr_ton, _udhi)) gsm_shift_codec = gsm0338.Codec(single_shift_decode_map=gsm0338.SINGLE_SHIFT_CHARACTER_SET_SPANISH) code2charset = {1:'GSM03.38', 2:'UTF-8', 4:'UTF-8', 8:'UTF-16BE'} try: log_msg = pdu.short_message.decode(code2charset[pdu.data_coding]) except UnicodeDecodeError as ex: print str(ex) log_msg = binascii.hexlify(pdu.short_message) log.debug("RX SMS: [ %s ]" % log_msg) if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL: # We cannot deliver any SMS to SMPP_TON_INTL #return smpplib.consts.SMPP_ESME_RSYSERR log.error("Unable to handle SMS for %s: SMPP_TON_INTL" % (pdu.destination_addr)) return smpplib.consts.SMPP_ESME_RINVDSTTON try: valid_src = num.is_number_known(pdu.source_addr) if (not num.is_number_internal(pdu.source_addr) and not sub.is_authorized(pdu.source_addr, 0)): log.error("Unable to handle SMS from %s: Unauthorised" % (pdu.source_addr)) return smpplib.consts.SMPP_ESME_RINVSRCADR except SubscriberException as ex: log.error("Unable to handle SMS from %s: %s" % (pdu.source_addr, ex)) return smpplib.consts.SMPP_ESME_RINVSRCADR try: ret = check_extensions(pdu, log_msg, _udhi) if ret is not -1: return ret except Exception as ex: log.error("Extension module raises Error[%s], refusing SMS", str(ex)) return smpplib.consts.SMPP_ESME_RSYSERR except ExtensionExceptionOK as ex: log.error("Extension module raises Error[%s], but sending OK to MS", str(ex)) return smpplib.consts.SMPP_ESME_ROK if pdu.user_message_reference is None: log.warning("PDU has no user_message_reference.") pdu.user_message_reference = 0 try: pdu.destination_addr = num.fivetoeleven(pdu.source_addr, pdu.destination_addr, log) dest_ip = num.get_current_bts(pdu.destination_addr) except NumberingException as ex: log.error("Unable to handle SMS for %s: %s" % (pdu.destination_addr, ex)) if no_unknown_delivery == 1: return smpplib.consts.SMPP_ESME_RINVDSTADR try: dest_ip = num.get_site_ip_hlr(pdu.destination_addr[:6]) if config.config['local_ip'] == dest_ip: return smpplib.consts.SMPP_ESME_RINVDSTADR log.debug('Will attempt forward to %s', dest_ip) except Exception as ex: log.error("Unable to handle SMS for %s: %s" % (pdu.destination_addr[:6], ex)) return smpplib.consts.SMPP_ESME_RINVDSTADR log.debug('Registered Delivery: %s' % pdu.registered_delivery) log.debug('ESM Class %s' % pdu.esm_class) if int(pdu.esm_class) == 4: pdu.esm_class = 8 log.info('--> RX Delivery Report for Uref(%s): %s ' % (pdu.user_message_reference, pdu.short_message)) pdu.short_message = ' ' if config.config['local_ip'] == dest_ip: ret = local_pass_pdu(pdu) if pdu.esm_class != 8: sms.save(pdu.source_addr, pdu.destination_addr, 'SMS_LOCAL') return smpplib.consts.SMPP_ESME_ROK # SMS destination has a Webphone Prefix. if (hasattr(config, 'sip_central_ip_address') and isinstance(config.sip_central_ip_address, list) and config.sip_central_ip_address[0] == dest_ip): log.info('--> RX SMS(%s) for Webphone.', pdu.user_message_reference) if _udhi: try: ret = multipart(pdu) if not ret: # We Have not got all the parts yet. log.debug("Accepting Part: %s" % binascii.hexlify(pdu.short_message)) return smpplib.consts.SMPP_ESME_ROK else: log.debug("Full SMS Message: %s" % ret.decode(code2charset[pdu.data_coding])) pdu.short_message = ret except UDHError: # Just Accept and drop it. return smpplib.consts.SMPP_ESME_ROK if sms.webphone_sms(pdu.source_addr, pdu.destination_addr, pdu.short_message, pdu.data_coding): return smpplib.consts.SMPP_ESME_ROK else: return smpplib.consts.SMPP_ESME_RSYSERR else: # Pass it off to the Queue. what to do here? send it to the remote site? # via rapi? # Should we decode the entire message? # what if the remote site is down try: #tremote = threading.Thread(target=remote_pass_pdu) stat = remote_pass_pdu(pdu, dest_ip) if stat == smpplib.consts.SMPP_ESME_ROK and pdu.esm_class != 8: sms.save(pdu.source_addr, pdu.destination_addr, 'SMS_INTERNAL') return stat except Exception as e: log.error("exception from remote_pass_pdu %s", str(e)) # Something bad happened return smpplib.consts.SMPP_ESME_RSYSERR
def rx_deliver_sm(pdu): global smpp_messages if not isinstance(pdu, smpplib.command.DeliverSM): mid = pdu.sequence log.debug('PDU Seq. #%s is not a DeliverSM' % pdu.sequence) return _udhi = pdu.esm_class & smpplib.consts.SMPP_GSMFEAT_UDHI log.info("--> RX SMS ref(%s) DataCoding (%s), TON(%s), UHDI(%s)" % (pdu.user_message_reference, pdu.data_coding, pdu.dest_addr_ton, _udhi)) gsm_shift_codec = gsm0338.Codec( single_shift_decode_map=gsm0338.SINGLE_SHIFT_CHARACTER_SET_SPANISH) code2charset = {1: 'GSM03.38', 2: 'UTF-8', 4: 'UTF-8', 8: 'UTF-16BE'} _start = 0 if _udhi: try: _udh_length = ord(pdu.short_message[:1]) _start = _udh_length + 1 udh = parse_udh(pdu.short_message[:_udh_length + 1]) if udh is False: log.warning('Accept and drop message.. %s', binascii.hexlify(pdu.short_message)) return smpplib.consts.SMPP_ESME_ROK ''' if udh['part_num'] == 1: smpp_messages[udh['csms_ref']]=[] log.debug('Part %s of %s' % (udh['part_num'], udh['parts'])) smpp_messages[udh['csms_ref']].append(pdu.short_message[_start:]) if udh['part_num'] == udh['parts']: _final = ''.join(smpp_messages[udh['csms_ref']]) smpp_messages[udh['csms_ref']] = None log.debug("Full SMS Message: %s" % _final.decode(code2charset[pdu.data_coding])) #local_submit_one('LOCAL_TEST', pdu.destination_addr, _final.decode(code2charset[pdu.data_coding])) ''' except Exception as ex: log.debug("UDHI: Other Exception: %s", str(ex)) template = "An exception of type {0} occurred. Arguments:\n{1!r}" message = template.format(type(ex).__name__, ex.args) log.debug(message) return smpplib.consts.SMPP_ESME_RSYSERR try: log_msg = pdu.short_message[_start:].decode( code2charset[pdu.data_coding]) except UnicodeDecodeError as ex: log_msg = binascii.hexlify(pdu.short_message[_start:]) print str(ex) log.debug("RX SMS: [ %s ]" % log_msg) if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL: # We cannot deliver any SMS to SMPP_TON_INTL #return smpplib.consts.SMPP_ESME_RSYSERR log.error("Unable to handle SMS for %s: SMPP_TON_INTL" % (pdu.destination_addr)) return smpplib.consts.SMPP_ESME_RINVDSTTON try: valid_src = num.is_number_known(pdu.source_addr) if (not num.is_number_internal(pdu.source_addr) and not sub.is_authorized(pdu.source_addr, 0)): log.error("Unable to handle SMS from %s: Unauthorised" % (pdu.source_addr)) return smpplib.consts.SMPP_ESME_RINVSRCADR except SubscriberException as ex: log.error("Unable to handle SMS from %s: %s" % (pdu.source_addr, ex)) return smpplib.consts.SMPP_ESME_RINVSRCADR try: if check_extensions(pdu): return smpplib.consts.SMPP_ESME_ROK except Exception as ex: log.error(str(ex)) return smpplib.consts.SMPP_ESME_RSYSERR if pdu.user_message_reference is None: log.warning("PDU has no user_message_reference.") pdu.user_message_reference = 0 try: pdu.destination_addr = num.fivetoeleven(pdu.source_addr, pdu.destination_addr, log) dest_ip = num.get_current_bts(pdu.destination_addr) except NumberingException as ex: log.error("Unable to handle SMS for %s: %s" % (pdu.destination_addr, ex)) if no_unknown_delivery == 1: return smpplib.consts.SMPP_ESME_RINVDSTADR try: dest_ip = num.get_site_ip_hlr(pdu.destination_addr[:6]) if config.config['local_ip'] == dest_ip: return smpplib.consts.SMPP_ESME_RINVDSTADR log.debug('Will attempt forward to %s', dest_ip) except Exception as ex: log.error("Unable to handle SMS for %s: %s" % (pdu.destination_addr[:6], ex)) return smpplib.consts.SMPP_ESME_RINVDSTADR log.debug('Registered Delivery: %s' % pdu.registered_delivery) log.debug('ESM Class %s' % pdu.esm_class) if int(pdu.esm_class) == 4: pdu.esm_class = 8 log.info('--> RX Delivery Report for Uref(%s): %s ' % (pdu.user_message_reference, pdu.short_message)) pdu.short_message = ' ' if config.config['local_ip'] == dest_ip: ret = local_pass_pdu(pdu) if pdu.esm_class != 8: sms.save(pdu.source_addr, pdu.destination_addr, 'SMS_LOCAL') return smpplib.consts.SMPP_ESME_ROK if (hasattr(config, 'sip_central_ip_address') and isinstance(config.sip_central_ip_address, list) and config.sip_central_ip_address[0] == dest_ip): log.info('--> RX SMS for Webphone(%s): %s ' % (pdu.user_message_reference, pdu.short_message)) if sms.webphone_sms(pdu.source_addr, pdu.destination_addr, pdu.short_message, pdu.data_coding): return smpplib.consts.SMPP_ESME_ROK else: return smpplib.consts.SMPP_ESME_RSYSERR else: # Pass it off to the Queue. what to do here? send it to the remote site? # via rapi? # Should we decode the entire message? # what if the remote site is down try: #tremote = threading.Thread(target=remote_pass_pdu) stat = remote_pass_pdu(pdu, dest_ip) if stat == smpplib.consts.SMPP_ESME_ROK and pdu.esm_class != 8: sms.save(pdu.source_addr, pdu.destination_addr, 'SMS_INTERNAL') return stat except Exception as e: log.error("exception from remote_pass_pdu %s", str(e)) # Something bad happened return smpplib.consts.SMPP_ESME_RSYSERR