""" Author: Mitchell Pomery (bobgeorge33) System to connect to OpenDispense2. Most of this code has been copied out of VendServer.py, then had variables updated so that it runs. 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 # A list of cards that should never be registered, and should never log in # - Some of these might have been registered before we knew they were duplicates CARD_BLACKLIST = [ 'AAAAAA==', # All zeroes, don't allow that. 'ISIjJA==', # CommBank credit cards ] class OpenDispense(DispenseInterface): _username = "" _disabled = True _loggedIn = False _userId = None def __init__(self, username=None, secret=False): pass def authUserIdPin(self, userId, pin): return self.authUserIdPin_db(userId, pin) #return self.authUserIdPin_file(userId, pin) def authUserIdPin_db(self, userId, pin): userId = int(userId) try: # Get username (TODO: Store the user ID in the dispense database too) info = pwd.getpwuid(userId) except KeyError: logging.info('getting pin for uid %d: user not in password file'%userId) return False 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("PIN_CHECK %s %s\n" % (info.pw_name, pin)); sockf.flush() rsp = sockf.readline() if not "200" in rsp: logging.info('checking pin for uid %d: Server said no - %r' % (userId, rsp)) return False #Login Successful logging.info('accepted pin for uid %d \'%s\'' % (userId, info.pw_name)) self._userid = userId self._loggedIn = True self._disabled = False self._username = info.pw_name return True def authUserIdPin_file(self, userId, pin): userId = int(userId) try: # Get info from info = pwd.getpwuid(userId) except KeyError: logging.info('getting pin for uid %d: user not in password file'%userId) return False 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): self._loggedIn = False self._username = None if DISPSRV_MIFARE: card_base64 = base64.b64encode(cardId) if card_base64 in CARD_BLACKLIST: logging.info("Blacklisted card base64:%s" % (card_base64,)) return False 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: logging.info("Rejected card base64:%s" % (card_base64,)) return False username = rsp.split('=')[1].strip() logging.info("Accepted card base64:%s for %s" % (card_base64,username,)) ## 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 #self._userid = info.pw_uid self._userid = None self._username = username 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 self._username = info.pw_name # If we get this far all is good self._loggedIn = True self._disabled = False return True def logOut(self): self._loggedIn = False self._disabled = False self._userId = None self._username = None def addCard(self, cardId): if not self.isLoggedIn(): return False if DISPSRV_MIFARE: card_base64 = base64.b64encode(cardId) if card_base64 in CARD_BLACKLIST: logging.info("Blacklisted card base64:%s" % (card_base64,)) return False logging.info('Enrolling card base64:%s to uid %s (%s)' % (card_base64, 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 getUsername(self): return self._username def getBalance(self): # Balance checking 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(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) 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) def isDisabled(self): return self._disabled 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 def logOut(self): self._username = "" self._disabled = True self._loggedIn = False self._userId = None """ This class abstracts the idea of item numbers. It removes the need for VendServer to know the mappings between inputted numbers and the equivalent itemId in OpenDispense. """ class OpenDispenseMapping(): @staticmethod def vendingMachineToOpenDispense(itemId): logging.debug("vendingMachineToOpenDispense(%s)" % (itemId,)) _mappingFile = "OpenDispenseMappings.conf" 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() if line.startswith(str(itemId) + " "): map = line[len(str(itemId)) + 1:].strip() print(map) return map # vim: noexpandtab ts=4 sw=4