Minor bug fixes to get it working
[uccvend-vendserver.git] / VendServer / VendServer.py
index d079b4e..dd35cd4 100755 (executable)
@@ -22,6 +22,7 @@ from SnackConfig import get_snack#, get_snacks
 import socket
 from posix import geteuid
 from LDAPConnector import get_uid,get_uname, set_card_id
+from OpenDispense import OpenDispense as Dispense
 
 CREDITS="""
 This vending machine software brought to you by:
@@ -65,6 +66,35 @@ STATE_GRANDFATHER_CLOCK,
 TEXT_SPEED = 0.8
 IDLE_SPEED = 0.05
 
+
+config_options = {
+       'DBServer': ('Database', 'Server'),
+       'DBName': ('Database', 'Name'),
+       'DBUser': ('VendingMachine', 'DBUser'),
+       'DBPassword': ('VendingMachine', 'DBPassword'),
+       
+       'ServiceName': ('VendingMachine', 'ServiceName'),
+       'ServicePassword': ('VendingMachine', 'Password'),
+       
+       'ServerName': ('DecServer', 'Name'),
+       'ConnectPassword': ('DecServer', 'ConnectPassword'),
+       'PrivPassword': ('DecServer', 'PrivPassword'),
+       }
+
+class VendConfigFile:
+       def __init__(self, config_file, options):
+               try:
+                       cp = ConfigParser.ConfigParser()
+                       cp.read(config_file)
+
+                       for option in options:
+                               section, name = options[option]
+                               value = cp.get(section, name)
+                               self.__dict__[option] = value
+               
+               except ConfigParser.Error, e:
+                       raise SystemExit("Error reading config file "+config_file+": " + str(e))
+
 class DispenseDatabaseException(Exception): pass
 
 class DispenseDatabase:
@@ -95,247 +125,9 @@ class DispenseDatabase:
                while notifier is not None:
                        self.process_requests()
                        notify = self.db.getnotify()
-
-def scroll_options(username, mk, welcome = False):
-       if welcome:
-               # Balance checking
-               acct, unused = Popen(['dispense', 'acct', username], close_fds=True, stdout=PIPE).communicate()
-               # this is fucking appalling
-               balance = acct[acct.find("$")+1:acct.find("(")].strip()
-        
-               msg = [(center('WELCOME'), False, TEXT_SPEED),
-                          (center(username), False, TEXT_SPEED),
-                          (center(balance), False, TEXT_SPEED),]
-       else:
-               msg = []
-       choices = ' '*10+'CHOICES: '
-
-       # Get coke contents
-       cokes = []
-       for i in range(0, 7):
-               args = ('dispense', 'iteminfo', 'coke:%i' % i)
-               info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
-               m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
-               cents = int(m.group(1))*100 + int(m.group(2))
-               cokes.append('%i %i %s' % (i, cents, m.group(3)));
-
-       for c in cokes:
-               c = c.strip()
-               (slot_num, price, slot_name) = c.split(' ', 2)
-               if slot_name == 'dead': continue
-               choices += '%s-(%sc)-%s8 '%(slot_name, price, slot_num)
-
-#      we don't want to print snacks for now since it'll be too large
-#      and there's physical bits of paper in the machine anyway - matt
-#      try:
-#              snacks = get_snacks()
-#      except:
-#              snacks = {}
-#
-#      for slot, ( name, price ) in snacks.items():
-#              choices += '%s8-%s (%sc) ' % ( slot, name, price )
-
-       choices += '55-DOOR '
-       choices += 'OR ANOTHER SNACK. '
-       choices += '99 TO READ AGAIN. '
-       choices += 'CHOICE?   '
-       msg.append((choices, False, None))
-       mk.set_messages(msg)
-
-_pin_uid = 0
-_pin_uname = 'root'
-_pin_pin = '----'
-
-def _check_pin(uid, pin):
-       global _pin_uid
-       global _pin_uname
-       global _pin_pin
-       print "_check_pin('",uid,"',---)"
-       if uid != _pin_uid:
-               try:
-                       info = pwd.getpwuid(uid)
-               except KeyError:
-                       logging.info('getting pin for uid %d: user not in password file'%uid)
-                       return None
-               if info.pw_dir == None: return False
-               pinfile = os.path.join(info.pw_dir, '.pin')
-               try:
-                       s = os.stat(pinfile)
-               except OSError:
-                       logging.info('getting pin for uid %d: .pin not found in home directory'%uid)
-                       return None
-               if s.st_mode & 077:
-                       logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%uid)
-                       os.chmod(pinfile, 0600)
-               try:
-                       f = file(pinfile)
-               except IOError:
-                       logging.info('getting pin for uid %d: I cannot read pin file'%uid)
-                       return None
-               pinstr = f.readline()
-               f.close()
-               if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
-                       logging.info('getting pin for uid %d: %s not a good pin'%(uid,repr(pinstr)))
-                       return None
-               _pin_uid = uid
-               _pin_pin = pinstr
-               _pin_uname = info.pw_name
-       else:
-               pinstr = _pin_pin
-       if pin == int(pinstr):
-               logging.info("Pin correct for %d",uid)
-       else:
-               logging.info("Pin incorrect for %d",uid)
-       return pin == int(pinstr)
-
-def acct_is_disabled(name=None):
-       global _pin_uname
-       if name == None:
-               name = _pin_uname
-       acct, unused = Popen(['dispense', 'acct', _pin_uname], close_fds=True, stdout=PIPE).communicate()
-       # this is fucking appalling
-       flags = acct[acct.find("(")+1:acct.find(")")].strip()
-       if 'disabled' in flags:
-               return True
-       if 'internal' in flags:
-               return True
-       return False
-
-def has_good_pin(uid):
-       return _check_pin(uid, None) != None
-
-def verify_user_pin(uid, pin, skip_pin_check=False):
-       if skip_pin_check or _check_pin(uid, pin) == True:
-               info = pwd.getpwuid(uid)
-               if skip_pin_check:
-                       if acct_is_disabled(info.pw_name):
-                               logging.info('refused mifare for disabled acct uid %d (%s)'%(uid,info.pw_name))
-                               return '-disabled-'
-                       logging.info('accepted mifare for uid %d (%s)'%(uid,info.pw_name))
-               else:
-                       logging.info('accepted pin for uid %d (%s)'%(uid,info.pw_name))
-               return info.pw_name
-       else:
-               logging.info('refused pin for uid %d'%(uid))
-               return None
-
-
-def cookie(v):
-       seed(time())
-       messages = ['  WASSUP! ', 'PINK FISH ', ' SECRETS ', '  ESKIMO  ', ' FORTUNES ', 'MORE MONEY']
-       choice = int(random()*len(messages))
-       msg = messages[choice]
-       left = range(len(msg))
-       for i in range(len(msg)):
-               if msg[i] == ' ': left.remove(i)
-       reveal = 1
-       while left:
-               s = ''
-               for i in range(0, len(msg)):
-                       if i in left:
-                               if reveal == 0:
-                                       left.remove(i)
-                                       s += msg[i]
-                               else:
-                                       s += chr(int(random()*26)+ord('A'))
-                               reveal += 1
-                               reveal %= 17
-                       else:
-                               s += msg[i]
-               v.display(s)
-
-def center(str):
-       LEN = 10
-       return ' '*((LEN-len(str))/2)+str
-
-
-
-idlers = []
-idler = None
-
-def setup_idlers(v):
-       global idlers, idler
-       idlers = [
-               #
-               GrayIdler(v),
-               GrayIdler(v,one="*",zero="-"),
-               GrayIdler(v,one="/",zero="\\"),
-               GrayIdler(v,one="X",zero="O"),
-               GrayIdler(v,one="*",zero="-",reorder=1),
-               GrayIdler(v,one="/",zero="\\",reorder=1),
-               GrayIdler(v,one="X",zero="O",reorder=1),
-               #
-               ClockIdler(v),
-               ClockIdler(v),
-               ClockIdler(v),
-               #
-               StringIdler(v), # Hello Cruel World
-               StringIdler(v, text="Kill 'em all", repeat=False),
-               StringIdler(v, text=CREDITS),
-               StringIdler(v, text=str(math.pi) + "            "),
-               StringIdler(v, text=str(math.e) + "            "),
-               StringIdler(v, text="    I want some pizza - please call Broadway Pizza on +61 8 9389 8500 - and order as Quinn - I am getting really hungry", repeat=False),
-               # "Hello World" in brainfuck
-               StringIdler(v, text=">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-] <.>+++++++++++[<++++++++>-]<-.--------.+++.------.--------.[-]>++++++++[<++++>- ]<+.[-]++++++++++."),
-               #
-               TrainIdler(v),
-               #
-               FileIdler(v, '/usr/share/common-licenses/GPL-2',affinity=2),
-               #
-               PipeIdler(v, "/usr/bin/getent", "passwd"),
-               FortuneIdler(v,affinity=20),
-               ]
-       disabled = [
-               ]
-
-def reset_idler(v, vstatus, t = None):
-       global idlers, idler
-       idler = GreetingIdler(v, t)
-       vstatus.time_of_next_idlestep = time()+idler.next()
-       vstatus.time_of_next_idler = None
-       vstatus.time_to_autologout = None
-       vstatus.change_state(STATE_IDLE, 1)
-
-def choose_idler():
-       global idlers, idler
-
-       # Implementation of the King Of the Hill algorithm from;
-       # http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/
-
-       #def weighted_choice_king(weights):
-       #       total = 0
-       #       winner = 0
-       #       for i, w in enumerate(weights):
-       #               total += w
-       #               if random.random() * total < w:
-       #                       winner = i
-       #       return winner
-       #       
-
-       total = 0
-       winner = None
-       
-       for choice in idlers:
-               weight = choice.affinity()
-               total += weight
-               if random() * total < weight:
-                       winner = choice
-
-       idler = winner
-
-       if idler:
-               idler.reset()
-
-def idle_step(vstatus):
-       global idler
-       if idler.finished():
-               choose_idler()
-               vstatus.time_of_next_idler = time() + 30
-       nextidle = idler.next()
-       if nextidle is None:
-               nextidle = IDLE_SPEED
-       vstatus.time_of_next_idlestep = time()+nextidle
-
+"""
+This class manages the current state of the vending machine.
+"""
 class VendState:
        def __init__(self,v):
                self.state_table = {}
@@ -353,671 +145,933 @@ class VendState:
 
        def change_state(self,newstate,newcounter=None):
                if self.state != newstate:
-                       #print "Changing state from: ", 
-                       #print self.state,
-                       #print " to ", 
-                       #print newstate 
                        self.state = newstate
 
                if newcounter is not None and self.counter != newcounter:
-                       #print "Changing counter from: ", 
-                       #print self.counter,
-                       #print " to ", 
-                       #print newcounter 
                        self.counter = newcounter
 
-
-
-def handle_tick_event(event, params, v, vstatus):
-       # don't care right now.
-       pass
-
-def handle_switch_event(event, params, v, vstatus):
-       # don't care right now.
-       pass
-
-
-def do_nothing(state, event, params, v, vstatus):
-       print "doing nothing (s,e,p)", state, " ", event, " ", params
-       pass
-
-def handle_getting_uid_idle(state, event, params, v, vstatus):
-       # don't care right now.
-       pass
-
-def handle_getting_pin_idle(state, event, params, v, vstatus):
-       # don't care right now.
-       pass
-
-def handle_get_selection_idle(state, event, params, v, vstatus):
-       global _last_card_id
-       # don't care right now.
-       ###
-       ### State logging out ..
-       if vstatus.time_to_autologout != None:
-               time_left = vstatus.time_to_autologout - time()
-               if time_left < 6 and (vstatus.last_timeout_refresh is None or vstatus.last_timeout_refresh > time_left):
-                       vstatus.mk.set_message('LOGOUT: '+str(int(time_left)))
-                       vstatus.last_timeout_refresh = int(time_left)
-                       vstatus.cur_selection = ''
-
-       if vstatus.time_to_autologout != None and vstatus.time_to_autologout - time() <= 0:
-               vstatus.time_to_autologout = None
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-               vstatus.cur_selection = ''
-               _last_card_id = -1
-               reset_idler(v, vstatus)
-
-       ### State fully logged out ... reset variables
-       if vstatus.time_to_autologout and not vstatus.mk.done(): 
-               vstatus.time_to_autologout = None
-       if vstatus.cur_user == '' and vstatus.time_to_autologout: 
-               vstatus.time_to_autologout = None
-       
-       ### State logged in
-       if len(vstatus.cur_pin) == PIN_LENGTH and vstatus.mk.done() and vstatus.time_to_autologout == None:
-               # start autologout
-               vstatus.time_to_autologout = time() + 15
-               vstatus.last_timeout_refresh = None
-
-       ## FIXME - this may need to be elsewhere.....
-       # need to check
-       vstatus.mk.update_display()
-
-
-
-def handle_get_selection_key(state, event, params, v, vstatus):
-       global _last_card_id
-       key = params
-       if len(vstatus.cur_selection) == 0:
-               if key == 11:
-                       vstatus.cur_pin = ''
-                       vstatus.cur_user = ''
-                       vstatus.cur_selection = ''
-                       _last_card_id = -1
-                       vstatus.mk.set_messages([(center('BYE!'), False, 1.5)])
-                       reset_idler(v, vstatus, 2)
-                       return
-               vstatus.cur_selection += chr(key + ord('0'))
-               vstatus.mk.set_message('SELECT: '+vstatus.cur_selection)
-               vstatus.time_to_autologout = None
-       elif len(vstatus.cur_selection) == 1:
-               if key == 11:
-                       vstatus.cur_selection = ''
-                       vstatus.time_to_autologout = None
-                       scroll_options(vstatus.username, vstatus.mk)
-                       return
-               else:
-                       vstatus.cur_selection += chr(key + ord('0'))
-                       if vstatus.cur_user:
-                               make_selection(v,vstatus)
-                               vstatus.cur_selection = ''
-                               vstatus.time_to_autologout = time() + 8
-                               vstatus.last_timeout_refresh = None
-                       else:
-                               # Price check mode.
-                               price_check(v,vstatus)
-                               vstatus.cur_selection = ''
-                               vstatus.time_to_autologout = None
-                               vstatus.last_timeout_refresh = None
-
-def make_selection(v, vstatus):
-       # should use sudo here
-       if vstatus.cur_selection == '55':
-               vstatus.mk.set_message('OPENSESAME')
-               logging.info('dispensing a door for %s'%vstatus.username)
-               if geteuid() == 0:
-                       #ret = os.system('su - "%s" -c "dispense door"'%vstatus.username)
-                       ret = os.system('dispense -u "%s" door'%vstatus.username)
+class VendServer():
+
+       v = None
+       vstatus = None
+       state = None
+       event = None
+       idlers = []
+       idler = None
+
+       _pin_uid = 0
+       _pin_uname = 'root'
+       _pin_pin = '----'
+       _last_card_id = -1
+
+       dispense = None
+
+       """
+       Show information to the user as to what can be dispensed.
+       """
+       def scroll_options(self, username, mk, welcome = False):
+               # If the user has just logged in, show them their balance
+               if welcome:
+                       balance = self.dispense.getBalance()
+                       
+                       msg = [(self.center('WELCOME'), False, TEXT_SPEED),
+                                  (self.center(self.dispense.getUsername()), False, TEXT_SPEED),
+                                  (self.center(balance), False, TEXT_SPEED),]
                else:
-                       ret = os.system('dispense door')
-               if ret == 0:
-                       logging.info('door opened')
-                       vstatus.mk.set_message(center('DOOR OPEN'))
+                       msg = []
+               choices = ' '*10+'CHOICES: '
+
+               # Show what is in the coke machine
+               # Need to update this so it uses the abstracted system
+               cokes = []
+               for i in ['08', '18', '28', '38', '48', '58', '68']:
+                       cokes.append((i, self.dispense.getItemInfo(i)))
+
+               for c in cokes:
+                       if c[1][0] == 'dead':
+                               continue
+                       choices += '%s-(%sc)-%s8 '%(c[1][0], c[1][1], c[0])
+
+               # Show the final few options
+               choices += '55-DOOR '
+               choices += 'OR ANOTHER SNACK. '
+               choices += '99 TO READ AGAIN. '
+               choices += 'CHOICE?   '
+               msg.append((choices, False, None))
+               # Send it to the display
+               mk.set_messages(msg)
+
+
+       """
+       Verify the users pin
+       """
+       """
+       def _check_pin(self, uid, pin):
+               print "_check_pin('",uid,"',---)"
+               if uid != self._pin_uid:
+                       try:
+                               info = pwd.getpwuid(uid)
+                       except KeyError:
+                               logging.info('getting pin for uid %d: user not in password file'%uid)
+                               return None
+                       if info.pw_dir == None: return False
+               pinfile = os.path.join(info.pw_dir, '.pin')
+                       try:
+                               s = os.stat(pinfile)
+                       except OSError:
+                               logging.info('getting pin for uid %d: .pin not found in home directory'%uid)
+                               return None
+                       if s.st_mode & 077:
+                               logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%uid)
+                               os.chmod(pinfile, 0600)
+                       try:
+                               f = file(pinfile)
+                       except IOError:
+                               logging.info('getting pin for uid %d: I cannot read pin file'%uid)
+                               return None
+                       pinstr = f.readline()
+                       f.close()
+                       if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
+                               logging.info('getting pin for uid %d: %s not a good pin'%(uid,repr(pinstr)))
+                               return None
+                       self._pin_uid = uid
+                       self._pin_pin = pinstr
+                       self._pin_uname = info.pw_name
                else:
