r70777 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r70776‎ | r70777 | r70778 >
Date:20:21, 9 August 2010
Author:daniel
Status:deferred
Tags:
Comment:
started standalong udp2xmpp bridge. work in progress
Modified paths:
  • /trunk/extensions/XMLRC/udp2xmpp.py (added) (history)

Diff [purge]

Index: trunk/extensions/XMLRC/udp2xmpp.py
@@ -0,0 +1,261 @@
 2+#!/usr/bin/python
 3+
 4+##############################################################################
 5+# UDP to XMPP relay server for XMLRC
 6+#
 7+#
 8+# Copyright (c) 2010, Wikimedia Deutschland; Author: Daniel Kinzler
 9+# All rights reserved.
 10+#
 11+# This program is free software: you can redistribute it and/or modify
 12+# it under the terms of the GNU General Public License as published by
 13+# the Free Software Foundation, either version 3 of the License, or
 14+# (at your option) any later version.
 15+#
 16+# This program is distributed in the hope that it will be useful,
 17+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 18+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 19+# GNU General Public License for more details.
 20+#
 21+# You should have received a copy of the GNU General Public License
 22+# along with this program. If not, see <http://www.gnu.org/licenses/>.
 23+##############################################################################
 24+
 25+import sys, os, time, select, socket
 26+import xmpp # using the xmpppy library <http://xmpppy.sourceforge.net/>, GPL
 27+import simplexml # using simplexml library <http://pypi.python.org/pypi/simplexml/0.6.1>, GPL
 28+
 29+class Relay:
 30+ def __init__( self, jabber, message_type = 'message', console_encoding = 'utf-8', message_encoding = 'utf-8', udp_buffsize = 8192 ):
 31+ self.jabber = jabber
 32+ self.message_type = message_type
 33+ self.console_encoding = console_encoding
 34+ self.message_encoding = message_encoding
 35+ self.udp_buffsize = udp_buffsize
 36+
 37+ self.targets = {}
 38+
 39+ self.udp_socket = None
 40+
 41+ def register_handlers(self):
 42+ self.jabber.RegisterHandler( 'message', self.process_message )
 43+
 44+ def warn(self, message):
 45+ sys.stderr.write( "WARNING: %s\n" % ( message.encode( self.console_encoding ) ) )
 46+
 47+ def info(self, message):
 48+ sys.stderr.write( "INFO: %s\n" % ( message.encode( self.console_encoding ) ) )
 49+
 50+ def debug(self, message):
 51+ sys.stderr.write( "DEBUG: %s\n" % ( message.encode( self.console_encoding ) ) )
 52+
 53+ def get_all_targets(self):
 54+ return self.targets.keys()
 55+
 56+ def get_target( self, stream_name ):
 57+ return self.get( stream_name )
 58+
 59+ def add_target( self, stream_name, target_jid ):
 60+ return self.targets[ stream_name ] = target_jid
 61+
 62+ def process_message(self, con, message):
 63+ type = message.getType()
 64+ fromjid = message.getFrom().getStripped()
 65+
 66+ if (message.getError()):
 67+ self.warn("received %s error from <%s>: %s\n" % (type, message.getError(), message.getFrom() ))
 68+ elif message.getBody():
 69+ self.debug("discarding %s message from <%s>: %s\n" % (type, message.getFrom(), message.getBody() ))
 70+
 71+ def process_rc_packet(self, data):
 72+ dom = simplexml.simplexml( data )
 73+
 74+ s = self.get_stream_name( dom )
 75+ t = self.get_target( s )
 76+
 77+ if not t:
 78+ self.warn( "no target addres known for %s, discarding message " % s )
 79+ else:
 80+ m = self.get_rc_text( dom )
 81+ self.send_message_to( m, t, dom )
 82+
 83+ def compose_message( self, message, xml = None, mtype = None ):
 84+ if type( message ) == unicode:
 85+ message = message.encode( self.message_encoding )
 86+
 87+ if type( message ) == str:
 88+ if mtype is None:
 89+ mtype = self.message_type
 90+
 91+ message = xmpp.protocol.Message( to, body= message, type= mtype )
 92+
 93+ if xml:
 94+ message.addChild( node = xml )
 95+ else:
 96+ if xml:
 97+ raise Exception("Message already composed, can't attach XML!")
 98+
 99+ if mtype is not None and mtype != message.getType():
 100+ raise Exception("Message already composed with incompatible type! ( %s != %s )" % (mtype, message.getType()) )
 101+
 102+ return message
 103+
 104+ def send_message_to( self, message, to, xml = None, mtype = None ):
 105+ message = self.compose_message( message, mtype = mtype, xml = xml )
 106+
 107+ return self.jabber.send( message )
 108+
 109+ def broadcast_message( self, message, xml = None, mtype = None ):
 110+ message = self.compose_message( message, mtype = mtype, xml = xml )
 111+ targets = self.get_all_targets()
 112+
 113+ for t in targets:
 114+ self.send_message_to( message, t )
 115+
 116+ def process_command(self, command):
 117+ if ( command == 'quit' ):
 118+ self.online = False
 119+ elif ( command.startswith( '/' ) ):
 120+ self.broadcast_message( command )
 121+
 122+ def udp_connect( self, port, host = '0.0.0.0' ):
 123+ self.udp_socket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
 124+ self.udp_socket.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
 125+ self.udp_socket.bind( (host, port) )
 126+
 127+ def xmpp_connect( self, jid, password ):
 128+ con= self.jabber.connect()
 129+
 130+ if not con:
 131+ self.warn( 'could not connect!' )
 132+ return False
 133+
 134+ self.debug( 'connected with %s' % con )
 135+
 136+ auth= self.jabber.auth( jid.getNode(), password, resource= jid.getResource() )
 137+
 138+ if not auth:
 139+ self.warn( 'could not authenticate as %s!' %s jid )
 140+ return False
 141+
 142+ self.debug('authenticated using %s as %s' % ( auth, jid ) )
 143+
 144+ self.register_handlers()
 145+
 146+ self.info( 'connected %s' % ( jid ) )
 147+
 148+ self.on_xmpp_connect()
 149+
 150+ return con
 151+
 152+ def on_xmpp_connect( self ):
 153+ self.jabber.sendInitPresence(self)
 154+ self.roster = self.jabber.getRoster()
 155+
 156+ def service_loop( self ):
 157+ udp_socket = self.udp_socket
 158+ xml_socket = self.jabber.Connection._sock
 159+ stdin_socket = sys.stdin
 160+
 161+ self.online = 1
 162+
 163+ while self.online:
 164+ (i , o, e) = select.select(socketlist.keys(),[],[],1)
 165+ for sock in i:
 166+ if sock == xmpp_socket:
 167+ self.jabber.Process(1)
 168+
 169+ if not cl.isConnected():
 170+ self.warn("connection lost, reconnecting...")
 171+ self.jabber.reconnectAndReauth()
 172+ self.warn("re-connect successful.")
 173+ self.on_xmpp_connect()
 174+
 175+ elif sock == udp_socket:
 176+ msg = socket.recvfrom( self.udp_buffsize )
 177+
 178+ bot.process_rc_packet( msg )
 179+
 180+ elif sock == stdio_socket:
 181+ msg = sys.stdin.readline().rstrip('\r\n')
 182+
 183+ if (msg.startswith('/')):
 184+ bot.process_command( msg )
 185+ else:
 186+ bot.broadcast_message( msg )
 187+
 188+ else:
 189+ raise Exception("Unknown socket: %s" % repr(sock))
 190+
 191+ # FIXME: error recovery (especially when send failed)
 192+
 193+ for sock in e:
 194+ raise Exception("Error in socket: %s" % repr(sock))
 195+
 196+ self.info("service loop terminated, disconnecting")
 197+ self.jabber.disconnect()
 198+ self.udp_socket.close()
 199+ self.info("done.")
 200+
 201+class ChatRelay ( Relay ):
 202+ def __init__( self, jabber, args** ):
 203+ super( ChatRelay, self ).__init__( jabber, **args )
 204+
 205+
 206+class GroupChatRelay (ChatRelay):
 207+ def __init__( self, jabber, group_nick, args** ):
 208+ super( ChatRelay, self ).__init__( jabber, room_jid, **args )
 209+
 210+ self.group_nick = group_nick;
 211+
 212+ def add_target( self, stream_name, target_jid ):
 213+ super(ChatRelay, self).add_target(stream_name, target_jid)
 214+ self.join_muc( target_jid, self.group_nick )
 215+
 216+ def join_muc(self, room, nick):
 217+ # use our own desired nickname as resource part of the group's JID
 218+ jid = room.__str__( wresource= 0 ) + "/" + nick;
 219+
 220+ #create presence stanza
 221+ join = xmpp.Presence( to= jid )
 222+
 223+ #announce full MUC support
 224+ join.addChild( name = 'x', namespace = 'http://jabber.org/protocol/muc' )
 225+
 226+ self.jabber.send( join )
 227+
 228+ self.info('joined room %s' % room)
 229+
 230+ return True
 231+
 232+if __name__ == '__main__':
 233+
 234+ if len(sys.argv) < 4:
 235+ print "Syntax: ytalk tojis myjid mypass"
 236+ sys.exit(0)
 237+
 238+ tojid = sys.argv[1]
 239+ myjid = sys.argv[2]
 240+ mypass = sys.argv[3]
 241+ group = None
 242+ type = 'chat'
 243+
 244+ jid=xmpp.protocol.JID(myjid)
 245+
 246+ if tojid.startswith('#'):
 247+ tojid = tojid[1:]
 248+ group = tojid + "/" + jid.getNode()
 249+ type = 'groupchat'
 250+
 251+ cl=xmpp.Client(jid.getDomain(),debug=[])
 252+
 253+ bot=Bot(cl,tojid,type)
 254+
 255+ if not bot.xmpp_connect():
 256+ sys.stderr.write("Could not connect to server, or password mismatch!\n")
 257+ sys.exit(1)
 258+
 259+ if group and not bot.xmpp_join(group):
 260+ sys.stderr.write("Could not join group "+group+"!\n")
 261+ sys.exit(1)
 262+

Status & tagging log