wokkel integration (not finished yet)
This commit is contained in:
parent
10b195a345
commit
4c7d7563d3
3 changed files with 189 additions and 141 deletions
|
|
@ -25,7 +25,6 @@ from sat_bridge_frontend.DBus import DBusBridgeFrontend
|
|||
from quick_frontend.quick_contact_management import QuickContactManagement
|
||||
|
||||
|
||||
|
||||
class QuickApp():
|
||||
"""This class contain the main methods needed for the frontend"""
|
||||
|
||||
|
|
@ -126,8 +125,8 @@ class QuickApp():
|
|||
self.contactList.replace(jid, show=show, status=status, name=name, group=group)
|
||||
|
||||
|
||||
if type=="unavailable" and jid.short in self.onlineContact:
|
||||
self.onlineContact.remove(jid.short)
|
||||
if type=="unavailable" and jid in self.onlineContact:
|
||||
self.onlineContact.remove(jid)
|
||||
self.CM.remove(jid)
|
||||
self.contactList.remove(jid)
|
||||
|
||||
|
|
|
|||
323
sat.tac
323
sat.tac
|
|
@ -24,12 +24,14 @@ from twisted.application import internet, service
|
|||
from twisted.internet import glib2reactor, protocol, task
|
||||
glib2reactor.install()
|
||||
|
||||
from twisted.words.protocols.jabber import client, jid, xmlstream, error
|
||||
from twisted.words.protocols.jabber import jid, xmlstream, error
|
||||
from twisted.words.xish import domish
|
||||
|
||||
from twisted.internet import reactor
|
||||
import pdb
|
||||
|
||||
from wokkel import client, disco, xmppim
|
||||
|
||||
from sat_bridge.DBus import DBusBridge
|
||||
import logging
|
||||
from logging import debug, info, error
|
||||
|
|
@ -47,15 +49,132 @@ logging.basicConfig(level=logging.DEBUG,
|
|||
###
|
||||
|
||||
|
||||
class SatXMPPClient(client.XMPPClient):
|
||||
|
||||
def __init__(self, jid, password, host=None, port=5222):
|
||||
client.XMPPClient.__init__(self, jid, password, host, port)
|
||||
self.factory.clientConnectionLost = self.connectionLost
|
||||
self.__connected=False
|
||||
|
||||
def _authd(self, xmlstream):
|
||||
print "SatXMPPClient"
|
||||
client.XMPPClient._authd(self, xmlstream)
|
||||
self.__connected=True
|
||||
print "********** CONNECTED **********"
|
||||
self.streamInitialized()
|
||||
|
||||
def streamInitialized(self):
|
||||
"""Called after _authd"""
|
||||
self.keep_alife = task.LoopingCall(self.xmlstream.send, " ") #Needed to avoid disconnection (specially with openfire)
|
||||
self.keep_alife.start(180)
|
||||
|
||||
def isConnected(self):
|
||||
return self.__connected
|
||||
|
||||
def connectionLost(self, connector, unused_reason):
|
||||
print "********** DISCONNECTED **********"
|
||||
try:
|
||||
self.keep_alife.stop()
|
||||
except AttributeError:
|
||||
debug("No keep_alife")
|
||||
|
||||
|
||||
class SAT:
|
||||
class SatMessageProtocol(xmppim.MessageProtocol):
|
||||
|
||||
def __init__(self, host):
|
||||
xmppim.MessageProtocol.__init__(self)
|
||||
self.host = host
|
||||
|
||||
def onMessage(self, message):
|
||||
debug (u"got_message from: %s", message["from"])
|
||||
for e in message.elements():
|
||||
if e.name == "body":
|
||||
self.host.bridge.newMessage(message["from"], e.children[0])
|
||||
self.host.memory.addToHistory(self.host.me, jid.JID(message["from"]), self.host.me, "chat", e.children[0])
|
||||
break
|
||||
|
||||
class SatRosterProtocol(xmppim.RosterClientProtocol):
|
||||
|
||||
def __init__(self, host):
|
||||
xmppim.RosterClientProtocol.__init__(self)
|
||||
self.host = host
|
||||
|
||||
def rosterCb(self, roster):
|
||||
for jid, item in roster.iteritems():
|
||||
info ("new contact in roster list: %s", jid)
|
||||
#FIXME: fill attributes
|
||||
self.host.memory.addContact(jid, {}, item.groups)
|
||||
self.host.bridge.newContact(jid, {}, item.groups)
|
||||
|
||||
def requestRoster(self):
|
||||
""" ask the server for Roster list """
|
||||
debug("requestRoster")
|
||||
self.getRoster().addCallback(self.rosterCb)
|
||||
|
||||
def removeItem(self, to):
|
||||
"""Remove a contact from roster list"""
|
||||
to_jid=jid.JID(to)
|
||||
xmppim.RosterClientProtocol.removeItem(self, to_jid)
|
||||
#TODO: check IQ result
|
||||
|
||||
def addItem(self, to):
|
||||
"""Add a contact to roster list"""
|
||||
to_jid=jid.JID(to)
|
||||
xmppim.RosterClientProtocol.addItem(self, to_jid)
|
||||
#TODO: check IQ result
|
||||
|
||||
class SatPresenceProtocol(xmppim.PresenceClientProtocol):
|
||||
|
||||
def __init__(self, host):
|
||||
xmppim.PresenceClientProtocol.__init__(self)
|
||||
self.host = host
|
||||
|
||||
def availableReceived(self, entity, show=None, statuses=None, priority=0):
|
||||
info ("presence update for [%s]", entity)
|
||||
|
||||
### we check if the status is not about subscription ###
|
||||
#FIXME: type is not needed anymore
|
||||
#TODO: management of differents statuses (differents languages)
|
||||
status = statuses.values()[0] if len(statuses) else ""
|
||||
self.host.memory.addPresenceStatus(entity.full(), "", show or "",
|
||||
status or "", int(priority))
|
||||
|
||||
#now it's time to notify frontends
|
||||
pdb.set_trace()
|
||||
self.host.bridge.presenceUpdate(entity.full(), "", show or "",
|
||||
status or "", int(priority))
|
||||
|
||||
def unavailableReceived(self, entity, statuses=None):
|
||||
#TODO: management of differents statuses (differents languages)
|
||||
status = statuses.values()[0] if len(statuses) else ""
|
||||
self.host.memory.addPresenceStatus(entity.full(), "unavailable", "",
|
||||
status or "", 0)
|
||||
|
||||
#now it's time to notify frontends
|
||||
self.host.bridge.presenceUpdate(entity.full(), "unavailable", "",
|
||||
status or "", 0)
|
||||
|
||||
|
||||
def subscribedReceived(self, entity):
|
||||
debug ("subscription approved for [%s]" % entity)
|
||||
|
||||
def unsubscribedReceived(self, entity):
|
||||
debug ("unsubscription confirmed for [%s]" % entity)
|
||||
|
||||
def subscribeReceived(self, entity):
|
||||
#FIXME: auto answer for subscribe request, must be checked !
|
||||
debug ("subscription request for [%s]" % entity)
|
||||
self.subscribed(entity)
|
||||
|
||||
def unsubscribeReceived(self, entity):
|
||||
debug ("unsubscription asked for [%s]" % entity)
|
||||
|
||||
class SAT(service.Service):
|
||||
|
||||
def __init__(self):
|
||||
#self.reactor=reactor
|
||||
self.memory=Memory()
|
||||
self.server_features=[] #XXX: temp dic, need to be transfered into self.memory in the future
|
||||
self.connected=False #FIXME: use twisted var instead
|
||||
|
||||
self._iq_cb_map = {} #callback called when ns is found on IQ
|
||||
self._waiting_conf = {} #callback called when a confirmation is received
|
||||
|
|
@ -98,33 +217,39 @@ class SAT:
|
|||
self.plugins[plug_info['import_name']] = getattr(mod, plug_info['main'])(self)
|
||||
|
||||
def connect(self):
|
||||
if (self.connected):
|
||||
if (self.isConnected()):
|
||||
info("already connected !")
|
||||
return
|
||||
print "connecting..."
|
||||
reactor.connectTCP(self.memory.getParamV("Server", "Connection"), 5222, self.factory)
|
||||
self.me = jid.JID(self.memory.getParamV("JabberID", "Connection"))
|
||||
self.xmppclient = SatXMPPClient(self.me, self.memory.getParamV("Password", "Connection"),
|
||||
self.memory.getParamV("Server", "Connection"), 5222)
|
||||
self.xmppclient.streamInitialized = self.streamInitialized
|
||||
|
||||
self.messageProt = SatMessageProtocol(self)
|
||||
self.messageProt.setHandlerParent(self.xmppclient)
|
||||
|
||||
self.roster = SatRosterProtocol(self)
|
||||
self.roster.setHandlerParent(self.xmppclient)
|
||||
|
||||
self.presence = SatPresenceProtocol(self)
|
||||
self.presence.setHandlerParent(self.xmppclient)
|
||||
|
||||
self.xmppclient.startService()
|
||||
|
||||
def disconnect(self):
|
||||
if (not self.connected):
|
||||
if (not self.isConnected()):
|
||||
info("not connected !")
|
||||
return
|
||||
info("Disconnecting...")
|
||||
self.factory.stopTrying()
|
||||
if self.xmlstream:
|
||||
self.xmlstream.sendFooter()
|
||||
self.xmppclient.stopService()
|
||||
|
||||
def getService(self):
|
||||
print "GetService !"
|
||||
"""if (self.connected):
|
||||
info("already connected !")
|
||||
return"""
|
||||
info("Getting service...")
|
||||
self.me = jid.JID(self.memory.getParamV("JabberID", "Connection"))
|
||||
self.factory = client.XMPPClientFactory(self.me, self.memory.getParamV("Password", "Connection"))
|
||||
self.factory.clientConnectionLost = self.connectionLost
|
||||
self.factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,self.authd)
|
||||
self.factory.addBootstrap(xmlstream.INIT_FAILED_EVENT,self.failed)
|
||||
return internet.TCPClient(self.memory.getParamV("Server", "Connection"), 5222, self.factory)
|
||||
def startService(self):
|
||||
info("Salut à toi ô mon frère !")
|
||||
self.connect()
|
||||
|
||||
def stopService(self):
|
||||
info("Salut aussi à Rantanplan")
|
||||
|
||||
def run(self):
|
||||
debug("running app")
|
||||
|
|
@ -134,38 +259,30 @@ class SAT:
|
|||
debug("stopping app")
|
||||
reactor.stop()
|
||||
|
||||
def authd(self,xmlstream):
|
||||
self.xmlstream=xmlstream
|
||||
roster=client.IQ(xmlstream,'get')
|
||||
roster.addElement(('jabber:iq:roster', 'query'))
|
||||
roster.addCallback(self.rosterCb)
|
||||
roster.send()
|
||||
debug("server = %s",self.memory.getParamV("Server", "Connection"))
|
||||
|
||||
def streamInitialized(self):
|
||||
"""Called when xmlstream is OK"""
|
||||
SatXMPPClient.streamInitialized(self.xmppclient)
|
||||
debug ("XML stream is initialized")
|
||||
self.xmlstream = self.xmppclient.xmlstream
|
||||
self.me = self.xmppclient.jid #in case of the ressource has changed
|
||||
|
||||
self.roster.requestRoster()
|
||||
|
||||
self.presence.available()
|
||||
|
||||
#FIXME:tmp
|
||||
self.xmlstream.addObserver("/iq[@type='set' or @type='get']", self.iqCb)
|
||||
"""
|
||||
###FIXME: tmp disco ###
|
||||
#self.discoHandler = disco.discoHandler()
|
||||
self.memory.registerFeature("http://jabber.org/protocol/disco#info")
|
||||
self.disco(self.memory.getParamV("Server", "Connection"), self.serverDisco)
|
||||
|
||||
|
||||
#we now send our presence status
|
||||
self.setPresence(status="Online")
|
||||
|
||||
# add a callback for the messages
|
||||
xmlstream.addObserver('/message', self.gotMessage)
|
||||
xmlstream.addObserver('/presence', self.presenceCb)
|
||||
xmlstream.addObserver("/iq[@type='set' or @type='get']", self.iqCb)
|
||||
print "********** CONNECTED **********"
|
||||
self.connected=True
|
||||
self.keep_alife = task.LoopingCall(self.xmlstream.send, " ") #Needed to avoid disconnection (specially with openfire)
|
||||
self.keep_alife.start(180)
|
||||
|
||||
#reactor.callLater(2,self.sendFile,"goffi2@jabber.goffi.int/Psi", "/tmp/fakefile")
|
||||
|
||||
def connectionLost(self, connector, unused_reason):
|
||||
print "********** DISCONNECTED **********"
|
||||
if self.keep_alife:
|
||||
self.keep_alife.stop()
|
||||
self.connected=False
|
||||
"""
|
||||
|
||||
|
||||
def sendMessage(self,to,msg,type='chat'):
|
||||
|
|
@ -186,86 +303,64 @@ class SAT:
|
|||
self.memory.setParam(name, value, namespace)
|
||||
self.bridge.paramUpdate(name, value, namespace)
|
||||
|
||||
def setRoster(self, to):
|
||||
"""Add a contact to roster list"""
|
||||
to_jid=jid.JID(to)
|
||||
roster=client.IQ(self.xmlstream,'set')
|
||||
query=roster.addElement(('jabber:iq:roster', 'query'))
|
||||
item=query.addElement("item")
|
||||
item.attributes["jid"]=to_jid.userhost()
|
||||
roster.send()
|
||||
#TODO: check IQ result
|
||||
|
||||
def delRoster(self, to):
|
||||
"""Remove a contact from roster list"""
|
||||
to_jid=jid.JID(to)
|
||||
roster=client.IQ(self.xmlstream,'set')
|
||||
query=roster.addElement(('jabber:iq:roster', 'query'))
|
||||
item=query.addElement("item")
|
||||
item.attributes["jid"]=to_jid.userhost()
|
||||
item.attributes["subscription"]="remove"
|
||||
roster.send()
|
||||
#TODO: check IQ result
|
||||
|
||||
|
||||
def failed(self,xmlstream):
|
||||
debug("failed: %s", xmlstream.getErrorMessage())
|
||||
debug("failed: %s", dir(xmlstream))
|
||||
|
||||
def isConnected(self):
|
||||
return self.connected
|
||||
try:
|
||||
if self.xmppclient.isConnected():
|
||||
return True
|
||||
except AttributeError:
|
||||
#xmppclient not available
|
||||
pass
|
||||
return False
|
||||
|
||||
## jabber methods ##
|
||||
|
||||
def disco (self, item, callback, node=None):
|
||||
"""XEP-0030 Service discovery Feature."""
|
||||
disco=client.IQ(self.xmlstream,'get')
|
||||
"""disco=client.IQ(self.xmlstream,'get')
|
||||
disco["from"]=self.me.full()
|
||||
disco["to"]=item
|
||||
disco.addElement(('http://jabber.org/protocol/disco#info', 'query'))
|
||||
disco.addCallback(callback)
|
||||
disco.send()
|
||||
disco.send()"""
|
||||
|
||||
|
||||
def setPresence(self, to="", type="", show="", status="", priority=0):
|
||||
"""Send our presence information"""
|
||||
presence = domish.Element(('jabber:client', 'presence'))
|
||||
if not type in ["", "unavailable", "subscribed", "subscribe",
|
||||
"unsubscribe", "unsubscribed", "prob", "error"]:
|
||||
error("Type error !")
|
||||
#TODO: throw an error
|
||||
return
|
||||
to_jid=jid.JID(to)
|
||||
#TODO: refactor subscription bridge API
|
||||
if type=="":
|
||||
self.presence.available(to_jid, show, status, priority)
|
||||
elif type=="subscribe":
|
||||
self.presence.subscribe(to_jid)
|
||||
elif type=="subscribed":
|
||||
self.presence.subscribed(to_jid)
|
||||
elif type=="unsubscribe":
|
||||
self.presence.unsubscribe(to_jid)
|
||||
elif type=="unsubscribed":
|
||||
self.presence.unsubscribed(to_jid)
|
||||
|
||||
if to:
|
||||
presence.attributes["to"]=to
|
||||
if type:
|
||||
presence.attributes["type"]=type
|
||||
|
||||
for element in ["show", "status", "priority"]:
|
||||
if locals()[element]:
|
||||
presence.addElement(element).addContent(unicode(locals()[element]))
|
||||
|
||||
self.xmlstream.send(presence)
|
||||
|
||||
def addContact(self, to):
|
||||
"""Add a contact in roster list"""
|
||||
to_jid=jid.JID(to)
|
||||
self.setRoster(to_jid.userhost())
|
||||
self.roster.addItem(to_jid.userhost())
|
||||
self.setPresence(to_jid.userhost(), "subscribe")
|
||||
|
||||
def delContact(self, to):
|
||||
"""Remove contact from roster list"""
|
||||
to_jid=jid.JID(to)
|
||||
self.delRoster(to_jid.userhost())
|
||||
self.roster.removeItem(to_jid.userhost())
|
||||
self.bridge.contactDeleted(to)
|
||||
|
||||
def gotMessage(self,message):
|
||||
debug (u"got_message from: %s", message["from"])
|
||||
for e in message.elements():
|
||||
if e.name == "body":
|
||||
self.bridge.newMessage(message["from"], e.children[0])
|
||||
self.memory.addToHistory(self.me, jid.JID(message["from"]), self.me, "chat", e.children[0])
|
||||
break
|
||||
|
||||
## callbacks ##
|
||||
|
||||
|
|
@ -286,52 +381,7 @@ class SAT:
|
|||
self._iq_cb_map[uri](stanza)
|
||||
#TODO: manage errors stanza
|
||||
|
||||
def presenceCb(self, elem):
|
||||
info ("presence update for [%s]", elem.getAttribute("from"))
|
||||
debug("\n\nXML=\n%s\n\n", elem.toXml())
|
||||
presence={}
|
||||
presence["jid"]=elem.getAttribute("from")
|
||||
presence["type"]=elem.getAttribute("type") or ""
|
||||
presence["show"]=""
|
||||
presence["status"]=""
|
||||
presence["priority"]=0
|
||||
|
||||
for item in elem.elements():
|
||||
if presence.has_key(item.name):
|
||||
presence[item.name]=item.children[0]
|
||||
|
||||
### we check if the status is not about subscription ###
|
||||
#TODO: check that from jid is one we wants to subscribe (ie: check a recent subscription asking)
|
||||
if jid.JID(presence["jid"]).userhost()!=self.me.userhost():
|
||||
if presence["type"]=="subscribed":
|
||||
debug ("subscription answer")
|
||||
elif presence["type"]=="unsubscribed":
|
||||
debug ("unsubscription answer")
|
||||
elif presence["type"]=="subscribe":
|
||||
#FIXME: auto answer for subscribe request, must be checked !
|
||||
debug ("subscription request")
|
||||
self.setPresence(to=presence["jid"], type="subscribed")
|
||||
else:
|
||||
#We keep presence information only if it is not for subscription
|
||||
self.memory.addPresenceStatus(presence["jid"], presence["type"], presence["show"],
|
||||
presence["status"], int(presence["priority"]))
|
||||
|
||||
#now it's time to notify frontends
|
||||
self.bridge.presenceUpdate(presence["jid"], presence["type"], presence["show"],
|
||||
presence["status"], int(presence["priority"]))
|
||||
|
||||
def rosterCb(self,roster):
|
||||
for contact in roster.firstChildElement().elements():
|
||||
info ("new contact in roster list: %s", contact['jid'])
|
||||
#and now the groups
|
||||
groups=[]
|
||||
for group in contact.elements():
|
||||
if group.name!="group":
|
||||
error("Unexpected element !")
|
||||
break
|
||||
groups.append(str(group))
|
||||
self.memory.addContact(contact['jid'], contact.attributes, groups)
|
||||
self.bridge.newContact(contact['jid'], contact.attributes, groups)
|
||||
|
||||
def serverDisco(self, disco):
|
||||
"""xep-0030 Discovery Protocol."""
|
||||
|
|
@ -390,8 +440,7 @@ class SAT:
|
|||
|
||||
|
||||
application = service.Application('SàT')
|
||||
sat = SAT()
|
||||
service = sat.getService()
|
||||
service = SAT()
|
||||
service.setServiceParent(application)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ class Memory:
|
|||
def addContact(self, JID, attributes, groups):
|
||||
debug("Memory addContact: %s",JID)
|
||||
assert(isinstance(attributes,dict))
|
||||
assert(isinstance(groups,list))
|
||||
assert(isinstance(groups,set))
|
||||
self.contact[JID]=[attributes, groups]
|
||||
|
||||
def addPresenceStatus(self, jid, type, show, status, priority):
|
||||
|
|
|
|||
Loading…
Reference in a new issue