-                       logging.warning('user %s tried to dispense a bad door'%vstatus.username)
-                       vstatus.mk.set_message(center('BAD DOOR'))
-               sleep(1)
-       elif vstatus.cur_selection == '81':
-               cookie(v)
-       elif vstatus.cur_selection == '99':
-               scroll_options(vstatus.username, vstatus.mk)
-               vstatus.cur_selection = ''
-               return
-       elif vstatus.cur_selection[1] == '8':
-               v.display('GOT DRINK?')
-               if ((os.system('dispense -u "%s" coke:%s'%(vstatus.username, vstatus.cur_selection[0])) >> 8) != 0):
-                       v.display('SEEMS NOT')
+                       pinstr = self._pin_pin
+               if pin == int(pinstr):
+                       logging.info("Pin correct for %d",uid)
                else:
-                       v.display('GOT DRINK!')
-       else:
-               # first see if it's a named slot
-               try:
-                       price, shortname, name = get_snack( vstatus.cur_selection )
-               except:
-                       price, shortname, name = get_snack( '--' )
-               dollarprice = "$%.2f" % ( price / 100.0 )
-               v.display(vstatus.cur_selection+' - %s'%dollarprice)
-#              exitcode = os.system('dispense -u "%s" give \>snacksales %d "%s"'%(vstatus.username, price, name)) >> 8
-#              exitcode = os.system('dispense -u "%s" give \>sales\:snack %d "%s"'%(vstatus.username, price, name)) >> 8
-               exitcode = os.system('dispense -u "%s" snack:%s'%(vstatus.username, vstatus.cur_selection)) >> 8
-               if (exitcode == 0):
-                       # magic dispense syslog service
-                       syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "vended %s (slot %s) for %s" % (name, vstatus.cur_selection, vstatus.username))
-                       (worked, code, string) = v.vend(vstatus.cur_selection)
-                       if worked:
-                               v.display('THANK YOU')
+                       logging.info("Pin incorrect for %d",uid)
+               return pin == int(pinstr)
+       """
+
+       """
+       Check if the users account has been disabled
+       """
+       """
+       def acct_is_disabled(self, name=None):
+               if name == None:
+                       name = self._pin_uname
+               acct, unused = Popen(['dispense', 'acct', self._pin_uname], close_fds=True, stdout=PIPE).communicate()
+               # this is fucking appalling
+               flags = acct[acct.find("(")+1:acct.find(")")].strip()
+               if 'disabled' in flags:
+                       return True
+               if 'internal' in flags:
+                       return True
+               return False
+       """
+
+       """
+       Check that the user has a valid pin set
+       """
+       """
+       def has_good_pin(self, uid):
+               return self._check_pin(uid, None) != None
+       """
+
+       """
+       Verify the users pin.
+       """
+       """
+       def verify_user_pin(self, uid, pin, skip_pin_check=False):
+               if skip_pin_check or self._check_pin(uid, pin) == True:
+                       info = pwd.getpwuid(uid)
+                       if skip_pin_check:
+                               if self.acct_is_disabled(info.pw_name):
+                                       logging.info('refused mifare for disabled acct uid %d (%s)'%(uid,info.pw_name))
+                                       return '-disabled-'
+                               logging.info('accepted mifare for uid %d (%s)'%(uid,info.pw_name))
                        else:
