5e413630f6ce6adc552f1744c303f46e019ca7de
[uccvend-vendserver.git] / VendServer / OpenDispense.py
1 """
2 Author: Mitchell Pomery (bobgeorge33)
3
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
8 """
9
10 from DispenseInterface import DispenseInterface
11 import os
12 import logging
13 import re
14 import pwd
15 import base64
16 import socket
17 from subprocess import Popen, PIPE
18 from LDAPConnector import get_uid,get_uname, set_card_id
19
20 DISPENSE_ENDPOINT = ("localhost", 11020)
21 DISPSRV_MIFARE = True
22
23 class OpenDispense(DispenseInterface):
24         _username = ""
25         _disabled = True
26         _loggedIn = False
27         _userId = None
28
29         def __init__(self, username=None, secret=False):
30                 pass
31
32         def authUserIdPin(self, userId, pin):
33                 userId = int(userId)
34
35                 try:
36                         # Get info from 
37                         info = pwd.getpwuid(userId)
38                 except KeyError:
39                         logging.info('getting pin for uid %d: user not in password file'%userId)
40                         return False
41
42                 if info.pw_dir == None: return False
43                 pinfile = os.path.join(info.pw_dir, '.pin')
44                 try:
45                         s = os.stat(pinfile)
46                 except OSError:
47                         logging.info('getting pin for uid %d: .pin not found in home directory'%userId)
48                         return False
49                 if s.st_mode & 077:
50                         logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%userId)
51                         os.chmod(pinfile, 0600)
52                 try:
53                         f = file(pinfile)
54                 except IOError:
55                         logging.info('getting pin for uid %d: I cannot read pin file'%userId)
56                         return False
57                 pinstr = f.readline().strip()
58                 f.close()
59                 if not re.search('^[0-9]{4}$', pinstr):
60                         logging.info('getting pin for uid %d: %s not a good pin'%(userId,repr(pinstr)))
61                         return False
62
63                 if pinstr == str(pin):
64                         #Login Successful
65                         self._userid = userId
66                         self._loggedIn = True
67                         self._disabled = False
68                         self._username = info.pw_name
69                         return True
70                 
71                 # Login Unsuccessful
72                 return False
73
74         def authMifareCard(self, cardId):
75                 self._loggedIn = False
76                 self._username = None
77                 if DISPSRV_MIFARE:
78                         card_base64 = base64.b64encode(cardId)
79                         
80                         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
81                         sock.connect(DISPENSE_ENDPOINT)
82                         logging.debug('connected to dispsrv')
83                         sockf = sock.makefile()
84                         sockf.write("AUTHIDENT\n"); sockf.flush()
85                         rsp = sockf.readline()
86                         assert "200" in rsp
87                         logging.debug('authenticated')
88                         sockf.write("AUTHCARD %s\n" % (card_base64,)); sockf.flush()
89                         rsp = sockf.readline()
90                         if not "200" in rsp:
91                                 logging.info("Rejected card base64:%s" % (card_base64,))
92                                 return False
93                         username = rsp.split('=')[1].strip()
94                         logging.info("Accepted card base64:%s for %s" % (card_base64,username,))
95
96                         # Check for thier username
97                         try:
98                                 # Get info from the system (by username)
99                                 info = pwd.getpwnam(username)
100                         except KeyError:
101                                 logging.info('getting info for user \'%s\': user not in password file' % (username,))
102                                 return False
103                 else:
104                         # Get the users ID
105                         self._userid = get_uid(cardId)
106
107                         # Check for thier username
108                         try:
109                                 # Get info from the system (by UID)
110                                 info = pwd.getpwuid(self._userid)
111                         except KeyError:
112                                 logging.info('getting info for uid %d: user not in password file' % (self._userid,))
113                                 return False
114
115                 # If we get this far all is good
116                 self._loggedIn = True
117                 self._disabled = False
118                 self._userid = info.pw_uid
119                 self._username = info.pw_name
120                 return True
121         def logOut(self):
122             self._loggedIn = False
123             self._disabled = False
124             self._userId = None
125             self._username = None
126
127         def addCard(self, cardId):
128                 if not self.isLoggedIn():
129                         return False
130                 if DISPSRV_MIFARE:
131                         card_base64 = base64.b64encode(cardId)
132                         logging.info('Enrolling card %s to uid %s (%s)' % (cardId, self._userId, self._username))
133                         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
134                         sock.connect(DISPENSE_ENDPOINT)
135                         sockf = sock.makefile()
136                         sockf.write("AUTHIDENT\n")
137                         sockf.flush(); rsp = sockf.readline()
138                         assert "200" in rsp
139                         sockf.write("SETEUSER %s\n" % (self._username,))
140                         sockf.flush(); rsp = sockf.readline()
141                         assert "200" in rsp
142                         sockf.write("CARD_ADD %s\n" % (card_base64,))
143                         sockf.flush(); rsp = sockf.readline()
144                         if "200" in rsp:
145                                 return True
146                         else:
147                                 return False
148                 else:
149                         if get_uid(cardId) != None:
150                                 return False
151                         else:
152                                 logging.info('Enrolling card %s to uid %s (%s)' % (cardId, self._userId, self._username))
153                                 set_card_id(self._userId, cardId)
154                                 return True
155
156         def isLoggedIn(self):
157                 return self._loggedIn
158
159         def getUsername(self):
160                 return self._username
161
162         def getBalance(self):
163                 # Balance checking
164                 if self.isLoggedIn():
165                         acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
166                 else:
167                         return None
168                 balance = acct[acct.find("$")+1:acct.find("(")].strip()
169                 return balance
170
171         def getItemInfo(self, itemId):
172                 logging.debug("getItemInfo(%s)" % (itemId,))
173                 itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
174                 args = ('dispense', 'iteminfo', itemId)
175                 info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
176                 m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
177                 if m == None:
178                         return("dead", 0)
179                 cents = int(m.group(1))*100 + int(m.group(2))
180                 # return (name, price in cents)
181                 return (m.group(3), cents)
182
183         def isDisabled(self):
184                 return self._disabled
185
186         def dispenseItem(self, itemId):
187                 if not self.isLoggedIn() or self.getItemInfo(itemId)[0] == "dead":
188                         return False
189                 else:
190                         print('dispense -u "%s" %s'%(self._username, itemId))
191                         #os.system('dispense -u "%s" %s'%(self._username, itemId))
192                         return True
193
194         def logOut(self):
195                 self._username = ""
196                 self._disabled = True
197                 self._loggedIn = False
198                 self._userId = None
199
200 """
201 This class abstracts the idea of item numbers.
202 It removes the need for VendServer to know the mappings between inputted numbers
203 and the equivalent itemId in OpenDispense.
204 """
205 class OpenDispenseMapping():
206
207         @staticmethod
208         def vendingMachineToOpenDispense(itemId):
209                 logging.debug("vendingMachineToOpenDispense(%s)" % (itemId,))
210                 _mappingFile = "OpenDispenseMappings.conf"
211                 try:
212                         fh = open(_mappingFile)
213                 except IOError:
214                         if itemId[1] == '8':
215                                 return 'coke:' + itemId[0]
216                         elif itemId[1] == '5':
217                                 if itemId[0] == '5':
218                                         return 'door'
219                                 else:
220                                         return None
221                         else:
222                                 return 'snack:' + itemId
223                 map = ""
224                 for line in fh:
225                         #line = line.strip()
226                         if line.startswith(str(itemId) + " "):
227                                 map = line[len(str(itemId)) + 1:].strip()
228                                 print(map)
229                 return map
230

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