summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/default_config.cfg8
-rw-r--r--doc/source/commands.rst6
-rw-r--r--doc/source/configuration.rst18
-rw-r--r--doc/source/dev/events.rst32
-rw-r--r--doc/source/keys.rst16
-rw-r--r--doc/source/usage.rst51
-rw-r--r--plugins/otr.py67
-rw-r--r--plugins/quote.py22
-rw-r--r--plugins/tell.py5
-rw-r--r--src/core/commands.py2
-rw-r--r--src/core/handlers.py72
-rw-r--r--src/tabs/conversationtab.py28
-rw-r--r--src/tabs/muctab.py7
-rw-r--r--src/tabs/xmltab.py17
-rw-r--r--src/windows/input_placeholders.py6
15 files changed, 268 insertions, 89 deletions
diff --git a/data/default_config.cfg b/data/default_config.cfg
index ef863ee4..c1f766b0 100644
--- a/data/default_config.cfg
+++ b/data/default_config.cfg
@@ -116,9 +116,12 @@ use_bookmarks_method =
# use this option to force the use of local bookmarks
# possible values are: anything/false
-
use_remote_bookmarks = true
+# Whether you want all bookmarks, even those without
+# autojoin, to be open on startup
+open_all_bookmarks = false
+
# What will be put after the name, when using autocompletion at the
# beginning of the input. A space will always be added after that
after_completion = ,
@@ -461,8 +464,9 @@ show_useless_separator = false
exec_remote = false
# Path of the FIFO in which the remote commands will be sent.
+# The "poezio.fifo" file will be created in this directory
# Used with exec_remote set to true, see the documentation of /link for details
-# Defaults to ./poezio.fifo
+# Defaults to ./
remote_fifo_path =
# Defines if all tabs are resized at the same time (if set to false)
diff --git a/doc/source/commands.rst b/doc/source/commands.rst
index fc3aefa4..449d4095 100644
--- a/doc/source/commands.rst
+++ b/doc/source/commands.rst
@@ -541,10 +541,16 @@ XML tab commands
~~~~~~~~~~~~~~~~
.. glossary::
+ :sorted:
/clear [XML tab version]
Clear the current buffer.
+ /dump
+ **Usage:** ``/dump <filename>``
+
+ Write the content of the XML buffer into a file.
+
/reset
Reset the stanza filter.
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index cb2fbddb..32d82f7a 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -62,8 +62,11 @@ and certificate validation.
**Default value:** ``[empty]``
- The fingerprint of the SSL certificate as a hexadecimal string, you should
- not touch it, except if know what you are doing.
+ The SHA-2 fingerprint of the SSL certificate as a hexadecimal string,
+ you should not touch it, except if know what you are doing.
+
+ .. note:: the fingerprint was previously stored in SHA-1, and has been
+ silently upgraded to SHA-2 if the SHA-1 still matched.
ciphers
@@ -194,6 +197,13 @@ Options related to account configuration, nickname…
The status message poezio will send when connecting.
+ open_all_bookmarks
+
+ **Default value:** ``false``
+
+ If this option is set to ``true``, all remote bookmarks, even
+ those that do not have autojoin, will be opened on startup.
+ (the tabs without autojoin will not be joined)
@@ -860,9 +870,11 @@ Other
remote_fifo_path
- **Default value:** ``./poezio.fifo``
+ **Default value:** ``./``
The path of the FIFO used to send the commands (see the :term:`exec_remote` option).
+ Poezio will try to create a :file:`poezio.fifo` file in this directory.
+
save_status
diff --git a/doc/source/dev/events.rst b/doc/source/dev/events.rst
index 7092d66b..770445a0 100644
--- a/doc/source/dev/events.rst
+++ b/doc/source/dev/events.rst
@@ -53,10 +53,11 @@ The following events are poezio-only events, for SleekXMPP events, check out
- **message:** :py:class:`~sleekxmpp.Message` that will be sent
- **tab:** :py:class:`~tabs.ConversationTab` source
- Same thing than :term:`conversation_say`, but after XHTML generation of the body, if needed.
- This means you must not insert any colors in the body in the handler, since
- it may lead to send invalid XML. This hook is less safe than :term:`conversation_say` and
- you should probably not need it.
+ Same thing than :term:`conversation_say`, but after XHTML generation
+ of the body, if needed. This means you must not insert any colors
+ in the body in the handler, since it may lead to send
+ invalid XML. This hook is less safe than :term:`conversation_say`
+ and you should probably not need it.
muc_msg
- **message:** :py:class:`~sleekxmpp.Message` received
@@ -96,7 +97,7 @@ The following events are poezio-only events, for SleekXMPP events, check out
normal_presence
- **presence:** :py:class:`~sleekxmpp.Presence` received
- - **resource:** :py:class:`Resource <str>` that emitted the :py:class:`~sleekxmpp.Presence`
+ - **resource:** :py:class:`Resource <str>` that emitted the :py:class:`~sleekxmpp.Presence`
Triggered when a presence is received from a contact.
@@ -104,18 +105,26 @@ The following events are poezio-only events, for SleekXMPP events, check out
- **presence:** :py:class:`~sleekxmpp.Presence` received
- **tab:** :py:class:`~tabs.MucTab` source
- Triggered when a presence is received from someone in a :py:class:`~tabs.MucTab`.
+ Triggered when a presence is received from someone in a
+ :py:class:`~tabs.MucTab`.
joining_muc
- **presence:** :py:class:`~~sleekxmpp.Presence` to be sent
+ Triggered when joining a MUC. The presence can thus be modified
+ before being sent.
+
+ changing_nick
+ - **presence:** :py:class:`~~sleekxmpp.Presence` to be sent
- Triggered when joining a MUC. The presence can thus be modified before being sent.
+ Triggered when the user changes his/her nickname on a MUC. The
+ presence can thus be modified before being sent.
send_normal_presence
- **presence:** :py:class:`~sleekxmpp.Presence` sent
- Triggered when poezio sends a new :py:class:`~sleekxmpp.Presence` stanza. The presence can thus be modified before being sent.
+ Triggered when poezio sends a new :py:class:`~sleekxmpp.Presence`
+ stanza. The presence can thus be modified before being sent.
muc_join
- **presence:** :py:class:`~sleekxmpp.Presence` received
@@ -148,7 +157,8 @@ The following events are poezio-only events, for SleekXMPP events, check out
- **message**:py:class:`~sleekxmpp.Message` received
- **tab:** :py:class:`~tabs.PrivateTab` source
- Triggered when a private message (that goes in a :py:class:`~tabs.PrivateTab`)
- is ignored automatically by poezio.
+ Triggered when a private message (that goes in a
+ :py:class:`~tabs.PrivateTab`) is ignored automatically by poezio.
- **tab** is always ``None``, except when a tab has already been opened.
+ **tab** is always ``None``, except when a tab has already been
+ opened.
diff --git a/doc/source/keys.rst b/doc/source/keys.rst
index d0b06eed..c590fffb 100644
--- a/doc/source/keys.rst
+++ b/doc/source/keys.rst
@@ -15,6 +15,8 @@ Key bindings listing
--------------------
Some key bindings are available only in some tabs, others are global.
+.. _global-keys:
+
Global keys
~~~~~~~~~~~
These keys work in **any** tab.
@@ -46,6 +48,8 @@ highlight > message > non-empty input).
**Alt-C**: Scroll the information buffer down.
+.. _input-keys:
+
Input keys
~~~~~~~~~~
These keys concern only the inputs.
@@ -75,9 +79,11 @@ for example in conjunction with the bind command, to help you know how to
bind something to a key combination without having to remember how to write
them by hand.
+.. _chattab-keys:
Chat tab input keys
~~~~~~~~~~~~~~~~~~~
+
These keys work in any conversation tab (MultiUserChat, Private or
Conversation tabs).
@@ -118,6 +124,8 @@ current conversation, if any.
- u: Underlined
- o: Stop formatting
+.. _muctab-keys:
+
MultiUserChat tab input keys
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -133,8 +141,11 @@ These keys work only in the MultiUserChat tab.
**tabulation**: Complete a nick.
+.. _muclisttab-keys:
+
MultiUserChat List tab input keys
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
These keys work only in the MultiUserChat List tab (obtained with :term:`/list`).
**Up**: Go up one row.
@@ -152,6 +163,8 @@ These keys work only in the MultiUserChat List tab (obtained with :term:`/list`)
**PageDown**: Scroll a page of chats down.
+.. _rostertab-keys:
+
Roster tab input keys
~~~~~~~~~~~~~~~~~~~~~
@@ -184,8 +197,11 @@ These keys work only in the Roster tab (the tab number 0).
**PageDown**: Scroll a page of contacts down.
+.. _forms-keys:
+
Data Forms tab keys
~~~~~~~~~~~~~~~~~~~
+
**Ctrl+y**: Validate the form, send it and close the tab.
**Ctrl+g**: Cancel that form (do not send your changes) and close the
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index c4d49fde..726198e3 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -8,13 +8,13 @@ Poezio is composed of tabs which can be of various types. Each tab type has
a distinct interface, list of commands and list of key shortcuts, in addition
to the global commands and key shortcuts.
-The Tab list
-~~~~~~~~~~~~
+Tab list
+~~~~~~~~
-Since Poezio 0.7.5, there are now two ways to show the available tabs:
+There are two ways of showing the open tabs:
-The old way: horizontal list
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Horizontal list
+^^^^^^^^^^^^^^^
This is the default method.
@@ -24,8 +24,8 @@ has a number, each time you open a new tab, it gets the next available number.
.. figure:: ./images/tab_bar.png
:alt: Example of 5 opened tabs
-The new way: vertical list
-^^^^^^^^^^^^^^^^^^^^^^^^^^
+Vertical list
+^^^^^^^^^^^^^
On all tabs, you get a pane on the left side of the screen that shows a list
of the opened tabs. As stated above, each tab has a number, and each time you
@@ -57,6 +57,8 @@ Generalities
:ref:`global-commands`
+:ref:`Global shortcuts <global-keys>`
+
The tab numbered **0** is always the **roster** tab, the other tabs can be of any
type.
@@ -65,11 +67,11 @@ type.
The status of a tab is represented by its color:
-* **blue** (tab **0**): an inactive tab of any type, nothing new to see
+* **Blue** (tab **0**): an inactive tab of any type, nothing new to see
there.
-* **purple** (tab **1**): a :ref:`muctab` with at least one new
+* **Purple** (tab **1**): a :ref:`muctab` with at least one new
unread message.
-* **green** (tab **2**): a tab of a private conversation (:ref:`privatetab` or :ref:`conversationtab`)
+* **Green** (tab **2**): a tab of a private conversation (:ref:`privatetab` or :ref:`conversationtab`)
with a new message to read.
* **Cyan** (tab **3**): the current tab.
* **Red** (tab **4**): a :ref:`muctab` with at least one new highlight
@@ -92,19 +94,22 @@ Roster tab
:ref:`Specific commands <rostertab-commands>`
+:ref:`Specific shortcuts <rostertab-keys>`
+
This is a unique tab, always numbered **0**. It contains the list of your
-contacts. You can add/remove/edit/search contacts from there, and you can open
-a conversation with them.
+contacts. You can add (:term:`/add`, :term:`/accept`), remove
+(:term:`/remove`) and search contacts from there, and you can open
+a conversation with them (``Enter`` key).
-Use the **direction arrows** to browse the list, the ``Space`` key to fold or unfold a group
-or a contact.
+Use the **direction arrows** (↑↓) to browse the list, the ``Space`` key to
+fold or unfold a group or a contact.
.. figure:: ./images/roster.png
:alt: The roster tab
-#. The area where information messages are displayed.
-#. The actual list of contacts. The first level is group, the second is the
- contacts and the third is the resources of you online contacts.
+#. Area where information messages are displayed.
+#. Actual list of contacts. The first level is group, the second is the
+ contacts and the third is the resources of your online contacts.
#. More informations about the selected contact.
.. _muctab:
@@ -114,6 +119,10 @@ MultiUserChat tab
:ref:`Specific commands <muctab-commands>`
+:ref:`Specific shortcuts <muctab-keys>`
+
+:ref:`Chat shortcuts <chattab-keys>`
+
This tab contains a multi-user conversation.
.. figure:: ./images/muc.png
@@ -164,6 +173,8 @@ Private tab
~~~~~~~~~~~
:ref:`Specific commands <privatetab-commands>`
+:ref:`Chat shortcuts <chattab-keys>`
+
This is the tab opened with the :term:`/query` command in a :ref:`muctab`, letting you talk in private
with a participant of a multi-user chat.
@@ -180,6 +191,8 @@ Conversation tab
:ref:`Specific commands <conversationtab-commands>`
+:ref:`Chat shortcuts <chattab-keys>`
+
A tab opened from the roster or :term:`/message`, to talk in private with one of your contacts.
.. figure:: ./images/conversation.png
@@ -194,6 +207,8 @@ status message of the contact. Plugins may add some elements to the status line.
Dataforms tab
~~~~~~~~~~~~~
+:ref:`Specific shortcuts <formtab-keys>`
+
This tab lets you view a form received from a remote entity, edit the values and
send everything back. It is mostly used to configure MUCs with the
:term:`/configure` command but can actually be used for almost anything.
@@ -211,6 +226,8 @@ You can then send the completed form using ``Ctrl+y`` or cancel using ``Ctrl+g``
List tab
~~~~~~~~
+:ref:`Specific shortcuts <muclisttab-keys>`
+
This tab lists all public rooms on a MUC service (with the :term:`/list` command).
It is currently very limited but will be improved in the future. There currently
is no way to search a room.
diff --git a/plugins/otr.py b/plugins/otr.py
index 74b06cf1..c2e5a663 100644
--- a/plugins/otr.py
+++ b/plugins/otr.py
@@ -41,8 +41,8 @@ Install the python module:
cd pure-python-otr
python3 setup.py install --user
-You can also use pip with the requirements.txt at the root of
-the poezio directory.
+You can also use pip in a virtualenv (built-in as pyvenv_ with python since 3.3)
+with the requirements.txt at the root of the poezio directory.
Usage
@@ -143,25 +143,34 @@ Configuration
Allow OTRv1
+ timeout
+ **Default:** ``3``
+
+ The number of seconds poezio will wait until notifying you
+ that the OTR session was not established. A negative or null
+ value will disable this notification.
+
log
**Default:** false
Log conversations (OTR start/end marker, and messages).
-The :term:`allow_v1`, :term:`allow_v2`, :term:`decode_html`
+The :term:`allow_v1`, :term:`allow_v2`, :term:`decode_xhtml`
and :term:`log` configuration parameters are tab-specific.
Important details
-----------------
-The OTR session is considered for a full jid, but the trust is considered
-with a bare JID. This is important to know in the case of Private Chats, since
-you cannot always get the real the JID of your contact (or check if the same
-nick is used by different people).
+The OTR session is considered for a full JID (e.g. toto@example/**client1**),
+but the trust is set with a bare JID (e.g. toto@example). This is important
+in the case of Private Chats (in a chatroom), since you cannot always get the
+real JID of your contact (or check if the same nick is used by different people).
.. _Off The Record messaging: http://wiki.xmpp.org/web/OTR
+.. _pyvenv: https://docs.python.org/3/using/scripts.html#pyvenv-creating-virtual-environments
"""
+
from gettext import gettext as _
import potr
import logging
@@ -588,6 +597,7 @@ class Plugin(BasePlugin):
name = tab.name
color_jid = '\x19%s}' % dump_tuple(get_theme().COLOR_MUC_JID)
color_info = '\x19%s}' % dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ color_normal = '\x19%s}' % dump_tuple(get_theme().COLOR_NORMAL_TEXT)
if isinstance(tab, DynamicConversationTab) and tab.locked_resource:
name = safeJID(tab.name)
name.resource = tab.locked_resource
@@ -597,15 +607,54 @@ class Plugin(BasePlugin):
context.disconnect()
elif arg == 'start' or arg == 'refresh':
otr = self.get_context(name)
+ secs = self.config.get('timeout', 3)
+ def notify_otr_timeout():
+ if otr.state != STATE_ENCRYPTED:
+ text = _('%(jid_c)s%(jid)s%(info)s did not enable'
+ ' OTR after %(sec)s seconds.') % {
+ 'jid': tab.name,
+ 'info': color_info,
+ 'jid_c': color_jid,
+ 'sec': secs}
+ tab.add_message(text, typ=0)
+ self.core.refresh_window()
+ if secs > 0:
+ event = self.api.create_delayed_event(secs, notify_otr_timeout)
+ self.api.add_timed_event(event)
self.core.xmpp.send_message(mto=name, mtype='chat',
mbody=self.contexts[name].sendMessage(0, b'?OTRv?').decode())
+ text = _('%(info)sOTR request to %(jid_c)s%(jid)s%(info)s sent.') % {
+ 'jid': tab.name,
+ 'info': color_info,
+ 'jid_c': color_jid}
+ tab.add_message(text, typ=0)
elif arg == 'ourfpr':
fpr = self.account.getPrivkey()
- self.api.information('Your OTR key fingerprint is %s' % fpr, 'OTR')
+ text = _('%(info)sYour OTR key fingerprint is %(norm)s%(fpr)s.') % {
+ 'jid': tab.name,
+ 'info': color_info,
+ 'norm': color_normal,
+ 'fpr': fpr}
+ tab.add_message(text, typ=0)
elif arg == 'fpr':
if name in self.contexts:
ctx = self.contexts[name]
- self.api.information('The key fingerprint for %s is %s' % (name, ctx.getCurrentKey()) , 'OTR')
+ if ctx.getCurrentKey() is not None:
+ text = _('%(info)sThe key fingerprint for %(jid_c)s'
+ '%(jid)s%(info)s is %(norm)s%(fpr)s%(info)s.') % {
+ 'jid': tab.name,
+ 'info': color_info,
+ 'norm': color_normal,
+ 'jid_c': color_jid,
+ 'fpr': ctx.getCurrentKey()}
+ tab.add_message(text, typ=0)
+ else:
+ text = _('%(jid_c)s%(jid)s%(info)s has no'
+ ' key currently in use.') % {
+ 'jid': tab.name,
+ 'info': color_info,
+ 'jid_c': color_jid}
+ tab.add_message(text, typ=0)
elif arg == 'drop':
# drop the privkey (and obviously, end the current conversations before that)
for context in self.contexts.values():
diff --git a/plugins/quote.py b/plugins/quote.py
index 3236ef4d..8a4fd95c 100644
--- a/plugins/quote.py
+++ b/plugins/quote.py
@@ -19,6 +19,28 @@ Usage
If there is a message at 21:12:23, it will be put in the input. If there
isn’t, you will get a warning.
+
+Options
+-------
+
+.. glossary::
+ :sorted:
+
+ before_quote
+
+ **Default value:** ``[empty]``
+
+ Text to insert before the quote. ``%(nick)s`` and ``%(time)s`` can
+ be used to insert the nick of the user who sent the message or the
+ time of the message.
+
+ after_quote
+
+ **Default value:** ``[empty]``
+
+ Text to insert after the quote. ``%(nick)s`` and ``%(time)s`` can
+ be used to insert the nick of the user who sent the message or the
+ time of the message.
"""
from plugin import BasePlugin
from xhtml import clean_text
diff --git a/plugins/tell.py b/plugins/tell.py
index 6aa8357d..cfa76e9c 100644
--- a/plugins/tell.py
+++ b/plugins/tell.py
@@ -61,7 +61,7 @@ class Plugin(BasePlugin):
if not nick in self.tabs[tab]:
self.tabs[tab][nick] = []
self.tabs[tab][nick].append(msg)
- self.api.information('Will tell %s' % nick, 'Info')
+ self.api.information('Message for %s queued' % nick, 'Info')
def command_untell(self, args):
"""/untell <nick>"""
@@ -72,10 +72,11 @@ class Plugin(BasePlugin):
if not nick in self.tabs[tab]:
return
del self.tabs[tab][nick]
+ self.api.information('Messages for %s unqueued' % nick, 'Info')
def completion_untell(self, the_input):
tab = self.api.current_tab()
if not tab in self.tabs:
return the_input.auto_completion([], '')
- return the_input.auto_completion(list(self.tabs[tab]), '')
+ return the_input.auto_completion(list(self.tabs[tab]), '', quotify=False)
diff --git a/src/core/commands.py b/src/core/commands.py
index 07b0cb1e..c27263e2 100644
--- a/src/core/commands.py
+++ b/src/core/commands.py
@@ -943,7 +943,7 @@ def command_xml_tab(self, arg=''):
def command_adhoc(self, arg):
arg = arg.split()
if len(arg) > 1:
- return self.command_help('list')
+ return self.command_help('ad-hoc')
elif arg:
jid = safeJID(arg[0])
else:
diff --git a/src/core/handlers.py b/src/core/handlers.py
index fe0865d3..b46b949f 100644
--- a/src/core/handlers.py
+++ b/src/core/handlers.py
@@ -9,7 +9,7 @@ import curses
import ssl
import time
import functools
-from hashlib import sha1
+from hashlib import sha1, sha512
from gettext import gettext as _
from slixmpp import InvalidJID
@@ -891,24 +891,25 @@ def on_session_start(self, event):
def _join_initial_rooms(bookmarks):
"""Join all rooms given in the iterator `bookmarks`"""
for bm in 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', 20)
- 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)
+ if bm.autojoin or config.get('open_all_bookmarks', False):
+ 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', 20)
+ 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)
@@ -1101,16 +1102,27 @@ def validate_ssl(self, pem):
config.set_and_save('certificate', cert)
der = ssl.PEM_cert_to_DER_cert(pem)
- digest = sha1(der).hexdigest().upper()
- found_cert = ':'.join(i + j for i, j in zip(digest[::2], digest[1::2]))
+ sha1_digest = sha1(der).hexdigest().upper()
+ sha1_found_cert = ':'.join(i + j for i, j in zip(sha1_digest[::2], sha1_digest[1::2]))
+ sha2_digest = sha512(der).hexdigest().upper()
+ sha2_found_cert = ':'.join(i + j for i, j in zip(sha2_digest[::2], sha2_digest[1::2]))
if cert:
- if found_cert == cert:
- log.debug('Cert %s OK', found_cert)
+ if sha1_found_cert == cert:
+ log.debug('Cert %s OK', sha1_found_cert)
+ log.debug('Current hash is SHA-1, moving to SHA-2 (%s)',
+ sha2_found_cert)
+ config.set_and_save('certificate', sha2_found_cert)
+ return
+ elif sha2_found_cert == cert:
+ log.debug('Cert %s OK', sha2_found_cert)
return
else:
saved_input = self.current_tab().input
- log.debug('\nWARNING: CERTIFICATE CHANGED old: %s, new: %s\n', cert, found_cert)
- input = windows.YesNoInput(text="WARNING! Server certificate has changed, accept? (y/n) (%s)" % found_cert)
+ log.debug('\nWARNING: CERTIFICATE CHANGED old: %s, new: %s\n', cert, sha2_found_cert)
+ 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()
@@ -1121,16 +1133,16 @@ def validate_ssl(self, pem):
self.current_tab().input = saved_input
self.paused = False
if input.value:
- self.information('Setting new certificate: old: %s, new: %s' % (cert, found_cert), 'Info')
- log.debug('Setting certificate to %s', found_cert)
- if not config.silent_set('certificate', found_cert):
+ 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')
else:
self.information('You refused to validate the certificate. You are now disconnected', 'Info')
self.xmpp.disconnect()
else:
- log.debug('First time. Setting certificate to %s', found_cert)
- if not config.silent_set('certificate', found_cert):
+ 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')
def _composing_tab_state(tab, state):
diff --git a/src/tabs/conversationtab.py b/src/tabs/conversationtab.py
index a6cb0d59..cc9e6b2e 100644
--- a/src/tabs/conversationtab.py
+++ b/src/tabs/conversationtab.py
@@ -385,11 +385,15 @@ class DynamicConversationTab(ConversationTab):
assert(resource)
if resource != self.locked_resource:
self.locked_resource = resource
-
- message = _('\x19%s}Conversation locked to %s/%s.') % (
- dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
- self.name,
- resource)
+ info = '\x19%s}' % dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ jid_c = '\x19%s}' % dump_tuple(get_theme().COLOR_MUC_JID)
+
+ message = _('%(info)sConversation locked to '
+ '%(jid_c)s%(jid)s/%(resource)s%(info)s.') % {
+ 'info': info,
+ 'jid_c': jid_c,
+ 'jid': self.name,
+ 'resource': resource}
self.add_message(message, typ=0)
self.check_features()
@@ -405,16 +409,18 @@ class DynamicConversationTab(ConversationTab):
self.remote_wants_chatstates = None
if self.locked_resource != None:
self.locked_resource = None
+ info = '\x19%s}' % dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ jid_c = '\x19%s}' % dump_tuple(get_theme().COLOR_MUC_JID)
if from_:
- message = _('\x19%s}Conversation unlocked '
- '(received activity from %s).') % (
- dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
- from_)
+ message = _('%(info)sConversation unlocked (received activity'
+ ' from %(jid_c)s%(jid)s%(info)s).') % {
+ 'info': info,
+ 'jid_c': jid_c,
+ 'jid': from_}
self.add_message(message, typ=0)
else:
- message = _('\x19%s}Conversation unlocked.') % (
- dump_tuple(get_theme().COLOR_INFORMATION_TEXT))
+ message = _('%sConversation unlocked.') % info
self.add_message(message, typ=0)
def get_dest_jid(self):
diff --git a/src/tabs/muctab.py b/src/tabs/muctab.py
index 5ebaf522..f526ec80 100644
--- a/src/tabs/muctab.py
+++ b/src/tabs/muctab.py
@@ -355,7 +355,8 @@ class MucTab(ChatTab):
dump_tuple(theme.color_role(user.role)),
user.role or 'None',
'\n%s' % user.status if user.status else '')
- self.core.information(info, 'Info')
+ self.add_message(info, typ=0)
+ self.core.refresh_window()
def command_configure(self, arg):
"""
@@ -1525,11 +1526,11 @@ class MucTab(ChatTab):
config.get_by_tabname('notify_messages',
True, self.name)):
self.state = 'message'
- if time:
+ if time and not txt.startswith('/me'):
txt = '\x19%(info_col)s}%(txt)s' % {
'txt': txt,
'info_col': dump_tuple(get_theme().COLOR_LOG_MSG)}
- elif (not nickname or time) and not txt.startswith('/me '):
+ elif not nickname:
txt = '\x19%(info_col)s}%(txt)s' % {
'txt': txt,
'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}
diff --git a/src/tabs/xmltab.py b/src/tabs/xmltab.py
index 7cbc9fb6..083e97c5 100644
--- a/src/tabs/xmltab.py
+++ b/src/tabs/xmltab.py
@@ -11,12 +11,14 @@ import logging
log = logging.getLogger(__name__)
import curses
+import os
from slixmpp.xmlstream import matcher
from slixmpp.xmlstream.handler import Callback
from . import Tab
import windows
+from xhtml import clean_text
class XMLTab(Tab):
def __init__(self):
@@ -45,6 +47,10 @@ class XMLTab(Tab):
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.'))
self.input = self.default_help_message
self.key_func['^T'] = self.close
self.key_func['^I'] = self.completion
@@ -111,6 +117,17 @@ class XMLTab(Tab):
self.filter = ''
self.refresh()
+ def command_dump(self, arg):
+ """/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))
+ try:
+ with open(filename, 'w') as fd:
+ fd.write(text)
+ except Exception as e:
+ self.core.information('Could not write the XML dump: %s' % e, 'Error')
+
def on_slash(self):
"""
'/' is pressed, activate the input
diff --git a/src/windows/input_placeholders.py b/src/windows/input_placeholders.py
index 0887cfc5..8bcf1524 100644
--- a/src/windows/input_placeholders.py
+++ b/src/windows/input_placeholders.py
@@ -33,6 +33,9 @@ class HelpText(Win):
def do_command(self, key, raw=False):
return False
+ def on_delete(self):
+ return
+
class YesNoInput(Win):
"""
A Window just displaying a Yes/No input
@@ -77,3 +80,6 @@ class YesNoInput(Win):
keyboard.continuation_keys_callback = cb
keyboard.continuation_keys_callback = cb
+ def on_delete(self):
+ return
+