From 393a3e5e73cf38e8743f057c5d592a9a9b39b6f2 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 15 Feb 2014 21:05:23 +0100 Subject: [PATCH] Added check to location setter that makes sure to stop a location-loop forming. This closes #454. --- src/objects/models.py | 54 +++++++++++++++++++++++++++++++++++++- src/utils/idmapper/base.py | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/objects/models.py b/src/objects/models.py index 0586d9168e..0ce22c6084 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -17,6 +17,7 @@ transparently through the decorating TypeClass. import traceback from django.db import models from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist from src.typeclasses.models import (TypedObject, TagHandler, NickHandler, AliasHandler, AttributeHandler) @@ -26,7 +27,8 @@ from src.commands.cmdsethandler import CmdSetHandler from src.commands import cmdhandler from src.scripts.scripthandler import ScriptHandler from src.utils import logger -from src.utils.utils import make_iter, to_str, to_unicode, variable_from_module +from src.utils.utils import (make_iter, to_str, to_unicode, + variable_from_module, dbref) from django.utils.translation import ugettext as _ @@ -180,6 +182,56 @@ class ObjectDB(TypedObject): _GA(self, "save")() cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del) + # location getsetter + def __location_get(self): + "Get location" + loc = _GA(_GA(self, "dbobj"), "db_location") + return _GA(loc, "typeclass") if loc else loc + + def __location_set(self, location): + "Set location, checking for loops and allowing dbref" + if isinstance(location, (basestring, int)): + # allow setting of #dbref + dbid = dbref(location, reqhash=False) + if dbid: + try: + location = ObjectDB.objects.get(id=dbid) + except ObjectDoesNotExist: + # maybe it is just a name that happens to look like a dbid + pass + try: + def is_loc_loop(loc, depth=0): + "Recursively traverse target location, trying to catch a loop." + if depth > 10: + return + elif loc == self: + raise RuntimeError + elif loc == None: + raise RuntimeWarning + return is_loc_loop(_GA(_GA(loc, "dbobj"), "db_location"), depth + 1) + try: + is_loc_loop(location) + except RuntimeWarning: + pass + # actually set the field + _SA(_GA(self, "dbobj"), "db_location", _GA(location, "dbobj") if location else location) + _GA(_GA(self, "dbobj"), "save")(update_fields=["db_location"]) + except RuntimeError: + errmsg = "Error: %s.location = %s creates a location loop." % (self.key, location) + logger.log_errmsg(errmsg) + raise RuntimeError(errmsg) + except Exception, e: + errmsg = "Error (%s): %s is not a valid location." % (str(e), location) + logger.log_errmsg(errmsg) + raise Exception(errmsg) + + def __location_del(self): + "Cleably delete the location reference" + _SA(_GA(self, "dbobj"), "db_location", None) + _GA(_GA(self, "dbobj"), "save")(upate_fields=["db_location"]) + location = property(__location_get, __location_set, __location_del) + + class Meta: "Define Django meta options" verbose_name = "Object" diff --git a/src/utils/idmapper/base.py b/src/utils/idmapper/base.py index 8817e3190f..c099e94e33 100755 --- a/src/utils/idmapper/base.py +++ b/src/utils/idmapper/base.py @@ -2,7 +2,7 @@ Django ID mapper Modified for Evennia by making sure that no model references -leave caching unexpectedly (no use if WeakRefs). +leave caching unexpectedly (no use of WeakRefs). Also adds cache_size() for monitoring the size of the cache. """