da_funk 0.4.4

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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.yardopts +12 -0
  4. data/Gemfile +7 -0
  5. data/Gemfile.lock +21 -0
  6. data/README.md +108 -0
  7. data/README_GUIDE.md +52 -0
  8. data/RELEASE_NOTES.md +23 -0
  9. data/Rakefile +69 -0
  10. data/da_funk.gemspec +31 -0
  11. data/ext/da_funk/Makefile +5 -0
  12. data/ext/da_funk/extconf.rb +8 -0
  13. data/guides/sample_input_output.rb +14 -0
  14. data/guides/sample_message_iso8583.rb +14 -0
  15. data/guides/sample_network_gprs.rb +26 -0
  16. data/guides/sample_read_magnect_card.rb +8 -0
  17. data/guides/sample_socket.rb +29 -0
  18. data/guides/sample_transaction.rb +23 -0
  19. data/guides/sample_transaction_download_application.rb +9 -0
  20. data/guides/sample_transaction_download_file.rb +6 -0
  21. data/guides/sample_transaction_download_parameter_file.rb +18 -0
  22. data/guides/sample_transaction_iso8583.rb +222 -0
  23. data/imgs/daft-punk-da-funk.jpg +0 -0
  24. data/lib/da_funk/rake_task.rb +129 -0
  25. data/lib/da_funk/test.rb +87 -0
  26. data/lib/da_funk.rb +39 -0
  27. data/lib/device/application.rb +41 -0
  28. data/lib/device/audio.rb +17 -0
  29. data/lib/device/crypto.rb +52 -0
  30. data/lib/device/display.rb +47 -0
  31. data/lib/device/helper.rb +161 -0
  32. data/lib/device/io.rb +86 -0
  33. data/lib/device/magnetic.rb +79 -0
  34. data/lib/device/network.rb +192 -0
  35. data/lib/device/notification.rb +116 -0
  36. data/lib/device/notification_callback.rb +47 -0
  37. data/lib/device/notification_event.rb +29 -0
  38. data/lib/device/params_dat.rb +119 -0
  39. data/lib/device/printer.rb +35 -0
  40. data/lib/device/runtime.rb +22 -0
  41. data/lib/device/setting.rb +63 -0
  42. data/lib/device/support.rb +28 -0
  43. data/lib/device/system.rb +61 -0
  44. data/lib/device/transaction/download.rb +268 -0
  45. data/lib/device/transaction/emv.rb +45 -0
  46. data/lib/device/transaction/iso.rb +75 -0
  47. data/lib/device/version.rb +11 -0
  48. data/lib/device/walk.rb +8 -0
  49. data/lib/device.rb +28 -0
  50. data/lib/ext/kernel.rb +9 -0
  51. data/lib/file_db.rb +47 -0
  52. data/lib/iso8583/bitmap.rb +114 -0
  53. data/lib/iso8583/codec.rb +197 -0
  54. data/lib/iso8583/exception.rb +4 -0
  55. data/lib/iso8583/field.rb +90 -0
  56. data/lib/iso8583/fields.rb +171 -0
  57. data/lib/iso8583/message.rb +455 -0
  58. data/lib/iso8583/util.rb +91 -0
  59. data/lib/iso8583/version.rb +6 -0
  60. data/lib/serfx/commands.rb +191 -0
  61. data/lib/serfx/connection.rb +160 -0
  62. data/lib/serfx/exceptions.rb +5 -0
  63. data/lib/serfx/response.rb +28 -0
  64. data/lib/serfx.rb +27 -0
  65. data/lib/version.rb +5 -0
  66. data/lib/zip.rb +29 -0
  67. data/test/integration/getc_test.rb +6 -0
  68. data/test/integration/mrb_eval_test.rb +36 -0
  69. data/test/integration/notification_test.rb +20 -0
  70. data/test/integration/params_dat_test.rb +11 -0
  71. data/test/test_helper.rb +18 -0
  72. data/test/unit/device/display_test.rb +43 -0
  73. data/test/unit/device/helper_test.rb +61 -0
  74. data/test/unit/device/notification_callback_test.rb +6 -0
  75. data/test/unit/device/notification_event_test.rb +73 -0
  76. data/test/unit/device/notification_test.rb +21 -0
  77. data/utils/command_line_platform.rb +67 -0
  78. data/utils/test_run.rb +3 -0
  79. metadata +177 -0