-                               print "Vend Failed:", code, string
-                               v.display('VEND FAIL')
-               elif (exitcode == 5):   # RV_BALANCE
-                       v.display('NO MONEY?')
-               elif (exitcode == 4):   # RV_ARGUMENTS (zero give causes arguments)
-                       v.display('EMPTY SLOT')
-               elif (exitcode == 1):   # RV_BADITEM (Dead slot)
-                       v.display('EMPTY SLOT')
+                               logging.info('accepted pin for uid %d (%s)'%(uid,info.pw_name))
+                       return info.pw_name
                else:
-                       syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %d)" % (name, vstatus.cur_selection, vstatus.username, exitcode))
-                       v.display('UNK ERROR')
-       sleep(1)
+                       logging.info('refused pin for uid %d'%(uid))
+                       return None
+       """
+
+       """
+       In here just for fun.
+       """
+       def cookie(self):
+               seed(time())
+               messages = ['  WASSUP! ', 'PINK FISH ', ' SECRETS ', '  ESKIMO  ', ' FORTUNES ', 'MORE MONEY']
+               choice = int(random()*len(messages))
+               msg = messages[choice]
+               left = range(len(msg))
+               for i in range(len(msg)):
+                       if msg[i] == ' ': left.remove(i)
+               reveal = 1
+               while left:
+                       s = ''
+                       for i in range(0, len(msg)):
+                               if i in left:
+                                       if reveal == 0:
+                                               left.remove(i)
+                                               s += msg[i]
+                                       else:
+                                               s += chr(int(random()*26)+ord('A'))
+                                       reveal += 1
+                                       reveal %= 17
+                               else:
+                                       s += msg[i]
+                       self.v.display(s)
+
+       """
+       Format text so it will appear centered on the screen.
+       """
+       def center(self, str):
+               LEN = 10
+               return ' '*((LEN-len(str))/2)+str
+
+       """
+       Configure the things that will appear on screen whil the machine is idling.
+       """
+       def setup_idlers(self):
+               self.idlers = [
+                       #
+                       GrayIdler(self.v),
+                       GrayIdler(self.v,one="*",zero="-"),
+                       GrayIdler(self.v,one="/",zero="\\"),
+                       GrayIdler(self.v,one="X",zero="O"),
+                       GrayIdler(self.v,one="*",zero="-",reorder=1),
+                       GrayIdler(self.v,one="/",zero="\\",reorder=1),
+                       GrayIdler(self.v,one="X",zero="O",reorder=1),
+                       #
+                       ClockIdler(self.v),
+                       ClockIdler(self.v),
+                       ClockIdler(self.v),
+                       #
+                       StringIdler(self.v), # Hello Cruel World
+                       StringIdler(self.v, text="Kill 'em all", repeat=False),
+                       StringIdler(self.v, text=CREDITS),
+                       StringIdler(self.v, text=str(math.pi) + "            "),
+                       StringIdler(self.v, text=str(math.e) + "            "),
+                       StringIdler(self.v, text="    I want some pizza - please call Broadway Pizza on +61 8 9389 8500 - and order as Quinn - I am getting really hungry", repeat=False),
+                       # "Hello World" in brainfuck
+                       StringIdler(self.v, text=">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-] <.>+++++++++++[<++++++++>-]<-.--------.+++.------.--------.[-]>++++++++[<++++>- ]<+.[-]++++++++++."),
+                       #
+                       TrainIdler(self.v),
+                       #
+                       FileIdler(self.v, '/usr/share/common-licenses/GPL-2',affinity=2),
+                       #
+                       PipeIdler(self.v, "/usr/bin/getent", "passwd"),
+                       FortuneIdler(self.v,affinity=20),
+                       ]
+               disabled = [
+                       ]
+
+       """
+       Go back to the default idler.
+       """
+       def reset_idler(self, t = None):
+               self.idler = GreetingIdler(self.v, t)
+               self.vstatus.time_of_next_idlestep = time()+self.idler.next()
+               self.vstatus.time_of_next_idler = None
+               self.vstatus.time_to_autologout = None
+               self.vstatus.change_state(STATE_IDLE, 1)
+
+       """
+       Change to a random idler.
+       """
+       def choose_idler(self):
+
+               # Implementation of the King Of the Hill algorithm from;
+               # http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/
+
+               #def weighted_choice_king(weights):
+               #       total = 0
+               #       winner = 0
+               #       for i, w in enumerate(weights):
+               #               total += w
+               #               if random.random() * total < w:
+               #                       winner = i
+               #       return winner
+               #       
+
+               total = 0
+               winner = None
+               
+               for choice in self.idlers:
+                       weight = choice.affinity()
+                       total += weight
+                       if random() * total < weight:
+                               winner = choice
+
+               self.idler = winner
+
+               if self.idler:
+                       self.idler.reset()
+       """
+       Run every step while the machine is idling.
+       """
+       def idle_step(self):
+               if self.idler.finished():
+                       self.choose_idler()
+                       self.vstatus.time_of_next_idler = time() + 30
+               nextidle = self.idler.next()
+               if nextidle is None:
+                       nextidle = IDLE_SPEED
+               self.vstatus.time_of_next_idlestep = time()+nextidle
+
+       """
+       These next two events trigger no response in the code.
+       """
+       def handle_tick_event(self, event, params):
+               # don't care right now.
+               pass
 
+       def handle_switch_event(self, event, params):
+               # don't care right now.
+               pass
 
-def price_check(v, vstatus):
-       if vstatus.cur_selection[1] == '8':
-               args = ('dispense', 'iteminfo', 'coke:' + vstatus.cur_selection[0])
-               info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
-               dollarprice = re.match("\s*[a-z]+:\d+\s+(\d+\.\d\d)\s+([^\n]+)", info).group(1)
-       else:
-               # first see if it's a named slot
-               try:
-                       price, shortname, name = get_snack( vstatus.cur_selection )
-               except:
-                       price, shortname, name = get_snack( '--' )
-               dollarprice = "$%.2f" % ( price / 100.0 )
-       v.display(vstatus.cur_selection+' - %s'%dollarprice)
+       """
+       Don't do anything for this event.
+       """
+       def do_nothing(self, event, params):
+               print "doing nothing (s,e,p)", state, " ", event, " ", params
+               pass
 
+       """
+       These next few entrie tell us to do nothing while we are idling
+       """
+       def handle_getting_uid_idle(self, event, params):
+               # don't care right now.
+               pass
 
-def handle_getting_pin_key(state, event, params, v, vstatus):
-       #print "handle_getting_pin_key (s,e,p)", state, " ", event, " ", params
-       key = params
-       if len(vstatus.cur_pin) < PIN_LENGTH:
-               if key == 11:
-                       if vstatus.cur_pin == '':
-                               vstatus.cur_user = ''
-                               reset_idler(v, vstatus)
+       def handle_getting_pin_idle(self, event, params):
+               # don't care right now.
+               pass
 
+       """
+       While logged in and waiting for user input, slowly get closer to logging out.
+       """
+       def handle_get_selection_idle(self, event, params):
+               # don't care right now.
+               ###
+               ### State logging out ..
+               if self.vstatus.time_to_autologout != None:
+                       time_left = self.vstatus.time_to_autologout - time()
+                       if time_left < 6 and (self.vstatus.last_timeout_refresh is None or self.vstatus.last_timeout_refresh > time_left):
+                               self.vstatus.mk.set_message('LOGOUT: '+str(int(time_left)))
+                               self.vstatus.last_timeout_refresh = int(time_left)
+                               self.vstatus.cur_selection = ''
+
+               if self.vstatus.time_to_autologout != None and self.vstatus.time_to_autologout - time() <= 0:
+                       self.vstatus.time_to_autologout = None
+                       self.vstatus.cur_user = ''
+                       self.vstatus.cur_pin = ''
+                       self.vstatus.cur_selection = ''
+                       self._last_card_id = -1
+                       self.reset_idler()
+
+               ### State fully logged out ... reset variables
+               if self.vstatus.time_to_autologout and not self.vstatus.mk.done(): 
+                       self.vstatus.time_to_autologout = None
+               if self.vstatus.cur_user == '' and self.vstatus.time_to_autologout: 
+                       self.vstatus.time_to_autologout = None
+               
+               ### State logged in
+               if len(self.vstatus.cur_pin) == PIN_LENGTH and self.vstatus.mk.done() and self.vstatus.time_to_autologout == None:
+                       # start autologout
+                       self.vstatus.time_to_autologout = time() + 15
+                       self.vstatus.last_timeout_refresh = None
+
+               ## FIXME - this may need to be elsewhere.....
+               # need to check
+               self.vstatus.mk.update_display()
+
+       """
+       Triggered on user input while logged in.
+       """
+       def handle_get_selection_key(self, event, params):
+               key = params
+               if len(self.vstatus.cur_selection) == 0:
+                       if key == 11:
+                               self.vstatus.cur_pin = ''
+                               self.vstatus.cur_user = ''
+                               self.vstatus.cur_selection = ''
+                               _last_card_id = -1
+                               self.vstatus.mk.set_messages([(self.center('BYE!'), False, 1.5)])
+                               self.reset_idler(2)
                                return
-                       vstatus.cur_pin = ''
-                       vstatus.mk.set_message('PIN: ')
-                       return
-               vstatus.cur_pin += chr(key + ord('0'))
-               vstatus.mk.set_message('PIN: '+'X'*len(vstatus.cur_pin))
-               if len(vstatus.cur_pin) == PIN_LENGTH:
-                       vstatus.username = verify_user_pin(int(vstatus.cur_user), int(vstatus.cur_pin))
-                       if vstatus.username:
-                               v.beep(0, False)
-                               vstatus.cur_selection = ''
-                               vstatus.change_state(STATE_GET_SELECTION)
-                               scroll_options(vstatus.username, vstatus.mk, True)
+                       self.vstatus.cur_selection += chr(key + ord('0'))
+                       self.vstatus.mk.set_message('SELECT: '+self.vstatus.cur_selection)
+                       self.vstatus.time_to_autologout = None
+               elif len(self.vstatus.cur_selection) == 1:
+                       if key == 11:
+                               self.vstatus.cur_selection = ''
+                               self.vstatus.time_to_autologout = None
+                               self.scroll_options(self.vstatus.username, self.vstatus.mk)
                                return
                        else:
-                               v.beep(40, False)
-                               vstatus.mk.set_messages(
-                                       [(center('BAD PIN'), False, 1.0),
-                                        (center('SORRY'), False, 0.5)])
-                               vstatus.cur_user = ''
-                               vstatus.cur_pin = ''
-                       
-                               reset_idler(v, vstatus, 2)
+                               self.vstatus.cur_selection += chr(key + ord('0'))
+                               if self.vstatus.cur_user:
+                                       self.make_selection()
+                                       self.vstatus.cur_selection = ''
+                                       self.vstatus.time_to_autologout = time() + 8
+                                       self.vstatus.last_timeout_refresh = None
+                               else:
+                                       # Price check mode.
+                                       self.dispense.getItemInfo(self.vstatus.cur_selection)
+                                       self.vstatus.cur_selection = ''
+                                       self.vstatus.time_to_autologout = None
+                                       self.vstatus.last_timeout_refresh = None
+
+       """
+       Triggered when the user has entered the id of something they would like to purchase.
+       """
+       def make_selection(self):
+               # should use sudo here
+               if self.vstatus.cur_selection == '55':
+                       self.vstatus.mk.set_message('OPENSESAME')
+                       logging.info('dispensing a door for %s'%self.vstatus.username)
+                       if geteuid() == 0:
+                               ret = os.system('dispense -u "%s" door'%self.vstatus.username)
+                       else:
+                               ret = os.system('dispense door')
+                       if ret == 0:
+                               logging.info('door opened')
+                               self.vstatus.mk.set_message(self.center('DOOR OPEN'))
+                       else:
+                               logging.warning('user %s tried to dispense a bad door'%self.vstatus.username)
+                               self.vstatus.mk.set_message(self.center('BAD DOOR'))
+                       sleep(1)
+               elif self.vstatus.cur_selection == '81':
+                       self.cookie()
+               elif self.vstatus.cur_selection == '99':
+                       self.scroll_options(self.vstatus.username, self.vstatus.mk)
+                       self.vstatus.cur_selection = ''
+                       return
+               elif self.vstatus.cur_selection[1] == '8':
+                       self.v.display('GOT DRINK?')
+                       if ((os.system('dispense -u "%s" coke:%s'%(self.vstatus.username, self.vstatus.cur_selection[0])) >> 8) != 0):
+                               self.v.display('SEEMS NOT')
+                       else:
+                               self.v.display('GOT DRINK!')
+               else:
+                       # first see if it's a named slot
+                       try:
+                               price, shortname, name = get_snack( self.vstatus.cur_selection )
+                       except:
+                               price, shortname, name = get_snack( '--' )
+                       dollarprice = "$%.2f" % ( price / 100.0 )
+                       self.v.display(self.vstatus.cur_selection+' - %s'%dollarprice)
+                       exitcode = os.system('dispense -u "%s" snack:%s'%(self.vstatus.username, self.vstatus.cur_selection)) >> 8
+                       if (exitcode == 0):
+                               # magic dispense syslog service
+                               syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "vended %s (slot %s) for %s" % (name, self.vstatus.cur_selection, self.vstatus.username))
+                               (worked, code, string) = self.v.vend(self.vstatus.cur_selection)
+                               if worked:
+                                       self.v.display('THANK YOU')
+                               else:
+                                       print "Vend Failed:", code, string
+                                       self.v.display('VEND FAIL')
+                       elif (exitcode == 5):   # RV_BALANCE
+                               self.v.display('NO MONEY?')
+                       elif (exitcode == 4):   # RV_ARGUMENTS (zero give causes arguments)
+                               self.v.display('EMPTY SLOT')
+                       elif (exitcode == 1):   # RV_BADITEM (Dead slot)
+                               self.v.display('EMPTY SLOT')
+                       else:
+                               syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %d)" % (name, self.vstatus.cur_selection, self.vstatus.username, exitcode))
+                               self.v.display('UNK ERROR')
+               sleep(1)
 
+       """
+       Find the price of an item.
+       """
+       """
+       def price_check():
+               if self.vstatus.cur_selection[1] == '8':
+                       args = ('dispense', 'iteminfo', 'coke:' + self.vstatus.cur_selection[0])
+                       info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
+                       dollarprice = re.match("\s*[a-z]+:\d+\s+(\d+\.\d\d)\s+([^\n]+)", info).group(1)
+               else:
+                       # first see if it's a named slot
+                       try:
+                               price, shortname, name = get_snack( self.vstatus.cur_selection )
+                       except:
+                               price, shortname, name = get_snack( '--' )
+                       dollarprice = "$%.2f" % ( price / 100.0 )
+               self.v.display(self.vstatus.cur_selection+' - %s'%dollarprice)
+       """
+
+       """
+       Triggered when the user presses a button while entering their pin.
+       """
+       def handle_getting_pin_key(self, event, params):
+               key = params
+               if len(self.vstatus.cur_pin) < PIN_LENGTH:
+                       if key == 11:
+                               if self.vstatus.cur_pin == '':
+                                       self.vstatus.cur_user = ''
+                                       slef.reset_idler()
+
+                                       return
+                               self.vstatus.cur_pin = ''
+                               self.vstatus.mk.set_message('PIN: ')
                                return
+                       self.vstatus.cur_pin += chr(key + ord('0'))
+                       self.vstatus.mk.set_message('PIN: '+'X'*len(self.vstatus.cur_pin))
+                       if len(self.vstatus.cur_pin) == PIN_LENGTH:
+                               self.dispense.authUserIdPin(self.vstatus.cur_user, self.vstatus.cur_pin)
+                               if self.dispense.getUsername():
+                                       self.v.beep(0, False)
+                                       self.vstatus.cur_selection = ''
+                                       self.vstatus.change_state(STATE_GET_SELECTION)
+                                       self.scroll_options(self.vstatus.username, self.vstatus.mk, True)
+                                       return
+                               else:
+                                       self.v.beep(40, False)
+                                       self.vstatus.mk.set_messages(
+                                               [(self.center('BAD PIN'), False, 1.0),
+                                                (self.center('SORRY'), False, 0.5)])
+                                       self.vstatus.cur_user = ''
+                                       self.vstatus.cur_pin = ''
+                               
+                                       self.reset_idler(2)
+
+                                       return
+
+       """
+       Triggered when the user presses a button while entering their user id.
+       """
+       def handle_getting_uid_key(self, event, params):
+               key = params
+               # complicated key handling here:
+
+               if len(self.vstatus.cur_user) == 0 and key == 9:
+                       self.vstatus.cur_selection = ''
+                       self.vstatus.time_to_autologout = None
+                       self.vstatus.mk.set_message('PRICECHECK')
+                       sleep(0.5)
+                       self.scroll_options('', self.vstatus.mk)
+                       self.vstatus.change_state(STATE_GET_SELECTION)
+                       return
 
+               if len(self.vstatus.cur_user) <8:
+                       if key == 11:
+                               self.vstatus.cur_user = ''
 
-def handle_getting_uid_key(state, event, params, v, vstatus):
-       #print "handle_getting_uid_key (s,e,p)", state, " ", event, " ", params
-       key = params
-
-       # complicated key handling here:
+                               self.reset_idler()
+                               return
+                       self.vstatus.cur_user += chr(key + ord('0'))
+                       #logging.info('dob: '+vstatus.cur_user)
+                       if len(self.vstatus.cur_user) > 5:
+                               self.vstatus.mk.set_message('>'+self.vstatus.cur_user)
+                       else:
+                               self.vstatus.mk.set_message('UID: '+self.vstatus.cur_user)
+               
+               if len(self.vstatus.cur_user) == 5:
+                       uid = int(self.vstatus.cur_user)
 
-       if len(vstatus.cur_user) == 0 and key == 9:
-               vstatus.cur_selection = ''
-               vstatus.time_to_autologout = None
-               vstatus.mk.set_message('PRICECHECK')
-               sleep(0.5)
-               scroll_options('', vstatus.mk)
-               vstatus.change_state(STATE_GET_SELECTION)
-               return
+                       if uid == 0:
+                               logging.info('user '+self.vstatus.cur_user+' has a bad PIN')
+                               pfalken="""
+       CARRIER DETECTED
 
