OpenDispense - Fix to actually work
[uccvend-vendserver.git] / VendServer / OpenDispense.py
index f8cee0d..c8dcf83 100644 (file)
@@ -1,10 +1,3 @@
-from DispenseInterface import DispenseInterface
-import os
-import re
-import pwd
-from subprocess import Popen, PIPE
-from LDAPConnector import get_uid,get_uname, set_card_id
-
 """
 Author: Mitchell Pomery (bobgeorge33)
 
@@ -13,97 +6,187 @@ Most of this code has been copied out of VendServer.py, then had variables updat
 This is so VendServer can easily operate regardless of the current accounting backend.
 Documentation for this code can be found inder Dispence.DispenceInterface
 """
+
+from DispenseInterface import DispenseInterface
+import os
+import logging
+import re
+import pwd
+import base64
+import socket
+from subprocess import Popen, PIPE
+from LDAPConnector import get_uid,get_uname, set_card_id
+
+DISPENSE_ENDPOINT = ("localhost", 11020)
+DISPSRV_MIFARE = True
+
 class OpenDispense(DispenseInterface):
-        _username = None
-        _disabled = True
-        _loggedIn = False
+       _username = ""
+       _disabled = True
+       _loggedIn = False
        _userId = None
 
-        def __init__(self, userId=None, username=None, loggedIn=False):
-               self._username = username
-                self._loggedIn = loggedIn
-               self._userId = userId
-               
-               acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
-               # this is fucking appalling
-               flags = acct[acct.find("(")+1:acct.find(")")].strip()
-               if 'disabled' in flags:
-                       self._disabled = True
-               if 'internal' in flags:
-                       self._disabled = True
-                self._disabled = False
+       def __init__(self, username=None, secret=False):
+               pass
+
+       def authUserIdPin(self, userId, pin):
+               userId = int(userId)
 
-       @staticmethod
-       def authUserIdPin(userId, pin):
                try:
                        # Get info from 
-                        info = pwd.getpwuid(userId)
-                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().strip()
-                f.close()
-                if not re.search('^[0-9]{4}$', pinstr):
-                        logging.info('getting pin for uid %d: %s not a good pin'%(uid,repr(pinstr)))
-                        return None
-               return OpenDispense(userId, info.pw_name, (int(pin)==int(pinstr)))
+                       info = pwd.getpwuid(userId)
+               except KeyError:
+                       logging.info('getting pin for uid %d: user not in password file'%userId)
+                       return False
 
-       @staticmethod
-       def authMifareCard(cardId):
-               return OpenDispense(get_uid(cardId), get_uname(get_uid(cardId)), True)
+               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'%userId)
+                       return False
+               if s.st_mode & 077:
+                       logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%userId)
+                       os.chmod(pinfile, 0600)
+               try:
+                       f = file(pinfile)
+               except IOError:
+                       logging.info('getting pin for uid %d: I cannot read pin file'%userId)
+                       return False
+               pinstr = f.readline().strip()
+               f.close()
+               if not re.search('^[0-9]{4}$', pinstr):
+                       logging.info('getting pin for uid %d: %s not a good pin'%(userId,repr(pinstr)))
+                       return False
+
+               if pinstr == str(pin):
+                       #Login Successful
+                       self._userid = userId
+                       self._loggedIn = True
+                       self._disabled = False
+                       self._username = info.pw_name
+                       return True
+               
+               # Login Unsuccessful
+               return False
+
+       def authMifareCard(self, cardId):
+               if DISPSRV_MIFARE:
+                       card_base64 = base64.b64encode(cardId)
+                       
+                       sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+                       sock.connect(DISPENSE_ENDPOINT)
+                       logging.debug('connected to dispsrv')
+                       sockf = sock.makefile()
+                       sockf.write("AUTHIDENT\n"); sockf.flush()
+                       rsp = sockf.readline()
+                       assert "200" in rsp
+                       logging.debug('authenticated')
+                       sockf.write("AUTHCARD %s\n" % (card_base64,)); sockf.flush()
+                       rsp = sockf.readline()
+                       if not "200" in rsp:
+                               raise ValueError, "no UID found for card ID"
+                       username = rsp.split('=')[1].strip()
+
+                       # Check for thier username
+                       try:
+                               # Get info from the system (by username)
+                               info = pwd.getpwnam(username)
+                       except KeyError:
+                               logging.info('getting info for user \'%s\': user not in password file' % (username,))
+                               return False
+               else:
+                       # Get the users ID
+                       self._userid = get_uid(cardId)
+
+                       # Check for thier username
+                       try:
+                               # Get info from the system (by UID)
+                               info = pwd.getpwuid(self._userid)
+                       except KeyError:
+                               logging.info('getting info for uid %d: user not in password file' % (self._userid,))
+                               return False
+
+               # If we get this far all is good
+               self._loggedIn = True
+               self._disabled = False
+               self._userid = info.pw_uid
+               self._username = info.pw_name
+               return True
 
        def addCard(self, cardId):
