In Files

Parent

Included Modules

Files

CTAPI::Cardterminal

Represents a cardterminal. Mixes in CTAPI methods and constants.

Constants

LOCK

Global lock for managment of cardterminals.

Attributes

number[R]

The cardterminal number assigned to this object.

interface[R]

The interface this cardterminal is connected to. This is a value of PORT_COM1, PORT_COM2, PORT_COM3, PORT_COM4, PORT_LPT1, PORT_LPT2, PORT_Modem or PORT_Printer.

slot[RW]

The slot of the card terminal where the card is inserted. This is a value in the range of ICC1, ICC2,…, ICC14. Default is ICC1.

chunk_size[R]

The maximum size of the chunks of data that are read from/written to the card. Can be set by calling the chunk_size= method.

manufacturer[R]

The Manufacturer object for this cardterminal.

card[R]

Card object of the inserted chipcard.

Public Class Methods

cardterminals() click to toggle source

Returns the mapping of cardterminal numbers to Cardterminal instances as a hash.

# File lib/ctapi.rb, line 17
    def self.cardterminals
      @cardterminals ||= {}
    end
new(interface, number = nil) click to toggle source

Creates a new Cardterminal object for this interface and initializes the card terminal that is connected. If no cardterminal number is given, a number (that is not currently in use) is assigned to this object. Otherwise this object is forced to use the given card terminal number. BTW: number has to be >= 0.

# File lib/ctapi.rb, line 31
    def initialize(interface, number = nil)
      LOCK.synchronize do
        @interface = interface
        @slot = ICC1
        @chunk_size = 255
        if number
          @number = number
        else
          @number = 0
          while cardterminals.key?(@number)
            @number += 1
          end
        end
        ct_init(@number, interface)
        cardterminals[@number] = true
        @manufacturer = get_manufacturer
        reset
        select_file # master file
      end
    end
open(interface, number = nil) click to toggle source

Takes a block and yields to an initialized Cardterminal object. After the block has been called, the cardterminal is closed again.

# File lib/ctapi.rb, line 102
    def self.open(interface, number = nil)
      cardterminal = self.new(interface, cardterminal)
      yield cardterminal
    ensure
      cardterminal.close if cardterminal
    end

Public Instance Methods

card_inserted?() click to toggle source

Returns true if a card is inserted in the cardterminal at the moment, false if the cardterminal is empty.

# File lib/ctapi.rb, line 339
    def card_inserted?
      cs = card_status
      cs == :card || cs == :card_connect ? true : false
    end
card_status() click to toggle source

Returns the card status as symbol: :no_card, :card, :card_connect.

# File lib/ctapi.rb, line 325
    def card_status
      response = request_card_status
      case response[0]
      when CTBCS_DATA_STATUS_NOCARD
        :no_card
      when CTBCS_DATA_STATUS_CARD
        :card
      when CTBCS_DATA_STATUS_CARD_CONNECT
        :card_connect
      end
    end
cardterminals() click to toggle source

Convenience method to access Cardterminal.cardterminals.

# File lib/ctapi.rb, line 22
    def cardterminals
      self.class.cardterminals
    end
change_pin(old_pin, new_pin) click to toggle source

The pin of this card is changed from old_pin to new_pin. They can be given as strings or arrays of characters. A true result is returned if the sending was successful.

# File lib/ctapi.rb, line 256
    def change_pin(old_pin, new_pin)
      old_pin = old_pin.unpack("C*") if old_pin.is_a? String
      new_pin = new_pin.unpack("C*") if new_pin.is_a? String
      data = old_pin + new_pin
      unless data.size <= 255
        raise ArgumentError.new("size of old and new pin must be <= 255")
      end
      change_pin = [ 0, 0x24, 0, 0, data.size ] + data
      response = data(@slot, HOST, change_pin)
      response.successful? or return
      true
    end
chunk_size=(size) click to toggle source

Sets the size of the chunks of data that are read from/written to the card to size: (0 < size <= 255).

