-Copyright (c) 2005, 2006 Jeremy Stanley <fungi@yuggoth.org>. All rights
-reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- - Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- - Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+Copyright (c) 2004-2008 Jeremy Stanley <fungi@yuggoth.org>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Core objects for the mudpy engine."""
-# Copyright (c) 2005, 2006 Jeremy Stanley <fungi@yuggoth.org>. All rights
-# reserved. Licensed per terms in the LICENSE file distributed with this
-# software.
+# Copyright (c) 2004-2008 Jeremy Stanley <fungi@yuggoth.org>. Permission
+# to use, copy, modify, and distribute this software is granted under
+# terms provided in the LICENSE file distributed with this software.
# import some things we need
from ConfigParser import RawConfigParser
self.error = ""
self.input_queue = []
self.last_address = ""
+ self.last_input = universe.get_time()
self.menu_choices = {}
self.menu_seen = False
self.negotiation_pause = 0
self.connection.close()
self.remove()
+ def check_idle(self):
+ """Warn or disconnect idle users as appropriate."""
+ idletime = universe.get_time() - self.last_input
+ linkdead_dict = universe.categories["internal"]["time"].getdict("linkdead")
+ if self.state in linkdead_dict: linkdead_state = self.state
+ else: linkdead_state = "default"
+ if idletime > linkdead_dict[linkdead_state]:
+ self.send("$(eol)$(red)You've done nothing for far too long... goodbye!$(nrm)$(eol)", flush=True, add_prompt=False)
+ logline = "Disconnecting "
+ if self.account and self.account.get("name"): logline += self.account.get("name")
+ else: logline += "an unknown user"
+ logline += " after idling too long in a " + self.state + " state."
+ log(logline, 2)
+ self.state = "disconnecting"
+ self.menu_seen = False
+ idle_dict = universe.categories["internal"]["time"].getdict("idle")
+ if self.state in idle_dict: idle_state = self.state
+ else: idle_state = "default"
+ if idletime == idle_dict[idle_state]:
+ self.send("$(eol)$(red)If you continue to be unproductive, you'll be shown the door...$(nrm)$(eol)")
+
def reload(self):
"""Save, load a new user and relocate the connection."""
self.state = "disconnecting"
self.menu_seen = False
+ # check for an idle connection and act appropriately
+ else: self.check_idle()
+
# if output is paused, decrement the counter
if self.state == "initial":
if self.negotiation_pause: self.negotiation_pause -= 1
# ignoring color escape sequences
relative_position = 0
+ # whether the current character is part of a telnet IAC sequence
+ iac_counter = 0
+
# whether the current character is part of a color escape sequence
escape = False
# iterate over each character from the begining of the text
for each_character in text:
+ # the current character is the telnet IAC character
+ if each_character == IAC and not iac_counter:
+ iac_counter = 2
+
+ # the current character is within an IAC sequence
+ elif iac_counter:
+
+ # the current character is another IAC,
+ # terminating the sequence
+ if each_character == IAC:
+ iac_counter = 0
+
+ # otherwise, decrement the IAC counter
+ else:
+ iac_counter -= 1
+
# the current character is the escape character
- if each_character == chr(27):
+ elif each_character == chr(27) and not escape:
escape = True
# the current character is within an escape sequence
elif escape:
# the current character is m, which terminates the
- # current escape sequence
+ # escape sequence
if each_character == "m":
escape = False
# the current character meets the requested maximum line width,
# so we need to backtrack and find a space at which to wrap
- elif relative_position == width:
+ elif relative_position == width and not each_character == "\r":
# distance of the current character examined from the
# relative position
def replace_macros(user, text, is_input=False):
"""Replaces macros in text output."""
+ # third person pronouns
+ pronouns = {
+ "female": { "obj": "her", "pos": "hers", "sub": "she" },
+ "male": { "obj": "him", "pos": "his", "sub": "he" },
+ "neuter": { "obj": "it", "pos": "its", "sub": "it" }
+ }
+
+ # a dict of replacement macros
+ macros = {
+ "eol": "\r\n",
+ "bld": chr(27) + "[1m",
+ "nrm": chr(27) + "[0m",
+ "blk": chr(27) + "[30m",
+ "blu": chr(27) + "[34m",
+ "cyn": chr(27) + "[36m",
+ "grn": chr(27) + "[32m",
+ "mgt": chr(27) + "[35m",
+ "red": chr(27) + "[31m",
+ "yel": chr(27) + "[33m",
+ }
+
+ # add dynamic macros where possible
+ if user.account:
+ account_name = user.account.get("name")
+ if account_name:
+ macros["account"] = account_name
+ if user.avatar:
+ avatar_gender = user.avatar.get("gender")
+ if avatar_gender:
+ macros["tpop"] = pronouns[avatar_gender]["obj"]
+ macros["tppp"] = pronouns[avatar_gender]["pos"]
+ macros["tpsp"] = pronouns[avatar_gender]["sub"]
+
# loop until broken
while True:
- # third person pronouns
- pronouns = {
- "female": { "obj": "her", "pos": "hers", "sub": "she" },
- "male": { "obj": "him", "pos": "his", "sub": "he" },
- "neuter": { "obj": "it", "pos": "its", "sub": "it" }
- }
-
- # a dict of replacement macros
- macros = {
- "$(eol)": "\r\n",
- "$(bld)": chr(27) + "[1m",
- "$(nrm)": chr(27) + "[0m",
- "$(blk)": chr(27) + "[30m",
- "$(blu)": chr(27) + "[34m",
- "$(cyn)": chr(27) + "[36m",
- "$(grn)": chr(27) + "[32m",
- "$(mgt)": chr(27) + "[35m",
- "$(red)": chr(27) + "[31m",
- "$(yel)": chr(27) + "[33m",
- }
-
- # add dynamic macros where possible
- if user.account:
- account_name = user.account.get("name")
- if account_name:
- macros["$(account)"] = account_name
- if user.avatar:
- avatar_gender = user.avatar.get("gender")
- if avatar_gender:
- macros["$(tpop)"] = pronouns[avatar_gender]["obj"]
- macros["$(tppp)"] = pronouns[avatar_gender]["pos"]
- macros["$(tpsp)"] = pronouns[avatar_gender]["sub"]
-
# find and replace per the macros dict
macro_start = text.find("$(")
if macro_start == -1: break
macro_end = text.find(")", macro_start) + 1
- macro = text[macro_start:macro_end]
+ macro = text[macro_start+2:macro_end-1]
if macro in macros.keys():
- text = text.replace(macro, macros[macro])
+ replacement = macros[macro]
+
+ # this is how we handle local file inclusion (dangerous!)
+ elif macro.startswith("inc:"):
+ incfile = path_join(universe.startdir, macro[4:])
+ if exists(incfile):
+ incfd = file(incfile)
+ replacement = ""
+ for line in incfd:
+ if line.endswith("\n") and not line.endswith("\r\n"):
+ line = line.replace("\n", "\r\n")
+ replacement += line
+ # lose the trailing eol
+ replacement = replacement[:-2]
+ else:
+ replacement = ""
+ log("Couldn't read included " + incfile + " file.", 6)
# if we get here, log and replace it with null
else:
- text = text.replace(macro, "")
+ replacement = ""
if not is_input:
log("Unexpected replacement macro " + macro + " encountered.", 6)
+ # and now we act on the replacement
+ text = text.replace("$(" + macro + ")", replacement)
+
# replace the look-like-a-macro sequence
text = text.replace("$_(", "$(")
# since we got input, flag that the menu/prompt needs to be redisplayed
user.menu_seen = False
+ # update the last_input timestamp while we're at it
+ user.last_input = universe.get_time()
+
def generic_menu_handler(user):
"""A generic menu choice handler."""