@@ -0,0 +1,171 @@
1
+ module ISO8583
2
+
3
+ # This file contains a number of preinstantiated Field definitions. You
4
+ # will probably need to create own fields in your implementation, please
5
+ # see Field and Codec for further discussion on how to do this.
6
+ # The fields currently available are those necessary to implement the
7
+ # Berlin Groups Authorization Spec.
8
+ #
9
+ # The following fields are available:
10
+ #
11
+ # [+LL+] special form to de/encode variable length indicators, two bytes ASCII numerals
12
+ # [+LLL+] special form to de/encode variable length indicators, two bytes ASCII numerals
13
+ # [+LL_BCD+] special form to de/encode variable length indicators, two BCD digits
14
+ # [+LLVAR_N+] two byte variable length ASCII numeral, payload ASCII numerals
15
+ # [+LLLVAR_N+] three byte variable length ASCII numeral, payload ASCII numerals
16
+ # [+LLVAR_Z+] two byte variable length ASCII numeral, payload Track2 data
17
+ # [+LLVAR_AN+] two byte variable length ASCII numeral, payload ASCII
18
+ # [+LLVAR_ANS+] two byte variable length ASCII numeral, payload ASCII+special
19
+ # [+LLLVAR_AN+] three byte variable length ASCII numeral, payload ASCII
20
+ # [+LLLVAR_ANS+] three byte variable length ASCII numeral, payload ASCII+special
21
+ # [+LLVAR_B+] Two byte variable length binary payload
22
+ # [+LLLVAR_B+] Three byte variable length binary payload
23
+ # [+A+] fixed length letters, represented in ASCII
24
+ # [+N+] fixed lengh numerals, repesented in ASCII, padding right justified using zeros
25
+ # [+AN+] fixed lengh ASCII [A-Za-z0-9], padding left justified using spaces.
26
+ # [+ANP+] fixed lengh ASCII [A-Za-z0-9] and space, padding left, spaces
27
+ # [+ANS+] fixed length ASCII [\x20-\x7E], padding left, spaces
28
+ # [+B+] binary data, padding left using nulls (0x00)
29
+ # [+MMDDhhmmss+] Date, formatted as described in ASCII numerals
30
+ # [+YYMMDDhhmmss+] Date, formatted as named in ASCII numerals
31
+ # [+YYMM+] Expiration Date, formatted as named in ASCII numerals
32
+ # [+Hhmmss+] Date, formatted in ASCII hhmmss
33
+
34
+
35
+ # Special form to de/encode variable length indicators, two bytes ASCII numerals
36
+ LL = Field.new
37
+ LL.name = "LL"
38
+ LL.length = 2
39
+ LL.codec = ASCII_Number
40
+ LL.padding = lambda {|value|
41
+ sprintf("%02d", value)
42
+ }
43
+ # Special form to de/encode variable length indicators, three bytes ASCII numerals
44
+ LLL = Field.new
45
+ LLL.name = "LLL"
46
+ LLL.length = 3
47
+ LLL.codec = ASCII_Number
48
+ LLL.padding = lambda {|value|
49
+ sprintf("%03d", value)
50
+ }
51
+
52
+ LL_BCD = BCDField.new
53
+ LL_BCD.length = 2
54
+ LL_BCD.codec = Packed_Number
55
+
56
+ # Two byte variable length ASCII numeral, payload ASCII numerals
57
+ LLVAR_N = Field.new
58
+ LLVAR_N.length = LL
59
+ LLVAR_N.codec = ASCII_Number
60
+
61
+ # Three byte variable length ASCII numeral, payload ASCII numerals
62
+ LLLVAR_N = Field.new
63
+ LLLVAR_N.length = LLL
64
+ LLLVAR_N.codec = ASCII_Number
65
+
66
+ # Two byte variable length ASCII numeral, payload Track2 data
67
+ LLVAR_Z = Field.new
68
+ LLVAR_Z.length = LL
69
+ LLVAR_Z.codec = Track2
70
+
71
+ # Two byte variable length ASCII numeral, payload ASCII, fixed length, zeropadded (right)
72
+ LLVAR_AN = Field.new
73
+ LLVAR_AN.length = LL
74
+ LLVAR_AN.codec = AN_Codec
75
+
76
+ # Two byte variable length ASCII numeral, payload ASCII+special
77
+ LLVAR_ANS = Field.new
78
+ LLVAR_ANS.length = LL
79
+ LLVAR_ANS.codec = ANS_Codec
80
+
81
+ # Three byte variable length ASCII numeral, payload ASCII, fixed length, zeropadded (right)
82
+ LLLVAR_AN = Field.new
83
+ LLLVAR_AN.length = LLL
84
+ LLLVAR_AN.codec = AN_Codec
85
+
86
+ # Three byte variable length ASCII numeral, payload ASCII+special
87
+ LLLVAR_ANS = Field.new
88
+ LLLVAR_ANS.length = LLL
89
+ LLLVAR_ANS.codec = ANS_Codec
90
+
91
+ # Two byte variable length binary payload
92
+ LLVAR_B = Field.new
93
+ LLVAR_B.length = LL
94
+ LLVAR_B.codec = Null_Codec
95
+
96
+
97
+ # Three byte variable length binary payload
98
+ LLLVAR_B = Field.new
99
+ LLLVAR_B.length = LLL
100
+ LLLVAR_B.codec = Null_Codec
101
+
102
+ # Fixed lengh numerals, repesented in ASCII, padding right justified using zeros
103
+ N = Field.new
104
+ N.codec = ASCII_Number
105
+ N.padding = lambda {|val, len|
106
+ sprintf("%0#{len}d", val)
107
+ }
108
+
109
+ N_BCD = BCDField.new
110
+ N_BCD.codec = Packed_Number
111
+
112
+ PADDING_LEFT_JUSTIFIED_SPACES = lambda {|val, len|
113
+ sprintf "%-#{len}s", val
114
+ }
115
+
116
+ # Fixed length ASCII letters [A-Za-z]
117
+ A = Field.new
118
+ A.codec = A_Codec
119
+
120
+ # Fixed lengh ASCII [A-Za-z0-9], padding left justified using spaces.
121
+ AN = Field.new
122
+ AN.codec = AN_Codec
123
+ AN.padding = PADDING_LEFT_JUSTIFIED_SPACES
124
+
125
+ # Fixed lengh ASCII [A-Za-z0-9] and space, padding left, spaces
126
+ ANP = Field.new
127
+ ANP.codec = ANP_Codec
128
+ ANP.padding = PADDING_LEFT_JUSTIFIED_SPACES
129
+
130
+ # Fixed length ASCII [\x20-\x7E], padding left, spaces
131
+ ANS = Field.new
132
+ ANS.codec = ANS_Codec
133
+ ANS.padding = PADDING_LEFT_JUSTIFIED_SPACES
134
+
135
+ # Binary data, padding left using nulls (0x00)
136
+ B = Field.new
137
+ B.codec = Null_Codec
138
+ B.padding = lambda {|val, len|
139
+ while val.length < len
140
+ val = val + "\000"
141
+ end
142
+ val
143
+ }
144
+
145
+ # Date, formatted as described in ASCII numerals
146
+ MMDDhhmmss = Field.new
147
+ MMDDhhmmss.codec = MMDDhhmmssCodec
148
+ MMDDhhmmss.length = 10
149
+
150
+ #Date, formatted as described in ASCII numerals
151
+ YYMMDDhhmmss = Field.new
152
+ YYMMDDhhmmss.codec = YYMMDDhhmmssCodec
153
+ YYMMDDhhmmss.length = 12
154
+
155
+ #Date, formatted as described in ASCII numerals
156
+ YYMM = Field.new
157
+ YYMM.codec = YYMMCodec
158
+ YYMM.length = 4
159
+
160
+ Hhmmss = Field.new
161
+ Hhmmss.codec = HhmmssCodec
162
+ Hhmmss.length = 6
163
+
164
+ MMDD = Field.new
165
+ MMDD.codec = MMDDCodec
166
+ MMDD.length = 4
167
+
168
+ YYMMDD = Field.new
169
+ YYMMDD.codec = YYMMDDCodec
170
+ YYMMDD.length = 6
171
+ end
@@ -0,0 +1,455 @@
1
+
2
+ module ISO8583
3
+
4
+ # The class `Message` defines functionality to describe classes
5
+ # representing different type of messages, or message families.
6
+ # A message family consists of a number of possible message types that
7
+ # are allowed, and a way of naming and encoding the bitmaps allowed in
8
+ # the messages.
9
+ #
10
+ # To create your own message, start by subclassing Message:
11
+ #
12
+ # class MyMessage < Message
13
+ # (...)
14
+ # end
15
+ #
16
+ # the subtyped message should be told how the MTI is encoded:
17
+ #
18
+ # class MyMessage < Message
19
+ # mti_format N, :length => 4
20
+ # (...)
21
+ # end
22
+ #
23
+ # `N` above is an instance of Field which encodes numbers into their
24
+ # ASCII representations in a fixed length field. The option `length=>4`
25
+ # indicates the length of the fixed field.
26
+ #
27
+ # Next, the allowed message types are specified:
28
+ #
29
+ # class MyMessage < Message
30
+ # (...)
31
+ # mti 1100, "Authorization Request Acquirer Gateway"
32
+ # mti 1110, "Authorization Request Response Issuer Gateway"
33
+ # (...)
34
+ # end
35
+ #
36
+ # This basically defines to message types, 1100 and 1110 which may
37
+ # be accessed later either via their name or value:
38
+ #
39
+ # mes = MyMessage.new 1100
40
+ #
41
+ # or
42
+ # mes = MyMessage.new "Authorization Request Acquirer Gateway"
43
+ #
44
+ # or
45
+ # mes = MyMessage.new
46
+ # mes.mti = 1110 # or Auth. Req. Acq. Gateway ...
47
+ #
48
+ # Finally the allowed bitmaps, their names and the encoding rules are
49
+ # specified:
50
+ #
51
+ # class MyMessage < Message
52
+ # (...)
53
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
54
+ # bmp 3, "Processing Code", N, :length => 6
55
+ # bmp 4, "Amount (Transaction)", N, :length => 12
56
+ # bmp 6, "Amount, Cardholder Billing" , N, :length => 12
57
+ # (...)
58
+ # end
59
+ #
60
+ # The example above defines four bitmaps (2,3,4 and 6), and provides
61
+ # their bitmap number and description. The PAN field is variable length
62
+ # encoded (LL length indicator, ASCII, contents numeric, ASCII) and the
63
+ # maximum length of the field is limited to 19 using options.
64
+ #
65
+ # The other fields are fixed length numeric ASCII fields (the length of the fields is
66
+ # indicated by the `:length` options.)
67
+ #
68
+ # This message may be used as follows in order to interpret a received message.:
69
+ #
70
+ # mes = MyMessage.parse inputData
71
+ # puts mes[2] # prints the PAN from the message.
72
+ #
73
+ # Constructing own messages works as follows:
74
+ #
75
+ # mes = MyMessage.new 1100
76
+ # mes[2]= 474747474747
77
+ # # Alternatively
78
+ # mes["Primary Account Number (PAN)"]= 4747474747
79
+ # mes[3] = 1234 # padding is added by the Field en/decoder
80
+ # mes["Amount (Transaction)"] = 100
81
+ # mes[6] = 200
82
+ #
83
+ # the convenience method bmp_alias may be used in defining the class in
84
+ # order to provide direct access to fields using methods:
85
+ #
86
+ # class MyMessage < Message
87
+ # (...)
88
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
89
+ # (...)
90
+ # bmp_alias 2, :pan
91
+ # end
92
+ #
93
+ # this allows accessing fields in the following manner:
94
+ #
95
+ # mes = MyMessage.new 1100
96
+ # mes.pan = 474747474747
97
+ # puts mes.pan
98
+ # # Identical functionality to:
99
+ # mes[2]= 474747474747
100
+ # # or:
101
+ # mes["Primary Account Number (PAN)"]= 4747474747
102
+ #
103
+ # Most of the work in implementing a new set of message type lays in
104
+ # figuring out the correct fields to use defining the Message class via
105
+ # bmp.
106
+ #
107
+ class Message
108
+
109
+ # The value of the MTI (Message Type Indicator) of this message.
110
+ attr_reader :mti, :size_endianness
111
+
112
+ # Instantiate a new instance of this type of Message
113
+ # optionally specifying an mti.
114
+ def initialize(mti = nil, size_endianness = :little, format = :ascii)
115
+ # values is an internal field used to collect all the
116
+ # bmp number | bmp name | field en/decoders | values
117
+ # which are set in this message.
118
+ @values = {}
119
+ @size_endianness = size_endianness
120
+ @format = :ascii
121
+ self.mti = mti if mti
122
+ end
123
+
124
+ def build
125
+ message = self.to_b
126
+ message_size = self.size(message)
127
+
128
+ if size_endianness == :little
129
+ binary_size = [message_size].pack("s")
130
+ elsif size_endianness == :big
131
+ binary_size = [message_size].pack("s>")
132
+ else
133
+ raise ISO8583Exception.new "size endianness no recognized"
134
+ end
135
+
136
+ binary_size + message
137
+ end
138
+
139
+ def size(message)
140
+ case format
141
+ when :ascii
142
+ message.size
143
+ when :bcd
144
+ (message.size / 2)
145
+ else
146
+ raise ISO8583Exception.new "format no recognized"
147
+ end
148
+ end
149
+
150
+ # Set the mti of the Message using either the actual value
151
+ # or the name of the message type that was defined using
152
+ # Message.mti
153
+ #
154
+ # === Example
155
+ # class MyMessage < Message
156
+ # (...)
157
+ # mti 1100, "Authorization Request Acquirer Gateway"
158
+ # end
159
+ #
160
+ # mes = MyMessage.new
161
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
162
+ def mti=(value)
163
+ num, name = _get_mti_definition(value)
164
+ @mti = num
165
+ end
166
+
167
+ # Set a field in this message, `key` is either the
168
+ # bmp number or it's name.
169
+ # ===Example
170
+ #
171
+ # mes = BlaBlaMessage.new
172
+ # mes[2]=47474747 # bmp 2 is generally the PAN
173
+ # mes["Primary Account Number"]=47474747 # if thats what you called the field in Message.bmp.
174
+ def []=(key, value)
175
+ bmp_def = _get_definition key
176
+ bmp_def.value = value
177
+ @values[bmp_def.bmp] = bmp_def
178
+ end
179
+
180
+ # Retrieve the decoded value of the contents of a bitmap
181
+ # described either by the bitmap number or name.
182
+ #
183
+ # ===Example
184
+ #
185
+ # mes = BlaBlaMessage.parse someMessageBytes
186
+ # mes[2] # bmp 2 is generally the PAN
187
+ # mes["Primary Account Number"] # if thats what you called the field in Message.bmp.
188
+ def [](key)
189
+ bmp_def = _get_definition key
190
+ bmp = @values[bmp_def.bmp]
191
+ bmp ? bmp.value : nil
192
+ end
193
+
194
+ # Retrieve the byte representation of the bitmap.
195
+ def to_b
196
+ raise ISO8583Exception.new "no MTI set!" unless mti
197
+ mti_enc = self.class._mti_format.encode(mti)
198
+ mti_enc << _body.join
199
+
200
+ # length = mti_enc.length.to_s(16).rjust(4, '0')
201
+
202
+ length_dec = [mti_enc.length]
203
+ length_hex = length_dec.pack("s>")
204
+ mti_enc = length_hex + mti_enc
205
+
206
+ return mti_enc
207
+ end
208
+
209
+ # Returns a nicely formatted representation of this
210
+ # message.
211
+ def to_s
212
+ _mti_name = _get_mti_definition(mti)[1]
213
+ str = "MTI:#{mti} (#{_mti_name})\n\n"
214
+ _max = @values.values.max {|a,b|
215
+ a.name.length <=> b.name.length
216
+ }
217
+ _max_name = _max.name.length
218
+
219
+ @values.keys.sort.each{|bmp_num|
220
+ _bmp = @values[bmp_num]
221
+ str += ("%03d %#{_max_name}s : %s\n" % [bmp_num, _bmp.name, _bmp.value])
222
+ }
223
+ str
224
+ end
225
+
226
+
227
+ # METHODS starting with an underscore are meant for
228
+ # internal use only ...
229
+
230
+ # Returns an array of two byte arrays:
231
+ # [bitmap_bytes, message_bytes]
232
+ def _body
233
+ bitmap = Bitmap.new
234
+ message = ""
235
+ @values.keys.sort.each do |bmp_num|
236
+ bitmap.set(bmp_num)
237
+ enc_value = @values[bmp_num].encode
238
+ message << enc_value
239
+ end
240
+ [ bitmap.to_s.to_i(2).to_s(16).upcase, message ]
241
+ end
242
+
243
+ def _get_definition(key) #:nodoc:
244
+ b = self.class._definitions[key]
245
+ unless b
246
+ raise ISO8583Exception.new "no definition for field: #{key}"
247
+ end
248
+ b.dup
249
+ end
250
+
251
+ # return [mti_num, mti_value] for key being either
252
+ # mti_num or mti_value
253
+ def _get_mti_definition(key)
254
+ num_hash,name_hash = self.class._mti_definitions
255
+ if num_hash[key]
256
+ [key, num_hash[key]]
257
+ elsif name_hash[key]
258
+ [name_hash[key], key]
259
+ else
260
+ raise ISO8583Exception.new("MTI: #{key} not allowed!")
261
+ end
262
+ end
263
+
264
+ class << self
265
+
266
+ # Defines how the message type indicator is encoded into bytes.
267
+ # ===Params:
268
+ # * field : the decoder/encoder for the MTI
269
+ # * opts : the options to pass to this field
270
+ #
271
+ # === Example
272
+ # class MyMessage < Message
273
+ # mti_format N, :length =>4
274
+ # (...)
275
+ # end
276
+ #
277
+ # encodes the mti of this message using the `N` field (fixed
278
+ # length, plain ASCII) and sets the fixed lengh to 4 bytes.
279
+ #
280
+ # See also: mti
281
+ def mti_format(field, opts)
282
+ f = field.dup
283
+ _handle_opts(f, opts)
284
+ @mti_format = f
285
+ end
286
+
287
+ # Defines the message types allowed for this type of message and
288
+ # gives them names
289
+ #
290
+ # === Example
291
+ # class MyMessage < Message
292
+ # (...)
293
+ # mti 1100, "Authorization Request Acquirer Gateway"
294
+ # end
295
+ #
296
+ # mes = MyMessage.new
297
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
298
+ #
299
+ # See Also: mti_format
300
+ def mti(value, name)
301
+ @mtis_v ||= {}
302
+ @mtis_n ||= {}
303
+ @mtis_v[value] = name
304
+ @mtis_n[name] = value
305
+ end
306
+
307
+ # Define a bitmap in the message
308
+ # ===Params:
309
+ # * bmp : bitmap number
310
+ # * name : human readable form
311
+ # * field : field for encoding/decoding
312
+ # * opts : options to pass to the field, e.g. length for fxed len fields.
313
+ #
314
+ # ===Example
315
+ #
316
+ # class MyMessage < Message
317
+ # bmp 2, "PAN", LLVAR_N, :max =>19
318
+ # (...)
319
+ # end
320
+ #
321
+ # creates a class MyMessage that allows for a bitmap 2 which
322
+ # is named "PAN" and encoded by an LLVAR_N Field. The maximum
323
+ # length of the value is 19. This class may be used as follows:
324
+ #
325
+ # mes = MyMessage.new
326
+ # mes[2] = 474747474747 # or mes["PAN"] = 4747474747
327
+ #
328
+ def bmp(bmp, name, field, opts = nil)
329
+ @defs ||= {}
330
+
331
+ field = field.dup
332
+ field.name = name
333
+ field.bmp = bmp
334
+ _handle_opts(field, opts) if opts
335
+
336
+ bmp_def = ISO8583::BMP.new bmp, name, field
337
+
338
+ @defs[bmp] = bmp_def
339
+ @defs[name] = bmp_def
340
+ end
341
+
342
+ # Create an alias to access bitmaps directly using a method.
343
+ # Example:
344
+ # class MyMessage < Message
345
+ # (...)
346
+ # bmp 2, "PAN", LLVAR_N
347
+ # (...)
348
+ # bmp_alias 2, :pan
349
+ # end #class
350
+ #
351
+ # would allow you to access the PAN like this:
352
+ #
353
+ # mes.pan = 1234
354
+ # puts mes.pan
355
+ #
356
+ # instead of:
357
+ #
358
+ # mes[2] = 1234
359
+ #
360
+ def bmp_alias(bmp, aliaz)
361
+ define_method (aliaz) {
362
+ bmp_ = @values[bmp]
363
+ bmp_ ? bmp_.value : nil
364
+ }
365
+
366
+ define_method ("#{aliaz}=") {|value|
367
+ self[bmp] = value
368
+ # bmp_def = _get_definition(bmp)
369
+ # bmp_def.value= value
370
+ # @values[bmp] = bmp_def
371
+ }
372
+ end
373
+
374
+ # Parse the bytes `str` returning a message of the defined type.
375
+ def parse(str)
376
+ message = self.new
377
+ message.mti, rest = _mti_format.parse(str)
378
+
379
+ bmp,rest = Bitmap.parse(rest)
380
+ bmp.each {|bit|
381
+ if bit > 1 && bit <= 128
382
+ bmp_def = _definitions[bit]
383
+ value, rest = bmp_def.field.parse(rest)
384
+ message[bit] = value
385
+ end
386
+ }
387
+ message
388
+ end
389
+
390
+ # access the mti definitions applicable to the Message
391
+ #
392
+ # returns a pair of hashes containing:
393
+ #
394
+ # mti_value => mti_name
395
+ #
396
+ # mti_name => mti_value
397
+ #
398
+ def _mti_definitions
399
+ [@mtis_v, @mtis_n]
400
+ end
401
+
402
+ # Access the field definitions of this class, this is a
403
+ # hash containing [bmp_number, BMP] and [bitmap_name, BMP]
404
+ # pairs.
405
+ #
406
+ def _definitions
407
+ @defs
408
+ end
409
+
410
+ # Returns the field definition to format the mti.
411
+ def _mti_format
412
+ @mti_format
413
+ end
414
+
415
+
416
+
417
+ # METHODS starting with an underscore are meant for
418
+ # internal use only ...
419
+
420
+ # Modifies the field definitions of the fields passed
421
+ # in through the `bmp` and `mti_format` class methods.
422
+ #
423
+ def _handle_opts(field, opts)
424
+ opts.each {|key, value|
425
+ key = (key.to_s+"=").to_sym
426
+ if field.respond_to?(key)
427
+ field.send(key, value)
428
+ else
429
+ warn "unknown option #{key} for #{field.name}"
430
+ end
431
+ }
432
+ end
433
+ end
434
+ end
435
+
436
+ # Internal class used to tie together name, bitmap number, field en/decoder
437
+ # and the value of the corresponding field
438
+ class BMP
439
+ attr_accessor :bmp
440
+ attr_accessor :name
441
+ attr_accessor :field
442
+ attr_accessor :value
443
+
444
+ def initialize(bmp, name, field)
445
+ @bmp = bmp
446
+ @name = name
447
+ @field = field
448
+ end
449
+
450
+ def encode
451
+ field.encode(value)
452
+ end
453
+ end
454
+ end
455
+
@@ -0,0 +1,91 @@
1
+
2
+ module ISO8583
3
+
4
+ # general utilities
5
+ #
6
+ # Convert a String of bytes to their hex representation
7
+ # E.g.:
8
+ #
9
+ # b2hex "\x12\x34\xab" => "1234AB"
10
+ #
11
+ def b2hex(byte_string)
12
+ r = byte_string.unpack("H*")[0]
13
+ r.length > 1 ? r : " "
14
+ end
15
+
16
+ #
17
+ # Convert a String containing hex data to
18
+ # a String containing the corresponding bytes:
19
+ #
20
+ # hex2b "abcd12" => "\xa\cd\12"
21
+ #
22
+ def hex2b(hex_string)
23
+ string = hex_string.gsub(/\s+/, "")
24
+ raise ISO8583Exception.new("Invalid Hex chars: #{hex_string}") unless string =~ /^[A-Fa-f0-9]*$/
25
+ raise ISO8583Exception.new("Uneven number of Hex chars #{hex_string}") unless ( (string.length % 2) == 0)
26
+ [string].pack("H*")
27
+ end
28
+
29
+ def _conv(str, mapping)
30
+ _str = ""
31
+ str.each_byte{|byte|
32
+ _str << mapping[byte]
33
+ }
34
+ _str
35
+ end
36
+
37
+ #
38
+ # Convert a String of ASCII chars to EBCDIC
39
+ #
40
+ def ascii2ebcdic(ascii)
41
+ _conv(ascii, US_ASCII2IBM037)
42
+ end
43
+
44
+ #
45
+ # Convert an EBCDIC String to ASCII
46
+ #
47
+ def ebcdic2ascii(ebcdic)
48
+ _conv(ebcdic, IBM0372US_ASCII)
49
+ end
50
+
51
+ # The charsets supported by iconv aren't guranteed. At the very least MACs don't support ebcdic,
52
+ # so providing rudimentary mappings here.
53
+ US_ASCII2IBM037 = [
54
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
55
+ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f,
56
+ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61,
57
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f,
58
+ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
59
+ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xba, 0xe0, 0xbb, 0xb0, 0x6d,
60
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
61
+ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07,
62
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
63
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
64
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
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
+ ]
71
+
72
+ IBM0372US_ASCII = [
73
+ 0x00, 0x01, 0x02, 0x03, 0x3f, 0x09, 0x3f, 0x7f, 0x3f, 0x3f, 0x3f, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
74
+ 0x10, 0x11, 0x12, 0x13, 0x3f, 0x0a, 0x08, 0x3f, 0x18, 0x19, 0x3f, 0x3f, 0x1c, 0x1d, 0x1e, 0x1f,
75
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x0a, 0x17, 0x1b, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x05, 0x06, 0x07,
76
+ 0x3f, 0x3f, 0x16, 0x3f, 0x3f, 0x3f, 0x3f, 0x04, 0x3f, 0x3f, 0x3f, 0x3f, 0x14, 0x15, 0x3f, 0x1a,
77
+ 0x20, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x2e, 0x3c, 0x28, 0x2b, 0x7c,
78
+ 0x26, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x3f,
79
+ 0x2d, 0x2f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,
80
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22,
81
+ 0x3f, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
82
+ 0x3f, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
83
+ 0x3f, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
84
+ 0x5e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x5b, 0x5d, 0x3f, 0x3f, 0x3f, 0x3f,
85
+ 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
86
+ 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
87
+ 0x5c, 0x3f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
88
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
89
+ ]
90
+
91
+ end