from traceback import format_tb
if USE_DB: import pg
from time import time, sleep, mktime, localtime
-from popen2 import popen2
+from subprocess import Popen, PIPE
from LATClient import LATClient, LATClientException
from SerialClient import SerialClient, SerialClientException
from VendingMachine import VendingMachine, VendingException
from SnackConfig import get_snack#, get_snacks
import socket
from posix import geteuid
-from LDAPConnector import get_uid, set_card_id
+from LDAPConnector import get_uid,get_uname, set_card_id
CREDITS="""
This vending machine software brought to you by:
def scroll_options(username, mk, welcome = False):
if welcome:
- # Balance checking: crap code, [DAA]'s fault
- # Updated 2011 to handle new dispense [MRD]
- raw_acct = os.popen('dispense acct %s' % username)
- acct = raw_acct.read()
+ # 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()
- raw_acct.close()
msg = [(center('WELCOME'), False, TEXT_SPEED),
(center(username), False, TEXT_SPEED),
# Get coke contents
cokes = []
for i in range(0, 7):
- cmd = 'dispense iteminfo coke:%i' % i
- raw = os.popen(cmd)
- info = raw.read()
- raw.close()
+ 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)));
msg.append((choices, False, None))
mk.set_messages(msg)
-def get_acct_state(uid):
- try:
- info = pwd.getpwuid(uid)
- except KeyError:
- logging.info('getting pin for uid %d: user not in password file'%uid)
- return 'invalid'
- ret = os.system('dispense acct %s' % (info.pw_name))
- if ret != 0:
- return 'invalid'
-
- # TODO: Disabled account check (done in server pin check now)
+_pin_uid = 0
+_pin_uname = 'root'
+_pin_pin = '----'
- return 'good'
+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):
- info = pwd.getpwuid(uid)
- if skip_pin_check:
- logging.info('accepted mifare for uid %d (%s)'%(uid,info.pw_name))
- elif os.system('dispense pincheck %04i %s' % (pin, info.pw_name)) != 0:
+ 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
- else:
- logging.info('accepted pin for uid %d (%s)'%(uid,info.pw_name))
- return info.pw_name
def cookie(v):
ClockIdler(v),
StringIdler(v),
TrainIdler(v),
+ # "Hello World" in brainfuck
+ StringIdler(v, text=">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-] <.>+++++++++++[<++++++++>-]<-.--------.+++.------.--------.[-]>++++++++[<++++>- ]<+.[-]++++++++++."),
]
disabled = [
]
v.display('SEEMS NOT')
else:
v.display('GOT DRINK!')
- #v.display('SEE FRIDGE')
else:
# first see if it's a named slot
try:
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
- # For some reason, this causes the machine and this code to desync
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))
- v.vend(vstatus.cur_selection)
- v.display('THANK YOU')
+ (worked, code, string) = v.vend(vstatus.cur_selection)
+ if worked:
+ v.display('THANK YOU')
+ 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)
def price_check(v, vstatus):
if vstatus.cur_selection[1] == '8':
- v.display(center('SEE COKE'))
+ 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:
except:
price, shortname, name = get_snack( '--' )
dollarprice = "$%.2f" % ( price / 100.0 )
- v.display(vstatus.cur_selection+' - %s'%dollarprice)
+ v.display(vstatus.cur_selection+' - %s'%dollarprice)
def handle_getting_pin_key(state, event, params, v, vstatus):
return
- acct_state = get_acct_state(uid)
- if acct_state == 'invalid':
- logging.info('user '+vstatus.cur_user+' is not in the database')
- vstatus.mk.set_messages(
- [(' '*10+'INVALID PIN SETUP'+' '*11, False, 3)])
- vstatus.cur_user = ''
- vstatus.cur_pin = ''
-
- reset_idler(v, vstatus, 3)
- return
- elif acct_state == 'locked':
- logging.info('user '+vstatus.cur_user+' is locked')
+ 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)
+
return
- elif acct_state == 'good':
- 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
- else:
- logging.error('user '+vstatus.cur_user+' has an unknown account state'+acct_state)
+
+ if acct_is_disabled():
+ logging.info('user '+vstatus.cur_user+' is disabled')
vstatus.mk.set_messages(
- [(' '*10+'INVALID PIN SETUP'+' '*11, False, 3)])
+ [(' '*11+'ACCOUNT DISABLED'+' '*11, False, 3)])
vstatus.cur_user = ''
vstatus.cur_pin = ''
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
try:
vstatus.cur_user = get_uid(card_id)
logging.info('Mapped card id to uid %s'%vstatus.cur_user)
- vstatus.username = verify_user_pin(int(vstatus.cur_user), None, True)
+ 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
- if vstatus.username:
+ reset_idler(v, vstatus, 2)
+ return
+ elif vstatus.username:
v.beep(0, False)
vstatus.cur_selection = ''
vstatus.change_state(STATE_GET_SELECTION)
try:
run_forever(rfh, wfh, options, config_opts)
- except VendingException as e:
+ except VendingException:
logging.error("Connection died, trying again...")
- logging.info("Exception: "+e.__str__())
logging.info("Trying again in 5 seconds.")
sleep(5)