示例#1
0
def list_privmsgs(handle=None):
    """ Return all private messages for given user handle. """
    db_priv = DBProxy(PRIVDB)
    if handle:
        return db_priv.get(handle, set())
    # flatten list of [set(1, 2), set(3, 4)] to set(1, 2, 3, 4)
    return set([_idx for indices in db_priv.values() for _idx in indices])
示例#2
0
    def queue_for_network(self):
        """ Queue message for networks, hosting or sending. """
        log = logging.getLogger(__name__)

        # check all tags of message; if they match a message network,
        # either record for hosting servers, or schedule for delivery.
        for tag in self.tags:

            # server networks offered by this server,
            # message is for a network we host
            if tag in get_ini(section='msg', key='server_tags', split=True):
                with DBProxy('{0}trans'.format(tag)) as transdb:
                    self.body = u''.join((self.body, format_origin_line()))
                    self.save()
                    transdb[self.idx] = self.idx
                log.info(
                    '[{tag}] Stored for network (msgid {self.idx}).'.format(
                        tag=tag, self=self))

            # server networks this server is a member of,
            # message is for a another network, queue for delivery
            elif tag in get_ini(section='msg', key='network_tags', split=True):
                with DBProxy('{0}queues'.format(tag)) as queuedb:
                    queuedb[self.idx] = tag
                log.info('[{tag}] Message (msgid {self.idx}) queued '
                         'for delivery'.format(tag=tag, self=self))
示例#3
0
def list_privmsgs(handle=None):
    """ Return all private messages for given user handle. """
    db_priv = DBProxy(PRIVDB)
    if handle:
        return db_priv.get(handle, set())
    # flatten list of [set(1, 2), set(3, 4)] to set(1, 2, 3, 4)
    return set([_idx for indices in db_priv.values() for _idx in indices])
示例#4
0
文件: userbase.py 项目: hick/x84
    def get(self, key, default=None):
        # pylint: disable=C0111,
        #        Missing docstring
        from x84.bbs import ini
        log = logging.getLogger(__name__)
        adb = DBProxy(USERDB, 'attrs')

        if self.handle not in adb:
            if ini.CFG.getboolean('session', 'tap_db'):
                log.debug(
                    'User({!r}).get(key={!r}) returns default={!r}'.format(
                        self.handle, key, default))
            return default

        attrs = adb.get(self.handle, {})
        if key not in attrs:
            if ini.CFG.getboolean('session', 'tap_db'):
                log.debug(
                    'User({!r}.get(key={!r}) returns default={!r}'.format(
                        self.handle, key, default))
            return default

        if ini.CFG.getboolean('session', 'tap_db'):
            log.debug('User({!r}.get(key={!r}) returns value.'.format(
                self.handle, key))
        return attrs[key]
示例#5
0
 def delete(self):
     """ Delete group record, enforces referential integrity with Users. """
     udb = DBProxy(USERDB)
     for chk_user in self.members:
         user = udb[chk_user]
         if self.name in user.groups:
             user.group_del(self.name)
             user.save()
     del DBProxy(GROUPDB)[self.name]
示例#6
0
def list_msgs(tags=None):
    """ Return set of indices matching ``tags``, or all by default. """
    if tags is not None and 0 != len(tags):
        msgs = set()
        db_tag = DBProxy(TAGDB)
        for tag in (_tag for _tag in tags if _tag in db_tag):
            msgs.update(db_tag[tag])
        return msgs
    return set(int(key) for key in DBProxy(MSGDB).keys())
示例#7
0
文件: userbase.py 项目: hick/x84
 def delete(self):
     """
     Delete group record from database, and from .groups of any users.
     """
     udb = DBProxy(USERDB)
     for chk_user in self.members:
         user = udb[chk_user]
         if self.name in user.groups:
             user.group_del(self.name)
             user.save()
     del DBProxy(GROUPDB)[self.name]
