1 """User objects for the MUFF Engine"""
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.
6 # user accounts are stored in ini-style files supported by ConfigParser
9 # test for existence of the account dir with os.listdir and os.mkdir to make it
12 # string.replace is used to perform substitutions for color codes and the like
15 # hack to load all modules in the muff package
17 for module in muff.__all__:
18 exec("import " + module)
21 """This is a connected user."""
24 """Default values for the in-memory user variables."""
32 # the current client ip address
35 # the previous client ip address
36 self.last_address = ""
38 # the current socket connection object
39 self.connection = None
41 # a flag to denote whether the user is authenticated
42 self.authenticated = False
44 # number of times password entry has failed during this session
45 self.password_tries = 1
47 # the current state of the user
48 self.state = "entering account name"
50 # flag to indicate whether a menu has been displayed
51 self.menu_seen = False
53 # current error condition, if any
56 # fifo-style queue for lines of user input
59 # fifo-style queue for blocks of user output
60 self.output_queue = []
62 # holding pen for unterminated user input
63 self.partial_input = ""
65 # flag to indicate the current echo status of the client
68 # an object containing persistent account data
69 self.record = ConfigParser.SafeConfigParser()
72 """Retrieve account data from cold storage."""
74 # what the filename for the user account should be
75 filename = muffconf.config_data.get("files", "accounts") + "/" + self.name
77 # try to load the password hash and last connection ipa
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)
83 # if we can't, that's okay too
87 def get_passhash(self):
88 """Retrieve the user's account password hash from storage."""
90 # what the filename for the user account could be
91 filename = muffconf.config_data.get("files", "accounts") + "/" + self.proposed_name
93 # create a temporary account record object
94 temporary_record = ConfigParser.SafeConfigParser()
96 # try to load the indicated account and get a password hash
98 temporary_record.read(filename)
99 self.passhash = temporary_record.get("account", "passhash")
102 # otherwise, the password hash is empty
108 """Record account data to cold storage."""
110 # the user account must be authenticated to save
111 if self.authenticated:
113 # create an account section if it doesn't exist
114 if not self.record.has_section("account"):
115 self.record.add_section("account")
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)
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()
127 # if the directory doesn't exist, create it
128 # TODO: create account_path with 0700 perms
130 if os.listdir(account_path): pass
132 os.mkdir(account_path, )
134 # open the user account file for writing
135 # TODO: create filename with 0600 perms
136 record_file = file(filename, "w")
138 # dump the account data to it
139 self.record.write(record_file)
141 # close the user account file
145 """Send the user their current menu."""
146 self.send(muffmenu.get_menu(self))
149 """Remove a user from the list of connected users."""
150 muffvars.userlist.remove(self)
152 def send(self, output, eol="$(eol)"):
153 """Send arbitrary text to a connected user."""
155 # only when there is actual output
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
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")
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")
173 # the user's account name
174 output = string.replace(output, "$(account)", self.name)
176 # wrap the text at 80 characters
177 # TODO: prompt user for preferred wrap width
178 output = muffmisc.wrap_ansi_text(output, 80)
180 # drop the formatted output into the output queue
181 self.output_queue.append(output)
183 # try to send the last item in the queue, remove it and
184 # flag that menu display is not needed
186 self.connection.send(self.output_queue[0])
187 self.output_queue.remove(self.output_queue[0])
188 self.menu_seen = False
190 # but if we can't, that's okay too