-               set_card_id(self._userId, cardId)
+               if not self.isLoggedIn():
+                       return False
+               if DISPSRV_MIFARE:
+                       card_base64 = base64.b64encode(cardId)
+                       logging.info('Enrolling card %s to uid %s (%s)' % (cardId, self._userId, self._username))
+                       sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+                       sock.connect(DISPENSE_ENDPOINT)
+                       sockf = sock.makefile()
+                       sockf.write("AUTHIDENT\n")
+                       sockf.flush(); rsp = sockf.readline()
+                       assert "200" in rsp
+                       sockf.write("SETEUSER %s\n", self._username)
+                       sockf.flush(); rsp = sockf.readline()
+                       assert "200" in rsp
+                       sockf.write("CARD_ADD %s\n", card_base64)
+                       sockf.flush(); rsp = sockf.readline()
+                       if "200" in rsp:
+                               return True
+                       else:
+                               return False
+               else:
+                       if get_uid(cardId) != None:
+                               return False
+                       else:
+                               logging.info('Enrolling card %s to uid %s (%s)' % (cardId, self._userId, self._username))
+                               set_card_id(self._userId, cardId)
+                               return True
 
-        def isLoggedIn(self):
-                return self._loggedIn
+       def isLoggedIn(self):
+               return self._loggedIn
 
-        def getUsername(self):
-                return self._username
+       def getUsername(self):
+               return self._username
 
-        def getBalance(self):
+       def getBalance(self):
                # Balance checking
-                acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
-                # this is fucking appalling
-                balance = acct[acct.find("$")+1:acct.find("(")].strip()
-                return balance
+               if self.isLoggedIn():
+                       acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
+               else:
+                       return None
+               balance = acct[acct.find("$")+1:acct.find("(")].strip()
+               return balance
 
-        def getItemInfo(itemId):
+       def getItemInfo(self, itemId):
+               logging.debug("getItemInfo(%s)" % (itemId,))
                itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
                args = ('dispense', 'iteminfo', itemId)
-                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))
+               info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
+               m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
+               if m == None:
+                       return("dead", 0)
+               cents = int(m.group(1))*100 + int(m.group(2))
                # return (name, price in cents)
-                return (m.group(3), cents)
+               return (m.group(3), cents)
 
-        def isDisabled(self):
-               return False
+       def isDisabled(self):
+               return self._disabled
 
-        def dispenseItem(self, itemId):
-               itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
-               if itemId == "":
+       def dispenseItem(self, itemId):
+               if not self.isLoggedIn() or self.getItemInfo(itemId)[0] == "dead":
                        return False
                else:
                        print('dispense -u "%s" %s'%(self._username, itemId))
-                       os.system('dispense -u "%s" %s'%(self._username, itemId))
-                       return True
+                       #os.system('dispense -u "%s" %s'%(self._username, itemId))
+                       return True
+
+       def logOut(self):
+               self._username = ""
+               self._disabled = True
+               self._loggedIn = False
+               self._userId = None
 
 """
 This class abstracts the idea of item numbers.
@@ -114,8 +197,20 @@ class OpenDispenseMapping():
 
        @staticmethod
        def vendingMachineToOpenDispense(itemId):
+               logging.debug("vendingMachineToOpenDispense(%s)" % (itemId,))
                _mappingFile = "OpenDispenseMappings.conf"
-               fh = open(_mappingFile)
+               try:
+                       fh = open(_mappingFile)
+               except IOError:
+                       if itemId[1] == '8':
+                               return 'coke:' + itemId[0]
+                       elif itemId[1] == '5':
+                               if itemId[0] == '5':
+                                       return 'door'
+                               else:
+                                       return None
+                       else:
+                               return 'snack:' + itemId
                map = ""
                for line in fh:
                        #line = line.strip()

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