示例#8
0
文件: userbase.py 项目: quastdog/x84
 def __delitem__(self, key):
     # pylint: disable=C0111,
     #        Missing docstring
     uadb = DBProxy(USERDB, 'attrs')
     uadb.acquire()
     attrs = uadb[self.handle]
     if key in attrs:
         attrs.__delitem__(key)
         uadb[self.handle] = attrs
     uadb.release()
     logger.info("del attr %r for user '%s'.", key, self.handle)
示例#9
0
def list_msgs(tags=None):
    """
    Return set of Msg keys matching 1 or more ``tags``, or all.
    """
    if tags is not None and 0 != len(tags):
        msgs = set()
        db_tag = DBProxy(TAGDB)
        for tag in (_tag for _tag in tags if _tag in db_tag):
            msgs.update(db_tag[tag])
        return msgs
    return set([int(key) for key in DBProxy(MSGDB).keys()])
示例#10
0
 def delete(self):
     """
     Remove user record from database, and as a member of any groups.
     """
     gdb = DBProxy(GROUPDB)
     for gname in self._groups:
         group = gdb[gname]
         if self.handle in group.members:
             group.remove(self.handle)
             group.save()
     del DBProxy(USERDB)[self.handle]
     logger.info("deleted user '%s'.", self.handle)
示例#11
0
 def delete(self):
     """ Remove user from user and group databases. """
     log = logging.getLogger(__name__)
     gdb = DBProxy(GROUPDB)
     with gdb:
         for gname in self._groups:
             group = gdb[gname]
             if self.handle in group.members:
                 group.remove(self.handle)
                 group.save()
     udb = DBProxy(USERDB)
     with udb:
         del udb[self.handle]
     log.info("deleted user '%s'.", self.handle)
示例#12
0
文件: userbase.py 项目: hick/x84
 def __delitem__(self, key):
     # pylint: disable=C0111,
     #        Missing docstring
     log = logging.getLogger(__name__)
     uadb = DBProxy(USERDB, 'attrs')
     with uadb:
         # retrieve attributes from uadb,
         attrs = uadb.get(self.handle, {})
         # delete attribute if exists
         if key in attrs:
             attrs.__delitem__(key)
             uadb[self.handle] = attrs
             log.info("User({!r}) delete attr {!r}."
                      .format(self.handle, key))
示例#13
0
文件: userbase.py 项目: hick/x84
 def __delitem__(self, key):
     # pylint: disable=C0111,
     #        Missing docstring
     log = logging.getLogger(__name__)
     uadb = DBProxy(USERDB, 'attrs')
     with uadb:
         # retrieve attributes from uadb,
         attrs = uadb.get(self.handle, {})
         # delete attribute if exists
         if key in attrs:
             attrs.__delitem__(key)
             uadb[self.handle] = attrs
             log.info("User({!r}) delete attr {!r}.".format(
                 self.handle, key))
示例#14
0
文件: userbase.py 项目: quastdog/x84
 def __setitem__(self, key, value):
     # pylint: disable=C0111,
     #        Missing docstring
     if self.handle == 'anonymous':
         logger.debug("set attr %r not possible for 'anonymous'", key)
         return
     adb = DBProxy(USERDB, 'attrs')
     adb.acquire()
     if not self.handle in adb:
         adb[self.handle] = dict([(key, value), ])
     else:
         attrs = adb[self.handle]
         attrs.__setitem__(key, value)
         adb[self.handle] = attrs
     adb.release()
     logger.info("set attr %r for user '%s'.", key, self.handle)
示例#15
0
文件: userbase.py 项目: hick/x84
def find_user(handle):
    """
    Given handle, discover and return matching database key case insensitively.
    The returned value may not be equal to the argument, or None if not found.
    """
    for key in DBProxy(USERDB).keys():
        if handle.lower() == key.decode('utf8').lower():
            return key
示例#16
0
def list_users():
    """
    Returns all user handles.

    :rtype: list
    :returns list of user handles.
    """
    return [handle.decode('utf8') for handle in DBProxy(USERDB).keys()]
示例#17
0
def get_user(handle):
    """
    Returns User record by handle.

    :rtype: User
    :returns: instance of :class:`User`
    """
    return DBProxy(USERDB)[handle]