# File lib/ctapi.rb, line 87
    def chunk_size=(size)
      raise ArgumentError.new("size must be > 0") unless size > 0 
      raise ArgumentError.new("size must be <= 255") unless size <= 255
      @chunk_size = size
    end
close() click to toggle source

Terminates the communication with the cardterminal associated with this object and releases the assigned cardterminal number.

# File lib/ctapi.rb, line 134
    def close
      LOCK.synchronize do
        ct_close(@number)
        cardterminals.delete @number
      end
    end
data(dad, sad, *commands) click to toggle source

Sends a sequence of commands to the card or the cardterminal (depending on destination/dad and source address/sad) and returns the response (or responses) to the calling program. A command can be given as an array of bytes [ 0x12, 0x23 ] or as a string of the form ‘12:23:a4’ or ‘12 23 a4’ or as a Command object.

# File lib/ctapi.rb, line 114
    def data(dad, sad, *commands)
      responses = []
      commands.each do |command|
        command =
          case command
          when String then Command.from_string(command)
          when Array then Command.from_array(command)
          else command
          end
        $DEBUG and debug(2, command)
        data = ct_data(@number, dad, sad, command.data)
        response = Response.new(data)
        $DEBUG and debug(1, response)
        responses << response
      end
      return *responses
    end
eject_icc() click to toggle source

Sends the eject card byte sequence to the card terminal and returns the Response object. This method is called by the Cardterminal#reset method.

# File lib/ctapi.rb, line 308
    def eject_icc
      eject = [ 0x20, 0x15, 0x01, 0x00, 0x00 ]
      data(CT, HOST, eject)
    end
enter_pin(pin) click to toggle source

The pin is sent to the card. It can be given as a string or an array of characters. A true result is returned if the sending was successful.

# File lib/ctapi.rb, line 242
    def enter_pin(pin)
      unless pin.size <= 255
        raise ArgumentError.new("size of pin must be <= 255")
      end
      pin = pin.unpack("C*") if pin.is_a? String
      enter_pin = [ 0, 0x20, 0, 0, pin.size ] + pin
      response = data(@slot, HOST, enter_pin)
      response.successful? or return
      true
    end
read(address = 0, size = nil) click to toggle source

Attempts to read data of length size starting at address. If size is nil, an attempt is made to read the whole card memory.

# File lib/ctapi.rb, line 168
    def read(address = 0, size = nil)
      if size == nil
        if @card
          size = @card.memory_size - address
        else
          size = chunk_size
        end
      elsif @card and address + size > @card.memory_size
        size = @card.memory_size - address
      end
      return if size <= 0
      data = ''
      caught = catch(:break) do
        while size >= chunk_size
          d = read_chunk(address, chunk_size)
          if d
            data << d
            address += chunk_size
            size -= chunk_size
          else
            break :break  
          end
        end
      end
      if caught != :break and size > 0
        d = read_chunk(address, size) and data << d
      end
      data
    end
request_card_status() click to toggle source

Requests the card status from the cardterminal. Returns the Response object or nil if the response wasn’t successful. This method is called by the card_inserted? method to find out if a card is inserted into the terminal.

# File lib/ctapi.rb, line 273
    def request_card_status
      get_card_status = [ CTBCS_CLA, CTBCS_INS_STATUS,
        CTBCS_P1_CT_KERNEL, CTBCS_P2_STATUS_ICC, 0 ]
      response = data(CT, HOST, get_card_status)
      response.successful? ? response : nil
    end
request_icc() click to toggle source

Sends a byte sequence to the card to get the answer to reset Response object. This method is called by the Cardterminal#reset method.

# File lib/ctapi.rb, line 283
    def request_icc
        get_atr = [ CTBCS_CLA, CTBCS_INS_REQUEST, CTBCS_P1_INTERFACE1,
               CTBCS_P2_REQUEST_GET_ATR, 0 ]
      @card_old = @card if @card
      @card, atr = nil, nil
      begin
        if card_inserted?
          atr = data(CT, HOST, get_atr)
          if atr
            if atr.not_changed?
              @card = @card_old
              return @card.atr
            end
            @card = Card.new(atr)
          end
        end
      rescue CTAPIError => e
        STDERR.puts "Caught: #{e}."
      end
      @card.atr
    end
