Пример #1
0
    def __init__(self, msn, to_invite = (), **k):
        Events.EventMixin.__init__(self)
        self.Bridge = None
        self.client = msn
        self._to_invite = set(to_invite)
        self._closed = False

        self._waschat = len(self._to_invite) > 1
        Conversation.__init__(self, msn)

        self.check_invite_list()

        self.protocol.register_conv(self)

        log.info("Added %r to msn's conversation list", self)

        self.buddies = {}
        self.typing_status = ObservableDict()

        self._pending_invite_callbacks = {}

        self.room_list.append(self.self_buddy)
        if self.ischat:
            cb = self.client.circle_buddies[self._chatbuddy]
            cb.sem.bind("resource_release", self._circle_unused)
            cb.sem.acquire()
            self.client.ns.JoinCircleConversation(self._chatbuddy)
            for bname in cb.buddy_names:
                if bname.lower().startswith(str(cb.guid).lower()):
                    continue
                self.buddy_join(bname)
Пример #2
0
        def __setitem__(self, key, val):
            set_func = getattr(self.localdefaults, 'set_%s' % key.replace('.', '_'), None)
            if set_func is not None:
                log.info('calling localpref setter function: %r', set_func)
                val = set_func(val)

            SavingDictBase.__setitem__(self, key, val)
            self.save(self)
Пример #3
0
        def __setitem__(self, key, val):
            set_func = getattr(self.localdefaults,
                               'set_%s' % key.replace('.', '_'), None)
            if set_func is not None:
                log.info('calling localpref setter function: %r', set_func)
                val = set_func(val)

            SavingDictBase.__setitem__(self, key, val)
            self.save(self)
Пример #4
0
    def __init__(self, protocol):
        '''
        MSNBuddies(msnobj)

        Create a new MSNBuddies dictionary with msnobj as the owner of all
        buddies that will be in this dictionary

        @param protocol    this dictionaries owner
        '''
        ObservableDict.__init__(self)
        self.protocol = protocol
Пример #5
0
def get_prefs():
    try:
        from common import profile
        return profile.prefs
    except ImportError:
        from util.observe import ObservableDict
        return ObservableDict()
Пример #6
0
    def _incoming_blob_notifications(self, newdata):
        def fix_underscore(d):
            for key in d.keys()[:]:
                if key and '_' in key:
                    d[key.replace('_', '.')] = d.pop(key)

        if not hasattr(self, 'notifications'):
            self.notifications = ObservableDict()
        else:
            fix_underscore(self.notifications)

        fix_underscore(newdata)

        self.notifications.update(newdata)
        import common.notifications

        # for any notification keys that exist in YAML, but not in the users
        # blob, add them with the values in the YAML 'default' key
        ni = common.notifications.get_notification_info()
        base = self.notifications[None]
        for k in ni:
            if k in base:
                continue

            try:
                defaults = ni[k].get('default', {})
                base[k] = [dict(reaction=v) for v in defaults.get('reaction', ())]
            except Exception:
                traceback.print_exc()
                continue

        import hooks
        hooks.notify('digsby.notifications.changed')
Пример #7
0
    def __init__(self, username, password, hub, server=None, **options):
        common.protocol.__init__(self, username, password, hub)

        self.root_group = Group('Root', self, 'Root')
        self.buddies = ObservableDict()
        self.self_buddy = FakeBuddy('self', self)
        self.buddies['self'] = self.self_buddy
        self.conversations = {}
Пример #8
0
    def __getitem__(self, buddy_name):
        if not isinstance(buddy_name, (str,unicode)):
            raise TypeError('buddy name must be a string (you gave a %s)' % \
                            (type(buddy_name)))

        buddy_name = str(buddy_name).lower()

        try:
            return ObservableDict.__getitem__(self, buddy_name)
        except (KeyError,):
            return self.setdefault(buddy_name, IrcBuddy(buddy_name, self.protocol))
Пример #9
0
    def __getitem__(self, buddy_name):
        if not isinstance(buddy_name, (str, unicode)):
            raise TypeError('buddy name must be a string (you gave a %s)' % \
                            (type(buddy_name)))

        buddy_name = str(buddy_name).lower()

        try:
            return ObservableDict.__getitem__(self, buddy_name)
        except (KeyError, ):
            return self.setdefault(buddy_name,
                                   IrcBuddy(buddy_name, self.protocol))
Пример #10
0
        def __getitem__(self, key):
            try:
                return SavingDictBase.__getitem__(self, key)
            except KeyError, e:
                try:
                    v = getattr(self.localdefaults, key.replace('.', '_'))
                except AttributeError:
                    try:
                        get_func = getattr(self.localdefaults, 'get_%s' % key.replace('.', '_'))
#                        log.info('calling localpref getter function: %r', get_func)
                        v = get_func()
                    except AttributeError:
                        raise e

                return v() if callable(v) else v
Пример #11
0
    def __init__(self):
        Observable.__init__(self)

        bud = MockBuddy('fakebuddy')

        self.name = 'fakebuddy'
        self.me = MockBuddy('digsby007')

        self.room_list = ObservableList([bud, self.me])
        self.typing_status = ObservableDict()
        self.buddy = bud
        self.messages = Queue()
        self.protocol = Storage(self_buddy=self.me,
                                buddies={'digsby007': self.me})
        self.ischat = False
Пример #12
0
        def __getitem__(self, key):
            try:
                return SavingDictBase.__getitem__(self, key)
            except KeyError, e:
                try:
                    v = getattr(self.localdefaults, key.replace('.', '_'))
                except AttributeError:
                    try:
                        get_func = getattr(self.localdefaults,
                                           'get_%s' % key.replace('.', '_'))
                        #                        log.info('calling localpref getter function: %r', get_func)
                        v = get_func()
                    except AttributeError:
                        raise e

                return v() if callable(v) else v
Пример #13
0
 def __delitem__(self, key):
     SavingDictBase.__delitem__(self, key)
     self.save(self)
Пример #14
0
 def __init__(self, save_func, *a, **k):
     self.save = save_func
     SavingDictBase.__init__(self, *a, **k)