示例#18
0
 def _apply_groups(self):
     """ Enforce referential integrity of user's groups. """
     log = logging.getLogger(__name__)
     gdb = DBProxy(GROUPDB)
     with gdb:
         for chk_grp in self._groups:
             if chk_grp not in gdb:
                 gdb[chk_grp] = Group(chk_grp, set([self.handle]))
                 log.info("created group {!r} for user {!r}.".format(
                     chk_grp, self.handle))
             # ensure membership in existing groups
             group = gdb[chk_grp]
             if self.handle not in group.members:
                 group.add(self.handle)
                 group.save()
         for gname, group in gdb.items():
             if gname not in self._groups and self.handle in group.members:
                 group.remove(self.handle)
                 group.save()
示例#19
0
def find_user(handle):
    """
    Discover and return matching user by ``handle``, case-insensitive.

    :returns: matching handle as str, or None if not found.
    :rtype: None or str.
    """
    for key in DBProxy(USERDB).keys():
        if handle.lower() == key.decode('utf8').lower():
            return key
示例#20
0
文件: userbase.py 项目: NuSkooler/x84
 def _apply_groups(self):
     """ Enforce referential integrity of user's groups. """
     log = logging.getLogger(__name__)
     gdb = DBProxy(GROUPDB)
     with gdb:
         for chk_grp in self._groups:
             if chk_grp not in gdb:
                 gdb[chk_grp] = Group(chk_grp, set([self.handle]))
                 log.info("created group {!r} for user {!r}."
                          .format(chk_grp, self.handle))
             # ensure membership in existing groups
             group = gdb[chk_grp]
             if self.handle not in group.members:
                 group.add(self.handle)
                 group.save()
         for gname, group in gdb.items():
             if gname not in self._groups and self.handle in group.members:
                 group.remove(self.handle)
                 group.save()
示例#21
0
 def save(self):
     """ Save user record to database. """
     log = logging.getLogger(__name__)
     assert isinstance(self._handle, unicode), ('handle must be unicode')
     assert len(self._handle) > 0, ('handle must be non-zero length')
     assert (None, None) != self._password, ('password must be set')
     assert self._handle != u'anonymous', ('anonymous may not be saved.')
     udb = DBProxy(USERDB)
     with udb:
         if 0 == len(udb) and self.is_sysop is False:
             log.warn('{!r}: First new user becomes sysop.'.format(
                 self.handle))
             self.group_add(u'sysop')
         is_new = self.handle not in udb
         udb[self.handle] = self
         if is_new:
             log.info("saved new user '%s'.", self.handle)
     adb = DBProxy(USERDB, 'attrs')
     with adb:
         if self.handle not in adb:
             adb[self.handle] = dict()
     self._apply_groups()
示例#22
0
文件: userbase.py 项目: hick/x84
 def save(self):
     """
     (re-)Save user record to database. Changes to user record to not
     automatically persist.  A call to the .save() method must be done.
     """
     log = logging.getLogger(__name__)
     assert type(self._handle) is unicode, ('handle must be unicode')
     assert len(self._handle) > 0, ('handle must be non-zero length')
     assert (None, None) != self._password, ('password must be set')
     assert self._handle != u'anonymous', ('anonymous may not be saved.')
     udb = DBProxy(USERDB)
     with udb:
         if 0 == len(udb) and self.is_sysop is False:
             log.warn('{!r}: First new user becomes sysop.'.format(
                 self.handle))
             self.group_add(u'sysop')
         udb[self.handle] = self
     adb = DBProxy(USERDB, 'attrs')
     with adb:
         if self.handle not in adb:
             adb[self.handle] = dict()
     self._apply_groups()
     log.info("saved user '%s'.", self.handle)