reset() click to toggle source

The cardterminal is reset by first calling eject_icc and then request_icc. If the reset was successful the Response object of request_icc is returned, otherwise a nontrue value is returned.

# File lib/ctapi.rb, line 317
    def reset
      eject_icc
      response = request_icc or return
      response.successful? or return
      response
    end
select_file(fid = [ 0x3f, 0 ]) click to toggle source

Sends the select file byte sequence to the card with a default value for the master file. Returns true if the response was successful, false otherwise.

# File lib/ctapi.rb, line 144
    def select_file(fid = [ 0x3f, 0 ])
      select_file = [ 0, 0xa4, 0, 0, 0x02, *fid ]
      response = data(@slot, HOST, select_file)
      response.successful?
    end
write(data, address = 0) click to toggle source

Attempts to write the string data to the card starting at address. On success returns a true value.

# File lib/ctapi.rb, line 216
    def write(data, address = 0)
      size = data.size
      if @card and address + size > @card.memory_size
        size = @card.memory_size - address
      end
      return if size <= 0
      offset = 0
      caught = catch(:break) do
        while size >= chunk_size
          write_chunk(address, data[offset, chunk_size]) or break :break
          address += chunk_size
          offset += chunk_size
          size -= chunk_size
        end
      end
      if caught == :break
        return false
      elsif size > 0
        write_chunk(address, data[offset, size])
      end
      true
    end

Private Instance Methods

card_changed?() click to toggle source

Returns true if the card has been changed in the cardterminal, false it is still the same card. If an error occured nil is returned.

# File lib/ctapi.rb, line 347
    def card_changed?
      response = reset or return
      response.changed? and return true
      response.not_changed? and return false
      return
    end
debug(direction, data) click to toggle source

Prints data with indication of direction to STDERR.

# File lib/ctapi.rb, line 382
    def debug(direction, data)
      direction =
      case direction
        when 1 then '>>> '
        when 2 then '<<< '
        else ''
      end
      STDERR.puts direction + data.to_s
    end
get_manufacturer() click to toggle source

Returns a Manufacturer object with information about the manufacturer of this cardterminal. Could return nil if the response was not successful.

# File lib/ctapi.rb, line 59
    def get_manufacturer
      get_manufacturer_status = [ CTBCS_CLA, CTBCS_INS_STATUS,
        CTBCS_P1_CT_KERNEL, CTBCS_P2_STATUS_MANUFACTURER, 0 ]
      response = data(CT, HOST, get_manufacturer_status)
      return nil unless response.successful?
      Manufacturer.new(response)
    end
read_chunk(address, size) click to toggle source

(Not documented)

# File lib/ctapi.rb, line 150
    def read_chunk(address, size)
      unless address <= 65535
        raise ArgumentError.new("address must be <= 65535")
      end
      unless size <= 255
        raise ArgumentError.new("size must be <= 255")
      end
      read_file = [ 0, 0xb0, address >> 8, address & 0xff, size ]
      response = data(@slot, HOST, read_file)
      response.successful? or return
      data = response.data
      data.slice!(-2, 2)
      data
    end
write_chunk(address, data) click to toggle source

(Not documented)

# File lib/ctapi.rb, line 198
    def write_chunk(address, data)
      unless address <= 65535
        raise ArgumentError.new("address must be <= 65535")
      end
      unless data.size <= 255
        raise ArgumentError.new("size of data must be <= 255")
      end
      data = data.unpack("C*") if data.is_a? String
      write_file = [ 0, 0xd6, address >> 8, address & 0xff, data.size ] +
        data
      response = data(@slot, HOST, write_file)
      response.successful? or return
      true
    end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.