# HG changeset patch # User Piotr Zielinski # Date 1123113277 0 # Node ID 744c0708d11f0b0a64ba0d9de100b6f6c147c1dc # Parent bbe84acea03a6dc5b0968922f87ec9b0cbba9c98 [gaim-migrate @ 13303] gaim-remote.py implements the functionality of standard gaim-remote, but using DBus. It can also call all gaim functions exported via DBus. dbus-analize-function.py can now produce dbus bindings for GHashTable arguments. committer: Tailor Script diff -r bbe84acea03a -r 744c0708d11f src/Makefile.am --- a/src/Makefile.am Wed Aug 03 22:58:06 2005 +0000 +++ b/src/Makefile.am Wed Aug 03 23:54:37 2005 +0000 @@ -146,12 +146,12 @@ if ENABLE_DBUS -dbus_sources = dbus-server.c dbus-useful.c +dbus_sources = dbus-server.c dbus-useful.c dbus_headers = dbus-server.h dbus-useful.h dbus-maybe.h CLEANFILES = dbus-bindings.c -dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h connection.h conversation.h core.h roomlist.h +dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h connection.h conversation.h core.h roomlist.h status.h server.h dbus-bindings.c: dbus-analyze-functions.py $(dbus_exported) diff -r bbe84acea03a -r 744c0708d11f src/dbus-analyze-functions.py --- a/src/dbus-analyze-functions.py Wed Aug 03 22:58:06 2005 +0000 +++ b/src/dbus-analyze-functions.py Wed Aug 03 23:54:37 2005 +0000 @@ -39,6 +39,7 @@ # "gboolean" : ("i", "int") # } + simpletypes = ["int", "gint", "guint", "gboolean"] # for enum in file("dbus-auto-enums.txt"): @@ -47,7 +48,10 @@ # functions that shouldn't be exported excluded = ["gaim_accounts_load", "gaim_account_set_presence", - "gaim_conv_placement_get_fnc_id", "gaim_conv_placement_add_fnc"] + "gaim_conv_placement_get_fnc_id", "gaim_conv_placement_add_fnc", + "gaim_presence_add_list"] + +stringlists = [] pointer = "#pointer#" @@ -77,7 +81,7 @@ for decl in cdecls: print decl - print "dbus_message_get_args(message_DBUS, error_DBUS, ", + print "%s(message_DBUS, error_DBUS, " % argfunc, for param in cparams: print "DBUS_TYPE_%s, &%s," % param, print "DBUS_TYPE_INVALID);" @@ -105,13 +109,14 @@ functions.append((function, dparams)) def c_clear(): - global cparams, cdecls, ccode, cparamsout, ccodeout, dparams + global cparams, cdecls, ccode, cparamsout, ccodeout, dparams, argfunc dparams = "" cparams = [] cdecls = [] ccode = [] cparamsout = [] ccodeout = [] + argfunc = "dbus_message_get_args" def addstring(*items): @@ -137,22 +142,30 @@ # processing an input parameter def inputvar(mytype, name): - global ccode, cparams, cdecls + global ccode, cparams, cdecls, ccodeout, argfunc const = False if mytype[0] == "const": mytype = mytype[1:] const = True - # simple types (int, gboolean, etc.) and enums - if (len(mytype) == 1) and \ - ((mytype[0] in simpletypes) or (mytype[0].startswith("Gaim"))): - cdecls.append("dbus_int32_t %s;" % name) - cparams.append(("INT32", name)) - addintype("i", name) - return + + - # pointers ... + if len(mytype) == 1: + # simple types (int, gboolean, etc.) and enums + if (mytype[0] in simpletypes) or (mytype[0].startswith("Gaim")): + cdecls.append("dbus_int32_t %s;" % name) + cparams.append(("INT32", name)) + addintype("i", name) + return + # va_list, replace by NULL + if mytype[0] == "va_list": + cdecls.append("va_list %s;" % name); + ccode.append("%s = NULL;" % name); + return + + # pointers ... if (len(mytype) == 2) and (mytype[1] == pointer): # strings if mytype[0] == "char": @@ -165,6 +178,19 @@ else: raise myexception + # memory leak if an error occurs later ... + elif mytype[0] == "GHashTable": + argfunc = "gaim_dbus_message_get_args" + cdecls.append("DBusMessageIter %s_ITER;" % name) + cdecls.append("GHashTable *%s;" % name) + cparams.append(("ARRAY", "%s_ITER" % name)) + ccode.append("%s = gaim_dbus_iter_hash_table(&%s_ITER, error_DBUS);" \ + % (name, name)) + ccode.append("CHECK_ERROR(error_DBUS);") + ccodeout.append("g_hash_table_destroy(%s);" % name) + addintype("a{ss}", name) + return + # known object types are transformed to integer handles elif mytype[0].startswith("Gaim"): cdecls.append("dbus_int32_t %s_ID;" % name) @@ -190,18 +216,26 @@ # processing an output parameter -def outputvar(mytype, name, call): +def outputvar(mytype, name, call, function): # the "void" type is simple ... if mytype == ["void"]: ccode.append("%s;" % call) # just call the function return - # a constant string - if mytype == ["const", "char", pointer]: + const = False + if mytype[0] == "const": + mytype = mytype[1:] + const = True + + + # a string + if mytype == ["char", pointer]: cdecls.append("const char *%s;" % name) ccode.append("%s = null_to_empty(%s);" % (name, call)) cparamsout.append(("STRING", name)) addouttype("s", name) + if not const: + ccodeout.append("g_free(%s);" % name) return # simple types (ints, booleans, enums, ...) @@ -225,17 +259,31 @@ return # GList*, GSList*, assume that list is a list of objects - # not a list of strings!!! - # this does NOT release memory occupied by the list + + # fixme: at the moment, we do NOT free the memory occupied by + # the list, we should free it if the list has NOT been declared const + + # fixme: we assume that this is a list of objects, not a list + # of strings + if mytype[0] in ["GList", "GSList"]: cdecls.append("dbus_int32_t %s_LEN;" % name) - cdecls.append("dbus_int32_t *%s;" % name) - ccode.append("%s = gaim_dbusify_%s(%s, FALSE, &%s_LEN);" % \ - (name, mytype[0], call, name)) - cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &%s, %s_LEN" \ + ccodeout.append("g_free(%s);" % name) + + if function in stringlists: + cdecls.append("char **%s;" % name) + ccode.append("%s = gaim_%s_to_array(%s, FALSE, &%s_LEN);" % \ + (name, mytype[0], call, name)) + cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &%s, %s_LEN" \ % (name, name)) - ccodeout.append("g_free(%s);" % name) - addouttype("ai", name) + addouttype("as", name) + else: + cdecls.append("dbus_int32_t *%s;" % name) + ccode.append("%s = gaim_dbusify_%s(%s, FALSE, &%s_LEN);" % \ + (name, mytype[0], call, name)) + cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &%s, %s_LEN" \ + % (name, name)) + addouttype("ai", name) return raise myexception @@ -255,16 +303,21 @@ function = function.lower() names = [] + unnamed = 0 for param in paramlist: tokens = param.split() - if len(tokens) < 2: + if len(tokens) == 0: raise myexception - type, name = tokens[:-1], tokens[-1] + if (len(tokens) == 1) or (tokens[-1] == pointer): + unnamed += 1 + type, name = tokens, "param%i" % unnamed + else: + type, name = tokens[:-1], tokens[-1] inputvar(type, name) names.append(name) outputvar(functiontype, "RESULT", - "%s(%s)" % (origfunction, ", ".join(names))) + "%s(%s)" % (origfunction, ", ".join(names)), function) c_print(function) diff -r bbe84acea03a -r 744c0708d11f src/dbus-bindings.h --- a/src/dbus-bindings.h Wed Aug 03 22:58:06 2005 +0000 +++ b/src/dbus-bindings.h Wed Aug 03 23:54:37 2005 +0000 @@ -56,10 +56,40 @@ CHECK_ERROR(error); \ } G_STMT_END + +dbus_bool_t +gaim_dbus_message_get_args (DBusMessage *message, + DBusError *error, + int first_arg_type, + ...); +dbus_bool_t +gaim_dbus_message_get_args_valist (DBusMessage *message, + DBusError *error, + int first_arg_type, + va_list var_args); + +dbus_bool_t +gaim_dbus_message_iter_get_args (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + ...); + +dbus_bool_t +gaim_dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args); + dbus_int32_t* gaim_dbusify_GList(GList *list, gboolean free_memory, dbus_int32_t *len); dbus_int32_t* gaim_dbusify_GSList(GSList *list, gboolean free_memory, dbus_int32_t *len); +gpointer* gaim_GList_to_array(GList *list, gboolean free_memory, + dbus_int32_t *len); +gpointer* gaim_GSList_to_array(GSList *list, gboolean free_memory, + dbus_int32_t *len); +GHashTable *gaim_dbus_iter_hash_table(DBusMessageIter *iter, DBusError *error); + const char* empty_to_null(const char *str); const char* null_to_empty(const char *s); diff -r bbe84acea03a -r 744c0708d11f src/dbus-server.c --- a/src/dbus-server.c Wed Aug 03 22:58:06 2005 +0000 +++ b/src/dbus-server.c Wed Aug 03 23:54:37 2005 +0000 @@ -96,7 +96,7 @@ gint gaim_dbus_pointer_to_id(gpointer node) { gint id = GPOINTER_TO_INT(g_hash_table_lookup(map_node_id, node)); - g_return_val_if_fail(id, 0); + g_return_val_if_fail(id || node == NULL, 0); return id; } @@ -136,7 +136,109 @@ return ptr; } + + +/**************************************************************************/ +/** @name Modified versions of some DBus functions */ +/**************************************************************************/ + +dbus_bool_t +gaim_dbus_message_get_args (DBusMessage *message, + DBusError *error, + int first_arg_type, + ...) +{ + dbus_bool_t retval; + va_list var_args; + + va_start (var_args, first_arg_type); + retval = gaim_dbus_message_get_args_valist (message, error, first_arg_type, var_args); + va_end (var_args); + + return retval; +} + +dbus_bool_t +gaim_dbus_message_get_args_valist (DBusMessage *message, + DBusError *error, + int first_arg_type, + va_list var_args) +{ + DBusMessageIter iter; + + dbus_message_iter_init (message, &iter); + return gaim_dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args); +} + +dbus_bool_t +gaim_dbus_message_iter_get_args(DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + ...) +{ + dbus_bool_t retval; + va_list var_args; + + va_start (var_args, first_arg_type); + retval = gaim_dbus_message_iter_get_args_valist(iter, error, first_arg_type, var_args); + va_end (var_args); + + return retval; +} + +#define TYPE_IS_CONTAINER(typecode) \ + ((typecode) == DBUS_TYPE_STRUCT || \ + (typecode) == DBUS_TYPE_DICT_ENTRY || \ + (typecode) == DBUS_TYPE_VARIANT || \ + (typecode) == DBUS_TYPE_ARRAY) + + +dbus_bool_t +gaim_dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args) +{ + int spec_type, msg_type, i; + + spec_type = first_arg_type; + + for(i=0; spec_type != DBUS_TYPE_INVALID; i++) { + msg_type = dbus_message_iter_get_arg_type (iter); + + if (msg_type != spec_type) { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Argument %d is specified to be of type \"%i\", but " + "is actually of type \"%i\"\n", i, + spec_type, msg_type); + return FALSE; + } + + if (!TYPE_IS_CONTAINER(spec_type)) { + gpointer ptr; + ptr = va_arg (var_args, gpointer); + dbus_message_iter_get_basic(iter, ptr); + } + else { + DBusMessageIter *sub; + sub = va_arg (var_args, DBusMessageIter*); + dbus_message_iter_recurse(iter, sub); + g_print("subiter %i:%i\n", (int) sub, * (int*) sub); + break; /* for testing only! */ + } + spec_type = va_arg (var_args, int); + if (!dbus_message_iter_next(iter) && spec_type != DBUS_TYPE_INVALID) { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Message has only %d arguments, but more were expected", i); + return FALSE; + } + } + return TRUE; +} + + + /**************************************************************************/ /** @name Useful functions */ /**************************************************************************/ @@ -155,9 +257,6 @@ return ""; } - - - dbus_int32_t* gaim_dbusify_GList(GList *list, gboolean free_memory, dbus_int32_t *len) { @@ -194,6 +293,78 @@ return array; } +gpointer* gaim_GList_to_array(GList *list, gboolean free_memory, + dbus_int32_t *len) +{ + gpointer *array; + int i; + GList *elem; + + *len = g_list_length(list); + array = g_new0(gpointer, g_list_length(list)); + for(i = 0, elem = list; elem != NULL; elem = elem->next, i++) + array[i] = elem->data; + + if (free_memory) + g_list_free(list); + + return array; +} + +gpointer* gaim_GSList_to_array(GSList *list, gboolean free_memory, + dbus_int32_t *len) +{ + gpointer *array; + int i; + GSList *elem; + + *len = g_slist_length(list); + array = g_new0(gpointer, g_slist_length(list)); + for(i = 0, elem = list; elem != NULL; elem = elem->next, i++) + array[i] = elem->data; + + if (free_memory) + g_slist_free(list); + + return array; +} + +GHashTable *gaim_dbus_iter_hash_table(DBusMessageIter *iter, DBusError *error) { + GHashTable *hash; + + /* we do not need to destroy strings because they are part of the message */ + hash = g_hash_table_new(g_str_hash, g_str_equal); + + do { + char *key, *value; + DBusMessageIter subiter; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) + goto error; /* With all due respect to Dijkstra, + this goto is for exception + handling, and it is ok because it + avoids duplication of the code + responsible for destroying the hash + table. Exceptional instructions + for exceptional situations. */ + + dbus_message_iter_recurse(iter, &subiter); + if (!gaim_dbus_message_iter_get_args(&subiter, error, + DBUS_TYPE_STRING, &key, + DBUS_TYPE_STRING, &value, + DBUS_TYPE_INVALID)) + goto error; /* same here */ + + g_hash_table_insert(hash, key, value); + } while (dbus_message_iter_next(iter)); + + return hash; + + error: + g_hash_table_destroy(hash); + return NULL; +} + /**************************************************************/ /* DBus bindings ... */ /**************************************************************/ @@ -260,7 +431,9 @@ } -/* Introspection */ +/**************************************************************************/ +/** @name Signals */ +/**************************************************************************/ static const char *gettext(const char **ptr) { const char *text = *ptr; @@ -307,9 +480,9 @@ type = gettext(&text); name = gettext(&text); - g_string_append_printf(str, - "\n", - name, type, direction); + g_string_append_printf(str, + "\n", + name, type, direction); } g_string_append(str, "\n"); } @@ -535,7 +708,3 @@ } -/* Introspection support */ - - - diff -r bbe84acea03a -r 744c0708d11f src/dbus-useful.c --- a/src/dbus-useful.c Wed Aug 03 22:58:06 2005 +0000 +++ b/src/dbus-useful.c Wed Aug 03 23:54:37 2005 +0000 @@ -1,4 +1,9 @@ +#include +#include + #include "conversation.h" +#include "util.h" + GaimAccount * @@ -46,3 +51,25 @@ } +/* DBusMessage *gaim_account_set_status_DBUS(DBusMessage *message_DBUS, DBusError *error_DBUS) */ +/* { */ +/* DBusMessage *reply; */ +/* DBusMessageIter iter; */ + +/* dbus_int32_t account, active; */ +/* char *status_id; */ + +/* dbus_message_iter_init(message, &iter); */ +/* const char *name; */ +/* const char *protocol; */ + +/* dbus_message_get_args(message_DBUS, error_DBUS, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &protocol, DBUS_TYPE_INVALID); */ +/* CHECK_ERROR(error_DBUS); */ +/* NULLIFY(name); */ +/* NULLIFY(protocol); */ +/* GAIM_DBUS_POINTER_TO_ID(RESULT, gaim_accounts_find_any(name, protocol), error_DBUS); */ +/* reply_DBUS = dbus_message_new_method_return (message_DBUS); */ +/* dbus_message_append_args(reply_DBUS, DBUS_TYPE_INT32, &RESULT, DBUS_TYPE_INVALID); */ +/* return reply_DBUS; */ + +/* } */ diff -r bbe84acea03a -r 744c0708d11f src/gaim-remote.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gaim-remote.py Wed Aug 03 23:54:37 2005 +0000 @@ -0,0 +1,157 @@ +#!/usr/bin/python + +import dbus +import re +import urllib +import sys + +import xml.dom.minidom + +xml.dom.minidom.Element.all = xml.dom.minidom.Element.getElementsByTagName + +obj = dbus.SessionBus().get_object("org.gaim.GaimService", "/org/gaim/GaimObject") +gaim = dbus.Interface(obj, "org.gaim.GaimInterface") + +class CheckedObject: + def __init__(self, obj): + self.obj = obj + + def __getattr__(self, attr): + return CheckedAttribute(self, attr) + +class CheckedAttribute: + 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 "Error: " + self.attr + " " + str(args) + " returned " + str(result) + return result + +cgaim = CheckedObject(gaim) + +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 execute(uri): + match = re.match(urlregexp, uri) + protocol = match.group(2) + 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] = urllib.unquote(value) + + accountname = params.get("account", "") + + if command == "goim": + account = cgaim.GaimAccountsFindConnected(accountname, protocol) + conversation = cgaim.GaimConversationNew(1, account, params["screenname"]) + if "message" in params: + im = cgaim.GaimConversationGetImData(conversation) + gaim.GaimConvImSend(im, params["message"]) + return None + + elif command == "gochat": + account = cgaim.GaimAccountsFindConnected(accountname, protocol) + connection = cgaim.GaimAccountGetConnection(account) + return gaim.ServJoinChat(connection, params) + + elif command == "addbuddy": + account = cgaim.GaimAccountsFindConnected(accountname, protocol) + return cgaim.GaimBlistRequestAddBuddy(account, params["screenname"], + params.get("group", ""), "") + + elif command == "setstatus": + if "account" in params: + accounts = [cgaim.GaimAccountsFindConnected(accountname, protocol)] + else: + accounts = gaim.GaimAccountsGetAllActive() + + for account in accounts: + status = gaim.GaimAccountGetStatus(account, params["status"]) + for key, value in params.items(): + if key not in ["state", "account"]: + gaim.GaimStatusSetAttrString(status, key, value) + gaim.GaimAccountSetStatusVargs(account, params["status"], 1) + return None + + elif command == "quit": + return gaim.GaimCoreQuit() + + 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 gaim.__getattr__(name)(*fargs) + else: + # introspect the object to get parameter names and types + # this is slow because the entire introspection info must be downloaded + data = dbus.Interface(obj, "org.freedesktop.DBus.Introspectable").\ + 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 "Don't know how to handle type \"%s\"" % type + return gaim.__getattr__(command)(*methodparams) + raise "Unknown command: %s" % command + +def example_code_do_not_call(): + execute("jabber:addbuddy?screenname=friend") + execute("setstatus?status=away&message=don't disturb") + + account = execute("GaimAccountsFindConnected?name=&protocol=") + execute("GaimConversationNew?type=1&account=%i&name=testone@localhost" % account) + + execute("jabber:addbuddy?screenname=friend") + execute("jabber:goim?screenname=testone@localhost&message=hi") + + execute("jabber:gochat?room=TestRoom&server=conference.localhost") + execute("jabber:goim?screenname=testone@localhost&message=hi") + + + + + +for arg in sys.argv[1:]: + print execute(arg) + + diff -r bbe84acea03a -r 744c0708d11f src/status.c --- a/src/status.c Wed Aug 03 22:58:06 2005 +0000 +++ b/src/status.c Wed Aug 03 23:54:37 2005 +0000 @@ -26,6 +26,7 @@ #include "blist.h" #include "core.h" +#include "dbus-maybe.h" #include "debug.h" #include "notify.h" #include "prefs.h" @@ -221,6 +222,7 @@ g_return_val_if_fail(name != NULL, NULL); status_type = g_new0(GaimStatusType, 1); + GAIM_DBUS_REGISTER_POINTER(status_type, GaimStatusType); status_type->primitive = primitive; status_type->id = g_strdup(id); @@ -296,6 +298,7 @@ g_list_free(status_type->attrs); } + GAIM_DBUS_UNREGISTER_POINTER(status_type); g_free(status_type); } @@ -504,6 +507,7 @@ g_return_val_if_fail(value_type != NULL, NULL); attr = g_new0(GaimStatusAttr, 1); + GAIM_DBUS_REGISTER_POINTER(attr, GaimStatusAttr); attr->id = g_strdup(id); attr->name = g_strdup(name); @@ -522,6 +526,7 @@ gaim_value_destroy(attr->value_type); + GAIM_DBUS_UNREGISTER_POINTER(attr); g_free(attr); } @@ -563,6 +568,7 @@ g_return_val_if_fail(presence != NULL, NULL); status = g_new0(GaimStatus, 1); + GAIM_DBUS_REGISTER_POINTER(status, GaimStatus); status->type = status_type; status->presence = presence; @@ -599,6 +605,7 @@ g_hash_table_destroy(status->attr_values); + GAIM_DBUS_UNREGISTER_POINTER(status); g_free(status); } @@ -1066,6 +1073,7 @@ g_return_val_if_fail(context != GAIM_PRESENCE_CONTEXT_UNSET, NULL); presence = g_new0(GaimPresence, 1); + GAIM_DBUS_REGISTER_POINTER(presence, GaimPresence); presence->context = context; @@ -1174,6 +1182,7 @@ g_hash_table_destroy(presence->status_table); + GAIM_DBUS_UNREGISTER_POINTER(presence); g_free(presence); }