示例#23
0
文件: userbase.py 项目: quastdog/x84
 def get(self, key, default=None):
     # pylint: disable=C0111,
     #        Missing docstring
     adb = DBProxy(USERDB, 'attrs')
     adb.acquire()
     if not self.handle in adb:
         logger.debug(
             '%r GET %r: default; missing attrs.', self.handle, key)
         val = default
     else:
         attrs = adb[self.handle]
         if not key in attrs:
             logger.debug('%r GET %r: default', self.handle, key)
             val = default
         else:
             logger.debug('%r GET %r%s.' % (
                 self.handle, key,
                 ' (size: %d)' % (len(attrs[key]),)
                 if hasattr(attrs[key], '__len__')
                 else '(1)'))
             val = attrs[key]
     adb.release()
     return val
示例#24
0
文件: userbase.py 项目: hick/x84
    def get(self, key, default=None):
        # pylint: disable=C0111,
        #        Missing docstring
        from x84.bbs import ini
        log = logging.getLogger(__name__)
        adb = DBProxy(USERDB, 'attrs')

        if self.handle not in adb:
            if ini.CFG.getboolean('session', 'tap_db'):
                log.debug('User({!r}).get(key={!r}) returns default={!r}'
                          .format(self.handle, key, default))
            return default

        attrs = adb.get(self.handle, {})
        if key not in attrs:
            if ini.CFG.getboolean('session', 'tap_db'):
                log.debug('User({!r}.get(key={!r}) returns default={!r}'
                          .format(self.handle, key, default))
            return default

        if ini.CFG.getboolean('session', 'tap_db'):
            log.debug('User({!r}.get(key={!r}) returns value.'
                      .format(self.handle, key))
        return attrs[key]
示例#25
0
文件: userbase.py 项目: quastdog/x84
 def save(self):
     """
     (re-)Save user record to databases. Changes to user record to not
     automaticly persist. a call to the .save method must be done.
     """
     assert type(self._handle) is unicode, ('handle must be unicode')
     assert len(self._handle) > 0, ('handle must be non-zero length')
     assert (None, None) != self._password, ('password must be set')
     assert self._handle != u'anonymous', (
         'anonymous user my not be saved.')
     udb = DBProxy(USERDB)
     udb.acquire()
     if 0 == len(udb) and self.is_sysop is False:
         logger.warn('%s: First new user becomes sysop.', self.handle)
         self.group_add(u'sysop')
     udb[self.handle] = self
     adb = DBProxy(USERDB, 'attrs')
     adb.acquire()
     if not self.handle in adb:
         adb[self.handle] = dict()
     adb.release()
     self._apply_groups(DBProxy(GROUPDB))
     udb.release()
     logger.info("saved user '%s'.", self.handle)
示例#26
0
 def __delitem__(self, key):
     # pylint: disable=C0111,
     #        Missing docstring
     uadb = DBProxy(USERDB, 'attrs')
     uadb.acquire()
     attrs = uadb[self.handle]
     if key in attrs:
         attrs.__delitem__(key)
         uadb[self.handle] = attrs
     uadb.release()
     logger.info("del attr %r for user '%s'.", key, self.handle)
示例#27
0
文件: userbase.py 项目: hick/x84
    def __setitem__(self, key, value):
        # pylint: disable=C0111,
        #        Missing docstring
        log = logging.getLogger(__name__)
        adb = DBProxy(USERDB, 'attrs')

        if self.handle == 'anonymous':
            log.debug("set attr {!r} not possible for 'anonymous'".format(key))
            return

        with adb:
            if self.handle not in adb:
                adb[self.handle] = dict([
                    (key, value),
                ])
            else:
                attrs = adb[self.handle]
                attrs.__setitem__(key, value)
                adb[self.handle] = attrs
        log.debug("set attr {!r} for user {!r}.".format(key, self.handle))
示例#28
0
 def __setitem__(self, key, value):
     # pylint: disable=C0111,
     #        Missing docstring
     if self.handle == 'anonymous':
         logger.debug("set attr %r not possible for 'anonymous'", key)
         return
     adb = DBProxy(USERDB, 'attrs')
     adb.acquire()
     if not self.handle in adb:
         adb[self.handle] = dict([
             (key, value),
         ])
     else:
         attrs = adb[self.handle]
         attrs.__setitem__(key, value)
         adb[self.handle] = attrs
     adb.release()
     logger.info("set attr %r for user '%s'.", key, self.handle)
