def start(self): """interface requirement""" if self.running(): return self.factory = XMPPClientFactory( self.jid, decrypt(self.SERVICECONFIG.JABBER_PASSWORD)) self.factory.addBootstrap(STREAM_CONNECTED_EVENT, self.connectionMade) self.factory.addBootstrap(STREAM_END_EVENT, self.connectionLost) self.factory.addBootstrap(STREAM_AUTHD_EVENT, self.connectionAuthenticated) self.factory.addBootstrap(STREAM_ERROR_EVENT, self.receivedError) self.factory.addBootstrap(INIT_FAILED_EVENT, self.initFailed) self.service = TCPClient(self.SERVICECONFIG.JABBER_SERVER, self.SERVICECONFIG.JABBER_PORT, self.factory) self.service.setServiceParent(self.parentService) #build/rebuild jabber teams if not os.path.exists(self.SERVICECONFIG.JABBER_TEAM_ROSTER): try: os.makedirs(self.SERVICECONFIG.JABBER_TEAM_ROSTER) except: log('Cannot load team rosters because %s does not exits' % \ self.SERVICECONFIG.JABBER_TEAM_ROSTER) return for name in os.listdir(self.SERVICECONFIG.JABBER_TEAM_ROSTER): f = (self.SERVICECONFIG.JABBER_TEAM_ROSTER, name) if os.path.isfile('%s/%s' % f): Team(name) #preload team rosters
def start(self): """interface requirement""" if self.running(): return self.factory = XMPPClientFactory( self.jid, decrypt(self.SERVICECONFIG.JABBER_PASSWORD) ) self.factory.addBootstrap(STREAM_CONNECTED_EVENT, self.connectionMade) self.factory.addBootstrap(STREAM_END_EVENT, self.connectionLost) self.factory.addBootstrap( STREAM_AUTHD_EVENT, self.connectionAuthenticated ) self.factory.addBootstrap(STREAM_ERROR_EVENT, self.receivedError) self.factory.addBootstrap(INIT_FAILED_EVENT, self.initFailed) self.service = TCPClient( self.SERVICECONFIG.JABBER_SERVER, self.SERVICECONFIG.JABBER_PORT, self.factory ) self.service.setServiceParent(self.parentService) #build/rebuild jabber teams if not os.path.exists(self.SERVICECONFIG.JABBER_TEAM_ROSTER): try: os.makedirs(self.SERVICECONFIG.JABBER_TEAM_ROSTER) except: log('Cannot load team rosters because %s does not exits' % \ self.SERVICECONFIG.JABBER_TEAM_ROSTER) return for name in os.listdir(self.SERVICECONFIG.JABBER_TEAM_ROSTER): f = (self.SERVICECONFIG.JABBER_TEAM_ROSTER,name) if os.path.isfile('%s/%s' % f): Team(name) #preload team rosters
class JabberClient(object): implements(IDroneDService) #requirement xmlstream = None connected = property( lambda self: self.xmlstream is not None ) parentService = None #interface required attribute service = None #interface required attribute SERVICENAME = 'jabber' #interface required attribute #this should be overrode in romeo SERVICECONFIG = dictwrapper({ 'JABBER_CHAT_NICK': config.HOSTNAME, 'JABBER_USERNAME': '******', 'JABBER_PASSWORD': '******', 'JABBER_SERVER': 'jabber.example.net', 'JABBER_CHAT_SERVICE': 'conference.jabber.example.net', 'JABBER_PORT': 5222, 'JABBER_RESOURCE': config.HOSTNAME, 'JABBER_BROADCAST_INTERVAL': 300, 'JABBER_VALIDATE_XML': True, 'JABBER_TRUST_ROOM': False, 'DEPUTY': '*****@*****.**', 'JABBER_TEAM_ROSTER': '/var/lib/droned/teams', 'CONVERSATION_RESPONSE_PERIOD': 180, 'JABBER_JOIN_CHATROOM': False, 'JABBER_TEAM_ROSTER': os.path.join(config.DRONED_HOMEDIR, 'teams'), }) #interface required attribute def running(self): """interface requirement""" return bool(self.service) and self.service.running def install(self, _parentService): """interface requirement""" self.parentService = _parentService user = self.SERVICECONFIG.JABBER_USERNAME server = self.SERVICECONFIG.JABBER_SERVER resource = self.SERVICECONFIG.JABBER_RESOURCE self.jid = JID("%(user)s@%(server)s/%(resource)s" % locals()) self.broadcastTask = LoopingCall(self.broadcastPresence) self.sendQueue = [] self.authenticated = False #load all jabber responders, after configuration import droned.responders droned.responders.loadAll() def start(self): """interface requirement""" if self.running(): return self.factory = XMPPClientFactory(self.jid, self.SERVICECONFIG.JABBER_PASSWORD) self.factory.addBootstrap(STREAM_CONNECTED_EVENT, self.connectionMade) self.factory.addBootstrap(STREAM_END_EVENT, self.connectionLost) self.factory.addBootstrap(STREAM_AUTHD_EVENT, self.connectionAuthenticated) self.factory.addBootstrap(STREAM_ERROR_EVENT, self.receivedError) self.factory.addBootstrap(INIT_FAILED_EVENT, self.initFailed) self.service = TCPClient( self.SERVICECONFIG.JABBER_SERVER, self.SERVICECONFIG.JABBER_PORT, self.factory ) self.service.setServiceParent(self.parentService) #build/rebuild jabber teams for name in os.listdir(self.SERVICECONFIG.JABBER_TEAM_ROSTER): f = (self.SERVICECONFIG.JABBER_TEAM_ROSTER,name) if os.path.isfile('%s/%s' % f): Team(name) #preload team rosters def stop(self): """interface requirement""" if self.service: self.factory.stopTrying() self.factory.stopFactory() self.service.disownServiceParent() self.service.stopService() self.service = None def connectionMade(self, xmlstream): log('connection made') self.xmlstream = xmlstream def connectionLost(self, xmlstream): log('connection lost') self.authenticated = False if self.broadcastTask.running: self.broadcastTask.stop() if self.connected: Event('jabber-offline').fire() self.xmlstream = None def connectionAuthenticated(self, xmlstream): log('connection authenticated') self.authenticated = True if not self.broadcastTask.running: self.broadcastTask.start(self.SERVICECONFIG.JABBER_BROADCAST_INTERVAL) xmlstream.addObserver('/message', self.receivedMessage) xmlstream.addObserver('/presence', self.receivedPresence) xmlstream.addObserver('/iq', self.receivedIQ) xmlstream.addObserver('/error', self.receivedError) Event('jabber-online').fire() while self.sendQueue: self.xmlstream.send( self.sendQueue.pop(0) ) def broadcastPresence(self): presence = Element( ('jabber:client','presence') ) #log('sending presence broadcast') self.xmlstream.send(presence) def sendMessage(self, to, body, useHTML=True, groupChat=False): message = Element( ('jabber:client','message') ) message['to'] = to message['type'] = (groupChat and 'groupchat') or 'chat' message.addElement('body', None, body) if useHTML: html = message.addElement('html', 'http://jabber.org/protocol/xhtml-im') htmlBody = html.addElement('body', 'http://www.w3.org/1999/xhtml') htmlBody.addRawXml( unicode(body) ) if self.SERVICECONFIG.JABBER_VALIDATE_XML: validateXml( html.toXml() ) #safeXml = filter(lambda char: ord(char) < 128, message.toXml()) #log('sending message: %s' % safeXml) log('sending message to %s: %s' % (to, body)) if self.authenticated: self.xmlstream.send(message) else: log("not connected, queueing message", warning=True) self.sendQueue.append(message) def requestAuthorization(self, to): request = Element( (None,'iq') ) request['type'] = 'set' request['id'] = 'auth-request:%s' % to query = Element( (None,'query') ) query['xmlns'] = 'jabber:iq:roster' item = Element( (None,'item') ) item['jid'] = to item['name'] = to.split('@')[0] query.addChild(item) request.addChild(query) log('sending auth request: %s' % request.toXml()) self.xmlstream.send(request) def joinChatRoom(self, room): presence = Element( (None,'presence') ) presence['from'] = self.jid.userhost() jid = '%s@%s/%s' % (room,self.SERVICECONFIG.JABBER_CHAT_SERVICE,self.SERVICECONFIG.JABBER_CHAT_NICK) presence['to'] = jid x = Element( ('http://jabber.org/protocol/muc','x') ) history = Element( (None,'history') ) history['maxchars'] = '0' x.addChild(history) presence.addChild(x) log('sending join: %s' % presence.toXml()) self.xmlstream.send(presence) def leaveChatRoom(self, jid): if '/' not in jid: jid += '/' + self.SERVICECONFIG.JABBER_CHAT_NICK presence = Element( (None,'presence') ) presence['from'] = self.jid.userhost() presence['to'] = jid presence['type'] = 'unavailable' log('sending leave: %s' % presence.toXml()) self.xmlstream.send(presence) def receivedMessage(self, e): # Extract the body of the message try: message = str([c for c in e.children if c.name == 'body'][0]) except: log('discarding invalid message (has no body!): %s' % e.toXml()) return # Discard delayed messages delays = [ x for x in e.children if x.name == 'delay' ] stamps = [ x for x in e.children \ if x.name == 'x' and \ x.compareAttribute('xmlns','jabber:x:delay') and \ x.hasAttribute('stamp') ] #stampstring = str( stamps[0].getAttribute('stamp') ) #timestamp = time.mktime( time.strptime(stampstring, "%Y%m%dT%H:%M:%S") ) if delays or stamps: log('discarding delayed message: %s' % e.toXml()) return # Route message to the right Conversation or ChatRoom entity if e.getAttribute('type') == 'chat': buddy = str( e['from'].split('/')[0] ) if not Conversation.exists(buddy): self.requestAuthorization(buddy) log('received message from %s: %s' % (buddy.split('@')[0], message)) Conversation(buddy).hear(message) elif e.getAttribute('type') == 'groupchat': room = e['from'].split('@')[0] log('received message [chatroom=%s]: %s' % (room, message)) ChatRoom(room).hear(message) else: log('received message of unknown type: %s' % e.toXml(), error=True) def receivedPresence(self, e): log('received presence: %s' % e.toXml()) if e.getAttribute('type') == 'subscribe': log('received authorization request from %s' % e['from']) response = Element( ('','presence') ) response['to'] = e['from'] response['type'] = 'subscribed' log('sending auth response: %s' % response.toXml()) self.xmlstream.send(response) buddy = str(e['from']) if not Conversation.exists(buddy): self.requestAuthorization(buddy) elif e.getAttribute('type') == 'unavailable': #fix for openfire jabber server randomly kicking clients out and prevent kicks CHAT = '@%s/%s' % (self.SERVICECONFIG.JABBER_CHAT_SERVICE,self.SERVICECONFIG.JABBER_CHAT_NICK) if e['to'] == self.jid.full() and e['from'].endswith(CHAT) and \ "status code='307'" in e.toXml(): try: log('%s has kicked me' % (e['from'],)) self.joinChatRoom(e['from'].split(CHAT)[0]) log('successfully rejoined room') except: err('Failed to recover from /kick') #elif any(1 for c in e.children if c.name == 'x'): #TODO detect buddies that go offline # if we have a Conversation then unsubscribe .notify from all events def receivedIQ(self, e): log('received iq: %s' % e.toXml()) def receivedError(self, f): log('received error: %s' % str(f)) def initFailed(self, failure): log('Failed to initialize jabber connection:\n%s' % failure.getTraceback()) self.stop() if failure.check(SASLAuthError): log('Will attempt to reconnect in 15 seconds...') reactor.callLater(15, self.start)
class JabberClient(object): implements(IDroneDService) #requirement xmlstream = None connected = property(lambda self: self.xmlstream is not None) parentService = None #interface required attribute service = None #interface required attribute SERVICENAME = 'jabber' #interface required attribute #this should be overrode in romeo SERVICECONFIG = dictwrapper({ 'JABBER_CHAT_NICK': config.HOSTNAME, 'JABBER_USERNAME': '******', 'JABBER_PASSWORD': '******', 'JABBER_SERVER': 'jabber.example.net', 'JABBER_CHAT_SERVICE': 'conference.jabber.example.net', 'JABBER_PORT': 5222, 'JABBER_RESOURCE': config.HOSTNAME, 'JABBER_BROADCAST_INTERVAL': 300, 'JABBER_VALIDATE_XML': True, 'JABBER_TRUST_ROOM': False, 'DEPUTY': '*****@*****.**', 'CONVERSATION_RESPONSE_PERIOD': 180, 'JABBER_JOIN_CHATROOM': False, 'JABBER_TEAM_ROSTER': os.path.join(config.DRONED_HOMEDIR, 'teams'), }) #interface required attribute def running(self): """interface requirement""" return bool(self.service) and self.service.running def install(self, _parentService): """interface requirement""" self.parentService = _parentService user = decrypt(self.SERVICECONFIG.JABBER_USERNAME) server = self.SERVICECONFIG.JABBER_SERVER resource = self.SERVICECONFIG.JABBER_RESOURCE self.jid = JID("%(user)s@%(server)s/%(resource)s" % locals()) self.broadcastTask = LoopingCall(self.broadcastPresence) self.sendQueue = [] self.authenticated = False #load all jabber responders, after configuration import droned.responders droned.responders.loadAll() def start(self): """interface requirement""" if self.running(): return self.factory = XMPPClientFactory( self.jid, decrypt(self.SERVICECONFIG.JABBER_PASSWORD)) self.factory.addBootstrap(STREAM_CONNECTED_EVENT, self.connectionMade) self.factory.addBootstrap(STREAM_END_EVENT, self.connectionLost) self.factory.addBootstrap(STREAM_AUTHD_EVENT, self.connectionAuthenticated) self.factory.addBootstrap(STREAM_ERROR_EVENT, self.receivedError) self.factory.addBootstrap(INIT_FAILED_EVENT, self.initFailed) self.service = TCPClient(self.SERVICECONFIG.JABBER_SERVER, self.SERVICECONFIG.JABBER_PORT, self.factory) self.service.setServiceParent(self.parentService) #build/rebuild jabber teams if not os.path.exists(self.SERVICECONFIG.JABBER_TEAM_ROSTER): try: os.makedirs(self.SERVICECONFIG.JABBER_TEAM_ROSTER) except: log('Cannot load team rosters because %s does not exits' % \ self.SERVICECONFIG.JABBER_TEAM_ROSTER) return for name in os.listdir(self.SERVICECONFIG.JABBER_TEAM_ROSTER): f = (self.SERVICECONFIG.JABBER_TEAM_ROSTER, name) if os.path.isfile('%s/%s' % f): Team(name) #preload team rosters def stop(self): """interface requirement""" if self.service: self.factory.stopTrying() self.factory.stopFactory() self.service.disownServiceParent() self.service.stopService() self.service = None def connectionMade(self, xmlstream): log('connection made') self.xmlstream = xmlstream def connectionLost(self, xmlstream): log('connection lost') self.authenticated = False if self.broadcastTask.running: self.broadcastTask.stop() if self.connected: Event('jabber-offline').fire() self.xmlstream = None def connectionAuthenticated(self, xmlstream): log('connection authenticated') self.authenticated = True if not self.broadcastTask.running: self.broadcastTask.start( self.SERVICECONFIG.JABBER_BROADCAST_INTERVAL) xmlstream.addObserver('/message', self.receivedMessage) xmlstream.addObserver('/presence', self.receivedPresence) xmlstream.addObserver('/iq', self.receivedIQ) xmlstream.addObserver('/error', self.receivedError) Event('jabber-online').fire() while self.sendQueue: self.xmlstream.send(self.sendQueue.pop(0)) def broadcastPresence(self): presence = Element(('jabber:client', 'presence')) #log('sending presence broadcast') self.xmlstream.send(presence) def sendMessage(self, to, body, useHTML=True, groupChat=False): message = Element(('jabber:client', 'message')) message['to'] = to message['type'] = (groupChat and 'groupchat') or 'chat' message.addElement('body', None, body) if useHTML: html = message.addElement('html', 'http://jabber.org/protocol/xhtml-im') htmlBody = html.addElement('body', 'http://www.w3.org/1999/xhtml') htmlBody.addRawXml(unicode(body)) if self.SERVICECONFIG.JABBER_VALIDATE_XML: validateXml(html.toXml()) #safeXml = filter(lambda char: ord(char) < 128, message.toXml()) #log('sending message: %s' % safeXml) log('sending message to %s: %s' % (to, body)) if self.authenticated: self.xmlstream.send(message) else: log("not connected, queueing message", warning=True) self.sendQueue.append(message) def requestAuthorization(self, to): request = Element((None, 'iq')) request['type'] = 'set' request['id'] = 'auth-request:%s' % to query = Element((None, 'query')) query['xmlns'] = 'jabber:iq:roster' item = Element((None, 'item')) item['jid'] = to item['name'] = to.split('@')[0] query.addChild(item) request.addChild(query) log('sending auth request: %s' % request.toXml()) self.xmlstream.send(request) def joinChatRoom(self, room): presence = Element((None, 'presence')) presence['from'] = self.jid.userhost() jid = '%s@%s/%s' % (room, self.SERVICECONFIG.JABBER_CHAT_SERVICE, self.SERVICECONFIG.JABBER_CHAT_NICK) presence['to'] = jid x = Element(('http://jabber.org/protocol/muc', 'x')) history = Element((None, 'history')) history['maxchars'] = '0' x.addChild(history) presence.addChild(x) log('sending join: %s' % presence.toXml()) self.xmlstream.send(presence) def leaveChatRoom(self, jid): if '/' not in jid: jid += '/' + self.SERVICECONFIG.JABBER_CHAT_NICK presence = Element((None, 'presence')) presence['from'] = self.jid.userhost() presence['to'] = jid presence['type'] = 'unavailable' log('sending leave: %s' % presence.toXml()) self.xmlstream.send(presence) def receivedMessage(self, e): # Extract the body of the message try: message = str([c for c in e.children if c.name == 'body'][0]) except: log('discarding invalid message (has no body!): %s' % e.toXml()) return # Discard delayed messages delays = [x for x in e.children if x.name == 'delay'] stamps = [ x for x in e.children \ if x.name == 'x' and \ x.compareAttribute('xmlns','jabber:x:delay') and \ x.hasAttribute('stamp') ] #stampstring = str( stamps[0].getAttribute('stamp') ) #timestamp = time.mktime( time.strptime(stampstring, "%Y%m%dT%H:%M:%S") ) if delays or stamps: log('discarding delayed message: %s' % e.toXml()) return # Route message to the right Conversation or ChatRoom entity if e.getAttribute('type') == 'chat': buddy = str(e['from'].split('/')[0]) if not Conversation.exists(buddy): self.requestAuthorization(buddy) log('received message from %s: %s' % (buddy.split('@')[0], message)) Conversation(buddy).hear(message) elif e.getAttribute('type') == 'groupchat': room = e['from'].split('@')[0] log('received message [chatroom=%s]: %s' % (room, message)) ChatRoom(room).hear(message) else: log('received message of unknown type: %s' % e.toXml(), error=True) def receivedPresence(self, e): log('received presence: %s' % e.toXml()) if e.getAttribute('type') == 'subscribe': log('received authorization request from %s' % e['from']) response = Element(('', 'presence')) response['to'] = e['from'] response['type'] = 'subscribed' log('sending auth response: %s' % response.toXml()) self.xmlstream.send(response) buddy = str(e['from']) if not Conversation.exists(buddy): self.requestAuthorization(buddy) elif e.getAttribute('type') == 'unavailable': #fix for openfire jabber server randomly kicking clients out and prevent kicks CHAT = '@%s/%s' % (self.SERVICECONFIG.JABBER_CHAT_SERVICE, self.SERVICECONFIG.JABBER_CHAT_NICK) if e['to'] == self.jid.full() and e['from'].endswith(CHAT) and \ "status code='307'" in e.toXml(): try: log('%s has kicked me' % (e['from'], )) self.joinChatRoom(e['from'].split(CHAT)[0]) log('successfully rejoined room') except: err('Failed to recover from /kick') #elif any(1 for c in e.children if c.name == 'x'): #TODO detect buddies that go offline # if we have a Conversation then unsubscribe .notify from all events def receivedIQ(self, e): log('received iq: %s' % e.toXml()) def receivedError(self, f): log('received error: %s' % str(f)) def initFailed(self, failure): log('Failed to initialize jabber connection:\n%s' % failure.getTraceback()) self.stop() if failure.check(SASLAuthError): log('Will attempt to reconnect in 15 seconds...') config.reactor.callLater(15, self.start)