-       if len(vstatus.cur_user) <8:
-               if key == 11:
-                       vstatus.cur_user = ''
+       CONNECT 128000
 
-                       reset_idler(v, vstatus)
-                       return
-               vstatus.cur_user += chr(key + ord('0'))
-               #logging.info('dob: '+vstatus.cur_user)
-               if len(vstatus.cur_user) > 5:
-                       vstatus.mk.set_message('>'+vstatus.cur_user)
-               else:
-                       vstatus.mk.set_message('UID: '+vstatus.cur_user)
-       
-       if len(vstatus.cur_user) == 5:
-               uid = int(vstatus.cur_user)
+       Welcome to Picklevision Sytems, Sunnyvale, CA
 
-               if uid == 0:
-                       logging.info('user '+vstatus.cur_user+' has a bad PIN')
-                       pfalken="""
-CARRIER DETECTED
+       Greetings Professor Falken.
 
-CONNECT 128000
 
-Welcome to Picklevision Sytems, Sunnyvale, CA
 
-Greetings Professor Falken.
 
+       Shall we play a game?
 
 
+       Please choose from the following menu:
 
-Shall we play a game?
+       1. Tic-Tac-Toe
+       2. Chess
+       3. Checkers
+       4. Backgammon
+       5. Poker
+       6. Toxic and Biochemical Warfare
+       7. Global Thermonuclear War
 
+       7 [ENTER]
 
-Please choose from the following menu:
+       Wouldn't you prefer a nice game of chess?
 
-1. Tic-Tac-Toe
-2. Chess
-3. Checkers
-4. Backgammon
-5. Poker
-6. Toxic and Biochemical Warfare
-7. Global Thermonuclear War
+       """.replace('\n','    ')
+                               self.vstatus.mk.set_messages([(pfalken, False, 10)])
+                               self.vstatus.cur_user = ''
+                               self.vstatus.cur_pin = ''
+                               
+                               self.reset_idler(10)
 