示例#29
0
 def get(self, key, default=None):
     # pylint: disable=C0111,
     #        Missing docstring
     adb = DBProxy(USERDB, 'attrs')
     adb.acquire()
     if not self.handle in adb:
         logger.debug('%r GET %r: default; missing attrs.', self.handle,
                      key)
         val = default
     else:
         attrs = adb[self.handle]
         if not key in attrs:
             logger.debug('%r GET %r: default', self.handle, key)
             val = default
         else:
             logger.debug('%r GET %r%s.' %
                          (self.handle, key, ' (size: %d)' %
                           (len(attrs[key]), ) if hasattr(
                               attrs[key], '__len__') else '(1)'))
             val = attrs[key]
     adb.release()
     return val
示例#30
0
文件: userbase.py 项目: hick/x84
 def save(self):
     """
     Save group record to database.
     """
     DBProxy(GROUPDB)[self.name] = self
示例#31
0
 def save(self):
     """
     (re-)Save user record to databases. Changes to user record to not
     automaticly persist. a call to the .save method must be done.
     """
     assert type(self._handle) is unicode, ('handle must be unicode')
     assert len(self._handle) > 0, ('handle must be non-zero length')
     assert (None, None) != self._password, ('password must be set')
     assert self._handle != u'anonymous', (
         'anonymous user may not be saved.')
     udb = DBProxy(USERDB)
     udb.acquire()
     if 0 == len(udb) and self.is_sysop is False:
         logger.warn('%s: First new user becomes sysop.', self.handle)
         self.group_add(u'sysop')
     udb[self.handle] = self
     adb = DBProxy(USERDB, 'attrs')
     adb.acquire()
     if not self.handle in adb:
         adb[self.handle] = dict()
     adb.release()
     self._apply_groups(DBProxy(GROUPDB))
     udb.release()
     logger.info("saved user '%s'.", self.handle)
示例#32
0
    def save(self, send_net=True, ctime=None):
        """
        Save message in 'Msgs' sqlite db, and record index in 'tags' db.
        """
        from x84.bbs.ini import CFG
        from x84.bbs import getsession

        session = getsession()
        use_session = bool(session is not None)
        log = logging.getLogger(__name__)
        new = self.idx is None or self._stime is None

        # persist message record to MSGDB
        db_msg = DBProxy(MSGDB, use_session=use_session)
        with db_msg:
            if new:
                self.idx = max(map(int, db_msg.keys()) or [-1]) + 1
                if ctime is not None:
                    self._ctime = self._stime = ctime
                else:
                    self._stime = datetime.datetime.now()
                new = True
            db_msg['%d' % (self.idx, )] = self

        # persist message idx to TAGDB
        db_tag = DBProxy(TAGDB, use_session=use_session)
        with db_tag:
            for tag in db_tag.keys():
                msgs = db_tag[tag]
                if tag in self.tags and self.idx not in msgs:
                    msgs.add(self.idx)
                    db_tag[tag] = msgs
                    log.debug("msg {self.idx} tagged '{tag}'".format(self=self,
                                                                     tag=tag))
                elif tag not in self.tags and self.idx in msgs:
                    msgs.remove(self.idx)
                    db_tag[tag] = msgs
                    log.info("msg {self.idx} removed tag '{tag}'".format(
                        self=self, tag=tag))
            for tag in [_tag for _tag in self.tags if _tag not in db_tag]:
                db_tag[tag] = set([self.idx])

        # persist message as child to parent;
        if not hasattr(self, 'parent'):
            self.parent = None
        assert self.parent not in self.children
        if self.parent is not None:
            parent_msg = get_msg(self.parent)
            if self.idx != parent_msg.idx:
                parent_msg.children.add(self.idx)
                parent_msg.save()
            else:
                log.error('Parent idx same as message idx; stripping')
                self.parent = None
                with db_msg:
                    db_msg['%d' % (self.idx)] = self

        if send_net and new and CFG.has_option('msg', 'network_tags'):
            self.queue_for_network()

        log.info(
            u"saved {new}{public}msg {post}, addressed to '{self.recipient}'.".
            format(new='new ' if new else '',
                   public='public ' if 'public' in self.tags else '',
                   post='post' if self.parent is None else 'reply',
                   self=self))
