diff options
author | louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13 <louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13> | 2010-08-31 23:11:02 +0000 |
---|---|---|
committer | louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13 <louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13> | 2010-08-31 23:11:02 +0000 |
commit | e84b23d1ad7dcb3afa3754ff3b1c1eca27a90548 (patch) | |
tree | c56b10c3ba46be749755b201ba69a72c1891fb28 | |
parent | d2fef9112d71c7cee2b5a29cb5887e37fb3c990e (diff) | |
download | poezio-e84b23d1ad7dcb3afa3754ff3b1c1eca27a90548.tar.gz poezio-e84b23d1ad7dcb3afa3754ff3b1c1eca27a90548.tar.bz2 poezio-e84b23d1ad7dcb3afa3754ff3b1c1eca27a90548.tar.xz poezio-e84b23d1ad7dcb3afa3754ff3b1c1eca27a90548.zip |
Switch to Sleekxmpp. fixed #1768
-rw-r--r-- | src/common.py | 42 | ||||
-rw-r--r-- | src/connection.py | 208 | ||||
-rw-r--r-- | src/gui.py | 522 | ||||
-rw-r--r-- | src/handler.py | 2 | ||||
-rw-r--r-- | src/logger.py (renamed from src/logging.py) | 0 | ||||
-rw-r--r-- | src/multiuserchat.py | 392 | ||||
-rw-r--r-- | src/poezio.py | 41 | ||||
-rw-r--r-- | src/room.py | 2 |
8 files changed, 412 insertions, 797 deletions
diff --git a/src/common.py b/src/common.py index 93eca601..75a0b940 100644 --- a/src/common.py +++ b/src/common.py @@ -47,8 +47,6 @@ import errno import time import traceback -import xmpp - ROOM_STATE_NONE = 11 ROOM_STATE_CURRENT = 10 ROOM_STATE_PRIVATE = 15 @@ -120,20 +118,11 @@ def is_in_path(command, return_abs_path=False): pass return False -def get_stripped_jid(jid): - """ - Return the stripped JID (bare representation) - nick@server/resource -> nick@server - """ - if isinstance(jid, basestring): - jid = xmpp.JID(jid) - return jid.getStripped() - def is_jid(jid): """ Return True if this is a valid JID """ - if xmpp.JID(jid).getNode() != '': + if jid.find('@') != -1: return True return False @@ -141,35 +130,34 @@ def jid_get_node(jid): """ nick@server/resource -> nick """ - if isinstance(jid, basestring): - jid = xmpp.JID(jid) - return jid.getNode() + return jid.split('@', 1)[0] def jid_get_domain(jid): """ nick@server/resource -> server """ - if isinstance(jid, basestring): - jid = xmpp.JID(jid) - return jid.getDomain() + return jid.split('@',1)[-1].split('/', 1)[0] -def jid_get_resource(jid): +def jid_get_resource(fulljid): """ nick@server/resource -> resource """ - if isinstance(jid, basestring): - jid = xmpp.JID(jid) - return jid.getResource() + if '/' in fulljid: + return fulljid.split('/', 1)[-1] + else: + return '' + +def jid_get_bare(fulljid): + """ + nick@server/resource -> nick@server + """ + return '%s@%s' % (jid_get_domain(fulljid), jid_get_node(fulljid)) def is_jid_the_same(a, b): """ Compare two bare jids """ - if isinstance(a, basestring): - a = xmpp.JID(a) - if isinstance(b, basestring): - b = xmpp.JID(b) - return a.bareMatch(b) + return jid_get_bare(a) == jid_get_bare(a) DISTRO_INFO = { 'Arch Linux': '/etc/arch-release', diff --git a/src/connection.py b/src/connection.py index 83631da0..9b315539 100644 --- a/src/connection.py +++ b/src/connection.py @@ -24,205 +24,27 @@ from gettext import (bindtextdomain, textdomain, bind_textdomain_codeset, gettext as _) import sys -import threading +import sleekxmpp -import xmpp from config import config -from logging import logger +from logger import logger from handler import Handler from common import jid_get_node, jid_get_domain, is_jid_the_same -class Connection(threading.Thread): +import logging + +class Connection(sleekxmpp.ClientXMPP): """ Receives everything from Jabber and emits the appropriate signals """ - def __init__(self, server, resource): - threading.Thread.__init__(self) - self.handler = Handler() - self.daemon = True # exit the program when this thread exits - if config.get('jid', '') == '': - self.server = server - else: - self.server = jid_get_domain(config.get('jid', '')) - self.resource = resource - self.online = 0 # 1:connected, 2:auth confirmed - self.jid = '' # we don't know our jid yet (anon account) - self.port = config.get('port', 5222) - self.client = xmpp.Client(self.server, debug=[]) - - def run(self): - """ - run in a thread - connect to server - """ - if not self.connect_to_server(self.server, self.port): - self.handler.emit('error', msg='Could not connect to server') - sys.exit(-1) - if not self.authenticate(config.get('jid', '') == ''): - self.handler.emit('error', msg='Could not authenticate to server') - sys.exit(-1) - # TODO, become invisible before sendInitPresence - self.client.sendInitPresence(requestRoster=0) - self.register_handlers() - - self.online = 1 # 2 when confirmation of our auth is received - while 1: - self.process() - - def connect_to_server(self, server, port): - """ - Connect to the server - """ - if config.get('use_proxy','false') == 'true': - return self.client.connect((server, port), - {'host': config.get("proxy_server", ""), - 'port': config.get("proxy_port", 1080), - 'user': config.get("proxy_user", ""), - 'password': config.get("proxy_password", - "") - }) - else: - return self.client.connect((server, port)) - - def authenticate(self, anon=True): - """ - Authenticate to the server - """ - if anon: - try: - self.client.auth(None, "", self.resource) - return True - except TypeError: - self.handler.emit('error', msg=_('Error: Could not authenticate. Please make sure the server you chose (%s) supports anonymous authentication' % (config.get('server', '')))) - return False - else: - password = config.get('password', '') - jid = config.get('jid', '') - auth = self.client.auth(jid_get_node(jid), password, "salut") - return True - - def register_handlers(self): - """ - registers handlers from xmpppy signals - """ - self.client.RegisterHandler('iq', self.on_get_time, typ='get', - ns="urn:xmpp:time") - self.client.RegisterHandler('iq', self.on_get_vcard) - self.client.RegisterHandler('iq', self.on_get_version, typ='get', - ns=xmpp.NS_VERSION) - self.client.RegisterHandler('presence', self.handler_presence) - self.client.RegisterHandler('message', self.handler_message) - - def error_message(self, stanza): - """ - handles the error messages - """ - from_ = stanza.getFrom() - if not from_: - room_name = '' - else: - room_name = from_.getStripped() - self.handler.emit('error-message', room=room_name, - error=stanza.getTag('error'), - msg=stanza.getError()) - raise xmpp.protocol.NodeProcessed - - def handler_presence(self, connection, presence): - """ - check if it's a normal or a muc presence - """ - is_muc = False - tags = presence.getTags('x') - for tag in tags: - if tag.getAttr('xmlns') == 'http://jabber.org/protocol/muc#user': - is_muc = True - if is_muc: - self.handler_muc_presence(connection, presence) - else: - self.handler_normal_presence(connection, presence) - - def handler_normal_presence(self, connection, presence): - """ - handles the non-MUC presences - """ - fro = presence.getFrom() - toj = presence.getAttr('to') - if presence.getType() == 'error': - self.error_message(presence) - return - if not toj or fro == toj: # own presence - self.online = 2 - self.jid = toj - self.handler.emit('on-connected', jid=fro) - - def handler_muc_presence(self, connection, presence): - """ - handles the presence messages - """ - if not connection: - return - self.handler.emit('room-presence', stanza=presence) - raise xmpp.protocol.NodeProcessed - - def handler_delayed_message(self, connection, message): - """ - handles the delayed messages - These are received when we join a muc and we are sent the - recent history - """ - if not connection: - return - self.handler.emit('room-delayed-message', stanza=message) - raise xmpp.protocol.NodeProcessed - - def handler_message(self, connection, message): - """ - handles the common messages - """ - if not connection: - return - if message.getType() == 'error': - self.error_message(message) - return - if message.getType() == 'groupchat': - self.handler.emit('room-message', stanza=message) - else: - self.handler.emit('private-message', stanza=message) - - raise xmpp.protocol.NodeProcessed - - def process(self, timeout=10): - """ - Main connection loop - It just waits for something to process (something is received - or something has to be sent) - """ - if self.online: - self.client.Process(timeout) - else: - logger.warning('disconnecting...') - sys.exit() - - def on_get_version(self, connection, iq): - """ - Handles the iq requesting our software version - """ - if not connection: - return - self.handler.emit('send-version', iq_obj=iq) - - def on_get_time(self, connection, iq): - """ - handles the iq requesting our time - """ - if not connection: - return - self.handler.emit('send-time', iq_obj=iq) - - def on_get_vcard(self, connection, iq): - """ - we received a vcard - """ - from common import debug - debug('\n====\n%s\n\n' % iq) + def __init__(self): + sleekxmpp.ClientXMPP.__init__(self, None, None, ssl=True, + resource=config.get('resource', 'poezio')) + self.registerPlugin('xep_0045') + + def start(self): + # TODO, try multiple servers + if self.connect((config.get('server', 'anon.louiz.org'), + config.get('port', 5222))): + self.process(threaded=True) @@ -33,6 +33,7 @@ from datetime import datetime import common import theme +import multiuserchat as muc from handler import Handler from config import config from window import Window @@ -40,7 +41,7 @@ from user import User from room import Room from message import Message from keyboard import read_char -from common import is_jid_the_same, jid_get_domain, is_jid +from common import is_jid_the_same, jid_get_domain, jid_get_resource, is_jid # http://xmpp.org/extensions/xep-0045.html#errorstatus ERROR_AND_STATUS_CODES = { @@ -61,15 +62,14 @@ class Gui(object): """ User interface using ncurses """ - def __init__(self, stdscr=None, muc=None): - self.init_curses(stdscr) - self.stdscr = stdscr - self.window = Window(stdscr) + def __init__(self, xmpp): + self.stdscr = curses.initscr() + self.init_curses(self.stdscr) + self.xmpp = xmpp + self.window = Window(self.stdscr) self.rooms = [Room('Info', '', self.window)] self.ignores = {} - self.muc = muc - self.commands = { 'help': (self.command_help, u'\_o< KOIN KOIN KOIN'), 'join': (self.command_join, _("Usage: /join [room_name][@server][/nick] [password]\nJoin: Join the specified room. You can specify a nickname after a slash (/). If no nickname is specified, you will use the default_nick in the configuration file. You can omit the room name: you will then join the room you\'re looking at (useful if you were kicked). You can also provide a room_name without specifying a server, the server of the room you're currently in will be used. You can also provide a password to join the room.\nExamples:\n/join room@server.tld\n/join room@server.tld/John\n/join room2\n/join /me_again\n/join\n/join room@server.tld/my_nick password\n/join / password")), @@ -136,14 +136,230 @@ class Gui(object): 'M-b': self.window.input.jump_word_left } - self.handler = Handler() - self.handler.connect('on-connected', self.on_connected) - self.handler.connect('join-room', self.join_room) - self.handler.connect('room-presence', self.room_presence) - self.handler.connect('room-message', self.room_message) - self.handler.connect('private-message', self.private_message) - self.handler.connect('error-message', self.room_error) - self.handler.connect('error', self.information) + # Add handlers + self.xmpp.add_event_handler("session_start", self.on_connected) + self.xmpp.add_event_handler("groupchat_presence", self.on_groupchat_presence) + self.xmpp.add_event_handler("groupchat_message", self.on_groupchat_message) + self.xmpp.add_event_handler("message", self.on_message) + # self.handler = Handler() + # self.handler.connect('on-connected', self.on_connected) + # self.handler.connect('join-room', self.join_room) + # self.handler.connect('room-presence', self.room_presence) + # self.handler.connect('room-message', self.room_message) + # self.handler.connect('private-message', self.private_message) + # self.handler.connect('error-message', self.room_error) + # self.handler.connect('error', self.information) + + def on_connected(self, event): + """ + Called when we are connected and authenticated + """ + self.information(_("Welcome on Poezio \o/!")) + self.information(_("Your JID is %s") % self.xmpp.fulljid) + + rooms = config.get('rooms', '') + if rooms == '' or not isinstance(rooms, str): + return + rooms = rooms.split(':') + for room in rooms: + args = room.split('/') + if args[0] == '': + return + roomname = args[0] + if len(args) == 2: + nick = args[1] + else: + default = os.environ.get('USER') if os.environ.get('USER') else 'poezio' + nick = config.get('default_nick', '') + if nick == '': + nick = default + self.open_new_room(roomname, nick) + muc.join_groupchat(self.xmpp, roomname, nick) + # Todo: SEND VCARD + return + if config.get('jid', '') == '': # Don't send the vcard if we're not anonymous + self.vcard_sender.start() # because the user ALREADY has one on the server + + def on_groupchat_presence(self, presence): + """ + Triggered whenever a presence stanza is received from a user in a multi-user chat room. + Display the presence on the room window and update the + presence information of the concerned user + """ + from_nick = presence['from'].resource + from_room = presence['from'].bare + room = self.get_room_by_name(from_room) + code = presence.find('{jabber:client}status') + status_codes = set([s.attrib['code'] for s in presence.findall('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}status')]) + # Check if it's not an error presence. + if presence['type'] == 'error': + return self.room_error(presence, from_room) + if not room: + return + else: + msg = None + affiliation = presence['muc']['affiliation'] + show = presence['muc']['type'] + status = presence['status'] + role = presence['muc']['role'] + jid = presence['muc']['jid'] + typ = presence['type'] + if not room.joined: # user in the room BEFORE us. + # ignore redondant presence message, see bug #1509 + if from_nick not in [user.nick for user in room.users]: + new_user = User(from_nick, affiliation, show, status, role) + room.users.append(new_user) + if from_nick.encode('utf-8') == room.own_nick: + room.joined = True + new_user.color = theme.COLOR_OWN_NICK + self.add_message_to_room(room, _("Your nickname is %s") % (from_nick)) + if '170' in status_codes: + self.add_message_to_room(room, 'Warning: this room is publicly logged') + else: + change_nick = '303' in status_codes + kick = '307' in status_codes and typ == 'unavailable' + user = room.get_user_by_name(from_nick) + # New user + if not user: + room.users.append(User(from_nick, affiliation, + show, status, role)) + hide_exit_join = config.get('hide_exit_join', -1) + if hide_exit_join != 0: + if not jid.full: + self.add_message_to_room(room, _("%(spec)s [%(nick)s] joined the room") % {'nick':from_nick, 'spec':theme.CHAR_JOIN}, colorized=True) + else: + self.add_message_to_room(room, _("%(spec)s [%(nick)s] (%(jid)s) joined the room") % {'spec':theme.CHAR_JOIN, 'nick':from_nick, 'jid':jid.full}, colorized=True) + # nick change + elif change_nick: + new_nick = presence.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}item').attrib['nick'] + if user.nick == room.own_nick: + room.own_nick = new_nick + # also change our nick in all private discussion of this room + for _room in self.rooms: + if _room.jid is not None and is_jid_the_same(_room.jid, room.name): + _room.own_nick = new_nick + user.change_nick(new_nick) + self.add_message_to_room(room, _('[%(old)s] is now known as [%(new)s]') % {'old':from_nick, 'new':new_nick}, colorized=True) + # rename the private tabs if needed + private_room = self.get_room_by_name('%s/%s' % (from_room, from_nick)) + if private_room: + self.add_message_to_room(private_room, _('[%(old_nick)s] is now known as [%(new_nick)s]') % {'old_nick':from_nick, 'new_nick':new_nick}, colorized=True) + new_jid = private_room.name.split('/')[0]+'/'+new_nick + private_room.jid = private_room.name = new_jid + + # kick + elif kick: + room.users.remove(user) + by = presence.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}item/{http://jabber.org/protocol/muc#user}actor') + reason = presence.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}item/{http://jabber.org/protocol/muc#user}reason') + by = by.attrib['jid'] if by else '' + reason = reason.text# if reason else '' + if from_nick == room.own_nick: # we are kicked + room.disconnect() + if by: + kick_msg = _("%(spec) [You] have been kicked by [%(by)s].") % {'spec': theme.CHAR_KICK, 'by':by} + else: + kick_msg = _("%(spec)s [You] have been kicked.") % {'spec':theme.CHAR_KICK} + # try to auto-rejoin + if config.get('autorejoin', 'false') == 'true': + muc.join_groupchat(self.xmpp, room.name, room.own_nick) + else: + if by: + kick_msg = _("%(spec)s [%(nick)s] has been kicked by %(by)s.") % {'spec':theme.CHAR_KICK, 'nick':from_nick, 'by':by} + else: + kick_msg = _("%(spec)s [%(nick)s] has been kicked") % {'spec':theme.CHAR_KICK, 'nick':from_nick} + if reason: + kick_msg += _(' Reason: %(reason)s') % {'reason': reason} + self.add_message_to_room(room, kick_msg, colorized=True) + + # user quit + elif typ == 'unavailable': + room.users.remove(user) + hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1 + if hide_exit_join == -1 or user.has_talked_since(hide_exit_join): + if not jid.full: + leave_msg = _('%(spec)s [%(nick)s] has left the room') % {'nick':from_nick, 'spec':theme.CHAR_QUIT} + else: + leave_msg = _('%(spec)s [%(nick)s] (%(jid)s) has left the room') % {'spec':theme.CHAR_QUIT, 'nick':from_nick, 'jid':jid.full} + if status: + leave_msg += ' (%s)' % status + self.add_message_to_room(room, leave_msg, colorized=True) + private_room = self.get_room_by_name('%s/%s' % (from_room, from_nick)) + if private_room: + if not status: + self.add_message_to_room(private_room, _('%(spec)s [%(nick)s] has left the room') % {'nick':from_nick, 'spec':theme.CHAR_QUIT}, colorized=True) + else: + self.add_message_to_room(private_room, _('%(spec)s [%(nick)s] has left the room (%(status)s)') % {'nick':from_nick, 'spec':theme.CHAR_QUIT, 'status': status}, colorized=True) + # status change + else: + # build the message + msg = _('%s changed his/her status: ')% from_nick + if affiliation != user.affiliation: + msg += _('affiliation: %s,') % affiliation + if role != user.role: + msg += _('role: %s,') % role + if show != user.show: + msg += _('show: %s,') % show + if status != user.status: + msg += _('status: %s,') % status + msg = msg[:-1] # remove the last "," + hide_status_change = config.get('hide_status_change', -1) if config.get('hide_status_change', -1) >= -1 else -1 + if (hide_status_change == -1 or \ + user.has_talked_since(hide_status_change) or\ + user.nick == room.own_nick)\ + and\ + (affiliation != user.affiliation or\ + role != user.role or\ + show != user.show or\ + status != user.status): + # display the message in the room + self.add_message_to_room(room, msg) + private_room = self.get_room_by_name(from_room) + if private_room: # display the message in private + self.add_message_to_room(private_room, msg) + # finally, effectively change the user status + user.update(affiliation, show, status, role) + if room == self.current_room(): + self.window.user_win.refresh(room.users) + self.window.input.refresh() + doupdate() + + def on_message(self, message): + """ + When receiving private message from a muc OR a normal message + (from one of our contacts) + """ + if message['type'] == 'groupchat': + return None + # Differentiate both type of messages, and call the appropriate handler. + jid_from = message['from'] + for room in self.rooms: + if room.jid is None and room.name == jid_from.bare: # check all the MUC we are in + return self.on_groupchat_private_message(message) + return self.on_normal_message(message) + + def on_groupchat_private_message(self, message): + """ + We received a Private Message (from someone in a Muc) + """ + jid = message['from'] + nick_from = jid.user + room_from = jid.server + room = self.get_room_by_name(jid.full) # get the tab with the private conversation + if not room: # It's the first message we receive: create the tab + room = self.open_private_window(room_from, nick_from.encode('utf-8'), False) + if not room: + return + body = message['body'] + self.add_message_to_room(room, body, None, nick_from) + self.window.input.refresh() + doupdate() + + def on_normal_message(self, message): + """ + When receiving "normal" messages (from someone in our roster) + """ + return def resize_window(self): """ @@ -152,14 +368,14 @@ class Gui(object): self.window.resize(self.stdscr) self.window.refresh(self.rooms) - def main_loop(self, stdscr): + def main_loop(self): """ main loop waiting for the user to press a key """ self.refresh_window() while True: doupdate() - char=read_char(stdscr) + char=read_char(self.stdscr) try: # if this is not a valide utf-8 char, discard it char.decode('utf-8') except UnicodeDecodeError: @@ -205,17 +421,11 @@ class Gui(object): Reset terminal capabilities to what they were before ncurses init """ + # TODO remove me? curses.echo() curses.nocbreak() curses.endwin() - def on_connected(self, jid): - """ - We are connected when authentification confirmation is received - """ - self.information(_("Welcome on Poezio \o/!")) - self.information(_("Your JID is %s") % jid) - def refresh_window(self): """ Refresh everything @@ -223,9 +433,9 @@ class Gui(object): self.current_room().set_color_state(theme.COLOR_TAB_CURRENT) self.window.refresh(self.rooms) - def join_room(self, room, nick): + def open_new_room(self, room, nick, focus=True): """ - join the specified room (muc), using the specified nick + Open a new Tab containing a Muc room, using the specified nick """ r = Room(room, nick, self.window) self.current_room().set_color_state(theme.COLOR_TAB_NORMAL) @@ -236,7 +446,8 @@ class Gui(object): if ro.nb == 0: self.rooms.insert(self.rooms.index(ro), r) break - self.command_win("%s" % r.nb) + if focus: + self.command_win("%s" % r.nb) self.refresh_window() def completion(self): @@ -322,27 +533,28 @@ class Gui(object): self.current_room().scroll_up(self.window.text_win.height-1) self.refresh_window() - def room_error(self, room, error, msg): + def room_error(self, error, room_name): """ Display the error on the room window """ - if not error: - return - room = self.get_room_by_name(room) + room = self.get_room_by_name(room_name) if not room: room = self.get_room_by_name('Info') - code = error.getAttr('code') - typ = error.getAttr('type') - if error.getTag('text'): - body = error.getTag('text').getData() - else: # No description of the error is provided in the stanza - # If it's a standard error, use our own messages + msg = error['error']['type'] + condition = error['error']['condition'] + code = error['error']['code'] + body = error['error']['text'] + if not body: if code in ERROR_AND_STATUS_CODES.keys(): body = ERROR_AND_STATUS_CODES[code] else: - body = _('Unknown error') - self.add_message_to_room(room, _('Error: %(code)s-%(msg)s: %(body)s' % - {'msg':msg, 'code':code, 'body':body})) + body = condition or _('Unknown error') + if code: + self.add_message_to_room(room, _('Error: %(code)s - %(msg)s: %(body)s' % + {'msg':msg, 'body':body, 'code':code})) + else: + self.add_message_to_room(room, _('Error: %(msg)s: %(body)s' % + {'msg':msg, 'body':body})) if code == '401': self.add_message_to_room(room, _('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)')) if code == '409': @@ -352,23 +564,6 @@ class Gui(object): self.add_message_to_room(room, _('You can join the room with an other nick, by typing "/join /other_nick"')) self.refresh_window() - def private_message(self, stanza): - """ - When a private message is received - """ - jid = stanza.getFrom() - nick_from = stanza.getFrom().getResource() - room_from = stanza.getFrom().getStripped() - room = self.get_room_by_name(jid) # get the tab with the private conversation - if not room: # It's the first message we receive: create the tab - room = self.open_private_window(room_from, nick_from.encode('utf-8'), False) - if not room: - return - body = stanza.getBody() - self.add_message_to_room(room, body, None, nick_from) - self.window.input.refresh() - doupdate() - def open_private_window(self, room_name, user_nick, focus=True): complete_jid = room_name.decode('utf-8')+'/'+user_nick for room in self.rooms: # if the room exists, focus it and return @@ -396,40 +591,41 @@ class Gui(object): self.refresh_window() return r - def room_message(self, stanza, date=None): + def on_groupchat_message(self, message): """ - Display the message on the room window + Triggered whenever a message is received from a multi-user chat room. """ - delay_tag = stanza.getTag('delay', namespace='urn:xmpp:delay') - if delay_tag: + # FIXME: not receiving subjects? :/ + delay_tag = message.find('{urn:xmpp:delay}delay') + if delay_tag is not None: delayed = True - date = common.datetime_tuple(delay_tag.getAttr('stamp')) + date = common.datetime_tuple(delay_tag.attrib['stamp']) else: # We support the OLD and deprecated XEP: http://xmpp.org/extensions/xep-0091.html # But it sucks, please, Jabber servers, don't do this :( - delay_tag = stanza.getTag('x', namespace='jabber:x:delay') - if delay_tag: + delay_tag = message.find('{jabber:x:delay}x') + if delay_tag is not None: delayed = True - date = common.datetime_tuple(delay_tag.getAttr('stamp')) + date = common.datetime_tuple(delay_tag.attrib['stamp']) else: delayed = False - if stanza.getType() != 'groupchat': - return # ignore all messages not comming from a MUC - nick_from = stanza.getFrom().getResource() - room_from = stanza.getFrom().getStripped() + date = None + nick_from = message['from'].resource + room_from = message.getMucroom() + room = self.get_room_by_name(room_from) if (self.ignores.has_key(room_from)) and (nick_from in self.ignores[room_from]): return room = self.get_room_by_name(room_from) - if not room: - self.information(_("message received for a non-existing room: %s") % (room_from)) + if not room: + self.information(_("message received for a non-existing room: %s") % (room_from)) return - body = stanza.getBody() - subject = stanza.getSubject() + body = message['body']#stanza.getBody() + subject = message['subject']#stanza.getSubject() if subject: if nick_from: - self.add_message_to_room(room, _("%(nick)s changed the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, date) + self.add_message_to_room(room, _("%(nick)s changed the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, time=date) else: - self.add_message_to_room(room, _("The subject is: %(subject)s") % {'subject':subject}, date) + self.add_message_to_room(room, _("The subject is: %(subject)s") % {'subject':subject}, time=date) room.topic = subject.encode('utf-8').replace('\n', '|') if room == self.current_room(): self.window.topic_win.refresh(room.topic) @@ -442,139 +638,6 @@ class Gui(object): self.refresh_window() doupdate() - def room_presence(self, stanza): - """ - Display the presence on the room window and update the - presence information of the concerned user - """ - from_nick = stanza.getFrom().getResource() - from_room = stanza.getFrom().getStripped() - room = self.get_room_by_name(from_room) - if not room: - return - else: - msg = None - affiliation = stanza.getAffiliation() - show = stanza.getShow() - status = stanza.getStatus() - role = stanza.getRole() - jid = stanza.getJid() - if not room.joined: # user in the room BEFORE us. - # ignore redondant presence message, see bug #1509 - if from_nick not in [user.nick for user in room.users]: - new_user = User(from_nick, affiliation, show, status, role) - room.users.append(new_user) - if from_nick.encode('utf-8') == room.own_nick: - room.joined = True - self.add_message_to_room(room, _("Your nickname is %s") % (from_nick)) - # Check for a 170 status code - for xtag in stanza.getTags('x'): - for child in xtag.getTags('status'): - if child.getAttr('code') == '170': - self.add_message_to_room(room, 'Warning: this room is publicly logged') - new_user.color = theme.COLOR_OWN_NICK - else: - change_nick = stanza.getStatusCode() == '303' - kick = stanza.getStatusCode() == '307' - user = room.get_user_by_name(from_nick) - # New user - if not user: - room.users.append(User(from_nick, affiliation, - show, status, role)) - hide_exit_join = config.get('hide_exit_join', -1) - if hide_exit_join != 0: - if not jid: - self.add_message_to_room(room, _("%(spec)s [%(nick)s] joined the room") % {'nick':from_nick, 'spec':theme.CHAR_JOIN}, colorized=True) - else: - self.add_message_to_room(room, _("%(spec)s [%(nick)s] (%(jid)s) joined the room") % {'spec':theme.CHAR_JOIN, 'nick':from_nick, 'jid':jid}, colorized=True) - # nick change - elif change_nick: - if user.nick == room.own_nick: - room.own_nick = stanza.getNick().encode('utf-8') - # also change our nick in all private discussion of this room - for _room in self.rooms: - if _room.jid is not None and is_jid_the_same(_room.jid, room.name): - _room.own_nick = stanza.getNick() - user.change_nick(stanza.getNick()) - self.add_message_to_room(room, _('[%(old)s] is now known as [%(new)s]') % {'old':from_nick, 'new':stanza.getNick()}, colorized=True) - # rename the private tabs if needed - private_room = self.get_room_by_name(stanza.getFrom()) - if private_room: - self.add_message_to_room(private_room, _('[%(old_nick)s] is now known as [%(new_nick)s]') % {'old_nick':from_nick, 'new_nick':stanza.getNick()}, colorized=True) - new_jid = private_room.name.split('/')[0]+'/'+stanza.getNick() - private_room.jid = new_jid - private_room.name = new_jid - - # kick - elif kick: - room.users.remove(user) - try: - reason = stanza.getReason() - except: - reason = '' - try: - by = stanza.getActor() - except: - by = None - if from_nick == room.own_nick: # we are kicked - room.disconnect() - if by: - self.add_message_to_room(room, _("%(spec) [You] have been kicked by [%(by)s]. Reason: {%(reason)s}") % {'spec': theme.CHAR_KICK, 'by':by, 'reason':reason}, colorized=True) - else: - self.add_message_to_room(room, _("%(spec)s [You] have been kicked. Reason: %(reason)s") % {'reason':reason, 'spec':theme.CHAR_KICK}, colorized=True) - # try to auto-rejoin - if config.get('autorejoin', 'false') == 'true': - self.muc.join_room(room.name, room.own_nick) - else: - if by: - self.add_message_to_room(room, _("%(spec)s [%(nick)s] has been kicked by %(by)s. Reason: %(reason)s") % {'spec':theme.CHAR_KICK, 'nick':from_nick, 'by':by, 'reason':reason}, colorized=True) - else: - self.add_message_to_room(room, _("%(spec)s [%(nick)s] has been kicked. Reason: %(reason)s") % {'nick':from_nick, 'reason':reason, 'spec':theme.CHAR_KICK}, colorized=True) - # user quit - elif status == 'offline' or role == 'none': - room.users.remove(user) - hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1 - if hide_exit_join == -1 or user.has_talked_since(hide_exit_join): - if not jid: - self.add_message_to_room(room, _('%(spec)s [%(nick)s] has left the room') % {'nick':from_nick, 'spec':theme.CHAR_QUIT}, colorized=True) - else: - self.add_message_to_room(room, _('%(spec)s [%(nick)s] (%(jid)s) has left the room') % {'spec':theme.CHAR_QUIT, 'nick':from_nick, 'jid':jid}, colorized=True) - private_room = self.get_room_by_name(stanza.getFrom()) - if private_room: - self.add_message_to_room(private_room, _('%(spec)s [%(nick)s] has left the room') % {'nick':from_nick, 'spec':theme.CHAR_KICK}, colorized=True) - # status change - else: - # build the message - msg = _('%s changed his/her status: ')% from_nick - if affiliation != user.affiliation: - msg += _('affiliation: %s,') % affiliation - if role != user.role: - msg += _('role: %s,') % role - if show != user.show: - msg += _('show: %s,') % show - if status != user.status: - msg += _('status: %s,') % status - msg = msg[:-1] # remove the last "," - hide_status_change = config.get('hide_status_change', -1) if config.get('hide_status_change', -1) >= -1 else -1 - if (hide_status_change == -1 or \ - user.has_talked_since(hide_status_change) or\ - user.nick == room.own_nick)\ - and\ - (affiliation != user.affiliation or\ - role != user.role or\ - show != user.show or\ - status != user.status): - # display the message in the room - self.add_message_to_room(room, msg) - private_room = self.get_room_by_name(stanza.getFrom()) - if private_room: # display the message in private - self.add_message_to_room(private_room, msg) - # finally, effectively change the user status - user.update(affiliation, show, status, role) - if room == self.current_room(): - self.window.user_win.refresh(room.users) - self.window.input.refresh() - doupdate() def add_message_to_room(self, room, txt, time=None, nickname=None, colorized=False): """ @@ -611,10 +674,10 @@ class Gui(object): self.add_message_to_room(self.current_room(), _("Error: unknown command (%s)") % (command)) elif self.current_room().name != 'Info': if self.current_room().jid is not None: - self.muc.send_private_message(self.current_room().name, line) + muc.send_private_message(self.xmpp, self.current_room().name, line) self.add_message_to_room(self.current_room(), line.decode('utf-8'), None, self.current_room().own_nick.decode('utf-8')) else: - self.muc.send_message(self.current_room().name, line) + muc.send_groupchat_message(self.xmpp, self.current_room().name, line) self.window.input.refresh() doupdate() @@ -640,6 +703,8 @@ class Gui(object): """ /whois <nickname> """ + # TODO + return args = arg.split() room = self.current_room() if len(args) != 1: @@ -701,7 +766,9 @@ class Gui(object): if self.current_room().name == 'Info' or not self.current_room().joined: return roomname = self.current_room().name - self.muc.eject_user(roomname, 'kick', nick, reason) + res = muc.eject_user(self.xmpp, roomname, nick, reason) + if res['type'] == 'error': + self.room_error(res, roomname) def command_say(self, arg): """ @@ -710,10 +777,10 @@ class Gui(object): line = arg if self.current_room().name != 'Info': if self.current_room().jid is not None: - self.muc.send_private_message(self.current_room().name, line) + muc.send_private_message(self.xmpp, self.current_room().name, line) self.add_message_to_room(self.current_room(), line.decode('utf-8'), None, self.current_room().own_nick) else: - self.muc.send_message(self.current_room().name, line) + muc.send_groupchat_message(self.xmpp, self.current_room().name, line) self.window.input.refresh() doupdate() @@ -751,7 +818,7 @@ class Gui(object): # use the server of the current room if available # check if the current room's name has a server if is_jid(self.current_room().name): - room += '@%s' % jid_get_domain(self.current_room().name.encode('utf-8')) + room += '@%s' % jid_get_domain(self.current_room().name) else: # no server could be found, print a message and return self.add_message_to_room(self.current_room(), _("You didn't specify a server for the room you want to join")) return @@ -759,14 +826,14 @@ class Gui(object): if len(args) == 2: # a password is provided password = args[1] if r and r.joined: # if we are already in the room - self.add_message_to_room(self.current_room(), _("already in room [%s]") % room) + self.command_win('%s' % (r.nb)) return - self.muc.join_room(room, nick, password) + room = room.lower() + self.xmpp.plugin['xep_0045'].joinMUC(room, nick, password) if not r: # if the room window exists, we don't recreate it. - self.join_room(room, nick) + self.open_new_room(room, nick) else: r.own_nick = nick - # r.own_nick = nick r.users = [] def command_bookmark(self, arg): @@ -852,7 +919,7 @@ class Gui(object): msg = None for room in self.rooms: if room.joined: - self.muc.change_show(room.name, room.own_nick, show, msg) + muc.change_show(self.xmpp, room.name, room.own_nick, show, msg) def command_ignore(self, arg): """ @@ -926,7 +993,7 @@ class Gui(object): else: msg = None if room.joined: - self.muc.quit_room(room.name, room.own_nick, msg) + muc.leave_groupchat(self.xmpp, room.name, room.own_nick, arg) self.rooms.remove(self.current_room()) self.refresh_window() @@ -955,7 +1022,7 @@ class Gui(object): r = self.open_private_window(room.name, user.nick.decode('utf-8')) if r and len(args) > 1: msg = arg[len(nick)+1:] - self.muc.send_private_message(r.name, msg) + muc.send_private_message(r.name, msg) self.add_message_to_room(r, msg.decode('utf-8'), None, r.own_nick) def command_topic(self, arg): @@ -970,7 +1037,7 @@ class Gui(object): subject = ' '.join(args) if not room.joined or room.name == "Info": return - self.muc.change_subject(room.name, subject) + muc.change_subject(self.xmpp, room.name, subject) def command_link(self, arg): """ @@ -1029,7 +1096,7 @@ class Gui(object): room = self.current_room() if not room.joined or room.name == "Info": return - self.muc.change_nick(room.name, nick) + muc.change_nick(self.xmpp, room.name, nick) def information(self, msg): """ @@ -1047,8 +1114,9 @@ class Gui(object): msg = arg else: msg = None - if msg: - self.muc.disconnect(self.rooms, msg) - sleep(0.2) # :( - self.reset_curses() + for room in self.rooms: + if not room.jid and room.name != 'Info': + muc.leave_groupchat(self.xmpp, room.name, room.own_nick, msg) + self.xmpp.disconnect() + self.reset_curses() sys.exit() diff --git a/src/handler.py b/src/handler.py index a8bebe23..5542a7a1 100644 --- a/src/handler.py +++ b/src/handler.py @@ -17,6 +17,8 @@ from singleton import Singleton +#Todo, it's not a singleton. Oh, also, remove-me + class Handler(Singleton): """ This class is the global handler for the software's signals. diff --git a/src/logging.py b/src/logger.py index 1134244f..1134244f 100644 --- a/src/logging.py +++ b/src/logger.py diff --git a/src/multiuserchat.py b/src/multiuserchat.py index bd60641e..41b1ebd5 100644 --- a/src/multiuserchat.py +++ b/src/multiuserchat.py @@ -1,4 +1,3 @@ -# Copyright 2009, 2010 Erwan Briand # Copyright 2010, Florent Le Coz <louizatakk@fedoraproject.org> # This program is free software: you can redistribute it and/or modify @@ -13,331 +12,92 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Implementation of the XEP-0045: Multi-User Chat. +""" +Implementation of the XEP-0045: Multi-User Chat. +Add some facilities that are not available on the XEP_0045 +sleek plugin +""" -from xmpp import NS_MUC_ADMIN, NS_MUC -from xmpp.protocol import Presence, Iq, Message, JID -import xmpp -import common -import threading -import os +import sleekxmpp -from time import (altzone, gmtime, localtime, strftime, timezone) +from xml.etree import cElementTree as ET -from handler import Handler -from config import config -from common import get_stripped_jid -from common import is_jid +from common import debug -class VcardSender(threading.Thread): +def send_private_message(xmpp, jid, line): """ - avatar sending is really slow (don't know why...) - use a thread to send it... + Send a private message """ - def __init__(self, connection): - threading.Thread.__init__(self) - self.connection = connection - self.handler = Handler() + msg = xmpp.makeMessage(jid) + msg['to'] = jid + msg['type'] = 'chat' + msg['body'] = line + msg.send() - def run(self): - self.send_vcard() - - def send_vcard(self): - """ - Method stolen from Gajim (thanks) - ## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com> - ## Junglecow J <junglecow AT gmail.com> - ## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org> - ## Travis Shirk <travis AT pobox.com> - ## Nikos Kouremenos <kourem AT gmail.com> - ## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org> - ## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com> - ## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com> - ## Jean-Marie Traissard <jim AT lapin.org> - ## Stephan Erb <steve-e AT h3c.de> - ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org> - (one of these people coded this method, probably) - """ - if not self.connection: - return - vcard = { - "FN":config.get('full_name', ''), - "URL":config.get('website', ''), - "EMAIL":{ - "USERID":config.get('email', '') - }, - "DESC":config.get('comment', 'A proud Poezio user') - } - photo_file_path = config.get('photo', '../data/poezio_80.png') - (image, mime_type, sha1) = common.get_base64_from_file(photo_file_path) - if image: - vcard['PHOTO'] = {"TYPE":mime_type,"BINVAL":image} - iq = xmpp.Iq(typ = 'set') - iq2 = iq.addChild('vCard', namespace=xmpp.NS_VCARD) - for i in vcard: - if i == 'jid': - continue - if isinstance(vcard[i], dict): - iq3 = iq2.addChild(i) - for j in vcard[i]: - iq3.addChild(j).setData(vcard[i][j]) - elif isinstance(vcard[i], list): - for j in vcard[i]: - iq3 = iq2.addChild(i) - for k in j: - iq3.addChild(k).setData(j[k]) - else: - iq2.addChild(i).setData(vcard[i]) - self.connection.send(iq) - iq = xmpp.Iq(typ = 'set') - iq2 = iq.addChild('vCard', namespace=xmpp.NS_VCARD_UPDATE) - iq2.addChild('PHOTO').setData(sha1) - self.connection.send(iq) - -class MultiUserChat(object): - def __init__(self, connection): - self.connection = connection - self.vcard_sender = VcardSender(self.connection) - - self.rooms = [] - self.rn = {} - - self.own_jid = None - - self.handler = Handler() - self.handler.connect('join-room', self.join_room) - self.handler.connect('on-connected', self.on_connected) - self.handler.connect('send-version', self.send_version) - self.handler.connect('send-time', self.send_time) - - def on_connected(self, jid): - self.own_jid = jid - rooms = config.get('rooms', '') - if rooms == '' or type(rooms) != str: - return - else: - rooms = rooms.split(':') - for room in rooms: - args = room.split('/') - if args[0] == '': - return - roomname = args[0] - if len(args) == 2: - nick = args[1] - else: - default = os.environ.get('USER') if os.environ.get('USER') else 'poezio' - nick = config.get('default_nick', '') - if nick == '': - nick = default - self.handler.emit('join-room', room=roomname, nick=nick) - if config.get('jid', '') == '': # Don't send the vcard if we're not anonymous - self.vcard_sender.start() # because the user ALREADY has one on the server - - def send_message(self, room, message): - mes = Message(to=room) - mes.setBody(message) - mes.setType('groupchat') - self.connection.send(mes) - - def send_private_message(self, user_jid, message): - mes = Message(to=user_jid) - mes.setBody(message) - mes.setType('chat') - self.connection.send(mes) - - def request_vcard(self, room_name, nickname): - """ - Request the vCard of an user, over a MUC or not - """ - request = Iq(typ='get', to='%s/%s'% (room_name, nickname)) - vcard_tag = request.addChild(name='vCard', namespace='vcard-temp') - self.connection.send(request) - - def join_room(self, room, nick, password=None): - """Join a new room""" - pres = Presence(to='%s/%s' % (room, nick)) - pres.setFrom('%s'%self.own_jid) - x_tag = pres.addChild(name='x', namespace=NS_MUC) - if password: - passwd = x_tag.addChild(name='password') - passwd.setData(password) - muc_history_length = config.get('muc_history_length', -1) - if muc_history_length >= 0: - history_tag = x_tag.addChild(name='history') - if muc_history_length == 0: - history_tag.setAttr('maxchars', 0) - else: - history_tag.setAttr('maxstanzas', muc_history_length) - self.connection.send(pres) - - def quit_room(self, room, nick, msg=None): - """Quit a room""" - if room is None and nick is None: - self.on_disconnect() - return - - pres = Presence(to='%s/%s' % (room, nick), typ='unavailable') - if msg: - pres.setStatus(msg) - self.connection.send(pres) - - def disconnect(self, rooms, msg): - """ - """ - for room in rooms: - if room.jid is None and room.joined: - pres = Presence(to='%s' % room.name, - typ='unavailable') - pres.setStatus(msg) - self.connection.send(pres) - - def on_disconnect(self): - """Called at disconnection""" - for room in self.rooms: - pres = Presence(to='%s/%s' % (room, self.rn[room]), - typ='unavailable') - self.connection.send(pres) - - def on_iq(self, iq): - """Receive a MUC iq notification""" - from_ = iq.getFrom().__str__() - - if get_stripped_jid(from_) in self.rooms: - children = iq.getChildren() - for child in children: - if child.getName() == 'error': - code = int(child.getAttr('code')) - msg = None - - echildren = child.getChildren() - for echild in echildren: - if echild.getName() == 'text': - msg = echild.getData() - - self.handler.emit('on-muc-error', - room=from_, - code=code, - msg=msg) - - def on_presence(self, presence): - """Receive a MUC presence notification""" - from_ = presence.getFrom().__str__() - if get_stripped_jid(from_) in self.rooms: - self.handler.emit('on-muc-presence-changed', - jid=from_.encode('utf-8'), - priority=presence.getPriority(), - show=presence.getShow(), - status=presence.getStatus(), - stanza=presence - ) - - def on_message(self, message): - """Receive a MUC message notification""" - from_ = message.getFrom().__str__().encode('utf-8') - - if get_stripped_jid(from_) in self.rooms: - body_ = message.getBody() - type_ = message.getType() - subj_ = message.getSubject() - self.handler.emit('on-muc-message-received', - jid=from_, msg=body_, subject=subj_, - typ=type_, stanza=message) - - def eject_user(self, room, action, nick, reason): - """Eject an user from a room""" - iq = Iq(typ='set', to=room) - query = iq.addChild('query', namespace=NS_MUC_ADMIN) - item = query.addChild('item') - - if action == 'kick': - item.setAttr('role', 'none') - if is_jid(nick): - item.setAttr('jid', nick) - else: - item.setAttr('nick', nick) - elif action == 'ban': - item.setAttr('affiliation', 'outcast') - item.setAttr('jid', nick) - - if reason is not None: - rson = item.addChild('reason') - rson.setData(reason) - - self.connection.send(iq) - - def change_role(self, room, nick, role): - """Change the role of an user""" - iq = Iq(typ='set', to=room) - query = iq.addChild('query', namespace=NS_MUC_ADMIN) - item = query.addChild('item') - item.setAttr('nick', nick) - item.setAttr('role', role) - - self.connection.send(iq) - - def change_aff(self, room, jid, aff): - """Change the affiliation of an user""" - iq = Iq(typ='set', to=room) - query = iq.addChild('query', namespace=NS_MUC_ADMIN) - item = query.addChild('item') - item.setAttr('jid', jid) - item.setAttr('affiliation', aff) - - self.connection.send(iq) - - def change_subject(self, room, subject): - """Change the subject of a room""" - message = Message(typ='groupchat', to=room) - subj = message.addChild('subject') - subj.setData(subject) +def send_groupchat_message(xmpp, jid, line): + """ + Send a message to the groupchat + """ + msg = xmpp.makeMessage(jid) + msg['type'] = 'groupchat' + msg['body'] = line + msg.send() - self.connection.send(message) +def change_show(xmpp, jid, own_nick, show, status): + """ + Change our 'Show' + """ + pres = xmpp.makePresence(pto='%s/%s' % (jid, own_nick), + pfrom=xmpp.fulljid) + if show: # if show is None, don't put a <show /> tag. It means "online" + pres['type'] = show + if status: + pres['status'] = status + debug('Change presence: %s\n' % (pres)) + pres.send() + +def change_subject(xmpp, jid, subject): + """ + Change the room subject + """ + msg = xmpp.makeMessage(jid) + msg['type'] = 'groupchat' + msg['subject'] = subject + msg['from'] = xmpp.jid + msg.send() - def change_nick(self, room, nick): - """Change the nickname""" - pres = Presence(to='%s/%s' % (room, nick)) - self.connection.send(pres) +def change_nick(xmpp, jid, nick): + """ + Change our own nick in a room + """ + xmpp.makePresence(pto='%s/%s' % (jid, nick), + pfrom=xmpp.jid).send() - def change_show(self, room, nick, show, status): - pres = Presence(to='%s/%s' % (room, nick)) - if show: # if show is None, don't put a <show /> tag. It means "online" - pres.setShow(show) - if status: - pres.setStatus(status) - self.connection.send(pres) +def join_groupchat(xmpp, jid, nick, password=None): + """ + Join the groupchat + """ + xmpp.plugin['xep_0045'].joinMUC(jid, nick, password) - def send_version(self, iq_obj): - """ - from gajim and modified - """ - iq_obj = iq_obj.buildReply('result') - qp = iq_obj.getTag('query') - if config.get('send_poezio_info', 'true') == 'true': - qp.setTagData('name', 'Poezio') - qp.setTagData('version', '0.6.3 dev') - else: - qp.setTagData('name', 'Unknown') - qp.setTagData('version', 'Unknown') - if config.get('send_os_info', 'true') == 'true': - qp.setTagData('os', common.get_os_info()) - else: - qp.setTagData('os', 'Unknown') - self.connection.send(iq_obj) - raise xmpp.protocol.NodeProcessed +def leave_groupchat(xmpp, jid, own_nick, msg): + """ + Leave the groupchat + """ + xmpp.plugin['xep_0045'].leaveMUC(jid, own_nick, msg) - def send_time(self, iq_obj): - """ - from gajim - """ - iq_obj = iq_obj.buildReply('result') - qp = iq_obj.setTag('time', - namespace="urn:xmpp:time") - if config.get('send_time', 'true') == 'true': - qp.setTagData('utc', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())) - isdst = localtime().tm_isdst - zone = -(timezone, altzone)[isdst] / 60 - tzo = (zone / 60, abs(zone % 60)) - qp.setTagData('tzo', '%+03d:%02d' % (tzo)) - self.connection.send(iq_obj) - raise xmpp.protocol.NodeProcessed +def eject_user(xmpp, jid, nick, reason): + """ + (try to) Eject an user from the room + """ + iq = xmpp.makeIqSet() + query = ET.Element('{http://jabber.org/protocol/muc#admin}query') + item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {'nick':nick, 'role':'none'}) + if reason: + reason_el = ET.Element('{http://jabber.org/protocol/muc#admin}reason') + reason_el.text = reason + item.append(reason_el) + query.append(item) + iq.append(query) + iq['to'] = jid + return iq.send() diff --git a/src/poezio.py b/src/poezio.py index a37e28b3..22af358e 100644 --- a/src/poezio.py +++ b/src/poezio.py @@ -25,26 +25,6 @@ import threading import sys import traceback -def installThreadExcepthook(): - """ - Workaround for sys.excepthook thread bug - See http://bugs.python.org/issue1230540 - Python, you made me sad :( - """ - init_old = threading.Thread.__init__ - def init(self, *args, **kwargs): - init_old(self, *args, **kwargs) - run_old = self.run - def run_with_except_hook(*args, **kw): - try: - run_old(*args, **kw) - except (KeyboardInterrupt, SystemExit): - raise - except: - sys.excepthook(*sys.exc_info()) - self.run = run_with_except_hook - threading.Thread.__init__ = init - class MyStdErr(object): def __init__(self, fd): """ @@ -59,6 +39,7 @@ class MyStdErr(object): Restaure the good ol' sys.stderr, because we need it in order to print the tracebacks """ + sys.stderr.close() sys.stderr = self.old_stderr my_stderr = MyStdErr(open('/dev/null', 'a')) @@ -77,30 +58,24 @@ def exception_handler(type_, value, trace): sys.excepthook = exception_handler -import sys -import curses import signal from connection import Connection -from multiuserchat import MultiUserChat from config import config from gui import Gui -from curses import initscr signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore ctrl-c def main(): """ - main function + The main function consist of the Connection initialization + then the gui (ncurses) init, connection handlers and then the + connection is "started" """ - resource = config.get('resource', 'poezio') - server = config.get('server', 'anon.louiz.org:jeproteste.info') - connection = Connection(server, resource) - connection.start() - stdscr = initscr() - gui = Gui(stdscr, MultiUserChat(connection.client)) - gui.main_loop(stdscr) + xmpp = Connection() # Connection init + gui = Gui(xmpp) # Gui init. + xmpp.start() # Connect to remote server + gui.main_loop() # Refresh the screen, wait for user events etc if __name__ == '__main__': - installThreadExcepthook() main() diff --git a/src/room.py b/src/room.py index 02aa336f..4fd7a799 100644 --- a/src/room.py +++ b/src/room.py @@ -19,7 +19,7 @@ from datetime import datetime from random import randrange from config import config -from logging import logger +from logger import logger from message import Message import common |