diff --git a/VendServer/OpenDispense.py b/VendServer/OpenDispense.py index 6ada5be678525c43905f745bb76ee10eadcda1d8..f1100ed9293764a8603489052ca9a866e9c731e1 100644 --- a/VendServer/OpenDispense.py +++ b/VendServer/OpenDispense.py @@ -14,8 +14,6 @@ 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) @@ -39,7 +37,7 @@ class OpenDispense(DispenseInterface): return self.authUserIdPin_db(userId, pin) #return self.authUserIdPin_file(userId, pin) - def _connect(self, authenticte:bool=True, set_euid:bool=False): + def _connect(self, authenticate:bool=True, set_euid:bool=False): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) try: @@ -49,7 +47,7 @@ class OpenDispense(DispenseInterface): return None logging.debug('connected to dispsrv') conn = Connection( sock.makefile('rw', encoding='utf-8') ) - if authenticte: + if authenticate: rsp = conn.send_command("AUTHIDENT") if not "200" in rsp: logging.info('Server said no to AUTHIDENT! - %r' % (rsp,)) @@ -73,7 +71,7 @@ class OpenDispense(DispenseInterface): logging.info('getting pin for uid %d: user not in password file'%userId) return False - conn = self._connect(authenticte=True) + conn = self._connect() if conn is None: logging.error("getting pin for uid {}: Unable to open connection".format(userId)) return False @@ -202,35 +200,83 @@ class OpenDispense(DispenseInterface): def getBalance(self): # Balance checking - if self.isLoggedIn(): - acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE, encoding='utf-8').communicate() - else: - return None - balance = acct[acct.find("$")+1:acct.find("(")].strip() - return balance + if not self.isLoggedIn(): + return "?" + + conn = self._connect(authenticate=False) + if conn is None: + return "?" + cmd = "USER_INFO {}".format(self._username) + rsp = conn.send_command(cmd) + try: + code,rest = rsp.split(" ", 1) + if code != "202": + raise ValueError("Code not 202") + _user,_name,balance,flags = rest.split(" ") + return "{:.2f}".format(int(balance) / 100) + except ValueError as e: + logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e)) + return "?" - def getItemInfo(self, itemId): + def getItemInfo(self, itemId: str): logging.debug("getItemInfo(%s)" % (itemId,)) itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId) - args = ('dispense', 'iteminfo', itemId) - info, unused = Popen(args, close_fds=True, stdout=PIPE, encoding='utf-8').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) + + conn = self._connect(authenticate=False) + if conn is None: + return ("dead", 0,) + cmd = "ITEM_INFO {}".format(itemId) + rsp = conn.send_command(cmd) + try: + code,rest = rsp.split(" ", 1) + if code != "202": + raise ValueError("Code not 202") + _item,itemid,status,price_cents,name = rest.split(" ", 4) + return (name, int(price_cents),) + except ValueError as e: + logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e)) + return ("dead", 0,) def isDisabled(self): return self._disabled def dispenseItem(self, itemId): + logging.debug("getItemInfo(%s)" % (itemId,)) if not self.isLoggedIn() or self.getItemInfo(itemId)[0] == "dead": + return "999" + + conn = self._connect(set_euid=True) + if conn is None: + return "999" + + cmd = "DISPENSE {}".format( OpenDispenseMapping.vendingMachineToOpenDispense(itemId) ) + rsp = conn.send_command(cmd) + try: + code,rest = rsp.split(" ", 1) + return code + except ValueError as e: + logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e)) + return 999 + def openDoor(self): + if not self.isLoggedIn(): + return False + + conn = self._connect(set_euid=True) + if conn is None: + return False + + cmd = "DISPENSE door:0".format() + rsp = conn.send_command(cmd) + try: + code,rest = rsp.split(" ", 1) + if code == "200": + return True + if code == "402": # "Poor You" + return False + raise ValueError("Unknown code") + except ValueError as e: + logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e)) return False - else: - print(('dispense -u "%s" %s'%(self._username, itemId))) - #os.system('dispense -u "%s" %s'%(self._username, itemId)) - return True class Connection(object): def __init__(self, sockf): diff --git a/VendServer/VendServer.py b/VendServer/VendServer.py index 46b31418523f2264be1a94f781fe81253bc150a6..4e059ece3e2f941cb5a335aa8f0bee56a57e2d29 100755 --- a/VendServer/VendServer.py +++ b/VendServer/VendServer.py @@ -8,7 +8,6 @@ import sys, os, string, re, pwd, signal, math, syslog import logging, logging.handlers from traceback import format_tb from time import time, sleep, mktime, localtime -from subprocess import Popen, PIPE #from .LATClient import LATClient, LATClientException from .SerialClient import SerialClient, SerialClientException from .VendingMachine import VendingMachine, VendingException @@ -198,7 +197,7 @@ class VendServer(): """ Format text so it will appear centered on the screen. """ - def center(self, str): + def center(self, str: str): LEN = 10 return ' '*((LEN-len(str))//2)+str @@ -412,11 +411,7 @@ class VendServer(): 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: + if self.dispense.openDoor(): logging.info('door opened') self.vstatus.mk.set_message(self.center('DOOR OPEN')) else: @@ -430,21 +425,21 @@ class VendServer(): self.vstatus.cur_selection = '' return elif self.vstatus.cur_selection[1] == '8': + # Drinks: No need to vend, just print a funny message and ask the server to vend from coke + logging.info('dispensing drink {} for {}'.format(self.vstatus.cur_selection, self.vstatus.username)) self.v.display('GOT DRINK?') - if ((os.system('dispense -u "%s" coke:%s'%(self.vstatus.username, self.vstatus.cur_selection[0])) >> 8) != 0): + if self.dispense.dispenseItem(self.vstatus.cur_selection) == "200": 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( '--' ) + logging.info('dispensing snack {} for {}'.format(self.vstatus.cur_selection, self.vstatus.username)) + # Snacks: Show the name/price then dispense it + name, price = self.dispense.getItemInfo( self.vstatus.cur_selection ) 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): + status = self.dispense.dispenseItem(self.vstatus.cur_selection) + if status == "200": # magic dispense syslog service (worked, code, string) = self.v.vend(self.vstatus.cur_selection) if worked: @@ -454,14 +449,12 @@ class VendServer(): print("Vend Failed:", code, string) syslog.syslog(syslog.LOG_WARNING | syslog.LOG_LOCAL4, "vending %s (slot %s) for %s FAILED %r %r" % (name, self.vstatus.cur_selection, self.vstatus.username, code, string)) self.v.display('VEND FAIL') - elif (exitcode == 5): # RV_BALANCE + elif status == "402": # 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) + elif status == "406": # 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)) + syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %s)" % (name, self.vstatus.cur_selection, self.vstatus.username, status)) self.v.display('UNK ERROR') sleep(1) @@ -675,16 +668,16 @@ class VendServer(): ### we live in 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]]) + 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]]) + onthehour = mktime((hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \ + 0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8])) #print "when it fashionable to wear a onion on your hip" diff --git a/VendServer/VendingMachine.py b/VendServer/VendingMachine.py index 86a1342db1a3bf92b4b5ec4135e96d4fe9330911..b10f7505382a9ee21067132e56ba36c17d7ef6ba 100644 --- a/VendServer/VendingMachine.py +++ b/VendServer/VendingMachine.py @@ -116,9 +116,10 @@ class VendingMachine: logging.warning('Unhandled event! (%s %s)\n'%(code,text)) def authed_message(self, message): - print('self.challenge = %04x' % self.challenge) if self.challenge == None: + print('self.challenge = None') return message + print('self.challenge = %04x' % self.challenge) crc = do_crc('%c%c'%(self.challenge >> 8, self.challenge & 0xff)) crc = do_crc(self.secret, crc) crc = do_crc(message, crc)