示例#33
0
def get_msg(idx=0):
    """ Return Msg record instance by index ``idx``. """
    return DBProxy(MSGDB)['%d' % int(idx)]
示例#34
0
文件: userbase.py 项目: hick/x84
def get_user(handle):
    """
    Returns User record, keyed by handle.
    """
    return DBProxy(USERDB)[handle]
示例#35
0
def list_tags():
    """ Return set of available tags. """
    return [_tag.decode('utf8') for _tag in DBProxy(TAGDB).keys()]
示例#36
0
    def save(self):
        """
        Save message in 'Msgs' sqlite db, and record index in 'tags' db.
        """
        db_msg = DBProxy(MSGDB)
        new = self.idx is None or self._stime is None
        # persist message record to MSGDB
        db_msg.acquire()
        if new:
            self.idx = max([int(key) for key in db_msg.keys()] or [-1]) + 1
            self._stime = datetime.datetime.now()
            new = True
        db_msg['%d' % (self.idx,)] = self
        db_msg.release()

        # persist message idx to TAGDB
        db_tag = DBProxy(TAGDB)
        db_tag.acquire()
        for tag, msgs in db_tag.iteritems():
            if tag in self.tags and not self.idx in msgs:
                msgs.add(self.idx)
                db_tag[tag] = msgs
                logger.info(u"msg %s tagged '%s'", self.idx, tag,)
            elif tag not in self.tags and self.idx in msgs:
                msgs.remove(self.idx)
                db_tag[tag] = msgs
                logger.info(u"msg %s untagged '%s'", self.idx, tag,)
        for tag in [_tag for _tag in self.tags if not _tag in db_tag]:
            db_tag[tag] = set([self.idx])
        db_tag.release()

        # persist message as child to parent;
        if not hasattr(self, 'parent'):
            self.parent = None
        assert self.parent not in self.children
        if self.parent is not None:
            parent_msg = get_msg(self.parent)
            if not hasattr(parent_msg, 'children'):
                parent_msg.children = set(
                )  # intermediary conversion; deleteme
            parent_msg.children.add(self.idx)
            parent_msg.save()

        logger.info(u"saved %s%s%s, addressed to '%s'.",
                    'new ' if new else u'',
                    'public ' if 'public' in self.tags else u'',
                    'message' if self.parent is None else u'reply',
                    self.recipient,)
示例#37
0
文件: userbase.py 项目: hick/x84
 def __getitem__(self, key):
     # pylint: disable=C0111,
     #        Missing docstring
     return DBProxy(USERDB, 'attrs')[self.handle][key]
示例#38
0
文件: msgbase.py 项目: ztaylor/x84
    def save(self, send_net=True, ctime=None):
        """
        Save message in 'Msgs' sqlite db, and record index in 'tags' db.
        """
        from x84.bbs.ini import CFG
        from x84.bbs import getsession

        session = getsession()
        use_session = bool(session is not None)
        log = logging.getLogger(__name__)
        new = self.idx is None or self._stime is None

        # persist message record to MSGDB
        db_msg = DBProxy(MSGDB, use_session=use_session)
        with db_msg:
            if new:
                self.idx = max([int(key) for key in db_msg.keys()] or [-1]) + 1
                if ctime is not None:
                    self._ctime = self._stime = ctime
                else:
                    self._stime = datetime.datetime.now()
                new = True
            db_msg['%d' % (self.idx,)] = self

        # persist message idx to TAGDB
        db_tag = DBProxy(TAGDB, use_session=use_session)
        with db_tag:
            for tag in db_tag.keys():
                msgs = db_tag[tag]
                if tag in self.tags and self.idx not in msgs:
                    msgs.add(self.idx)
                    db_tag[tag] = msgs
                    log.debug("msg {self.idx} tagged '{tag}'"
                              .format(self=self, tag=tag))
                elif tag not in self.tags and self.idx in msgs:
                    msgs.remove(self.idx)
                    db_tag[tag] = msgs
                    log.info("msg {self.idx} removed tag '{tag}'"
                             .format(self=self, tag=tag))
            for tag in [_tag for _tag in self.tags if _tag not in db_tag]:
                db_tag[tag] = set([self.idx])

        # persist message as child to parent;
        if not hasattr(self, 'parent'):
            self.parent = None
        assert self.parent not in self.children
        if self.parent is not None:
            parent_msg = get_msg(self.parent)
            if self.idx != parent_msg.idx:
                parent_msg.children.add(self.idx)
                parent_msg.save()
            else:
                log.error('Parent idx same as message idx; stripping')
                self.parent = None
                with db_msg:
                    db_msg['%d' % (self.idx)] = self

        if send_net and new and CFG.has_option('msg', 'network_tags'):
            self.queue_for_network()

        log.info(
            u"saved {new}{public}msg {post}, addressed to '{self.recipient}'."
            .format(new='new ' if new else '',
                    public='public ' if 'public' in self.tags else '',
                    post='post' if self.parent is None else 'reply',
                    self=self))
