smartcard 0.4.11 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ # Function declarations for the PC/SC FFI.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+
9
+ # :nodoc: namespace
10
+ module Smartcard::PCSC
11
+
12
+
13
+ # FFI to the native PC/SC libraries.
14
+ module FFILib
15
+ str_suffix = FFI::Platform.windows? ? 'A' : ''
16
+ attach_function :establish_context, 'SCardEstablishContext',
17
+ [Scope, :pointer, :pointer, :pointer], Status
18
+ attach_function :release_context, 'SCardReleaseContext', [Word], Status
19
+ attach_function :is_valid_context, 'SCardIsValidContext', [Word], Status
20
+ attach_function :list_reader_groups, 'SCardListReaderGroups' + str_suffix,
21
+ [Word, :pointer, :pointer], Status
22
+ attach_function :list_readers, 'SCardListReaders' + str_suffix,
23
+ [Word, :pointer, :pointer, :pointer], Status
24
+ attach_function :get_status_change, 'SCardGetStatusChange' + str_suffix,
25
+ [Word, Word, :pointer, Word], Status
26
+
27
+ attach_function :card_connect, 'SCardConnect' + str_suffix,
28
+ [Word, :string, Share, Protocol, :pointer, :pointer], Status
29
+ attach_function :card_reconnect, 'SCardReconnect',
30
+ [Word, Share, Protocol, Disposition, :pointer], Status
31
+ attach_function :card_disconnect, 'SCardDisconnect',
32
+ [Word, Disposition], Status
33
+ attach_function :begin_transaction, 'SCardBeginTransaction', [Word], Status
34
+ attach_function :end_transaction, 'SCardEndTransaction',
35
+ [Word, Disposition], Status
36
+ attach_function :get_attrib, 'SCardGetAttrib',
37
+ [Word, Attribute, :pointer, :pointer], Status
38
+ attach_function :set_attrib, 'SCardSetAttrib',
39
+ [Word, Attribute, :pointer, Word], Status
40
+ attach_function :transmit, 'SCardTransmit',
41
+ [Word, IoRequest, :pointer, Word, IoRequest, :pointer,
42
+ :pointer], Status
43
+ attach_function :card_control, 'SCardControl',
44
+ [Word, Word, :pointer, Word, :pointer, Word, :pointer], Status
45
+ attach_function :card_status, 'SCardStatus' + str_suffix,
46
+ [Word, :pointer, :pointer, :pointer, :pointer, :pointer,
47
+ :pointer], Status
48
+ end # module Smartcard::PCSC::FFILib
49
+
50
+ end # namespace Smartcard::PCSC
@@ -0,0 +1,38 @@
1
+ # Declaration for the module enclosing the PC/SC FFI hooks.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+
9
+ # :nodoc: namespace
10
+ module Smartcard::PCSC
11
+
12
+
13
+ # FFI to the native PC/SC libraries.
14
+ module FFILib
15
+ extend FFI::Library
16
+ # NOTE: the information is hacked together from the PCSClite headers reader.h,
17
+ # winscard.h, and pcsclite.h, available in /usr/include/PCSC
18
+
19
+ if FFI::Platform.windows?
20
+ ffi_lib 'winscard'
21
+ elsif FFI::Platform.mac?
22
+ ffi_lib '/System/Library/Frameworks/PCSC.framework/PCSC'
23
+ else
24
+ ffi_lib 'pcsclite'
25
+ end
26
+ end
27
+
28
+ end # namespace Smartcard::PCSC
29
+
30
+ # Add Fixnum#ord to Ruby 1.8.6's Fixnum, if it's not already there.
31
+ unless 0.respond_to?(:ord) or '0'.respond_to?(:ord)
32
+ # :nodoc: all
33
+ class Fixnum
34
+ def ord
35
+ self
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ # Function declarations for the PC/SC FFI.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+
9
+ # :nodoc: namespace
10
+ module Smartcard::PCSC
11
+
12
+
13
+ # FFI to the native PC/SC libraries.
14
+ module FFILib
15
+ if FFI::Platform.mac?
16
+ # 64-bit MacOS is different from 64-bit Linux.
17
+ Word = :uint32
18
+ else
19
+ Word = :ulong
20
+ end
21
+
22
+ # Used to synthesize pointers to unsigned integers.
23
+ class WordPtr < FFI::Struct
24
+ # NOTE: this hack is necessary because FFI::Pointer can only read signed
25
+ # values right now (FFI 0.5.3).
26
+ layout :value, Word
27
+ end
28
+
29
+ # Used to obtain information and state changes about all PC/SC readers.
30
+ class ReaderStateQuery < FFI::Struct
31
+ layout :reader_name, :pointer,
32
+ :user_data, :pointer,
33
+ :current_state, Word,
34
+ :event_state, Word,
35
+ :atr_length, Word,
36
+ :atr, [:char, Consts::MAX_ATR_SIZE]
37
+ end
38
+
39
+ # Low-level protocol information for APDU transmission and reception.
40
+ class IoRequest < FFI::Struct
41
+ layout :protocol, Word,
42
+ :pci_length, Word
43
+ end
44
+
45
+ # Global variables for IoRequests for protocols T=0, T=1, and RAW.
46
+ begin
47
+ PCI_T0 = attach_variable :pci_t0, 'g_rgSCardT0Pci', IoRequest
48
+ PCI_T1 = attach_variable :pci_t1, 'g_rgSCardT1Pci', IoRequest
49
+ PCI_RAW = attach_variable :pci_raw, 'g_rgSCardRawPci', IoRequest
50
+ rescue
51
+ # Couldn't find the global variables, so we must be on Windows.
52
+
53
+ # TODO(costan): figure out the names of the Windows global variables
54
+ end
55
+ end # module Smartcard::PCSC::FFILib
56
+
57
+ end # namespace Smartcard::PCSC
@@ -1,5 +1,10 @@
1
1
  # Contains information about an exception at the PC/SC layer.
