npn47_iso8583 0.1.5.a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/8583.rb +5 -0
- data/lib/iso8583/berlin.rb +82 -0
- data/lib/iso8583/bitmap.rb +117 -0
- data/lib/iso8583/codec.rb +196 -0
- data/lib/iso8583/exception.rb +4 -0
- data/lib/iso8583/field.rb +90 -0
- data/lib/iso8583/fields.rb +75 -0
- data/lib/iso8583/message.rb +419 -0
- data/lib/iso8583/util.rb +94 -0
- data/lib/iso8583/version.rb +4 -0
- data/lib/iso8583.rb +13 -0
- data/test/BitmapTests.rb +80 -0
- data/test/message_test.rb +163 -0
- data/test/test_codec.rb +124 -0
- data/test/test_fields.rb +246 -0
- data/test/test_seperate_msg.rb +22 -0
- data/test/test_util.rb +32 -0
- metadata +66 -0
@@ -0,0 +1,419 @@
|
|
1
|
+
# Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
|
2
|
+
# MIT License, for details, see the LICENSE file accompaning
|
3
|
+
# this distribution
|
4
|
+
|
5
|
+
module ISO8583
|
6
|
+
|
7
|
+
# The class `Message` defines functionality to describe classes
|
8
|
+
# representing different type of messages, or message families.
|
9
|
+
# A message family consists of a number of possible message types that
|
10
|
+
# are allowed, and a way of naming and encoding the bitmaps allowed in
|
11
|
+
# the messages.
|
12
|
+
#
|
13
|
+
# To create your own message, start by subclassing Message:
|
14
|
+
#
|
15
|
+
# class MyMessage < Message
|
16
|
+
# (...)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# the subtyped message should be told how the MTI is encoded:
|
20
|
+
#
|
21
|
+
# class MyMessage < Message
|
22
|
+
# mti_format N, :length => 4
|
23
|
+
# (...)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# `N` above is an instance of Field which encodes numbers into their
|
27
|
+
# ASCII representations in a fixed length field. The option `length=>4`
|
28
|
+
# indicates the length of the fixed field.
|
29
|
+
#
|
30
|
+
# Next, the allowed message types are specified:
|
31
|
+
#
|
32
|
+
# class MyMessage < Message
|
33
|
+
# (...)
|
34
|
+
# mti 1100, "Authorization Request Acquirer Gateway"
|
35
|
+
# mti 1110, "Authorization Request Response Issuer Gateway"
|
36
|
+
# (...)
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# This basically defines to message types, 1100 and 1110 which may
|
40
|
+
# be accessed later either via their name or value:
|
41
|
+
#
|
42
|
+
# mes = MyMessage.new 1100
|
43
|
+
#
|
44
|
+
# or
|
45
|
+
# mes = MyMessage.new "Authorization Request Acquirer Gateway"
|
46
|
+
#
|
47
|
+
# or
|
48
|
+
# mes = MyMessage.new
|
49
|
+
# mes.mti = 1110 # or Auth. Req. Acq. Gateway ...
|
50
|
+
#
|
51
|
+
# Finally the allowed bitmaps, their names and the encoding rules are
|
52
|
+
# specified:
|
53
|
+
#
|
54
|
+
# class MyMessage < Message
|
55
|
+
# (...)
|
56
|
+
# bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
|
57
|
+
# bmp 3, "Processing Code", N, :length => 6
|
58
|
+
# bmp 4, "Amount (Transaction)", N, :length => 12
|
59
|
+
# bmp 6, "Amount, Cardholder Billing" , N, :length => 12
|
60
|
+
# (...)
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# The example above defines four bitmaps (2,3,4 and 6), and provides
|
64
|
+
# their bitmap number and description. The PAN field is variable length
|
65
|
+
# encoded (LL length indicator, ASCII, contents numeric, ASCII) and the
|
66
|
+
# maximum length of the field is limited to 19 using options.
|
67
|
+
#
|
68
|
+
# The other fields are fixed length numeric ASCII fields (the length of the fields is
|
69
|
+
# indicated by the `:length` options.)
|
70
|
+
#
|
71
|
+
# This message may be used as follows in order to interpret a received message.:
|
72
|
+
#
|
73
|
+
# mes = MyMessage.parse inputData
|
74
|
+
# puts mes[2] # prints the PAN from the message.
|
75
|
+
#
|
76
|
+
# Constructing own messages works as follows:
|
77
|
+
#
|
78
|
+
# mes = MyMessage.new 1100
|
79
|
+
# mes[2]= 474747474747
|
80
|
+
# # Alternatively
|
81
|
+
# mes["Primary Account Number (PAN)"]= 4747474747
|
82
|
+
# mes[3] = 1234 # padding is added by the Field en/decoder
|
83
|
+
# mes["Amount (Transaction)"] = 100
|
84
|
+
# mes[6] = 200
|
85
|
+
#
|
86
|
+
# the convenience method bmp_alias may be used in defining the class in
|
87
|
+
# order to provide direct access to fields using methods:
|
88
|
+
#
|
89
|
+
# class MyMessage < Message
|
90
|
+
# (...)
|
91
|
+
# bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
|
92
|
+
# (...)
|
93
|
+
# bmp_alias 2, :pan
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# this allows accessing fields in the following manner:
|
97
|
+
#
|
98
|
+
# mes = MyMessage.new 1100
|
99
|
+
# mes.pan = 474747474747
|
100
|
+
# puts mes.pan
|
101
|
+
# # Identical functionality to:
|
102
|
+
# mes[2]= 474747474747
|
103
|
+
# # or:
|
104
|
+
# mes["Primary Account Number (PAN)"]= 4747474747
|
105
|
+
#
|
106
|
+
# Most of the work in implementing a new set of message type lays in
|
107
|
+
# figuring out the correct fields to use defining the Message class via
|
108
|
+
# bmp.
|
109
|
+
#
|
110
|
+
class Message
|
111
|
+
|
112
|
+
# The value of the MTI (Message Type Indicator) of this message.
|
113
|
+
attr_reader :mti
|
114
|
+
|
115
|
+
# Instantiate a new instance of this type of Message
|
116
|
+
# optionally specifying an mti.
|
117
|
+
def initialize(mti = nil)
|
118
|
+
# values is an internal field used to collect all the
|
119
|
+
# bmp number | bmp name | field en/decoders | values
|
120
|
+
# which are set in this message.
|
121
|
+
@values = {}
|
122
|
+
self.mti = mti if mti
|
123
|
+
end
|
124
|
+
|
125
|
+
# Set the mti of the Message using either the actual value
|
126
|
+
# or the name of the message type that was defined using
|
127
|
+
# Message.mti
|
128
|
+
#
|
129
|
+
# === Example
|
130
|
+
# class MyMessage < Message
|
131
|
+
# (...)
|
132
|
+
# mti 1100, "Authorization Request Acquirer Gateway"
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# mes = MyMessage.new
|
136
|
+
# mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
|
137
|
+
def mti=(value)
|
138
|
+
num, name = _get_mti_definition(value)
|
139
|
+
@mti = num
|
140
|
+
end
|
141
|
+
|
142
|
+
# Set a field in this message, `key` is either the
|
143
|
+
# bmp number or it's name.
|
144
|
+
# ===Example
|
145
|
+
#
|
146
|
+
# mes = BlaBlaMessage.new
|
147
|
+
# mes[2]=47474747 # bmp 2 is generally the PAN
|
148
|
+
# mes["Primary Account Number"]=47474747 # if thats what you called the field in Message.bmp.
|
149
|
+
def []=(key, value)
|
150
|
+
bmp_def = _get_definition key
|
151
|
+
bmp_def.value = value
|
152
|
+
@values[bmp_def.bmp] = bmp_def
|
153
|
+
end
|
154
|
+
|
155
|
+
# Retrieve the decoded value of the contents of a bitmap
|
156
|
+
# described either by the bitmap number or name.
|
157
|
+
#
|
158
|
+
# ===Example
|
159
|
+
#
|
160
|
+
# mes = BlaBlaMessage.parse someMessageBytes
|
161
|
+
# mes[2] # bmp 2 is generally the PAN
|
162
|
+
# mes["Primary Account Number"] # if thats what you called the field in Message.bmp.
|
163
|
+
def [](key)
|
164
|
+
bmp_def = _get_definition key
|
165
|
+
bmp = @values[bmp_def.bmp]
|
166
|
+
bmp ? bmp.value : nil
|
167
|
+
end
|
168
|
+
|
169
|
+
# Retrieve the byte representation of the bitmap.
|
170
|
+
def to_b
|
171
|
+
raise ISO8583Exception.new "no MTI set!" unless mti
|
172
|
+
mti_enc = self.class._mti_format.encode(mti)
|
173
|
+
mti_enc << _body.join
|
174
|
+
end
|
175
|
+
|
176
|
+
# Returns a nicely formatted representation of this
|
177
|
+
# message.
|
178
|
+
def to_s
|
179
|
+
_mti_name = _get_mti_definition(mti)[1]
|
180
|
+
str = "MTI:#{mti} (#{_mti_name})\n\n"
|
181
|
+
_max = @values.values.max {|a,b|
|
182
|
+
a.name.length <=> b.name.length
|
183
|
+
}
|
184
|
+
_max_name = _max.name.length
|
185
|
+
|
186
|
+
@values.keys.sort.each{|bmp_num|
|
187
|
+
_bmp = @values[bmp_num]
|
188
|
+
str += ("%03d %#{_max_name}s : %s\n" % [bmp_num, _bmp.name, _bmp.value])
|
189
|
+
}
|
190
|
+
str
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
# METHODS starting with an underscore are meant for
|
195
|
+
# internal use only ...
|
196
|
+
|
197
|
+
# Returns an array of two byte arrays:
|
198
|
+
# [bitmap_bytes, message_bytes]
|
199
|
+
def _body
|
200
|
+
bitmap = Bitmap.new
|
201
|
+
message = ""
|
202
|
+
@values.keys.sort.each do |bmp_num|
|
203
|
+
bitmap.set(bmp_num)
|
204
|
+
enc_value = @values[bmp_num].encode
|
205
|
+
message << enc_value
|
206
|
+
end
|
207
|
+
[ bitmap.to_bytes, message ]
|
208
|
+
end
|
209
|
+
|
210
|
+
def _get_definition(key) #:nodoc:
|
211
|
+
b = self.class._definitions[key]
|
212
|
+
unless b
|
213
|
+
raise ISO8583Exception.new "no definition for field: #{key}"
|
214
|
+
end
|
215
|
+
b.dup
|
216
|
+
end
|
217
|
+
|
218
|
+
# return [mti_num, mti_value] for key being either
|
219
|
+
# mti_num or mti_value
|
220
|
+
def _get_mti_definition(key)
|
221
|
+
num_hash,name_hash = self.class._mti_definitions
|
222
|
+
if num_hash[key]
|
223
|
+
[key, num_hash[key]]
|
224
|
+
elsif name_hash[key]
|
225
|
+
[name_hash[key], key]
|
226
|
+
else
|
227
|
+
raise ISO8583Exception.new("MTI: #{key} not allowed!")
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class << self
|
232
|
+
|
233
|
+
# Defines how the message type indicator is encoded into bytes.
|
234
|
+
# ===Params:
|
235
|
+
# * field : the decoder/encoder for the MTI
|
236
|
+
# * opts : the options to pass to this field
|
237
|
+
#
|
238
|
+
# === Example
|
239
|
+
# class MyMessage < Message
|
240
|
+
# mti_format N, :length =>4
|
241
|
+
# (...)
|
242
|
+
# end
|
243
|
+
#
|
244
|
+
# encodes the mti of this message using the `N` field (fixed
|
245
|
+
# length, plain ASCII) and sets the fixed lengh to 4 bytes.
|
246
|
+
#
|
247
|
+
# See also: mti
|
248
|
+
def mti_format(field, opts)
|
249
|
+
f = field.dup
|
250
|
+
_handle_opts(f, opts)
|
251
|
+
@mti_format = f
|
252
|
+
end
|
253
|
+
|
254
|
+
# Defines the message types allowed for this type of message and
|
255
|
+
# gives them names
|
256
|
+
#
|
257
|
+
# === Example
|
258
|
+
# class MyMessage < Message
|
259
|
+
# (...)
|
260
|
+
# mti 1100, "Authorization Request Acquirer Gateway"
|
261
|
+
# end
|
262
|
+
#
|
263
|
+
# mes = MyMessage.new
|
264
|
+
# mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
|
265
|
+
#
|
266
|
+
# See Also: mti_format
|
267
|
+
def mti(value, name)
|
268
|
+
@mtis_v ||= {}
|
269
|
+
@mtis_n ||= {}
|
270
|
+
@mtis_v[value] = name
|
271
|
+
@mtis_n[name] = value
|
272
|
+
end
|
273
|
+
|
274
|
+
# Define a bitmap in the message
|
275
|
+
# ===Params:
|
276
|
+
# * bmp : bitmap number
|
277
|
+
# * name : human readable form
|
278
|
+
# * field : field for encoding/decoding
|
279
|
+
# * opts : options to pass to the field, e.g. length for fxed len fields.
|
280
|
+
#
|
281
|
+
# ===Example
|
282
|
+
#
|
283
|
+
# class MyMessage < Message
|
284
|
+
# bmp 2, "PAN", LLVAR_N, :max =>19
|
285
|
+
# (...)
|
286
|
+
# end
|
287
|
+
#
|
288
|
+
# creates a class MyMessage that allows for a bitmap 2 which
|
289
|
+
# is named "PAN" and encoded by an LLVAR_N Field. The maximum
|
290
|
+
# length of the value is 19. This class may be used as follows:
|
291
|
+
#
|
292
|
+
# mes = MyMessage.new
|
293
|
+
# mes[2] = 474747474747 # or mes["PAN"] = 4747474747
|
294
|
+
#
|
295
|
+
def bmp(bmp, name, field, opts = nil)
|
296
|
+
@defs ||= {}
|
297
|
+
|
298
|
+
field = field.dup
|
299
|
+
field.name = name
|
300
|
+
field.bmp = bmp
|
301
|
+
_handle_opts(field, opts) if opts
|
302
|
+
|
303
|
+
bmp_def = BMP.new bmp, name, field
|
304
|
+
|
305
|
+
@defs[bmp] = bmp_def
|
306
|
+
@defs[name] = bmp_def
|
307
|
+
end
|
308
|
+
|
309
|
+
# Create an alias to access bitmaps directly using a method.
|
310
|
+
# Example:
|
311
|
+
# class MyMessage < Message
|
312
|
+
# (...)
|
313
|
+
# bmp 2, "PAN", LLVAR_N
|
314
|
+
# (...)
|
315
|
+
# bmp_alias 2, :pan
|
316
|
+
# end #class
|
317
|
+
#
|
318
|
+
# would allow you to access the PAN like this:
|
319
|
+
#
|
320
|
+
# mes.pan = 1234
|
321
|
+
# puts mes.pan
|
322
|
+
#
|
323
|
+
# instead of:
|
324
|
+
#
|
325
|
+
# mes[2] = 1234
|
326
|
+
#
|
327
|
+
def bmp_alias(bmp, aliaz)
|
328
|
+
define_method (aliaz) {
|
329
|
+
bmp_ = @values[bmp]
|
330
|
+
bmp_ ? bmp_.value : nil
|
331
|
+
}
|
332
|
+
|
333
|
+
define_method ("#{aliaz}=") {|value|
|
334
|
+
self[bmp] = value
|
335
|
+
# bmp_def = _get_definition(bmp)
|
336
|
+
# bmp_def.value= value
|
337
|
+
# @values[bmp] = bmp_def
|
338
|
+
}
|
339
|
+
end
|
340
|
+
|
341
|
+
# Parse the bytes `str` returning a message of the defined type.
|
342
|
+
def parse(str)
|
343
|
+
message = self.new
|
344
|
+
message.mti, rest = _mti_format.parse(str)
|
345
|
+
bmp,rest = Bitmap.parse(rest)
|
346
|
+
bmp.each {|bit|
|
347
|
+
bmp_def = _definitions[bit]
|
348
|
+
value, rest = bmp_def.field.parse(rest)
|
349
|
+
message[bit] = value
|
350
|
+
}
|
351
|
+
message
|
352
|
+
end
|
353
|
+
|
354
|
+
# access the mti definitions applicable to the Message
|
355
|
+
#
|
356
|
+
# returns a pair of hashes containing:
|
357
|
+
#
|
358
|
+
# mti_value => mti_name
|
359
|
+
#
|
360
|
+
# mti_name => mti_value
|
361
|
+
#
|
362
|
+
def _mti_definitions
|
363
|
+
[@mtis_v, @mtis_n]
|
364
|
+
end
|
365
|
+
|
366
|
+
# Access the field definitions of this class, this is a
|
367
|
+
# hash containing [bmp_number, BMP] and [bitmap_name, BMP]
|
368
|
+
# pairs.
|
369
|
+
#
|
370
|
+
def _definitions
|
371
|
+
@defs
|
372
|
+
end
|
373
|
+
|
374
|
+
# Returns the field definition to format the mti.
|
375
|
+
def _mti_format
|
376
|
+
@mti_format
|
377
|
+
end
|
378
|
+
|
379
|
+
|
380
|
+
|
381
|
+
# METHODS starting with an underscore are meant for
|
382
|
+
# internal use only ...
|
383
|
+
|
384
|
+
# Modifies the field definitions of the fields passed
|
385
|
+
# in through the `bmp` and `mti_format` class methods.
|
386
|
+
#
|
387
|
+
def _handle_opts(field, opts)
|
388
|
+
opts.each_pair {|key, value|
|
389
|
+
key = (key.to_s+"=").to_sym
|
390
|
+
if field.respond_to?(key)
|
391
|
+
field.send(key, value)
|
392
|
+
else
|
393
|
+
warn "unknown option #{key} for #{field.name}"
|
394
|
+
end
|
395
|
+
}
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
# Internal class used to tie together name, bitmap number, field en/decoder
|
401
|
+
# and the value of the corresponding field
|
402
|
+
class BMP
|
403
|
+
attr_accessor :bmp
|
404
|
+
attr_accessor :name
|
405
|
+
attr_accessor :field
|
406
|
+
attr_accessor :value
|
407
|
+
|
408
|
+
def initialize(bmp, name, field)
|
409
|
+
@bmp = bmp
|
410
|
+
@name = name
|
411
|
+
@field = field
|
412
|
+
end
|
413
|
+
|
414
|
+
def encode
|
415
|
+
field.encode(value)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
end
|
data/lib/iso8583/util.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
|
2
|
+
# MIT License, for details, see the LICENSE file accompaning
|
3
|
+
# this distribution
|
4
|
+
|
5
|
+
module ISO8583
|
6
|
+
|
7
|
+
# general utilities
|
8
|
+
#
|
9
|
+
# Convert a String of bytes to their hex representation
|
10
|
+
# E.g.:
|
11
|
+
#
|
12
|
+
# b2hex "\x12\x34\xab" => "1234AB"
|
13
|
+
#
|
14
|
+
def b2hex(byte_string)
|
15
|
+
r = byte_string.unpack("H*")[0]
|
16
|
+
r.length > 1 ? r : " "
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Convert a String containing hex data to
|
21
|
+
# a String containing the corresponding bytes:
|
22
|
+
#
|
23
|
+
# hex2b "abcd12" => "\xa\cd\12"
|
24
|
+
#
|
25
|
+
def hex2b(hex_string)
|
26
|
+
string = hex_string.gsub(/\s+/, "")
|
27
|
+
raise ISO8583Exception.new("Invalid Hex chars: #{hex_string}") unless string =~ /^[A-Fa-f0-9]*$/
|
28
|
+
raise ISO8583Exception.new("Uneven number of Hex chars #{hex_string}") unless ( (string.length % 2) == 0)
|
29
|
+
[string].pack("H*")
|
30
|
+
end
|
31
|
+
|
32
|
+
def _conv(str, mapping)
|
33
|
+
_str = ""
|
34
|
+
str.each_byte{|byte|
|
35
|
+
_str << mapping[byte]
|
36
|
+
}
|
37
|
+
_str
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Convert a String of ASCII chars to EBCDIC
|
42
|
+
#
|
43
|
+
def ascii2ebcdic(ascii)
|
44
|
+
_conv(ascii, US_ASCII2IBM037)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Convert an EBCDIC String to ASCII
|
49
|
+
#
|
50
|
+
def ebcdic2ascii(ebcdic)
|
51
|
+
_conv(ebcdic, IBM0372US_ASCII)
|
52
|
+
end
|
53
|
+
|
54
|
+
# The charsets supported by iconv aren't guranteed. At the very least MACs don't support ebcdic,
|
55
|
+
# so providing rudimentary mappings here.
|
56
|
+
US_ASCII2IBM037 = [
|
57
|
+
0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
58
|
+
0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f,
|
59
|
+
0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61,
|
60
|
+
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f,
|
61
|
+
0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
|
62
|
+
0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xba, 0xe0, 0xbb, 0xb0, 0x6d,
|
63
|
+
0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
|
64
|
+
0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07,
|
65
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
66
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
67
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
68
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
69
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
70
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
71
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
72
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
73
|
+
]
|
74
|
+
|
75
|
+
IBM0372US_ASCII = [
|
76
|
+
0x00, 0x01, 0x02, 0x03, 0x3f, 0x09, 0x3f, 0x7f, 0x3f, 0x3f, 0x3f, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
77
|
+
0x10, 0x11, 0x12, 0x13, 0x3f, 0x0a, 0x08, 0x3f, 0x18, 0x19, 0x3f, 0x3f, 0x1c, 0x1d, 0x1e, 0x1f,
|
78
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x0a, 0x17, 0x1b, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x05, 0x06, 0x07,
|
79
|
+
0x3f, 0x3f, 0x16, 0x3f, 0x3f, 0x3f, 0x3f, 0x04, 0x3f, 0x3f, 0x3f, 0x3f, 0x14, 0x15, 0x3f, 0x1a,
|
80
|
+
0x20, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x2e, 0x3c, 0x28, 0x2b, 0x7c,
|
81
|
+
0x26, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x3f,
|
82
|
+
0x2d, 0x2f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,
|
83
|
+
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22,
|
84
|
+
0x3f, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
85
|
+
0x3f, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
86
|
+
0x3f, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
87
|
+
0x5e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x5b, 0x5d, 0x3f, 0x3f, 0x3f, 0x3f,
|
88
|
+
0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
89
|
+
0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
90
|
+
0x5c, 0x3f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
91
|
+
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
92
|
+
]
|
93
|
+
|
94
|
+
end
|
data/lib/iso8583.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
module ISO8583
|
5
|
+
require "iso8583/field"
|
6
|
+
require "iso8583/codec"
|
7
|
+
require "iso8583/fields"
|
8
|
+
require "iso8583/exception"
|
9
|
+
require "iso8583/bitmap"
|
10
|
+
require "iso8583/message"
|
11
|
+
require "iso8583/util"
|
12
|
+
require "iso8583/version"
|
13
|
+
end
|
data/test/BitmapTests.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'lib/iso8583'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
include ISO8583
|
5
|
+
|
6
|
+
class BitmapTests < Test::Unit::TestCase
|
7
|
+
def test_create_empty
|
8
|
+
b = Bitmap.new
|
9
|
+
assert_equal(b.to_s.size, 64, "proper length: 64")
|
10
|
+
b.set(112)
|
11
|
+
assert_equal(b.to_s.size, 128, "proper length: 128")
|
12
|
+
assert_equal(b.to_s, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000")
|
13
|
+
b.unset(112)
|
14
|
+
5.step(20,2){|i| b.set(i)}
|
15
|
+
assert(b.to_s.size==64, "proper length: 64")
|
16
|
+
assert_equal(b.to_s, "0000101010101010101000000000000000000000000000000000000000000000")
|
17
|
+
assert b[5]
|
18
|
+
assert b[7]
|
19
|
+
assert b[11]
|
20
|
+
assert !b[12]
|
21
|
+
assert !b[199]
|
22
|
+
|
23
|
+
assert_raises(ISO8583Exception) {b.set 1000 }
|
24
|
+
assert_raises(ISO8583Exception) { b.set 1 }
|
25
|
+
assert_raises(ISO8583Exception) { b.set -1 }
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_out_of_bounds_errors
|
29
|
+
b = Bitmap.new
|
30
|
+
assert_raises(ISO8583Exception) {b.set 1000 }
|
31
|
+
assert_raises(ISO8583Exception) { b.set 1 }
|
32
|
+
assert_raises(ISO8583Exception) { b.set -1 }
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_parse_bmp
|
36
|
+
# 0000000001001001001001001001001001001001001001001001001001000000
|
37
|
+
# generated by: 10.step(60,3) {|i| mp.set(i)}
|
38
|
+
|
39
|
+
tst = "\x00\x49\x24\x92\x49\x24\x92\x40"
|
40
|
+
b = Bitmap.new tst
|
41
|
+
10.step(60,3) {|i|
|
42
|
+
assert(b[i], "bit #{i} is not set.")
|
43
|
+
assert(!b[i+i], "bit #{i+i} is set.")
|
44
|
+
}
|
45
|
+
|
46
|
+
#10000000000000000001000000100000010000001000000100000010000001000000100000010000001000000100000010000001000000100000010000001000
|
47
|
+
# generated by: 20.step(128,7) {|i| mp.set(i)}
|
48
|
+
tst = "\x80\x00\x10\x20\x40\x81\x02\x04\x08\x10\x20\x40\x81\x02\x04\x08"
|
49
|
+
b = Bitmap.new tst
|
50
|
+
20.step(128,7) {|i|
|
51
|
+
assert(b[i], "bit #{i} is not set. (128 bit map)")
|
52
|
+
assert(!b[i+i], "bit #{i+i} is set. (128 bit map)")
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_parse_rest
|
57
|
+
tst = "\x00\x49\x24\x92\x49\x24\x92\x40\x31\x32\x33\x34"
|
58
|
+
b, rest = Bitmap.parse tst
|
59
|
+
10.step(60,3) {|i|
|
60
|
+
assert(b[i], "bit #{i} is not set.")
|
61
|
+
assert(!b[i+i], "bit #{i+i} is set.")
|
62
|
+
}
|
63
|
+
assert_equal("1234", rest)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_each
|
67
|
+
bmp = Bitmap.new
|
68
|
+
bmp.set(2)
|
69
|
+
bmp.set(3)
|
70
|
+
bmp.set(5)
|
71
|
+
bmp.set(6)
|
72
|
+
arr = []
|
73
|
+
bmp.each{|bit|
|
74
|
+
arr.push bit
|
75
|
+
}
|
76
|
+
assert_equal [2,3,5,6], arr
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|