From 2370dab7d57711dfbc483e5e7f57acfce56c7dff Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Tue, 25 Jun 2019 10:18:27 -0400 Subject: remove unused scripts --- weechat/.weechat/python/anotify.py | 472 ----- weechat/.weechat/python/autoload/anotify.py | 1 - weechat/.weechat/python/autoload/autosavekey.py | 1 - weechat/.weechat/python/autoload/otr.py | 1 - weechat/.weechat/python/autosavekey.py | 263 --- weechat/.weechat/python/autosort.py | 87 +- weechat/.weechat/python/otr.py | 2135 ----------------------- 7 files changed, 68 insertions(+), 2892 deletions(-) delete mode 100644 weechat/.weechat/python/anotify.py delete mode 120000 weechat/.weechat/python/autoload/anotify.py delete mode 120000 weechat/.weechat/python/autoload/autosavekey.py delete mode 120000 weechat/.weechat/python/autoload/otr.py delete mode 100644 weechat/.weechat/python/autosavekey.py delete mode 100644 weechat/.weechat/python/otr.py (limited to 'weechat/.weechat/python') diff --git a/weechat/.weechat/python/anotify.py b/weechat/.weechat/python/anotify.py deleted file mode 100644 index c83f62b..0000000 --- a/weechat/.weechat/python/anotify.py +++ /dev/null @@ -1,472 +0,0 @@ -# -*- coding: utf-8 -*- -# -# anotify.py -# Copyright (c) 2012 magnific0 -# -# based on: -# growl.py -# Copyright (c) 2011 Sorin Ionescu -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - -SCRIPT_NAME = 'anotify' -SCRIPT_AUTHOR = 'magnific0' -SCRIPT_VERSION = '1.0.1' -SCRIPT_LICENSE = 'MIT' -SCRIPT_DESC = 'Sends libnotify notifications upon events.' - - -# Changelog -# 2014-05-10: v1.0.1 Change hook_print callback argument type of -# displayed/highlight (WeeChat >= 1.0) -# 2012-09-20: v1.0.0 Forked from original and adapted for libnotify. - -# ----------------------------------------------------------------------------- -# Settings -# ----------------------------------------------------------------------------- -SETTINGS = { - 'show_public_message': 'off', - 'show_private_message': 'on', - 'show_public_action_message': 'off', - 'show_private_action_message': 'on', - 'show_notice_message': 'off', - 'show_invite_message': 'on', - 'show_highlighted_message': 'on', - 'show_server': 'on', - 'show_channel_topic': 'on', - 'show_dcc': 'on', - 'show_upgrade_ended': 'on', - 'sticky': 'off', - 'sticky_away': 'on', - 'icon': '/usr/share/pixmaps/weechat.xpm', -} - - -# ----------------------------------------------------------------------------- -# Imports -# ----------------------------------------------------------------------------- -try: - import re - import os - import weechat - import pynotify - IMPORT_OK = True -except ImportError as error: - IMPORT_OK = False - if str(error).find('weechat') != -1: - print('This script must be run under WeeChat.') - print('Get WeeChat at http://www.weechat.org.') - else: - weechat.prnt('', 'anotify: {0}'.format(error)) - -# ----------------------------------------------------------------------------- -# Globals -# ----------------------------------------------------------------------------- -TAGGED_MESSAGES = { - 'public message or action': set(['irc_privmsg', 'notify_message']), - 'private message or action': set(['irc_privmsg', 'notify_private']), - 'notice message': set(['irc_notice', 'notify_private']), - 'invite message': set(['irc_invite', 'notify_highlight']), - 'channel topic': set(['irc_topic', ]), - #'away status': set(['away_info', ]), -} - - -UNTAGGED_MESSAGES = { - 'away status': - re.compile(r'^You ((\w+).){2,3}marked as being away', re.UNICODE), - 'dcc chat request': - re.compile(r'^xfer: incoming chat request from (\w+)', re.UNICODE), - 'dcc chat closed': - re.compile(r'^xfer: chat closed with (\w+)', re.UNICODE), - 'dcc get request': - re.compile( - r'^xfer: incoming file from (\w+) [^:]+: ((?:,\w|[^,])+),', - re.UNICODE), - 'dcc get completed': - re.compile(r'^xfer: file ([^\s]+) received from \w+: OK', re.UNICODE), - 'dcc get failed': - re.compile( - r'^xfer: file ([^\s]+) received from \w+: FAILED', - re.UNICODE), - 'dcc send completed': - re.compile(r'^xfer: file ([^\s]+) sent to \w+: OK', re.UNICODE), - 'dcc send failed': - re.compile(r'^xfer: file ([^\s]+) sent to \w+: FAILED', re.UNICODE), -} - - -DISPATCH_TABLE = { - 'away status': 'set_away_status', - 'public message or action': 'notify_public_message_or_action', - 'private message or action': 'notify_private_message_or_action', - 'notice message': 'notify_notice_message', - 'invite message': 'notify_invite_message', - 'channel topic': 'notify_channel_topic', - 'dcc chat request': 'notify_dcc_chat_request', - 'dcc chat closed': 'notify_dcc_chat_closed', - 'dcc get request': 'notify_dcc_get_request', - 'dcc get completed': 'notify_dcc_get_completed', - 'dcc get failed': 'notify_dcc_get_failed', - 'dcc send completed': 'notify_dcc_send_completed', - 'dcc send failed': 'notify_dcc_send_failed', -} - - -STATE = { - 'icon': None, - 'is_away': False -} - - -# ----------------------------------------------------------------------------- -# Notifiers -# ----------------------------------------------------------------------------- -def cb_irc_server_connected(data, signal, signal_data): - '''Notify when connected to IRC server.''' - if weechat.config_get_plugin('show_server') == 'on': - a_notify( - 'Server', - 'Server Connected', - 'Connected to network {0}.'.format(signal_data)) - return weechat.WEECHAT_RC_OK - - -def cb_irc_server_disconnected(data, signal, signal_data): - '''Notify when disconnected to IRC server.''' - if weechat.config_get_plugin('show_server') == 'on': - a_notify( - 'Server', - 'Server Disconnected', - 'Disconnected from network {0}.'.format(signal_data)) - return weechat.WEECHAT_RC_OK - - -def cb_notify_upgrade_ended(data, signal, signal_data): - '''Notify on end of WeeChat upgrade.''' - if weechat.config_get_plugin('show_upgrade_ended') == 'on': - a_notify( - 'WeeChat', - 'WeeChat Upgraded', - 'WeeChat has been upgraded.') - return weechat.WEECHAT_RC_OK - - -def notify_highlighted_message(prefix, message): - '''Notify on highlighted message.''' - if weechat.config_get_plugin("show_highlighted_message") == "on": - a_notify( - 'Highlight', - 'Highlighted Message', - "{0}: {1}".format(prefix, message), - priority=pynotify.URGENCY_CRITICAL) - - -def notify_public_message_or_action(prefix, message, highlighted): - '''Notify on public message or action.''' - if prefix == ' *': - regex = re.compile(r'^(\w+) (.+)$', re.UNICODE) - match = regex.match(message) - if match: - prefix = match.group(1) - message = match.group(2) - notify_public_action_message(prefix, message, highlighted) - else: - if highlighted: - notify_highlighted_message(prefix, message) - elif weechat.config_get_plugin("show_public_message") == "on": - a_notify( - 'Public', - 'Public Message', - '{0}: {1}'.format(prefix, message)) - - -def notify_private_message_or_action(prefix, message, highlighted): - '''Notify on private message or action.''' - regex = re.compile(r'^CTCP_MESSAGE.+?ACTION (.+)$', re.UNICODE) - match = regex.match(message) - if match: - notify_private_action_message(prefix, match.group(1), highlighted) - else: - if prefix == ' *': - regex = re.compile(r'^(\w+) (.+)$', re.UNICODE) - match = regex.match(message) - if match: - prefix = match.group(1) - message = match.group(2) - notify_private_action_message(prefix, message, highlighted) - else: - if highlighted: - notify_highlighted_message(prefix, message) - elif weechat.config_get_plugin("show_private_message") == "on": - a_notify( - 'Private', - 'Private Message', - '{0}: {1}'.format(prefix, message)) - - -def notify_public_action_message(prefix, message, highlighted): - '''Notify on public action message.''' - if highlighted: - notify_highlighted_message(prefix, message) - elif weechat.config_get_plugin("show_public_action_message") == "on": - a_notify( - 'Action', - 'Public Action Message', - '{0}: {1}'.format(prefix, message), - priority=pynotify.URGENCY_NORMAL) - - -def notify_private_action_message(prefix, message, highlighted): - '''Notify on private action message.''' - if highlighted: - notify_highlighted_message(prefix, message) - elif weechat.config_get_plugin("show_private_action_message") == "on": - a_notify( - 'Action', - 'Private Action Message', - '{0}: {1}'.format(prefix, message), - priority=pynotify.URGENCY_NORMAL) - - -def notify_notice_message(prefix, message, highlighted): - '''Notify on notice message.''' - regex = re.compile(r'^([^\s]*) [^:]*: (.+)$', re.UNICODE) - match = regex.match(message) - if match: - prefix = match.group(1) - message = match.group(2) - if highlighted: - notify_highlighted_message(prefix, message) - elif weechat.config_get_plugin("show_notice_message") == "on": - a_notify( - 'Notice', - 'Notice Message', - '{0}: {1}'.format(prefix, message)) - - -def notify_invite_message(prefix, message, highlighted): - '''Notify on channel invitation message.''' - if weechat.config_get_plugin("show_invite_message") == "on": - regex = re.compile( - r'^You have been invited to ([^\s]+) by ([^\s]+)$', re.UNICODE) - match = regex.match(message) - if match: - channel = match.group(1) - nick = match.group(2) - a_notify( - 'Invite', - 'Channel Invitation', - '{0} has invited you to join {1}.'.format(nick, channel)) - - -def notify_channel_topic(prefix, message, highlighted): - '''Notify on channel topic change.''' - if weechat.config_get_plugin("show_channel_topic") == "on": - regex = re.compile( - r'^\w+ has (?:changed|unset) topic for ([^\s]+)' + - '(?:(?: from "(?:(?:"\w|[^"])+)")? to "((?:"\w|[^"])+)")?', - re.UNICODE) - match = regex.match(message) - if match: - channel = match.group(1) - topic = match.group(2) or '' - a_notify( - 'Channel', - 'Channel Topic', - "{0}: {1}".format(channel, topic)) - - -def notify_dcc_chat_request(match): - '''Notify on DCC chat request.''' - if weechat.config_get_plugin("show_dcc") == "on": - nick = match.group(1) - a_notify( - 'DCC', - 'Direct Chat Request', - '{0} wants to chat directly.'.format(nick)) - - -def notify_dcc_chat_closed(match): - '''Notify on DCC chat termination.''' - if weechat.config_get_plugin("show_dcc") == "on": - nick = match.group(1) - a_notify( - 'DCC', - 'Direct Chat Ended', - 'Direct chat with {0} has ended.'.format(nick)) - - -def notify_dcc_get_request(match): - 'Notify on DCC get request.' - if weechat.config_get_plugin("show_dcc") == "on": - nick = match.group(1) - file_name = match.group(2) - a_notify( - 'DCC', - 'File Transfer Request', - '{0} wants to send you {1}.'.format(nick, file_name)) - - -def notify_dcc_get_completed(match): - 'Notify on DCC get completion.' - if weechat.config_get_plugin("show_dcc") == "on": - file_name = match.group(1) - a_notify('DCC', 'Download Complete', file_name) - - -def notify_dcc_get_failed(match): - 'Notify on DCC get failure.' - if weechat.config_get_plugin("show_dcc") == "on": - file_name = match.group(1) - a_notify('DCC', 'Download Failed', file_name) - - -def notify_dcc_send_completed(match): - 'Notify on DCC send completion.' - if weechat.config_get_plugin("show_dcc") == "on": - file_name = match.group(1) - a_notify('DCC', 'Upload Complete', file_name) - - -def notify_dcc_send_failed(match): - 'Notify on DCC send failure.' - if weechat.config_get_plugin("show_dcc") == "on": - file_name = match.group(1) - a_notify('DCC', 'Upload Failed', file_name) - - -# ----------------------------------------------------------------------------- -# Utility -# ----------------------------------------------------------------------------- -def set_away_status(match): - status = match.group(1) - if status == 'been ': - STATE['is_away'] = True - if status == 'longer ': - STATE['is_away'] = False - - -def cb_process_message( - data, - wbuffer, - date, - tags, - displayed, - highlight, - prefix, - message -): - '''Delegates incoming messages to appropriate handlers.''' - tags = set(tags.split(',')) - functions = globals() - is_public_message = tags.issuperset( - TAGGED_MESSAGES['public message or action']) - buffer_name = weechat.buffer_get_string(wbuffer, 'name') - dcc_buffer_regex = re.compile(r'^irc_dcc\.', re.UNICODE) - dcc_buffer_match = dcc_buffer_regex.match(buffer_name) - highlighted = False - if int(highlight): - highlighted = True - # Private DCC message identifies itself as public. - if is_public_message and dcc_buffer_match: - notify_private_message_or_action(prefix, message, highlighted) - return weechat.WEECHAT_RC_OK - # Pass identified, untagged message to its designated function. - for key, value in UNTAGGED_MESSAGES.items(): - match = value.match(message) - if match: - functions[DISPATCH_TABLE[key]](match) - return weechat.WEECHAT_RC_OK - # Pass identified, tagged message to its designated function. - for key, value in TAGGED_MESSAGES.items(): - if tags.issuperset(value): - functions[DISPATCH_TABLE[key]](prefix, message, highlighted) - return weechat.WEECHAT_RC_OK - return weechat.WEECHAT_RC_OK - - -def a_notify(notification, title, description, priority=pynotify.URGENCY_LOW): - '''Returns whether notifications should be sticky.''' - is_away = STATE['is_away'] - icon = STATE['icon'] - time_out = 5000 - if weechat.config_get_plugin('sticky') == 'on': - time_out = 0 - if weechat.config_get_plugin('sticky_away') == 'on' and is_away: - time_out = 0 - try: - pynotify.init("wee-notifier") - wn = pynotify.Notification(title, description, icon) - wn.set_urgency(priority) - wn.set_timeout(time_out) - wn.show() - except Exception as error: - weechat.prnt('', 'anotify: {0}'.format(error)) - - -# ----------------------------------------------------------------------------- -# Main -# ----------------------------------------------------------------------------- -def main(): - '''Sets up WeeChat notifications.''' - # Initialize options. - for option, value in SETTINGS.items(): - if not weechat.config_is_set_plugin(option): - weechat.config_set_plugin(option, value) - # Initialize. - name = "WeeChat" - icon = "/usr/share/pixmaps/weechat.xpm" - notifications = [ - 'Public', - 'Private', - 'Action', - 'Notice', - 'Invite', - 'Highlight', - 'Server', - 'Channel', - 'DCC', - 'WeeChat' - ] - STATE['icon'] = icon - # Register hooks. - weechat.hook_signal( - 'irc_server_connected', - 'cb_irc_server_connected', - '') - weechat.hook_signal( - 'irc_server_disconnected', - 'cb_irc_server_disconnected', - '') - weechat.hook_signal('upgrade_ended', 'cb_upgrade_ended', '') - weechat.hook_print('', '', '', 1, 'cb_process_message', '') - - -if __name__ == '__main__' and IMPORT_OK and weechat.register( - SCRIPT_NAME, - SCRIPT_AUTHOR, - SCRIPT_VERSION, - SCRIPT_LICENSE, - SCRIPT_DESC, - '', - '' -): - main() diff --git a/weechat/.weechat/python/autoload/anotify.py b/weechat/.weechat/python/autoload/anotify.py deleted file mode 120000 index 1517fef..0000000 --- a/weechat/.weechat/python/autoload/anotify.py +++ /dev/null @@ -1 +0,0 @@ -../anotify.py \ No newline at end of file diff --git a/weechat/.weechat/python/autoload/autosavekey.py b/weechat/.weechat/python/autoload/autosavekey.py deleted file mode 120000 index fd537c7..0000000 --- a/weechat/.weechat/python/autoload/autosavekey.py +++ /dev/null @@ -1 +0,0 @@ -../autosavekey.py \ No newline at end of file diff --git a/weechat/.weechat/python/autoload/otr.py b/weechat/.weechat/python/autoload/otr.py deleted file mode 120000 index 61d1b18..0000000 --- a/weechat/.weechat/python/autoload/otr.py +++ /dev/null @@ -1 +0,0 @@ -../otr.py \ No newline at end of file diff --git a/weechat/.weechat/python/autosavekey.py b/weechat/.weechat/python/autosavekey.py deleted file mode 100644 index 2a3b0ad..0000000 --- a/weechat/.weechat/python/autosavekey.py +++ /dev/null @@ -1,263 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2013-2018 by nils_2 -# -# save channel key from protected channel(s) to autojoin or secure data -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# idea by freenode.elsae -# -# 2018-05-11: nils_2, (freenode.#weechat) -# 0.4 : make script python3 compatible -# : add /help text -# -# 2015-05-09: nils_2, (freenode.#weechat) -# 0.3 : fix: ValueError (reported by: Darpa) -# -# 2014-12-20: nils_2, (freenode.#weechat) -# 0.2 : add option "add" to automatically add channel/key to autojoin option after a /join (idea by Prezident) -# -# 2013-10-03: nils_2, (freenode.#weechat) -# 0.1 : initial release -# -# requires: WeeChat version 0.3.2 -# -# Development is currently hosted at -# https://github.com/weechatter/weechat-scripts - -try: - import weechat,re - -except Exception: - print("This script must be run under WeeChat.") - print("Get WeeChat now at: http://www.weechat.org/") - quit() - -SCRIPT_NAME = "autosavekey" -SCRIPT_AUTHOR = "nils_2 " -SCRIPT_VERSION = "0.4" -SCRIPT_LICENSE = "GPL" -SCRIPT_DESC = "save channel key from protected channel(s) to autojoin option or secure data" - -OPTIONS = { 'mute' : ('off','execute command silently, only error messages will be displayed.'), - 'secure' : ('off','change channel key in secure data.'), - 'add' : ('off','adds channel and key to autojoin list on /join, if channel/key does not already exists'), - } -# /join #channel key -# signal = freenode,irc_raw_in_324 -# signal_data = :asimov.freenode.net 324 nick #channel +modes key -def irc_raw_in_324_cb(data, signal, signal_data): - parsed = get_hashtable(signal_data) - server = signal.split(',',1)[0] - argv = parsed['arguments'].split(" ") - - # buffer without channel key - if len(argv) < 4: - return weechat.WEECHAT_RC_OK - - channel = argv[1] - new_key = argv[3] - - autojoin_list = get_autojoin(server) - if not autojoin_list: - return weechat.WEECHAT_RC_OK - - # check autojoin for space - if len(re.findall(r" ", autojoin_list)) > 1: - weechat.prnt('', '%s%s: autojoin format for server "%s" invalid (two or more spaces).' % (weechat.prefix('error'),SCRIPT_NAME,server) ) - return weechat.WEECHAT_RC_OK - - # no keylist, only channels in autojoin option - if len(re.findall(r" ", autojoin_list)) == 0: - argv_channels = autojoin_list.split(',') - argv_keys = [] - else: - # split autojoin option to a channel and a key list - arg_channel,arg_keys = autojoin_list.split(' ') - argv_channels = arg_channel.split(',') - argv_keys = arg_keys.split(',') - - # check channel position - try: - channel_position = argv_channels.index(channel) - except ValueError: - channel_position = -1 - - sec_data = 0 - # does buffer already exist in autojoin list? - if channel_position >= 0: - # remove channel from list - argv_channels.pop(channel_position) - # check if there is at least one key in list - if len(argv_keys) >= 1: - # check channel position and number of keys - if channel_position <= len(argv_keys): - # remove key from list - sec_data = check_key_for_secure(argv_keys,channel_position) - sec_data_name = argv_keys[channel_position][11:-1] - argv_keys.pop(channel_position) - else: - if OPTIONS['add'].lower() == 'off': - return weechat.WEECHAT_RC_OK - - # add channel and key at first position - argv_channels.insert(0, channel) - argv_keys.insert(0,new_key) - - - # check weechat version and if secure option is on and secure data will be used for this key? - if int(version) >= 0x00040200 and OPTIONS['secure'].lower() == 'on' and sec_data == 1: - weechat.command('','%s/secure set %s %s' % (use_mute(),sec_data_name,new_key)) - else: - if sec_data == 1: - weechat.prnt('', '%s%s: key for channel "%s.%s" not changed! option "plugins.var.python.%s.secure" is off and you are using secured data for key.' % (weechat.prefix('error'),SCRIPT_NAME,server,channel,SCRIPT_NAME) ) - return weechat.WEECHAT_RC_OK - new_joined_option = '%s %s' % (','.join(argv_channels),','.join(argv_keys)) - save_autojoin_option(server,new_joined_option) - return weechat.WEECHAT_RC_OK - -# replace an already existing channel key with an new one -# when OP changes channel key -def irc_raw_in_mode_cb(data, signal, signal_data): - parsed = get_hashtable(signal_data) - - server = signal.split(',',1)[0] - argv = parsed['arguments'].split(" ") - - if argv[1] != "+k": - return weechat.WEECHAT_RC_OK - - channel = argv[0] - new_key = argv[2] - - add_key_to_list(server,channel,new_key) - return weechat.WEECHAT_RC_OK - -def add_key_to_list(server,channel,new_key): - autojoin_list = get_autojoin(server) - if not autojoin_list: - return weechat.WEECHAT_RC_OK - - # check autojoin for space - if len(re.findall(r" ", autojoin_list)) == 0: - weechat.prnt('', '%s%s: no password(s) set in autojoin for server "%s".' % (weechat.prefix('error'),SCRIPT_NAME,server) ) - return weechat.WEECHAT_RC_OK - if len(re.findall(r" ", autojoin_list)) > 1: - weechat.prnt('', '%s%s: autojoin format for server "%s" invalid (two or more spaces).' % (weechat.prefix('error'),SCRIPT_NAME,server) ) - return weechat.WEECHAT_RC_OK - - - # split autojoin option to a channel and a key list - arg_channel,arg_keys = autojoin_list.split(' ') - argv_channels = arg_channel.split(',') - argv_keys = arg_keys.split(',') - - # search for channel name in list of channels and get position - if channel in argv_channels: - channel_pos_in_list = argv_channels.index(channel) - # enough keys in list? list counts from 0! - if channel_pos_in_list + 1 > len(argv_keys): - weechat.prnt('', '%s%s: not enough keys in list or channel position is not valid. check out autojoin option for server "%s".' % (weechat.prefix('error'),SCRIPT_NAME,server) ) - return weechat.WEECHAT_RC_OK - - sec_data = check_key_for_secure(argv_keys,channel_pos_in_list) - - # check weechat version and if secure option is on and secure data will be used for this key? - if int(version) >= 0x00040200 and OPTIONS['secure'].lower() == 'on' and sec_data == 1: - sec_data_name = argv_keys[channel_pos_in_list][11:-1] - weechat.command('','%s/secure set %s %s' % (use_mute(),sec_data_name,new_key)) - else: - if sec_data == 1: - weechat.prnt('', '%s%s: key for channel "%s.%s" not changed! option "plugins.var.python.%s.secure" is off and you are using secured data for key.' % (weechat.prefix('error'),SCRIPT_NAME,server,channel,SCRIPT_NAME) ) - return weechat.WEECHAT_RC_OK - argv_keys[channel_pos_in_list] = new_key - new_joined_option = '%s %s' % (','.join(argv_channels),','.join(argv_keys)) - save_autojoin_option(server,new_joined_option) - return weechat.WEECHAT_RC_OK - -def get_hashtable(string): - parsed = weechat.info_get_hashtable('irc_message_parse', dict(message=string)) - try: - parsed['message'] = parsed['arguments'].split(' :', 1)[1] - except: - parsed['message'] = "" - return parsed - -def get_autojoin(server): - return weechat.config_string(weechat.config_get('irc.server.%s.autojoin' % server)) - -def find_element_in_list(element,list_element): - try: - index_element=list_element.index(element) - return index_element - except ValueError: - return -1 - -def save_autojoin_option(server,new_joined_option): - weechat.command('','%s/set irc.server.%s.autojoin %s' % (use_mute(),server,new_joined_option)) - -def use_mute(): - use_mute = '' - if OPTIONS['mute'].lower() == 'on': - use_mute = '/mute ' - return use_mute - -# check key for "${sec.data." -def check_key_for_secure(argv_keys,position): - sec_data = 0 - if argv_keys[position][0:11] == '${sec.data.': - sec_data = 1 - return sec_data - -def cmd_autosavekey(data, buffer, args): - weechat.command('', '/help %s' % SCRIPT_NAME) - return weechat.WEECHAT_RC_OK - -# ================================[ weechat options & description ]=============================== -def init_options(): - for option,value in OPTIONS.items(): - if not weechat.config_is_set_plugin(option): - weechat.config_set_plugin(option, value[0]) - OPTIONS[option] = value[0] - else: - OPTIONS[option] = weechat.config_get_plugin(option) - weechat.config_set_desc_plugin(option, '%s (default: "%s")' % (value[1], value[0])) - -def toggle_refresh(pointer, name, value): - global OPTIONS - option = name[len('plugins.var.python.' + SCRIPT_NAME + '.'):] # get optionname - OPTIONS[option] = value # save new value - return weechat.WEECHAT_RC_OK - -# ================================[ main ]=============================== -if __name__ == "__main__": - if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', ''): - weechat.hook_command(SCRIPT_NAME,SCRIPT_DESC, - '', - 'You have to edit options with: /set *autosavekey*\n' - 'I suggest using /iset script or /fset plugin.\n', - '', - 'cmd_autosavekey', - '') - version = weechat.info_get("version_number", "") or 0 - - if int(version) >= 0x00030200: - init_options() - weechat.hook_config( 'plugins.var.python.' + SCRIPT_NAME + '.*', 'toggle_refresh', '' ) - weechat.hook_signal("*,irc_raw_in_mode","irc_raw_in_mode_cb","") - weechat.hook_signal("*,irc_raw_in_324","irc_raw_in_324_cb","") - else: - weechat.prnt("","%s%s %s" % (weechat.prefix("error"),SCRIPT_NAME,": needs version 0.3.2 or higher")) - weechat.command("","/wait 1ms /python unload %s" % SCRIPT_NAME) diff --git a/weechat/.weechat/python/autosort.py b/weechat/.weechat/python/autosort.py index 46a840c..8f7c263 100644 --- a/weechat/.weechat/python/autosort.py +++ b/weechat/.weechat/python/autosort.py @@ -25,6 +25,10 @@ # # Changelog: +# 3.6: +# * Add more documentation on provided info hooks. +# 3.5: +# * Add ${info:autosort_escape,...} to escape arguments for other info hooks. # 3.4: # * Fix rate-limit of sorting to prevent high CPU load and lock-ups. # * Fix bug in parsing empty arguments for info hooks. @@ -76,7 +80,7 @@ import weechat SCRIPT_NAME = 'autosort' SCRIPT_AUTHOR = 'Maarten de Vries ' -SCRIPT_VERSION = '3.4' +SCRIPT_VERSION = '3.6' SCRIPT_LICENSE = 'GPL3' SCRIPT_DESC = 'Flexible automatic (or manual) buffer sorting based on eval expressions.' @@ -184,7 +188,7 @@ class Config: 'irc_last': '${if:${buffer.plugin.name}==irc}', 'irc_raw_first': '${if:${buffer.full_name}!=irc.irc_raw}', 'irc_raw_last': '${if:${buffer.full_name}==irc.irc_raw}', - 'hashless_name': '${info:autosort_replace,#,,${buffer.name}}', + 'hashless_name': '${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}', }) default_signal_delay = 5 @@ -746,6 +750,17 @@ def parse_args(args, max = None): if args is None: break return result, args +def on_info_escape(pointer, name, arguments): + result = '' + for c in arguments: + if c == '\\': + result += '\\\\' + elif c == ',': + result += '\\,' + else: + result +=c + return result + def on_info_replace(pointer, name, arguments): arguments, rest = parse_args(arguments, 3) if rest or len(arguments) < 3: @@ -910,6 +925,36 @@ Rename a helper variable. Swap the expressions of two helper variables in the list. +{*white}# Info hooks{reset} +Autosort comes with a number of info hooks to add some extra functionality to regular weechat eval strings. +Info hooks can be used in eval strings in the form of {cyan}${{info:some_hook,arguments}}{reset}. + +Commas and backslashes in arguments to autosort info hooks (except for {cyan}${{info:autosort_escape}}{reset}) must be escaped with a backslash. + +{*white}${{info:{brown}autosort_replace{white},{cyan}pattern{white},{cyan}replacement{white},{cyan}source{white}}}{reset} +Replace all occurrences of {cyan}pattern{reset} with {cyan}replacement{reset} in the string {cyan}source{reset}. +Can be used to ignore certain strings when sorting by replacing them with an empty string. + +For example: {cyan}${{info:autosort_replace,cat,dog,the dog is meowing}}{reset} expands to "the cat is meowing". + +{*white}${{info:{brown}autosort_order{white},{cyan}value{white},{cyan}option0{white},{cyan}option1{white},{cyan}option2{white},{cyan}...{white}}} +Generate a zero-padded number that corresponds to the index of {cyan}value{reset} in the list of options. +If one of the options is the special value {brown}*{reset}, then any value not explicitly mentioned will be sorted at that position. +Otherwise, any value that does not match an option is assigned the highest number available. +Can be used to easily sort buffers based on a manual sequence. + +For example: {cyan}${{info:autosort_order,${{server}},freenode,oftc,efnet}}{reset} will sort freenode before oftc, followed by efnet and then any remaining servers. +Alternatively, {cyan}${{info:autosort_order,${{server}},freenode,oftc,*,efnet}}{reset} will sort any unlisted servers after freenode and oftc, but before efnet. + +{*white}${{info:{brown}autosort_escape{white},{cyan}text{white}}}{reset} +Escape commas and backslashes in {cyan}text{reset} by prepending them with a backslash. +This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. +Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments. + +For example, it can be used to safely pass buffer names to {cyan}${{info:autosort_replace}}{reset} like so: +{cyan}${{info:autosort_replace,##,#,${{info:autosort_escape,${{buffer.name}}}}}}{reset}. + + {*white}# Description Autosort is a weechat script to automatically keep your buffers sorted. The sort order can be customized by defining your own sort rules, but the default should @@ -934,17 +979,6 @@ readable. They can be used in the main sort rules as variables. For example, a helper variable named `{cyan}foo{reset}` can be accessed in a main rule with the string `{cyan}${{foo}}{reset}`. -{*white}# Replacing substrings{reset} -There is no default method for replacing text inside eval expressions. However, -autosort adds a `replace` info hook that can be used inside eval expressions: - {cyan}${{info:autosort_replace,from,to,text}}{reset} - -For example, to strip all hashes from a buffer name, you could write: - {cyan}${{info:autosort_replace,#,,${{buffer.name}}}}{reset} - -You can escape commas and backslashes inside the arguments by prefixing them with -a backslash. - {*white}# Automatic or manual sorting{reset} By default, autosort will automatically sort your buffer list whenever a buffer is opened, merged, unmerged or renamed. This should keep your buffers sorted in @@ -973,15 +1007,29 @@ structure with the following setting (modify to suit your need): command_completion = '%(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort)' -info_replace_description = 'Replace all occurences of `from` with `to` in the string `text`.' -info_replace_arguments = 'from,to,text' +info_replace_description = ( + 'Replace all occurrences of `pattern` with `replacement` in the string `source`. ' + 'Can be used to ignore certain strings when sorting by replacing them with an empty string. ' + 'See /help autosort for examples.' +) +info_replace_arguments = 'pattern,replacement,source' info_order_description = ( - 'Get a zero padded index of a value in a list of possible values.' - 'If the value is not found, the index for `*` is returned.' - 'If there is no `*` in the list, the highest index + 1 is returned.' + 'Generate a zero-padded number that corresponds to the index of `value` in the list of options. ' + 'If one of the options is the special value `*`, then any value not explicitly mentioned will be sorted at that position. ' + 'Otherwise, any value that does not match an option is assigned the highest number available. ' + 'Can be used to easily sort buffers based on a manual sequence. ' + 'See /help autosort for examples.' +) +info_order_arguments = 'value,first,second,third,...' + +info_escape_description = ( + 'Escape commas and backslashes in `text` by prepending them with a backslash. ' + 'This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. ' + 'Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments.' + 'See /help autosort for examples.' ) -info_order_arguments = 'value,first,second,third,...' +info_escape_arguments = 'text' if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): @@ -1014,6 +1062,7 @@ if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, weechat.hook_config('autosort.*', 'on_config_changed', '') weechat.hook_completion('plugin_autosort', '', 'on_autosort_complete', '') weechat.hook_command('autosort', command_description.format(**colors), '', '', command_completion, 'on_autosort_command', '') + weechat.hook_info('autosort_escape', info_escape_description, info_escape_arguments, 'on_info_escape', '') weechat.hook_info('autosort_replace', info_replace_description, info_replace_arguments, 'on_info_replace', '') weechat.hook_info('autosort_order', info_order_description, info_order_arguments, 'on_info_order', '') diff --git a/weechat/.weechat/python/otr.py b/weechat/.weechat/python/otr.py deleted file mode 100644 index 0ccfb35..0000000 --- a/weechat/.weechat/python/otr.py +++ /dev/null @@ -1,2135 +0,0 @@ -# -*- coding: utf-8 -*- -"""otr - WeeChat script for Off-the-Record IRC messaging - -DISCLAIMER: To the best of my knowledge this script securely provides OTR -messaging in WeeChat, but I offer no guarantee. Please report any security -holes you find. - -Copyright (c) 2012-2015 Matthew M. Boedicker - Nils Görs - Daniel "koolfy" Faucon - Felix Eckhofer - -Report issues at https://github.com/mmb/weechat-otr - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -# pylint: disable=too-many-lines - -from __future__ import unicode_literals - -import collections -import glob -import io -import os -import platform -import re -import traceback -import shlex -import shutil -import sys - -import potr -import weechat - -class PythonVersion2(object): - """Python 2 version of code that must differ between Python 2 and 3.""" - - def __init__(self): - import cgi - self.cgi = cgi - - import HTMLParser - self.html_parser = HTMLParser - self.html_parser_init_kwargs = {} - - import htmlentitydefs - self.html_entities = htmlentitydefs - - def html_escape(self, strng): - """Escape HTML characters in a string.""" - return self.cgi.escape(strng) - - def unicode(self, *args, **kwargs): - """Return the Unicode version of a string.""" - return unicode(*args, **kwargs) - - def unichr(self, *args, **kwargs): - """Return the one character string of a Unicode character number.""" - return unichr(*args, **kwargs) - - def to_unicode(self, strng): - """Convert a utf-8 encoded string to a Unicode.""" - if isinstance(strng, unicode): - return strng - return strng.decode('utf-8', 'replace') - - def to_str(self, strng): - """Convert a Unicode to a utf-8 encoded string.""" - return strng.encode('utf-8', 'replace') - -class PythonVersion3(object): - """Python 3 version of code that must differ between Python 2 and 3.""" - - def __init__(self, minor): - self.minor = minor - - import html - self.html = html - - import html.parser - self.html_parser = html.parser - if self.minor >= 4: - self.html_parser_init_kwargs = {'convert_charrefs' : True} - else: - self.html_parser_init_kwargs = {} - - import html.entities - self.html_entities = html.entities - - def html_escape(self, strng): - """Escape HTML characters in a string.""" - return self.html.escape(strng, quote=False) - - def unicode(self, *args, **kwargs): - """Return the Unicode version of a string.""" - return str(*args, **kwargs) - - def unichr(self, *args, **kwargs): - """Return the one character string of a Unicode character number.""" - return chr(*args, **kwargs) - - def to_unicode(self, strng): - """Convert a utf-8 encoded string to unicode.""" - if isinstance(strng, bytes): - return strng.decode('utf-8', 'replace') - return strng - - def to_str(self, strng): - """Convert a Unicode to a utf-8 encoded string.""" - return strng - -# We cannot use version_info.major as this is only supported on python >= 2.7 -if sys.version_info[0] >= 3: - PYVER = PythonVersion3(sys.version_info.minor) -else: - PYVER = PythonVersion2() - -SCRIPT_NAME = 'otr' -SCRIPT_DESC = 'Off-the-Record messaging for IRC' -SCRIPT_HELP = """{description} - -Quick start: - -Add an OTR item to the status bar by adding '[otr]' to the config setting -weechat.bar.status.items. This will show you whether your current conversation -is encrypted, authenticated and logged. /set otr.* for OTR status bar -customization options. - -Start a private conversation with a friend who has OTR: /query yourpeer hi - -In the private chat buffer: /otr start - -If you have not authenticated your peer yet, follow the instructions for -authentication. - -You can, at any time, see the current OTR session status and fingerprints with: -/otr status - -View OTR policies for your peer: /otr policy - -View default OTR policies: /otr policy default - -Start/Stop log recording for the current OTR session: /otr log [start|stop] -This will be reverted back to the previous log setting at the end of the session. - -To refresh the OTR session: /otr refresh - -To end your private conversation: /otr finish - -This script supports only OTR protocol version 2. -""".format(description=SCRIPT_DESC) - -SCRIPT_AUTHOR = 'Matthew M. Boedicker' -SCRIPT_LICENCE = 'GPL3' -SCRIPT_VERSION = '1.9.2' - -OTR_DIR_NAME = 'otr' - -OTR_QUERY_RE = re.compile(r'\?OTR(\?|\??v[a-z\d]*\?)') - -POLICIES = { - 'allow_v2' : 'allow OTR protocol version 2, effectively enable OTR ' - 'since v2 is the only supported version', - 'require_encryption' : 'refuse to send unencrypted messages when OTR is ' - 'enabled', - 'log' : 'enable logging of OTR conversations', - 'send_tag' : 'advertise your OTR capability using the whitespace tag', - 'html_escape' : 'escape HTML special characters in outbound messages', - 'html_filter' : 'filter HTML in incoming messages', - } - -READ_ONLY_POLICIES = { - 'allow_v1' : False, - } - -ACTION_PREFIX = '/me ' -IRC_ACTION_RE = re.compile('^\x01ACTION (?P.*)\x01$') -PLAIN_ACTION_RE = re.compile('^'+ACTION_PREFIX+'(?P.*)$') - -IRC_SANITIZE_TABLE = dict((ord(char), None) for char in '\n\r\x00') - -# Patch potr.proto.TaggedPlaintext to not end plaintext tags in a space. -# -# When POTR adds OTR tags to plaintext it puts them at the end of the message. -# The tags end in a space which gets stripped off by WeeChat because it -# strips trailing spaces from commands. This causes OTR initiation to fail so -# the following code adds an extra tab at the end of the plaintext tags if -# they end in a space. -# -# The patched version also skips OTR tagging for CTCP messages because it -# breaks the CTCP format. -def patched__bytes__(self): - """Patched potr.proto.TaggedPlainText.__bytes__.""" - # Do not tag CTCP messages. - if self.msg.startswith(b'\x01') and \ - self.msg.endswith(b'\x01'): - return self.msg - - data = self.msg + potr.proto.MESSAGE_TAG_BASE - for v in self.versions: - data += potr.proto.MESSAGE_TAGS[v] - if data.endswith(b' '): - data += b'\t' - return data - -potr.proto.TaggedPlaintext.__bytes__ = patched__bytes__ - -def command(buf, command_str): - """Wrap weechat.command() with utf-8 encode.""" - debug(command_str) - weechat.command(buf, PYVER.to_str(command_str)) - -def privmsg(server, nick, message): - """Send a private message to a nick.""" - for line in message.splitlines(): - command('', '/quote -server {server} PRIVMSG {nick} :{line}'.format( - server=irc_sanitize(server), - nick=irc_sanitize(nick), - line=irc_sanitize(line))) - -def build_privmsg_in(fromm, target, msg): - """Build inbound IRC PRIVMSG command.""" - return ':{user} PRIVMSG {target} :{msg}'.format( - user=irc_sanitize(fromm), - target=irc_sanitize(target), - msg=irc_sanitize(msg)) - -def build_privmsgs_in(fromm, target, msg, prefix=''): - """Build an inbound IRC PRIVMSG command for each line in msg. - If prefix is supplied, prefix each line of msg with it.""" - cmd = [] - for line in msg.splitlines(): - cmd.append(build_privmsg_in(fromm, target, prefix+line)) - return '\r\n'.join(cmd) - -def build_privmsg_out(target, msg): - """Build outbound IRC PRIVMSG command(s).""" - cmd = [] - for line in msg.splitlines(): - cmd.append('PRIVMSG {target} :{line}'.format( - target=irc_sanitize(target), - line=irc_sanitize(line))) - return '\r\n'.join(cmd) - -def irc_sanitize(msg): - """Remove NUL, CR and LF characters from msg. - The (utf-8 encoded version of a) string returned from this function - should be safe to use as an argument in an irc command.""" - return PYVER.unicode(msg).translate(IRC_SANITIZE_TABLE) - -def prnt(buf, message): - """Wrap weechat.prnt() with utf-8 encode.""" - weechat.prnt(buf, PYVER.to_str(message)) - -def print_buffer(buf, message, level='info'): - """Print message to buf with prefix, - using color according to level.""" - prnt(buf, '{prefix}\t{msg}'.format( - prefix=get_prefix(), - msg=colorize(message, 'buffer.{0}'.format(level)))) - -def get_prefix(): - """Returns configured message prefix.""" - return weechat.string_eval_expression( - config_string('look.prefix'), - {}, {}, {}) - -def debug(msg): - """Send a debug message to the OTR debug buffer.""" - debug_option = config_get_prefixed('general.debug') - - if not weechat.config_boolean(debug_option): - return - - debug_buffer = weechat.buffer_search('python', 'OTR Debug') - if not debug_buffer: - debug_buffer = weechat.buffer_new('OTR Debug', '', '', '', '') - weechat.buffer_set(debug_buffer, 'title', 'OTR Debug') - weechat.buffer_set(debug_buffer, 'localvar_set_no_log', '1') - - prnt(debug_buffer, ('{script} debug\t{text}'.format( - script=SCRIPT_NAME, text=PYVER.unicode(msg)))) - -def current_user(server_name): - """Get the nick and server of the current user on a server.""" - return irc_user(info_get('irc_nick', server_name), server_name) - -def irc_user(nick, server): - """Build an IRC user string from a nick and server.""" - return '{nick}@{server}'.format( - nick=nick.lower(), - server=server) - -def isupport_value(server, feature): - """Get the value of an IRC server feature.""" - args = '{server},{feature}'.format(server=server, feature=feature) - return info_get('irc_server_isupport_value', args) - -def is_a_channel(channel, server): - """Return true if a string has an IRC channel prefix.""" - prefixes = \ - tuple(isupport_value(server, 'CHANTYPES')) + \ - tuple(isupport_value(server, 'STATUSMSG')) - - # If the server returns nothing for CHANTYPES and STATUSMSG use - # default prefixes. - if not prefixes: - prefixes = ('#', '&', '+', '!', '@') - - return channel.startswith(prefixes) - -class PrivmsgParseException(Exception): - """Exception class for PRIVMSG parsing exceptions.""" - pass - -def parse_irc_privmsg(message, server): - """Parse an IRC PRIVMSG command and return a dictionary. - - Either the to_channel key or the to_nick key will be set depending on - whether the message is to a nick or a channel. The other will be None. - - Example input: - - :nick!user@host PRIVMSG #weechat :message here - - Output: - - {'from': 'nick!user@host', - 'from_nick': 'nick', - 'to': '#weechat', - 'to_channel': '#weechat', - 'to_nick': None, - 'text': 'message here'} - """ - - weechat_result = weechat.info_get_hashtable( - 'irc_message_parse', dict(message=message)) - - if weechat_result['command'].upper() == 'PRIVMSG': - target, text = PYVER.to_unicode( - weechat_result['arguments']).split(' ', 1) - if text.startswith(':'): - text = text[1:] - - result = { - 'from': PYVER.to_unicode(weechat_result['host']), - 'to' : target, - 'text': text, - } - - if weechat_result['host']: - result['from_nick'] = PYVER.to_unicode(weechat_result['nick']) - else: - result['from_nick'] = '' - - if is_a_channel(target, server): - result['to_channel'] = target - result['to_nick'] = None - else: - result['to_channel'] = None - result['to_nick'] = target - - return result - else: - raise PrivmsgParseException(message) - -def has_otr_end(msg): - """Return True if the message is the end of an OTR message.""" - return msg.endswith('.') or msg.endswith(',') - -def first_instance(objs, klass): - """Return the first object in the list that is an instance of a class.""" - for obj in objs: - if isinstance(obj, klass): - return obj - -def config_prefix(option): - """Add the config prefix to an option and return the full option name.""" - return '{script}.{option}'.format( - script=SCRIPT_NAME, - option=option) - -def config_color(option): - """Get the color of a color config option.""" - return weechat.color(weechat.config_color(config_get_prefixed( - 'color.{0}'.format(option)))) - -def config_string(option): - """Get the string value of a config option with utf-8 decode.""" - return PYVER.to_unicode(weechat.config_string( - config_get_prefixed(option))) - -def config_get(option): - """Get the value of a WeeChat config option.""" - return weechat.config_get(PYVER.to_str(option)) - -def config_get_prefixed(option): - """Get the value of a script prefixed WeeChat config option.""" - return config_get(config_prefix(option)) - -def buffer_get_string(buf, prop): - """Wrap weechat.buffer_get_string() with utf-8 encode/decode.""" - if buf is not None: - encoded_buf = PYVER.to_str(buf) - else: - encoded_buf = None - - return PYVER.to_unicode(weechat.buffer_get_string( - encoded_buf, PYVER.to_str(prop))) - -def buffer_is_private(buf): - """Return True if a buffer is private.""" - return buffer_get_string(buf, 'localvar_type') == 'private' - -def info_get(info_name, arguments): - """Wrap weechat.info_get() with utf-8 encode/decode.""" - return PYVER.to_unicode(weechat.info_get( - PYVER.to_str(info_name), PYVER.to_str(arguments))) - -def msg_irc_from_plain(msg): - """Transform a plain-text message to irc format. - This will replace lines that start with /me with the respective - irc command.""" - return PLAIN_ACTION_RE.sub('\x01ACTION \g\x01', msg) - -def msg_plain_from_irc(msg): - """Transform an irc message to plain-text. - Any ACTION found will be rewritten as /me .""" - return IRC_ACTION_RE.sub(ACTION_PREFIX + r'\g', msg) - -def default_peer_args(args, buf): - """Get the nick and server of a remote peer from command arguments or - a buffer. - - args is the [nick, server] slice of arguments from a command. - If these are present, return them. If args is empty and the buffer buf - is private, return the remote nick and server of buf.""" - result = None, None - - if len(args) == 2: - result = tuple(args) - else: - if buffer_is_private(buf): - result = ( - buffer_get_string(buf, 'localvar_channel'), - buffer_get_string(buf, 'localvar_server')) - - return result - -def format_default_policies(): - """Return current default policies formatted as a string for the user.""" - buf = io.StringIO() - - buf.write('Current default OTR policies:\n') - - for policy, desc in sorted(POLICIES.items()): - buf.write(' {policy} ({desc}) : {value}\n'.format( - policy=policy, - desc=desc, - value=config_string('policy.default.{0}'.format(policy)))) - - buf.write('Change default policies with: /otr policy default NAME on|off') - - return buf.getvalue() - -def to_bytes(strng): - """Convert a python str or unicode to bytes.""" - return strng.encode('utf-8', 'replace') - -def colorize(msg, color): - """Colorize each line of msg using color.""" - result = [] - colorstr = config_color(color) - - for line in msg.splitlines(): - result.append('{color}{msg}'.format( - color=colorstr, - msg=line)) - - return '\r\n'.join(result) - -def accounts(): - """Return a list of all IrcOtrAccounts sorted by name.""" - result = [] - for key_path in glob.iglob(os.path.join(OTR_DIR, '*.key3')): - key_name, _ = os.path.splitext(os.path.basename(key_path)) - result.append(ACCOUNTS[key_name]) - - return sorted(result, key=lambda account: account.name) - -def show_account_fingerprints(): - """Print all account names and their fingerprints to the core buffer.""" - table_formatter = TableFormatter() - for account in accounts(): - table_formatter.add_row([ - account.name, - str(account.getPrivkey())]) - print_buffer('', table_formatter.format()) - -def show_peer_fingerprints(grep=None): - """Print peer names and their fingerprints to the core buffer. - - If grep is passed in, show all peer names containing that substring.""" - trust_descs = { - '' : 'unverified', - 'smp' : 'SMP verified', - 'verified' : 'verified', - } - - table_formatter = TableFormatter() - for account in accounts(): - for peer, peer_data in sorted(account.trusts.items()): - for fingerprint, trust in sorted(peer_data.items()): - if grep is None or grep in peer: - table_formatter.add_row([ - peer, - account.name, - potr.human_hash(fingerprint), - trust_descs[trust], - ]) - print_buffer('', table_formatter.format()) - -def private_key_file_path(account_name): - """Return the private key file path for an account.""" - return os.path.join(OTR_DIR, '{0}.key3'.format(account_name)) - -def read_private_key(key_file_path): - """Return the private key in a private key file.""" - debug(('read private key', key_file_path)) - - with open(key_file_path, 'rb') as key_file: - return potr.crypt.PK.parsePrivateKey(key_file.read())[0] - -def get_context(account_name, context_name): - """Return a context from an account.""" - return ACCOUNTS[account_name].getContext(context_name) - -def get_server_context(server, peer_nick): - """Return the context for the current server user and peer.""" - return get_context(current_user(server), irc_user(peer_nick, server)) - -class AccountDict(collections.defaultdict): - """Dictionary that adds missing keys as IrcOtrAccount instances.""" - - def __missing__(self, key): - debug(('add account', key)) - self[key] = IrcOtrAccount(key) - - return self[key] - -class Assembler(object): - """Reassemble fragmented OTR messages. - - This does not deal with OTR fragmentation, which is handled by potr, but - fragmentation of received OTR messages that are too large for IRC. - """ - def __init__(self): - self.clear() - - def add(self, data): - """Add data to the buffer.""" - self.value += data - - def clear(self): - """Empty the buffer.""" - self.value = '' - - def is_done(self): - """Return True if the buffer is a complete message.""" - return self.is_query() or \ - not to_bytes(self.value).startswith(potr.proto.OTRTAG) or \ - has_otr_end(self.value) - - def get(self): - """Return the current value of the buffer and empty it.""" - result = self.value - self.clear() - - return result - - def is_query(self): - """Return true if the buffer is an OTR query.""" - return OTR_QUERY_RE.search(self.value) - -class IrcContext(potr.context.Context): - """Context class for OTR over IRC.""" - - def __init__(self, account, peername): - super(IrcContext, self).__init__(account, peername) - - self.peer_nick, self.peer_server = peername.split('@', 1) - self.in_assembler = Assembler() - self.in_otr_message = False - self.in_smp = False - self.smp_question = False - - def policy_config_option(self, policy): - """Get the option name of a policy option for this context.""" - return config_prefix('.'.join([ - 'policy', self.peer_server, self.user.nick, self.peer_nick, - policy.lower()])) - - def getPolicy(self, key): - """Get the value of a policy option for this context.""" - key_lower = key.lower() - - if key_lower in READ_ONLY_POLICIES: - result = READ_ONLY_POLICIES[key_lower] - elif key_lower == 'send_tag' and self.no_send_tag(): - result = False - else: - option = config_get(self.policy_config_option(key)) - - if option == '': - option = config_get(self.user.policy_config_option(key)) - - if option == '': - option = config_get_prefixed('.'.join( - ['policy', self.peer_server, key_lower])) - - if option == '': - option = config_get_prefixed( - 'policy.default.{0}'.format(key_lower)) - - result = bool(weechat.config_boolean(option)) - - debug(('getPolicy', key, result)) - - return result - - def inject(self, msg, appdata=None): - """Send a message to the remote peer.""" - if isinstance(msg, potr.proto.OTRMessage): - msg = PYVER.unicode(msg) - else: - msg = PYVER.to_unicode(msg) - - debug(('inject', msg, 'len {0}'.format(len(msg)), appdata)) - - privmsg(self.peer_server, self.peer_nick, msg) - - def setState(self, newstate): - """Handle state transition.""" - debug(('state', self.state, newstate)) - - if self.is_encrypted(): - if newstate == potr.context.STATE_ENCRYPTED: - self.print_buffer( - 'Private conversation has been refreshed.', 'success') - elif newstate == potr.context.STATE_FINISHED: - self.print_buffer( - '{peer} has ended the private conversation. You should do ' - 'the same:\n/otr finish'.format(peer=self.peer_nick)) - elif newstate == potr.context.STATE_ENCRYPTED: - # unencrypted => encrypted - trust = self.getCurrentTrust() - - # Disable logging before any proof of OTR activity is generated. - # This is necessary when the session is started automatically, and - # not by /otr start. - if not self.getPolicy('log'): - self.previous_log_level = self.disable_logging() - else: - self.previous_log_level = self.get_log_level() - if self.is_logged(): - self.hint( - 'You have enabled the recording to disk of OTR ' - 'conversations. By doing this you are potentially ' - 'putting yourself and your correspondent in danger. ' - 'Please consider disabling this policy with ' - '"/otr policy default log off". To disable logging ' - 'for this OTR session, use "/otr log stop"') - - if trust is None: - fpr = str(self.getCurrentKey()) - self.print_buffer('New fingerprint: {0}'.format(fpr), 'warning') - self.setCurrentTrust('') - - if bool(trust): - self.print_buffer( - 'Authenticated secured OTR conversation started.', - 'success') - else: - self.print_buffer( - 'Unauthenticated secured OTR conversation started.', - 'warning') - self.hint(self.verify_instructions()) - - if self.state != potr.context.STATE_PLAINTEXT and \ - newstate == potr.context.STATE_PLAINTEXT: - self.print_buffer('Private conversation ended.') - - # If we altered the logging value, restore it. - if self.previous_log_level is not None: - self.restore_logging(self.previous_log_level) - - super(IrcContext, self).setState(newstate) - - def maxMessageSize(self, appdata=None): - """Return the max message size for this context.""" - # remove 'PRIVMSG :' from max message size - result = self.user.maxMessageSize - 10 - len(self.peer_nick) - debug('max message size {0}'.format(result)) - - return result - - def buffer(self): - """Get the buffer for this context.""" - return info_get( - 'irc_buffer', '{server},{nick}'.format( - server=self.peer_server, - nick=self.peer_nick - )) - - def print_buffer(self, msg, level='info'): - """Print a message to the buffer for this context. - level is used to colorize the message.""" - buf = self.buffer() - - # add [nick] prefix if we have only a server buffer for the query - if self.peer_nick and not buffer_is_private(buf): - msg = '[{nick}] {msg}'.format( - nick=self.peer_nick, - msg=msg) - - print_buffer(buf, msg, level) - - def hint(self, msg): - """Print a message to the buffer but only when hints are enabled.""" - hints_option = config_get_prefixed('general.hints') - - if weechat.config_boolean(hints_option): - self.print_buffer(msg, 'hint') - - def smp_finish(self, message=False, level='info'): - """Reset SMP state and send a message to the user.""" - self.in_smp = False - self.smp_question = False - - self.user.saveTrusts() - if message: - self.print_buffer(message, level) - - def handle_tlvs(self, tlvs): - """Handle SMP states.""" - if tlvs: - smp1q = first_instance(tlvs, potr.proto.SMP1QTLV) - smp3 = first_instance(tlvs, potr.proto.SMP3TLV) - smp4 = first_instance(tlvs, potr.proto.SMP4TLV) - - if first_instance(tlvs, potr.proto.SMPABORTTLV): - debug('SMP aborted by peer') - self.smp_finish('SMP aborted by peer.', 'warning') - elif self.in_smp and not self.smpIsValid(): - debug('SMP aborted') - self.smp_finish('SMP aborted.', 'error') - elif first_instance(tlvs, potr.proto.SMP1TLV): - debug('SMP1') - self.in_smp = True - - self.print_buffer( - """Peer has requested SMP verification. -Respond with: /otr smp respond """) - elif smp1q: - debug(('SMP1Q', smp1q.msg)) - self.in_smp = True - self.smp_question = True - - self.print_buffer( - """Peer has requested SMP verification: {msg} -Respond with: /otr smp respond """.format( - msg=PYVER.to_unicode(smp1q.msg))) - elif first_instance(tlvs, potr.proto.SMP2TLV): - if not self.in_smp: - debug('Received unexpected SMP2') - self.smp_finish() - else: - debug('SMP2') - self.print_buffer('SMP progressing.') - elif smp3 or smp4: - if smp3: - debug('SMP3') - elif smp4: - debug('SMP4') - - if self.smpIsSuccess(): - - if self.smp_question: - self.smp_finish( - 'SMP verification succeeded.', 'success') - if not self.is_verified: - self.print_buffer( - 'You may want to authenticate your peer by ' - 'asking your own question:\n' - "/otr smp ask <'question'> 'secret'") - else: - self.smp_finish( - 'SMP verification succeeded.', 'success') - - else: - self.smp_finish('SMP verification failed.', 'error') - - def verify_instructions(self): - """Generate verification instructions for user.""" - return """You can verify that this contact is who they claim to be in -one of the following ways: - -1) Verify each other's fingerprints using a secure channel: - Your fingerprint : {your_fp} - {peer}'s fingerprint : {peer_fp} - then use the command: /otr trust {peer_nick} {peer_server} - -2) SMP pre-shared secret that you both know: - /otr smp ask {peer_nick} {peer_server} 'secret' - -3) SMP pre-shared secret that you both know with a question: - /otr smp ask {peer_nick} {peer_server} <'question'> 'secret' - -Note: You can safely omit specifying the peer and server when - executing these commands from the appropriate conversation - buffer -""".format( - your_fp=self.user.getPrivkey(), - peer=self.peer, - peer_nick=self.peer_nick, - peer_server=self.peer_server, - peer_fp=potr.human_hash(self.crypto.theirPubkey.cfingerprint())) - - def is_encrypted(self): - """Return True if the conversation with this context's peer is - currently encrypted.""" - return self.state == potr.context.STATE_ENCRYPTED - - def is_verified(self): - """Return True if this context's peer is verified.""" - return bool(self.getCurrentTrust()) - - def format_policies(self): - """Return current policies for this context formatted as a string for - the user.""" - buf = io.StringIO() - - buf.write('Current OTR policies for {peer}:\n'.format( - peer=self.peer)) - - for policy, desc in sorted(POLICIES.items()): - buf.write(' {policy} ({desc}) : {value}\n'.format( - policy=policy, - desc=desc, - value='on' if self.getPolicy(policy) else 'off')) - - buf.write('Change policies with: /otr policy NAME on|off') - - return buf.getvalue() - - def is_logged(self): - """Return True if conversations with this context's peer are currently - being logged to disk.""" - infolist = weechat.infolist_get('logger_buffer', '', '') - - buf = self.buffer() - - result = False - - while weechat.infolist_next(infolist): - if weechat.infolist_pointer(infolist, 'buffer') == buf: - result = bool(weechat.infolist_integer(infolist, 'log_enabled')) - break - - weechat.infolist_free(infolist) - - return result - - def get_log_level(self): - """Return the current logging level for this context's peer - or -1 if the buffer uses the default log level of weechat.""" - infolist = weechat.infolist_get('logger_buffer', '', '') - - buf = self.buffer() - - if not config_get(self.get_logger_option_name()): - result = -1 - else: - result = 0 - - while weechat.infolist_next(infolist): - if weechat.infolist_pointer(infolist, 'buffer') == buf: - result = weechat.infolist_integer(infolist, 'log_level') - break - - weechat.infolist_free(infolist) - - return result - - def get_logger_option_name(self): - """Returns the logger config option for this context's buffer.""" - buf = self.buffer() - name = buffer_get_string(buf, 'name') - plugin = buffer_get_string(buf, 'plugin') - - return 'logger.level.{plugin}.{name}'.format( - plugin=plugin, name=name) - - def disable_logging(self): - """Return the previous logger level and set the buffer logger level - to 0. If it was already 0, return None.""" - # If previous_log_level has not been previously set, return the level - # we detect now. - if not hasattr(self, 'previous_log_level'): - previous_log_level = self.get_log_level() - - if self.is_logged(): - weechat.command(self.buffer(), '/mute logger disable') - self.print_buffer( - 'Logs have been temporarily disabled for the session. ' - 'They will be restored upon finishing the OTR session.') - - return previous_log_level - - # If previous_log_level was already set, it means we already altered it - # and that we just detected an already modified logging level. - # Return the pre-existing value so it doesn't get lost, and we can - # restore it later. - else: - return self.previous_log_level - - def restore_logging(self, previous_log_level): - """Restore the log level of the buffer.""" - buf = self.buffer() - - if (previous_log_level >= 0) and (previous_log_level < 10): - self.print_buffer( - 'Restoring buffer logging value to: {0}'.format( - previous_log_level), 'warning') - weechat.command(buf, '/mute logger set {0}'.format( - previous_log_level)) - - if previous_log_level == -1: - logger_option_name = self.get_logger_option_name() - self.print_buffer( - 'Restoring buffer logging value to default', 'warning') - weechat.command(buf, '/mute unset {0}'.format( - logger_option_name)) - - del self.previous_log_level - - def msg_convert_in(self, msg): - """Transform incoming OTR message to IRC format. - This includes stripping html, converting plain-text ACTIONs - and character encoding conversion. - Only character encoding is changed if context is unencrypted.""" - msg = PYVER.to_unicode(msg) - - if not self.is_encrypted(): - return msg - - if self.getPolicy('html_filter'): - try: - msg = IrcHTMLParser.parse(msg) - except PYVER.html_parser.HTMLParseError: - pass - - return msg_irc_from_plain(msg) - - def msg_convert_out(self, msg): - """Convert an outgoing IRC message to be sent over OTR. - This includes escaping html, converting ACTIONs to plain-text - and character encoding conversion - Only character encoding is changed if context is unencrypted.""" - if self.is_encrypted(): - msg = msg_plain_from_irc(msg) - - if self.getPolicy('html_escape'): - msg = PYVER.html_escape(msg) - - # potr expects bytes to be returned - return to_bytes(msg) - - def no_send_tag(self): - """Skip OTR whitespace tagging to bots and services. - - Any nicks matching the otr.general.no_send_tag_regex config setting - will not be tagged. - """ - no_send_tag_regex = config_string('general.no_send_tag_regex') - debug(('no_send_tag', no_send_tag_regex, self.peer_nick)) - if no_send_tag_regex: - return re.match(no_send_tag_regex, self.peer_nick, re.IGNORECASE) - - def __repr__(self): - return PYVER.to_str(( - '<{0} {1:x} peer_nick={c.peer_nick} peer_server={c.peer_server}>' - ).format(self.__class__.__name__, id(self), c=self)) - -class IrcOtrAccount(potr.context.Account): - """Account class for OTR over IRC.""" - - contextclass = IrcContext - - PROTOCOL = 'irc' - MAX_MSG_SIZE = 415 - - def __init__(self, name): - super(IrcOtrAccount, self).__init__( - name, IrcOtrAccount.PROTOCOL, IrcOtrAccount.MAX_MSG_SIZE) - - self.nick, self.server = self.name.split('@', 1) - - # IRC messages cannot have newlines, OTR query and "no plugin" text - # need to be one message - self.defaultQuery = self.defaultQuery.replace("\n", ' ') - - self.key_file_path = private_key_file_path(name) - self.fpr_file_path = os.path.join(OTR_DIR, '{0}.fpr'.format(name)) - - self.load_trusts() - - def load_trusts(self): - """Load trust data from the fingerprint file.""" - if os.path.exists(self.fpr_file_path): - with open(self.fpr_file_path) as fpr_file: - for line in fpr_file: - debug(('load trust check', line)) - - context, account, protocol, fpr, trust = \ - PYVER.to_unicode(line[:-1]).split('\t') - - if account == self.name and \ - protocol == IrcOtrAccount.PROTOCOL: - debug(('set trust', context, fpr, trust)) - self.setTrust(context, fpr, trust) - - def loadPrivkey(self): - """Load key file. - - If no key file exists, load the default key. If there is no default - key, a new key will be generated automatically by potr.""" - debug(('load private key', self.key_file_path)) - - if os.path.exists(self.key_file_path): - return read_private_key(self.key_file_path) - else: - default_key = config_string('general.defaultkey') - if default_key: - default_key_path = private_key_file_path(default_key) - - if os.path.exists(default_key_path): - shutil.copyfile(default_key_path, self.key_file_path) - return read_private_key(self.key_file_path) - - def savePrivkey(self): - """Save key file.""" - debug(('save private key', self.key_file_path)) - - with open(self.key_file_path, 'wb') as key_file: - key_file.write(self.getPrivkey().serializePrivateKey()) - - def saveTrusts(self): - """Save trusts.""" - with open(self.fpr_file_path, 'w') as fpr_file: - for uid, trusts in self.trusts.items(): - for fpr, trust in trusts.items(): - debug(('trust write', uid, self.name, - IrcOtrAccount.PROTOCOL, fpr, trust)) - fpr_file.write(PYVER.to_str('\t'.join( - (uid, self.name, IrcOtrAccount.PROTOCOL, fpr, trust)))) - fpr_file.write('\n') - - def end_all_private(self): - """End all currently encrypted conversations.""" - for context in self.ctxs.values(): - if context.is_encrypted(): - context.disconnect() - - def policy_config_option(self, policy): - """Get the option name of a policy option for this account.""" - return config_prefix('.'.join([ - 'policy', self.server, self.nick, policy.lower()])) - -class IrcHTMLParser(PYVER.html_parser.HTMLParser): - """A simple HTML parser that throws away anything but newlines and links""" - - @staticmethod - def parse(data): - """Create a temporary IrcHTMLParser and parse a single string""" - parser = IrcHTMLParser(**PYVER.html_parser_init_kwargs) - parser.feed(data) - parser.close() - return parser.result - - def reset(self): - """Forget all state, called from __init__""" - PYVER.html_parser.HTMLParser.reset(self) - self.result = '' - self.linktarget = '' - self.linkstart = 0 - - def handle_starttag(self, tag, attrs): - """Called when a start tag is encountered""" - if tag == 'br': - self.result += '\n' - elif tag == 'a': - attrs = dict(attrs) - if 'href' in attrs: - self.result += '[' - self.linktarget = attrs['href'] - self.linkstart = len(self.result) - - def handle_endtag(self, tag): - """Called when an end tag is encountered""" - if tag == 'a': - if self.linktarget: - if self.result[self.linkstart:] == self.linktarget: - self.result += ']' - else: - self.result += ']({0})'.format(self.linktarget) - self.linktarget = '' - - def handle_data(self, data): - """Called for character data (i.e. text)""" - self.result += data - - def handle_entityref(self, name): - """Called for entity references, such as &""" - try: - self.result += PYVER.unichr( - PYVER.html_entities.name2codepoint[name]) - except KeyError: - self.result += '&{0};'.format(name) - - def handle_charref(self, name): - """Called for character references, such as '""" - try: - if name.startswith('x'): - self.result += PYVER.unichr(int(name[1:], 16)) - else: - self.result += PYVER.unichr(int(name)) - except ValueError: - self.result += '&#{0};'.format(name) - -class TableFormatter(object): - """Format lists of string into aligned tables.""" - - def __init__(self): - self.rows = [] - self.max_widths = None - - def add_row(self, row): - """Add a row to the table.""" - self.rows.append(row) - row_widths = [len(s) for s in row] - if self.max_widths is None: - self.max_widths = row_widths - else: - self.max_widths = list(map(max, self.max_widths, row_widths)) - - def format(self): - """Return the formatted table as a string.""" - return '\n'.join([self.format_row(row) for row in self.rows]) - - def format_row(self, row): - """Format a single row as a string.""" - return ' |'.join( - [s.ljust(self.max_widths[i]) for i, s in enumerate(row)]) - -def message_in_cb(data, modifier, modifier_data, string): - """Incoming message callback""" - debug(('message_in_cb', data, modifier, modifier_data, string)) - - parsed = parse_irc_privmsg( - PYVER.to_unicode(string), PYVER.to_unicode(modifier_data)) - debug(('parsed message', parsed)) - - # skip processing messages to public channels - if parsed['to_channel']: - return string - - server = PYVER.to_unicode(modifier_data) - - context = get_server_context(server, parsed['from_nick']) - - context.in_assembler.add(parsed['text']) - - result = '' - - if context.in_assembler.is_done(): - try: - msg, tlvs = context.receiveMessage( - # potr expects bytes - to_bytes(context.in_assembler.get())) - - debug(('receive', msg, tlvs)) - - if msg: - result = PYVER.to_str(build_privmsgs_in( - parsed['from'], parsed['to'], - context.msg_convert_in(msg))) - - context.handle_tlvs(tlvs) - except potr.context.ErrorReceived as err: - context.print_buffer('Received OTR error: {0}'.format( - PYVER.to_unicode(err.args[0])), 'error') - except potr.context.NotEncryptedError: - context.print_buffer( - 'Received encrypted data but no private session established.', - 'warning') - except potr.context.NotOTRMessage: - result = string - except potr.context.UnencryptedMessage as err: - result = PYVER.to_str(build_privmsgs_in( - parsed['from'], parsed['to'], PYVER.to_unicode( - msg_plain_from_irc(err.args[0])), - 'Unencrypted message received: ')) - - weechat.bar_item_update(SCRIPT_NAME) - - return result - -def message_out_cb(data, modifier, modifier_data, string): - """Outgoing message callback.""" - result = '' - - # If any exception is raised in this function, WeeChat will not send the - # outgoing message, which could be something that the user intended to be - # encrypted. This paranoid exception handling ensures that the system - # fails closed and not open. - try: - debug(('message_out_cb', data, modifier, modifier_data, string)) - - parsed = parse_irc_privmsg( - PYVER.to_unicode(string), PYVER.to_unicode(modifier_data)) - debug(('parsed message', parsed)) - - # skip processing messages to public channels - if parsed['to_channel']: - return string - - server = PYVER.to_unicode(modifier_data) - - context = get_server_context(server, parsed['to_nick']) - is_query = OTR_QUERY_RE.search(parsed['text']) - - parsed_text_bytes = to_bytes(parsed['text']) - - is_otr_message = \ - parsed_text_bytes[:len(potr.proto.OTRTAG)] == potr.proto.OTRTAG - - if is_otr_message and not is_query: - if not has_otr_end(parsed['text']): - debug('in OTR message') - context.in_otr_message = True - else: - debug('complete OTR message') - result = string - elif context.in_otr_message: - if has_otr_end(parsed['text']): - context.in_otr_message = False - debug('in OTR message end') - result = string - else: - debug(('context send message', parsed['text'], parsed['to_nick'], - server)) - - if context.policyOtrEnabled() and \ - not context.is_encrypted() and \ - not is_query and \ - context.getPolicy('require_encryption'): - context.print_buffer( - 'Your message will not be sent, because policy requires an ' - 'encrypted connection.', 'error') - context.hint( - 'Wait for the OTR connection or change the policy to allow ' - 'clear-text messages:\n' - '/otr policy require_encryption off') - - try: - ret = context.sendMessage( - potr.context.FRAGMENT_SEND_ALL, - context.msg_convert_out(parsed['text'])) - - if ret: - debug(('sendMessage returned', ret)) - result = PYVER.to_str( - build_privmsg_out( - parsed['to_nick'], PYVER.to_unicode(ret) - )) - - except potr.context.NotEncryptedError as err: - if err.args[0] == potr.context.EXC_FINISHED: - context.print_buffer( - 'Your message was not sent. End your private ' - 'conversation:\n/otr finish', - 'error') - else: - raise - - weechat.bar_item_update(SCRIPT_NAME) - # pylint: disable=bare-except - except: - try: - print_buffer('', traceback.format_exc(), 'error') - print_buffer('', 'Versions: {versions}'.format( - versions=dependency_versions()), 'error') - context.print_buffer( - 'Failed to send message. See core buffer for traceback.', - 'error') - # pylint: disable=bare-except - except: - pass - - return result - -def shutdown(): - """Script unload callback.""" - debug('shutdown') - - weechat.config_write(CONFIG_FILE) - - for account in ACCOUNTS.values(): - account.end_all_private() - - free_all_config() - - weechat.bar_item_remove(OTR_STATUSBAR) - - return weechat.WEECHAT_RC_OK - -def command_cb(data, buf, args): - """Parse and dispatch WeeChat OTR commands.""" - result = weechat.WEECHAT_RC_ERROR - - arg_parts = [PYVER.to_unicode(arg) for arg in shlex.split(args)] - - if len(arg_parts) in (1, 3) and arg_parts[0] in ('start', 'refresh'): - nick, server = default_peer_args(arg_parts[1:3], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - # We need to wall disable_logging() here so that no OTR-related - # buffer messages get logged at any point. disable_logging() will - # be called again when effectively switching to encrypted, but - # the previous_log_level we set here will be preserved for later - # restoring. - if not context.getPolicy('log'): - context.previous_log_level = context.disable_logging() - else: - context.previous_log_level = context.get_log_level() - - context.hint( - 'Sending OTR query... Please await confirmation of the OTR ' - 'session being started before sending a message.') - if not context.getPolicy('send_tag'): - context.hint( - 'To try OTR on all conversations with {peer}: /otr ' - 'policy send_tag on'.format(peer=context.peer)) - - privmsg(server, nick, '?OTR?') - - result = weechat.WEECHAT_RC_OK - elif len(arg_parts) in (1, 3) and arg_parts[0] in ('finish', 'end'): - nick, server = default_peer_args(arg_parts[1:3], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - context.disconnect() - - result = weechat.WEECHAT_RC_OK - - elif len(arg_parts) in (1, 3) and arg_parts[0] == 'status': - nick, server = default_peer_args(arg_parts[1:3], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - if context.is_encrypted(): - context.print_buffer( - 'This conversation is encrypted.', 'success') - context.print_buffer("Your fingerprint is: {0}".format( - context.user.getPrivkey())) - context.print_buffer("Your peer's fingerprint is: {0}".format( - potr.human_hash(context.crypto.theirPubkey.cfingerprint()))) - if context.is_verified(): - context.print_buffer( - "The peer's identity has been verified.", - 'success') - else: - context.print_buffer( - "You have not verified the peer's identity yet.", - 'warning') - else: - context.print_buffer( - "This current conversation is not encrypted.", - 'warning') - - result = weechat.WEECHAT_RC_OK - - elif len(arg_parts) in range(2, 7) and arg_parts[0] == 'smp': - action = arg_parts[1] - - if action == 'respond': - # Check if nickname and server are specified - if len(arg_parts) == 3: - nick, server = default_peer_args([], buf) - secret = arg_parts[2] - elif len(arg_parts) == 5: - nick, server = default_peer_args(arg_parts[2:4], buf) - secret = arg_parts[4] - else: - return weechat.WEECHAT_RC_ERROR - - if secret: - secret = PYVER.to_str(secret) - - context = get_server_context(server, nick) - context.smpGotSecret(secret) - - result = weechat.WEECHAT_RC_OK - - elif action == 'ask': - question = None - secret = None - - # Nickname and server are not specified - # Check whether it's a simple challenge or a question/answer request - if len(arg_parts) == 3: - nick, server = default_peer_args([], buf) - secret = arg_parts[2] - elif len(arg_parts) == 4: - nick, server = default_peer_args([], buf) - secret = arg_parts[3] - question = arg_parts[2] - - # Nickname and server are specified - # Check whether it's a simple challenge or a question/answer request - elif len(arg_parts) == 5: - nick, server = default_peer_args(arg_parts[2:4], buf) - secret = arg_parts[4] - elif len(arg_parts) == 6: - nick, server = default_peer_args(arg_parts[2:4], buf) - secret = arg_parts[5] - question = arg_parts[4] - else: - return weechat.WEECHAT_RC_ERROR - - context = get_server_context(server, nick) - - if secret: - secret = PYVER.to_str(secret) - if question: - question = PYVER.to_str(question) - - try: - context.smpInit(secret, question) - except potr.context.NotEncryptedError: - context.print_buffer( - 'There is currently no encrypted session with {0}.'.format( - context.peer), 'error') - else: - if question: - context.print_buffer('SMP challenge sent...') - else: - context.print_buffer('SMP question sent...') - context.in_smp = True - result = weechat.WEECHAT_RC_OK - - elif action == 'abort': - # Nickname and server are not specified - if len(arg_parts) == 2: - nick, server = default_peer_args([], buf) - # Nickname and server are specified - elif len(arg_parts) == 4: - nick, server = default_peer_args(arg_parts[2:4], buf) - else: - return weechat.WEECHAT_RC_ERROR - - context = get_server_context(server, nick) - - if context.in_smp: - try: - context.smpAbort() - except potr.context.NotEncryptedError: - context.print_buffer( - 'There is currently no encrypted session with {0}.' - .format(context.peer), 'error') - else: - debug('SMP aborted') - context.smp_finish('SMP aborted.') - result = weechat.WEECHAT_RC_OK - - elif len(arg_parts) in (1, 3) and arg_parts[0] == 'trust': - nick, server = default_peer_args(arg_parts[1:3], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - - if context.crypto.theirPubkey is not None: - context.setCurrentTrust('verified') - context.print_buffer('{peer} is now authenticated.'.format( - peer=context.peer)) - - weechat.bar_item_update(SCRIPT_NAME) - else: - context.print_buffer( - 'No fingerprint for {peer}. Start an OTR conversation ' - 'first: /otr start'.format(peer=context.peer), - 'error') - - result = weechat.WEECHAT_RC_OK - elif len(arg_parts) in (1, 3) and arg_parts[0] == 'distrust': - nick, server = default_peer_args(arg_parts[1:3], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - - if context.crypto.theirPubkey is not None: - context.setCurrentTrust('') - context.print_buffer( - '{peer} is now de-authenticated.'.format( - peer=context.peer)) - - weechat.bar_item_update(SCRIPT_NAME) - else: - context.print_buffer( - 'No fingerprint for {peer}. Start an OTR conversation ' - 'first: /otr start'.format(peer=context.peer), 'error') - - result = weechat.WEECHAT_RC_OK - - elif len(arg_parts) in (1, 2) and arg_parts[0] == 'log': - nick, server = default_peer_args([], buf) - if len(arg_parts) == 1: - if nick is not None and server is not None: - context = get_server_context(server, nick) - - if context.is_encrypted(): - if context.is_logged(): - context.print_buffer( - 'This conversation is currently being logged.', - 'warning') - result = weechat.WEECHAT_RC_OK - - else: - context.print_buffer( - 'This conversation is currently NOT being logged.') - result = weechat.WEECHAT_RC_OK - else: - context.print_buffer( - 'OTR LOG: Not in an OTR session', 'error') - result = weechat.WEECHAT_RC_OK - - else: - print_buffer('', 'OTR LOG: Not in an OTR session', 'error') - result = weechat.WEECHAT_RC_OK - - if len(arg_parts) == 2: - if nick is not None and server is not None: - context = get_server_context(server, nick) - - if arg_parts[1] == 'start' and \ - not context.is_logged() and \ - context.is_encrypted(): - if context.previous_log_level is None: - context.previous_log_level = context.get_log_level() - context.print_buffer( - 'From this point on, this conversation will be ' - 'logged. Please keep in mind that by doing so you ' - 'are potentially putting yourself and your ' - 'interlocutor at risk. You can disable this by doing ' - '/otr log stop', - 'warning') - weechat.command(buf, '/mute logger set 9') - result = weechat.WEECHAT_RC_OK - - elif arg_parts[1] == 'stop' and \ - context.is_logged() and \ - context.is_encrypted(): - if context.previous_log_level is None: - context.previous_log_level = context.get_log_level() - weechat.command(buf, '/mute logger set 0') - context.print_buffer( - 'From this point on, this conversation will NOT be ' - 'logged ANYMORE.') - result = weechat.WEECHAT_RC_OK - - elif not context.is_encrypted(): - context.print_buffer( - 'OTR LOG: Not in an OTR session', 'error') - result = weechat.WEECHAT_RC_OK - - else: - # Don't need to do anything. - result = weechat.WEECHAT_RC_OK - - else: - print_buffer('', 'OTR LOG: Not in an OTR session', 'error') - - elif len(arg_parts) in (1, 2, 3, 4) and arg_parts[0] == 'policy': - if len(arg_parts) == 1: - nick, server = default_peer_args([], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - - context.print_buffer(context.format_policies()) - else: - prnt('', format_default_policies()) - - result = weechat.WEECHAT_RC_OK - - elif len(arg_parts) == 2 and arg_parts[1].lower() == 'default': - nick, server = default_peer_args([], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - - context.print_buffer(format_default_policies()) - else: - prnt('', format_default_policies()) - - result = weechat.WEECHAT_RC_OK - - elif len(arg_parts) == 3 and arg_parts[1].lower() in POLICIES: - nick, server = default_peer_args([], buf) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - - policy_var = context.policy_config_option(arg_parts[1].lower()) - - command('', '/set {policy} {value}'.format( - policy=policy_var, - value=arg_parts[2])) - - context.print_buffer(context.format_policies()) - - result = weechat.WEECHAT_RC_OK - - elif len(arg_parts) == 4 and \ - arg_parts[1].lower() == 'default' and \ - arg_parts[2].lower() in POLICIES: - nick, server = default_peer_args([], buf) - - policy_var = "otr.policy.default." + arg_parts[2].lower() - - command('', '/set {policy} {value}'.format( - policy=policy_var, - value=arg_parts[3])) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - - context.print_buffer(format_default_policies()) - else: - prnt('', format_default_policies()) - - result = weechat.WEECHAT_RC_OK - elif len(arg_parts) in (1, 2) and arg_parts[0] == 'fingerprint': - if len(arg_parts) == 1: - show_account_fingerprints() - result = weechat.WEECHAT_RC_OK - elif len(arg_parts) == 2: - if arg_parts[1] == 'all': - show_peer_fingerprints() - else: - show_peer_fingerprints(grep=arg_parts[1]) - result = weechat.WEECHAT_RC_OK - - return result - -def otr_statusbar_cb(data, item, window): - """Update the statusbar.""" - if window: - buf = weechat.window_get_pointer(window, 'buffer') - else: - # If the bar item is in a root bar that is not in a window, window - # will be empty. - buf = weechat.current_buffer() - - if not buffer_is_private(buf): - return '' - - local_user = irc_user( - buffer_get_string(buf, 'localvar_nick'), - buffer_get_string(buf, 'localvar_server')) - - remote_user = irc_user( - buffer_get_string(buf, 'localvar_channel'), - buffer_get_string(buf, 'localvar_server')) - - context = get_context(local_user, remote_user) - - encrypted_str = config_string('look.bar.state.encrypted') - unencrypted_str = config_string('look.bar.state.unencrypted') - authenticated_str = config_string('look.bar.state.authenticated') - unauthenticated_str = config_string('look.bar.state.unauthenticated') - logged_str = config_string('look.bar.state.logged') - notlogged_str = config_string('look.bar.state.notlogged') - - bar_parts = [] - - if context.is_encrypted(): - if encrypted_str: - bar_parts.append(''.join([ - config_color('status.encrypted'), - encrypted_str, - config_color('status.default')])) - - if context.is_verified(): - if authenticated_str: - bar_parts.append(''.join([ - config_color('status.authenticated'), - authenticated_str, - config_color('status.default')])) - elif unauthenticated_str: - bar_parts.append(''.join([ - config_color('status.unauthenticated'), - unauthenticated_str, - config_color('status.default')])) - - if context.is_logged(): - if logged_str: - bar_parts.append(''.join([ - config_color('status.logged'), - logged_str, - config_color('status.default')])) - elif notlogged_str: - bar_parts.append(''.join([ - config_color('status.notlogged'), - notlogged_str, - config_color('status.default')])) - - elif unencrypted_str: - bar_parts.append(''.join([ - config_color('status.unencrypted'), - unencrypted_str, - config_color('status.default')])) - - result = config_string('look.bar.state.separator').join(bar_parts) - - if result: - result = '{color}{prefix}{result}'.format( - color=config_color('status.default'), - prefix=config_string('look.bar.prefix'), - result=result) - - if context.is_encrypted(): - weechat.buffer_set(buf, 'localvar_set_otr_encrypted', 'true') - else: - weechat.buffer_set(buf, 'localvar_set_otr_encrypted', 'false') - - if context.is_verified(): - weechat.buffer_set(buf, 'localvar_set_otr_authenticated', 'true') - else: - weechat.buffer_set(buf, 'localvar_set_otr_authenticated', 'false') - - if context.is_logged(): - weechat.buffer_set(buf, 'localvar_set_otr_logged', 'true') - else: - weechat.buffer_set(buf, 'localvar_set_otr_logged', 'false') - - return result - -def bar_config_update_cb(data, option): - """Callback for updating the status bar when its config changes.""" - weechat.bar_item_update(SCRIPT_NAME) - - return weechat.WEECHAT_RC_OK - -def policy_completion_cb(data, completion_item, buf, completion): - """Callback for policy tab completion.""" - for policy in POLICIES: - weechat.hook_completion_list_add( - completion, policy, 0, weechat.WEECHAT_LIST_POS_SORT) - - return weechat.WEECHAT_RC_OK - -def policy_create_option_cb(data, config_file, section, name, value): - """Callback for creating a new policy option when the user sets one - that doesn't exist.""" - weechat.config_new_option( - config_file, section, name, 'boolean', '', '', 0, 0, value, value, 0, - '', '', '', '', '', '') - - return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED - -def logger_level_update_cb(data, option, value): - """Callback called when any logger level changes.""" - weechat.bar_item_update(SCRIPT_NAME) - - return weechat.WEECHAT_RC_OK - -def buffer_switch_cb(data, signal, signal_data): - """Callback for buffer switched. - - Used for updating the status bar item when it is in a root bar. - """ - weechat.bar_item_update(SCRIPT_NAME) - - return weechat.WEECHAT_RC_OK - -def buffer_closing_cb(data, signal, signal_data): - """Callback for buffer closed. - - It closes the OTR session when the buffer is about to be closed. - """ - result = weechat.WEECHAT_RC_ERROR - nick, server = default_peer_args([], signal_data) - - if nick is not None and server is not None: - context = get_server_context(server, nick) - context.disconnect() - - result = weechat.WEECHAT_RC_OK - return result - -def init_config(): - """Set up configuration options and load config file.""" - global CONFIG_FILE - CONFIG_FILE = weechat.config_new(SCRIPT_NAME, '', '') - - global CONFIG_SECTIONS - CONFIG_SECTIONS = {} - - CONFIG_SECTIONS['general'] = weechat.config_new_section( - CONFIG_FILE, 'general', 0, 0, '', '', '', '', '', '', '', '', '', '') - - for option, typ, desc, default in [ - ('debug', - 'boolean', - 'OTR script debugging', - 'off'), - ('hints', - 'boolean', - 'Give helpful hints how to use this script and how to stay ' - 'secure while using OTR (recommended)', - 'on'), - ('defaultkey', - 'string', - 'default private key to use for new accounts (nick@server)', - ''), - ('no_send_tag_regex', - 'string', - 'do not OTR whitespace tag messages to nicks matching this regex ' - '(case insensitive)', - '^(alis|chanfix|global|.+serv|\*.+)$'), - ]: - weechat.config_new_option( - CONFIG_FILE, CONFIG_SECTIONS['general'], option, typ, desc, '', 0, - 0, default, default, 0, '', '', '', '', '', '') - - CONFIG_SECTIONS['color'] = weechat.config_new_section( - CONFIG_FILE, 'color', 0, 0, '', '', '', '', '', '', '', '', '', '') - - for option, desc, default, update_cb in [ - ('status.default', - 'status bar default color', - 'default', - 'bar_config_update_cb'), - ('status.encrypted', - 'status bar encrypted indicator color', - 'green', - 'bar_config_update_cb'), - ('status.unencrypted', - 'status bar unencrypted indicator color', - 'lightred', - 'bar_config_update_cb'), - ('status.authenticated', - 'status bar authenticated indicator color', - 'green', - 'bar_config_update_cb'), - ('status.unauthenticated', - 'status bar unauthenticated indicator color', - 'lightred', - 'bar_config_update_cb'), - ('status.logged', - 'status bar logged indicator color', - 'lightred', - 'bar_config_update_cb'), - ('status.notlogged', - 'status bar not logged indicator color', - 'green', - 'bar_config_update_cb'), - ('buffer.hint', - 'text color for hints', - 'lightblue', - ''), - ('buffer.info', - 'text color for informational messages', - 'default', - ''), - ('buffer.success', - 'text color for success messages', - 'lightgreen', - ''), - ('buffer.warning', - 'text color for warnings', - 'yellow', - ''), - ('buffer.error', - 'text color for errors', - 'lightred', - ''), - ]: - weechat.config_new_option( - CONFIG_FILE, CONFIG_SECTIONS['color'], option, 'color', desc, '', 0, - 0, default, default, 0, '', '', update_cb, '', '', '') - - CONFIG_SECTIONS['look'] = weechat.config_new_section( - CONFIG_FILE, 'look', 0, 0, '', '', '', '', '', '', '', '', '', '') - - for option, desc, default, update_cb in [ - ('bar.prefix', - 'prefix for OTR status bar item', - 'OTR:', - 'bar_config_update_cb'), - ('bar.state.encrypted', - 'shown in status bar when conversation is encrypted', - 'SEC', - 'bar_config_update_cb'), - ('bar.state.unencrypted', - 'shown in status bar when conversation is not encrypted', - '!SEC', - 'bar_config_update_cb'), - ('bar.state.authenticated', - 'shown in status bar when peer is authenticated', - 'AUTH', - 'bar_config_update_cb'), - ('bar.state.unauthenticated', - 'shown in status bar when peer is not authenticated', - '!AUTH', - 'bar_config_update_cb'), - ('bar.state.logged', - 'shown in status bar when peer conversation is being logged to ' - 'disk', - 'LOG', - 'bar_config_update_cb'), - ('bar.state.notlogged', - 'shown in status bar when peer conversation is not being logged ' - 'to disk', - '!LOG', - 'bar_config_update_cb'), - ('bar.state.separator', - 'separator for states in the status bar', - ',', - 'bar_config_update_cb'), - ('prefix', - 'prefix used for messages from otr (note: content is evaluated, ' - 'see /help eval)', - '${color:default}:! ${color:brown}otr${color:default} !:', - ''), - ]: - weechat.config_new_option( - CONFIG_FILE, CONFIG_SECTIONS['look'], option, 'string', desc, '', - 0, 0, default, default, 0, '', '', update_cb, '', '', '') - - CONFIG_SECTIONS['policy'] = weechat.config_new_section( - CONFIG_FILE, 'policy', 1, 1, '', '', '', '', '', '', - 'policy_create_option_cb', '', '', '') - - for option, desc, default in [ - ('default.allow_v2', - 'default allow OTR v2 policy', - 'on'), - ('default.require_encryption', - 'default require encryption policy', - 'off'), - ('default.log', - 'default enable logging to disk', - 'off'), - ('default.send_tag', - 'default send tag policy', - 'off'), - ('default.html_escape', - 'default HTML escape policy', - 'off'), - ('default.html_filter', - 'default HTML filter policy', - 'on'), - ]: - weechat.config_new_option( - CONFIG_FILE, CONFIG_SECTIONS['policy'], option, 'boolean', desc, '', - 0, 0, default, default, 0, '', '', '', '', '', '') - - weechat.config_read(CONFIG_FILE) - -def free_all_config(): - """Free all config options, sections and config file.""" - for section in CONFIG_SECTIONS.values(): - weechat.config_section_free_options(section) - weechat.config_section_free(section) - - weechat.config_free(CONFIG_FILE) - -def create_dir(): - """Create the OTR subdirectory in the WeeChat config directory if it does - not exist.""" - if not os.path.exists(OTR_DIR): - weechat.mkdir_home(OTR_DIR_NAME, 0o700) - -def git_info(): - """If this script is part of a git repository return the repo state.""" - result = None - script_dir = os.path.dirname(os.path.realpath(__file__)) - git_dir = os.path.join(script_dir, '.git') - if os.path.isdir(git_dir): - import subprocess - try: - # We can't use check_output here without breaking compatibility - # for Python 2.6, but we ignore the return value anyway, so Popen - # is only slightly more complicated: - process = subprocess.Popen([ - 'git', - '--git-dir', git_dir, - '--work-tree', script_dir, - 'describe', '--dirty', '--always', - ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output = process.communicate()[0] - if output: - result = PYVER.to_unicode(output).lstrip('v').rstrip() - except OSError: - pass - - return result - -def weechat_version_ok(): - """Check if the WeeChat version is compatible with this script. - - If WeeChat version < 0.4.2 log an error to the core buffer and return - False. Otherwise return True. - """ - weechat_version = weechat.info_get('version_number', '') or 0 - if int(weechat_version) < 0x00040200: - error_message = ( - '{script_name} requires WeeChat version >= 0.4.2. The current ' - 'version is {current_version}.').format( - script_name=SCRIPT_NAME, - current_version=weechat.info_get('version', '')) - prnt('', error_message) - return False - return True - -SCRIPT_VERSION = git_info() or SCRIPT_VERSION - -def dependency_versions(): - """Return a string containing the versions of all dependencies.""" - return ('weechat-otr {script_version}, ' - 'potr {potr_major}.{potr_minor}.{potr_patch}-{potr_sub}, ' - 'Python {python_version}, ' - 'WeeChat {weechat_version}' - ).format( - script_version=SCRIPT_VERSION, - potr_major=potr.VERSION[0], - potr_minor=potr.VERSION[1], - potr_patch=potr.VERSION[2], - potr_sub=potr.VERSION[3], - python_version=platform.python_version(), - weechat_version=weechat.info_get('version', '')) - -def excepthook(typ, value, tracebak): - """Add dependency versions to tracebacks.""" - sys.stderr.write('Versions: ') - sys.stderr.write(dependency_versions()) - sys.stderr.write('\n') - - sys.__excepthook__(typ, value, tracebak) - -sys.excepthook = excepthook - -if weechat.register( - SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENCE, SCRIPT_DESC, - 'shutdown', ''): - if weechat_version_ok(): - init_config() - - OTR_DIR = os.path.join(info_get('weechat_dir', ''), OTR_DIR_NAME) - create_dir() - - ACCOUNTS = AccountDict() - - weechat.hook_modifier('irc_in_privmsg', 'message_in_cb', '') - weechat.hook_modifier('irc_out_privmsg', 'message_out_cb', '') - - weechat.hook_command( - SCRIPT_NAME, SCRIPT_HELP, - 'start [NICK SERVER] || ' - 'refresh [NICK SERVER] || ' - 'finish [NICK SERVER] || ' - 'end [NICK SERVER] || ' - 'status [NICK SERVER] || ' - 'smp ask [NICK SERVER] [QUESTION] SECRET || ' - 'smp respond [NICK SERVER] SECRET || ' - 'smp abort [NICK SERVER] || ' - 'trust [NICK SERVER] || ' - 'distrust [NICK SERVER] || ' - 'log [start|stop] || ' - 'policy [POLICY on|off] || ' - 'fingerprint [SEARCH|all]', - '', - 'start %(nick) %(irc_servers) %-||' - 'refresh %(nick) %(irc_servers) %-||' - 'finish %(nick) %(irc_servers) %-||' - 'end %(nick) %(irc_servers) %-||' - 'status %(nick) %(irc_servers) %-||' - 'smp ask|respond %(nick) %(irc_servers) %-||' - 'smp abort %(nick) %(irc_servers) %-||' - 'trust %(nick) %(irc_servers) %-||' - 'distrust %(nick) %(irc_servers) %-||' - 'log start|stop %-||' - 'policy %(otr_policy) on|off %-||' - 'fingerprint all %-||', - 'command_cb', - '') - - weechat.hook_completion( - 'otr_policy', 'OTR policies', 'policy_completion_cb', '') - - weechat.hook_config('logger.level.irc.*', 'logger_level_update_cb', '') - - weechat.hook_signal('buffer_switch', 'buffer_switch_cb', '') - weechat.hook_signal('buffer_closing', 'buffer_closing_cb', '') - - OTR_STATUSBAR = weechat.bar_item_new( - SCRIPT_NAME, 'otr_statusbar_cb', '') - weechat.bar_item_update(SCRIPT_NAME) -- cgit 1.4.1