Commit bdb39dd3 by Owo Sugiana

Kali pertama

0 parents
dist
*egg-info
0.1 2019-02-04
--------------
- Kali pertama
include *.txt *.rst *.py
recursive-include opensipkd *.py
Perangkat untuk Daemon ISO8583
==============================
Di dalamnya ada:
* Class terkait pembuatan daemon TCP/IP, baik sebagai server maupun client.
* Class terkait dokumen ISO8583.
File mode changed
from ISO8583.ISO8583 import ISO8583
class BaseISO8583(ISO8583):
def __init__(self, from_iso=None, debug=False):
ISO8583.__init__(self, debug=debug)
self.from_iso = from_iso
bits = self.get_bit_definition()
for bit in bits:
short_name, long_name, type_, size, valueType = bits[bit]
self.redefineBit(
bit, short_name, long_name, type_, size, valueType)
def redefineBit(self, bit, short_name, long_name, type_, size, valueType):
if type_ in ('LL', 'LLL'):
LenForm = 'A' # ASCII
else:
LenForm = '-'
format_ = 'A' # ASCII
ISO8583.redefineBit(
self, bit, short_name, long_name, type_, LenForm, size,
valueType, format_)
def getRawIso(self):
raw = ISO8583.getRawIso(self)
if isinstance(raw, bytes):
raw = raw.decode('utf-8')
return raw.upper()
def setIsoContent(self, raw):
ISO8583.setIsoContent(self, raw.encode('utf-8'))
def get_bit_definition(self):
return {}
def get_value(self, bit):
v = self.getBit(bit)
type_ = self.getBitType(bit)
if type_.find('LL') < 0:
return v
size_length = len(type_)
return v[size_length:]
def get_values(self):
r = {}
for item in self.getBitsAndValues():
bit = int(item['bit'])
r[bit] = self.get_value(bit)
return r
def copy(self, bits=[], from_iso=None):
if not from_iso:
from_iso = self.from_iso
values = from_iso.get_values()
for bit in values:
if bits and bit not in bits: # Hanya bit tertentu ?
continue
value = values[bit]
self.setBit(bit, value)
from .doc import Doc
from .job import Job
from datetime import datetime
from ISO8583.ISOErrors import BitNotSet
from opensipkd.string import (
DateTimeVar,
exception_message,
)
from opensipkd.waktu import create_datetime
from ..doc import BaseISO8583
from .structure import (
NETWORK_BITS,
RC_OK,
RC_OTHER_ERROR,
)
class Doc(BaseISO8583):
def __init__(self, *args, **kwargs):
BaseISO8583.__init__(self, *args, **kwargs)
self.transmission = DateTimeVar()
if self.from_iso:
self.set_response()
def get_bit_definition(self):
return NETWORK_BITS
def get_func_name(self):
return self.is_echo_request() or self.is_sign_on_request() or \
self.is_sign_off_request()
# Dipanggil Job.process()
def process(self):
func_name = self.from_iso.get_func_name()
if func_name:
func = getattr(self, func_name)
func()
else:
self.ack_function_not_found()
def is_response(self):
return self.is_network_response()
def set_response(self):
if self.from_iso.is_network_request():
self.set_network_response()
def is_ok_response(self):
try:
return self.getBit(39) == RC_OK
except BitNotSet:
pass
def set_transmission(self):
kini = datetime.now()
self.setBit(7, kini.strftime('%m%d%H%M%S'))
def get_transmission(self):
raw = self.get_value(7)
self.transmission.set_raw(raw)
# without time zone
t = self.transmission.get_value()
# with time zone
return create_datetime(
t.year, t.month, t.day, t.hour, t.minute, t.second)
def set_stan(self): # System Trace Audit Number
kini = datetime.now()
self.setBit(11, kini.strftime('%H%M%S'))
def get_stan(self):
return self.get_value(11)
###########
# Network #
###########
def set_network_request(self):
self.setMTI('0800')
self.set_transmission()
self.set_stan()
def set_network_response(self):
self.setMTI('0810')
self.copy([11, 70])
self.set_transmission()
def set_func_code(self, code):
self.set_network_request()
self.setBit(70, code)
def get_func_code(self):
return self.getBit(70)
def is_network_request(self):
return self.getMTI() == '0800'
def is_network_response(self):
return self.getMTI() == '0810'
###################
# Network Sign On #
###################
def is_sign_on_request(self):
return self.is_network_request() and \
self.get_func_code() == '001' and \
'sign_on_response'
def is_sign_on_response(self):
return self.is_network_response() and \
self.get_func_code() == '001'
def sign_on_request(self):
self.set_func_code('001')
def sign_on_response(self):
self.set_network_response()
self.ack()
#####################
# Network Echo Test #
#####################
def is_echo_request(self):
return self.is_network_request() and \
self.get_func_code() == '301' and \
'echo_response'
def is_echo_response(self):
return self.is_network_response() and \
self.get_func_code() == '301'
def is_echo(self):
return self.is_echo_request() or self.is_echo_response()
def echo_request(self):
self.set_func_code('301')
def echo_response(self):
self.set_network_response()
self.ack()
####################
# Network Sign Off #
####################
def is_sign_off_request(self):
return self.is_network_request() and \
self.get_func_code() == '002' and \
'sign_off_response'
def is_sign_off_response(self):
return self.is_network_request() and self.get_func_code() == '002'
def is_sign_off(self):
return self.is_sign_off_request() or self.is_sign_off_response()
def sign_off_request(self):
self.set_func_code('002')
def sign_off_response(self):
self.set_network_response()
self.ack()
###################
# Acknowledgement #
###################
def ack(self, code=RC_OK, msg=''):
self.setBit(39, code)
self.set_transmission()
self.ack_log(msg)
def ack_log(self, msg):
pass
def ack_other(self, msg='Ada masalah yang belum dipahami'):
self.ack(RC_OTHER_ERROR, msg)
def ack_function_not_found(self):
self.ack_other('Fungsi tidak ditemukan')
def ack_unknown(self):
self.ack_other('Unknown')
from time import time
from . import Doc
class Job:
def __init__(self, connection):
self.connection = connection
self.conf = connection.conf
self.echo_time = None
self.sign_on_time = False
self.do_echo = self.is_need_echo()
def get_iso_class(self):
return Doc
def create_iso(self, from_iso=None):
cls = self.get_iso_class()
return cls(from_iso=from_iso)
def get_iso(self):
if self.do_echo:
self.do_echo = False
return self.echo_request()
if not self.is_need_echo():
return
jeda = time() - self.connection.connected_time
is_timeout = jeda >= self.conf['timeout'] - 5
if not self.echo_time and is_timeout:
return self.echo_request()
# Dipanggil Parser.run()
def raw_to_iso(self, raw):
iso = self.create_iso()
iso.setIsoContent(raw)
return iso
# Dipanggil Connection.process()
def process(self, from_iso):
if from_iso.is_response():
if self.sign_on_time:
return
return self.sign_on_request()
iso = self.create_iso(from_iso=from_iso)
iso.process()
return iso
# Dipanggil forwarder.py
def on_receive_raw(self, raw):
self.echo_time = None
def is_need_echo(self):
return self.conf['echo']
def echo_request(self):
self.echo_time = time()
iso = self.create_iso()
iso.echo_request()
return iso
def sign_on_request(self):
iso = self.create_iso()
iso.sign_on_request()
self.sign_on_time = time()
return iso
# Penerjemahan file PDF Iso Spesification Pajak Daerah-BJB Ver 1.7
#################
# Redefine bits #
#################
NETWORK_BITS = {
7: ['Transmission', 'Transmission Datetime', 'N', 10, 'n'],
11: ['STAN', 'System Trace Audit Number', 'N', 6, 'n'],
39: ['Response', 'Response Code', 'N', 2, 'n'],
70: ['Function', 'System Function Code', 'N', 3, 'n'],
}
#########################
# Response Code, Bit 39 #
#########################
RC_OK = '00'
RC_OTHER_ERROR = '76'
import os
import sys
if sys.version_info.major == 2:
import imp
else:
import importlib.machinery
MODULES_PATH = os.path.split(__file__)[0]
MSG_NOT_FOUND = 'Module file {} not found'
def get_streamer_module(name):
filename = os.path.join(MODULES_PATH, 'streamer', name) + '.py'
if not os.path.exists(filename):
msg = MSG_NOT_FOUND.format(filename)
raise Exception(msg)
if sys.version_info.major == 2:
return imp.load_source(name, filename)
loader = importlib.machinery.SourceFileLoader(name, filename)
return loader.load_module()
class Streamer:
def __init__(self):
self.raw = ''
self.size = 0
# Override please.
def get(self, raw):
return raw
# Override please.
def set(self, raw):
return raw
from . import Streamer as BaseStreamer
# Size Nama Keterangan
# 3 Prefix ISO
# 2 Product Indicator Kode produk standard paket data yang digunakan. Nilai
# yang diperbolehkan adalah 04.
# 2 Release Number Release number dari produk paket data SPO yang
# digunakan.
# Nilainya adalah 10.
# 3 Status Untuk menginformasikan masalah yang timbul pada saat
# interpretasi paket data yang diterima. Bilamana sebuah
# paket data ditolak oleh Sistem SPO, maka Sistem SPO
# akan menuliskan kode bit map dari paket data yang
# ditolak dan akan mengirimkan kembali paket data ini ke
# Host Bank. Kemudian pada kode pengenal paket data
# (MTI) pada bit pertama akan diganti dengan 9.
# Contoh : request 0200 -> response 9200
# 1 Originator Code Kode entity pengirim paket data. Nilainya adalah
# 1 : Semua paket data (kecuali Network Mgt).
# 6 : Network Management Message
# 1 Responder Code Kode entity yang memberikan tanggapan terhadap sebuah
# paket data. Nilainya adalah:
# 7 : Interchange (Sistem SPO).
#
# Contoh saat digabungkan : ISO041000017
class Streamer(BaseStreamer):
# Override Stremer.get
def __init__(self, *args, **kwargs):
BaseStreamer.__init__(self, *args, **kwargs)
self.header = None
def get(self, raw):
self.header = raw[:12]
self.raw = raw[12:]
return self.raw
def set(self, raw):
header = 'ISO0410000'
if raw[:2] == '08':
header += '6'
else:
header += '1'
header += '7'
return header + raw
from . import Streamer as BaseStreamer
# 4 byte pertama adalah raw length
class Streamer(BaseStreamer):
def get_size(self, raw):
return int(raw[:4])
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 4:
self.raw = raw
return
size = self.size = self.get_size(raw)
self.raw = ''
raw = raw[4:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
size = str(len(raw)).zfill(4)
return size + raw
from .bjb import Streamer as BaseStreamer
# 4 byte pertama adalah raw length
# Karakter terakhir adalah hexa 03
class Streamer(BaseStreamer):
# Override Stremer.get
def get(self, raw):
raw_iso = BaseStreamer.get(self, raw)
if raw_iso:
return raw_iso[:-1] # Hapus karakter terakhir hexa 03
# Override Stremer.set
def set(self, raw):
raw += '\x03'
return BaseStreamer.set(self, raw)
from . import Streamer as BaseStreamer
# 4 byte pertama adalah raw length
# Dalam 1 request bisa saja ada 2 transaksi, bahkan
# bisa saja string transaksi yang belum lengkap
class Streamer(BaseStreamer):
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 4:
self.raw = raw
return
size = self.size = int(raw[:4])
self.raw = ''
raw = raw[4:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
size = str(len(raw)).zfill(4)
return size + raw
from . import Streamer as BaseStreamer
# 3 byte pertama ISO
# 9 byte terakhir numerik
# Contoh: ISO011000017
# Network header BRI tidak memuat size data, praktis
# setiap request dianggap sebuah transaksi.
class Streamer(BaseStreamer):
# Override Stremer.get
def __init__(self, *args, **kwargs):
BaseStreamer.__init__(self, *args, **kwargs)
self.header = None
def get(self, raw):
self.header = raw[:12]
self.raw = raw[12:]
return self.raw
def set(self, raw):
header = self.header or 'ISO'.ljust(12, '0')
return header + raw
from . import Streamer as BaseStreamer
# 2 byte pertama size
# Dalam 1 request bisa saja ada 2 transaksi, bahkan
# bisa saja string transaksi yang belum lengkap
class Streamer(BaseStreamer):
def get_size(self, raw):
a, b = raw
a = ord(a) * 256
b = ord(b)
return a + b
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 2:
self.raw = raw
return
size = self.size = self.get_size(raw[:2])
self.raw = ''
raw = raw[2:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
raw = raw.upper()
size = len(raw)
a = size % 256
b = size / 256
header = chr(b) + chr(a)
return header + raw
from . import Streamer as BaseStreamer
# 4 byte pertama adalah raw length
# Dalam 1 request bisa saja ada 2 transaksi, bahkan
# bisa saja string transaksi yang belum lengkap
class Streamer(BaseStreamer):
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 4:
self.raw = raw
return
size = self.size = int(raw[:4])
self.raw = ''
raw = raw[4:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
size = str(len(raw)).zfill(4)
return size + raw
from . import Streamer as BaseStreamer
# 4 byte pertama adalah raw length
# Dalam 1 request bisa saja ada 2 transaksi, bahkan
# bisa saja string transaksi yang belum lengkap
class Streamer(BaseStreamer):
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 4:
self.raw = raw
return
size = self.size = int(raw[:4])
self.raw = ''
raw = raw[4:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
size = str(len(raw)).zfill(4)
return size + raw
from . import Streamer as BaseStreamer
# 4 byte pertama adalah raw length
class Streamer(BaseStreamer):
def get_size(self, raw):
return int(raw[:4])
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 4:
self.raw = raw
return
size = self.size = self.get_size(raw)
self.raw = ''
raw = raw[4:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
size = str(len(raw)).zfill(4)
return size + raw
from . import Streamer as BaseStreamer
# 2 byte pertama size
class Streamer(BaseStreamer):
def get_size(self, raw):
a, b = raw
a = ord(a) * 256
b = ord(b)
return a + b
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 2:
self.raw = raw
return
size = self.size = self.get_size(raw[:2])
self.raw = ''
raw = raw[2:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
size = len(raw)
a = size / 256
b = size % 256
header = chr(a) + chr(b)
return header + raw
# Tanpa network header
from . import Streamer
from . import Streamer as BaseStreamer
# 2 byte pertama size
# Dalam 1 request bisa saja ada 2 transaksi, bahkan
# bisa saja string transaksi yang belum lengkap
class Streamer(BaseStreamer):
def get_size(self, raw):
a, b = raw
a = ord(a)
b = ord(b) * 256
return a + b
# Override Stremer.get
def get(self, raw):
if self.size:
size = self.size - len(self.raw)
else:
raw = self.raw + raw
if len(raw) < 2:
self.raw = raw
return
size = self.size = self.get_size(raw[:2])
self.raw = ''
raw = raw[2:]
self.raw += raw[:size]
if len(self.raw) == self.size:
raw_iso = self.raw
self.size = 0
self.raw = raw[size:] # Sisa
return raw_iso
self.raw += raw[size:]
# Override Stremer.set
def set(self, raw):
size = len(raw)
a = size % 256
b = size / 256
header = chr(a) + chr(b)
return header + raw
File mode changed
import sys
import socket
from .connection import Connection
class Client(Connection):
def __init__(self, conf):
Connection.__init__(self, conf)
self.address = (conf['ip'], conf['port'])
self.request = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def connect(self):
self.request.connect(self.address)
self.set_connected_time()
def on_refused(self, err):
self.close()
def before_loop(self):
if sys.version_info.major == 2:
self.before_loop_v2()
return
try:
self.connect()
Connection.before_loop(self)
except OSError as err:
self.on_socket_error(err)
except socket.timeout as err:
self.on_socket_error(err)
except ConnectionRefusedError as err:
self.on_refused(err)
except socket.error as err:
self.on_socket_error(err)
def before_loop_v2(self):
try:
self.connect()
Connection.before_loop(self)
except OSError as err:
self.on_socket_error(err)
except socket.timeout as err:
self.on_socket_error(err)
except socket.error as err:
if err.errno == 111:
self.on_refused(err)
else:
self.on_socket_error(err)
import sys
import socket
from time import (
time,
sleep,
)
def join_ip_port(ip, port):
return ':'.join([ip, str(port)])
class Connection:
def __init__(self, conf=None):
self.conf = conf
self.connected_time = None
self.running = False
def process(self, raw):
return self.job.process(raw)
def get_streamer(self):
cls = self.conf['streamer_cls']
return cls()
def is_loop(self):
sleep(1)
return self.running
def set_timeout(self):
try:
self.request.settimeout(self.get_timeout())
except OSError:
pass
def before_loop(self):
self.set_timeout()
self.streamer = self.get_streamer()
self.running = True
self.busy = False
self.create_job()
def get_receive_size(self):
return 2048
def get_timeout(self):
return self.conf['timeout']
def run(self):
self.before_loop()
while self.is_loop():
if not self.connected_time:
continue
self.busy = True
self.on_loop()
self.busy = False
def on_loop(self):
raw = self.receive_raw()
if not raw:
if self.is_timeout():
self.close_because_timeout()
return
raw = self.streamer.get(raw)
if not raw:
return
raw = self.process(raw)
if raw:
self.send(raw)
def receive_raw(self):
try:
raw = self.request.recv(self.get_receive_size())
if isinstance(raw, bytes):
raw = raw.decode('utf-8')
if raw:
self.on_receive_raw(raw)
return raw
except socket.error as err:
self.on_socket_error(err)
except socket.timeout as err:
self.on_socket_error(err)
def is_connected(self):
return self.connected_time
def set_connected_time(self):
self.connected_time = time()
def on_receive_raw(self, raw):
self.set_connected_time()
self.job.on_receive_raw(raw)
def close_because_timeout(self):
self.close()
def is_timeout(self):
if self.connected_time:
return time() - self.connected_time > self.get_timeout()
return True
def raw_for_send(self, raw):
return self.streamer.set(raw)
def send(self, raw):
raw = self.raw_for_send(raw)
if sys.version_info.major > 2:
raw = raw.encode('utf-8')
try:
self.request.sendall(raw)
except socket.error as err:
self.on_socket_error(err)
def on_socket_error(self, err):
self.close()
def stop_loop(self):
self.running = False
self.connected_time = None
def close(self):
self.stop_loop()
try:
self.request.settimeout(1)
self.request.close()
except socket.error:
pass
def create_job(self):
module = self.conf['module_obj']
self.job = module.Job(self)
class ConnectionManager:
def __init__(self):
# Kumpulan class Connection
self.conns = []
def count(self, ip_port):
count = 0
for this_ip_port, x in self.conns:
if this_ip_port == ip_port:
count += 1
return count
def get_first(self, ip_port):
index = -1
for this_ip_port, conn in self.conns:
index += 1
if this_ip_port == ip_port:
return conn, index
return None, None
def get_last(self, ip_port):
index = -1
found_conn = found_index = None
for this_ip_port, conn in self.conns:
index += 1
if this_ip_port == ip_port:
found_conn = conn
found_index = index
return found_conn, found_index
def add(self, conn):
ip_port = join_ip_port(conn.conf['ip'], conn.conf['port'])
old_conn, index = self.get_first(ip_port)
self.conns.append([ip_port, conn])
count = self.count(ip_port)
if old_conn:
# Hanya boleh satu connection, jadi tutup sebelumnya
self.close_old_connection(old_conn)
def close_old_connection(self, old_conn):
old_conn.close()
sleep(2)
def remove(self, index):
ip_port, conn = self.conns[index]
del self.conns[index][0] # ip_port
del self.conns[index][0] # connection object
del self.conns[index]
sleep(0.1)
count = self.count(ip_port)
def __iter__(self): # for loop
for ip_port, conn in self.conns:
yield [ip_port, conn]
def __contains__(self, ip_port): # in operator
conn, index = self.get_first(ip_port)
if index is None:
return
return index > -1
def __getitem__(self, s):
if isinstance(s, str): # key like dictionary
ip_port = s
conn, index = self.get_last(ip_port)
return conn
return self.conns[s] # slice like list
import sys
from .connection import Connection
if sys.version_info.major == 2:
from SocketServer import (
BaseRequestHandler,
TCPServer,
ThreadingMixIn,
)
else:
from socketserver import (
BaseRequestHandler,
TCPServer,
ThreadingMixIn,
)
class Server(ThreadingMixIn, TCPServer):
allow_reuse_address = True
class RequestHandler(BaseRequestHandler, Connection):
def handle(self): # Override BaseRequestHandler.handle()
self.set_connected_time()
self.run()
import os
from setuptools import setup
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.rst')) as f:
README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
CHANGES = f.read()
line = CHANGES.splitlines()[0]
version = line.split()[0]
requires = [
'ebcdic',
'ISO8583',
]
packages = [
'opensipkd',
'opensipkd.tcp',
'opensipkd.tcp',
'opensipkd.streamer',
'opensipkd.iso8583',
'opensipkd.iso8583.network',
]
setup(
name='opensipkd-iso8583',
version=version,
description='Perangkat untuk Daemon ISO8583',
long_description=README + '\n\n' + CHANGES,
author='Owo Sugiana',
author_email='sugiana@gmail.com',
license='PostgreSQL License',
packages=packages,
install_requires=requires,
zip_safe=False,
)
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!