--- /dev/null
+#!/usr/bin/env python2.4
+
+import ldap
+import ldap.filter
+
+LDAP_TIMEOUT = 10
+
+def get_ldap_connection():
+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/ssl/UCC-CA.crt')
+ ldap.set_option(ldap.OPT_X_TLS,1)
+ ldap.set_option(ldap.OPT_X_TLS_ALLOW,1)
+ #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255)
+ conn = ldap.initialize('ldaps://mussel.ucc.gu.uwa.edu.au:636/')
+
+ binddn = 'cn=admin,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au'
+ passfile = open('/etc/pam_ldap.secret')
+ password = passfile.readline().strip()
+ passfile.close()
+
+ conn.simple_bind_s(binddn, password)
+ return conn
+
+def get_uid(card_id):
+ ldapconn = get_ldap_connection()
+
+ basedn = 'ou=People,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au'
+ filter = ldap.filter.filter_format('(uccDispenseMIFARE=%s)', (card_id, ))
+ attrs = ('uidNumber',)
+
+ results = ldapconn.search_st(basedn, ldap.SCOPE_SUBTREE, filter, attrs, timeout=LDAP_TIMEOUT)
+
+ ldapconn.unbind()
+
+ if len(results) != 1:
+ raise ValueError, "no UID found for card ID"
+
+ return results[0][1]['uidNumber'][0]
+
+def set_card_id(uidNumber, card_id):
+ ldapconn = get_ldap_connection()
+
+ basedn = 'ou=People,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au'
+ filter = ldap.filter.filter_format('(uidNumber=%s)', (uidNumber, ))
+ attrs = ('objectClass', )
+
+ results = ldapconn.search_st(basedn, ldap.SCOPE_SUBTREE, filter, attrs, timeout=LDAP_TIMEOUT)
+
+ if len(results) != 1:
+ raise "ValueError", 'error in uidNumber'
+
+ user_dn = results[0][0]
+
+ mod_attrs = []
+
+ # Does it have the correct object class?
+ if 'uccDispenseAccount' not in results[0][1]['objectClass']:
+ # Add uccDispenseAccount objectclass
+ mod_attrs.append((ldap.MOD_ADD, 'objectClass', 'uccDispenseAccount'))
+
+ # Add MIFARE Card ID
+ mod_attrs.append((ldap.MOD_ADD, 'uccDispenseMIFARE', card_id))
+
+ # Use a double-try here to work around something that's fixed in Python 2.5
+ try:
+ try:
+ ldapconn.modify_s(user_dn, mod_attrs)
+ except ldap.TYPE_OR_VALUE_EXISTS, e:
+ pass
+ finally:
+ ldapconn.unbind()
+
+if __name__ == '__main__':
+ #print get_uid('\x01\x02\x03\x04\x05\x06')
+ set_card_id('11251', '\x01\x02\x03\x04\x05\x06')
--- /dev/null
+from MIFAREDriver import MIFAREReader, MIFAREException\r
+from serial import Serial\r
+from LDAPConnector import get_uid, set_card_id\r
+\r
+class MIFAREClient:\r
+ def __init__(self):\r
+ self.port = Serial('/dev/ttyS2', baudrate = 19200)\r
+ self.reader = MIFAREReader(self.port)\r
+ self.reader.set_led(red = False, green = True)\r
+ self.reader.beep(100)\r
+ \r
+ def get_card(self):\r
+ self.reader.set_led(red = True, green = False)\r
+ try:\r
+ card_id, capacity = self.reader.select_card()\r
+ except MIFAREException:\r
+ self.reader.set_led(red = False, green = True)\r
+ return None\r
+ else:\r
+ self.reader.set_led(red = False, green = True)\r
+ self.reader.beep(100)\r
+ return card_id\r
+ \r
+ def add_card(self, uid):\r
+ self.reader.set_led(red = True, green = False)\r
+ for attempt in range(5):\r
+ self.reader.beep(50)\r
+ try:\r
+ card_id, capacity = self.reader.select_card()\r
+ except MIFAREException:\r
+ pass\r
+ else:\r
+ set_card_id(card_id, uid)\r
+ self.reader.set_led(red = False, green = True)\r
+ return True\r
+ self.reader.set_led(red = False, green = True)\r
+ return False
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python2.5
+
+'''mifare - a library for interacting with MIFARE readers.
+Requires Python 2.5.
+
+Licensed under an MIT-style license: see LICENSE file for details.
+'''
+
+import serial
+
+xor = lambda x, y: x ^ y
+def checksum(string):
+ return chr(reduce(xor, [ord(i) for i in string]))
+
+
+class MIFAREException(Exception):
+ pass
+
+
+class MIFARECommunicationException(MIFAREException):
+ pass
+
+
+class MIFAREAuthenticationException(MIFAREException):
+ pass
+
+
+class MIFAREReader:
+ '''An interface to a particular MIFARE reader.'''
+
+ def __init__(self, io):
+ '''Returns an interface to a MIFARE reader given a file-like object.
+ The file-like object is generally a pyserial Serial object.'''
+ self.io = io
+ if isinstance(self.io, serial.Serial):
+ self.io.setTimeout(2)
+ self.address = '\x00\x00'
+
+ def get_absolute_block(self, vector):
+ if vector[0] < 32:
+ return vector[0] * 4 + vector[1]
+ else:
+ # Sectors below 32 are 4 blocks
+ # Sectors above are 16 blocks
+ # Thus, sector 32 starts at block 128, 33 at 144, and so on
+ return 128 + (vector[0] - 32) * 16 + vector[1]
+
+ def send_packet(self, data):
+ '''Constructs a packet for the supplied data string, sends it to the
+ MIFARE reader, then returns the response (if any) to the commmand.'''
+
+ # Occasionally the reader inserts extra trailing characters into its
+ # responses, so flush the buffers if possible beforehand.
+ if isinstance(self.io, serial.Serial):
+ self.io.flushInput()
+ self.io.flushOutput()
+
+ # XXX - Needs more error checking.
+ data = '\x00' + self.address + data
+ packet = '\xAA\xBB' + chr(len(data)) + data + checksum(data)
+ self.io.write(packet)
+ response = ''
+ header = self.io.read(2)
+ if header == '\xaa\xbb':
+ length = ord(self.io.read(1))
+ data = self.io.read(length)
+ packet_xsum = self.io.read(1)
+ if checksum(data) == packet_xsum and len(data) == length:
+ # Strip off separator and address header
+ return data[3:]
+ else:
+ raise MIFARECommunicationException, "Invalid response received"
+
+ def set_antenna(self, state = True):
+ """Turn the card reader's antenna on or off (no return value)"""
+ command = '\x0C\x01' + chr(int(state))
+ response = self.send_packet(command)
+ if response == '\x0c\x01\x00':
+ return None
+ else:
+ raise MIFAREException, 'command failed: set_antenna (%s)' % state
+
+ def select_card(self, include_halted = False):
+ """Selects a card and returns a tuple of (serial number, capacity).
+
+ If include_halted is set, may select a card that halt() has previously
+ been called on."""
+
+ # Request type of card available
+ command = command = '\x01\x02'
+ if include_halted:
+ command += '\x52'
+ else:
+ command += '\x26'
+
+ card_type_response = self.send_packet(command)
+
+ if card_type_response[2] == '\x14':
+ raise MIFAREException, "select_card: no card available"
+ card_type = card_type_response[3:5]
+
+ if card_type == '\x44\x00': # MIFARE UltraLight
+ raise NotImplementedError, "UltraLight card selected - no functions available"
+
+ else:
+ # Otherwise, must be a standard MIFARE card.
+ # Anticollision
+ command = '\x02\x02\x04'
+ # No error handling on this command
+ serial = self.send_packet(command)[3:]
+
+ # Select the card for use
+ capacity = ord(self.send_packet('\x03\x02' + serial)[3])
+ return (serial, capacity)
+
+ def sector_login(self, blockvect, key, keytype=0):
+ """Log in to a block using the six-byte key.
+
+ Use a keytype of 1 to use key B."""
+ sector = self.get_absolute_block((blockvect[0], 0))
+
+ if len(key) != 6:
+ raise ValueError, 'key must be a six-byte string'
+
+ keytype = 96 + keytype
+
+ data = chr(keytype) + chr(sector) + key
+
+ result = self.send_packet('\x07\x02' + data)
+ if ord(result[2]) == 22:
+ raise MIFAREAuthenticationException, "incorrect key provided"
+
+ return
+
+ def read_block(self, blockvect):
+ "Read the 16-byte block at vector (sector, block)."
+ block = self.get_absolute_block(blockvect)
+
+ result = self.send_packet('\x08\x02' + chr(block))
+ return result[3:19]
+
+ def write_block(self, blockvect, data):
+ """Write the 16 bytes in data to the block at vector (sector, block)."""
+ block = self.get_absolute_block(blockvect)
+ if len(data) != 16:
+ raise ValueError, "invalid data length - must be 16 bytes"
+
+ result = self.send_packet('\x09\x02' + chr(block) + data)
+ return
+
+ def write_key(self, key):
+ pass
+
+ def value_block_increment(self, blocknum, increment):
+ pass
+
+ def value_block_decrement(self, blocknum, decrement):
+ pass
+
+ def copy_block(self, source, dest):
+ pass
+
+ def halt(self):
+ """Halt the current card - no further transactions will be performed with it."""
+ self.send_packet('\x04\x02')
+
+ def set_led(self, red = False, green = False):
+ led_state = 0
+ if red:
+ led_state += 1
+ if green:
+ led_state += 2
+ self.send_packet('\x07\x01' + chr(led_state))
+
+ def beep(self, length):
+ '''Beep for a specified length of milliseconds.'''
+ length = int(round(length / 10.))
+ if length > 255:
+ length = 255
+ self.send_packet('\x06\x01' + chr(length))
+
+ def reset(self):
+ pass