smartcard 0.4.11 → 0.5.0

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.
@@ -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