1 """User objects for the MUFF Engine"""
3 # Copyright (c) 2005 MUDpy, The Fungi <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")
101 # otherwise, the password hash is empty
106 """Record account data to cold storage."""
108 # the user account must be authenticated to save
109 if self.authenticated:
111 # create an account section if it doesn't exist
112 if not self.record.has_section("account"):
113 self.record.add_section("account")
115 # write some in-memory data to the record
116 self.record.set("account", "name", self.name)
117 self.record.set("account", "passhash", self.passhash)
118 self.record.set("account", "last_address", self.address)
120 # the account files live here
121 account_path = muffconf.config_data.get("files", "accounts")
122 # the filename to which we'll write
123 filename = account_path + "/" + self.name.lower()
125 # if the directory doesn't exist, create it
126 # TODO: create account_path with 0700 perms
128 if os.listdir(account_path): pass
130 os.mkdir(account_path, )
132 # open the user account file for writing
133 # TODO: create filename with 0600 perms
134 record_file = file(filename, "w")
136 # dump the account data to it
137 self.record.write(record_file)
139 # close the user account file
143 """Send the user their current menu."""
144 self.send(muffmenu.get_menu(self))
147 """Remove a user from the list of connected users."""
148 muffvars.userlist.remove(self)
150 def send(self, output, eol="$(eol)"):
151 """Send arbitrary text to a connected user."""
153 # only when there is actual output
156 # start with a newline, append the message, then end
157 # with the optional eol string passed to this function
158 output = "$(eol)" + output + eol
160 # replace eol markers with a crlf
161 # TODO: search for markers and replace from a dict
162 output = string.replace(output, "$(eol)", "\r\n")
164 # replace display markers with ansi escapse sequences
165 output = string.replace(output, "$(bld)", chr(27)+"[1m")
166 output = string.replace(output, "$(nrm)", chr(27)+"[0m")
167 output = string.replace(output, "$(blk)", chr(27)+"[30m")
168 output = string.replace(output, "$(grn)", chr(27)+"[32m")
169 output = string.replace(output, "$(red)", chr(27)+"[31m")
171 # the user's account name
172 output = string.replace(output, "$(account)", self.name)
174 # wrap the text at 80 characters
175 # TODO: prompt user for preferred wrap width
176 output = muffmisc.wrap_ansi_text(output, 80)
178 # drop the formatted output into the output queue
179 self.output_queue.append(output)
181 # try to send the last item in the queue, remove it and
182 # flag that menu display is not needed
184 self.connection.send(self.output_queue[0])
185 self.output_queue.remove(self.output_queue[0])
186 self.menu_seen = False
188 # but if we can't, that's okay too