Imported from archive.
[mudpy.git] / lib / muff / muffuser.py
1 """User objects for the MUFF Engine"""
2
3 # Copyright (c) 2005 mudpy, Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
4 # Licensed per terms in the LICENSE file distributed with this software.
5
6 # user accounts are stored in ini-style files supported by ConfigParser
7 import ConfigParser
8
9 # test for existence of the account dir with os.listdir and os.mkdir to make it
10 import os
11
12 # string.replace is used to perform substitutions for color codes and the like
13 import string
14
15 # hack to load all modules in the muff package
16 import muff
17 for module in muff.__all__:
18         exec("import " + module)
19
20 class User:
21         """This is a connected user."""
22
23         def __init__(self):
24                 """Default values for the in-memory user variables."""
25
26                 # the account name
27                 self.name = ""
28
29                 # the password hash
30                 self.passhash = ""
31
32                 # the current client ip address
33                 self.address = ""
34
35                 # the previous client ip address
36                 self.last_address = ""
37
38                 # the current socket connection object
39                 self.connection = None
40
41                 # a flag to denote whether the user is authenticated
42                 self.authenticated = False
43
44                 # number of times password entry has failed during this session
45                 self.password_tries = 1
46
47                 # the current state of the user
48                 self.state = "entering account name"
49
50                 # flag to indicate whether a menu has been displayed
51                 self.menu_seen = False
52
53                 # current error condition, if any
54                 self.error = ""
55
56                 # fifo-style queue for lines of user input
57                 self.input_queue = []
58
59                 # fifo-style queue for blocks of user output
60                 self.output_queue = []
61
62                 # holding pen for unterminated user input
63                 self.partial_input = ""
64
65                 # flag to indicate the current echo status of the client
66                 self.echoing = True
67
68                 # an object containing persistent account data
69                 self.record = ConfigParser.SafeConfigParser()
70
71         def load(self):
72                 """Retrieve account data from cold storage."""
73
74                 # what the filename for the user account should be
75                 filename = muffconf.config_data.get("files", "accounts") + "/" + self.name
76
77                 # try to load the password hash and last connection ipa
78                 try:
79                         self.record.read(filename)
80                         self.passhash = self.record.get("account", "passhash")
81                         self.last_address = self.record.get("account", "last_address", self.address)
82
83                 # if we can't, that's okay too
84                 except:
85                         pass
86
87         def get_passhash(self):
88                 """Retrieve the user's account password hash from storage."""
89
90                 # what the filename for the user account could be
91                 filename = muffconf.config_data.get("files", "accounts") + "/" + self.proposed_name
92
93                 # create a temporary account record object
94                 temporary_record = ConfigParser.SafeConfigParser()
95
96                 # try to load the indicated account and get a password hash
97                 try:
98                         temporary_record.read(filename)
99                         self.passhash = temporary_record.get("account", "passhash")
100                         return True
101
102                 # otherwise, the password hash is empty
103                 except:
104                         self.passhash = ""
105                         return False
106
107         def save(self):
108                 """Record account data to cold storage."""
109
110                 # the user account must be authenticated to save
111                 if self.authenticated:
112
113                         # create an account section if it doesn't exist
114                         if not self.record.has_section("account"):
115                                 self.record.add_section("account")
116
117                         # write some in-memory data to the record
118                         self.record.set("account", "name", self.name)
119                         self.record.set("account", "passhash", self.passhash)
120                         self.record.set("account", "last_address", self.address)
121
122                         # the account files live here
123                         account_path = muffconf.config_data.get("files", "accounts")
124                         # the filename to which we'll write
125                         filename = account_path + "/" + self.name.lower()
126
127                         # if the directory doesn't exist, create it
128                         # TODO: create account_path with 0700 perms
129                         try:
130                                 if os.listdir(account_path): pass
131                         except:
132                                 os.mkdir(account_path, )
133
134                         # open the user account file for writing
135                         # TODO: create filename with 0600 perms
136                         record_file = file(filename, "w")
137
138                         # dump the account data to it
139                         self.record.write(record_file)
140
141                         # close the user account file
142                         record_file.close()
143
144         def show_menu(self):
145                 """Send the user their current menu."""
146                 self.send(muffmenu.get_menu(self))
147
148         def remove(self):
149                 """Remove a user from the list of connected users."""
150                 muffvars.userlist.remove(self)
151
152         def send(self, output, eol="$(eol)"):
153                 """Send arbitrary text to a connected user."""
154
155                 # only when there is actual output
156                 if output:
157
158                         # start with a newline, append the message, then end
159                         # with the optional eol string passed to this function
160                         output = "$(eol)" + output + eol
161
162                         # replace eol markers with a crlf
163                         # TODO: search for markers and replace from a dict
164                         output = string.replace(output, "$(eol)", "\r\n")
165
166                         # replace display markers with ansi escapse sequences
167                         output = string.replace(output, "$(bld)", chr(27)+"[1m")
168                         output = string.replace(output, "$(nrm)", chr(27)+"[0m")
169                         output = string.replace(output, "$(blk)", chr(27)+"[30m")
170                         output = string.replace(output, "$(grn)", chr(27)+"[32m")
171                         output = string.replace(output, "$(red)", chr(27)+"[31m")
172
173                         # the user's account name
174                         output = string.replace(output, "$(account)", self.name)
175
176                         # wrap the text at 80 characters
177                         # TODO: prompt user for preferred wrap width
178                         output = muffmisc.wrap_ansi_text(output, 80)
179
180                         # drop the formatted output into the output queue
181                         self.output_queue.append(output)
182
183                         # try to send the last item in the queue, remove it and
184                         # flag that menu display is not needed
185                         try:
186                                 self.connection.send(self.output_queue[0])
187                                 self.output_queue.remove(self.output_queue[0])
188                                 self.menu_seen = False
189
190                         # but if we can't, that's okay too
191                         except:
192                                 pass
193