-7 [ENTER]
+                               return
 
-Wouldn't you prefer a nice game of chess?
+                       """
+                       if not self.has_good_pin(uid):
+                               logging.info('user '+self.vstatus.cur_user+' has a bad PIN')
+                               self.vstatus.mk.set_messages(
+                                       [(' '*10+'INVALID PIN SETUP'+' '*11, False, 3)])
+                               self.vstatus.cur_user = ''
+                               self.vstatus.cur_pin = ''
+                               
+                               self.reset_idler(3)
 
-""".replace('\n','    ')
-                       vstatus.mk.set_messages([(pfalken, False, 10)])
-                       vstatus.cur_user = ''
-                       vstatus.cur_pin = ''
-                       
-                       reset_idler(v, vstatus, 10)
+                               return
+                       """
+
+                       """
+                       if self.dispense.isDisabled():
+                               logging.info('user '+self.vstatus.cur_user+' is disabled')
+                               self.vstatus.mk.set_messages(
+                                       [(' '*11+'ACCOUNT DISABLED'+' '*11, False, 3)])
+                               self.vstatus.cur_user = ''
+                               self.vstatus.cur_pin = ''
+                               
+                               self.reset_idler(3)
+                               return
+                       """
 
+                       self.vstatus.cur_pin = ''
+                       self.vstatus.mk.set_message('PIN: ')
+                       logging.info('need pin for user %s'%self.vstatus.cur_user)
+                       self.vstatus.change_state(STATE_GETTING_PIN)
                        return
 
-               if not has_good_pin(uid):
-                       logging.info('user '+vstatus.cur_user+' has a bad PIN')
-                       vstatus.mk.set_messages(
-                               [(' '*10+'INVALID PIN SETUP'+' '*11, False, 3)])
-                       vstatus.cur_user = ''
-                       vstatus.cur_pin = ''
-                       
-                       reset_idler(v, vstatus, 3)
-
+       """
+       Triggered when a key is pressed and the machine is idling.
+       """
+       def handle_idle_key(self, event, params):
+               key = params
+               if key == 11:
+                       self.vstatus.cur_user = ''
+                       self.reset_idler()
                        return
                
-               if acct_is_disabled():
-                       logging.info('user '+vstatus.cur_user+' is disabled')
-                       vstatus.mk.set_messages(
-                               [(' '*11+'ACCOUNT DISABLED'+' '*11, False, 3)])
-                       vstatus.cur_user = ''
-                       vstatus.cur_pin = ''
-                       
-                       reset_idler(v, vstatus, 3)
-                       return
-
-
-               vstatus.cur_pin = ''
-               vstatus.mk.set_message('PIN: ')
-               logging.info('need pin for user %s'%vstatus.cur_user)
-               vstatus.change_state(STATE_GETTING_PIN)
-               return
-
-
-def handle_idle_key(state, event, params, v, vstatus):
-       #print "handle_idle_key (s,e,p)", state, " ", event, " ", params
-
-       key = params
-
-       if key == 11:
-               vstatus.cur_user = ''
-               reset_idler(v, vstatus)
-               return
-       
-       vstatus.change_state(STATE_GETTING_UID)
-       run_handler(event, key, v, vstatus)
-
-
-def handle_idle_tick(state, event, params, v, vstatus):
-       ### State idling
-       if vstatus.mk.done():
-               idle_step(vstatus)
-
-       if vstatus.time_of_next_idler and time() > vstatus.time_of_next_idler:
-               vstatus.time_of_next_idler = time() + 30
-               choose_idler()
-       
-       ###
-
-       vstatus.mk.update_display()
-
-       vstatus.change_state(STATE_GRANDFATHER_CLOCK)
-       run_handler(event, params, v, vstatus)
-       sleep(0.05)
-
-def beep_on(when, before=0):
-       start = int(when - before)
-       end = int(when)
-       now = int(time())
-
-       if now >= start and now <= end:
-               return 1
-       return 0
-
-def handle_idle_grandfather_tick(state, event, params, v, vstatus):
-       ### check for interesting times
-       now = localtime()
-
-       quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
-       halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
-       threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
-       fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
-
-       hourfromnow = localtime(time() + 3600)
-       
-       #onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
-       onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
-               0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
-
-       ## check for X seconds to the hour
-       ## if case, update counter to 2
-       if beep_on(onthehour,15) \
-               or beep_on(halfhour,0) \
-               or beep_on(quarterhour,0) \
-               or beep_on(threequarterhour,0) \
-               or beep_on(fivetothehour,0):
-               vstatus.change_state(STATE_GRANDFATHER_CLOCK,2)
-               run_handler(event, params, v, vstatus)
-       else:
-               vstatus.change_state(STATE_IDLE)
+               self.vstatus.change_state(STATE_GETTING_UID)
+               self.run_handler(event, params)
+
+       """
+       What to do when there is nothing to do.
+       """
+       def handle_idle_tick(self, event, params):
+               ### State idling
+               if self.vstatus.mk.done():
+                       self.idle_step()
+
+               if self.vstatus.time_of_next_idler and time() > self.vstatus.time_of_next_idler:
+                       self.vstatus.time_of_next_idler = time() + 30
+                       self.choose_idler()
+               
+               ###
 
-def handle_grandfather_tick(state, event, params, v, vstatus):
-       go_idle = 1
+               self.vstatus.mk.update_display()
 
-       msg = []
-       ### we live in interesting times
-       now = localtime()
+               self.vstatus.change_state(STATE_GRANDFATHER_CLOCK)
+               self.run_handler(event, params)
+               sleep(0.05)
 
-       quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
-       halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
-       threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
-       fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
+       """
+       Manages the beeps for the grandfather clock
+       """
+       def beep_on(self, when, before=0):
+               start = int(when - before)
+               end = int(when)
+               now = int(time())
 
-       hourfromnow = localtime(time() + 3600)
-       
-#      onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
-       onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
-               0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
+               if now >= start and now <= end:
+                       return 1
+               return 0
 
+       def handle_idle_grandfather_tick(self, event, params):
+               ### check for interesting times
+               now = localtime()
 
-       #print "when it fashionable to wear a onion on your hip"
+               quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
+               halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
+               threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
+               fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
 
-       if beep_on(onthehour,15):
-               go_idle = 0
-               next_hour=((hourfromnow[3] + 11) % 12) + 1
-               if onthehour - time() < next_hour and onthehour - time() > 0:
-                       v.beep(0, False)
+               hourfromnow = localtime(time() + 3600)
+               
+               #onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
+               onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
+                       0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
+
+               ## check for X seconds to the hour
+               ## if case, update counter to 2
+               if self.beep_on(onthehour,15) \
+                       or self.beep_on(halfhour,0) \
+                       or self.beep_on(quarterhour,0) \
+                       or self.beep_on(threequarterhour,0) \
+                       or self.beep_on(fivetothehour,0):
+                       self.vstatus.change_state(STATE_GRANDFATHER_CLOCK,2)
+                       self.run_handler(event, params)
+               else:
+                       self.vstatus.change_state(STATE_IDLE)
 
-                       t = int(time())
-                       if (t % 2) == 0:
-                               msg.append(("DING!", False, None))
-                       else:
-                               msg.append(("     DING!", False, None))
-               elif int(onthehour - time()) == 0:
-                       v.beep(255, False)
-                       msg.append(("   BONG!", False, None))
-                       msg.append(("     IT'S "+ str(next_hour) + "O'CLOCK AND ALL IS WELL .....", False, TEXT_SPEED*4))
-       elif beep_on(halfhour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append((" HALFHOUR ", False, 50))
-       elif beep_on(quarterhour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append((" QTR HOUR ", False, 50))
-       elif beep_on(threequarterhour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append((" 3 QTR HR ", False, 50))
-       elif beep_on(fivetothehour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append(("Quick run to your lectures!  Hurry! Hurry!", False, TEXT_SPEED*4))
-       else:
+       def handle_grandfather_tick(self, event, params):
                go_idle = 1
-       
-       ## check for X seconds to the hour
 
-       if len(msg):
-               vstatus.mk.set_messages(msg)
-               sleep(1)
+               msg = []
+               ### we live in interesting times
+               now = localtime()
 
-       vstatus.mk.update_display()
-       ## if no longer case, return to idle
-
-       ## change idler to be clock
-       if go_idle and vstatus.mk.done():
-               vstatus.change_state(STATE_IDLE,1)
-
-def handle_door_idle(state, event, params, v, vstatus):
-       def twiddle(clock,v,wise = 2):
-               if (clock % 4 == 0):
-                       v.display("-FEED  ME-")
-               elif (clock % 4 == 1+wise):
-                       v.display("\\FEED  ME/")
-               elif (clock % 4 == 2):
-                       v.display("-FEED  ME-")
-               elif (clock % 4 == 3-wise):
-                       v.display("/FEED  ME\\")
-
-       # don't care right now.
-       now = int(time())
-
-       if ((now % 60 % 2) == 0):
-               twiddle(now, v)
-       else:
-               twiddle(now, v, wise=0)
-
-
-def handle_door_event(state, event, params, v, vstatus):
-       if params == 0:  #door open
-               vstatus.change_state(STATE_DOOR_OPENING)
-               logging.warning("Entering open door mode")
-               v.display("-FEED  ME-")
-               #door_open_mode(v);
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-       elif params == 1:  #door closed
-               vstatus.change_state(STATE_DOOR_CLOSING)
-               reset_idler(v, vstatus, 3)
-
-               logging.warning('Leaving open door mode')
-               v.display("-YUM YUM!-")
-
-_last_card_id = -1
-
-def handle_mifare_event(state, event, params, v, vstatus):
-       global _last_card_id
-       card_id = params
-       # Translate card_id into uid.
-       if card_id == None or card_id == _last_card_id:
-               return
-
-       _last_card_id = card_id
-       
-       try:
-               vstatus.cur_user = get_uid(card_id)
-               logging.info('Mapped card id to uid %s'%vstatus.cur_user)
-               vstatus.username = get_uname(vstatus.cur_user)
-               if acct_is_disabled(vstatus.username):
-                       vstatus.username = '-disabled-'
-       except ValueError:
-               vstatus.username = None
-       if vstatus.username == '-disabled-':
-               v.beep(40, False)
-               vstatus.mk.set_messages(
-                       [(center('ACCT DISABLED'), False, 1.0),
-                        (center('SORRY'), False, 0.5)])
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-               vstatus.username = None
-       
-               reset_idler(v, vstatus, 2)
-               return
-       elif vstatus.username:
-               v.beep(0, False)
-               vstatus.cur_selection = ''
-               vstatus.change_state(STATE_GET_SELECTION)
-               scroll_options(vstatus.username, vstatus.mk, True)
-               return
-       else:
-               v.beep(40, False)
-               vstatus.mk.set_messages(
-                       [(center('BAD CARD'), False, 1.0),
-                        (center('SORRY'), False, 0.5)])
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-               _last_card_id = -1
-       
-               reset_idler(v, vstatus, 2)
-               return
+               quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
+               halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
+               threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
+               fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
 
-def handle_mifare_add_user_event(state, event, params, v, vstatus):
-       global _last_card_id
-       card_id = params
+               hourfromnow = localtime(time() + 3600)
+               
+       #       onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
+               onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
+                       0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
 
-       # Translate card_id into uid.
-       if card_id == None or card_id == _last_card_id:
-               return
 
-       _last_card_id = card_id
-       
-       try:
-               if get_uid(card_id) != None:
-                       vstatus.mk.set_messages(
-                               [(center('ALREADY'), False, 0.5),
-                                (center('ENROLLED'), False, 0.5)])
+               #print "when it fashionable to wear a onion on your hip"
 
-                       # scroll_options(vstatus.username, vstatus.mk)
-                       return
-       except ValueError:
-               pass
+               if self.beep_on(onthehour,15):
+                       go_idle = 0
+                       next_hour=((hourfromnow[3] + 11) % 12) + 1
+                       if onthehour - time() < next_hour and onthehour - time() > 0:
+                               self.v.beep(0, False)
 
-       logging.info('Enrolling card %s to uid %s (%s)'%(card_id, vstatus.cur_user, vstatus.username))
-       set_card_id(vstatus.cur_user, card_id)
-       vstatus.mk.set_messages(
-               [(center('CARD'), False, 0.5),
-                (center('ENROLLED'), False, 0.5)])
-
-       # scroll_options(vstatus.username, vstatus.mk)
-
-def return_to_idle(state,event,params,v,vstatus):
-       reset_idler(v, vstatus)
-
-def create_state_table(vstatus):
-       vstatus.state_table[(STATE_IDLE,TICK,1)] = handle_idle_tick
-       vstatus.state_table[(STATE_IDLE,KEY,1)] = handle_idle_key
-       vstatus.state_table[(STATE_IDLE,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_IDLE,MIFARE,1)] = handle_mifare_event
-
-       vstatus.state_table[(STATE_DOOR_OPENING,TICK,1)] = handle_door_idle
-       vstatus.state_table[(STATE_DOOR_OPENING,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_DOOR_OPENING,KEY,1)] = do_nothing
-       vstatus.state_table[(STATE_DOOR_OPENING,MIFARE,1)] = do_nothing
-
-       vstatus.state_table[(STATE_DOOR_CLOSING,TICK,1)] = return_to_idle
-       vstatus.state_table[(STATE_DOOR_CLOSING,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_DOOR_CLOSING,KEY,1)] = do_nothing
-       vstatus.state_table[(STATE_DOOR_CLOSING,MIFARE,1)] = do_nothing
-
-       vstatus.state_table[(STATE_GETTING_UID,TICK,1)] = handle_getting_uid_idle
-       vstatus.state_table[(STATE_GETTING_UID,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GETTING_UID,KEY,1)] = handle_getting_uid_key
-       vstatus.state_table[(STATE_GETTING_UID,MIFARE,1)] = handle_mifare_event
-
-       vstatus.state_table[(STATE_GETTING_PIN,TICK,1)] = handle_getting_pin_idle
-       vstatus.state_table[(STATE_GETTING_PIN,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GETTING_PIN,KEY,1)] = handle_getting_pin_key
-       vstatus.state_table[(STATE_GETTING_PIN,MIFARE,1)] = handle_mifare_event
-
-       vstatus.state_table[(STATE_GET_SELECTION,TICK,1)] = handle_get_selection_idle
-       vstatus.state_table[(STATE_GET_SELECTION,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GET_SELECTION,KEY,1)] = handle_get_selection_key
-       vstatus.state_table[(STATE_GET_SELECTION,MIFARE,1)] = handle_mifare_add_user_event
-
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,1)] = handle_idle_grandfather_tick
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,2)] = handle_grandfather_tick
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,2)] = handle_door_event
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,1)] = do_nothing
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,2)] = do_nothing
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,MIFARE,1)] = handle_mifare_event
-
-def get_state_table_handler(vstatus, state, event, counter):
-       return vstatus.state_table[(state,event,counter)]
-
-def time_to_next_update(vstatus):
-       idle_update = vstatus.time_of_next_idlestep - time()
-       if not vstatus.mk.done() and vstatus.mk.next_update is not None:
-               mk_update = vstatus.mk.next_update - time()
-               if mk_update < idle_update:
-                       idle_update = mk_update
-       return idle_update
-
-def run_forever(rfh, wfh, options, cf):
-       v = VendingMachine(rfh, wfh, USE_MIFARE)
-       vstatus = VendState(v)
-       create_state_table(vstatus)
-
-       logging.debug('PING is ' + str(v.ping()))
-
-       if USE_DB: db = DispenseDatabase(v, cf.DBServer, cf.DBName, cf.DBUser, cf.DBPassword)
-
-       setup_idlers(v)
-       reset_idler(v, vstatus)
-
-       # This main loop was hideous and the work of the devil.
-       # This has now been fixed (mostly) - mtearle
-       #
-       #
-       # notes for later surgery
-       #   (event, counter, ' ')
-       #        V
-       #   d[      ] = (method)
-       #
-       # ( return state - not currently implemented )
+                               t = int(time())
+                               if (t % 2) == 0:
+                                       msg.append(("DING!", False, None))
+                               else:
+                                       msg.append(("     DING!", False, None))
+                       elif int(onthehour - time()) == 0:
+                               self.v.beep(255, False)
+                               msg.append(("   BONG!", False, None))
+                               msg.append(("     IT'S "+ str(next_hour) + "O'CLOCK AND ALL IS WELL .....", False, TEXT_SPEED*4))
+               elif self.beep_on(halfhour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append((" HALFHOUR ", False, 50))
+               elif self.beep_on(quarterhour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append((" QTR HOUR ", False, 50))
+               elif self.beep_on(threequarterhour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append((" 3 QTR HR ", False, 50))
+               elif self.beep_on(fivetothehour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append(("Quick run to your lectures!  Hurry! Hurry!", False, TEXT_SPEED*4))
+               else:
+                       go_idle = 1
+               
+               ## check for X seconds to the hour
+
+               if len(msg):
+                       self.vstatus.mk.set_messages(msg)
+                       sleep(1)
+
+               self.vstatus.mk.update_display()
+               ## if no longer case, return to idle
+
+               ## change idler to be clock
+               if go_idle and self.vstatus.mk.done():
+                       self.vstatus.change_state(STATE_IDLE,1)
+
+       """
+       What to do when the door is open.
+       """
+       def handle_door_idle(self, event, params):
+               def twiddle(clock,v,wise = 2):
+                       if (clock % 4 == 0):
+                               v.display("-FEED  ME-")
+                       elif (clock % 4 == 1+wise):
+                               v.display("\\FEED  ME/")
+                       elif (clock % 4 == 2):
+                               v.display("-FEED  ME-")
+                       elif (clock % 4 == 3-wise):
+                               v.display("/FEED  ME\\")
+
+               # don't care right now.
+               now = int(time())
+
+               if ((now % 60 % 2) == 0):
+                       twiddle(now, self.v)
+               else:
+                       twiddle(now, self.v, wise=0)
+
+       """
+       What to do when the door is opened or closed.
+       """
+       def handle_door_event(self, event, params):
+               if params == 0:  #door open
+                       self.vstatus.change_state(STATE_DOOR_OPENING)
+                       logging.warning("Entering open door mode")
+                       self.v.display("-FEED  ME-")
+                       #door_open_mode(v);
+                       self.vstatus.cur_user = ''
+                       self.vstatus.cur_pin = ''
+               elif params == 1:  #door closed
+                       self.vstatus.change_state(STATE_DOOR_CLOSING)
+                       self.reset_idler(3)
+
+                       logging.warning('Leaving open door mode')
+                       self.v.display("-YUM YUM!-")
+
+       """
+       Triggered when a user swipes their caed, and the machine is logged out.
+       """
+       def handle_mifare_event(self, event, params):
+               card_id = params
+               # Translate card_id into uid.
+               if card_id == None or card_id == self._last_card_id:
+                       return
 
-       while True:
-               if USE_DB:
-                       try:
-                               db.handle_events()
-                       except DispenseDatabaseException, e:
-                               logging.error('Database error: '+str(e))
+               self._last_card_id = card_id
+               
+               self.dispense.authMifareCard(card_id)
+               logging.info('Mapped card id to uid %s'%self.dispense.getUsername())
+               if not self.dispense.isLoggedIn():
+                       self.v.beep(40, False)
+                       self.vstatus.mk.set_messages(
+                               [(self.center('BAD CARD'), False, 1.0),
+                                (self.center('SORRY'), False, 0.5)])
+                       self.vstatus.cur_user = ''
+                       self.vstatus.cur_pin = ''
+                       self._last_card_id = -1
+               
+                       self.reset_idler(2)
+                       return
+               elif self.dispense.isDisabled():
+                       self.v.beep(40, False)
+                       self.vstatus.mk.set_messages(
+                               [(self.center('ACCT DISABLED'), False, 1.0),
+                                (self.center('SORRY'), False, 0.5)])
+                       self.dispense.logOut()
+                       self.reset_idler(2)
+                       return
+               else:
+                       self.vstatus.cur_selection = ''
+                       self.vstatus.change_state(STATE_GET_SELECTION)
+                       self.scroll_options(self.vstatus.username, self.vstatus.mk, True)
+                       return
 
-               timeout = time_to_next_update(vstatus)
-               e = v.next_event(timeout)
-               (event, params) = e
+       """
+       Triggered when a user swipes their card and the machine is logged in.
+       """
+       def handle_mifare_add_user_event(self, event, params):
+               card_id = params
 
-               run_handler(event, params, v, vstatus)
+               # Translate card_id into uid.
+               if card_id == None or card_id == self._last_card_id:
+                       return
 
-#              logging.debug('Got event: ' + repr(e))
+               self._last_card_id = card_id
 
+               res = self.dispense.addCard(card_id)
 
-def run_handler(event, params, v, vstatus):
-       handler = get_state_table_handler(vstatus,vstatus.state,event,vstatus.counter)
-       if handler:
-               handler(vstatus.state, event, params, v, vstatus)
+               if get_uid(card_id) != None:
+                       self.vstatus.mk.set_messages(
+                               [(self.center('ALREADY'), False, 0.5),
+                                (self.center('ENROLLED'), False, 0.5)])
+               else:
+                       logging.info('Enrolling card %s to uid %s (%s)'%(card_id, self.vstatus.cur_user, self.vstatus.username))
+                       self.set_card_id(self.vstatus.cur_user, self.card_id)
+                       self.vstatus.mk.set_messages(
+                               [(self.center('CARD'), False, 0.5),
+                                (self.center('ENROLLED'), False, 0.5)])
+
+       def return_to_idle(self, event, params):
+               self.reset_idler()
+
+       """
+       Maps what to do when the state changes.
+       """
+       def create_state_table(self):
+               self.vstatus.state_table[(STATE_IDLE,TICK,1)] = self.handle_idle_tick
+               self.vstatus.state_table[(STATE_IDLE,KEY,1)] = self.handle_idle_key
+               self.vstatus.state_table[(STATE_IDLE,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_IDLE,MIFARE,1)] = self.handle_mifare_event
+
+               self.vstatus.state_table[(STATE_DOOR_OPENING,TICK,1)] = self.handle_door_idle
+               self.vstatus.state_table[(STATE_DOOR_OPENING,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_DOOR_OPENING,KEY,1)] = self.do_nothing
+               self.vstatus.state_table[(STATE_DOOR_OPENING,MIFARE,1)] = self.do_nothing
+
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,TICK,1)] = self.return_to_idle
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,KEY,1)] = self.do_nothing
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,MIFARE,1)] = self.do_nothing
+
+               self.vstatus.state_table[(STATE_GETTING_UID,TICK,1)] = self.handle_getting_uid_idle
+               self.vstatus.state_table[(STATE_GETTING_UID,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GETTING_UID,KEY,1)] = self.handle_getting_uid_key
+               self.vstatus.state_table[(STATE_GETTING_UID,MIFARE,1)] = self.handle_mifare_event
+
+               self.vstatus.state_table[(STATE_GETTING_PIN,TICK,1)] = self.handle_getting_pin_idle
+               self.vstatus.state_table[(STATE_GETTING_PIN,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GETTING_PIN,KEY,1)] = self.handle_getting_pin_key
+               self.vstatus.state_table[(STATE_GETTING_PIN,MIFARE,1)] = self.handle_mifare_event
+
+               self.vstatus.state_table[(STATE_GET_SELECTION,TICK,1)] = self.handle_get_selection_idle
+               self.vstatus.state_table[(STATE_GET_SELECTION,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GET_SELECTION,KEY,1)] = self.handle_get_selection_key
+               self.vstatus.state_table[(STATE_GET_SELECTION,MIFARE,1)] = self.handle_mifare_add_user_event
+
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,1)] = self.handle_idle_grandfather_tick
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,2)] = self.handle_grandfather_tick
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,2)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,1)] = self.do_nothing
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,2)] = self.do_nothing
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,MIFARE,1)] = self.handle_mifare_event
+
+       """
+       Get what to do on a state change.
+       """
+       def get_state_table_handler(self, state, event, counter):
+               return self.vstatus.state_table[(state,event,counter)]
+
+       def time_to_next_update(self):
+               idle_update = self.vstatus.time_of_next_idlestep - time()
+               if not self.vstatus.mk.done() and self.vstatus.mk.next_update is not None:
+                       mk_update = self.vstatus.mk.next_update - time()
+                       if mk_update < idle_update:
+                               idle_update = mk_update
+               return idle_update
+
+       def run_forever(self, rfh, wfh, options, cf):
+               self.v = VendingMachine(rfh, wfh, USE_MIFARE)
+               self.dispense = Dispense()
+               self.vstatus = VendState(self.v)
+               self.create_state_table()
+
+               logging.debug('PING is ' + str(self.v.ping()))
+
+               if USE_DB: db = DispenseDatabase(v, cf.DBServer, cf.DBName, cf.DBUser, cf.DBPassword)
+
+               self.setup_idlers()
+               self.reset_idler()
+
+               while True:
+                       if USE_DB:
+                               try:
+                                       db.handle_events()
+                               except DispenseDatabaseException, e:
+                                       logging.error('Database error: '+str(e))
+
+                       timeout = self.time_to_next_update()
+                       (event, params) = self.v.next_event(timeout)
+                       self.run_handler(event, params)
+
+       def run_handler(self, event, params):
+               handler = self.get_state_table_handler(self.vstatus.state,event,self.vstatus.counter)
+               if handler:
+                       handler(event, params)
 
+"""
+Connect to the machine.
+"""
 def connect_to_vend(options, cf):
 
        if options.use_lat:
@@ -1042,12 +1096,15 @@ def connect_to_vend(options, cf):
                
        return rfh, wfh
 
+"""
+Parse arguments from the command line
+"""
 def parse_args():
        from optparse import OptionParser
 
        op = OptionParser(usage="%prog [OPTION]...")
        op.add_option('-f', '--config-file', default='/etc/dispense2/servers.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of /etc/dispense/servers.conf')
-       op.add_option('--serial', action='store_true', default=True, dest='use_serial', help='use the serial port')
+       op.add_option('--serial', action='store_true', default=False, dest='use_serial', help='use the serial port')
        op.add_option('--lat', action='store_true', default=False, dest='use_lat', help='use LAT')
        op.add_option('--virtualvend', action='store_false', default=True, dest='use_serial', help='use the virtual vending server instead of LAT')
        op.add_option('-n', '--hostname', dest='host', default='localhost', help='the hostname to connect to for virtual vending machine mode (default: localhost)')
@@ -1065,34 +1122,6 @@ def parse_args():
 
        return options
 
-config_options = {
-       'DBServer': ('Database', 'Server'),
-       'DBName': ('Database', 'Name'),
-       'DBUser': ('VendingMachine', 'DBUser'),
-       'DBPassword': ('VendingMachine', 'DBPassword'),
-       
-       'ServiceName': ('VendingMachine', 'ServiceName'),
-       'ServicePassword': ('VendingMachine', 'Password'),
-       
-       'ServerName': ('DecServer', 'Name'),
-       'ConnectPassword': ('DecServer', 'ConnectPassword'),
-       'PrivPassword': ('DecServer', 'PrivPassword'),
-       }
-
-class VendConfigFile:
-       def __init__(self, config_file, options):
-               try:
-                       cp = ConfigParser.ConfigParser()
-                       cp.read(config_file)
-
-                       for option in options:
-                               section, name = options[option]
-                               value = cp.get(section, name)
-                               self.__dict__[option] = value
-               
-               except ConfigParser.Error, e:
-                       raise SystemExit("Error reading config file "+config_file+": " + str(e))
-
 def create_pid_file(name):
        try:
                pid_file = file(name, 'w')
@@ -1180,7 +1209,8 @@ def do_vend_server(options, config_opts):
 #              run_forever(rfh, wfh, options, config_opts)
                
                try:
-                       run_forever(rfh, wfh, options, config_opts)
+                       vserver = VendServer()
+                       vserver.run_forever(rfh, wfh, options, config_opts)
                except VendingException:
                        logging.error("Connection died, trying again...")
                        logging.info("Trying again in 5 seconds.")

UCC git Repository :: git.ucc.asn.au