Пример #15
0
def testapp(pypath = None, appname = 'Digsby', skinname = 'default', prefs = None, username = '******',
            on_message = lambda message: None,
            plugins = True, logging = True):
    'A test application framework for test __main__s.'


    if wxMSW: preload_comctrls()

    import gettext, os.path

    import options
    sys.opts, _args = options.parser.parse_args()
    import logextensions

    digsbysite.COLORIZE_EXCEPTIONS = sys.opts.console_color

    # Install gui elements
    gettext.install(appname, unicode=True)

    from bootstrap import install_N_
    install_N_()

    # Create the app
    app = TestApp()
    app.SetAppName(appname)

    # initialize stdpaths
    from stdpaths import init
    init()

    if wxMSW:
        import gui.native.win.winutil as winutil
        winutil.disable_callback_filter()

    from gui import skin
    from gui.toolbox import setuplogging
    if logging:
        import logging
        setuplogging(level=logging.INFO)


    # make wxLogError go to stderr, not popup dialogs
    wx.Log.SetActiveTarget(wx.LogStderr())

    app.PreShutdown = Delegate()

    if pypath is None:
        pypath = discover_digsby_root()

    sys.path.insert(0, pypath)

    skin.set_resource_paths([
                             util.program_dir() / 'res', # Apparently this has to be first?
                             stdpaths.userdata,
                             stdpaths.config,
                             ])

    if plugins:
        from main import init_plugins
        app.plugins = init_plugins()
    else:
        app.plugins = []


    skin.skininit(os.path.join(pypath, 'res'), skinname = skinname)

    from util.threads.threadpool import ThreadPool
    ThreadPool(5)

    from prefs.prefsdata import flatten
    import syck
    from util.observe import ObservableDict


    prefs_path = os.path.join(pypath, 'res', 'defaults.yaml')

    prefs = ObservableDict(prefs) if prefs is not None else ObservableDict()
    prefs.update({'appearance.skin': skinname,
                  'appearance.variant': None,
                  'debug.shell.font': shellfont()})
    import common
    common.set_active_prefs(prefs, {})

    from util.observe import ObservableDict

    sys.modules['digsbyprofile'] = Storage()
    import digsbyprofile
    from common.notifications import default_notifications
    p = digsbyprofile.profile = Storage(name     = username,
                                    username = username,
                                    prefs    = prefs,
                                    on_message = on_message,
                                    notifications = default_notifications)



    f = file(prefs_path)
    defaults = Storage(flatten(syck.load(f)))
    f.close()
    user = ObservableDict(defaults)
    user.update(prefs)

    from prefs.prefsdata import localprefs
    import prefs
    p.defaultprefs = prefs.defaultprefs()
    p.localprefs = localprefs()

    import common
    common.setfakeprefs(user)

    def toggle_prefs(user=user, defaults=defaults):
        import prefs
        prefs.edit(user, defaults, None)

    def toggle_crust(app=app):
        if not getattr(app, 'crust', None):
            import gui.shell
            wins =  wx.GetTopLevelWindows()
            parent = wins[0] if wins else None
            app.crust = gui.shell.PyCrustFrame(None)
            if parent is not None:
                parent.crust = app.crust
            app.crust.Bind(wx.EVT_CLOSE, lambda evt: app.Exit())
        app.crust.toggle_shown()

        # keyboard focus goes to shell prompt
        if app.crust.IsShown():
            app.crust.crust.SetFocus()

    def on_key(e):
        code = e.GetKeyCode()
        if code == wx.WXK_F11:
            toggle_prefs()
        elif code == wx.WXK_F12:
            toggle_crust()
        elif code == wx.WXK_F5:
            from gui import skin
            skin.reload()
        else:
            e.Skip()

    app.Bind(wx.EVT_KEY_DOWN, on_key)
    app.toggle_crust = toggle_crust

    from main import initialize_webkit, SetStatusPrompt
    initialize_webkit()
    app.SetStatusPrompt = SetStatusPrompt

    return app
Пример #16
0
 def __init__(self, protocol):
     ObservableDict.__init__(self)
     self.protocol = protocol
Пример #17
0
class ODictNetData(NetData):
    fallback = lambda self: ObservableDict()
    basetype = dict
