summaryrefslogtreecommitdiff
path: root/src/core/handlers.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/handlers.py')
-rw-r--r--src/core/handlers.py242
1 files changed, 160 insertions, 82 deletions
diff --git a/src/core/handlers.py b/src/core/handlers.py
index 50dca216..828c39d1 100644
--- a/src/core/handlers.py
+++ b/src/core/handlers.py
@@ -12,14 +12,12 @@ import ssl
import sys
import time
from hashlib import sha1, sha512
-from gettext import gettext as _
from os import path
from slixmpp import InvalidJID
-from slixmpp.stanza import Message
-from slixmpp.xmlstream.stanzabase import StanzaBase
+from slixmpp.xmlstream.stanzabase import StanzaBase, ElementBase
+from xml.etree import ElementTree as ET
-import bookmark
import common
import fixes
import pep
@@ -32,11 +30,64 @@ from config import config, CACHE_DIR
from contact import Resource
from logger import logger
from roster import roster
-from text_buffer import CorrectionError
+from text_buffer import CorrectionError, AckError
from theming import dump_tuple, get_theme
from . commands import dumb_callback
+try:
+ from pygments import highlight
+ from pygments.lexers import get_lexer_by_name
+ from pygments.formatters import HtmlFormatter
+ LEXER = get_lexer_by_name('xml')
+ FORMATTER = HtmlFormatter(noclasses=True)
+ PYGMENTS = True
+except ImportError:
+ PYGMENTS = False
+
+def _join_initial_rooms(self, bookmarks):
+ """Join all rooms given in the iterator `bookmarks`"""
+ for bm in bookmarks:
+ if not (bm.autojoin or config.get('open_all_bookmarks')):
+ continue
+ tab = self.get_tab_by_name(bm.jid, tabs.MucTab)
+ nick = bm.nick if bm.nick else self.own_nick
+ if not tab:
+ self.open_new_room(bm.jid, nick, focus=False)
+ self.initial_joins.append(bm.jid)
+ histo_length = config.get('muc_history_length')
+ if histo_length == -1:
+ histo_length = None
+ if histo_length is not None:
+ histo_length = str(histo_length)
+ # do not join rooms that do not have autojoin
+ # but display them anyway
+ if bm.autojoin:
+ muc.join_groupchat(self, bm.jid, nick,
+ passwd=bm.password,
+ maxhistory=histo_length,
+ status=self.status.message,
+ show=self.status.show)
+
+def check_bookmark_storage(self, features):
+ private = 'jabber:iq:private' in features
+ pep_ = 'http://jabber.org/protocol/pubsub#publish' in features
+ self.bookmarks.available_storage['private'] = private
+ self.bookmarks.available_storage['pep'] = pep_
+ def _join_remote_only(iq):
+ if iq['type'] == 'error':
+ type_ = iq['error']['type']
+ condition = iq['error']['condition']
+ if not (type_ == 'cancel' and condition == 'item-not-found'):
+ self.information('Unable to fetch the remote'
+ ' bookmarks; %s: %s' % (type_, condition),
+ 'Error')
+ return
+ remote_bookmarks = self.bookmarks.remote()
+ _join_initial_rooms(self, remote_bookmarks)
+ if not self.xmpp.anon and config.get('use_remote_bookmarks'):
+ self.bookmarks.get_remote(self.xmpp, self.information, _join_remote_only)
+
def on_session_start_features(self, _):
"""
Enable carbons & blocking on session start if wanted and possible
@@ -47,11 +98,13 @@ def on_session_start_features(self, _):
features = iq['disco_info']['features']
rostertab = self.get_tab_by_name('Roster', tabs.RosterInfoTab)
rostertab.check_blocking(features)
+ rostertab.check_saslexternal(features)
if (config.get('enable_carbons') and
'urn:xmpp:carbons:2' in features):
self.xmpp.plugin['xep_0280'].enable()
self.xmpp.add_event_handler('carbon_received', self.on_carbon_received)
self.xmpp.add_event_handler('carbon_sent', self.on_carbon_sent)
+ self.check_bookmark_storage(features)
self.xmpp.plugin['xep_0030'].get_info(jid=self.xmpp.boundjid.domain,
callback=callback)
@@ -173,11 +226,31 @@ def on_message(self, message):
jid_from = message['from']
for tab in self.get_tabs(tabs.MucTab):
if tab.name == jid_from.bare:
+ if message['type'] == 'chat':
+ return self.on_groupchat_private_message(message)
+ return self.on_normal_message(message)
+
+def on_error_message(self, message):
+ """
+ When receiving any message with type="error"
+ """
+ jid_from = message['from']
+ for tab in self.get_tabs(tabs.MucTab):
+ if tab.name == jid_from.bare:
if message['type'] == 'error':
- return self.room_error(message, jid_from)
+ return self.room_error(message, jid_from.bare)
else:
return self.on_groupchat_private_message(message)
- return self.on_normal_message(message)
+ tab = self.get_conversation_by_jid(message['from'], create=False)
+ error_msg = self.get_error_message(message, deprecated=True)
+ if not tab:
+ return self.information(error_msg, 'Error')
+ error = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_CHAR_NACK),
+ error_msg)
+ if not tab.nack_message('\n' + error, message['id'], message['to']):
+ tab.add_message(error, typ=0)
+ self.refresh_window()
+
def on_normal_message(self, message):
"""
@@ -185,7 +258,7 @@ def on_normal_message(self, message):
muc participant)
"""
if message['type'] == 'error':
- return self.information(self.get_error_message(message, deprecated=True), 'Error')
+ return
elif message['type'] == 'headline' and message['body']:
return self.information('%s says: %s' % (message['from'], message['body']), 'Headline')
@@ -448,7 +521,7 @@ def on_groupchat_message(self, message):
tab = self.get_tab_by_name(room_from, tabs.MucTab)
if not tab:
- self.information(_("message received for a non-existing room: %s") % (room_from))
+ self.information("message received for a non-existing room: %s" % (room_from))
muc.leave_groupchat(self.xmpp, room_from, self.own_nick, msg='')
return
@@ -689,7 +762,10 @@ def on_subscription_request(self, presence):
contact = roster.get_and_set(jid)
roster.update_contact_groups(contact)
contact.pending_in = True
- self.information('%s wants to subscribe to your presence' % jid, 'Roster')
+ self.information('%s wants to subscribe to your presence, '
+ 'use /accept <jid> or /deny <jid> to accept '
+ 'or reject the query.' % jid,
+ 'Roster')
self.get_tab_by_number(0).state = 'highlight'
roster.modified()
if isinstance(self.current_tab(), tabs.RosterInfoTab):
@@ -782,7 +858,7 @@ def on_got_offline(self, presence):
return
jid = presence['from']
if not logger.log_roster_change(jid.bare, 'got offline'):
- self.information(_('Unable to write in the log file'), 'Error')
+ self.information('Unable to write in the log file', 'Error')
# If a resource got offline, display the message in the conversation with this
# precise resource.
if jid.resource:
@@ -806,7 +882,7 @@ def on_got_online(self, presence):
return
roster.modified()
if not logger.log_roster_change(jid.bare, 'got online'):
- self.information(_('Unable to write in the log file'), 'Error')
+ self.information('Unable to write in the log file', 'Error')
resource = Resource(jid.full, {
'priority': presence.get_priority() or 0,
'status': presence['status'],
@@ -843,7 +919,7 @@ def on_failed_connection(self, error):
"""
We cannot contact the remote server
"""
- self.information(_("Connection to remote server failed: %s" % (error,)), _('Error'))
+ self.information("Connection to remote server failed: %s" % (error,), 'Error')
def on_disconnected(self, event):
"""
@@ -854,9 +930,10 @@ def on_disconnected(self, event):
roster.modified()
for tab in self.get_tabs(tabs.MucTab):
tab.disconnect()
- self.information(_("Disconnected from server."), _('Error'))
- if not self.legitimate_disconnect and config.get('auto_reconnect', False):
- self.information(_("Auto-reconnecting."), _('Info'))
+ msg_typ = 'Error' if not self.legitimate_disconnect else 'Info'
+ self.information("Disconnected from server.", msg_typ)
+ if not self.legitimate_disconnect and config.get('auto_reconnect', True):
+ self.information("Auto-reconnecting.", 'Info')
self.xmpp.connect()
def on_stream_error(self, event):
@@ -864,29 +941,29 @@ def on_stream_error(self, event):
When we receive a stream error
"""
if event and event['text']:
- self.information(_('Stream error: %s') % event['text'], _('Error'))
+ self.information('Stream error: %s' % event['text'], 'Error')
def on_failed_all_auth(self, event):
"""
Authentication failed
"""
- self.information(_("Authentication failed (bad credentials?)."),
- _('Error'))
+ self.information("Authentication failed (bad credentials?).",
+ 'Error')
self.legitimate_disconnect = True
def on_no_auth(self, event):
"""
Authentication failed (no mech)
"""
- self.information(_("Authentication failed, no login method available."),
- _('Error'))
+ self.information("Authentication failed, no login method available.",
+ 'Error')
self.legitimate_disconnect = True
def on_connected(self, event):
"""
Remote host responded, but we are not yet authenticated
"""
- self.information(_("Connected to server."), 'Info')
+ self.information("Connected to server.", 'Info')
def on_connecting(self, event):
"""
@@ -901,11 +978,12 @@ def on_session_start(self, event):
self.connection_time = time.time()
if not self.plugins_autoloaded: # Do not reload plugins on reconnection
self.autoload_plugins()
- self.information(_("Authentication success."), 'Info')
- self.information(_("Your JID is %s") % self.xmpp.boundjid.full, 'Info')
+ self.information("Authentication success.", 'Info')
+ self.information("Your JID is %s" % self.xmpp.boundjid.full, 'Info')
if not self.xmpp.anon:
# request the roster
self.xmpp.get_roster()
+ roster.update_contact_groups(self.xmpp.boundjid.bare)
# send initial presence
if config.get('send_initial_presence'):
pres = self.xmpp.make_presence()
@@ -913,37 +991,9 @@ def on_session_start(self, event):
pres['status'] = self.status.message
self.events.trigger('send_normal_presence', pres)
pres.send()
- bookmark.get_local()
- def _join_initial_rooms(bookmarks):
- """Join all rooms given in the iterator `bookmarks`"""
- for bm in bookmarks:
- if bm.autojoin or config.get('open_all_bookmarks'):
- tab = self.get_tab_by_name(bm.jid, tabs.MucTab)
- nick = bm.nick if bm.nick else self.own_nick
- if not tab:
- self.open_new_room(bm.jid, nick, False)
- self.initial_joins.append(bm.jid)
- histo_length = config.get('muc_history_length')
- if histo_length == -1:
- histo_length = None
- if histo_length is not None:
- histo_length = str(histo_length)
- # do not join rooms that do not have autojoin
- # but display them anyway
- if bm.autojoin:
- muc.join_groupchat(self, bm.jid, nick,
- passwd=bm.password,
- maxhistory=histo_length,
- status=self.status.message,
- show=self.status.show)
- def _join_remote_only():
- remote_bookmarks = (bm for bm in bookmark.bookmarks if (bm.method in ("pep", "privatexml")))
- _join_initial_rooms(remote_bookmarks)
- if not self.xmpp.anon and config.get('use_remote_bookmarks'):
- bookmark.get_remote(self.xmpp, _join_remote_only)
- # join all the available bookmarks. As of yet, this is just the local
- # ones
- _join_initial_rooms(bookmark.bookmarks)
+ self.bookmarks.get_local()
+ # join all the available bookmarks. As of yet, this is just the local ones
+ _join_initial_rooms(self, self.bookmarks)
if config.get('enable_user_nick'):
self.xmpp.plugin['xep_0172'].publish_nick(nick=self.own_nick, callback=dumb_callback)
@@ -1024,12 +1074,12 @@ def on_groupchat_subject(self, message):
# Do not display the message if the subject did not change or if we
# receive an empty topic when joining the room.
if nick_from:
- tab.add_message(_("\x19%(info_col)s}%(nick)s set the subject to: %(subject)s") %
+ tab.add_message("\x19%(info_col)s}%(nick)s set the subject to: %(subject)s" %
{'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT), 'nick':nick_from, 'subject':subject},
time=None,
typ=2)
else:
- tab.add_message(_("\x19%(info_col)s}The subject is: %(subject)s") %
+ tab.add_message("\x19%(info_col)s}The subject is: %(subject)s" %
{'subject':subject, 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
time=None,
typ=2)
@@ -1052,7 +1102,10 @@ def on_receipt(self, message):
if not conversation:
return
- conversation.ack_message(msg_id)
+ try:
+ conversation.ack_message(msg_id, self.xmpp.boundjid)
+ except AckError:
+ log.debug('Error while receiving an ack', exc_info=True)
def on_data_form(self, message):
"""
@@ -1083,19 +1136,21 @@ def room_error(self, error, room_name):
Display the error in the tab
"""
tab = self.get_tab_by_name(room_name, tabs.MucTab)
+ if not tab:
+ return
error_message = self.get_error_message(error)
tab.add_message(error_message, highlight=True, nickname='Error',
nick_color=get_theme().COLOR_ERROR_MSG, typ=2)
code = error['error']['code']
if code == '401':
- msg = _('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)')
+ msg = 'To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)'
tab.add_message(msg, typ=2)
if code == '409':
if config.get('alternative_nickname') != '':
self.command_join('%s/%s'% (tab.name, tab.own_nick+config.get('alternative_nickname')))
else:
if not tab.joined:
- tab.add_message(_('You can join the room with an other nick, by typing "/join /other_nick"'), typ=2)
+ tab.add_message('You can join the room with an other nick, by typing "/join /other_nick"', typ=2)
self.refresh_window()
def outgoing_stanza(self, stanza):
@@ -1103,7 +1158,20 @@ def outgoing_stanza(self, stanza):
We are sending a new stanza, write it in the xml buffer if needed.
"""
if self.xml_tab:
- self.add_message_to_text_buffer(self.xml_buffer, '\x191}<--\x19o %s' % stanza)
+ if PYGMENTS:
+ xhtml_text = highlight('%s' % stanza, LEXER, FORMATTER)
+ poezio_colored = xhtml.xhtml_to_poezio_colors(xhtml_text, force=True).rstrip('\x19o').strip()
+ else:
+ poezio_colored = '%s' % stanza
+ self.add_message_to_text_buffer(self.xml_buffer, poezio_colored,
+ nickname=get_theme().CHAR_XML_OUT)
+ try:
+ if self.xml_tab.match_stanza(ElementBase(ET.fromstring(stanza))):
+ self.add_message_to_text_buffer(self.xml_tab.filtered_buffer, poezio_colored,
+ nickname=get_theme().CHAR_XML_OUT)
+ except:
+ log.debug('', exc_info=True)
+
if isinstance(self.current_tab(), tabs.XMLTab):
self.current_tab().refresh()
self.doupdate()
@@ -1113,11 +1181,27 @@ def incoming_stanza(self, stanza):
We are receiving a new stanza, write it in the xml buffer if needed.
"""
if self.xml_tab:
- self.add_message_to_text_buffer(self.xml_buffer, '\x192}-->\x19o %s' % stanza)
+ if PYGMENTS:
+ xhtml_text = highlight('%s' % stanza, LEXER, FORMATTER)
+ poezio_colored = xhtml.xhtml_to_poezio_colors(xhtml_text, force=True).rstrip('\x19o').strip()
+ else:
+ poezio_colored = '%s' % stanza
+ self.add_message_to_text_buffer(self.xml_buffer, poezio_colored,
+ nickname=get_theme().CHAR_XML_IN)
+ try:
+ if self.xml_tab.match_stanza(stanza):
+ self.add_message_to_text_buffer(self.xml_tab.filtered_buffer, poezio_colored,
+ nickname=get_theme().CHAR_XML_IN)
+ except:
+ log.debug('', exc_info=True)
if isinstance(self.current_tab(), tabs.XMLTab):
self.current_tab().refresh()
self.doupdate()
+def ssl_invalid_chain(self, tb):
+ self.information('The certificate sent by the server is invalid.', 'Error')
+ self.disconnect()
+
def validate_ssl(self, pem):
"""
Check the server certificate using the slixmpp ssl_cert event
@@ -1151,40 +1235,34 @@ def validate_ssl(self, pem):
self.information('New certificate found (sha-2 hash:'
' %s)\nPlease validate or abort' % sha2_found_cert,
'Warning')
- input = windows.YesNoInput(text="WARNING! Server certificate has changed, accept? (y/n)")
- self.current_tab().input = input
- input.resize(1, self.current_tab().width, self.current_tab().height-1, 0)
- input.refresh()
- self.doupdate()
- old_loop = asyncio.get_event_loop()
- new_loop = asyncio.new_event_loop()
- asyncio.set_event_loop(new_loop)
- new_loop.add_reader(sys.stdin, self.on_input_readable)
- future = asyncio.Future()
- @asyncio.coroutine
- def check_input(future):
- while input.value is None:
- yield from asyncio.sleep(0.01)
+ def check_input():
self.current_tab().input = saved_input
self.paused = False
if input.value:
self.information('Setting new certificate: old: %s, new: %s' % (cert, sha2_found_cert), 'Info')
log.debug('Setting certificate to %s', sha2_found_cert)
if not config.silent_set('certificate', sha2_found_cert):
- self.information(_('Unable to write in the config file'), 'Error')
+ self.information('Unable to write in the config file', 'Error')
else:
self.information('You refused to validate the certificate. You are now disconnected', 'Info')
- self.xmpp.disconnect()
+ self.disconnect()
new_loop.stop()
asyncio.set_event_loop(old_loop)
- asyncio.async(check_input(future))
+ input = windows.YesNoInput(text="WARNING! Server certificate has changed, accept? (y/n)", callback=check_input)
+ self.current_tab().input = input
+ input.resize(1, self.current_tab().width, self.current_tab().height-1, 0)
+ input.refresh()
+ self.doupdate()
+ old_loop = asyncio.get_event_loop()
+ new_loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(new_loop)
+ new_loop.add_reader(sys.stdin, self.on_input_readable)
+ curses.beep()
new_loop.run_forever()
-
-
else:
log.debug('First time. Setting certificate to %s', sha2_found_cert)
if not config.silent_set('certificate', sha2_found_cert):
- self.information(_('Unable to write in the config file'), 'Error')
+ self.information('Unable to write in the config file', 'Error')
def _composing_tab_state(tab, state):
"""