comparison src/core/sat_main.py @ 1367:f71a0fc26886

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 10:52:28 +0100
parents ba87b940f07a
children 0befb14ecf62
comparison
equal deleted inserted replaced
1295:1e3b1f9ad6e2 1367:f71a0fc26886
81 sys.exit(1) 81 sys.exit(1)
82 self.bridge.register("getReady", lambda: self._initialised) 82 self.bridge.register("getReady", lambda: self._initialised)
83 self.bridge.register("getVersion", lambda: C.APP_VERSION) 83 self.bridge.register("getVersion", lambda: C.APP_VERSION)
84 self.bridge.register("getProfileName", self.memory.getProfileName) 84 self.bridge.register("getProfileName", self.memory.getProfileName)
85 self.bridge.register("getProfilesList", self.memory.getProfilesList) 85 self.bridge.register("getProfilesList", self.memory.getProfilesList)
86 self.bridge.register("getEntityData", lambda _jid, keys, profile: self.memory.getEntityData(jid.JID(_jid), keys, profile)) 86 self.bridge.register("getEntityData", lambda jid_, keys, profile: self.memory.getEntityData(jid.JID(jid_), keys, profile))
87 self.bridge.register("getEntitiesData", self.memory._getEntitiesData)
87 self.bridge.register("asyncCreateProfile", self.memory.asyncCreateProfile) 88 self.bridge.register("asyncCreateProfile", self.memory.asyncCreateProfile)
88 self.bridge.register("asyncDeleteProfile", self.memory.asyncDeleteProfile) 89 self.bridge.register("asyncDeleteProfile", self.memory.asyncDeleteProfile)
89 self.bridge.register("asyncConnect", self.asyncConnect) 90 self.bridge.register("asyncConnect", self.asyncConnect)
90 self.bridge.register("disconnect", self.disconnect) 91 self.bridge.register("disconnect", self.disconnect)
91 self.bridge.register("getContacts", self.getContacts) 92 self.bridge.register("getContacts", self.getContacts)
92 self.bridge.register("getContactsFromGroup", self.getContactsFromGroup) 93 self.bridge.register("getContactsFromGroup", self.getContactsFromGroup)
93 self.bridge.register("getLastResource", self.memory._getLastResource) 94 self.bridge.register("getMainResource", self.memory._getMainResource)
94 self.bridge.register("getPresenceStatuses", self.memory._getPresenceStatuses) 95 self.bridge.register("getPresenceStatuses", self.memory._getPresenceStatuses)
95 self.bridge.register("getWaitingSub", self.memory.getWaitingSub) 96 self.bridge.register("getWaitingSub", self.memory.getWaitingSub)
96 self.bridge.register("getWaitingConf", self.getWaitingConf) 97 self.bridge.register("getWaitingConf", self.getWaitingConf)
97 self.bridge.register("sendMessage", self._sendMessage) 98 self.bridge.register("sendMessage", self._sendMessage)
98 self.bridge.register("getConfig", self._getConfig) 99 self.bridge.register("getConfig", self._getConfig)
262 263
263 plugin_conn_cb = [] 264 plugin_conn_cb = []
264 for plugin in self.plugins.iteritems(): 265 for plugin in self.plugins.iteritems():
265 if plugin[1].is_handler: 266 if plugin[1].is_handler:
266 plugin[1].getHandler(profile).setHandlerParent(current) 267 plugin[1].getHandler(profile).setHandlerParent(current)
267 connected_cb = getattr(plugin[1], "profileConnected", None) 268 connected_cb = getattr(plugin[1], "profileConnected", None) # profile connected is called after client is ready and roster is got
268 if connected_cb: 269 if connected_cb:
269 plugin_conn_cb.append((plugin[0], connected_cb)) 270 plugin_conn_cb.append((plugin[0], connected_cb))
271 try:
272 yield plugin[1].profileConnecting(profile) # profile connecting is called before actually starting client
273 except AttributeError:
274 pass
270 275
271 current.startService() 276 current.startService()
272 277
273 yield current.getConnectionDeferred() 278 yield current.getConnectionDeferred()
274 yield current.roster.got_roster # we want to be sure that we got the roster 279 yield current.roster.got_roster # we want to be sure that we got the roster
287 if not success: 292 if not success:
288 log.error("error (plugin %(name)s): %(failure)s" % 293 log.error("error (plugin %(name)s): %(failure)s" %
289 {'name': plugin_conn_cb[idx][0], 'failure': result}) 294 {'name': plugin_conn_cb[idx][0], 'failure': result})
290 295
291 yield list_d.addCallback(logPluginResults) # FIXME: we should have a timeout here, and a way to know if a plugin freeze 296 yield list_d.addCallback(logPluginResults) # FIXME: we should have a timeout here, and a way to know if a plugin freeze
292 # TODO: mesure time to launch of each plugin 297 # TODO: mesure launch time of each plugin
293 298
294 def _authenticateProfile(self, password, profile): 299 def _authenticateProfile(self, password, profile):
295 """Authenticate the profile. 300 """Authenticate the profile.
296 301
297 @param password (string): the SàT profile password 302 @param password (string): the SàT profile password
329 if disconnected_cb: 334 if disconnected_cb:
330 disconnected_cb(profile) 335 disconnected_cb(profile)
331 336
332 def getContacts(self, profile_key): 337 def getContacts(self, profile_key):
333 client = self.getClient(profile_key) 338 client = self.getClient(profile_key)
334 ret = [] 339 def got_roster(dummy):
335 for item in client.roster.getItems(): # we get all items for client's roster 340 ret = []
336 # and convert them to expected format 341 for item in client.roster.getItems(): # we get all items for client's roster
337 attr = client.roster.getAttributes(item) 342 # and convert them to expected format
338 ret.append([item.jid.userhost(), attr, item.groups]) 343 attr = client.roster.getAttributes(item)
339 return ret 344 ret.append([item.jid.userhost(), attr, item.groups])
345 return ret
346
347 return client.roster.got_roster.addCallback(got_roster)
340 348
341 def getContactsFromGroup(self, group, profile_key): 349 def getContactsFromGroup(self, group, profile_key):
342 client = self.getClient(profile_key) 350 client = self.getClient(profile_key)
343 return [jid_.full() for jid_ in client.roster.getJidsFromGroup(group)] 351 return [jid_.full() for jid_ in client.roster.getJidsFromGroup(group)]
344 352
454 return self.sendMessage(to_jid, msg, subject, mess_type, {unicode(key): unicode(value) for key, value in extra.items()}, profile_key=profile_key) 462 return self.sendMessage(to_jid, msg, subject, mess_type, {unicode(key): unicode(value) for key, value in extra.items()}, profile_key=profile_key)
455 463
456 def sendMessage(self, to_jid, msg, subject=None, mess_type='auto', extra={}, no_trigger=False, profile_key=C.PROF_KEY_NONE): 464 def sendMessage(self, to_jid, msg, subject=None, mess_type='auto', extra={}, no_trigger=False, profile_key=C.PROF_KEY_NONE):
457 #FIXME: check validity of recipient 465 #FIXME: check validity of recipient
458 profile = self.memory.getProfileName(profile_key) 466 profile = self.memory.getProfileName(profile_key)
459 assert(profile) 467 assert profile
460 client = self.profiles[profile] 468 client = self.profiles[profile]
461 if extra is None: 469 if extra is None:
462 extra = {} 470 extra = {}
463 mess_data = { # we put data in a dict, so trigger methods can change them 471 mess_data = { # we put data in a dict, so trigger methods can change them
464 "to": to_jid, 472 "to": to_jid,
477 mess_data["type"] = 'normal' 485 mess_data["type"] = 'normal'
478 elif not mess_data["to"].resource: # if to JID has a resource, the type is not 'groupchat' 486 elif not mess_data["to"].resource: # if to JID has a resource, the type is not 'groupchat'
479 # we may have a groupchat message, we check if the we know this jid 487 # we may have a groupchat message, we check if the we know this jid
480 try: 488 try:
481 entity_type = self.memory.getEntityData(mess_data["to"], ['type'], profile)["type"] 489 entity_type = self.memory.getEntityData(mess_data["to"], ['type'], profile)["type"]
482 #FIXME: should entity_type manage ressources ? 490 #FIXME: should entity_type manage resources ?
483 except (exceptions.UnknownEntityError, KeyError): 491 except (exceptions.UnknownEntityError, KeyError):
484 entity_type = "contact" 492 entity_type = "contact"
485 493
486 if entity_type == "chatroom": 494 if entity_type == "chatroom":
487 mess_data["type"] = 'groupchat' 495 mess_data["type"] = 'groupchat'
495 503
496 if not no_trigger and not send_only: 504 if not no_trigger and not send_only:
497 if not self.trigger.point("sendMessage", mess_data, pre_xml_treatments, post_xml_treatments, profile): 505 if not self.trigger.point("sendMessage", mess_data, pre_xml_treatments, post_xml_treatments, profile):
498 return defer.succeed(None) 506 return defer.succeed(None)
499 507
500 log.debug(_("Sending jabber message of type [%(type)s] to %(to)s...") % {"type": mess_data["type"], "to": to_jid.full()}) 508 log.debug(_(u"Sending message (type {type}, to {to})").format(type=mess_data["type"], to=to_jid.full()))
501 509
502 def cancelErrorTrap(failure): 510 def cancelErrorTrap(failure):
503 """A message sending can be cancelled by a plugin treatment""" 511 """A message sending can be cancelled by a plugin treatment"""
504 failure.trap(exceptions.CancelError) 512 failure.trap(exceptions.CancelError)
505 513
570 def setPresence(self, to_jid=None, show="", statuses=None, profile_key=C.PROF_KEY_NONE): 578 def setPresence(self, to_jid=None, show="", statuses=None, profile_key=C.PROF_KEY_NONE):
571 """Send our presence information""" 579 """Send our presence information"""
572 if statuses is None: 580 if statuses is None:
573 statuses = {} 581 statuses = {}
574 profile = self.memory.getProfileName(profile_key) 582 profile = self.memory.getProfileName(profile_key)
575 assert(profile) 583 assert profile
576 priority = int(self.memory.getParamA("Priority", "Connection", profile_key=profile)) 584 priority = int(self.memory.getParamA("Priority", "Connection", profile_key=profile))
577 self.profiles[profile].presence.available(to_jid, show, statuses, priority) 585 self.profiles[profile].presence.available(to_jid, show, statuses, priority)
578 #XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource) 586 #XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource)
579 if '' in statuses: 587 if '' in statuses:
580 statuses['default'] = statuses[''] 588 statuses['default'] = statuses['']
586 """Called to manage subscription 594 """Called to manage subscription
587 @param subs_type: subsciption type (cf RFC 3921) 595 @param subs_type: subsciption type (cf RFC 3921)
588 @param raw_jid: unicode entity's jid 596 @param raw_jid: unicode entity's jid
589 @param profile_key: profile""" 597 @param profile_key: profile"""
590 profile = self.memory.getProfileName(profile_key) 598 profile = self.memory.getProfileName(profile_key)
591 assert(profile) 599 assert profile
592 to_jid = jid.JID(raw_jid) 600 to_jid = jid.JID(raw_jid)
593 log.debug(_('subsciption request [%(subs_type)s] for %(jid)s') % {'subs_type': subs_type, 'jid': to_jid.full()}) 601 log.debug(_('subsciption request [%(subs_type)s] for %(jid)s') % {'subs_type': subs_type, 'jid': to_jid.full()})
594 if subs_type == "subscribe": 602 if subs_type == "subscribe":
595 self.profiles[profile].presence.subscribe(to_jid) 603 self.profiles[profile].presence.subscribe(to_jid)
596 elif subs_type == "subscribed": 604 elif subs_type == "subscribed":
604 return self.addContact(jid.JID(to_jid_s), profile_key) 612 return self.addContact(jid.JID(to_jid_s), profile_key)
605 613
606 def addContact(self, to_jid, profile_key): 614 def addContact(self, to_jid, profile_key):
607 """Add a contact in roster list""" 615 """Add a contact in roster list"""
608 profile = self.memory.getProfileName(profile_key) 616 profile = self.memory.getProfileName(profile_key)
609 assert(profile) 617 assert profile
610 #self.profiles[profile].roster.addItem(to_jid) #XXX: disabled (cf http://wokkel.ik.nu/ticket/56)) 618 # presence is sufficient, as a roster push will be sent according to RFC 6121 §3.1.2
611 self.profiles[profile].presence.subscribe(to_jid) 619 self.profiles[profile].presence.subscribe(to_jid)
612 620
613 def _updateContact(self, to_jid_s, name, groups, profile_key): 621 def _updateContact(self, to_jid_s, name, groups, profile_key):
614 return self.updateContact(jid.JID(to_jid_s), name, groups, profile_key) 622 return self.updateContact(jid.JID(to_jid_s), name, groups, profile_key)
615 623
616 def updateContact(self, to_jid, name, groups, profile_key): 624 def updateContact(self, to_jid, name, groups, profile_key):
617 """update a contact in roster list""" 625 """update a contact in roster list"""
618 profile = self.memory.getProfileName(profile_key) 626 profile = self.memory.getProfileName(profile_key)
619 assert(profile) 627 assert profile
620 groups = set(groups) 628 groups = set(groups)
621 roster_item = RosterItem(to_jid) 629 roster_item = RosterItem(to_jid)
622 roster_item.name = name or None 630 roster_item.name = name or None
623 roster_item.groups = set(groups) 631 roster_item.groups = set(groups)
624 self.profiles[profile].roster.updateItem(roster_item) 632 return self.profiles[profile].roster.setItem(roster_item)
625 633
626 def _delContact(self, to_jid_s, profile_key): 634 def _delContact(self, to_jid_s, profile_key):
627 return self.delContact(jid.JID(to_jid_s), profile_key) 635 return self.delContact(jid.JID(to_jid_s), profile_key)
628 636
629 def delContact(self, to_jid, profile_key): 637 def delContact(self, to_jid, profile_key):
630 """Remove contact from roster list""" 638 """Remove contact from roster list"""
631 profile = self.memory.getProfileName(profile_key) 639 profile = self.memory.getProfileName(profile_key)
632 assert(profile) 640 assert profile
633 self.profiles[profile].roster.removeItem(to_jid) 641 self.profiles[profile].presence.unsubscribe(to_jid) # is not asynchronous
634 self.profiles[profile].presence.unsubscribe(to_jid) 642 return self.profiles[profile].roster.removeItem(to_jid)
635
636 643
637 ## Discovery ## 644 ## Discovery ##
638 # discovery methods are shortcuts to self.memory.disco 645 # discovery methods are shortcuts to self.memory.disco
639 # the main difference with client.disco is that self.memory.disco manage cache 646 # the main difference with client.disco is that self.memory.disco manage cache
640 647
768 @param callback_id: id of the action (callback) to launch 775 @param callback_id: id of the action (callback) to launch
769 @param data: optional data 776 @param data: optional data
770 @profile_key: %(doc_profile_key)s 777 @profile_key: %(doc_profile_key)s
771 @return: a deferred which fire a dict where key can be: 778 @return: a deferred which fire a dict where key can be:
772 - xmlui: a XMLUI need to be displayed 779 - xmlui: a XMLUI need to be displayed
780 - validated: if present, can be used to launch a callback, it can have the values
781 - C.BOOL_TRUE
782 - C.BOOL_FALSE
773 """ 783 """
774 profile = self.memory.getProfileName(profile_key) 784 profile = self.memory.getProfileName(profile_key)
775 if not profile: 785 if not profile:
776 raise exceptions.ProfileUnknownError(_('trying to launch action with a non-existant profile')) 786 raise exceptions.ProfileUnknownError(_('trying to launch action with a non-existant profile'))
777 787
846 856
847 return callback_id 857 return callback_id
848 858
849 def getMenus(self, language='', security_limit=C.NO_SECURITY_LIMIT): 859 def getMenus(self, language='', security_limit=C.NO_SECURITY_LIMIT):
850 """Return all menus registered 860 """Return all menus registered
861
851 @param language: language used for translation, or empty string for default 862 @param language: language used for translation, or empty string for default
852 @param security_limit: %(doc_security_limit)s 863 @param security_limit: %(doc_security_limit)s
853 @return: array of tuple with: 864 @return: array of tuple with:
854 - menu id (same as callback_id) 865 - menu id (same as callback_id)
855 - menu type 866 - menu type
856 - raw menu path (array of strings) 867 - raw menu path (array of strings)
857 - translated menu path 868 - translated menu path
858 869 - extra (dict(unicode, unicode)): extra data where key can be:
870 - icon: name of the icon to use (TODO)
871 - help_url: link to a page with more complete documentation (TODO)
859 """ 872 """
860 ret = [] 873 ret = []
861 for menu_id, menu_data in self._menus.iteritems(): 874 for menu_id, menu_data in self._menus.iteritems():
862 type_ = menu_data['type'] 875 type_ = menu_data['type']
863 path = menu_data['path'] 876 path = menu_data['path']
865 if security_limit!=C.NO_SECURITY_LIMIT and (menu_security_limit==C.NO_SECURITY_LIMIT or menu_security_limit>security_limit): 878 if security_limit!=C.NO_SECURITY_LIMIT and (menu_security_limit==C.NO_SECURITY_LIMIT or menu_security_limit>security_limit):
866 continue 879 continue
867 languageSwitch(language) 880 languageSwitch(language)
868 path_i18n = [_(elt) for elt in path] 881 path_i18n = [_(elt) for elt in path]
869 languageSwitch() 882 languageSwitch()
870 ret.append((menu_id, type_, path, path_i18n)) 883 extra = {} # TODO: manage extra data like icon
884 ret.append((menu_id, type_, path, path_i18n, extra))
871 885
872 return ret 886 return ret
873 887
874 def getMenuHelp(self, menu_id, language=''): 888 def getMenuHelp(self, menu_id, language=''):
875 """ 889 """