Пример #18
0
class MSNP21Conversation(Conversation, Events.EventMixin):
    events = Events.EventMixin.events | set((
        'buddy_join',
        'buddy_leave',
        'AllContactsLeft',
        'MessageAckReceived',
        'ServerErrorReceived',
    ))
    def _repr(self):
        return ' chatbuddy=%r' % getattr(self, '_chatbuddy', None)

    def __init__(self, msn, to_invite = (), **k):
        Events.EventMixin.__init__(self)
        self.Bridge = None
        self.client = msn
        self._to_invite = set(to_invite)
        self._closed = False

        self._waschat = len(self._to_invite) > 1
        Conversation.__init__(self, msn)

        self.check_invite_list()

        self.protocol.register_conv(self)

        log.info("Added %r to msn's conversation list", self)

        self.buddies = {}
        self.typing_status = ObservableDict()

        self._pending_invite_callbacks = {}

        self.room_list.append(self.self_buddy)
        if self.ischat:
            cb = self.client.circle_buddies[self._chatbuddy]
            cb.sem.bind("resource_release", self._circle_unused)
            cb.sem.acquire()
            self.client.ns.JoinCircleConversation(self._chatbuddy)
            for bname in cb.buddy_names:
                if bname.lower().startswith(str(cb.guid).lower()):
                    continue
                self.buddy_join(bname)

    def check_invite_list(self):
        to_invite = tuple(self._to_invite)

        if len(to_invite) >= 1:
            self._chatbuddy = to_invite[0]
            if len(to_invite) == 1:
                self._chat_target_name = to_invite[0]
                cinfo = self.protocol.get_contact_info(self._chat_target_name)
                if cinfo is None:
                    self._chat_target_type = MSNAB.ClientType.ChatMember
                else:
                    self._chat_target_type = cinfo.type
            else:
                self._chat_target_name = None
                self._chat_target_type = MSNAB.ClientType.ChatMember
        else:
            self._chatbuddy = None

    @property
    def chat_room_name(self):
        if self.ischat:
            return self.name

        return None

    def connect(self):
        pass

    def connected(self):
        return self.protocol.ns is not None and self.protocol.ns.connected()

    def Disconnect(self):
        log.info('Disconnecting. unregistering %r from client (%r)', self, self.client)
        if self.Bridge is not None:
            self.event('AllContactsLeft') # Will cleanup bridge
            self.Bridge = None
            # cleanup bridge
        self.client.unregister_conv(self)

    def exit(self, force_close = False):
        log.info("%r exiting", self)
        self._closed = True

        if self.ischat and self._chat_target_type == MSNAB.IMAddressInfoType.TemporaryGroup:
            circle = self.protocol.circle_buddies[self._chatbuddy]
            circle.sem.release()
            self._chatbuddy = None
            self._chat_target_name = None
        Conversation.exit(self)
        self.Disconnect()

    def _circle_unused(self):
        log.info("Circle no longer in use. Leaving...")
        circle = self.protocol.circle_buddies[self._chatbuddy]
        circle.sem.unbind('resource_release', self._circle_unused)
        self.protocol.ns.leave_temp_circle(circle.name)

    @property
    def name(self):
        if self.ischat:
            return self.buddy.alias

        names = self._clean_list()
        count = len(names)
        aliases = [self.protocol.get_buddy(n).alias for n in names if n != self.self_buddy.name]
        if count == 2:
            who = aliases[0]
        elif count == 3:
            who = '%s and %s' % tuple(sorted(aliases))
        else:
            who = '%d people' % (count - 1)

        return who

    @property
    def chat_id(self):
        if self._chatbuddy is None:
            raise CircleNotReadyException()
        return '%s:%s' % (int(self._chat_target_type), self._chatbuddy)

    @property
    def ischat(self):
        return getattr(self, '_chat_target_type', 1) in (MSNAB.IMAddressInfoType.Circle, MSNAB.IMAddressInfoType.TemporaryGroup)

    @property
    def buddy(self):
        if self.ischat:
            return self.protocol.circle_buddies.get(self._chatbuddy)

        l = self._clean_list()

        try:
            l.remove(self.self_buddy.name)
        except ValueError:
            pass

        if len(l) == 1:
            answer = l[0]
            if isinstance(answer, basestring):
                answer = self.protocol.get_buddy(answer)
            return answer

        return self.protocol.get_buddy(self._chatbuddy)

    def _clean_list(self):
        l = set(x.name for x in self.room_list) | set(self._to_invite)

        circle = self.client.circle_buddies.get(self._chatbuddy, None)
        if circle is not None:
            l.update(circle.buddy_names)
            l.discard(circle.name)

        return list(l)

    @property
    def self_buddy(self):
        return self.protocol.self_buddy

    @callbacks.callsback
    def _send_message(self, text, callback = None, **k):
        pass

    @callbacks.callsback
    def invite(self, buddy, callback = None):
        name = getattr(buddy, 'name', buddy)
        self._pending_invite_callbacks[name] = callback

        is_new_circle = False

        def do_invites(circle_name):
            circle = self.protocol.circle_buddies[circle_name]

            if is_new_circle:
                circle.sem.bind("resource_release", self._circle_unused)
                circle.sem.acquire()

            old_name, self._chatbuddy = self._chatbuddy, circle_name
            self._chat_target_name = circle_name
            self._chat_target_type = (MSNAB.IMAddressInfoType.Circle
                                      if getattr(circle, 'circle', None) is not None
                                      else MSNAB.IMAddressInfoType.TemporaryGroup)

            if old_name != self._chatbuddy:
                self.protocol.ns.invite_to_circle(circle_name, old_name)
            self.protocol.ns.invite_to_circle(circle_name, name)

        if not self.ischat:
            is_new_circle = True
            self.protocol.ns.make_temp_circle(success = do_invites,
                                              error = callback.error)

        else:
            do_invites(self._chatbuddy)

    def on_message_recv(self, name, msg, sms = False):
        buddy = self.buddies[name] = self.protocol.get_buddy(name)
        self.typing_status[buddy] = None

        if hasattr(msg, 'html'):
            message = msg.html().replace('\n', '<br />')
            content_type = 'text/html'
        else:
            message = msg
            content_type = 'text/plain'

        did_receive = self.received_message(buddy, message, sms = sms, content_type = content_type)

        if name != self.self_buddy.name and did_receive:
            Conversation.incoming_message(self)

    def on_action_recv(self, name, action_type, action_text):
        self._stop_exit_timer()

        buddy = self.buddies[name] = self.protocol.get_buddy(name)

        if action_type == 'custom':
            if action_text is not None:
                #Translators: ex: Frank nudged you!
                message = _('{name} {action}').format(name = buddy.alias, action = action_text)
                self.system_message(message)
        else:
            text = dict(
                        wink  = _('{name} winked at you!'),
                        nudge = _('{name} nudged you!'),
                        ).get(action_type, None)
            if text is not None:
                message = text.format(name = buddy.alias)
                self.system_message(message)

    def on_typing_notification(self, name, typing):
        buddy = self.buddies[name] = self.protocol.get_buddy(name)
        self.typing_status[buddy] = 'typing' if typing else None

        log.info('%s is %styping', name, '' if typing else 'not ')

    @Events.event
    def buddy_join(self, name):
        buddy = self.buddies[name] = self.protocol.get_buddy(name)

        if buddy is not self.self_buddy and self.self_buddy not in self.room_list:
            self.on_buddy_join(self.self_buddy.name)

        if buddy not in self.room_list:
            self.room_list.append(buddy)
        if not self._chatbuddy:
            self._chatbuddy = name

        log.info('Got buddy join event (%s). self.ischat = %r', name, self.ischat)

        self.notify('ischat')
        super(MSNP21Conversation, self).buddy_join(buddy)

        self.invite_success(name)

        return name

    def invite_success(self, name):
        cb = self._pending_invite_callbacks.pop(name, None)
        if cb is not None:
            cb.success()

    def invite_failure(self, name):
        cb = self._pending_invite_callbacks.pop(name, None)
        if cb is not None:
            cb.error()

    def on_buddy_leave(self, name, notify = True):
        self._type_override = None
        buddy = self.buddies[name] = self.protocol.get_buddy(name)

        try:
            self.room_list.remove(buddy)
        except ValueError:
            log.info('Buddy %r wasn\'t in room but left anyway (?)', name)

        in_room = set(self._clean_list()) - self._to_invite
        in_room.discard(self.self_buddy.name)
        self.typing_status.pop(buddy, None)
        self.event('contacts_changed')

        super(MSNP21Conversation, self).buddy_leave(buddy)
        self.notify('ischat')

    def fed_message(self, msg):
        self.recv_msg(msg)

    def recv_msg(self, msg):
        if msg.name not in self.room_list:
            if not self.ischat:
                assert msg.name == self._chat_target_name, (msg.name, self._chat_target_name)
                self.buddy_join(msg.name)

        try:
            getattr(self, 'recv_msg_%s' % msg.type, self.recv_msg_unknown)(msg)
        except Exception, e:
            import traceback
            traceback.print_exc()

            log.error('Exception handling MSG: %r, msg = %r', e, msg)
Пример #19
0
    def __init__(self, identity):
        Observable.__init__(self)
        ChatProtocol.__init__(self)

        self.identity = identity

        from AccountManager import AccountManager

        self.PreDisconnectHooks  = Delegate()
        self.PostDisconnectHooks = Delegate()

        if not getattr(getattr(sys, 'opts', None), 'limit_log', True):
            DelayedStreamLimiter = lambda s: s
        else:
            from fileutil import DelayedStreamLimiter

        self.consolehandlers = defaultdict(lambda: console_handler_class(DelayedStreamLimiter(sys.stdout)))

        self._status = None

        self.error_count = 0
        self.offline_reason = StateMixin.Reasons.NONE
        self.account_manager = AccountManager(profile = self)
        self.last_hiber_req = None
        self.hibernated = False
        self.linked_observers = False
        self.xfers = ObservableList()
        self.prefs = ObservableDict()
        self.defaultprefs = ObservableDict(prefs.defaultprefs())
        self.quiet = False
        self.prefs_loaded = False

        # set the common.pref lookup to point to our prefs
        import common
        common.set_active_prefs(self.prefs, self.defaultprefs)

        self.prefs.add_observer(self._prefs_changed)

        self.has_authorized = False

        self.statuses = ObservableList()
        self.statuses.add_observer(self._statuses_changed)

        self._xfercount = 0
        self.xfers.add_observer(self._on_file_transfer)

        self.widgets = ObservableList()

        self._encrypter, self._decrypter = util.cryptography.cipher_functions(sha1(self.password.encode('utf8')).digest()[:16])

        self.log_sizes = LogSizeDict()

        global profile
        if profile not in (self, None):
            warnmsg = 'Another DigsbyProfile has been created but the old one is still around!'
            if __debug__:
                raise ValueError(warnmsg)
            else:
                log.critical(warnmsg)

        profile = self  # hack! BuddyListStore needs profile.username

        from contacts.buddyliststore import BuddyListStore
        self.blist = BuddyListStore(self.account_manager.connected_accounts)

        self.set_contact_info = self.blist.set_contact_info
        self.get_contact_info = self.blist.get_contact_info

        from BlobManager import BlobManager
        self.blob_manager = BlobManager(self)

        self.account_manager.add_observer(self.check_loading, 'got_accounts')
        self.account_manager.add_observer(self.on_accounts_loaded, 'accounts_loaded')

        self.blob_manager.add_observer(self.check_loading, 'loading')
        self.loaded = False

        self.OnReturnFromIdle = Delegate()
        self.on_message = PausableDelegate()

        self.OnStatusChange = Delegate()

        self.setup_hub()

        self.idle_timer = None
        self.idle = False

        self.plugins_setup = False
        self.connection = None
        self.setup_plugins()

        self.do_local_load()
