2 Author: Mitchell Pomery (bobgeorge33)
4 System to connect to OpenDispense2.
5 Most of this code has been copied out of VendServer.py, then had variables updated so that it runs.
6 This is so VendServer can easily operate regardless of the current accounting backend.
7 Documentation for this code can be found inder Dispence.DispenceInterface
10 from DispenseInterface import DispenseInterface
17 from subprocess import Popen, PIPE
18 from LDAPConnector import get_uid,get_uname, set_card_id
20 DISPENSE_ENDPOINT = ("localhost", 11020)
23 # A list of cards that should never be registered, and should never log in
24 # - Some of these might have been registered before we knew they were duplicates
26 'AAAAAA==', # All zeroes, don't allow that.
27 'ISIjJA==', # CommBank credit cards
30 class OpenDispense(DispenseInterface):
36 def __init__(self, username=None, secret=False):
39 def authUserIdPin(self, userId, pin):
40 return self.authUserIdPin_db(userId, pin)
41 #return self.authUserIdPin_file(userId, pin)
43 def authUserIdPin_db(self, userId, pin):
47 # Get username (TODO: Store the user ID in the dispense database too)
48 info = pwd.getpwuid(userId)
50 logging.info('getting pin for uid %d: user not in password file'%userId)
53 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
54 sock.connect(DISPENSE_ENDPOINT)
55 logging.debug('connected to dispsrv')
56 sockf = sock.makefile()
57 sockf.write("AUTHIDENT\n"); sockf.flush()
58 rsp = sockf.readline()
60 logging.debug('authenticated')
61 sockf.write("PIN_CHECK %s %s\n" % (info.pw_name, pin)); sockf.flush()
62 rsp = sockf.readline()
64 logging.info('checking pin for uid %d: Server said no - %r' % (userId, rsp))
67 logging.info('accepted pin for uid %d \'%s\'' % (userId, info.pw_name))
70 self._disabled = False
71 self._username = info.pw_name
74 def authUserIdPin_file(self, userId, pin):
79 info = pwd.getpwuid(userId)
81 logging.info('getting pin for uid %d: user not in password file'%userId)
84 if info.pw_dir == None: return False
85 pinfile = os.path.join(info.pw_dir, '.pin')
89 logging.info('getting pin for uid %d: .pin not found in home directory'%userId)
92 logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%userId)
93 os.chmod(pinfile, 0600)
97 logging.info('getting pin for uid %d: I cannot read pin file'%userId)
99 pinstr = f.readline().strip()
101 if not re.search('^[0-9]{4}$', pinstr):
102 logging.info('getting pin for uid %d: %s not a good pin'%(userId,repr(pinstr)))
105 if pinstr == str(pin):
107 self._userid = userId
108 self._loggedIn = True
109 self._disabled = False
110 self._username = info.pw_name
116 def authMifareCard(self, cardId):
117 self._loggedIn = False
118 self._username = None
120 card_base64 = base64.b64encode(cardId)
122 if card_base64 in CARD_BLACKLIST:
123 logging.info("Blacklisted card base64:%s" % (card_base64,))
126 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
127 sock.connect(DISPENSE_ENDPOINT)
128 logging.debug('connected to dispsrv')
129 sockf = sock.makefile()
130 sockf.write("AUTHIDENT\n"); sockf.flush()
131 rsp = sockf.readline()
133 logging.debug('authenticated')
134 sockf.write("AUTHCARD %s\n" % (card_base64,)); sockf.flush()
135 rsp = sockf.readline()
137 logging.info("Rejected card base64:%s" % (card_base64,))
139 username = rsp.split('=')[1].strip()
140 logging.info("Accepted card base64:%s for %s" % (card_base64,username,))
142 ## Check for thier username
144 # # Get info from the system (by username)
145 # info = pwd.getpwnam(username)
147 # logging.info('getting info for user \'%s\': user not in password file' % (username,))
149 #self._userid = info.pw_uid
151 self._username = username
154 self._userid = get_uid(cardId)
156 # Check for thier username
158 # Get info from the system (by UID)
159 info = pwd.getpwuid(self._userid)
161 logging.info('getting info for uid %d: user not in password file' % (self._userid,))
163 self._username = info.pw_name
165 # If we get this far all is good
166 self._loggedIn = True
167 self._disabled = False
171 self._loggedIn = False
172 self._disabled = False
174 self._username = None
176 def addCard(self, cardId):
177 if not self.isLoggedIn():
180 card_base64 = base64.b64encode(cardId)
181 if card_base64 in CARD_BLACKLIST:
182 logging.info("Blacklisted card base64:%s" % (card_base64,))
184 logging.info('Enrolling card base64:%s to uid %s (%s)' % (card_base64, self._userId, self._username))
185 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
186 sock.connect(DISPENSE_ENDPOINT)
187 sockf = sock.makefile()
188 sockf.write("AUTHIDENT\n")
189 sockf.flush(); rsp = sockf.readline()
191 sockf.write("SETEUSER %s\n" % (self._username,))
192 sockf.flush(); rsp = sockf.readline()
194 sockf.write("CARD_ADD %s\n" % (card_base64,))
195 sockf.flush(); rsp = sockf.readline()
201 if get_uid(cardId) != None:
204 logging.info('Enrolling card %s to uid %s (%s)' % (cardId, self._userId, self._username))
205 set_card_id(self._userId, cardId)
208 def isLoggedIn(self):
209 return self._loggedIn
211 def getUsername(self):
212 return self._username
214 def getBalance(self):
216 if self.isLoggedIn():
217 acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
220 balance = acct[acct.find("$")+1:acct.find("(")].strip()
223 def getItemInfo(self, itemId):
224 logging.debug("getItemInfo(%s)" % (itemId,))
225 itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
226 args = ('dispense', 'iteminfo', itemId)
227 info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
228 m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
231 cents = int(m.group(1))*100 + int(m.group(2))
232 # return (name, price in cents)
233 return (m.group(3), cents)
235 def isDisabled(self):
236 return self._disabled
238 def dispenseItem(self, itemId):
239 if not self.isLoggedIn() or self.getItemInfo(itemId)[0] == "dead":
242 print('dispense -u "%s" %s'%(self._username, itemId))
243 #os.system('dispense -u "%s" %s'%(self._username, itemId))
248 self._disabled = True
249 self._loggedIn = False
253 This class abstracts the idea of item numbers.
254 It removes the need for VendServer to know the mappings between inputted numbers
255 and the equivalent itemId in OpenDispense.
257 class OpenDispenseMapping():
260 def vendingMachineToOpenDispense(itemId):
261 logging.debug("vendingMachineToOpenDispense(%s)" % (itemId,))
262 _mappingFile = "OpenDispenseMappings.conf"
264 fh = open(_mappingFile)
267 return 'coke:' + itemId[0]
268 elif itemId[1] == '5':
274 return 'snack:' + itemId
278 if line.startswith(str(itemId) + " "):
279 map = line[len(str(itemId)) + 1:].strip()
284 # vim: noexpandtab ts=4 sw=4