diff options
author | Gary Kramlich <grim@reaperworld.com> | 2018-11-15 23:05:20 -0600 |
---|---|---|
committer | Gary Kramlich <grim@reaperworld.com> | 2018-11-15 23:05:20 -0600 |
commit | e4aa76a07e1acb2803b40433932b6fe0c39610ba (patch) | |
tree | 7642ec686d626a3c9cdae76f0d03904f4510a467 | |
parent | 9e7afb6baec4254cc53cd6dd107b71d848e829d9 (diff) | |
download | pidgin-e4aa76a07e1acb2803b40433932b6fe0c39610ba.tar.gz |
Get the rest of the stragglers
-rw-r--r-- | libpurple/data/purple3-url-handler.desktop.in.in | 14 | ||||
-rwxr-xr-x | libpurple/purple3-remote | 266 | ||||
-rwxr-xr-x | libpurple/purple3-send | 31 | ||||
-rwxr-xr-x | libpurple/purple3-send-async | 29 | ||||
-rwxr-xr-x | libpurple/purple3-url-handler | 373 | ||||
-rw-r--r-- | pidgin/data/im.pidgin.Pidgin3.appdata.xml.in (renamed from pidgin/data/im.pidgin.Pidgin.appdata.xml.in) | 0 | ||||
-rw-r--r-- | pidgin/data/im.pidgin.Pidgin3.desktop.in.in (renamed from pidgin/data/im.pidgin.Pidgin.desktop.in.in) | 0 | ||||
-rw-r--r-- | pidgin/meson.build | 6 |
8 files changed, 716 insertions, 3 deletions
diff --git a/libpurple/data/purple3-url-handler.desktop.in.in b/libpurple/data/purple3-url-handler.desktop.in.in new file mode 100644 index 0000000000..09513fa627 --- /dev/null +++ b/libpurple/data/purple3-url-handler.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +Name=Pidgin +GenericName=Internet Messenger +Comment=Chat over IM. Supports AIM, Google Talk, Jabber/XMPP, and more +Exec=purple3-url-handler %u +TryExec=purple3-url-handler +Icon=pidgin +StartupNotify=false +Terminal=false +NoDisplay=true +Type=Application +Categories=Network;InstantMessaging;RemoteAccess;ConsoleOnly; +MimeType=x-scheme-handler/aim;x-scheme-handler/gg;x-scheme-handler/icq;x-scheme-handler/irc;x-scheme-handler/msnim;x-scheme-handler/sip;x-scheme-handler/xmpp;x-scheme-handler/ymsgr +@USES_MM_CHAT_SECTION@ diff --git a/libpurple/purple3-remote b/libpurple/purple3-remote new file mode 100755 index 0000000000..1f392af129 --- /dev/null +++ b/libpurple/purple3-remote @@ -0,0 +1,266 @@ +#!/usr/bin/env python + +from __future__ import absolute_import, division, print_function + +import codecs +import re +import sys +try: + from urllib.parse import unquote +except ImportError: + from urllib import unquote +import xml.dom.minidom + +import dbus + + +sys.stdin = codecs.getwriter('utf-8')(sys.stdin) +sys.stdout = codecs.getwriter('utf-8')(sys.stdout) + +xml.dom.minidom.Element.all = xml.dom.minidom.Element.getElementsByTagName + +obj = None +try: + obj = dbus.SessionBus().get_object("im.pidgin.purple.PurpleService", + "/im/pidgin/purple/PurpleObject") +except: + pass + +purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface") + + +class CheckedObject(object): + def __init__(self, obj): + self.obj = obj + + def __getattr__(self, attr): + return CheckedAttribute(self, attr) + + +class CheckedAttribute(object): + def __init__(self, cobj, attr): + self.cobj = cobj + self.attr = attr + + def __call__(self, *args): + result = self.cobj.obj.__getattr__(self.attr)(*args) + if result == 0: + raise Exception("Error: %s %s returned %s" % + (self.attr, args, result)) + return result + + +def show_help(requested=False): + print("""This program uses D-Bus to communicate with purple. + +Usage: + + %s "command1" "command2" ... + +Each command is of one of the three types: + + [protocol:]commandname?param1=value1¶m2=value2&... + FunctionName?param1=value1¶m2=value2&... + FunctionName(value1,value2,...) + +The second and third form are provided for completeness but their use +is not recommended; use purple-send or purple-send-async instead. The +second form uses introspection to find out the parameter names and +their types, therefore it is rather slow. + +Examples of commands: + + jabber:goim?screenname=testone@localhost&message=hi + jabber:gochat?room=TestRoom&server=conference.localhost + jabber:getinfo?screenname=testone@localhost + jabber:addbuddy?screenname=my friend + + setstatus?status=away&message=don't disturb + getstatus + getstatusmessage + quit + + PurpleAccountsFindConnected?name=&protocol=jabber + PurpleAccountsFindConnected(,jabber) +""" % (sys.argv[0], )) + if (requested): + sys.exit(0) + else: + sys.exit(1) + + +cpurple = CheckedObject(purple) + +urlregexp = r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?" + + +def extendlist(list, length, fill): + if len(list) < length: + return list + [fill] * (length - len(list)) + else: + return list + + +def convert(value): + try: + return int(value) + except: + return value + + +def findaccount(accountname, protocolname): + try: + # prefer connected accounts + account = cpurple.PurpleAccountsFindConnected(accountname, + protocolname) + return account + except: + # try to get any account and connect it + account = cpurple.PurpleAccountsFindAny(accountname, protocolname) + purple.PurpleAccountSetStatusVargs(account, "online", 1) + purple.PurpleAccountConnect(account) + return account + + +def execute(uri): + match = re.match(urlregexp, uri) + protocol = match.group(2) + if protocol == "xmpp": + protocol = "jabber" + if protocol is not None: + protocol = "prpl-" + protocol + command = match.group(5) + paramstring = match.group(7) + params = {} + if paramstring is not None: + for param in paramstring.split("&"): + key, value = extendlist(param.split("=", 1), 2, "") + params[key] = unquote(value) + + accountname = params.get("account", "") + + if command == "goim": + account = findaccount(accountname, protocol) + conversation = cpurple.PurpleConversationNew(1, account, + params["screenname"]) + if "message" in params: + im = cpurple.PurpleConversationGetImData(conversation) + purple.PurpleConvImSend(im, params["message"]) + return None + + elif command == "gochat": + account = findaccount(accountname, protocol) + connection = cpurple.PurpleAccountGetConnection(account) + return purple.PurpleServJoinChat(connection, params) + + elif command == "addbuddy": + account = findaccount(accountname, protocol) + return cpurple.PurpleBlistRequestAddBuddy(account, + params["screenname"], + params.get("group", ""), + "") + + elif command == "setstatus": + current = purple.PurpleSavedstatusGetCurrent() + + if "status" in params: + status_id = params["status"] + status_type = purple.PurplePrimitiveGetTypeFromId(status_id) + else: + status_type = purple.PurpleSavedstatusGetType(current) + status_id = purple.PurplePrimitiveGetIdFromType(status_type) + + if "message" in params: + message = params["message"] + else: + message = purple.PurpleSavedstatusGetMessage(current) + + if "account" in params: + accounts = [cpurple.PurpleAccountsFindAny(accountname, protocol)] + + for account in accounts: + status = purple.PurpleAccountGetStatus(account, status_id) + type = purple.PurpleStatusGetType(status) + purple.PurpleSavedstatusSetSubstatus(current, account, type, + message) + purple.PurpleSavedstatusActivateForAccount(current, account) + else: + saved = purple.PurpleSavedstatusNew("", status_type) + purple.PurpleSavedstatusSetMessage(saved, message) + purple.PurpleSavedstatusActivate(saved) + + return None + + elif command == "getstatus": + current = purple.PurpleSavedstatusGetCurrent() + status_type = purple.PurpleSavedstatusGetType(current) + status_id = purple.PurplePrimitiveGetIdFromType(status_type) + return status_id + + elif command == "getstatusmessage": + current = purple.PurpleSavedstatusGetCurrent() + return purple.PurpleSavedstatusGetMessage(current) + + elif command == "getinfo": + account = findaccount(accountname, protocol) + connection = cpurple.PurpleAccountGetConnection(account) + return purple.ServGetInfo(connection, params["screenname"]) + + elif command == "quit": + return purple.PurpleCoreQuit() + + elif command == "uri": + return None + + else: + match = re.match(r"(\w+)\s*\(([^)]*)\)", command) + if match is not None: + name = match.group(1) + argstr = match.group(2) + if argstr == "": + args = [] + else: + args = argstr.split(",") + fargs = [] + for arg in args: + fargs.append(convert(arg.strip())) + return purple.__getattr__(name)(*fargs) + else: + # Introspect the object to get parameter names and types. This is + # slow because the entire introspection info must be downloaded. + interface = dbus.Interface(obj, + "org.freedesktop.DBus.Introspectable") + data = interface.Introspect() + introspect = xml.dom.minidom.parseString(data).documentElement + for method in introspect.all("method"): + if command == method.getAttribute("name"): + methodparams = [] + for arg in method.all("arg"): + if arg.getAttribute("direction") == "in": + value = params[arg.getAttribute("name")] + type = arg.getAttribute("type") + if type == "s": + methodparams.append(value) + elif type == "i": + methodparams.append(int(value)) + else: + raise Exception( + "Don't know how to handle type \"%s\"" % ( + type, )) + return purple.__getattr__(command)(*methodparams) + show_help() + + +if len(sys.argv) == 1: + show_help() +elif sys.argv[1] == "--help" or sys.argv[1] == "-h": + show_help(True) +elif obj is None: + print("No existing libpurple instance detected.") + sys.exit(1) + +for arg in sys.argv[1:]: + output = execute(arg) + + if output is not None: + print(output) diff --git a/libpurple/purple3-send b/libpurple/purple3-send new file mode 100755 index 0000000000..8eec0b0516 --- /dev/null +++ b/libpurple/purple3-send @@ -0,0 +1,31 @@ +#!/bin/sh + +METHOD_NAME=$1 + +if test -z "$METHOD_NAME" +then + cat <<EOF +This program calls purple API functions using DBus and prints the return value. +If you are not interested in the return value, use purple-send-async. + +Usage: + + $0 method-name type1:parameter1 type2:parameter2 ... + +This shell script just invokes dbus-send, see man dbus-send for how +to specify the parameters. + +Examples: + + $0 PurpleAccountsFindConnected string: string:jabber + $0 PurpleAccountsGetAll + $0 PurpleCoreQuit + +Use dbus-viewer to get the list of supported functions and their parameters. +EOF + exit 1 +fi + +shift +dbus-send --dest=im.pidgin.purple.PurpleService --print-reply --type=method_call /im/pidgin/purple/PurpleObject im.pidgin.purple.PurpleInterface.$METHOD_NAME "$@" + diff --git a/libpurple/purple3-send-async b/libpurple/purple3-send-async new file mode 100755 index 0000000000..c2e1ab64f6 --- /dev/null +++ b/libpurple/purple3-send-async @@ -0,0 +1,29 @@ +#!/bin/sh + +METHOD_NAME=$1 + +if test -z "$METHOD_NAME" +then + cat <<EOF +This program calls purple API functions using DBus. As opposed to purple-send, +it does not print the return value. + +Usage: + + $0 method-name type1:parameter1 type2:parameter2 ... + +This shell script just invokes dbus-send, see man dbus-send for how +to specify the parameters. + +Examples: + + $0 PurpleCoreQuit + +Use dbus-viewer to get the list of supported functions and their parameters. +EOF + exit 1 +fi + +shift +dbus-send --dest=im.pidgin.purple.PurpleService --type=method_call /im/pidgin/purple/PurpleObject im.pidgin.purple.PurpleInterface.$METHOD_NAME "$@" + diff --git a/libpurple/purple3-url-handler b/libpurple/purple3-url-handler new file mode 100755 index 0000000000..227ea57075 --- /dev/null +++ b/libpurple/purple3-url-handler @@ -0,0 +1,373 @@ +#!/usr/bin/env python + +from __future__ import absolute_import, division, print_function + +import dbus +import re +import sys +import time +try: + from urllib.parse import unquote_plus +except ImportError: + from urllib import unquote_plus + + +bus = dbus.SessionBus() +obj = None +try: + obj = bus.get_object("im.pidgin.purple.PurpleService", + "/im/pidgin/purple/PurpleObject") +except dbus.DBusException as e: + if e._dbus_error_name == "org.freedesktop.DBus.Error.ServiceUnknown": + print("Error: no libpurple-powered client is running. " + "Try starting Pidgin or Finch.") + sys.exit(1) +purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface") + + +class CheckedObject(object): + def __init__(self, obj): + self.obj = obj + + def __getattr__(self, attr): + return CheckedAttribute(self, attr) + + +class CheckedAttribute(object): + def __init__(self, cobj, attr): + self.cobj = cobj + self.attr = attr + + def __call__(self, *args): + # Redirect stderr to suppress the printing of an " Introspect error" + # message if nothing is listening on the bus. We print a friendly + # error message ourselves. + real_stderr = sys.stderr + sys.stderr = None + result = self.cobj.obj.__getattr__(self.attr)(*args) + sys.stderr = real_stderr + +# This can be useful for debugging. +# if result == 0: +# print("Error:", self.attr, str(args), "returned", str(result)) + + return result + + +cpurple = CheckedObject(purple) + + +def extendlist(list, length, fill): + if len(list) < length: + return list + [fill] * (length - len(list)) + else: + return list + + +def convert(value): + try: + return int(value) + except: + return value + + +def account_not_found(): + print("No matching account found.") + sys.exit(1) + + +def bring_account_online(account): + if not cpurple.PurpleAccountIsConnected(account): + # The last argument is meant to be a GList * but the D-Bus binding + # generator thing just wants a UInt32, which is pretty failing. + # Happily, passing a 0 to mean an empty list turns out to work anyway. + purple.PurpleAccountSetStatusList(account, "online", 1, 0) + purple.PurpleAccountConnect(account) + + +def findaccount(protocolname, accountname="", matcher=None): + if matcher: + for account in cpurple.PurpleAccountsGetAll(): + if protocolname != cpurple.PurpleAccountGetProtocolId(account): + continue + if (accountname != "" and + accountname != cpurple.PurpleAccountGetUsername(account)): + continue + if matcher(account): + bring_account_online(account) + return account + account_not_found() + + # prefer connected accounts + account = cpurple.PurpleAccountsFindConnected(accountname, protocolname) + if account != 0: + return account + + # try to get any account and connect it + account = cpurple.PurpleAccountsFindAny(accountname, protocolname) + if account == 0: + account_not_found() + + bring_account_online(account) + return account + + +def goim(account, screenname, message=None): + # XXX: 1 == PURPLE_CONV_TYPE_IM + conversation = cpurple.PurpleConversationNew(1, account, screenname) + if message: + purple.PurpleConvSendConfirm(conversation, message) + + +def gochat(account, params, message=None): + connection = cpurple.PurpleAccountGetConnection(account) + purple.PurpleServJoinChat(connection, params) + + if message is not None: + for i in range(20): + # XXX: 2 == PURPLE_CONV_TYPE_CHAT + conversation = purple.PurpleFindConversationWithAccount( + 2, + params.get("channel", params.get("room")), + account) + if conversation: + purple.PurpleConvSendConfirm(conversation, message) + break + else: + time.sleep(0.5) + + +def addbuddy(account, screenname, group="", alias=""): + cpurple.PurpleBlistRequestAddBuddy(account, screenname, group, alias) + + +def aim(uri): + protocol = "prpl-aim" + match = re.match(r"^aim:([^?]*)(\?(.*))", uri) + if not match: + print("Invalid aim URI: %s" % (uri, )) + return + + command = unquote_plus(match.group(1)) + paramstring = match.group(3) + params = {} + if paramstring: + for param in paramstring.split("&"): + key, value = extendlist(param.split("=", 1), 2, "") + params[key] = unquote_plus(value) + accountname = params.get("account", "") + screenname = params.get("screenname", "") + + account = findaccount(protocol, accountname) + + if command.lower() == "goim": + goim(account, screenname, params.get("message")) + elif command.lower() == "gochat": + gochat(account, params) + elif command.lower() == "addbuddy": + addbuddy(account, screenname, params.get("group", "")) + + +def gg(uri): + protocol = "prpl-gg" + match = re.match(r"^gg:(.*)", uri) + if not match: + print("Invalid gg URI: %s" % (uri, )) + return + + screenname = unquote_plus(match.group(1)) + account = findaccount(protocol) + goim(account, screenname) + + +def icq(uri): + protocol = "prpl-icq" + match = re.match(r"^icq:([^?]*)(\?(.*))", uri) + if not match: + print("Invalid icq URI: %s" % (uri, )) + return + + command = unquote_plus(match.group(1)) + paramstring = match.group(3) + params = {} + if paramstring: + for param in paramstring.split("&"): + key, value = extendlist(param.split("=", 1), 2, "") + params[key] = unquote_plus(value) + accountname = params.get("account", "") + screenname = params.get("screenname", "") + + account = findaccount(protocol, accountname) + + if command.lower() == "goim": + goim(account, screenname, params.get("message")) + elif command.lower() == "gochat": + gochat(account, params) + elif command.lower() == "addbuddy": + addbuddy(account, screenname, params.get("group", "")) + + +def irc(uri): + protocol = "prpl-irc" + match = re.match(r"^irc:(//([^/]*))?/?([^?]*)(\?(.*))?", uri) + if not match: + print("Invalid irc URI: %s" % (uri, )) + return + + server = unquote_plus(match.group(2) or "") + target = match.group(3) or "" + query = match.group(5) or "" + + modifiers = {} + if target: + for modifier in target.split(",")[1:]: + modifiers[modifier] = True + + paramstring = match.group(5) + params = {} + if paramstring: + for param in paramstring.split("&"): + key, value = extendlist(param.split("=", 1), 2, "") + params[key] = unquote_plus(value) + + def correct_server(account): + username = cpurple.PurpleAccountGetUsername(account) + return (server == "" or + "@" in username and server == username.split("@")[1]) + + account = findaccount(protocol, matcher=correct_server) + + if target != "": + if "isnick" in modifiers: + goim(account, unquote_plus(target.split(",")[0]), + params.get("msg")) + else: + channel = unquote_plus(target.split(",")[0]) + if channel[0] != "#": + channel = "#" + channel + gochat(account, + {"server": server, + "channel": channel, + "password": params.get("key", "")}, + params.get("msg")) + + +def sip(uri): + protocol = "prpl-simple" + match = re.match(r"^sip:(.*)", uri) + if not match: + print("Invalid sip URI: %s" % (uri, )) + return + + screenname = unquote_plus(match.group(1)) + account = findaccount(protocol) + goim(account, screenname) + + +def xmpp(uri): + protocol = "prpl-jabber" + match = re.match( + r"^xmpp:(//([^/?#]*)/?)?([^?#]*)(\?([^;#]*)(;([^#]*))?)?(#(.*))?", + uri) + if not match: + print("Invalid xmpp URI: %s" % (uri, )) + return + + tmp = match.group(2) + if tmp: + accountname = unquote_plus(tmp) + else: + accountname = "" + + screenname = unquote_plus(match.group(3)) + + tmp = match.group(5) + if tmp: + command = unquote_plus(tmp) + else: + command = "" + + paramstring = match.group(7) + params = {} + if paramstring: + for param in paramstring.split(";"): + key, value = extendlist(param.split("=", 1), 2, "") + params[key] = unquote_plus(value) + + account = findaccount(protocol, accountname) + + if command.lower() == "message": + goim(account, screenname, params.get("body")) + elif command.lower() == "join": + room, server = screenname.split("@") + gochat(account, {"room": room, "server": server}) + elif command.lower() == "roster": + addbuddy(account, screenname, params.get("group", ""), + params.get("name", "")) + else: + goim(account, screenname) + + +def gtalk(uri): + protocol = "prpl-jabber" + match = re.match(r"^gtalk:([^?]*)(\?(.*))", uri) + if not match: + print("Invalid gtalk URI: %s" % (uri, )) + return + + command = unquote_plus(match.group(1)) + paramstring = match.group(3) + params = {} + if paramstring: + for param in paramstring.split("&"): + key, value = extendlist(param.split("=", 1), 2, "") + params[key] = unquote_plus(value) + accountname = params.get("from_jid", "") + jid = params.get("jid", "") + + account = findaccount(protocol, accountname) + + if command.lower() == "chat": + goim(account, jid) + elif command.lower() == "call": + # XXX V&V prompt to establish call + goim(account, jid) + + +def main(argv=sys.argv): + if len(argv) != 2 or argv[1] == "--help" or argv[1] == "-h": + print("Usage: %s URI" % (argv[0], )) + print("Example: %s \"xmpp:romeo@montague.net?message\"" % (argv[0], )) + + if len(argv) != 2: + sys.exit(1) + else: + return 0 + + uri = argv[1] + type = uri.split(":")[0] + + try: + if type == "aim": + aim(uri) + elif type == "gg": + gg(uri) + elif type == "icq": + icq(uri) + elif type == "irc": + irc(uri) + elif type == "sip": + sip(uri) + elif type == "xmpp": + xmpp(uri) + elif type == "gtalk": + gtalk(uri) + else: + print("Unknown protocol: %s" % (type, )) + except dbus.DBusException as e: + print("Error: %s" % (str(e), )) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/pidgin/data/im.pidgin.Pidgin.appdata.xml.in b/pidgin/data/im.pidgin.Pidgin3.appdata.xml.in index dcc77bc72f..dcc77bc72f 100644 --- a/pidgin/data/im.pidgin.Pidgin.appdata.xml.in +++ b/pidgin/data/im.pidgin.Pidgin3.appdata.xml.in diff --git a/pidgin/data/im.pidgin.Pidgin.desktop.in.in b/pidgin/data/im.pidgin.Pidgin3.desktop.in.in index 7bc5ef7f00..7bc5ef7f00 100644 --- a/pidgin/data/im.pidgin.Pidgin.desktop.in.in +++ b/pidgin/data/im.pidgin.Pidgin3.desktop.in.in diff --git a/pidgin/meson.build b/pidgin/meson.build index cd98eee6d9..f228065cfc 100644 --- a/pidgin/meson.build +++ b/pidgin/meson.build @@ -199,7 +199,7 @@ if ENABLE_GTK variables : ['plugindir=${libdir}/pidgin']) if INSTALL_I18N - DESKTOP_FILE = 'im.pidgin.Pidgin.desktop' + DESKTOP_FILE = 'im.pidgin.Pidgin3.desktop' desktop_file_in = configure_file( input : 'data/' + DESKTOP_FILE + '.in.in', output : DESKTOP_FILE + '.in', @@ -213,8 +213,8 @@ if ENABLE_GTK install_dir : get_option('datadir') + '/applications') appdata = i18n.merge_file( - input : 'data/im.pidgin.Pidgin.appdata.xml.in', - output : 'im.pidgin.Pidgin.appdata.xml', + input : 'data/im.pidgin.Pidgin3.appdata.xml.in', + output : 'im.pidgin.Pidgin3.appdata.xml', po_dir : meson.source_root() + '/po', install : true, install_dir : get_option('datadir') + '/metainfo') |