From 00f25b7ddc59daca7af533c135d6ecce443ec801 Mon Sep 17 00:00:00 2001 From: emdee Date: Wed, 23 Nov 2022 19:26:46 +0000 Subject: [PATCH] rolled back and fixed startls --- jabber.py | 1610 ++++++++++++++++++--------------------------- support_jabber.py | 133 ++++ 2 files changed, 763 insertions(+), 980 deletions(-) create mode 100644 support_jabber.py diff --git a/jabber.py b/jabber.py index 838778f..36bb9c4 100644 --- a/jabber.py +++ b/jabber.py @@ -16,253 +16,244 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -""" -Jabber/XMPP protocol for WeeChat. -(this script requires WeeChat 0.3.0 (or newer) and xmpppy library) - -This will use the value of the environment variable https_proxy -(lowercase with the s) to set an HTTP proxy. - -For help, see /help jabber - -/jabber list - add - | [[:]] - connect|disconnect|del [] - alias [add|del ] - away [] - buddies - priority [] - status [] - presence | [online|chat|away|xa|dnd] - -""" -# History: # -SCRIPT_NAME = "jabber" -SCRIPT_AUTHOR = "Sebastien Helleu " -SCRIPT_VERSION = "2.0" -SCRIPT_LICENSE = "GPL3" -SCRIPT_DESC = "Jabber/XMPP protocol for WeeChat" -SCRIPT_COMMAND = SCRIPT_NAME +# +# Jabber/XMPP protocol for WeeChat. +# (this script requires WeeChat 0.3.0 (or newer) and xmpppy library) +# +# For help, see /help jabber +# Happy chat, enjoy :) +# +# History: +# 2013-09-30, Nils Görs : +# version 1.6: add support of /secure for passwords and jid +# : fix stdout/stderr when no JID was set +# 2013-05-14, Billiam : +# version 1.5: fix unicode encoding error in /jabber buddies +# 2013-05-03, Sebastien Helleu : +# version 1.4: add tags in user messages: notify_xxx, no_highlight, +# nick_xxx, prefix_nick_xxx, log1 +# 2012-05-12, Sebastian Rydberg : +# version 1.3: Added support for fetching names from roster +# 2012-04-11, Sebastien Helleu : +# version 1.2: fix deletion of server options +# 2012-03-09, Sebastien Helleu : +# version 1.1: fix reload of config file +# 2012-01-03, Sebastien Helleu : +# version 1.0: changes for future compatibility with Python 3.x +# 2011-12-15, Sebastien Helleu : +# version 0.9: fix utf-8 encoding problem on jid +# 2011-03-21, Isaac Raway : +# version 0.8: search chat buffer before opening it +# 2011-02-13, Sebastien Helleu : +# version 0.7: use new help format for command arguments +# 2010-11-23, xt +# version 0.6: change format of sent ping, to match RFC +# 2010-10-05, xt, +# version 0.5: no highlight for status/presence messages +# 2010-10-01, xt, +# version 0.4: +# add kick and invite +# 2010-08-03, Aleksey V. Zapparov : +# version 0.3: +# add /jabber priority [priority] +# add /jabber status [message] +# add /jabber presence [online|chat|away|xa|dnd] +# 2010-08-02, Aleksey V. Zapparov : +# version 0.2.1: +# fix prexence is set for current resource instead of sending +# special presences for all buddies +# 2010-08-02, Aleksey V. Zapparov : +# version 0.2: +# add priority and away_priority of resource +# 2010-08-02, Sebastien Helleu : +# version 0.1: first official version +# 2010-08-01, ixti : +# fix bug with non-ascii resources +# 2010-06-09, iiijjjiii : +# add connect server and port options (required for google talk) +# add private option permitting messages to be displayed in separate +# chat buffers or in a single server buffer +# add jid aliases +# add keepalive ping +# 2010-03-17, xt : +# add autoreconnect option, autoreconnects on protocol error +# 2010-03-17, xt : +# add autoconnect option, add new command /jmsg with -server option +# 2009-02-22, Sebastien Helleu : +# first version (unofficial) +# -import os import re -import traceback import warnings +import_ok = True + try: - import weechat as w - weechat = w - import_ok = True + import weechat + w = weechat except: print("This script must be run under WeeChat.") print("Get WeeChat now at: http://www.weechat.org/") import_ok = False +from support_jabber import aget_proxy + # On import, xmpp may produce warnings about using hashlib instead of # deprecated sha and md5. Since the code producing those warnings is # outside this script, catch them and ignore. original_filters = warnings.filters[:] -warnings.filterwarnings("ignore", category=DeprecationWarning) +warnings.filterwarnings("ignore",category=DeprecationWarning) try: import xmpp -except: - print("Package python-xmpp (xmpppy) must be installed to use Xmpp protocol.") - print("Get xmpppy with your package manager, or at this URL: http://xmpppy.sourceforge.net/") + from xmpp import dispatcher + from xmpp import transports + dispatcher.DefaultTimeout = 60 +except ImportError as e: + print(f"Package python-xmpp (xmpppy) must be installed to use Jabber protocol. {e}") import_ok = False finally: warnings.filters = original_filters -def LOG_debug(what, level): - if jabber_debug_enabled(): - w_prnt('', what) - -def w_prnt(where, what='', *args): - if args: - w.prnt('', w.prefix('!') + str(repr(args))) - elif not what: - w.prnt('', w.prefix('%') + str(where)) - elif type(what) != str or type(where) != str: - w.prnt('', w.prefix('$') + str(where) + str(what)) - else: - try: - w.prnt(where, what) - except Exception as e: - w.prnt('', 'w_prnt: ' + str(e)) +SCRIPT_NAME = "jabber" +SCRIPT_AUTHOR = "Sebastien Helleu " +SCRIPT_VERSION = "1.7.01" +SCRIPT_LICENSE = "GPL3" +SCRIPT_DESC = "Jabber/XMPP protocol for WeeChat" +SCRIPT_COMMAND = SCRIPT_NAME + +def LOG_debug(what, level='none'): + # if jabber_debug_enabled(): + weechat.prnt('', what) # ==============================[ global vars ]=============================== jabber_servers = [] jabber_server_options = { "jid" : { "type" : "string", - "desc": "xmpp id (user@server.tld)", - "min": 0, - "max": 0, + "desc" : "jabber id (user@server.tld)", + "min" : 0, + "max" : 0, "string_values": "", - "default": "", - "value": "", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "", + "value" : "", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "priority": { "type": "integer", - "desc": "Default resource priority", - "min": 0, - "max": 65535, + "priority" : { "type" : "integer", + "desc" : "Default resource priority", + "min" : 0, + "max" : 65535, "string_values": "", - "default": "8", - "value": "8", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "8", + "value" : "8", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "away_priority": { "type": "integer", - "desc": "Resource priority on away", - "min": 0, - "max": 65535, + "away_priority": { "type" : "integer", + "desc" : "Resource priority on away", + "min" : 0, + "max" : 65535, "string_values": "", - "default": "0", - "value": "0", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "0", + "value" : "0", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "password": { "type": "string", - "desc": "password for xmpp id on server", - "min": 0, - "max": 0, + "password" : { "type" : "string", + "desc" : "password for jabber id on server (evaluated)", + "min" : 0, + "max" : 0, "string_values": "", - "default": "", - "value": "", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "", + "value" : "", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "server": { "type": "string", - "desc": "connect server host or ip, eg. talk.google.com", - "min": 0, - "max": 0, + "server" : { "type" : "string", + "desc" : "connect server host or ip, eg. talk.google.com", + "min" : 0, + "max" : 0, "string_values": "", - "default": "", - "value": "", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "", + "value" : "", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "ssl_ver": { "type": "string", - "desc": "Miniumum SSL version - empty for no SSL, else tlsv1.1|tlsv1.2|tlsv1.3", - "min": 0, - "max": 0, + "port" : { "type" : "integer", + "desc" : "connect server port, eg. 5223", + "min" : 0, + "max" : 65535, "string_values": "", - "default": "", - "value": "", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "5222", + "value" : "5222", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "port": { "type": "integer", - "desc": "connect server port, eg. 5222 for not SSL", - "min": 0, - "max": 65535, + "autoconnect" : { "type" : "boolean", + "desc" : "automatically connect to server when script is starting", + "min" : 0, + "max" : 0, "string_values": "", - "default": "5222", - "value": "5222", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "off", + "value" : "off", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "autoconnect": { "type": "boolean", - "desc": "automatically connect to server when script is starting", - "min": 0, - "max": 0, + "autoreconnect": { "type" : "boolean", + "desc" : "automatically reconnect to server when disconnected", + "min" : 0, + "max" : 0, "string_values": "", - "default": "off", - "value": "off", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "off", + "value" : "off", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "autoreconnect": { "type": "boolean", - "desc": "automatically reconnect to server when disconnected", - "min": 0, - "max": 0, + "private" : { "type" : "boolean", + "desc" : "display messages in separate chat buffers instead of a single server buffer", + "min" : 0, + "max" : 0, "string_values": "", - "default": "off", - "value": "off", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "on", + "value" : "on", + "check_cb" : "", + "change_cb" : "", + "delete_cb" : "", }, - "private": { "type": "boolean", - "desc": "display messages in separate chat buffers instead of a single server buffer", - "min": 0, - "max": 0, + "ping_interval": { "type" : "integer", + "desc" : "Number of seconds between server pings. 0 = disable", + "min" : 60, + "max" : 9999999, "string_values": "", - "default": "on", - "value": "on", - "check_cb": "", - "change_cb": "", - "delete_cb": "", + "default" : "0", + "value" : "0", + "check_cb" : "ping_interval_check_cb", + "change_cb" : "", + "delete_cb" : "", }, - "ping_interval": { "type": "integer", - "desc": "Number of seconds between server pings. 0 = disable", - "min": 0, - "max": 9999999, + "ping_timeout" : { "type" : "integer", + "desc" : "Number of seconds to allow ping to respond before timing out", + "min" : 0, + "max" : 9999999, "string_values": "", - "default": "0", - "value": "0", - "check_cb": "ping_interval_check_cb", - "change_cb": "", - "delete_cb": "", + "default" : "10", + "value" : "10", + "check_cb" : "ping_timeout_check_cb", + "change_cb" : "", + "delete_cb" : "", }, - "ping_timeout": { "type": "integer", - "desc": "Number of seconds to allow ping to respond before timing out", - "min": 0, - "max": 9999999, - "string_values": "", - "default": "10", - "value": "10", - "check_cb": "ping_timeout_check_cb", - "change_cb": "", - "delete_cb": "", - }, - "CAfile": { "type": "string", - "desc": "CAfile - bundle of CA certificates in PEM", - "min": 0, - "max": 0, - "string_values": "", - "default": "/etc/ssl/certs/ca-certificates.crt", - "value": "", - "check_cb": "", - "change_cb": "", - "delete_cb": "", - }, - "cert_file": { "type": "string", - "desc": "client certificate file for authentication, in PEM", - "min": 0, - "max": 0, - "string_values": "", - "default": "", - "value": "", - "check_cb": "", - "change_cb": "", - "delete_cb": "", - }, - "key_file": { "type": "string", - "desc": "client private key file for authentication, in PEM", - "min": 0, - "max": 0, - "string_values": "", - "default": "", - "value": "", - "check_cb": "", - "change_cb": "", - "delete_cb": "", - }, - "ciphers": { "type": "string", - "desc": "ciphers for authentication, colon sep", + "proxy": { "type": "string", + "desc": "name of the weechat.proxy.* = or ''", "min": 0, "max": 0, "string_values": "", @@ -278,154 +269,63 @@ jabber_config_section = {} jabber_config_option = {} jabber_jid_aliases = {} # { 'alias1': 'jid1', 'alias2': 'jid2', ... } -class WeechatWrapper(object): - def __init__(self, wrapped_class): - self.wrapped_class = wrapped_class - - # Helper method used to encode/decode method calls. - def wrap_for_utf8(self, method): - def hooked(*args, **kwargs): - result = method(*encode_to_utf8(args), **encode_to_utf8(kwargs)) - # Prevent wrapped_class from becoming unwrapped - if result == self.wrapped_class: - return self - return decode_from_utf8(result) - - return hooked - - # Encode and decode everything sent to/received from weechat. We use the - # unicode type internally in wee-slack, but has to send utf8 to weechat. - def __getattr__(self, attr): - orig_attr = self.wrapped_class.__getattribute__(attr) - if callable(orig_attr): - return self.wrap_for_utf8(orig_attr) - else: - return decode_from_utf8(orig_attr) - - # Ensure all lines sent to weechat specifies a prefix. For lines after the - # first, we want to disable the prefix, which we do by specifying the same - # number of spaces, so it aligns correctly. - def prnt_date_tags(self, buffer, date, tags, message): - prefix, _, _ = message.partition("\t") - prefix = w.string_remove_color(encode_to_utf8(prefix), "") - prefix_spaces = " " * w.strlen_screen(prefix) - message = message.replace("\n", "\n{}\t".format(prefix_spaces)) - return self.wrap_for_utf8(self.wrapped_class.prnt_date_tags)( - buffer, date, tags, message - ) - -class ProxyWrapper(object): - def __init__(self): - self.proxy_name = w.config_string(w.config_get("weechat.network.proxy_curl")) - self.proxy_string = "" - self.proxy_type = "" - self.proxy_address = "" - self.proxy_port = "" - self.proxy_user = "" - self.proxy_password = "" - self.has_proxy = False - - if self.proxy_name: - self.proxy_string = "weechat.proxy.{}".format(self.proxy_name) - self.proxy_type = w.config_string( - w.config_get("{}.type".format(self.proxy_string)) - ) - if self.proxy_type == "http": - self.proxy_address = w.config_string( - w.config_get("{}.address".format(self.proxy_string)) - ) - self.proxy_port = w.config_integer( - w.config_get("{}.port".format(self.proxy_string)) - ) - self.proxy_user = w.config_string( - w.config_get("{}.username".format(self.proxy_string)) - ) - self.proxy_password = w.config_string( - w.config_get("{}.password".format(self.proxy_string)) - ) - self.has_proxy = True - else: - w_prnt( - "", - "\nWarning: weechat.network.proxy_curl is set to {} type (name: {}, conf string: {}). Only HTTP proxy is supported.\n\n".format( - self.proxy_type, self.proxy_name, self.proxy_string - ), - ) - - def curl(self): - if not self.has_proxy: - return "" - - if self.proxy_user and self.proxy_password: - user = "{}:{}@".format(self.proxy_user, self.proxy_password) - else: - user = "" - - if self.proxy_port: - port = ":{}".format(self.proxy_port) - else: - port = "" - - return "-x{}{}{}".format(user, self.proxy_address, port) - - # =================================[ config ]================================= def jabber_config_init(): """ Initialize config file: create sections and options in memory. """ global jabber_config_file, jabber_config_section - jabber_config_file = w.config_new("jabber", "jabber_config_reload_cb", "") + jabber_config_file = weechat.config_new("jabber", "jabber_config_reload_cb", "") if not jabber_config_file: return # look - jabber_config_section["look"] = w.config_new_section( + jabber_config_section["look"] = weechat.config_new_section( jabber_config_file, "look", 0, 0, "", "", "", "", "", "", "", "", "", "") if not jabber_config_section["look"]: - w.config_free(jabber_config_file) + weechat.config_free(jabber_config_file) return - jabber_config_option["debug"] = w.config_new_option( + jabber_config_option["debug"] = weechat.config_new_option( jabber_config_file, jabber_config_section["look"], "debug", "boolean", "display debug messages", "", 0, 0, "off", "off", 0, "", "", "", "", "", "") # color - jabber_config_section["color"] = w.config_new_section( + jabber_config_section["color"] = weechat.config_new_section( jabber_config_file, "color", 0, 0, "", "", "", "", "", "", "", "", "", "") if not jabber_config_section["color"]: - w.config_free(jabber_config_file) + weechat.config_free(jabber_config_file) return - jabber_config_option["message_join"] = w.config_new_option( + jabber_config_option["message_join"] = weechat.config_new_option( jabber_config_file, jabber_config_section["color"], "message_join", "color", "color for text in join messages", "", 0, 0, "green", "green", 0, "", "", "", "", "", "") - jabber_config_option["message_quit"] = w.config_new_option( + jabber_config_option["message_quit"] = weechat.config_new_option( jabber_config_file, jabber_config_section["color"], "message_quit", "color", "color for text in quit messages", "", 0, 0, "red", "red", 0, "", "", "", "", "", "") # server - jabber_config_section["server"] = w.config_new_section( + jabber_config_section["server"] = weechat.config_new_section( jabber_config_file, "server", 0, 0, "jabber_config_server_read_cb", "", "jabber_config_server_write_cb", "", "", "", "", "", "", "") if not jabber_config_section["server"]: - w.config_free(jabber_config_file) + weechat.config_free(jabber_config_file) return - jabber_config_section["jid_aliases"] = w.config_new_section( + jabber_config_section["jid_aliases"] = weechat.config_new_section( jabber_config_file, "jid_aliases", 0, 0, "jabber_config_jid_aliases_read_cb", "", "jabber_config_jid_aliases_write_cb", "", "", "", "", "", "", "") if not jabber_config_section["jid_aliases"]: - w.config_free(jabber_config_file) + weechat.config_free(jabber_config_file) return def jabber_config_reload_cb(data, config_file): """ Reload config file. """ - return w.config_reload(config_file) + return weechat.config_reload(config_file) def jabber_config_server_read_cb(data, config_file, section, option_name, value): """ Read server option in config file. """ global jabber_servers - rc = w.WEECHAT_CONFIG_OPTION_SET_ERROR + rc = weechat.WEECHAT_CONFIG_OPTION_SET_ERROR items = option_name.split(".", 1) if len(items) == 2: server = jabber_search_server_by_name(items[0]) @@ -433,52 +333,52 @@ def jabber_config_server_read_cb(data, config_file, section, option_name, value) server = Server(items[0]) jabber_servers.append(server) if server: - rc = w.config_option_set(server.options[items[1]], value, 1) + rc = weechat.config_option_set(server.options[items[1]], value, 1) return rc def jabber_config_server_write_cb(data, config_file, section_name): """ Write server section in config file. """ global jabber_servers - w.config_write_line(config_file, section_name, "") + weechat.config_write_line(config_file, section_name, "") for server in jabber_servers: for name, option in sorted(server.options.items()): - w.config_write_option(config_file, option) - return w.WEECHAT_RC_OK + weechat.config_write_option(config_file, option) + return weechat.WEECHAT_RC_OK def jabber_config_jid_aliases_read_cb(data, config_file, section, option_name, value): """ Read jid_aliases option in config file. """ global jabber_jid_aliases jabber_jid_aliases[option_name] = value - option = w.config_new_option( + option = weechat.config_new_option( config_file, section, option_name, "string", "jid alias", "", 0, 0, "", value, 0, "", "", "", "", "", "") if not option: - return w.WEECHAT_CONFIG_OPTION_SET_ERROR - return w.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED + return weechat.WEECHAT_CONFIG_OPTION_SET_ERROR + return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED def jabber_config_jid_aliases_write_cb(data, config_file, section_name): """ Write jid_aliases section in config file. """ global jabber_jid_aliases - w.config_write_line(config_file, section_name, "") + weechat.config_write_line(config_file, section_name, "") for alias, jid in sorted(jabber_jid_aliases.items()): - w.config_write_line(config_file, alias, jid) - return w.WEECHAT_RC_OK + weechat.config_write_line(config_file, alias, jid) + return weechat.WEECHAT_RC_OK def jabber_config_read(): """ Read jabber config file (jabber.conf). """ global jabber_config_file - return w.config_read(jabber_config_file) + return weechat.config_read(jabber_config_file) def jabber_config_write(): """ Write jabber config file (jabber.conf). """ global jabber_config_file - return w.config_write(jabber_config_file) + return weechat.config_write(jabber_config_file) def jabber_debug_enabled(): """ Return True if debug is enabled. """ global jabber_config_options - if w.config_boolean(jabber_config_option["debug"]): + if weechat.config_boolean(jabber_config_option["debug"]): return True return False @@ -486,336 +386,38 @@ def jabber_config_color(color): """ Return color code for a jabber color option. """ global jabber_config_option if color in jabber_config_option: - return w.color(w.config_color(jabber_config_option[color])) + return weechat.color(weechat.config_color(jabber_config_option[color])) return "" def ping_timeout_check_cb(server_name, option, value): global jabber_config_file, jabber_config_section - ping_interval_option = w.config_search_option( + ping_interval_option = weechat.config_search_option( jabber_config_file, jabber_config_section["server"], "%s.ping_interval" % (server_name) ) - ping_interval = w.config_integer(ping_interval_option) + ping_interval = weechat.config_integer(ping_interval_option) if int(ping_interval) and int(value) >= int(ping_interval): - w_prnt("", "\njabber: unable to update 'ping_timeout' for server %s" % (server_name)) - w_prnt("", "jabber: to prevent multiple concurrent pings, ping_interval must be greater than ping_timeout") - return w.WEECHAT_CONFIG_OPTION_SET_ERROR - return w.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED + weechat.prnt("", "\njabber: unable to update 'ping_timeout' for server %s" % (server_name)) + weechat.prnt("", f"{SCRIPT_NAME}: to prevent multiple concurrent pings, ping_interval must be greater than ping_timeout") + return weechat.WEECHAT_CONFIG_OPTION_SET_ERROR + return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED def ping_interval_check_cb(server_name, option, value): global jabber_config_file, jabber_config_section - ping_timeout_option = w.config_search_option( + ping_timeout_option = weechat.config_search_option( jabber_config_file, jabber_config_section["server"], "%s.ping_timeout" % (server_name) ) - ping_timeout = w.config_integer(ping_timeout_option) + ping_timeout = weechat.config_integer(ping_timeout_option) if int(value) and int(ping_timeout) >= int(value): - w_prnt("", "\njabber: unable to update 'ping_interval' for server %s" % (server_name)) - w_prnt("", "jabber: to prevent multiple concurrent pings, ping_interval must be greater than ping_timeout") - return w.WEECHAT_CONFIG_OPTION_SET_ERROR - return w.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED + weechat.prnt("", "\njabber: unable to update 'ping_interval' for server %s" % (server_name)) + weechat.prnt("", f"{SCRIPT_NAME}: to prevent multiple concurrent pings, ping_interval must be greater than ping_timeout") + return weechat.WEECHAT_CONFIG_OPTION_SET_ERROR + return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED -import ssl -from xmpp.client import PlugIn -from xmpp.protocol import NodeProcessed -class HTTPPROXYsocket(xmpp.transports.TCPsocket): - """ HTTP (CONNECT) proxy connection class. Uses TCPsocket as the base class - redefines only connect method. Allows to use HTTP proxies like squid with - (optionally) simple authentication (using login and password). """ - def __init__(self, proxy, server, use_srv=True, buffer=None): - """ Caches proxy and target addresses. - 'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address) - and optional keys 'user' and 'password' to use for authentication. - 'server' argument is a tuple of host and port - just like TCPsocket uses. """ - xmpp.transports.TCPsocket.__init__(self, server, use_srv) - self.DBG_LINE = xmpp.transports.DBG_CONNECT_PROXY - self._proxy = proxy - self.buffer = buffer - - def connect(self, server=None): - """ Starts connection. Connects to proxy, supplies login and password to it - (if were specified while creating instance). Instructs proxy to make - connection to the target server. Returns non-empty sting on success. """ - if not xmpp.transports.TCPsocket.connect(self, - (self._proxy['host'], - self._proxy['port'])): - LOG_debug(f"Proxy not connect {(self._proxy['host'], self._proxy['port'])}",'start') - if self.buffer: - w_prnt(self.buffer, - "%sjabber: could not TCPsocket.connect" - % w.prefix("error")) - return - LOG_debug("Proxy server contacted, performing authentification",'start') - if not server: - server=self._server - connector = ['CONNECT %s:%s HTTP/1.0'%server, - 'Proxy-Connection: Keep-Alive', - 'Pragma: no-cache', - 'Host: %s:%s'%server, - 'User-Agent: HTTPPROXYsocket/v0.1'] - if 'user' in self._proxy and 'password' in self._proxy: - credentials = '%s:%s'%(self._proxy['user'],self._proxy['password']) - credentials = base64.encodestring(credentials).strip() - connector.append('Proxy-Authorization: Basic '+credentials) - connector.append('\r\n') - LOG_debug('Proxy sending connector','start') - # bytes? - self.send('\r\n'.join(connector)) - try: - reply = self.receive().replace(b'\r','') - reply = str(reply, 'UTF-8') - except IOError: - LOG_debug('Proxy suddenly disconnected','error') - self._owner.disconnected() - return - try: - proto, code, desc = reply.split('\n')[0].split(' ',2) - except Exception as e: - raise error(f'Invalid proxy reply {e}') - if code!='200': - LOG_debug('Invalid proxy reply: %s %s %s'%(proto,code,desc),'error') - self._owner.disconnected() - return - while reply.find('\n\n') == -1: - try: - reply_more = self.receive().replace(b'\r','') - reply += str(reply_more, 'UTF-8') - except IOError: - LOG_debug('Proxy suddenly disconnected','error') - self._owner.disconnected() - return - LOG_debug("Authentification successfull. XMPP server contacted.",'ok') - return 'ok' - -class TLS(xmpp.transports.PlugIn): - """ TLS connection used to encrypts already estabilished tcp connection.""" - - def PlugIn(self, owner, now=True, assl_dict=None): - """ If the 'now' argument is true then starts using encryption immidiatedly. - If 'now' in false then starts encryption as soon as TLS feature is - declared by the server (if it were already declared - it is ok). - """ - if 'TLS' in owner.__dict__: - return # Already enabled. - xmpp.client.PlugIn.PlugIn(self, owner) - DBG_LINE = 'TLS' - if now: - if not assl_dict: - assl_dict=dict(keyfile=None, - certfile=None, - cert_reqs=ssl.CERT_NONE, - ssl_version=ssl.PROTOCOL_TLS, - ca_certs=None, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - ciphers=None) - return self._startSSL(**assl_dict) - if self._owner.Dispatcher.Stream.features: - try: - self.FeaturesHandler(self._owner.Dispatcher, - self._owner.Dispatcher.Stream.features) - except NodeProcessed: - pass - else: - self._owner.RegisterHandlerOnce('features', - self.FeaturesHandler, - xmlns=xmpp.protocol.NS_STREAMS) - self.starttls = None - - def _startSSL(self, - keyfile=None, - certfile=None, - cert_reqs=ssl.CERT_NONE, - ssl_version=ssl.PROTOCOL_TLS, - ca_certs=None, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - ciphers=None): - """ Immidiatedly switch socket to TLS mode. Used internally. - Here we should switch pending_data to hint mode.""" - if not ca_certs: - ca_certs = w.config_string(self.options['CAfile']) - tcpsock=self._owner.Connection - tcpsock._sslObj = ssl.wrap_socket(tcpsock._sock, - keyfile=None, - certfile=certfile, - cert_reqs=ssl.CERT_NONE, - ssl_version=ssl.PROTOCOL_TLS, - ca_certs=ca_certs, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - ciphers=None) - tcpsock._sslIssuer = tcpsock._sslObj.getpeercert().get('issuer') - tcpsock._sslServer = tcpsock._sslObj.getpeercert().get('server') - tcpsock._recv = tcpsock._sslObj.read - tcpsock._send = tcpsock._sslObj.write - - tcpsock._seen_data = 1 - self._tcpsock=tcpsock - tcpsock.pending_data=self.pending_data - tcpsock._sslObj.setblocking(False) - - self.starttls='success' - -from xmpp import transports, dispatcher -class CommonClient(xmpp.client.CommonClient): - - def __init__(self, server, - port=5222, - debug=[], # 'always', 'nodebuilder' - buffer=None, - assl_dict=None): - xmpp.client.CommonClient.__init__(self, server, port, debug=debug) - self.buffer = buffer - self.assl_dict = assl_dict - - def connect(self,server_tuple=None, proxy=None, ssl=None,use_srv=False,transport=None): - """ Make a tcp/ip connection, protect it with tls/ssl if possible and start XMPP stream. - Returns None or 'tcp' or 'tls', depending on the result.""" - if not server_tuple: - server_tuple=(self.Server, self.Port) - self.assl_dict = None - - # bulletproofing - assert type(server_tuple) == tuple, server_tuple - assert len(server_tuple) >= 2, server_tuple - assert type(server_tuple[1]) in [int, str], server_tuple - assert type(server_tuple[0]) in [bytes, str], server_tuple - assert server_tuple[0] and server_tuple[1], server_tuple - - if transport: - pass - elif proxy: - transport = HTTPPROXYsocket(proxy, server_tuple, use_srv, ) - else: - transport = xmpp.transports.TCPsocket(server_tuple, use_srv) - if self.buffer: - w_prnt(self.buffer, - f"{w.prefix('network')}jabber: proxy: transport={transport}") - connected = transport.PlugIn(self) - if not connected: - serr = f"Failed to transport.PlugIn(self)" - LOG_debug(serr,'error') - transport.PlugOut() - return serr - self.connected = 'tcp' - - self._server_tuple = server_tuple - self._aProxy = proxy - if (ssl is None and self.Connection.getPort() in (5223, 443)): - ssl = True - if ssl: - try: # FIXME. This should be done in transports.py - TLS().PlugIn(self, now=True, assl_dict=self.assl_dict) - self.connected = 'ssl' - except socket.sslerror as e: - serr = f"Failed to transports.TLS().PlugIn(self, now=1)" - LOG_debug(serr,'error') - return serr - - dispatcher.Dispatcher().PlugIn(self) - while self.Dispatcher.Stream._document_attrs is None: - if not self.Process(1): - serr = f"Failed to dispatcher.Dispatcher().PlugIn(self)" - LOG_debug(serr,'error') - return serr - - if 'version' in self.Dispatcher.Stream._document_attrs and self.Dispatcher.Stream._document_attrs['version']=='1.0': - while not self.Dispatcher.Stream.features and self.Process(1): - # If we get version 1.0 stream the features tag MUST BE presented - pass - - return self.connected - -class Client(CommonClient): - """ Example client class, based on CommonClient. """ - def __init__(self, server, - port, - debug=[], # 'always', 'nodebuilder'] - buffer=None, - assl_dict=None): - self.buffer = buffer - if not assl_dict: - assl_dict=dict(keyfile=None, - certfile=None, - cert_reqs=ssl.CERT_NONE, - ssl_version=ssl.PROTOCOL_TLS, - ca_certs=None, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - ciphers=None) - self.assl_dict = assl_dict - # no ssl= - CommonClient.__init__(self, - server, - port, - buffer=self.buffer, - debug=debug) - - def connect(self, server_tuple=None, proxy=None, secure=None, use_srv=False, transport=None): - """Connect to XMPP server_tuple. If you want to specify different ip/port - to connect to you can pass it as tuple as first parameter. - If there is HTTP proxy between you and server - specify it's address and credentials (if needed) in the second argument. - - If you want ssl/tls support to be discovered and enable - automatically - leave third argument as None. (ssl will be - autodetected only if port is 5223 or 443) - - If you want to force SSL start (i.e. if port 5223 or 443 is - remapped to some non-standard port) then set it to 1. - - If you want to disable tls/ssl support completely, set it to 0. - - Example: connect(('192.168.5.5',5222),{'host':'proxy.my.net','port':8080,'user':'me','password':'secret'}) - Returns '' or 'tcp' or 'tls', depending on the result. - - """ - assert type(proxy) == dict - try: - cc = CommonClient(self) - self.connected = cc.connect(server_tuple, - proxy=proxy, - ssl=secure, - use_srv=use_srv, - transport=transport) - except Exception as e: - if self.buffer: - oerror = str(e) + ' ' + traceback.format_exc() - w_prnt(self.buffer, f"Error CCconnect {oerror} proxy={proxy}") - return '' - self.connected = 'tcp' - - if self.connected not in ['ssl', 'tcp']: - if self.buffer: - w_prnt(self.buffer, f"Error NOT Connected to {server_tuple} {self.connected}") - return '' - - if secure is not None and not secure: - # 0 or False - if self.buffer: - w_prnt(self.buffer, f"Connected {self.connected} to {server_tuple} proxy={proxy}") - return self.connected - - TLS().PlugIn(self, assl_dict=self.assl_dict) - - if 'version' not in self.Dispatcher.Stream._document_attrs or \ - not self.Dispatcher.Stream._document_attrs['version']=='1.0': - return self.connected - - while not self.Dispatcher.Stream.features and self.Process(1): - pass # If we get version 1.0 stream the features tag MUST BE presented - if not self.Dispatcher.Stream.features.getTag('starttls'): - return self.connected # TLS not supported by server - while not self.TLS.starttls and self.Process(1): - pass - if not hasattr(self, 'TLS') or self.TLS.starttls != 'success': - self.event('tls_failed') - return self.connected - self.connected = 'tls' - return self.connected +# ================================[ servers ]================================= class Server: """ Class to manage a server: buffer, connection, send/recv data. """ @@ -823,6 +425,8 @@ class Server: def __init__(self, name, **kwargs): """ Init server """ global jabber_config_file, jabber_config_section, jabber_server_options + if name and type(name) == bytes: + name = str(name, 'UTF-8') self.name = name # create options (user can set them with /set) self.options = {} @@ -833,22 +437,17 @@ class Server: values['name'] = name values.update(**kwargs) for option_name, props in jabber_server_options.items(): - self.options[option_name] = w.config_new_option( - jabber_config_file, - jabber_config_section["server"], - self.name + "." + option_name, props["type"], - props["desc"], - props["string_values"], - props["min"], - props["max"], - props["default"], - values[option_name], 0, + self.options[option_name] = weechat.config_new_option( + jabber_config_file, jabber_config_section["server"], + self.name + "." + option_name, props["type"], props["desc"], + props["string_values"], props["min"], props["max"], + props["default"], values[option_name], 0, props["check_cb"], self.name, props["change_cb"], "", props["delete_cb"], "") # internal data self.jid = None self.client = None - self.sock = None + self.sfn = None self.hook_fd = None self.buffer = "" self.chats = [] @@ -862,203 +461,176 @@ class Server: def option_string(self, option_name): """ Return a server option, as string. """ - return w.config_string(self.options[option_name]) + return weechat.config_string(self.options[option_name]) def option_boolean(self, option_name): """ Return a server option, as boolean. """ - return w.config_boolean(self.options[option_name]) + return weechat.config_boolean(self.options[option_name]) def option_integer(self, option_name): """ Return a server option, as string. """ - return w.config_integer(self.options[option_name]) + return weechat.config_integer(self.options[option_name]) - def make_proxy(self): - proxy = {} - pair = os.environ.get('https_proxy', '') - pair = pair.replace('https://', '') - pair = pair.replace('http://', '') - if ':' in pair: - phost, pport = pair.split(':', 1) - # '127.0.0.1' 9128 - proxy = {'host': phost, 'port': int(pport)} - return proxy - - def make_buffer(self): - bufname = "%s.server.%s" % (SCRIPT_NAME, self.name) - self.buffer = w.buffer_search("python", bufname) - if not self.buffer: - self.buffer = w.buffer_new(bufname, - "jabber_buffer_input_cb", "", - "jabber_buffer_close_cb", "") - if self.buffer: - w.buffer_set(self.buffer, "short_name", self.name) - w.buffer_set(self.buffer, "localvar_set_type", "server") - w.buffer_set(self.buffer, "localvar_set_server", self.name) - w.buffer_set(self.buffer, "nicklist", "1") - w.buffer_set(self.buffer, "nicklist_display_groups", "1") - w.buffer_set(self.buffer, "display", "auto") - + def DEBUG(self, *largs): + # if jabber_debug_enabled(): + message = repr(largs) + if len(largs) == 1: + message = largs[0] + if type(message) == bytes: + message = str(message, 'UTF-8') + if len(largs) == 2: + next = largs[1] + if type(next) in [list, tuple]: + next = ' '.join(next) + if type(next) == bytes: + next = str(next, 'UTF-8') + message += ': ' + next + elif len(largs) == 3: + next = largs[2] + if type(next) == bytes: + next = str(next, 'UTF-8') + message += ' - ' + next + LOG_debug(message) + def connect(self): - """ Connect to Jabber server. """ - try: - self.connect_() - except Exception as e: - oerror = str(e) + ' ' + traceback.format_exc() - w_prnt(self.buffer, f"%sError during connect {oerror}" - % w.prefix("error")) - - def connect_(self): """ Connect to Jabber server. """ if not self.buffer: - self.make_buffer() + bufname = "%s.server.%s" % (SCRIPT_NAME, self.name) + self.buffer = weechat.buffer_search("python", bufname) + if not self.buffer: + self.buffer = weechat.buffer_new(bufname, + "jabber_buffer_input_cb", "", + "jabber_buffer_close_cb", "") + if self.buffer: + weechat.buffer_set(self.buffer, "short_name", self.name) + weechat.buffer_set(self.buffer, "localvar_set_type", "server") + weechat.buffer_set(self.buffer, "localvar_set_server", self.name) + weechat.buffer_set(self.buffer, "nicklist", "1") + weechat.buffer_set(self.buffer, "nicklist_display_groups", "1") + weechat.buffer_set(self.buffer, "display", "auto") self.disconnect() if not eval_expression(self.option_string("jid")): - w_prnt(self.buffer, "%sjabber: JID must contain at least domain name" - % w.prefix("error")) + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: JID must contain at least domain name" + % weechat.prefix("error")) self.ping_up = False self.client = None return self.is_connected() - # server: Server object instance self.buddy = Buddy(jid=eval_expression(self.option_string("jid")), server=self) + transport = None server = self.option_string("server") port = self.option_integer("port") + aproxy = aget_proxy(self, ltypes=['http']) + if aproxy and 'type' in aproxy and aproxy['type'] in [0, 'http']: + lserver = (server, port,) + transport = transports.HTTPPROXYsocket(aproxy, lserver, use_srv=False) - secure = False - if port == 5222: - secure = False - elif port == 5223: - secure = True - elif ssl_ver != '': - secure = True - - if secure is False: - assl_dict = dict() - else: - ca_certs = w.config_string(self.options['CAfile']) - certfile = w.config_string(self.options['cert_file']) - keyfile = w.config_string(self.options['key_file']) - ssl_ver = w.config_string(self.options['ssl_ver']) - assert ssl_ver in ['', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'] - # CERT_NONE: 175f. (certificates ignored), - # CERT_OPTIONAL: 1760. (not required, but validated if provided), - # CERT_REQUIRED: 1761. (required and validated). - if ssl_ver == 'tlsv1': - cert_reqs = ssl.CERT_OPTIONAL - elif keyfile or certfile: - cert_reqs = ssl.CERT_REQUIRED - else: - cert_reqs = ssl.CERT_REQUIRED - ciphers = w.config_string(self.options['ciphers']) - # http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT - assl_dict = dict(keyfile=keyfile, - certfile=certfile, - cert_reqs=cert_reqs, - ssl_version=ssl.PROTOCOL_TLS, - ca_certs=ca_certs, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - ciphers=ciphers) - self.client = Client(self.buddy.domain, - port, - debug=[], - buffer=self.buffer, - assl_dict=assl_dict, - ) conn = None server_tuple = None - if not server: - # override - # pulled up from self.client.connect - server = self.buddy.domain - if port: - server_tuple = (server, int(port)) - else: - # override - # pulled up from self.client.connect - server_tuple = (server, 5222) - - proxy = {} - if os.environ.get('https_proxy', ''): - proxy = self.make_proxy() - + if server: + if port: + server_tuple = (server, port) + else: + server_tuple = (server) + + debug=['always', 'nodebuilder'] + self.client = xmpp.client.Client(server=self.buddy.domain, + debug=debug) + self.client.DEBUG = self.DEBUG + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: Server.connect {server_tuple} {aproxy} {self.client}" + % weechat.prefix("network")) # self.client.connect() may produce a "socket.ssl() is deprecated" # warning. Since the code producing the warning is outside this script, # catch it and ignore. original_filters = warnings.filters[:] warnings.filterwarnings("ignore", category=DeprecationWarning) - oerror = None - conn = None - use_srv = False - if False and proxy: - transport = HTTPPROXYsocket(proxy, - server_tuple, - use_srv=use_srv, - buffer=self.buffer) - w_prnt(self.buffer, - f"{w.prefix('network')}jabber: proxy: transport={transport}" - ) - else: - transport = None try: - conn = self.client.connect(server_tuple, - proxy=proxy, - secure=secure, - use_srv=False, + conn = self.client.connect(server=server_tuple, + proxy=aproxy, + secure=None, + use_srv=True, transport=transport, ) - except BaseException as e: - oerror = str(e) + ' ' + traceback.format_exc() + except AttributeError as e: + # transports error + conn = '' + except Exception as e: + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: error in connect {e}" + % weechat.prefix("error")) + conn = '' finally: warnings.filters = original_filters if not conn: - w_prnt(self.buffer, - f"{w.prefix('error')}jabber: could not connect: conn={conn} oerror={oerror}" - ) + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: could not connect" + % weechat.prefix("error")) self.ping_up = False self.client = None + return False + + # dunno + if hasattr(self.client, 'Connection') and self.client.Connection and \ + hasattr(self.client.Connection, '_tcpsock') and \ + hasattr(self.client.Connection._tcpsock, '_sslObj'): + ok = f"_sslObj={self.client.Connection._tcpsock._sslObj.fileno()}" + elif hasattr(self.client.Connection, '_sock'): + ok = f"_sock={self.client.Connection._sock.fileno()}" + elif transport: + ok = f"transport={transport._sock.fileno()}" else: - w_prnt(self.buffer, "jabber: connection ok with %s" % conn) - #? - self.ping_up = True - res = self.buddy.resource - if not res: - res = "WeeChat" + ok = 'ok' - w_prnt(self.buffer, f"jabber: auth as {self.buddy.username} {dir(self.client)}") - auth = self.client.auth(self.buddy.username, - eval_expression(self.option_string("password")), - res) + weechat.prnt(self.buffer, f"{SCRIPT_NAME}: connection {ok} with {conn}") + res = self.buddy.resource + if not res: + res = "WeeChat" - if auth: - w_prnt(self.buffer, f"{w.prefix('network')}authentication ok {auth}") + auth = self.client.auth(self.buddy.username, + eval_expression(self.option_string("password")), + res) - self.roster = self.client.getRoster() - self.client.RegisterHandler("presence", self.presence_handler) - self.client.RegisterHandler("iq", self.iq_handler) - self.client.RegisterHandler("message", self.message_handler) - self.client.sendInitPresence(requestRoster=1) - self.sock = self.client.Connection._sock.fileno() - self.hook_fd = w.hook_fd(self.sock, 1, 0, 0, "jabber_fd_cb", "") - w.buffer_set(self.buffer, "highlight_words", self.buddy.username) - w.buffer_set(self.buffer, "localvar_set_nick", self.buddy.username); - hook_away = w.hook_command_run("/away -all*", "jabber_away_command_run_cb", "") - - - # setting initial presence - priority = w.config_integer(self.options['priority']) - self.set_presence(show="",priority=priority) - - - self.ping_up = True + if auth: + if hasattr(self.client.Connection, '_sslObj'): + ok = f"_sslObj={self.client.Connection._sslObj.fileno()}" + self.sfn = self.client.Connection._sslObj.fileno() + elif hasattr(self.client.Connection, '_tcpsock'): + ok = f"_tcpsock={self.client.Connection._tcpsock.fileno()}" + self.sfn = self.client.Connection._sslObj.fileno() + elif hasattr(self.client.Connection, '_sock'): + ok = f"_sock={dir(self.client.Connection._sock)}" + self.sfn = self.client.Connection._sock.fileno() else: - w_prnt(self.buffer, "%sjabber: could not authenticate" - % w.prefix("error")) - self.ping_up = False - self.client = None + ok = 'ok' + weechat.prnt(self.buffer, f"{SCRIPT_NAME}: authentication {ok} (using %s)" % auth) + + self.roster = self.client.getRoster() + self.client.RegisterHandler("presence", self.presence_handler) + self.client.RegisterHandler("iq", self.iq_handler) + self.client.RegisterHandler("message", self.message_handler) + self.client.sendInitPresence(requestRoster=1) + if self.sfn < 0: + weechat.prnt(self.buffer, f"{SCRIPT_NAME}: NO hooking {self.sfn}") + else: + self.hook_fd = weechat.hook_fd(self.sfn, 1, 0, 0, "jabber_fd_cb", "") + weechat.prnt(self.buffer, f"{SCRIPT_NAME}: hooking {self.sfn}") + weechat.buffer_set(self.buffer, "highlight_words", self.buddy.username) + weechat.buffer_set(self.buffer, "localvar_set_nick", self.buddy.username); + hook_away = weechat.hook_command_run("/away -all*", "jabber_away_command_run_cb", "") + + + # setting initial presence + priority = weechat.config_integer(self.options['priority']) + self.set_presence(show="",priority=priority) + + + self.ping_up = True + else: + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: could not authenticate" + % weechat.prefix("error")) + self.ping_up = False + self.client = None return self.is_connected() def is_connected(self): @@ -1068,6 +640,21 @@ class Server: else: return True + def display_roster(self): + """Return connect status""" + if not self.roster: + if hasattr(self.client, 'Roster'): + self.roster = self.client.Roster + else: + self.roster = self.client.getRoster() + weechat.prnt(self.buffer, "") + weechat.prnt(self.buffer, "Roster:") + for elt in self.roster.getItems(): + weechat.prnt(self.buffer, repr(elt)) + + len_max = { 'alias': 5, 'jid': 5 } + return True + def add_chat(self, buddy): """Create a chat buffer for a buddy""" chat = Chat(self, buddy, switch_to_buffer=False) @@ -1087,7 +674,7 @@ class Server: def print_debug_server(self, message): """ Print debug message on server buffer. """ if jabber_debug_enabled(): - w_prnt(self.buffer, "%sjabber: %s" % (w.prefix("network"), message)) + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: %s" % (weechat.prefix("network"), message)) def print_debug_handler(self, handler_name, node): """ Print debug message for a handler on server buffer. """ @@ -1098,7 +685,7 @@ class Server: def print_error(self, message): """ Print error message on server buffer. """ if jabber_debug_enabled(): - w_prnt(self.buffer, "%sjabber: %s" % (w.prefix("error"), message)) + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: %s" % (weechat.prefix("error"), message)) def presence_handler(self, conn, node): self.print_debug_handler("presence", node) @@ -1117,7 +704,7 @@ class Server: if self.roster: name = self.roster.getName(buddy.bare_jid) if name: - buddy.set_name(name.encode("utf-8")) + buddy.set_name(name) buddy.set_status(status=status, away=away) self.update_nicklist(buddy=buddy, action=action) return @@ -1125,7 +712,7 @@ class Server: def iq_handler(self, conn, node): """ Receive iq message. """ self.print_debug_handler("iq", node) - #w_prnt(self.buffer, "jabber: iq handler") + #weechat.prnt(self.buffer, f"{SCRIPT_NAME}: iq handler") if node.getFrom() == self.buddy.domain: # type='result' => pong from server # type='error' => error message from server @@ -1136,13 +723,29 @@ class Server: if node.getType() in ['result', 'error']: self.delete_ping_timeout_timer() # Disable the timeout feature self.ping_up = True - if not self.client.isConnected() and w.config_boolean(self.options['autoreconnect']): + if not self.client.isConnected() and weechat.config_boolean(self.options['autoreconnect']): self.connect() def message_handler(self, conn, node): """ Receive message. """ self.print_debug_handler("message", node) node_type = node.getType() + if node_type in ['headline']: # MUC + a = """ + jabber: message_handler, xml message: │ + | b'\n \n \n \n │ + | \n \n │ + | test_h\n\n \n test_ │ + | h\n\n\n\n\n\ │ + | n \n │ + |
\n\n\n' """ + return if node_type not in ["message", "chat", None]: self.print_error("unknown message type: '%s'" % node_type) return @@ -1157,7 +760,7 @@ class Server: # buffer even if private is off. The buffer may have been created with # /jchat. recv_object = self - if not buddy.chat and w.config_boolean(self.options['private']): + if not buddy.chat and weechat.config_boolean(self.options['private']): self.add_chat(buddy) if buddy.chat: recv_object = buddy.chat @@ -1165,35 +768,50 @@ class Server: def recv(self): """ Receive something from Jabber server. """ + LOG_debug(f"recv: {self.client}") if not self.client: return try: self.client.Process(1) except xmpp.protocol.StreamError as e: - w_prnt('', '%s: Error from server: %s' %(SCRIPT_NAME, e)) + weechat.prnt('', '%s: Error from server: %s' %(SCRIPT_NAME, e)) self.disconnect() - if w.config_boolean(self.options['autoreconnect']): + if weechat.config_boolean(self.options['autoreconnect']): autoreconnect_delay = 30 - w.command('', '/wait %s /%s connect %s' % + weechat.command('', '/wait %s /%s connect %s' % (autoreconnect_delay, SCRIPT_COMMAND, self.name)) def recv_message(self, buddy, message): """ Receive a message from buddy. """ - w_prnt_date_tags(self.buffer, 0, - "notify_private,nick_%s,prefix_nick_%s,log1" % - (buddy.alias, - w.config_string(w.config_get("weechat.color.chat_nick_other"))), - "%s%s\t%s" % (w.color("chat_nick_other"), + LOG_debug(f"recv_message: {message}") + if type(message) == bytes: + message = str(message, 'UTF-8') + if type(buddy.alias) == bytes: + buddy.alias = str(buddy.alias, 'UTF-8') + nickc = weechat.config_get("weechat.color.chat_nick_other") + weechat.prnt_date_tags(self.buffer, + 0, + f"notify_private,nick_{buddy.alias}" + + f",prefix_nick_%s,log1" % + (weechat.config_string(nickc)), + "%s%s\t%s" % (weechat.color("chat_nick_other"), buddy.alias, message)) def print_status(self, nickname, status): """ Print a status in server window and in chat. """ - w_prnt_date_tags(self.buffer, 0, "no_highlight", "%s%s has status %s" % - (w.prefix("action"), + if type(nickname) == bytes: + nickname = str(nickname, 'UTF-8') + weechat.prnt_date_tags(self.buffer, + 0, + "no_highlight", + "%s%s has status %s" % + (weechat.prefix("action"), nickname, status)) for chat in self.chats: + if type(chat.buddy.alias) == bytes: + chat.buddy.alias = str(chat.buddy.alias, 'UTF-8') if nickname in chat.buddy.alias: chat.print_status(status) break @@ -1208,28 +826,35 @@ class Server: if isinstance(buddy, Buddy): recipient = buddy.jid if not self.ping_up: - w_prnt(self.buffer, "%sjabber: unable to send message, connection is down" - % w.prefix("error")) + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: unable to send message, connection is down" + % weechat.prefix("error")) return - if self.client: + if not self.client: + return + try: + # bytes? msg = xmpp.protocol.Message(to=recipient, body=message, typ='chat') self.client.send(msg) + except Exception as e: + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: send_message error {e}" + % weechat.prefix("error")) def send_message_from_input(self, input=''): """ Send a message from input text on server buffer. """ # Input must be of format "name: message" where name is a jid, bare_jid # or alias. The colon can be replaced with a comma as well. # Split input into name and message. + LOG_debug(f"send_message_from_input: {input}") if not re.compile(r'.+[:,].+').match(input): - w_prnt(self.buffer, "%sjabber: %s" % (w.prefix("network"), - "Invalid send format. Use jid: message" + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: %s" % (weechat.prefix("network"), + "Invalid send format {input}. Use jid: message" )) return name, message = re.split('[:,]', input, maxsplit=1) buddy = self.search_buddy_list(name, by='alias') if not buddy: - w_prnt(self.buffer, - "%sjabber: Invalid jid: %s" % (w.prefix("network"), + weechat.prnt(self.buffer, + f"%s{SCRIPT_NAME}: Invalid jid: %s" % (weechat.prefix("network"), name)) return # Send activity indicates user is no longer away, set it so @@ -1240,11 +865,14 @@ class Server: sender = self.buddy.alias except: sender = self.jid - w_prnt_date_tags(self.buffer, 0, + if type(sender) == bytes: + sender = str(sender, 'UTF-8') + weechat.prnt_date_tags(self.buffer, + 0, "notify_none,no_highlight,nick_%s,prefix_nick_%s,log1" % (sender, - w.config_string(w.config_get("weechat.color.chat_nick_self"))), - "%s%s\t%s" % (w.color("chat_nick_self"), + weechat.config_string(weechat.config_get("weechat.color.chat_nick_self"))), + "%s%s\t%s" % (weechat.color("chat_nick_self"), sender, message.strip())) @@ -1257,12 +885,12 @@ class Server: if message: show = "xa" status = message - priority = w.config_integer(self.options['away_priority']) + priority = weechat.config_integer(self.options['away_priority']) self.buddy.set_status(away=True, status=message) else: show = "" status = None - priority = w.config_integer(self.options['priority']) + priority = weechat.config_integer(self.options['priority']) self.buddy.set_status(away=False) self.set_presence(show, status, priority) @@ -1280,8 +908,8 @@ class Server: def display_buddies(self): """ Display buddies. """ - w_prnt(self.buffer, "") - w_prnt(self.buffer, "Buddies:") + weechat.prnt(self.buffer, "") + weechat.prnt(self.buffer, "Buddies:") len_max = { 'alias': 5, 'jid': 5 } lines = [] @@ -1289,7 +917,9 @@ class Server: alias = '' if buddy.alias != buddy.bare_jid: alias = buddy.alias - buddy_jid_string = buddy.jid.getStripped().encode('utf-8') + buddy_jid_string = buddy.jid.getStripped() + if type(buddy_jid_string) == bytes: + buddy_jid_string = str(buddy_jid_string, 'UTF-8') lines.append( { 'jid': buddy_jid_string, 'alias': alias, @@ -1300,9 +930,9 @@ class Server: if len(buddy_jid_string) > len_max['jid']: len_max['jid'] = len(buddy_jid_string) prnt_format = " %s%-" + str(len_max['jid']) + "s %-" + str(len_max['alias']) + "s %s" - w_prnt(self.buffer, prnt_format % ('', 'JID', 'Alias', 'Status')) + weechat.prnt(self.buffer, prnt_format % ('', 'JID', 'Alias', 'Status')) for line in lines: - w_prnt(self.buffer, prnt_format % (w.color("chat_nick"), + weechat.prnt(self.buffer, prnt_format % (weechat.color("chat_nick"), line['jid'], line['alias'], line['status'], @@ -1325,7 +955,7 @@ class Server: if wresource and jid.resource: # concatenate jid with resource delimiter first and encode them # into utf-8, else it will raise UnicodeException becaouse of - # slash character:(( + # slash character :(( return (jid_str + '/').encode("utf-8") + jid.resource.encode("utf-8") return jid_str.encode("utf-8") @@ -1372,8 +1002,8 @@ class Server: return if not action in ['remove', 'update']: return - ptr_nick_gui = w.nicklist_search_nick(self.buffer, "", buddy.alias) - w.nicklist_remove_nick(self.buffer, ptr_nick_gui) + ptr_nick_gui = weechat.nicklist_search_nick(self.buffer, "", buddy.alias) + weechat.nicklist_remove_nick(self.buffer, ptr_nick_gui) msg = '' prefix = '' color = '' @@ -1382,7 +1012,7 @@ class Server: nick_color = "bar_fg" if buddy.away: nick_color = "weechat.color.nicklist_away" - w.nicklist_add_nick(self.buffer, "", buddy.alias, + weechat.nicklist_add_nick(self.buffer, "", buddy.alias, nick_color, "", "", 1) if not ptr_nick_gui: msg = 'joined' @@ -1394,9 +1024,9 @@ class Server: prefix = 'quit' color = 'message_quit' if msg: - w_prnt(self.buffer, "%s%s%s%s has %s %s" - % (w.prefix(prefix), - w.color("chat_nick"), + weechat.prnt(self.buffer, "%s%s%s%s has %s %s" + % (weechat.prefix(prefix), + weechat.color("chat_nick"), buddy.alias, jabber_config_color(color), msg, @@ -1408,13 +1038,13 @@ class Server: self.delete_ping_timer() if not self.option_integer('ping_interval'): return - self.ping_timer = w.hook_timer( self.option_integer('ping_interval') * 1000, + self.ping_timer = weechat.hook_timer( self.option_integer('ping_interval') * 1000, 0, 0, "jabber_ping_timer", self.name) return def delete_ping_timer(self): if self.ping_timer: - w.unhook(self.ping_timer) + weechat.unhook(self.ping_timer) self.ping_time = None return @@ -1423,14 +1053,14 @@ class Server: self.delete_ping_timeout_timer() if not self.option_integer('ping_timeout'): return - self.ping_timeout_timer = w.hook_timer( + self.ping_timeout_timer = weechat.hook_timer( self.option_integer('ping_timeout') * 1000, 0, 1, "jabber_ping_timeout_timer", self.name) return def delete_ping_timeout_timer(self): if self.ping_timeout_timer: - w.unhook(self.ping_timeout_timer) + weechat.unhook(self.ping_timeout_timer) self.ping_timeout_timer = None return @@ -1461,21 +1091,21 @@ class Server: def disconnect(self): """ Disconnect from Jabber server. """ if self.hook_fd != None: - w.unhook(self.hook_fd) + weechat.unhook(self.hook_fd) self.hook_fd = None if self.client != None: #if self.client.isConnected(): # self.client.disconnect() self.client = None self.jid = None - self.sock = None + self.sfn = None self.buddy = None - w.nicklist_remove_all(self.buffer) + weechat.nicklist_remove_all(self.buffer) def close_buffer(self): """ Close server buffer. """ if self.buffer != "": - w.buffer_close(self.buffer) + weechat.buffer_close(self.buffer) self.buffer = "" def delete(self, deleteOptions=False): @@ -1488,13 +1118,12 @@ class Server: self.close_buffer() if deleteOptions: for name, option in self.options.items(): - w.config_option_free(option) + weechat.config_option_free(option) def eval_expression(option_name): """ Return a evaluated expression """ - version = int(w.info_get("version_number", "") or 0) if int(version) >= 0x00040200: - return w.string_eval_expression(option_name,{},{},{}) + return weechat.string_eval_expression(option_name,{},{},{}) else: return option_name @@ -1525,7 +1154,7 @@ def jabber_search_context_by_name(server_name): """Search for buffer given name of server. """ bufname = "%s.server.%s" % (SCRIPT_NAME, server_name) - return jabber_search_context(w.buffer_search("python", bufname)) + return jabber_search_context(weechat.buffer_search("python", bufname)) # =================================[ chats ]================================== @@ -1538,62 +1167,77 @@ class Chat: self.server = server self.buddy = buddy buddy.chat = self - bufname = "%s.%s.%s" % (SCRIPT_NAME, server.name, self.buddy.alias) - self.buffer = w.buffer_search("python", bufname) + if type(buddy.alias) == bytes: + buddy.alias = str(buddy.alias, 'UTF-8') + alias = self.buddy.alias + bufname = "%s.%s.%s" % (SCRIPT_NAME, server.name, alias) + self.buffer = weechat.buffer_search("python", bufname) if not self.buffer: - self.buffer = w.buffer_new(bufname, + self.buffer = weechat.buffer_new(bufname, "jabber_buffer_input_cb", "", "jabber_buffer_close_cb", "") self.buffer_title = self.buddy.alias if self.buffer: - w.buffer_set(self.buffer, "title", self.buffer_title) - w.buffer_set(self.buffer, "short_name", self.buddy.alias) - w.buffer_set(self.buffer, "localvar_set_type", "private") - w.buffer_set(self.buffer, "localvar_set_server", server.name) - w.buffer_set(self.buffer, "localvar_set_channel", self.buddy.alias) - w.hook_signal_send("logger_backlog", - w.WEECHAT_HOOK_SIGNAL_POINTER, self.buffer) + weechat.buffer_set(self.buffer, "title", self.buffer_title) + weechat.buffer_set(self.buffer, "short_name", self.buddy.alias) + weechat.buffer_set(self.buffer, "localvar_set_type", "private") + weechat.buffer_set(self.buffer, "localvar_set_server", server.name) + weechat.buffer_set(self.buffer, "localvar_set_channel", self.buddy.alias) + weechat.hook_signal_send("logger_backlog", + weechat.WEECHAT_HOOK_SIGNAL_POINTER, self.buffer) if switch_to_buffer: - w.buffer_set(self.buffer, "display", "auto") + weechat.buffer_set(self.buffer, "display", "auto") def recv_message(self, buddy, message): """ Receive a message from buddy. """ + LOG_debug(f"recv_message: {message}") + if type(message) == bytes: + message = str(message, 'UTF-8') + if type(buddy.alias) == bytes: + buddy.alias = str(buddy.alias, 'UTF-8') if buddy.alias != self.buffer_title: self.buffer_title = buddy.alias - w.buffer_set(self.buffer, "title", "%s" % self.buffer_title) - w_prnt_date_tags(self.buffer, 0, + weechat.buffer_set(self.buffer, "title", "%s" % self.buffer_title) + weechat.prnt_date_tags(self.buffer, 0, "notify_private,nick_%s,prefix_nick_%s,log1" % (buddy.alias, - w.config_string(w.config_get("weechat.color.chat_nick_other"))), - "%s%s\t%s" % (w.color("chat_nick_other"), + weechat.config_string(weechat.config_get("weechat.color.chat_nick_other"))), + "%s%s\t%s" % (weechat.color("chat_nick_other"), buddy.alias, message)) def send_message(self, message): """ Send message to buddy. """ if not self.server.ping_up: - w_prnt(self.buffer, "%sxmpp: unable to send message, connection is down" - % w.prefix("error")) + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: unable to send message, connection is down" + % weechat.prefix("error")) return - self.server.send_message(self.buddy, message) - w_prnt_date_tags(self.buffer, 0, - "notify_none,no_highlight,nick_%s,prefix_nick_%s,log1" % - (self.server.buddy.alias, - w.config_string(w.config_get("weechat.color.chat_nick_self"))), - "%s%s\t%s" % (w.color("chat_nick_self"), - self.server.buddy.alias, - message)) + if type(self.server.buddy.alias) == bytes: + self.server.buddy.alias = str(self.server.buddy.alias, 'UTF-8') + try: + self.server.send_message(self.buddy, message) + weechat.prnt_date_tags(self.buffer, 0, + "notify_none,no_highlight,nick_%s,prefix_nick_%s,log1" % + (self.server.buddy.alias, + weechat.config_string(weechat.config_get("weechat.color.chat_nick_self"))), + "%s%s\t%s" % (weechat.color("chat_nick_self"), + self.server.buddy.alias, + message)) + except Exception as e: + weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: error sending message {e}" + % weechat.prefix("error")) + def print_status(self, status): """ Print a status message in chat. """ - w_prnt(self.buffer, "%s%s has status %s" % - (w.prefix("action"), + weechat.prnt(self.buffer, "%s%s has status %s" % + (weechat.prefix("action"), self.buddy.alias, status)) def close_buffer(self): """ Close chat buffer. """ if self.buffer != "": - w.buffer_close(self.buffer) + weechat.buffer_close(self.buffer) self.buffer = "" def delete(self): @@ -1645,11 +1289,11 @@ class Buddy: str_colon = ": " if not self.status: str_colon = "" - return "%s(%saway%s%s%s)" % (w.color("chat_delimiters"), - w.color("chat"), + return "%s(%saway%s%s%s)" % (weechat.color("chat_delimiters"), + weechat.color("chat"), str_colon, self.status.replace("\n", " "), - w.color("chat_delimiters")) + weechat.color("chat_delimiters")) def parse_jid(self): """Parse the jid property. @@ -1682,6 +1326,8 @@ class Buddy: if not self.bare_jid: self.alias = '' if self.name: + if type(name) == bytes: + name = str(name, 'UTF-8') self.alias = self.name global jabber_jid_aliases for alias, jid in jabber_jid_aliases.items(): @@ -1691,6 +1337,8 @@ class Buddy: return def set_name(self, name=''): + if type(name) == bytes: + name = str(name, 'UTF-8') self.name = name self.set_alias() return @@ -1716,17 +1364,18 @@ class Buddy: def jabber_hook_commands_and_completions(): """ Hook commands and completions. """ - w.hook_command(SCRIPT_COMMAND, "Manage Jabber servers", + weechat.hook_command(SCRIPT_COMMAND, "Manage Jabber servers", "list || add [[:]]" " || connect|disconnect|del [] || alias [add|del ]" " || away [] || buddies || priority []" " || status [] || presence [online|chat|away|xa|dnd]" - " || debug || set []", + " || ping || debug || set []", " list: list servers and chats\n" " add: add a server\n" " connect: connect to server using password\n" "disconnect: disconnect from server\n" " del: delete server\n" + " ping: ping server\n" " alias: manage jid aliases\n" " away: set away with a message (if no message, away is unset)\n" " priority: set priority\n" @@ -1761,45 +1410,47 @@ def jabber_hook_commands_and_completions(): " || del %(jabber_servers)" " || alias add|del %(jabber_jid_aliases)" " || away" + " || ping" " || priority" + " || roster" " || status" " || presence online|chat|away|xa|dnd" " || buddies" " || debug", "jabber_cmd_jabber", "") - w.hook_command("jchat", "Chat with a Jabber buddy", + weechat.hook_command("jchat", "Chat with a Jabber buddy", "", "buddy: buddy id", "", "jabber_cmd_jchat", "") - w.hook_command("jmsg", "Send a messge to a buddy", + weechat.hook_command("jmsg", "Send a messge to a buddy", "[-server ] ", "server: name of jabber server buddy is on\n" " buddy: buddy id\n" " text: text to send", "", "jabber_cmd_jmsg", "") - w.hook_command("invite", "Add a buddy to your roster", + weechat.hook_command("invite", "Add a buddy to your roster", "", "buddy: buddy id", "", "jabber_cmd_invite", "") - w.hook_command("kick", "Remove a buddy from your roster, or deny auth", + weechat.hook_command("kick", "Remove a buddy from your roster, or deny auth", "", "buddy: buddy id", "", "jabber_cmd_kick", "") - w.hook_completion("jabber_servers", "list of jabber servers", + weechat.hook_completion("jabber_servers", "list of jabber servers", "jabber_completion_servers", "") - w.hook_completion("jabber_jid_aliases", "list of jabber jid aliases", + weechat.hook_completion("jabber_jid_aliases", "list of jabber jid aliases", "jabber_completion_jid_aliases", "") def jabber_list_servers_chats(name): """ List servers and chats. """ global jabber_servers - w_prnt("", "") + weechat.prnt("", "") if len(jabber_servers) > 0: - w_prnt("", "jabber servers:") + weechat.prnt("", "jabber servers:") for server in jabber_servers: if name == "" or server.name.find(name) >= 0: conn_server = '' @@ -1808,17 +1459,15 @@ def jabber_list_servers_chats(name): (server.option_string("server"), server.option_string("port"))) connected = "" - if server.sock and server.sock >= 0: + if server.sfn and server.sfn >= 0: connected = "(connected)" - else: - connected = "(not conn)" - w_prnt("", " %s - %s %s %s" % (server.name, + weechat.prnt("", " %s - %s %s %s" % (server.name, eval_expression(server.option_string("jid")), conn_server, connected)) for chat in server.chats: - w_prnt("", " chat with %s" % (chat.buddy)) + weechat.prnt("", " chat with %s" % (chat.buddy)) else: - w_prnt("", "jabber: no server defined") + weechat.prnt("", f"{SCRIPT_NAME}: no server defined") def jabber_cmd_jabber(data, buffer, args): """ Command '/jabber'. """ @@ -1837,49 +1486,47 @@ def jabber_cmd_jabber(data, buffer, args): if len(argv) >= 4: server = jabber_search_server_by_name(argv[1]) if server: - w_prnt("", "jabber: server '%s' already exists" % argv[1]) + weechat.prnt("", f"{SCRIPT_NAME}: server '%s' already exists" % argv[1]) else: kwargs = {'jid': argv[2], 'password': argv[3]} if len(argv) > 4: conn_server, _, conn_port = argv[4].partition(':') if conn_port and not conn_port.isdigit(): - w_prnt("", "jabber: error, invalid port, digits only") - return w.WEECHAT_RC_OK + weechat.prnt("", f"{SCRIPT_NAME}: error, invalid port, digits only") + return weechat.WEECHAT_RC_OK if conn_server: kwargs['server'] = conn_server if conn_port: kwargs['port'] = conn_port server = Server(argv[1], **kwargs) jabber_servers.append(server) - w_prnt("", "jabber: server '%s' created" % argv[1]) + weechat.prnt("", f"{SCRIPT_NAME}: server '%s' created" % argv[1]) else: - w_prnt("", "jabber: unable to add server, missing arguments") - w_prnt("", "jabber: usage: /jabber add name jid password [server[:port]]") + weechat.prnt("", f"{SCRIPT_NAME}: unable to add server, missing arguments") + weechat.prnt("", f"{SCRIPT_NAME}: usage: /jabber add name jid password [server[:port]]") elif argv[0] == "alias": alias_command = AliasCommand(buffer, argv=argv[1:]) alias_command.run() - elif argv[0] == "connect": server = None if len(argv) >= 2: server = jabber_search_server_by_name(argv[1]) if not server: - w_prnt("", "jabber: server '%s' not found" % argv[1]) + weechat.prnt("", f"{SCRIPT_NAME}: server '%s' not found" % argv[1]) else: context = jabber_search_context(buffer) if context["server"]: server = context["server"] if server: - if w.config_boolean(server.options['autoreconnect']): + if weechat.config_boolean(server.options['autoreconnect']): server.ping() # This will connect and update ping status server.add_ping_timer() else: server.connect() - elif argv[0] == "disconnect": server = None if len(argv) >= 2: server = jabber_search_server_by_name(argv[1]) if not server: - w_prnt("", "jabber: server '%s' not found" % argv[1]) + weechat.prnt("", f"{SCRIPT_NAME}: server '%s' not found" % argv[1]) else: context = jabber_search_context(buffer) if context["server"]: @@ -1894,9 +1541,9 @@ def jabber_cmd_jabber(data, buffer, args): if server: server.delete(deleteOptions=True) jabber_servers.remove(server) - w_prnt("", "jabber: server '%s' deleted" % argv[1]) + weechat.prnt("", f"{SCRIPT_NAME}: server '%s' deleted" % argv[1]) else: - w_prnt("", "jabber: server '%s' not found" % argv[1]) + weechat.prnt("", f"{SCRIPT_NAME}: server '%s' not found" % argv[1]) elif argv[0] == "send": if len(argv) >= 3: context = jabber_search_context(buffer) @@ -1914,27 +1561,28 @@ def jabber_cmd_jabber(data, buffer, args): context = jabber_search_context(buffer) if context["server"]: if len(argv) == 1: - w_prnt("", "jabber: priority = %d" % int(context["server"].presence.getPriority())) + weechat.prnt("", f"{SCRIPT_NAME}: priority = %d" % int(context["server"].presence.getPriority())) elif len(argv) == 2 and argv[1].isdigit(): context["server"].set_presence(priority=int(argv[1])) else: - w_prnt("", "jabber: you need to specify priority as positive integer between 0 and 65535") + weechat.prnt("", f"{SCRIPT_NAME}: you need to specify priority as positive integer between 0 and 65535") elif argv[0] == "status": context = jabber_search_context(buffer) if context["server"]: if len(argv) == 1: - w_prnt("", "jabber: status = %s" % context["server"].presence.getStatus()) + weechat.prnt("", f"{SCRIPT_NAME}: status = %s" % context["server"].presence.getStatus()) else: context["server"].set_presence(status=argv1eol) + elif argv[0] == "presence": context = jabber_search_context(buffer) if context["server"]: if len(argv) == 1: show = context["server"].presence.getShow() if show == "": show = "online" - w_prnt("", "jabber: presence = %s" % show) + weechat.prnt("", f"{SCRIPT_NAME}: presence = %s" % show) elif not re.match(r'^(?:online|chat|away|xa|dnd)$', argv[1]): - w_prnt("", "jabber: Presence should be one of: online, chat, away, xa, dnd") + weechat.prnt("", f"{SCRIPT_NAME}: Presence should be one of: online, chat, away, xa, dnd") else: if argv[1] == "online": show = "" else: show = argv[1] @@ -1943,16 +1591,23 @@ def jabber_cmd_jabber(data, buffer, args): context = jabber_search_context(buffer) if context["server"]: context["server"].display_buddies() - + elif argv[0] == "roster": + context = jabber_search_context(buffer) + if context["server"]: + context["server"].display_roster() + elif argv[0] == "ping": + context = jabber_search_context(buffer) + if context["server"]: + context["server"].ping() elif argv[0] == "debug": - w.config_option_set(jabber_config_option["debug"], "toggle", 1) + weechat.config_option_set(jabber_config_option["debug"], "toggle", 1) if jabber_debug_enabled(): - w_prnt("", "jabber: debug is now ON") + weechat.prnt("", f"{SCRIPT_NAME}: debug is now ON") else: - w_prnt("", "jabber: debug is now off") + weechat.prnt("", f"{SCRIPT_NAME}: debug is now off") else: - w_prnt("", "jabber: unknown action") - return w.WEECHAT_RC_OK + weechat.prnt("", f"{SCRIPT_NAME}: unknown action") + return weechat.WEECHAT_RC_OK def jabber_cmd_jchat(data, buffer, args): """ Command '/jchat'. """ @@ -1964,15 +1619,15 @@ def jabber_cmd_jchat(data, buffer, args): buddy = context["server"].add_buddy(jid=args) if not buddy.chat: context["server"].add_chat(buddy) - w.buffer_set(buddy.chat.buffer, "display", "auto") - return w.WEECHAT_RC_OK + weechat.buffer_set(buddy.chat.buffer, "display", "auto") + return weechat.WEECHAT_RC_OK def jabber_cmd_jmsg(data, buffer, args): """ Command '/jmsg'. """ if args: argv = args.split() if len(argv) < 2: - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK if argv[0] == '-server': context = jabber_search_context_by_name(argv[1]) recipient = argv[2] @@ -1985,7 +1640,7 @@ def jabber_cmd_jmsg(data, buffer, args): buddy = context['server'].search_buddy_list(recipient, by='alias') context["server"].send_message(buddy, message) - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK def jabber_cmd_invite(data, buffer, args): """ Command '/invite'. """ @@ -1993,7 +1648,7 @@ def jabber_cmd_invite(data, buffer, args): context = jabber_search_context(buffer) if context["server"]: context["server"].add_buddy(args) - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK def jabber_cmd_kick(data, buffer, args): """ Command '/kick'. """ @@ -2001,7 +1656,7 @@ def jabber_cmd_kick(data, buffer, args): context = jabber_search_context(buffer) if context["server"]: context["server"].del_buddy(args) - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK def jabber_away_command_run_cb(data, buffer, command): """ Callback called when /away -all command is run """ @@ -2014,7 +1669,7 @@ def jabber_away_command_run_cb(data, buffer, command): message = words[2] for server in jabber_servers: server.set_away(message) - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK class AliasCommand(object): """Class representing a jabber alias command, ie /jabber alias ...""" @@ -2041,37 +1696,38 @@ class AliasCommand(object): """Run a "/jabber alias add" command""" global jabber_jid_aliases if not self.alias or not self.jid: - w_prnt("", "\njabber: unable to add alias, missing arguments") - w_prnt("", "jabber: usage: /jabber alias add alias_name jid") + weechat.prnt("", "\njabber: unable to add alias, missing arguments") + weechat.prnt("", f"{SCRIPT_NAME}: usage: /jabber alias add alias_name jid") return # Restrict the character set of aliases. The characters must be writable to # config file. invalid_re = re.compile(r'[^a-zA-Z0-9\[\]\\\^_\-{|}@\.]') if invalid_re.search(self.alias): - w_prnt("", "\njabber: invalid alias: %s" % self.alias) - w_prnt("", "jabber: use only characters: a-z A-Z 0-9 [ \ ] ^ _ - { | } @ .") + weechat.prnt("", "\njabber: invalid alias: %s" % self.alias) + weechat.prnt("", f"{SCRIPT_NAME}: use only characters:" + + "a-z A-Z 0-9 [ \ ] ^ _ - { | } @ .") return # Ensure alias and jid are reasonable length. max_len = 64 if len(self.alias) > max_len: - w_prnt("", "\njabber: invalid alias: %s" % self.alias) - w_prnt("", "jabber: must be no more than %s characters long" % max_len) + weechat.prnt("", "\njabber: invalid alias: %s" % self.alias) + weechat.prnt("", f"{SCRIPT_NAME}: must be no more than %s characters long" % max_len) return if len(self.jid) > max_len: - w_prnt("", "\njabber: invalid jid: %s" % self.jid) - w_prnt("", "jabber: must be no more than %s characters long" % max_len) + weechat.prnt("", "\njabber: invalid jid: %s" % self.jid) + weechat.prnt("", f"{SCRIPT_NAME}: must be no more than %s characters long" % max_len) return jid = self.jid.encode("utf-8") alias = self.alias.encode("utf-8") if alias in jabber_jid_aliases.keys(): - w_prnt("", "\njabber: unable to add alias: %s" % (alias)) - w_prnt("", "jabber: alias already exists, delete first") + weechat.prnt("", "\njabber: unable to add alias: %s" % (alias)) + weechat.prnt("", f"{SCRIPT_NAME}: alias already exists, delete first") return if jid in jabber_jid_aliases.values(): - w_prnt("", "\njabber: unable to add alias: %s" % (alias)) + weechat.prnt("", "\njabber: unable to add alias: %s" % (alias)) for a, j in jabber_jid_aliases.items(): if j == jid: - w_prnt("", "jabber: jid '%s' is already aliased as '%s', delete first" % + weechat.prnt("", f"{SCRIPT_NAME}: jid '%s' is already aliased as '%s', delete first" % (j, a)) break jabber_jid_aliases[alias] = jid @@ -2099,18 +1755,18 @@ class AliasCommand(object): buddy.chat.delete() new_chat = server.add_chat(buddy) if switch_to_buffer: - w.buffer_set(new_chat.buffer, "display", "auto") + weechat.buffer_set(new_chat.buffer, "display", "auto") return def delete(self): """Run a "/jabber alias del" command""" global jabber_jid_aliases if not self.alias: - w_prnt("", "\njabber: unable to delete alias, missing arguments") - w_prnt("", "jabber: usage: /jabber alias del alias_name") + weechat.prnt("", "\njabber: unable to delete alias, missing arguments") + weechat.prnt("", f"{SCRIPT_NAME}: usage: /jabber alias del alias_name") return if not self.alias in jabber_jid_aliases: - w_prnt("", "\njabber: unable to delete alias '%s', not found" % (self.alias)) + weechat.prnt("", "\njabber: unable to delete alias '%s', not found" % (self.alias)) return jid = jabber_jid_aliases[self.alias] del jabber_jid_aliases[self.alias] @@ -2120,11 +1776,11 @@ class AliasCommand(object): def list(self): """Run a "/jabber alias" command to list aliases""" global jabber_jid_aliases - w_prnt("", "") + weechat.prnt("", "") if len(jabber_jid_aliases) <= 0: - w_prnt("", "jabber: no aliases defined") + weechat.prnt("", f"{SCRIPT_NAME}: no aliases defined") return - w_prnt("", "jabber jid aliases:") + weechat.prnt("", "jabber jid aliases:") len_alias = 5 len_jid = 5 for alias, jid in jabber_jid_aliases.items(): @@ -2133,9 +1789,9 @@ class AliasCommand(object): if len_jid < len(jid): len_jid = len(jid) prnt_format = " %-" + str(len_alias) + "s %-" + str(len_jid) + "s" - w_prnt("", prnt_format % ('Alias', 'JID')) + weechat.prnt("", prnt_format % ('Alias', 'JID')) for alias, jid in sorted(jabber_jid_aliases.items()): - w_prnt("", prnt_format % (alias, jid)) + weechat.prnt("", prnt_format % (alias, jid)) return def parse(self): @@ -2163,27 +1819,28 @@ def jabber_completion_servers(data, completion_item, buffer, completion): """ Completion with jabber server names. """ global jabber_servers for server in jabber_servers: - w.hook_completion_list_add(completion, server.name, - 0, w.WEECHAT_LIST_POS_SORT) - return w.WEECHAT_RC_OK + weechat.hook_completion_list_add(completion, server.name, + 0, weechat.WEECHAT_LIST_POS_SORT) + return weechat.WEECHAT_RC_OK def jabber_completion_jid_aliases(data, completion_item, buffer, completion): """ Completion with jabber alias names. """ global jabber_jid_aliases for alias, jid in sorted(jabber_jid_aliases.items()): - w.hook_completion_list_add(completion, alias, - 0, w.WEECHAT_LIST_POS_SORT) - return w.WEECHAT_RC_OK + weechat.hook_completion_list_add(completion, alias, + 0, weechat.WEECHAT_LIST_POS_SORT) + return weechat.WEECHAT_RC_OK # ==================================[ fd ]==================================== def jabber_fd_cb(data, fd): """ Callback for reading socket. """ global jabber_servers + weechat.prnt('', f"{SCRIPT_NAME}: jabber_fd_cb {fd}") for server in jabber_servers: - if server.sock == int(fd): + if server.sfn == int(fd): server.recv() - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK # ================================[ buffers ]================================= @@ -2197,7 +1854,7 @@ def jabber_buffer_input_cb(data, buffer, input_data): context["server"].display_buddies() else: context["server"].send_message_from_input(input=input_data) - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK def jabber_buffer_close_cb(data, buffer): """ Callback called when a jabber buffer is closed. """ @@ -2209,7 +1866,7 @@ def jabber_buffer_close_cb(data, buffer): context["server"].chats.remove(context["chat"]) elif context["server"]: context["server"].buffer = "" - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK # ==================================[ timers ]================================== @@ -2217,16 +1874,33 @@ def jabber_ping_timeout_timer(server_name, remaining_calls): server = jabber_search_server_by_name(server_name) if server: server.ping_time_out() - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK def jabber_ping_timer(server_name, remaining_calls): server = jabber_search_server_by_name(server_name) if server: server.ping() - return w.WEECHAT_RC_OK + return weechat.WEECHAT_RC_OK # ==================================[ main ]================================== +if __name__ == "__main__" and import_ok: + if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, + SCRIPT_LICENSE, SCRIPT_DESC, + "jabber_unload_script", ""): + + version = weechat.info_get("version_number", "") or 0 + jabber_hook_commands_and_completions() + jabber_config_init() + jabber_config_read() + for server in jabber_servers: + if weechat.config_boolean(server.options['autoreconnect']): + server.ping() # This will connect and update ping status + server.add_ping_timer() + else: + if weechat.config_boolean(server.options['autoconnect']): + server.connect() + # ==================================[ end ]=================================== def jabber_unload_script(): @@ -2236,28 +1910,4 @@ def jabber_unload_script(): for server in jabber_servers: server.disconnect() server.delete() - return w.WEECHAT_RC_OK - -if __name__ == "__main__": - # WeechatWrapper - w = weechat - - if w.register(SCRIPT_NAME, - SCRIPT_AUTHOR, - SCRIPT_VERSION, - SCRIPT_LICENSE, - SCRIPT_DESC, - "jabber_unload_script", ""): - - weechat_version = int(w.info_get("version_number", "") or 0) - jabber_hook_commands_and_completions() - jabber_config_init() - jabber_config_read() - for server in jabber_servers: - if w.config_boolean(server.options['autoreconnect']): - server.ping() # This will connect and update ping status - server.add_ping_timer() - else: - if w.config_boolean(server.options['autoconnect']): - server.connect() - + return weechat.WEECHAT_RC_OK diff --git a/support_jabber.py b/support_jabber.py new file mode 100644 index 0000000..b61aa37 --- /dev/null +++ b/support_jabber.py @@ -0,0 +1,133 @@ +import os + +import weechat +w = weechat + +def LOG_debug(what, level='none'): + # if jabber_debug_enabled(): + w.prnt('', what) + +class ProxyWrapper(object): + def __init__(self, name): + self.proxy_name = name + self.proxy_string = "" + self.proxy_type = "" + self.proxy_address = "" + self.proxy_port = "" + self.proxy_user = "" + self.proxy_password = "" + self.has_proxy = False + + def configure(self): + if self.proxy_name: + self.proxy_string = "weechat.proxy.{}".format(self.proxy_name) + self.proxy_type = w.config_string( + w.config_get("{}.type".format(self.proxy_string)) + ) + if self.proxy_type in ['socks5', "http"]: + self.proxy_address = w.config_string( + w.config_get("{}.address".format(self.proxy_string)) + ) + self.proxy_port = w.config_integer( + w.config_get("{}.port".format(self.proxy_string)) + ) + self.proxy_user = w.config_string( + w.config_get("{}.username".format(self.proxy_string)) + ) + self.proxy_password = w.config_string( + w.config_get("{}.password".format(self.proxy_string)) + ) + self.has_proxy = True + else: + w_prnt( + "", + "\nWarning: weechat.network.proxy_curl is set to {} type (name: {}, conf string: {}). Only HTTP and SOCKS5 proxies are supported.\n\n".format( + self.proxy_type, self.proxy_name, self.proxy_string + ), + ) + + def curl(self): + if not self.has_proxy: + return "" + + if self.proxy_user and self.proxy_password: + user = "{}:{}@".format(self.proxy_user, self.proxy_password) + else: + user = "" + + if self.proxy_port: + port = ":{}".format(self.proxy_port) + else: + port = "" + + return "-x{}{}{}".format(user, self.proxy_address, port) + +def make_proxy(self, sproxy): + proxy = {} + if sproxy.startswith('socks'): + pair = sproxy + pair = pair.replace('socks5h://', '') + pair = pair.replace('socks5://', '') + pair = pair.replace('socks4://', '') + pair = pair.replace('socks://', '') + if ':' in pair: + phost, pport = pair.split(':', 1) + # '127.0.0.1' 9050 + proxy = {'host': phost, 'port': int(pport)} + elif sproxy.startswith('http'): + pair = sproxy + pair = pair.replace('https://', '') + pair = pair.replace('http://', '') + if ':' in pair: + phost, pport = pair.split(':', 1) + # '127.0.0.1' 9128 + proxy = {'host': phost, 'port': int(pport)} + return proxy + +def aget_proxy(self, ltypes=None): + if ltypes is None: + ltypes = ['http', 'socks5'] + aproxy = {} + elt = f"jabber.server.{self.name}.proxy" + stor = w.config_string(w.config_get(elt)) + if stor: + saddress = w.config_string(w.config_get(f"weechat.proxy.{stor}.address")) + iport = w.config_integer(w.config_get(f"weechat.proxy.{stor}.port")) + itype = w.config_integer(w.config_get(f"weechat.proxy.{stor}.type")) + atypes = {0: 'http', 1: 'socks', 2: 'socks'} + stype = atypes[itype] + if stype in ltypes: + aproxy = {'host': saddress, + 'port': iport, + 'type': stype, + } + LOG_debug(f"aget_proxy proxy {stor} {aproxy}") + return aproxy + sproxy = w.config_string(w.config_get("weechat.network.proxy_curl")) + if sproxy: + oproxy = ProxyWrapper(sproxy) + if oproxy.proxy_type in ltypes: + oproxy.configure() + aproxy = {'host': oproxy.proxy_address, + 'port': int(oproxy.proxy_port), + 'type': oproxy.proxy_type, + } + LOG_debug(f"aget_proxy proxy {sproxy} {aproxy}") + return aproxy + + if 'socks_proxy' in ltypes: + if os.environ.get('socks_proxy', ''): + sproxy = os.environ.get('socks_proxy', '') + aproxy = make_proxy(self, sproxy) + LOG_debug(f"proxy {aproxy}") + LOG_debug(f"aget_proxy proxy env={sproxy} {aproxy}") + return aproxy + if 'https_proxy' in ltypes: + sproxy = os.environ.get('https_proxy', '') + if sproxy: + aproxy = make_proxy(self, sproxy) + LOG_debug(f"proxy {aproxy}") + LOG_debug(f"aget_proxy proxy env={sproxy} {aproxy}") + return aproxy + + return aproxy