summaryrefslogtreecommitdiff
path: root/src/tabs/xmltab.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/tabs/xmltab.py')
-rw-r--r--src/tabs/xmltab.py225
1 files changed, 173 insertions, 52 deletions
diff --git a/src/tabs/xmltab.py b/src/tabs/xmltab.py
index 083e97c5..6899cd6f 100644
--- a/src/tabs/xmltab.py
+++ b/src/tabs/xmltab.py
@@ -5,52 +5,104 @@ in order to only show the relevant ones, and it can also be frozen or
unfrozen on demand so that the relevant information is not drowned by
the traffic.
"""
-from gettext import gettext as _
-
import logging
log = logging.getLogger(__name__)
import curses
import os
from slixmpp.xmlstream import matcher
-from slixmpp.xmlstream.handler import Callback
+from slixmpp.xmlstream.tostring import tostring
+from slixmpp.xmlstream.stanzabase import ElementBase
+from xml.etree import ElementTree as ET
from . import Tab
+import text_buffer
import windows
from xhtml import clean_text
+from decorators import command_args_parser
+from common import safeJID
+
+
+class MatchJID(object):
+
+ def __init__(self, jid, dest=''):
+ self.jid = jid
+ self.dest = dest
+
+ def match(self, xml):
+ from_ = safeJID(xml['from'])
+ to_ = safeJID(xml['to'])
+ if self.jid.full == self.jid.bare:
+ from_ = from_.bare
+ to_ = to_.bare
+
+ if self.dest == 'from':
+ return from_ == self.jid
+ elif self.dest == 'to':
+ return to_ == self.jid
+ return self.jid in (from_, to_)
+
+ def __repr__(self):
+ return '%s%s%s' % (self.dest, ': ' if self.dest else '', self.jid)
+
+MATCHERS_MAPPINGS = {
+ MatchJID: ('JID', lambda obj: repr(obj)),
+ matcher.MatcherId: ('ID', lambda obj: obj._criteria),
+ matcher.MatchXMLMask: ('XMLMask', lambda obj: tostring(obj._criteria)),
+ matcher.MatchXPath: ('XPath', lambda obj: obj._criteria)
+}
class XMLTab(Tab):
def __init__(self):
Tab.__init__(self)
self.state = 'normal'
self.name = 'XMLTab'
- self.text_win = windows.TextWin()
- self.core.xml_buffer.add_window(self.text_win)
+ self.filters = []
+
+ self.core_buffer = self.core.xml_buffer
+ self.filtered_buffer = text_buffer.TextBuffer()
+
self.info_header = windows.XMLInfoWin()
+ self.text_win = windows.XMLTextWin()
+ self.core_buffer.add_window(self.text_win)
self.default_help_message = windows.HelpText("/ to enter a command")
+
self.register_command('close', self.close,
- shortdesc=_("Close this tab."))
+ shortdesc="Close this tab.")
self.register_command('clear', self.command_clear,
- shortdesc=_('Clear the current buffer.'))
+ shortdesc='Clear the current buffer.')
self.register_command('reset', self.command_reset,
- shortdesc=_('Reset the stanza filter.'))
+ shortdesc='Reset the stanza filter.')
self.register_command('filter_id', self.command_filter_id,
usage='<id>',
- desc=_('Show only the stanzas with the id <id>.'),
- shortdesc=_('Filter by id.'))
+ desc='Show only the stanzas with the id <id>.',
+ shortdesc='Filter by id.')
self.register_command('filter_xpath', self.command_filter_xpath,
usage='<xpath>',
- desc=_('Show only the stanzas matching the xpath <xpath>.'),
- shortdesc=_('Filter by XPath.'))
+ desc='Show only the stanzas matching the xpath <xpath>.'
+ ' Any occurrences of %n will be replaced by jabber:client.',
+ shortdesc='Filter by XPath.')
+ self.register_command('filter_jid', self.command_filter_jid,
+ usage='<jid>',
+ desc='Show only the stanzas matching the jid <jid> in from= or to=.',
+ shortdesc='Filter by JID.')
+ self.register_command('filter_from', self.command_filter_from,
+ usage='<jid>',
+ desc='Show only the stanzas matching the jid <jid> in from=.',
+ shortdesc='Filter by JID from.')
+ self.register_command('filter_to', self.command_filter_to,
+ usage='<jid>',
+ desc='Show only the stanzas matching the jid <jid> in to=.',
+ shortdesc='Filter by JID to.')
self.register_command('filter_xmlmask', self.command_filter_xmlmask,
- usage=_('<xml mask>'),
- desc=_('Show only the stanzas matching the given xml mask.'),
- shortdesc=_('Filter by xml mask.'))
+ usage='<xml mask>',
+ desc='Show only the stanzas matching the given xml mask.',
+ shortdesc='Filter by xml mask.')
self.register_command('dump', self.command_dump,
- usage=_('<filename>'),
- desc=_('Writes the content of the XML buffer into a file.'),
- shortdesc=_('Write in a file.'))
+ usage='<filename>',
+ desc='Writes the content of the XML buffer into a file.',
+ shortdesc='Write in a file.')
self.input = self.default_help_message
self.key_func['^T'] = self.close
self.key_func['^I'] = self.completion
@@ -63,6 +115,34 @@ class XMLTab(Tab):
self.filter_type = ''
self.filter = ''
+ def gen_filter_repr(self):
+ if not self.filters:
+ self.filter_type = ''
+ self.filter = ''
+ return
+ filter_types = map(lambda x: MATCHERS_MAPPINGS[type(x)][0], self.filters)
+ filter_strings = map(lambda x: MATCHERS_MAPPINGS[type(x)][1](x), self.filters)
+ self.filter_type = ','.join(filter_types)
+ self.filter = ','.join(filter_strings)
+
+ def update_filters(self, matcher):
+ if not self.filters:
+ messages = self.core_buffer.messages[:]
+ self.filtered_buffer.messages = []
+ self.core_buffer.del_window(self.text_win)
+ self.filtered_buffer.add_window(self.text_win)
+ else:
+ messages = self.filtered_buffer.messages
+ self.filtered_buffer.messages = []
+ self.filters.append(matcher)
+ new_messages = []
+ for msg in messages:
+ if self.match_stanza(ElementBase(ET.fromstring(clean_text(msg.txt)))):
+ new_messages.append(msg)
+ self.filtered_buffer.messages = new_messages
+ self.text_win.rebuild_everything(self.filtered_buffer)
+ self.gen_filter_repr()
+
def on_freeze(self):
"""
Freeze the display.
@@ -70,58 +150,94 @@ class XMLTab(Tab):
self.text_win.toggle_lock()
self.refresh()
- def command_filter_xmlmask(self, arg):
+ def match_stanza(self, stanza):
+ for matcher in self.filters:
+ if not matcher.match(stanza):
+ return False
+ return True
+
+ @command_args_parser.raw
+ def command_filter_xmlmask(self, mask):
"""/filter_xmlmask <xml mask>"""
try:
- handler = Callback('custom matcher', matcher.MatchXMLMask(arg),
- self.core.incoming_stanza)
- self.core.xmpp.remove_handler('custom matcher')
- self.core.xmpp.register_handler(handler)
- self.filter_type = "XML Mask Filter"
- self.filter = arg
+ self.update_filters(matcher.MatchXMLMask(mask))
self.refresh()
- except:
- self.core.information('Invalid XML Mask', 'Error')
+ except Exception as e:
+ self.core.information('Invalid XML Mask: %s' % e, 'Error')
self.command_reset('')
- def command_filter_id(self, arg):
+ @command_args_parser.raw
+ def command_filter_to(self, jid):
+ """/filter_jid_to <jid>"""
+ jid_obj = safeJID(jid)
+ if not jid_obj:
+ return self.core.information('Invalid JID: %s' % jid, 'Error')
+
+ self.update_filters(MatchJID(jid_obj, dest='to'))
+ self.refresh()
+
+ @command_args_parser.raw
+ def command_filter_from(self, jid):
+ """/filter_jid_from <jid>"""
+ jid_obj = safeJID(jid)
+ if not jid_obj:
+ return self.core.information('Invalid JID: %s' % jid, 'Error')
+
+ self.update_filters(MatchJID(jid_obj, dest='from'))
+ self.refresh()
+
+ @command_args_parser.raw
+ def command_filter_jid(self, jid):
+ """/filter_jid <jid>"""
+ jid_obj = safeJID(jid)
+ if not jid_obj:
+ return self.core.information('Invalid JID: %s' % jid, 'Error')
+
+ self.update_filters(MatchJID(jid_obj))
+ self.refresh()
+
+ @command_args_parser.quoted(1)
+ def command_filter_id(self, args):
"""/filter_id <id>"""
- self.core.xmpp.remove_handler('custom matcher')
- handler = Callback('custom matcher', matcher.MatcherId(arg),
- self.core.incoming_stanza)
- self.core.xmpp.register_handler(handler)
- self.filter_type = "Id Filter"
- self.filter = arg
+ if args is None:
+ return self.core.command_help('filter_id')
+
+ self.update_filters(matcher.MatcherId(args[0]))
self.refresh()
- def command_filter_xpath(self, arg):
+ @command_args_parser.raw
+ def command_filter_xpath(self, xpath):
"""/filter_xpath <xpath>"""
try:
- handler = Callback('custom matcher', matcher.MatchXPath(
- arg.replace('%n', self.core.xmpp.default_ns)),
- self.core.incoming_stanza)
- self.core.xmpp.remove_handler('custom matcher')
- self.core.xmpp.register_handler(handler)
- self.filter_type = "XPath Filter"
- self.filter = arg
+ self.update_filters(matcher.MatchXPath(xpath.replace('%n', self.core.xmpp.default_ns)))
self.refresh()
except:
self.core.information('Invalid XML Path', 'Error')
self.command_reset('')
- def command_reset(self, arg):
+ @command_args_parser.ignored
+ def command_reset(self):
"""/reset"""
- self.core.xmpp.remove_handler('custom matcher')
- self.core.xmpp.register_handler(self.core.all_stanzas)
+ if self.filters:
+ self.filters = []
+ self.filtered_buffer.del_window(self.text_win)
+ self.core_buffer.add_window(self.text_win)
+ self.text_win.rebuild_everything(self.core_buffer)
self.filter_type = ''
self.filter = ''
self.refresh()
- def command_dump(self, arg):
+ @command_args_parser.quoted(1)
+ def command_dump(self, args):
"""/dump <filename>"""
- xml = self.core.xml_buffer.messages[:]
- text = '\n'.join(('%s %s' % (msg.str_time, clean_text(msg.txt)) for msg in xml))
- filename = os.path.expandvars(os.path.expanduser(arg))
+ if args is None:
+ return self.core.command_help('dump')
+ if self.filters:
+ xml = self.filtered_buffer.messages[:]
+ else:
+ xml = self.core_buffer.messages[:]
+ text = '\n'.join(('%s %s %s' % (msg.str_time, msg.nickname, clean_text(msg.txt)) for msg in xml))
+ filename = os.path.expandvars(os.path.expanduser(args[0]))
try:
with open(filename, 'w') as fd:
fd.write(text)
@@ -151,12 +267,17 @@ class XMLTab(Tab):
def on_scroll_down(self):
return self.text_win.scroll_down(self.text_win.height-1)
- def command_clear(self, args):
+ @command_args_parser.ignored
+ def command_clear(self):
"""
/clear
"""
- self.core.xml_buffer.messages = []
- self.text_win.rebuild_everything(self.core.xml_buffer)
+ if self.filters:
+ buffer = self.core_buffer
+ else:
+ buffer = self.filtered_buffer
+ buffer.messages = []
+ self.text_win.rebuild_everything(buffer)
self.refresh()
self.core.doupdate()