Пример #20
0
class DigsbyProfile(Observable, ChatProtocol):
    'A collection of accounts and preferences.'

    MAX_ICON_SIZE  = 96
    MAX_ICON_BYTES = 64 * 1024

    protocol = 'digsby'

    @property
    def display_name(self):
        from common import pref
        return try_this(lambda: getattr(self, pref('profile.display_attr')), self.username)

    def __init__(self, identity):
        Observable.__init__(self)
        ChatProtocol.__init__(self)

        self.identity = identity

        from AccountManager import AccountManager

        self.PreDisconnectHooks  = Delegate()
        self.PostDisconnectHooks = Delegate()

        if not getattr(getattr(sys, 'opts', None), 'limit_log', True):
            DelayedStreamLimiter = lambda s: s
        else:
            from fileutil import DelayedStreamLimiter

        self.consolehandlers = defaultdict(lambda: console_handler_class(DelayedStreamLimiter(sys.stdout)))

        self._status = None

        self.error_count = 0
        self.offline_reason = StateMixin.Reasons.NONE
        self.account_manager = AccountManager(profile = self)
        self.last_hiber_req = None
        self.hibernated = False
        self.linked_observers = False
        self.xfers = ObservableList()
        self.prefs = ObservableDict()
        self.defaultprefs = ObservableDict(prefs.defaultprefs())
        self.quiet = False
        self.prefs_loaded = False

        # set the common.pref lookup to point to our prefs
        import common
        common.set_active_prefs(self.prefs, self.defaultprefs)

        self.prefs.add_observer(self._prefs_changed)

        self.has_authorized = False

        self.statuses = ObservableList()
        self.statuses.add_observer(self._statuses_changed)

        self._xfercount = 0
        self.xfers.add_observer(self._on_file_transfer)

        self.widgets = ObservableList()

        self._encrypter, self._decrypter = util.cryptography.cipher_functions(sha1(self.password.encode('utf8')).digest()[:16])

        self.log_sizes = LogSizeDict()

        global profile
        if profile not in (self, None):
            warnmsg = 'Another DigsbyProfile has been created but the old one is still around!'
            if __debug__:
                raise ValueError(warnmsg)
            else:
                log.critical(warnmsg)

        profile = self  # hack! BuddyListStore needs profile.username

        from contacts.buddyliststore import BuddyListStore
        self.blist = BuddyListStore(self.account_manager.connected_accounts)

        self.set_contact_info = self.blist.set_contact_info
        self.get_contact_info = self.blist.get_contact_info

        from BlobManager import BlobManager
        self.blob_manager = BlobManager(self)

        self.account_manager.add_observer(self.check_loading, 'got_accounts')
        self.account_manager.add_observer(self.on_accounts_loaded, 'accounts_loaded')

        self.blob_manager.add_observer(self.check_loading, 'loading')
        self.loaded = False

        self.OnReturnFromIdle = Delegate()
        self.on_message = PausableDelegate()

        self.OnStatusChange = Delegate()

        self.setup_hub()

        self.idle_timer = None
        self.idle = False

        self.plugins_setup = False
        self.connection = None
        self.setup_plugins()

        self.do_local_load()

    @property
    def password(self):
        return self.identity.password
    @property
    def username(self):
        return self.identity.name

    def setup_plugins(self, *a, **k):
        assert not self.plugins_setup
        if not self.plugins_setup:

            self.plugins_setup = True
            wx.CallAfter(self._setup_plugins)

    def _setup_plugins(self):
        for hook in Hook('digsby.profile.addons'):
            try:
                getattr(hook(self), 'setup', lambda *a, **k: None)()
            except Exception:
                traceback.print_exc()

        import plugin_manager.plugin_hub as plugin_hub
        plugin_hub.act('digsby.plugin.load.async')

    def stop_timers(self):
        if self.idle_timer:
            self.idle_timer.stop()

    def _get_status(self):
        if self._status is None:
            self._status = self.load_saved_status()

        return self._status

    def _set_status(self, val):

        # digsby go_idle

        self._status = val
        if val is not None:
            if val.status != 'Idle':
                self.save_status()

    status = property(_get_status, _set_status)

    def setup_hub(self):
        import hub
        h = hub.get_instance()
        if h.filter_message not in self.on_message:
            self.on_message += h.filter_message

        # register IM windows for incoming messages
        from gui.imwin import on_message
        self.on_message += lambda *a, **k: wx.CallAfter(on_message, *a, **k)

        self.on_message.pause()

    def set_profile_blob(self, new_profile):
        self.profile = new_profile

        fstr = self.profile

        for acct in profile.account_manager.connected_accounts:
            self.set_formatted_profile(acct.connection, fstr)

        self.save()

    def on_chat_invite(self, protocol, buddy, message, room_name, on_yes=None, on_no=None):
        @wx.CallAfter
        def after():
            import hub
            hub.get_instance().on_invite(protocol=protocol,
                buddy=buddy,
                message=message,
                room_name=room_name,
                on_yes=on_yes,
                on_no=on_no)

    def on_entered_chat(self, convo):
        return self.on_message(convo=convo, raisenow=True)

    def set_formatted_profile(self, protocol, fstr=None):
        if fstr is None:
            fstr = self.profile

        # $$plugin setprofile
        import plugin_manager.plugin_hub as plugin_hub
        if not plugin_hub.act('digsby.im.setprofile.pre', protocol, fstr):
            return

        plugin_hub.act('digsby.im.setprofile.async', protocol, fstr)

        add_promo_string = self.prefs.get('profile.promote', True)
        if fstr.bestFormat == "rtf":
            if add_promo_string:
                fstr = fstr + PROMOTE_STRING_RTF
            format = None
        else:
            #legacy profile support
            if add_promo_string:
                fstr = fstr.format_as("plaintext").encode('xml') + PROMOTE_STRING_HTML
            from gui.uberwidgets.formattedinput import get_default_format
            format = get_default_format('profile.formatting')

        netcall(lambda: protocol.set_profile(fstr, format))

    def set_profile(self, *a, **k):
        pass

    def _on_file_transfer(self, src, attr, old, new):
        if all((not getattr(x, 'autoshow', True)) for x in new if x not in (old or []) and
               (x.state not in (x.states.CompleteStates | x.states.FailStates))):
            self._xfercount = len(new)
            return
        new, old = len(self.xfers), self._xfercount

        if self.prefs.get('filetransfer.window.show_on_starting', True) and new > old:
            from gui.filetransfer import FileTransferDialog
            wx.CallAfter(FileTransferDialog.Display)

        self._xfercount = new

    def __repr__(self):
        return AccountBase._repr(self)

    def _reconnect(self, initial=False):
        if getattr(self, 'connection', None) is not None:
            self.connection.observers.clear()
            self.connection.Disconnect()
            del self.connection

        self.disconnecting = False

        extra = {}
        resource = getattr(getattr(sys, 'opts', None), 'resource', None)
        if resource is not None:
            extra['resource'] = resource
        elif getattr(sys, 'DEV', False):
            extra['resource'] = 'dev'
        import hub
        conn = self.connection = DigsbyProtocol(self.username, self.password,
                                                self, hub.get_instance(),
                                                DIGSBY_SERVER,  # srvs,
                                                do_tls = False,
                                                sasl_md5 = False,
                                                digsby_login=True,
                                                initial=initial,
                                                **extra
                                                )
        conn.account = self

        conn.add_observer(self.connection_state_changed, 'state')
        conn.add_observer(self.offline_changed, 'offline_reason')

        conn.Connect(on_success = getattr(getattr(self, 'callback', None), 'success', None),
                     on_fail = self.connect_error)

    def do_local_load(self):
        self.local_load_exc = None
        self.blob_manager.load_from_identity(identity = self.identity)
        self.account_manager.load_from_identity(identity = self.identity)

    def connect_error(self):
        if self.has_authorized:
            return self.callback.error()
        else:
            return self.local_login()

    def local_login(self):
        '''
        After a failed network login, attempt to "log in" with self.username and
        self.password to the local accounts store.
        '''
        try:
            exc, self.local_load_exc = self.local_load_exc, None
            if exc is not None:
                raise exc
        except digsbylocal.InvalidPassword:
            self.callback.error(DigsbyLoginError('auth'))
        except Exception:
            self.callback.error()
        else:
            self.blob_manager.local_load()

            # Setup connection and call load_cb
            self.connection_state_changed(None, 'state', None, DigsbyProtocol.Statuses.AUTHORIZED)

            self.connection_state_changed(None, 'state', None, DigsbyProtocol.Statuses.ONLINE)
            self.offline_reason = DigsbyProtocol.Reasons.CONN_LOST
            self.offline_changed(None, 'offline_reason', None,
                                 DigsbyProtocol.Reasons.CONN_LOST)
            self.connection_state_changed(None, 'state', None, DigsbyProtocol.Statuses.OFFLINE)
            self.account_manager.do_load_local_notification()

    def offline_changed(self, src, attr, old, new):
        self.notify(attr, old, new)

    def connection_state_changed(self, src, attr, old, new):
        assert False
        log.info('connection_state_changed %r -> %r', old, new)

        assert type(src) in (DigsbyProtocol, type(None))

        if attr == 'state' and new == getattr(DigsbyProtocol.Statuses, 'AUTHORIZED', Sentinel()):
            self.error_count = 0
            self.watch_account(self)
            self.has_authorized = True
            log.info('Calling load with cb of %r', self.callback)

            self.load(self.callback)
            conn = self.connection
            if conn is not None:
                conn._set_status_object(profile.status)

        elif attr == 'state' and new == DigsbyProtocol.Statuses.OFFLINE:
            self.setnotifyif('offline_reason', getattr(src, 'offline_reason', None))
            if not self.has_authorized and getattr(src, 'offline_reason', None) == DigsbyProtocol.Reasons.BAD_PASSWORD:
                self.unwatch_account(self)
            if self in self.account_manager.connected_accounts:
                self.account_manager.connected_accounts.remove(self)
                self.account_manager.unwatch_account(self)
            self.connection = None

            dccb = getattr(self, '_disconnect_cb', None)
            if dccb is not None:
                self._disconnect_cb = None
                dccb.success()

        elif attr == 'state' and new == DigsbyProtocol.Statuses.ONLINE:
            self.reconnected_callbacks(self.connection)

        self.notify('state', old, new)

    @property
    def state(self):
        return try_this(lambda: self.connection.state, StateMixin.Statuses.OFFLINE)

    def when_active(self, callback):
        if not hasattr(callback, '__call__'):
            raise TypeError('argument "callback" must be callable')

        if self.idle:
            if callback not in self.OnReturnFromIdle:
                self.OnReturnFromIdle += callback
                log.info('added a callback to the idle queue: %r', callback)
            else:
                log.info('callback already in idle queue')
        else:
            log.info('not idle, calling now')
            return callback()

    @wxcall
    def signoff(self, kicked=False):
        'Return to the splash screen.'

        # $$plugin unload
        import plugin_manager.plugin_hub as plugin_hub
        plugin_hub.act('digsby.plugin.unload.async')

        if platformName == 'win':
            return wx.GetApp().Restart()

        # todo: confirm if there are (active) file transfers

        # hide all top level windows
        top = wx.GetTopLevelWindows
        for win in top():
            win.Hide()

        del self.on_message[:]
        del self.OnReturnFromIdle[:]
        self.stop_timers()

        def dodisconnect(success = True):

            if not success:
                log.info('there was an error saving all blobs.')

            # disconnect all accounts
            with traceguard:
                self.disconnect()

            # destroy all top level windows
            f = getattr(wx.GetApp(), 'buddy_frame', None)
            if f:
                f.on_destroy()

            for win in top():
                with traceguard:
                    if not win.IsDestroyed():
                        win.Destroy()

            # clear input shortcuts
            from gui.input.inputmanager import input_manager
            input_manager.reset()

            import gc
            import observe
            gc.collect()

            numCleared = observe.clear_all()
            log.info('cleared %d observers dicts', numCleared)

            # show the splash, preventing autologin
            wx.GetApp().ShowSplash(autologin_override = False, kicked=kicked)

        log.info('saving all blobs before signoff...')
        self.save(success = dodisconnect,
                  error   = lambda: dodisconnect(False))

        from gui import toast
        toast.cancel_all()

    def _statuses_changed(self, src, attr, old, new):
        if not hasattr(self, 'status_timer'):
            self.status_timer = t = ResetTimer(STATUS_UPDATE_FREQ_SECS, self._on_status_timer)
            t.start()
        else:
            self.status_timer.reset()

    def _on_status_timer(self):
        self.status_timer.stop()
        netcall(lambda: self.save('statuses'))

    def _prefs_changed(self, src, attr, old, new):
        if not hasattr(self, 'pref_timer'):
            self.pref_timer = t = ResetTimer(PREF_UPDATE_FREQ_SECS, self._on_pref_timer)
            t.start()
        else:
            self.pref_timer.reset()

    def _on_pref_timer(self):
        self.pref_timer.stop()
        netcall(lambda: self.save('prefs'))

    def SetStatusMessage(self, message, editable = True, edit_toggle = True, **k):
        new_status = StatusMessage(title = None,
                                   status = self.status.status,
                                   message = message,
                                   editable = editable,
                                   edit_toggle = edit_toggle)

        import hooks
        hooks.notify('digsby.statistics.ui.select_status')
        self.set_status(new_status)

    def maybe_return_from_offline(self):
        '''Called by IM accounts when they are connecting to clear an "Offline" status.'''

        if hasattr(self, 'were_connected'):
            log.info("protocol has 'were_connected', deleting and setting Available")
            del self.were_connected

            status = getattr(self, 'were_connected_status', StatusMessage.Available)
            self.set_status(status)

    def set_status(self, status):
        '''
        Takes a StatusMessage object and sets the status in all connected (and
        which will connect in the future) accounts.
        '''
        if status == self.status:
            return log.warning('set_status got an identical status.')

        # $$plugin status change
        from plugin_manager import plugin_hub
        plugin_hub.act('digsby.im.mystatuschange.pre', status)

        if status == '':
            return

        plugin_hub.act('digsby.im.mystatuschange.async', status)

        for hook in Hook('digsby.im.statusmessages.set.pre'):  # can't use query or notify (want the chained effect)
            status = hook(status)

        log.warning('set_status got %r', status)

        accts = [a for a in self.account_manager.connected_accounts if a is not self]

        def allaccts(func):
            for a in accts:
                with traceguard:
                    func(a)

        Offline   = StatusMessage.Offline

        # disconnecting
        if status == Offline:
            log.info('disconnecting all connected accounts')

            # store a list of the accounts which were connected prior
            # to disconnecting.
            self.were_connected = accts[:]
            self.were_connected_status = self.status
            allaccts(lambda a: a.disconnect())
        #reconnecting
        elif self.status == Offline and hasattr(self, 'were_connected'):
            accts = self.were_connected
            del self.were_connected
            for acct in accts:
                with traceguard:
                    if acct in self.account_manager.accounts:
                        acct.connect(invisible=(status.for_account(acct).invisible))
                    else:
                        log.warning('not reconnecting %s', acct)
        else:
            for acct in self.account_manager.connected_accounts[:]:
                with traceguard:
                    prev_invis = self.status.for_account(acct).invisible
                    this_invis = status.for_account(acct).invisible
                    #going to/returning from invisible
                    if (prev_invis or this_invis) and this_invis != prev_invis:
                        acct.connection.set_invisible(this_invis)
                    #just setting a status
                    if not this_invis:
                        acct.connection._set_status_object(status)

        self.setnotifyif('status', status.copy(editable=None, edit_toggle=None))
        self.save_status()

        hooks.notify('digsby.im.statusmessages.set.post', self.status)

    def add_account(self, **attrdict):
        # $$plugin
        self.account_manager.add(Account(**attrdict), 'im')
        import plugin_manager.plugin_hub as plugin_hub
        plugin_hub.act('digsby.im.addaccount.async', attrdict['protocol'], attrdict['name'])

    def add_email_account(self, **info):
        protocol = info.get('protocol')
        name = info.get('name')

        self.account_manager.add(proto_init(protocol)(**info), 'em')

        # $$plugin
        import plugin_manager.plugin_hub as plugin_hub
        plugin_hub.act('digsby.email.addaccount.async', protocol, name)

    def add_social_account(self, **info):
        protocol = info.pop('protocol')
        name = info.get('name')

        acct = proto_init(protocol)(**info)
        self.account_manager.add(acct, 'so')

        # $$plugin
        import plugin_manager.plugin_hub as plugin_hub
        plugin_hub.act('digsby.social.addaccount.async', protocol, name)
        return acct

    def register_account(self, on_success, on_fail, **attrdict):
        newacct = Account(**attrdict)
        newacct.connect(register = True, on_success=on_success, on_fail=on_fail)

    def update_account(self, account, force=False):

        self.account_manager.update_account(account, force=force)

        # $$plugin
        import plugin_manager.plugin_hub as plugin_hub
        plugin_hub.act('digsby.updateaccount.async', account)

    def add_status_message(self, status_obj = None, **info):
        if status_obj is None:
            assert info
            self.statuses.append(StatusMessage(**info))
        else:
            assert info == {}
            self.statuses.append(status_obj)

    def remove_account(self, account):
        self.account_manager.remove(account)

        # $$plugin
        import plugin_manager.plugin_hub as plugin_hub
        plugin_hub.act('digsby.removeaccount.async', account)

    remove_email_account = \
    remove_social_account = \
    remove_account

    def remove_status_message(self, status_message):
        self.statuses.remove(status_message)

    def get_widgets(self):
        self.connection.get_widgets()

    def incoming_widgets(self, widgets):
        self.widgets[:] = widgets
        hooks.notify('digsby.widgets.result', widgets)

    def blob_failed(self, name):
        try:
            self.connection.Disconnect()
            self.offline_changed(None, 'offline_reason', None,
                                 DigsbyProtocol.Reasons.CONN_FAIL)
        except Exception:
            pass

    def update_blob(self, name, useful_data):
        if name == 'prefs':

            log.critical('prefs updated from the network')

            with self.prefs.flagged('network'):
                if 'defaultprefs' not in self.blob_manager.waiting_blobs:
                    new_prefs = dictadd(self.defaultprefs, useful_data)
                    self.prefs.update(new_prefs)
                else:
                    self.prefs.update(useful_data)
                    new_prefs = useful_data
                if hasattr(self, 'defaultprefs'):
                    for key in set(self.prefs.keys()) - (set(new_prefs.keys()) | set(self.defaultprefs.keys())):
                        self.prefs.pop(key, None)

            self.prefs_loaded = True
            hooks.notify('blobs.update.prefs', self.prefs)

        elif name == 'defaultprefs':
            if 'prefs' not in self.blob_manager.waiting_blobs:
                new_prefs = dictadd(useful_data, self.prefs)
                self.prefs.update(new_prefs)
                if hasattr(self, 'defaultprefs'):
                    for key in set(self.defaultprefs.keys()) - set(useful_data.keys()):
                        self.prefs.pop(key, None)
            self.defaultprefs.update(useful_data)

        elif name == 'buddylist':
            self.blist.update_data(useful_data)

        elif callable(getattr(self, '_incoming_blob_' + name, None)):
            getattr(self, '_incoming_blob_' + name)(useful_data)

        else:
            log.critical('replacing profile attribute %s', name)
            if name == 'statuses':
                assert False
            setattr(self, name, observable_type(useful_data))

    def _incoming_blob_profile(self, profile_str_or_fmtstr):
        from util.primitives.fmtstr import fmtstr

        # self.profile used to be a string, but now it is a fmtstr, and goes out
        # over the wire as a JSON dict.
        #
        # assume that if we cannot parse the incoming profile blob as JSON, then
        # it must be an old-style string profile.
        if isinstance(profile_str_or_fmtstr, dict):
            fstr = fmtstr.fromDict(profile_str_or_fmtstr)
        else:
            from gui.uberwidgets.formattedinput import get_default_format
            fstr = fmtstr.singleformat(profile_str_or_fmtstr, format=get_default_format('profile.formatting'))

        self.profile = fstr

    def _incoming_blob_statuses(self, newdata):
        data = [(StatusMessage(**d) if isinstance(d, dict)
                             else d) for d in newdata]
        self.statuses[:] = data

    def _incoming_blob_notifications(self, newdata):
        def fix_underscore(d):
            for key in d.keys()[:]:
                if key and '_' in key:
                    d[key.replace('_', '.')] = d.pop(key)

        if not hasattr(self, 'notifications'):
            self.notifications = ObservableDict()
        else:
            fix_underscore(self.notifications)

        fix_underscore(newdata)

        self.notifications.update(newdata)
        import common.notifications

        # for any notification keys that exist in YAML, but not in the users
        # blob, add them with the values in the YAML 'default' key
        ni = common.notifications.get_notification_info()
        base = self.notifications[None]
        for k in ni:
            if k in base:
                continue

            try:
                defaults = ni[k].get('default', {})
                base[k] = [dict(reaction=v) for v in defaults.get('reaction', ())]
            except Exception:
                traceback.print_exc()
                continue

        import hooks
        hooks.notify('digsby.notifications.changed')

    def load(self, cb):
        'Loads network data from the server.'
        self.loaded = False

        def callback(_cb=cb):
            self.loaded = True

            log.info('Calling callback that was given to load: %r', _cb)
            _cb(lambda *a, **k: None)

            self.link_observers()

        with traceguard:
            conn = self.connection
            if conn is not None:
                conn.change_state(self.connection.Statuses.SYNC_PREFS)

                def on_accounts_loaded():
                    # show the myspace account wizard if all you have are the automagic accounts
                    def after():
                        if len(self.accounts) == 0 and \
                           len(self.socialaccounts) == 0 and \
                           len(self.emailaccounts) == 0 and \
                           len(self.widgets) == 0:
                            import gui.accountwizard
                            gui.accountwizard.show()
                    wx.CallLater(1000, after)

                on_accounts_loaded_cc = CallCounter(2, on_accounts_loaded)

                def call_cc(*a, **k):
                    on_accounts_loaded_cc()
                import util.hook_util
                if not util.hook_util.OneShotHook(self, 'digsby.accounts.released.async')(call_cc, if_not_fired=True):
                    call_cc()
                if not util.hook_util.OneShotHook(self, 'digsby.widgets.result')(call_cc, if_not_fired=True):
                    call_cc()

                self.get_widgets()

        self.load_cb = callback

        log.info('Forcing check_loading call')
        self.check_loading()

    def link_observers(self):
        if self.linked_observers:
            return

        link = self.prefs.link
        for pref in ('become_idle', 'idle_after',):
            link('messaging.%s' % pref, getattr(self, '%s_changed' % pref))

        self.setup_logger()
        self.prefs.add_observer(self.link_logging)

        for key in self.prefs:
            self.link_logging(self.prefs, key)

        self.linked_observers = True

    def check_loading(self, src=None, attr=None, old=None, new=None):
        if self.account_manager.got_accounts and not self.blob_manager.loading:
#            if self.connection is not None:
#                log.warning('connection is not None')
#                self.connection.change_state(self.connection.Statuses.ONLINE)
            initialized()
            if not self.loaded:
                self._have_connected = True
#                cb, self.load_cb = self.load_cb, (lambda *a, **k: None)
                self.loaded = True
                self.link_observers()
#                log.info('Calling load_cb: %r', cb)
#                cb()

    def on_accounts_loaded(self, src, attr, old, new):
        if new:
            log.info('unpausing the message queue')
            wx.CallAfter(self.on_message.unpause)

    def link_logging(self, src, key, *a, **k):
        n = 'logging'
        if not isinstance(key, basestring) or not key.startswith(n):
            return
        logname = key[len(n):] or None
        if logname is not None:
            logname = logname.strip('.')
        newlevel = try_this(lambda: int(get(src, key)), 0)
        logging.log(100, 'Setting %s to level %d', logname or 'root', newlevel)

        import main
        if not hasattr(main, 'full_loggers'):
            return  # logging system not setup

        # don't bother modifying console handlers if we never setup any
        if not getattr(main, 'logging_to_stdout', False):
            return

        if not logname:
            logger = logging.getLogger('')
            s_handlers = [h for  h in logger.handlers if (h.__class__ is console_handler_class) and h not in main.full_loggers]
            s_handlers[0].setLevel(newlevel)
        else:
            rootlogger = logging.getLogger('')
            root_handlers = [h for  h in rootlogger.handlers if (h.__class__ is not console_handler_class) or h in main.full_loggers]
            handler = self.consolehandlers[newlevel]
            handler.setLevel(newlevel)

            from main import ConsoleFormatter
            formatter = ConsoleFormatter()

            handler.setFormatter(formatter)
            root_handlers.append(handler)
            new_logger = logging.getLogger(logname)
            new_logger.propagate = False
            new_logger.handlers[:] = root_handlers

    def setup_logger(self):
        'Sets up an IM and event logging object.'
        from common.logger import Logger
        logger = self.logger = Logger()

        # logger receives all messages, incoming and outgoing.
        def later(*a, **k):
            wx.CallLater(1000, threaded(self.logger.on_message), *a, **k)
        self.on_message += lambda *a, **k: wx.CallAfter(later, *a, **k)

        set = lambda attr: lambda val: setattr(self.logger, attr, val)
        link = lambda attr, cb: self.prefs.link(attr, cb, obj = logger)
        link('log.ims',       set('LogIMs'))
        link('log.ims',       set('LogChats'))

    @callsback
    def save(self, saveblobs = None, force = False, callback = None):
        '''
        Save one, or more, or all, data blobs.

        if saveblobs is:
            None: saves all of them
            a string: it must be one of the blob names
            a sequence: all blobs in the sequence will be saved
        '''

        if saveblobs is None:                    # None means all blobs
            saveblobs = self.blob_manager.blob_names
        elif isinstance(saveblobs, basestring):  # put a string into a list
            saveblobs = [saveblobs]

        # check for correct blobnames
        diff = set(saveblobs) - set(self.blob_manager.blob_names)
        if len(diff) > 0:
            raise ValueError('illegal blob names: %s' % ', '.join(diff))

        saveblobs = set(saveblobs)
        waiting = set(self.blob_manager.waiting_blobs)
        output = saveblobs - waiting

        if len(output) < len(saveblobs):
            log.info("blobs failed to save, not yet loaded: %r",
                     waiting & saveblobs)

        if self.blob_manager.loading:
            info('blobs still loading, disallowing save')
            callback.success()
            return
        else:
            saveblobs = list(output)

        info('saving blobs %s', ', '.join(saveblobs))

        cbs = []
        for name in saveblobs:
            if name == 'buddylist':
                cbs.append(partial(self.blob_manager.set_blob, name,
                                   data = self.blist.save_data(), force = force))
            elif name == 'prefs':
                cbs.append(partial(self.save_out_prefs, force = force))
            elif name == 'defaultprefs':
                pass
            elif name == 'statuses':
                data = [s.__getstate__(network=True) for s in self.statuses]
                for s in data:
                    s['format'] = dict(s['format']) if s['format'] is not None else None
                cbs.append(partial(self.blob_manager.set_blob, name,
                                   data = data,
                                   force = force))
            elif name == 'profile':
                data = self.profile.asDict()
                cbs.append(partial(self.blob_manager.set_blob, name, data=data, force=force))
            else:
                cbs.append(partial(self.blob_manager.set_blob, name,
                                   data = to_primitive(getattr(self, name)),
                                   force = force))

        do_cb(cbs, callback = callback)

    def backup_blobs(self, dir):
        pth = path(dir)
        from util.json import pydumps
        from time import time
        for name in ['profile', 'buddylist', 'notifications', 'prefs', 'statuses', 'icon']:
            if name == 'buddylist':
                data = self.blist.save_data()
            elif name == 'prefs':
                data = to_primitive(dictdiff(profile.defaultprefs, self.prefs))
            elif name == 'defaultprefs':
                pass
            elif name == 'statuses':
                data = [s.__getstate__() for s in self.statuses]
                for s in data:
                    s['format'] = dict(s['format'])
            else:
                data = to_primitive(getattr(self, name))
            f = pth / name + '_' + str(int(time())) + '.blob'
            with f.open('wb') as out:
                if name == 'icon':
                    out.write(data)
                else:
                    out.write(pydumps(data).encode('z'))

    @property
    def localprefs(self):
        return localprefs()

    @callsback
    def save_blob(self, name, data, callback = None):
        assert name not in ('buddylist', 'prefs', 'defaultprefs', 'statuses')

        log.critical('replacing attribute %s in profile', name)
        setattr(self, name, data)
        self.blob_manager.set_blob(name, data = to_primitive(getattr(self, name)),
                                 callback = callback)

    @callsback
    def save_out_prefs(self, force = False, callback = None):
        'Pack the data and send it to the server.'
        data = dictdiff(profile.defaultprefs, self.prefs)
        self.blob_manager.set_blob('prefs', data = to_primitive(data), force = force,
                                 callback = callback)

    @callsback
    def disconnect(self, callback = None):
        if getattr(self, 'disconnecting', False):
            return

        self.disconnecting = True
        self.PreDisconnectHooks()

        complete_disconnect = lambda: self._finish_disconnect(callback=callback)
        self.account_manager.disconnect_all(
            success = lambda :
                self.disconnect_profile(success = complete_disconnect,
                                        error = complete_disconnect))

        self._force_dc_timer = util.Timer(DISCONNECT_TIMEOUT, complete_disconnect)
        self._force_dc_timer.start()

        self.stop_timers()

    @callsback
    def disconnect_profile(self, callback = None):
        log.info('Disconnect digsbyprofile')
        self._disconnect_cb = callback
        if getattr(self, 'connection', None) is not None:
            self.connection.Disconnect()

    def _finish_disconnect(self, callback):
        try:
            log.info('finishing profile disconnect')
            if getattr(self, '_force_dc_timer', None) is not None:
                self._force_dc_timer.stop()
                self._force_dc_timer = None

            self.PostDisconnectHooks()

        finally:
            callback.success()

    def hibernate(self):
        #called from windows (should be on wx thread)
        self.last_hiber_req = HIBERNATE
        self.check_hibernate_state()

    def unhibernate(self, delay = 15):
        #called from windows (should be on wx thread)
        self.last_hiber_req = UNHIBERNATE
        delay = max(int(delay), 0)
        if delay:
            wx.CallLater(delay * 1000, self.check_hibernate_state)
        else:
            self.check_hibernate_state()

    def check_hibernate_state(self):
        if self.last_hiber_req == HIBERNATE:
            if self.hibernated:
                return
            else:
                self.hibernated = True
                self._do_hibernate()
                return
        elif self.last_hiber_req == UNHIBERNATE:
            if not self.hibernated:
                return
            else:
                self.hibernated = False
                self._do_unhibernate()
                return

    def _do_hibernate(self):
        log.warning("HIBERNATING")
        self.hibernated_im     = hibernated_im     = []
        self.hibernated_email  = hibernated_email  = []
        self.hibernated_social = hibernated_social = []
        for a in self.account_manager.connected_accounts[:]:
            if a is not self:
                with traceguard:
                    a.disconnect()
                    hibernated_im.append(a)

        for a in self.account_manager.emailaccounts:
            with traceguard:
                if a.enabled:
                    a.set_enabled(False)
                    a.disconnect()
                    hibernated_email.append(a)

        for a in self.account_manager.socialaccounts:
            with traceguard:
                if a.enabled:
                    a.set_enabled(False)
                    a.Disconnect()
                    hibernated_social.append(a)

        if getattr(self, 'connection', None) is not None:
            self.connection.Disconnect()
        log.warning("HIBERNATED")

    def _do_unhibernate(self):
        log.warning("UN-HIBERNATING")
        hibernated_im     = self.hibernated_im
        hibernated_email  = self.hibernated_email
        hibernated_social = self.hibernated_social
        for a in hibernated_im:
            with traceguard:
                a._reconnect()

        for a in hibernated_email:
            with traceguard:
                a.set_enabled(True)

        for a in hibernated_social:
            with traceguard:
                a.set_enabled(True)

        self._reconnect()
        log.warning("UN-HIBERNATED")

    @property
    def allow_status_changes(self):
        'Used by the main status combo do decide whether or not to show itself.'

        if hasattr(self, 'were_connected'):
            # This means that "Disconnected" was selected in the Status dialog
            # were_connected is a list of account objects to reconnect if the
            # status is changed again.
            return True

        connecting = [a for a in self.account_manager.accounts if getattr(a, 'connection', None) is not None and
                      a.connection.state != a.connection.Statuses.OFFLINE]

        if connecting:
            return True

        return False

    def plain_pw(self, password):
        "Returns pw decrypted with the profile's password as the key."

        return self._decrypter(password if password is not None else '').decode('utf-8')

    def crypt_pw(self, password):
        "Returns pw encrypted with the profile's password as the key."
        if password and not isinstance(password, unicode):
            print_stack()
        return self._encrypter((password if password is not None else '').encode('utf-8'))

    @property
    def is_connected(self):
        return bool(getattr(self, 'connection', None) and (self.connection.state == self.connection.states['Connected'] or
                                                           self.connection.is_connected))

    #
    # buddy icon
    #

    def get_icon_bitmap(self):
        'Returns the current buddy icon.'

        if self.icon is None:
            log.info('get_icon_bitmap: self.icon is None, returning None')
            return None
        elif self.icon == '\x01':
            # a single 1 byte in the database means "use the default"
            # and is set in newly created accounts.
            img = wx.Image(path('res') / 'digsbybig.png')
            if not img.Ok():
                log.warning('get_icon_bitmap: could not load digsbybig.png, returning None')
                return None
            return wx.BitmapFromImage(img).Resized(self.MAX_ICON_SIZE)
        else:
            try:
                return Image.open(StringIO(self.icon)).WXB
            except Exception:
                log.warning('could not create wxImageFromStream with profile.icon data')
                return None

    def get_icon_bytes(self):
        if self.icon is None:
            return None
        elif self.icon == '\x01':
            return (path('res') / 'digsbybig.png').bytes()
        else:
            return self.icon

    @property
    def name(self):
        return self.username

    def protocol_info(self):
        return protocols['digsby']

    @property
    def metacontacts(self):
        return self.blist.metacontacts

    @property
    def buddylist(self):
        'Returns the buddylist GUI window.'

        return wx.FindWindowByName('Buddy List').Children[0].blist

    def __getattr__(self, attr):
        try:
            return Observable.__getattribute__(self, attr)

        except AttributeError, e:
            try:
                return getattr(self.account_manager, attr)
            except AttributeError:
                raise e
Пример #21
0
 def __init__(self, protocol):
     ObservableDict.__init__(self)
     FilterDict.__init__(self, oscar._lowerstrip)
     self.protocol = protocol
Пример #22
0
 def __init__(self, save_func, *a, **k):
     self.save = save_func
     SavingDictBase.__init__(self, *a, **k)
Пример #23
0
 def __delitem__(self, key):
     SavingDictBase.__delitem__(self, key)
     self.save(self)
Пример #24
0
 def __init__(self, protocol):
     ObservableDict.__init__(self)
     self.protocol = protocol