2
- class Smartcard::PCSC::Exception
3
- # The error number returned by the failed PC/SC function call. Should be one of the Smartcard::PCSC::SCARD_E_ constants.
4
- attr_reader :errno
2
+ class Smartcard::PCSC::Exception < RuntimeError
3
+ def initialize(error_status)
4
+ @pcsc_status = error_status
5
+ super error_status.to_s
6
+ end
7
+
8
+ # The PC/SC error status that caused this error.
9
+ attr_reader :pcsc_status
5
10
  end
@@ -0,0 +1,167 @@
1
+ # Connects Ruby to the PC/SC resource manager (wraps SCARDCONTEXT).
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ require 'set'
8
+
9
+ # :nodoc: namespace
10
+ module Smartcard::PCSC
11
+
12
+
13
+ # A continuous array of reader state queries.
14
+ class ReaderStateQueries
15
+ # Creates an array of reader state queries.
16
+ #
17
+ # The states are unusable until they are assigned reader names by calling
18
+ # set_reader_name_of.
19
+ def initialize(num_states)
20
+ @_buffer = FFI::MemoryPointer.new :char,
21
+ FFILib::ReaderStateQuery.size * num_states
22
+ @queries = (0...num_states).map do |i|
23
+ FFILib::ReaderStateQuery.new @_buffer + FFILib::ReaderStateQuery.size * i
24
+ end
25
+ end
26
+
27
+ # A query in the array.
28
+ def [](index)
29
+ raise TypeError unless index.kind_of? Numeric
30
+ raise IndexError if index >= @queries.length
31
+ @queries[index]
32
+ end
33
+
34
+ # The number of queries in the array.
35
+ def length
36
+ @queries.length
37
+ end
38
+
39
+ # Updates all the queries to acknowledge status changes.
40
+ #
41
+ # This is a convenience method intended to be called after
42
+ # Smartcard::PCSC::Context#wait_for_status_change.
43
+ def ack_changes
44
+ @queries.each { |query| query.current_state = query.event_state }
45
+ end
46
+
47
+ # Called by FFI::AutoPointer to release the reader states array.
48
+ #
49
+ # This should not be called by client code.
50
+ def self._release_states(pointer)
51
+ pointer.free
52
+ end
53
+
54
+ # The low-level _SCARD_READERSTATE_ data.
55
+ #
56
+ # This should not be used by client code.
57
+ attr_reader :_buffer
58
+ end # class Smartcard::PCSC::ReaderStates
59
+
60
+
61
+ # :nodoc: extends the reader states with nice accessors
62
+ class FFILib::ReaderStateQuery
63
+ # The query's current state.
64
+ #
65
+ # Smartcard::PCSC::Context#wait_for_status_change blocks while the reader
66
+ # state equals this.
67
+ #
68
+ # The value is a Set whose elements are FFILib::CardState members.
69
+ def current_state
70
+ FFILib::ReaderStateQuery.unpack_state self[:current_state]
71
+ end
72
+
73
+ # Changes the query's current state.
74
+ #
75
+ # Smartcard::PCSC::Context#wait_for_status_change blocks while the reader
76
+ # state equals this.
77
+ #
78
+ # The new value can be a symbol in FFILib::CardState, or an Enumerable
79
+ # containing such symbols.
80
+ def current_state=(new_state)
81
+ self[:current_state] = FFILib::ReaderStateQuery.pack_state new_state
82
+ end
83
+
84
+ # The query's event state.
85
+ #
86
+ # Smartcard::PCSC::Context#wait_for_status_change updates this value before it
87
+ # returns.
88
+ #
89
+ # The value is a Set whose elements are FFILib::CardState members.
90
+ def event_state
91
+ FFILib::ReaderStateQuery.unpack_state self[:event_state]
92
+ end
93
+
94
+ # Changes the query's event state.
95
+ #
96
+ # Smartcard::PCSC::Context#wait_for_status_change updates this value before it
97
+ # returns.
98
+ #
99
+ # The new value can be a symbol in FFILib::CardState, or an Enumerable
100
+ # containing such symbols.
101
+ def event_state=(new_state)
102
+ self[:event_state] = FFILib::ReaderStateQuery.pack_state new_state
103
+ end
104
+
105
+ # The ATR of the smart-card in the query's reader.
106
+ #
107
+ # Smartcard::PCSC::Context#wait_for_status_change updates this value before it
108
+ # returns.
109
+ #
110
+ # The value is a string containing the ATR bytes.
111
+ def atr
112
+ self[:atr].to_ptr.get_bytes 0, self[:atr_length]
113
+ end
114
+
115
+ # Changes the smart-card ATR stored in the query.
116
+ #
117
+ # Smartcard::PCSC::Context#wait_for_status_change updates this value before it
118
+ # returns.
119
+ #
120
+ # The new value should be a string containing the ATR bytes.
121
+ def atr=(new_atr)
122
+ if new_atr.length > max_length = FFILib::Consts::MAX_ATR_SIZE
123
+ raise ArgumentError, "ATR above maximum length of #{max_length}"
124
+ end
125
+
126
+ self[:atr_length] = new_atr.length
127
+ self[:atr].to_ptr.put_bytes 0, new_atr, 0, new_atr.length
128
+ end
129
+
130
+ # The name of the reader referenceed by this query.
131
+ #
132
+ # Smartcard::PCSC::Context#wait_for_status_change never changes this value.
133
+ def reader_name
134
+ self[:reader_name].read_string
135
+ end
136
+
137
+ # Changes the name of the reader referenceed by this query.
138
+ #
139
+ # Smartcard::PCSC::Context#wait_for_status_change never changes this value.
140
+ def reader_name=(new_name)
141
+ self[:reader_name].free if self[:reader_name].kind_of? FFI::MemoryPointer
142
+ self[:reader_name] = FFI::MemoryPointer.from_string new_name
143
+ end
144
+
145
+ # Packs an unpacked card state (symbol or set of symbols) into a number.
146
+ #
147
+ # This should not be used by client code.
148
+ def self.pack_state(unpacked_state)
149
+ if unpacked_state.kind_of? Enumerable
150
+ state = 0
151
+ unpacked_state.each { |bit| state |= FFILib::CardState[bit] }
152
+ return state
153
+ end
154
+ FFILib::CardState[unpacked_state]
155
+ end
156
+
157
+ # Unpacks a numeric card state into a Set of symbols.
158
+ def self.unpack_state(packed_state)
159
+ state = Set.new
160
+ FFILib::CardState.to_h.each do |bit, mask|
161
+ state << bit if (packed_state & mask) == mask and mask != 0
162
+ end
163
+ state
164
+ end
165
+ end
166
+
167
+ end # namespace Smartcard::PCSC
data/smartcard.gemspec CHANGED
@@ -2,40 +2,42 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{smartcard}
5
- s.version = "0.4.11"
5
+ s.version = "0.5.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Victor Costan"]
9
- s.date = %q{2009-11-16}
9
+ s.date = %q{2009-11-27}
10
10
  s.description = %q{Interface with ISO 7816 smart cards.}
