Fix another place where logout isn't done
[uccvend-vendserver.git] / VendServer / VendServer.py
index dd35cd4..e2192b1 100755 (executable)
@@ -1,14 +1,12 @@
 #!/usr/bin/python
-# vim:ts=4
+# vim: ts=4 sts=4 sw=4 noexpandtab
 
-USE_DB = 0
 USE_MIFARE = 1
 
 import ConfigParser
 import sys, os, string, re, pwd, signal, math, syslog
 import logging, logging.handlers
 from traceback import format_tb
-if USE_DB: import pg
 from time import time, sleep, mktime, localtime
 from subprocess import Popen, PIPE
 from LATClient import LATClient, LATClientException
@@ -21,7 +19,6 @@ from Idler import GreetingIdler,TrainIdler,GrayIdler,StringIdler,ClockIdler,Fort
 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="""
@@ -95,36 +92,6 @@ class VendConfigFile:
                except ConfigParser.Error, e:
                        raise SystemExit("Error reading config file "+config_file+": " + str(e))
 
-class DispenseDatabaseException(Exception): pass
-
-class DispenseDatabase:
-       def __init__(self, vending_machine, host, name, user, password):
-               self.vending_machine = vending_machine
-               self.db = pg.DB(dbname = name, host = host, user = user, passwd = password)
-               self.db.query('LISTEN vend_requests')
-
-       def process_requests(self):
-               logging.debug('database processing')
-               query = 'SELECT request_id, request_slot FROM vend_requests WHERE request_handled = false'
-               try:
-                       outstanding = self.db.query(query).getresult()
-               except (pg.error,), db_err:
-                       raise DispenseDatabaseException('Failed to query database: %s\n'%(db_err.strip()))
-               for (id, slot) in outstanding:
-                       (worked, code, string) = self.vending_machine.vend(slot)
-                       logging.debug (str((worked, code, string)))
-                       if worked:
-                               query = 'SELECT vend_success(%s)'%id
-                               self.db.query(query).getresult()
-                       else:
-                               query = 'SELECT vend_failed(%s)'%id
-                               self.db.query(query).getresult()
-
-       def handle_events(self):
-               notifier = self.db.getnotify()
-               while notifier is not None:
-                       self.process_requests()
-                       notify = self.db.getnotify()
 """
 This class manages the current state of the vending machine.
 """
@@ -173,6 +140,7 @@ class VendServer():
                # If the user has just logged in, show them their balance
                if welcome:
                        balance = self.dispense.getBalance()
+                       balance = balance[:-4] + '.' + balance[-4] + balance[-2:]   # Work around display bug
                        
                        msg = [(self.center('WELCOME'), False, TEXT_SPEED),
                                   (self.center(self.dispense.getUsername()), False, TEXT_SPEED),
@@ -201,96 +169,6 @@ class VendServer():
                # 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:
-                       pinstr = self._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)
-       """
-
-       """
-       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:
-                               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
-       """
-
        """
        In here just for fun.
        """
@@ -458,12 +336,14 @@ class VendServer():
                                self.vstatus.last_timeout_refresh = int(time_left)
                                self.vstatus.cur_selection = ''
 
+               # Login timed out: Log out the current user.
                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.dispense.logOut()
                        self.reset_idler()
 
                ### State fully logged out ... reset variables
@@ -492,7 +372,8 @@ class VendServer():
                                self.vstatus.cur_pin = ''
                                self.vstatus.cur_user = ''
                                self.vstatus.cur_selection = ''
-                               _last_card_id = -1
+                               self._last_card_id = -1
+                               self.dispense.logOut()
                                self.vstatus.mk.set_messages([(self.center('BYE!'), False, 1.5)])
                                self.reset_idler(2)
                                return
@@ -503,18 +384,23 @@ class VendServer():
                        if key == 11:
                                self.vstatus.cur_selection = ''
                                self.vstatus.time_to_autologout = None
+                               self.dispense.logOut()
                                self.scroll_options(self.vstatus.username, self.vstatus.mk)
                                return
                        else:
                                self.vstatus.cur_selection += chr(key + ord('0'))
-                               if self.vstatus.cur_user:
+                               if self.dispense.isLoggedIn():
                                        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)
