def __init__(self, server, proxies={}, transport_class=DirectConnection): """Initializer @param server: the Notification server to connect to. @type server: tuple(host, port) @param proxies: proxies that we can use to connect @type proxies: {type: string => L{gnet.proxy.ProxyInfos}} @param transport_class: the transport class to use for the network connection @type transport_class: L{pymsn.transport.AbstractTransport}""" EventsDispatcher.__init__(self) self.__state = ClientState.CLOSED self._proxies = proxies self._transport_class = transport_class self._proxies = proxies self._transport = transport_class(server, ServerType.NOTIFICATION, self._proxies) self._protocol = msnp.NotificationProtocol(self, self._transport, self._proxies) self._switchboard_manager = SwitchboardManager(self) self._switchboard_manager.register_handler(SwitchboardConversation) self._p2p_session_manager = P2PSessionManager(self) self._msn_object_store = MSNObjectStore(self) self._external_conversations = {} self._sso = None self._profile = None self._address_book = None self._oim_box = None self.__die = False self.__connect_transport_signals() self.__connect_protocol_signals() self.__connect_switchboard_manager_signals()
class Client(EventsDispatcher): """This class provides way to connect to the notification server as well as methods to manage the contact list, and the personnal settings. @sort: __init__, login, logout, state, profile, address_book, msn_object_store, oim_box, spaces""" def __init__(self, server, proxies={}, transport_class=DirectConnection): """Initializer @param server: the Notification server to connect to. @type server: tuple(host, port) @param proxies: proxies that we can use to connect @type proxies: {type: string => L{gnet.proxy.ProxyInfos}} @param transport_class: the transport class to use for the network connection @type transport_class: L{pymsn.transport.AbstractTransport}""" EventsDispatcher.__init__(self) self.__state = ClientState.CLOSED self._proxies = proxies self._transport_class = transport_class self._proxies = proxies self._transport = transport_class(server, ServerType.NOTIFICATION, self._proxies) self._protocol = msnp.NotificationProtocol(self, self._transport, self._proxies) self._switchboard_manager = SwitchboardManager(self) self._switchboard_manager.register_handler(SwitchboardConversation) self._p2p_session_manager = P2PSessionManager(self) self._msn_object_store = MSNObjectStore(self) self._external_conversations = {} self._sso = None self._profile = None self._address_book = None self._oim_box = None self.__die = False self.__connect_transport_signals() self.__connect_protocol_signals() self.__connect_switchboard_manager_signals() ### public: @property def msn_object_store(self): """The MSNObjectStore instance associated with this client. @type: L{MSNObjectStore<pymsn.p2p.MSNObjectStore>}""" return self._msn_object_store @property def profile(self): """The profile of the current user @type: L{User<pymsn.profile.Profile>}""" return self._profile @property def address_book(self): """The address book of the current user @type: L{AddressBook<pymsn.service.AddressBook>}""" return self._address_book @property def oim_box(self): """The offline IM for the current user @type: L{OfflineIM<pymsn.service.OfflineIM>}""" return self._oim_box @property def spaces(self): """The MSN Spaces of the current user @type: L{Spaces<pymsn.service.Spaces>}""" return self._spaces @property def state(self): """The state of this Client @type: L{pymsn.event.ClientState}""" return self.__state def login(self, account, password): """Login to the server. @param account: the account to use for authentication. @type account: utf-8 encoded string @param password: the password needed to authenticate to the account @type password: utf-8 encoded string """ if (self._state != ClientState.CLOSED): logger.warning('login already in progress') self.__die = False self._profile = profile.Profile((account, password), self._protocol) self.__connect_profile_signals() self._transport.establish_connection() self._state = ClientState.CONNECTING def logout(self): """Logout from the server.""" if self.__state != ClientState.OPEN: # FIXME: we need something better return self.__die = True self._protocol.signoff() self._switchboard_manager.close() self.__state = ClientState.CLOSED ### protected: @rw_property def _state(): def fget(self): return self.__state def fset(self, state): self.__state = state self._dispatch("on_client_state_changed", state) return locals() def _register_external_conversation(self, conversation): for contact in conversation.participants: break if contact in self._external_conversations: logger.warning("trying to register an external conversation twice") return self._external_conversations[contact] = conversation def _unregister_external_conversation(self, conversation): for contact in conversation.participants: break del self._external_conversations[contact] ### private: def __connect_profile_signals(self): """Connect profile signals""" def property_changed(profile, pspec): method_name = "on_profile_%s_changed" % pspec.name.replace("-", "_") self._dispatch(method_name) self.profile.connect("notify::presence", property_changed) self.profile.connect("notify::display-name", property_changed) self.profile.connect("notify::personal-message", property_changed) self.profile.connect("notify::current-media", property_changed) self.profile.connect("notify::msn-object", property_changed) def __connect_contact_signals(self, contact): """Connect contact signals""" def event(contact, *args): event_name = args[-1] event_args = args[:-1] method_name = "on_contact_%s" % event_name.replace("-", "_") self._dispatch(method_name, contact, *event_args) def property_changed(contact, pspec): method_name = "on_contact_%s_changed" % pspec.name.replace("-", "_") self._dispatch(method_name, contact) contact.connect("notify::memberships", property_changed) contact.connect("notify::presence", property_changed) contact.connect("notify::display-name", property_changed) contact.connect("notify::personal-message", property_changed) contact.connect("notify::current-media", property_changed) contact.connect("notify::msn-object", property_changed) contact.connect("notify::client-capabilities", property_changed) def connect_signal(name): contact.connect(name, event, name) connect_signal("infos-changed") def __connect_transport_signals(self): """Connect transport signals""" def connect_success(transp): self._sso = SSO.SingleSignOn(self.profile.account, self.profile.password, self._proxies) self._address_book = AB.AddressBook(self._sso, self._proxies) self.__connect_addressbook_signals() self._oim_box = OIM.OfflineMessagesBox(self._sso, self, self._proxies) self.__connect_oim_box_signals() self._spaces = Spaces.Spaces(self._sso, self._proxies) self._state = ClientState.CONNECTED def connect_failure(transp, reason): self._dispatch("on_client_error", ClientErrorType.NETWORK, reason) self._state = ClientState.CLOSED def disconnected(transp, reason): if not self.__die: self._dispatch("on_client_error", ClientErrorType.NETWORK, reason) self.__die = False self._state = ClientState.CLOSED self._transport.connect("connection-success", connect_success) self._transport.connect("connection-failure", connect_failure) self._transport.connect("connection-lost", disconnected) def __connect_protocol_signals(self): """Connect protocol signals""" def state_changed(proto, param): state = proto.state if state == msnp.ProtocolState.AUTHENTICATING: self._state = ClientState.AUTHENTICATING elif state == msnp.ProtocolState.AUTHENTICATED: self._state = ClientState.AUTHENTICATED elif state == msnp.ProtocolState.SYNCHRONIZING: self._state = ClientState.SYNCHRONIZING elif state == msnp.ProtocolState.SYNCHRONIZED: self._state = ClientState.SYNCHRONIZED elif state == msnp.ProtocolState.OPEN: self._state = ClientState.OPEN im_contacts = self.address_book.contacts for contact in im_contacts: self.__connect_contact_signals(contact) def authentication_failed(proto): self._dispatch("on_client_error", ClientErrorType.AUTHENTICATION, AuthenticationError.INVALID_USERNAME_OR_PASSWORD) self.__die = True self._transport.lose_connection() def unmanaged_message_received(proto, sender, message): if sender in self._external_conversations: conversation = self._external_conversations[sender] conversation._on_message_received(message) else: conversation = ExternalNetworkConversation(self, [sender]) self._register_external_conversation(conversation) if self._dispatch("on_invite_conversation", conversation) == 0: logger.warning("No event handler attached for conversations") conversation._on_message_received(message) self._protocol.connect("notify::state", state_changed) self._protocol.connect("authentication-failed", authentication_failed) self._protocol.connect("unmanaged-message-received", unmanaged_message_received) def __connect_switchboard_manager_signals(self): """Connect Switchboard Manager signals""" def handler_created(switchboard_manager, handler_class, handler): if handler_class is SwitchboardConversation: if self._dispatch("on_invite_conversation", handler) == 0: logger.warning("No event handler attached for conversations") else: logger.warning("Unknown Switchboard Handler class %s" % handler_class) self._switchboard_manager.connect("handler-created", handler_created) def __connect_addressbook_signals(self): """Connect AddressBook signals""" def event(address_book, *args): event_name = args[-1] event_args = args[:-1] if event_name == "messenger-contact-added": self.__connect_contact_signals(event_args[0]) method_name = "on_addressbook_%s" % event_name.replace("-", "_") self._dispatch(method_name, *event_args) def error(address_book, error_code): self._dispatch("on_client_error", ClientErrorType.ADDRESSBOOK, error_code) self.__die = True self._transport.lose_connection() self.address_book.connect('error', error) def connect_signal(name): self.address_book.connect(name, event, name) connect_signal("messenger-contact-added") connect_signal("contact-deleted") connect_signal("contact-blocked") connect_signal("contact-unblocked") connect_signal("group-added") connect_signal("group-deleted") connect_signal("group-renamed") connect_signal("group-contact-added") connect_signal("group-contact-deleted") def __connect_oim_box_signals(self): """Connect Offline IM signals""" def event(oim_box, *args): method_name = "on_oim_%s" % args[-1].replace("-", "_") self._dispatch(method_name, *args[:-1]) def state_changed(oim_box, pspec): self._dispatch("on_oim_state_changed", oim_box.state) def error(oim_box, error_code): self._dispatch("on_client_error", ClientErrorType.OFFLINE_MESSAGES, error_code) self.oim_box.connect("notify::state", state_changed) self.oim_box.connect('error', error) def connect_signal(name): self.oim_box.connect(name, event, name) connect_signal("messages-received") connect_signal("messages-fetched") connect_signal("message-sent") connect_signal("messages-deleted")