11
11
  s.email = %q{victor@costan.us}
12
- s.extensions = ["ext/smartcard_pcsc/extconf.rb"]
13
- s.extra_rdoc_files = ["BUILD", "CHANGELOG", "LICENSE", "README", "ext/smartcard_pcsc/extconf.rb", "ext/smartcard_pcsc/pcsc.h", "ext/smartcard_pcsc/pcsc_card.c", "ext/smartcard_pcsc/pcsc_constants.c", "ext/smartcard_pcsc/pcsc_context.c", "ext/smartcard_pcsc/pcsc_exception.c", "ext/smartcard_pcsc/pcsc_io_request.c", "ext/smartcard_pcsc/pcsc_main.c", "ext/smartcard_pcsc/pcsc_multi_strings.c", "ext/smartcard_pcsc/pcsc_namespace.c", "ext/smartcard_pcsc/pcsc_reader_states.c", "ext/smartcard_pcsc/pcsc_surrogate_reader.h", "ext/smartcard_pcsc/pcsc_surrogate_wintypes.h", "lib/smartcard.rb", "lib/smartcard/gp/asn1_ber.rb", "lib/smartcard/gp/cap_loader.rb", "lib/smartcard/gp/des.rb", "lib/smartcard/gp/gp_card_mixin.rb", "lib/smartcard/iso/apdu_error.rb", "lib/smartcard/iso/auto_configurator.rb", "lib/smartcard/iso/iso_card_mixin.rb", "lib/smartcard/iso/jcop_remote_protocol.rb", "lib/smartcard/iso/jcop_remote_server.rb", "lib/smartcard/iso/jcop_remote_transport.rb", "lib/smartcard/iso/pcsc_transport.rb", "lib/smartcard/iso/transport.rb", "lib/smartcard/pcsc/pcsc_exception.rb"]
14
- s.files = ["BUILD", "CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "ext/smartcard_pcsc/extconf.rb", "ext/smartcard_pcsc/pcsc.h", "ext/smartcard_pcsc/pcsc_card.c", "ext/smartcard_pcsc/pcsc_constants.c", "ext/smartcard_pcsc/pcsc_context.c", "ext/smartcard_pcsc/pcsc_exception.c", "ext/smartcard_pcsc/pcsc_io_request.c", "ext/smartcard_pcsc/pcsc_main.c", "ext/smartcard_pcsc/pcsc_multi_strings.c", "ext/smartcard_pcsc/pcsc_namespace.c", "ext/smartcard_pcsc/pcsc_reader_states.c", "ext/smartcard_pcsc/pcsc_surrogate_reader.h", "ext/smartcard_pcsc/pcsc_surrogate_wintypes.h", "lib/smartcard.rb", "lib/smartcard/gp/asn1_ber.rb", "lib/smartcard/gp/cap_loader.rb", "lib/smartcard/gp/des.rb", "lib/smartcard/gp/gp_card_mixin.rb", "lib/smartcard/iso/apdu_error.rb", "lib/smartcard/iso/auto_configurator.rb", "lib/smartcard/iso/iso_card_mixin.rb", "lib/smartcard/iso/jcop_remote_protocol.rb", "lib/smartcard/iso/jcop_remote_server.rb", "lib/smartcard/iso/jcop_remote_transport.rb", "lib/smartcard/iso/pcsc_transport.rb", "lib/smartcard/iso/transport.rb", "lib/smartcard/pcsc/pcsc_exception.rb", "test/gp/asn1_ber_test.rb", "test/gp/cap_loader_test.rb", "test/gp/des_test.rb", "test/gp/gp_card_mixin_compat_test.rb", "test/gp/gp_card_mixin_test.rb", "test/gp/hello.apdu", "test/gp/hello.cap", "test/iso/auto_configurator_test.rb", "test/iso/iso_card_mixin_test.rb", "test/iso/iso_exception_test.rb", "test/iso/jcop_remote_test.rb", "test/pcsc/containers_test.rb", "test/pcsc/smoke_test.rb", "tests/ts_pcsc_ext.rb", "smartcard.gemspec"]
12
+ s.extra_rdoc_files = ["BUILD", "CHANGELOG", "LICENSE", "README", "lib/smartcard.rb", "lib/smartcard/gp/asn1_ber.rb", "lib/smartcard/gp/cap_loader.rb", "lib/smartcard/gp/des.rb", "lib/smartcard/gp/gp_card_mixin.rb", "lib/smartcard/iso/apdu_error.rb", "lib/smartcard/iso/auto_configurator.rb", "lib/smartcard/iso/iso_card_mixin.rb", "lib/smartcard/iso/jcop_remote_protocol.rb", "lib/smartcard/iso/jcop_remote_server.rb", "lib/smartcard/iso/jcop_remote_transport.rb", "lib/smartcard/iso/pcsc_transport.rb", "lib/smartcard/iso/transport.rb", "lib/smartcard/pcsc/card.rb", "lib/smartcard/pcsc/context.rb", "lib/smartcard/pcsc/ffi_autogen.rb", "lib/smartcard/pcsc/ffi_functions.rb", "lib/smartcard/pcsc/ffi_lib.rb", "lib/smartcard/pcsc/ffi_structs.rb", "lib/smartcard/pcsc/pcsc_exception.rb", "lib/smartcard/pcsc/reader_state_queries.rb", "tasks/ffi_codegen.rb"]
13
+ s.files = ["BUILD", "CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "lib/smartcard.rb", "lib/smartcard/gp/asn1_ber.rb", "lib/smartcard/gp/cap_loader.rb", "lib/smartcard/gp/des.rb", "lib/smartcard/gp/gp_card_mixin.rb", "lib/smartcard/iso/apdu_error.rb", "lib/smartcard/iso/auto_configurator.rb", "lib/smartcard/iso/iso_card_mixin.rb", "lib/smartcard/iso/jcop_remote_protocol.rb", "lib/smartcard/iso/jcop_remote_server.rb", "lib/smartcard/iso/jcop_remote_transport.rb", "lib/smartcard/iso/pcsc_transport.rb", "lib/smartcard/iso/transport.rb", "lib/smartcard/pcsc/card.rb", "lib/smartcard/pcsc/context.rb", "lib/smartcard/pcsc/ffi_autogen.rb", "lib/smartcard/pcsc/ffi_functions.rb", "lib/smartcard/pcsc/ffi_lib.rb", "lib/smartcard/pcsc/ffi_structs.rb", "lib/smartcard/pcsc/pcsc_exception.rb", "lib/smartcard/pcsc/reader_state_queries.rb", "tasks/ffi_codegen.rb", "test/gp/asn1_ber_test.rb", "test/gp/cap_loader_test.rb", "test/gp/des_test.rb", "test/gp/gp_card_mixin_compat_test.rb", "test/gp/gp_card_mixin_test.rb", "test/gp/hello.apdu", "test/gp/hello.cap", "test/iso/auto_configurator_test.rb", "test/iso/iso_card_mixin_test.rb", "test/iso/iso_exception_test.rb", "test/iso/jcop_remote_test.rb", "test/pcsc/card_test.rb", "test/pcsc/context_test.rb", "test/pcsc/reader_state_queries_test.rb", "smartcard.gemspec"]
15
14
  s.homepage = %q{http://www.costan.us/smartcard}
16
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Smartcard", "--main", "README"]
17
- s.require_paths = ["lib", "ext"]
16
+ s.require_paths = ["lib"]
18
17
  s.rubyforge_project = %q{smartcard}
19
18
  s.rubygems_version = %q{1.3.5}
20
19
  s.summary = %q{Interface with ISO 7816 smart cards.}
21
- s.test_files = ["test/gp/asn1_ber_test.rb", "test/gp/cap_loader_test.rb", "test/gp/des_test.rb", "test/gp/gp_card_mixin_compat_test.rb", "test/gp/gp_card_mixin_test.rb", "test/iso/auto_configurator_test.rb", "test/iso/iso_card_mixin_test.rb", "test/iso/iso_exception_test.rb", "test/iso/jcop_remote_test.rb", "test/pcsc/containers_test.rb", "test/pcsc/smoke_test.rb"]
20
+ s.test_files = ["test/iso/iso_card_mixin_test.rb", "test/iso/auto_configurator_test.rb", "test/iso/iso_exception_test.rb", "test/iso/jcop_remote_test.rb", "test/pcsc/card_test.rb", "test/pcsc/context_test.rb", "test/pcsc/reader_state_queries_test.rb", "test/gp/gp_card_mixin_test.rb", "test/gp/gp_card_mixin_compat_test.rb", "test/gp/cap_loader_test.rb", "test/gp/asn1_ber_test.rb", "test/gp/des_test.rb"]
22
21
 
23
22
  if s.respond_to? :specification_version then
24
23
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
24
  s.specification_version = 3
26
25
 
27
26
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<ffi>, [">= 0.5.3"])
28
28
  s.add_runtime_dependency(%q<rubyzip>, [">= 0.9.1"])
29
29
  s.add_runtime_dependency(%q<zerg_support>, [">= 0.1.5"])
30
30
  s.add_development_dependency(%q<echoe>, [">= 3.2"])
31
31
  s.add_development_dependency(%q<flexmock>, [">= 0.8.6"])
32
32
  else
33
+ s.add_dependency(%q<ffi>, [">= 0.5.3"])
33
34
  s.add_dependency(%q<rubyzip>, [">= 0.9.1"])
34
35
  s.add_dependency(%q<zerg_support>, [">= 0.1.5"])
35
36
  s.add_dependency(%q<echoe>, [">= 3.2"])
36
37
  s.add_dependency(%q<flexmock>, [">= 0.8.6"])
37
38
  end
38
39
  else
40
+ s.add_dependency(%q<ffi>, [">= 0.5.3"])
39
41
  s.add_dependency(%q<rubyzip>, [">= 0.9.1"])
40
42
  s.add_dependency(%q<zerg_support>, [">= 0.1.5"])
41
43
  s.add_dependency(%q<echoe>, [">= 3.2"])
@@ -0,0 +1,145 @@
1
+ # Automatically generates FFI code reflecting headers.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+ require 'ffi/tools/const_generator'
9
+ require 'ffi/tools/struct_generator'
10
+ require 'set'
11
+
12
+ desc 'Regenerate ffi_autogen.rb'
13
+ task :ffi_header do
14
+ Smartcard::Tasks.generate_ffi_header
15
+ end
16
+
17
+ # :nodoc: namespace
18
+ module Smartcard
19
+
20
+ module Tasks
21
+ def self.resolve_constants(pattern, format)
22
+ if FFI::Platform.mac?
23
+ header_path = '/System/Library/Frameworks/PCSC.framework/Headers/'
24
+ else
25
+ header_path = '/usr/include/PCSC/'
26
+ end
27
+ headers = Dir.glob(header_path + '*.h').map { |f| File.basename f }
28
+
29
+ consts = Set.new
30
+ headers.each do |header|
31
+ File.read("#{header_path}#{header}").each_line do |line|
32
+ tokens = line.split
33
+ next unless tokens[0] == '#define'
34
+ next if tokens[1].index '('
35
+ consts << tokens[1] if pattern =~ tokens[1]
36
+ end
37
+ end
38
+
39
+ const_gen = FFI::ConstGenerator.new(nil,
40
+ :cppflags => "-w -I#{header_path}") do |g|
41
+ headers.each { |header| g.include header }
42
+ consts.each { |const| g.const const, format }
43
+ end
44
+
45
+ const_gen.constants
46
+ end
47
+
48
+ def self.output_constants(constants, f)
49
+ constants.each do |name, const|
50
+ f.write " #{const.to_ruby}\n"
51
+ end
52
+ end
53
+
54
+ def self.output_enum(enum_name, constants, name_regexp, f)
55
+ f.write " #{enum_name} = enum [\n"
56
+
57
+ re = Regexp.new('^' + name_regexp + '$')
58
+ constants.each do |name, const|
59
+ ruby_name = re.match(name)[1]
60
+ f.write " :#{ruby_name.downcase}, Consts::#{name},\n"
61
+ end
62
+
63
+ f.write " ]\n"
64
+ end
65
+
66
+ def self.generate_ffi_header
67
+ File.open('lib/smartcard/pcsc/ffi_autogen.rb', 'w') do |f|
68
+ f.write "# Automatically generated by tasks/ffi_codegen.rb\n\n"
69
+
70
+ # Return codes.
71
+ stat_consts = resolve_constants(/^SCARD_(E|S|W)_.*$/, '0x%08X')
72
+ # Context scopes.
73
+ scope_consts = resolve_constants(/^SCARD_SCOPE_/, '0x%08X')
74
+ # Communication protocols (e.g. T=0).
75
+ proto_consts = resolve_constants(/^SCARD_PROTOCOL_/, '0x%08X')
76
+ # Reader sharing modes.
77
+ share_consts = resolve_constants(/^SCARD_SHARE_/, '0x%08X')
78
+ # Card dispositions.
79
+ disp_consts = resolve_constants(/^SCARD_([^_]*)_CARD$/, '0x%08X')
80
+ # Reader attributes.
81
+ attr_consts = resolve_constants(/^SCARD_ATTR_/, '0x%08lX')
82
+ # Card states.
83
+ card_state_consts = resolve_constants(/^SCARD_STATE_/, '0x%08X')
84
+ # Reader states.
85
+ reader_state_consts = resolve_constants(/^SCARD_[^_]*$/, '0x%08X')
86
+ # Misc constants.
87
+ misc_consts = resolve_constants(/^MAX_ATR_SIZE|INFINITE$/, '0x%08X')
88
+
89
+ f.write "# :nodoc: namespace\n"
90
+ f.write "module Smartcard::PCSC\n\n"
91
+ f.write "# :nodoc: auto-generated\n"
92
+ f.write "module FFILib\n"
93
+
94
+ f.write " # Constant values extracted from headers.\n"
95
+ f.write " module Consts\n"
96
+ output_constants stat_consts, f
97
+ output_constants scope_consts, f
98
+ output_constants proto_consts, f
99
+ output_constants share_consts, f
100
+ output_constants disp_consts, f
101
+ output_constants attr_consts, f
102
+ output_constants card_state_consts, f
103
+ output_constants reader_state_consts, f
104
+ output_constants misc_consts, f
105
+ f.write " end # module Smartcard::PCSC::FFILib::Consts\n\n"
106
+
107
+ f.write " # Error codes returned by the PCSC functions.\n"
108
+ output_enum 'Status', stat_consts, 'SCARD_._(.*)', f
109
+ f.write "\n"
110
+
111
+ f.write " # Values for the context's scope.\n"
112
+ output_enum 'Scope', scope_consts, 'SCARD_SCOPE_(.*)', f
113
+ f.write "\n"
114
+
115
+ f.write " # Smart-card communication protocols.\n"
116
+ output_enum 'Protocol', proto_consts, 'SCARD_PROTOCOL_(.*)', f
117
+ f.write "\n"
118
+
119
+ f.write " # Smart-card sharing modes.\n"
120
+ output_enum 'Share', share_consts, 'SCARD_SHARE_(.*)', f
121
+ f.write "\n"
122
+
123
+ f.write " # Smart-card dispositions.\n"
124
+ output_enum 'Disposition', disp_consts, 'SCARD_(.*)_CARD', f
125
+ f.write "\n"
126
+
127
+ f.write " # Smart-card reader attributes.\n"
128
+ output_enum 'Attribute', attr_consts, 'SCARD_ATTR_(.*)', f
129
+ f.write "\n"
130
+
131
+ f.write " # Smart-card states.\n"
132
+ output_enum 'CardState', card_state_consts, 'SCARD_STATE_(.*)', f
133
+ f.write "\n"
134
+
135
+ f.write " # Smart-card reader states.\n"
136
+ output_enum 'State', reader_state_consts, 'SCARD_(.*)', f
137
+ f.write "\n"
138
+
139
+
140
+ f.write "end # module Smartcard::PCSC::FFILib\n"
141
+ f.write "end # namespace Smartcard::PCSC\n"
142
+ end
143
+ end
144
+ end # namespace Smartcard::Tasks
145
+ end # namespace Smartcard