X-Git-Url: https://mudpy.org/gitweb?p=mudpy.git;a=blobdiff_plain;f=lib%2Fmuff%2Fmuffuser.py;h=9a5169ca66c10513211a6207dd835f36c644f809;hp=ebe0c32fb03716a73ab4281765ce1409426c2be7;hb=1ff00115321d800bec7313a3fdfc97a8b0b006fa;hpb=994d6e52ce3d5c719991e8f798642cdb8a24b7d1 diff --git a/lib/muff/muffuser.py b/lib/muff/muffuser.py index ebe0c32..9a5169c 100644 --- a/lib/muff/muffuser.py +++ b/lib/muff/muffuser.py @@ -68,11 +68,106 @@ class User: # an object containing persistent account data self.record = ConfigParser.SafeConfigParser() + def quit(self): + """Log, save, close the connection and remove.""" + if self.name: message = "User " + self.name + else: message = "An unnamed user" + message += " logged out." + muffmisc.log(message) + self.save() + self.connection.close() + self.remove() + + def reload(self): + """Save, load a new user and relocate the connection.""" + + # unauthenticated connections get the boot + if not self.authenticated: + muffmisc.log("An unauthenticated user was disconnected during reload.") + self.state = "disconnecting" + + # authenticated users + else: + + # save and get out of the list + self.save() + self.remove() + + # create a new user object + new_user = muffuser.User() + + # give it the same name + new_user.name = self.name + + # load from file + new_user.load() + + # set everything else equivalent + new_user.address = self.address + new_user.last_address = self.last_address + new_user.connection = self.connection + new_user.authenticated = self.authenticated + new_user.password_tries = self.password_tries + new_user.state = self.state + new_user.menu_seen = self.menu_seen + new_user.error = self.error + new_user.input_queue = self.input_queue + new_user.output_queue = self.output_queue + new_user.partial_input = self.partial_input + new_user.echoing = self.echoing + + # add it to the list + muffvars.userlist.append(new_user) + + # get rid of the old user object + del(self) + + def replace_old_connections(self): + """Disconnect active users with the same name.""" + + # the default return value + return_value = False + + # iterate over each user in the list + for old_user in muffvars.userlist: + + # the name is the same but it's not us + if old_user.name == self.name and old_user is not self: + + # make a note of it + muffmisc.log("User " + self.name + " reconnected--closing old connection to " + old_user.address + ".") + old_user.send("$(eol)$(red)New connection from " + self.address + ". Terminating old connection...$(nrm)$(eol)") + self.send("$(eol)$(red)Taking over old connection from " + old_user.address + ".$(nrm)") + + # close the old connection + old_user.connection.close() + + # replace the old connection with this one + old_user.connection = self.connection + old_user.last_address = old_user.address + old_user.address = self.address + old_user.echoing = self.echoing + + # take this one out of the list and delete + self.remove() + del(self) + return_value = True + break + + # true if an old connection was replaced, false if not + return return_value + + def authenticate(self): + """Flag the user as authenticated and disconnect duplicates.""" + if not self.state is "authenticated": + muffmisc.log("User " + self.name + " logged in.") + self.authenticated = True + def load(self): """Retrieve account data from cold storage.""" # what the filename for the user account should be - filename = muffconf.config_data.get("files", "accounts") + "/" + self.name + filename = muffconf.get("files", "accounts") + "/" + self.name # try to load the password hash and last connection ipa try: @@ -88,7 +183,7 @@ class User: """Retrieve the user's account password hash from storage.""" # what the filename for the user account could be - filename = muffconf.config_data.get("files", "accounts") + "/" + self.proposed_name + filename = muffconf.get("files", "accounts") + "/" + self.proposed_name # create a temporary account record object temporary_record = ConfigParser.SafeConfigParser() @@ -120,27 +215,29 @@ class User: self.record.set("account", "last_address", self.address) # the account files live here - account_path = muffconf.config_data.get("files", "accounts") + account_path = muffconf.get("files", "accounts") # the filename to which we'll write filename = account_path + "/" + self.name.lower() # if the directory doesn't exist, create it - # TODO: create account_path with 0700 perms try: if os.listdir(account_path): pass except: - os.mkdir(account_path, ) + os.mkdir(account_path) # open the user account file for writing - # TODO: create filename with 0600 perms record_file = file(filename, "w") # dump the account data to it self.record.write(record_file) # close the user account file + record_file.flush() record_file.close() + # set the permissions to 0600 + os.chmod(filename, 0600) + def show_menu(self): """Send the user their current menu.""" self.send(muffmenu.get_menu(self)) @@ -157,21 +254,11 @@ class User: # start with a newline, append the message, then end # with the optional eol string passed to this function - output = "$(eol)" + output + eol - - # replace eol markers with a crlf - # TODO: search for markers and replace from a dict - output = string.replace(output, "$(eol)", "\r\n") + # and the ansi escape to return to normal text + output = "\r\n" + output + eol + chr(27) + "[0m" - # replace display markers with ansi escapse sequences - output = string.replace(output, "$(bld)", chr(27)+"[1m") - output = string.replace(output, "$(nrm)", chr(27)+"[0m") - output = string.replace(output, "$(blk)", chr(27)+"[30m") - output = string.replace(output, "$(grn)", chr(27)+"[32m") - output = string.replace(output, "$(red)", chr(27)+"[31m") - - # the user's account name - output = string.replace(output, "$(account)", self.name) + # find and replace macros in the output + output = muffmisc.replace_macros(self, output) # wrap the text at 80 characters # TODO: prompt user for preferred wrap width @@ -191,3 +278,68 @@ class User: except: pass + def pulse(self): + """All the things to do to the user per increment.""" + + # if the world is terminating, disconnect + if muffvars.terminate_world: + self.state = "disconnecting" + self.menu_seen = False + + # show the user a menu as needed + self.show_menu() + + # disconnect users with the appropriate state + if self.state == "disconnecting": + self.quit() + + # the user is unique and not flagged to disconnect + else: + + # check for input and add it to the queue + self.enqueue_input() + + # there is input waiting in the queue + if self.input_queue: muffcmds.handle_user_input(self) + + def enqueue_input(self): + """Process and enqueue any new input.""" + + # check for some input + try: + input_data = self.connection.recv(1024) + except: + input_data = "" + + # we got something + if input_data: + + # tack this on to any previous partial + self.partial_input += input_data + + # separate multiple input lines + new_input_lines = self.partial_input.split("\n") + + # if input doesn't end in a newline, replace the + # held partial input with the last line of it + if not self.partial_input.endswith("\n"): + self.partial_input = new_input_lines.pop() + + # otherwise, chop off the extra null input and reset + # the held partial input + else: + new_input_lines.pop() + self.partial_input = "" + + # iterate over the remaining lines + for line in new_input_lines: + + # filter out non-printables + line = filter(lambda x: x>=' ' and x<='~', line) + + # strip off extra whitespace + line = line.strip() + + # put on the end of the queue + self.input_queue.append(line) +