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

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