+                                       (name,price) = self.dispense.getItemInfo(self.vstatus.cur_selection)
+                                       dollarprice = "$%.2f" % ( price / 100.0 )
+                                       dollarprice = dollarprice[:-4] + '.' + dollarprice[-4] + dollarprice[-2:]   # Work around display bug
+                                       self.v.display( self.vstatus.cur_selection+' - %s'%dollarprice)
+
                                        self.vstatus.cur_selection = ''
                                        self.vstatus.time_to_autologout = None
                                        self.vstatus.last_timeout_refresh = None
@@ -523,6 +409,7 @@ class VendServer():
        Triggered when the user has entered the id of something they would like to purchase.
        """
        def make_selection(self):
+               logging.debug('Dispense item "%s"' % (self.vstatus.cur_selection,))
                # should use sudo here
                if self.vstatus.cur_selection == '55':
                        self.vstatus.mk.set_message('OPENSESAME')
@@ -579,25 +466,6 @@ class VendServer():
                                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.
        """
@@ -607,6 +475,7 @@ class VendServer():
                        if key == 11:
                                if self.vstatus.cur_pin == '':
                                        self.vstatus.cur_user = ''
+                                       self.dispense.logOut()
                                        slef.reset_idler()
 
                                        return
@@ -616,8 +485,8 @@ class VendServer():
                        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():
+                               if self.dispense.authUserIdPin(self.vstatus.cur_user, self.vstatus.cur_pin):
+                                       self.vstatus.username = self.dispense.getUsername()
                                        self.v.beep(0, False)
                                        self.vstatus.cur_selection = ''
                                        self.vstatus.change_state(STATE_GET_SELECTION)
@@ -654,6 +523,7 @@ class VendServer():
                if len(self.vstatus.cur_user) <8:
                        if key == 11:
                                self.vstatus.cur_user = ''
+                               self.dispense.logOut()
 
                                self.reset_idler()
                                return
@@ -707,19 +577,7 @@ class VendServer():
 
                                return
 
-                       """
-                       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)
-
-                               return
-                       """
-
+                       # TODO Fix this up do we can check before logging in
                        """
                        if self.dispense.isDisabled():
                                logging.info('user '+self.vstatus.cur_user+' is disabled')
@@ -745,6 +603,7 @@ class VendServer():
                key = params
                if key == 11:
                        self.vstatus.cur_user = ''
+                       self.dispense.logOut()
                        self.reset_idler()
                        return
                
@@ -909,6 +768,7 @@ class VendServer():
                        logging.warning("Entering open door mode")
                        self.v.display("-FEED  ME-")
                        #door_open_mode(v);
+                       self.dispense.logOut()
                        self.vstatus.cur_user = ''
                        self.vstatus.cur_pin = ''
                elif params == 1:  #door closed
@@ -929,9 +789,7 @@ class VendServer():
 
                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():
+               if not self.dispense.authMifareCard(card_id):
                        self.v.beep(40, False)
                        self.vstatus.mk.set_messages(
                                [(self.center('BAD CARD'), False, 1.0),
@@ -943,6 +801,7 @@ class VendServer():
                        self.reset_idler(2)
                        return
                elif self.dispense.isDisabled():
+                       logging.info('Mapped card id to uid %s'%self.dispense.getUsername())
                        self.v.beep(40, False)
                        self.vstatus.mk.set_messages(
                                [(self.center('ACCT DISABLED'), False, 1.0),
@@ -951,6 +810,9 @@ class VendServer():
                        self.reset_idler(2)
                        return
                else:
+                       logging.info('Mapped card id to uid %s'%self.dispense.getUsername())
+                       self.vstatus.cur_user = '----'
+                       self.vstatus.username = self.dispense.getUsername()
                        self.vstatus.cur_selection = ''
                        self.vstatus.change_state(STATE_GET_SELECTION)
                        self.scroll_options(self.vstatus.username, self.vstatus.mk, True)
@@ -968,15 +830,11 @@ class VendServer():
 
                self._last_card_id = card_id
 
-               res = self.dispense.addCard(card_id)
-
-               if get_uid(card_id) != None:
+               if not self.dispense.addCard(card_id):
                        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)])
@@ -1048,18 +906,10 @@ class VendServer():
 
                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)

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