示例#39
0
    def save(self, send_net=True, ctime=None):
        """
        Save message to database, recording 'tags' db.

        As a side-effect, it may queue message for delivery to
        external systems, when configured.
        """
        log = logging.getLogger(__name__)
        session = getsession()
        use_session = bool(session is not None)
        new = self.idx is None or self._stime is None

        # persist message record to MSGDB
        with DBProxy(MSGDB, use_session=use_session) as db_msg:
            if new:
                self.idx = max(map(int, db_msg.keys()) or [-1]) + 1
                if ctime is not None:
                    self._ctime = self._stime = ctime
                else:
                    self._stime = datetime.datetime.now()
                new = True
            db_msg['%d' % (self.idx, )] = self

        # persist message idx to TAGDB
        with DBProxy(TAGDB, use_session=use_session) as db_tag:
            for tag in db_tag.keys():
                msgs = db_tag[tag]
                if tag in self.tags and self.idx not in msgs:
                    msgs.add(self.idx)
                    db_tag[tag] = msgs
                    log.debug("msg {self.idx} tagged '{tag}'".format(self=self,
                                                                     tag=tag))
                elif tag not in self.tags and self.idx in msgs:
                    msgs.remove(self.idx)
                    db_tag[tag] = msgs
                    log.info("msg {self.idx} removed tag '{tag}'".format(
                        self=self, tag=tag))
            for tag in [_tag for _tag in self.tags if _tag not in db_tag]:
                db_tag[tag] = set([self.idx])

        # persist message as child to parent;
        assert self.parent not in self.children, ('circular reference',
                                                  self.parent, self.children)
        if self.parent is not None:
            try:
                parent_msg = get_msg(self.parent)
            except KeyError:
                log.warn('Child message {0}.parent = {1}: '
                         'parent does not exist!'.format(
                             self.idx, self.parent))
            else:
                if self.idx != parent_msg.idx:
                    parent_msg.children.add(self.idx)
                    parent_msg.save()
                else:
                    log.error('Parent idx same as message idx; stripping')
                    self.parent = None
                    with db_msg:
                        db_msg['%d' % (self.idx)] = self

        # persist message record to PRIVDB
        if 'public' not in self.tags:
            with DBProxy(PRIVDB, use_session=use_session) as db_priv:
                db_priv[self.recipient] = (db_priv.get(self.recipient, set())
                                           | set([self.idx]))

        # if either any of 'server_tags' or 'network_tags' are enabled,
        # then queue for potential delivery.
        if send_net and new and (get_ini(section='msg', key='network_tags')
                                 or get_ini(section='msg', key='server_tags')):
            self.queue_for_network()

        log.info(u"saved {new} {public_or_private} {message_or_reply}"
                 u", addressed to '{self.recipient}'.".format(
                     new='new ' if new else '',
                     public_or_private=('public' if 'public' in self.tags else
                                        'private'),
                     message_or_reply=('message'
                                       if self.parent is None else 'reply'),
                     self=self))