gambas-source-code/gb.dbus/helper.c

1537 lines
33 KiB
C

/***************************************************************************
helper.c
(c) 2000-2017 Benoît Minisini <gambas@users.sourceforge.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#define __HELPER_C
#include "c_dbusvariant.h"
#include "c_dbusobserver.h"
#include "dbus_print_message.h"
#include "helper.h"
//#define DEBUG_ARG
typedef
void (*RETRIEVE_CALLBACK)(GB_TYPE type, void *data, void *param);
typedef
struct {
GB_COLLECTION col;
char *key;
}
COLLECTION_ADD;
bool DBUS_Debug = FALSE;
static int _watch_count = 0;
static void handle_message(int fd, int type, DBusConnection *connection)
{
//fprintf(stdout, "handle_message\n");
do
{
dbus_connection_read_write_dispatch(connection, -1);
}
while (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS);
}
static void check_message_now(DBusConnection *connection)
{
if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
handle_message(-1, 0, connection);
}
static void check_message(DBusConnection *connection)
{
GB.Post((GB_CALLBACK)check_message_now, (intptr_t)connection);
}
/***************************************************************************
Method signatures have the following syntax:
<ARG-1><ARG-2>...<ARG-n>
where <ARG-i> is the DBus signature of the i-th input argument. All
arguments are in order.
The signature of one argument can be a simple type:
y BYTE Byte
b BOOLEAN Boolean
n INT16 Short
q UINT16 Short
i INT32 Integer
u UINT32 Integer
x INT64 Long
t UINT64 Long
d DOUBLE Float
s STRING String
o OBJECT_PATH String
g SIGNATURE String
v VARIANT Variant
Or a compound type (T is any datatype):
aT ARRAY Array
(TT...) STRUCT Variant[]
{TT} DICT_ENTRY Collection
***************************************************************************/
typedef
struct {
const char *name;
const char *dbus;
}
CONV_TYPE;
CONV_TYPE _conv_type[] =
{
{ "DBusObject", "o" },
{ "Collection", "a{sv}" },
{ "Boolean[]", "ab" },
{ "Byte[]", "ay" },
{ "Short[]", "an" },
{ "Integer[]", "ai" },
{ "Long[]", "ax" },
{ "Single[]", "ad" },
{ "Float[]", "ad" },
{ "Date[]", "ad" },
{ "Pointer[]", "ax" },
{ "String[]", "as" },
{ "Variant[]", "av" },
{ "DBusObject[]", "ao" },
{ NULL }
};
static const char *to_dbus_type(GB_VALUE *arg)
{
CONV_TYPE *p;
GB_TYPE type, atype;
int a;
const char *dtype;
char *result;
switch(arg->type)
{
case GB_T_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING;
case GB_T_BYTE: return DBUS_TYPE_BYTE_AS_STRING;
case GB_T_SHORT: return DBUS_TYPE_INT16_AS_STRING;
case GB_T_INTEGER: return DBUS_TYPE_INT32_AS_STRING;
case GB_T_LONG: return DBUS_TYPE_INT64_AS_STRING;
case GB_T_POINTER: return DBUS_TYPE_INT64_AS_STRING;
case GB_T_SINGLE: return DBUS_TYPE_DOUBLE_AS_STRING;
case GB_T_FLOAT: return DBUS_TYPE_DOUBLE_AS_STRING;
case GB_T_STRING: return DBUS_TYPE_STRING_AS_STRING;
default: break;
}
if (arg->type >= GB_T_OBJECT)
{
//fprintf(stderr, "%s\n", GB.GetClassName((void *)arg->type));
for (p = _conv_type; p->name; p++)
{
if (GB.FindClass(p->name) == arg->type)
return p->dbus;
}
if (GB.Is(arg->_object.value, GB.FindClass("Array")))
{
type = arg->type;
a = 0;
for(;;)
{
atype = GB.GetArrayType(type);
if (atype <= GB_T_OBJECT)
break;
type = atype;
a++;
}
dtype = "v";
for (p = _conv_type; p->name; p++)
{
if (GB.FindClass(p->name) == type)
{
dtype = p->dbus;
break;
}
}
result = NULL;
while (a--)
result = GB.AddChar(result, 'a');
result = GB.AddString(result, dtype, strlen(dtype));
GB.FreeStringLater(result);
return result;
}
if (GB.Is(arg->_object.value, CLASS_DBusVariant))
return "v";
if (GB.Is(arg->_object.value, CLASS_DBusNull))
return "s";
return NULL;
}
return "v";
}
static char *array_from_dbus_type(const char *signature)
{
static char type[DBUS_MAXIMUM_SIGNATURE_LENGTH + 1];
DBusSignatureIter siter;
dbus_signature_iter_init(&siter, signature);
switch (dbus_signature_iter_get_current_type(&siter))
{
case DBUS_TYPE_BYTE: return "Byte[]";
case DBUS_TYPE_BOOLEAN: return "Boolean[]";
case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: return "Short[]";
case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: return "Integer[]";
case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: return "Long[]";
case DBUS_TYPE_DOUBLE: return "Float[]";
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE: return "String[]";
case DBUS_TYPE_VARIANT: return "Variant[]";
case DBUS_TYPE_DICT_ENTRY:
if (signature[1] == 's')
return "Collection";
else
return NULL;
case DBUS_TYPE_ARRAY:
{
DBusSignatureIter siter_contents;
char *type_contents;
char *sign_contents;
dbus_signature_iter_recurse(&siter, &siter_contents);
sign_contents = dbus_signature_iter_get_signature(&siter_contents);
type_contents = array_from_dbus_type(sign_contents);
dbus_free(sign_contents);
if (!type_contents)
return NULL;
if (type_contents != type)
strcpy(type, type_contents);
GB.GetArrayClass(GB.FindClass(type));
strcat(type, "[]");
return type;
}
default:
return "Variant[]";
}
}
static GB_TYPE from_dbus_type(const char *signature)
{
DBusSignatureIter siter;
dbus_signature_iter_init(&siter, signature);
switch (dbus_signature_iter_get_current_type(&siter))
{
case DBUS_TYPE_BYTE: return GB_T_BYTE;
case DBUS_TYPE_BOOLEAN: return GB_T_BOOLEAN;
case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: return GB_T_SHORT;
case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: return GB_T_INTEGER;
case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: return GB_T_LONG;
case DBUS_TYPE_DOUBLE: return GB_T_FLOAT;
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE: return GB_T_STRING;
case DBUS_TYPE_ARRAY:
{
DBusSignatureIter siter_contents;
char *type;
char *sign_contents;
dbus_signature_iter_recurse(&siter, &siter_contents);
sign_contents = dbus_signature_iter_get_signature(&siter_contents);
type = array_from_dbus_type(sign_contents);
dbus_free(sign_contents);
if (type)
return GB.FindClass(type);
else
return GB_T_VARIANT;
}
case DBUS_TYPE_STRUCT:
{
DBusSignatureIter siter_contents;
char *atype;
GB_TYPE type, type2;
char *sign_contents;
dbus_signature_iter_recurse(&siter, &siter_contents);
sign_contents = dbus_signature_iter_get_signature(&siter_contents);
atype = array_from_dbus_type(sign_contents);
dbus_free(sign_contents);
if (atype)
type = GB.FindClass(atype);
else
return GB.FindClass("Variant[]");
while (dbus_signature_iter_next(&siter_contents))
{
sign_contents = dbus_signature_iter_get_signature(&siter_contents);
atype = array_from_dbus_type(sign_contents);
dbus_free(sign_contents);
if (atype)
type2 = GB.FindClass(atype);
else
return GB.FindClass("Variant[]");
if (type != type2)
return GB.FindClass("Variant[]");
}
return type;
}
case DBUS_TYPE_VARIANT:
default: return GB_T_VARIANT;
}
}
static bool append_arg(DBusMessageIter *iter, const char *signature, GB_VALUE *arg)
{
DBusSignatureIter siter;
int type;
GB_TYPE gtype;
char *sign;
GB_VALUE rarg;
GB_VALUE targ;
if (arg->type == GB_T_VARIANT)
GB.Conv(arg, arg->_variant.value.type);
dbus_signature_iter_init(&siter, signature);
type = dbus_signature_iter_get_current_type(&siter);
sign = dbus_signature_iter_get_signature(&siter);
gtype = from_dbus_type(sign);
dbus_free(sign);
if (gtype == GB_T_NULL)
{
GB.Error("Unsupported datatype for signature '&1'", sign);
goto __UNSUPPORTED;
}
else if (gtype != GB_T_VARIANT)
{
// The contents of arg must not be modified, because it may have to be freed later by the caller
if (arg->type >= GB_T_OBJECT)
{
if (GB.Is(arg->_object.value, CLASS_DBusNull))
{
targ.type = GB_T_NULL;
GB.ReleaseValue(arg);
arg = &targ;
}
else if (GB.Is(arg->_object.value, CLASS_DBusObject) && type == DBUS_TYPE_OBJECT_PATH)
{
GB_VALUE *val = GB.GetProperty(arg->_object.value, "_Path");
if (val && (val->type == GB_T_STRING || val->type != GB_T_CSTRING))
{
targ = *val;
targ.type = GB_T_CSTRING;
// The value returned by GB.GetProperty() is not referenced, so it must not be freed.
GB.BorrowValue(&targ);
GB.ReleaseValue(arg);
arg = &targ;
}
}
else if (GB.Is(arg->_object.value, CLASS_DBusVariant))
{
CDBUSVARIANT *dbusvariant = (CDBUSVARIANT *)arg->_object.value;
targ.type = GB_T_VARIANT;
targ._variant.value = dbusvariant->value;
GB.BorrowValue(&targ);
GB.ReleaseValue(arg);
arg = &targ;
}
}
if (GB.Conv(arg, gtype))
{
//BREAKPOINT();
GB.ReleaseValue(arg);
GB.Error("Type mismatch");
return TRUE;
}
}
switch(type)
{
case DBUS_TYPE_BYTE:
{
unsigned char val;
val = arg->_integer.value;
dbus_message_iter_append_basic(iter, type, &val);
#ifdef DEBUG_ARG
fprintf(stderr, "%d ", (int)val);
#endif
break;
}
case DBUS_TYPE_BOOLEAN:
{
dbus_bool_t val;
val = arg->_boolean.value ? 1 : 0;
dbus_message_iter_append_basic(iter, type, &val);
#ifdef DEBUG_ARG
fprintf(stderr, "%s ", val ? "true" : "false");
#endif
break;
}
case DBUS_TYPE_INT16:
case DBUS_TYPE_UINT16:
{
dbus_int16_t val;
val = arg->_integer.value;
dbus_message_iter_append_basic(iter, type, &val);
#ifdef DEBUG_ARG
fprintf(stderr, "%d ", (int)val);
#endif
break;
}
case DBUS_TYPE_INT32:
case DBUS_TYPE_UINT32:
{
dbus_int32_t val;
val = arg->_integer.value;
dbus_message_iter_append_basic(iter, type, &val);
#ifdef DEBUG_ARG
fprintf(stderr, "%d ", val);
#endif
break;
}
case DBUS_TYPE_INT64:
case DBUS_TYPE_UINT64:
{
dbus_int64_t val;
val = arg->_long.value;
dbus_message_iter_append_basic(iter, type, &val);
#ifdef DEBUG_ARG
fprintf(stderr, "%" PRId64 " ", val);
#endif
break;
}
case DBUS_TYPE_DOUBLE:
{
double val;
val = arg->_float.value;
dbus_message_iter_append_basic(iter, type, &val);
#ifdef DEBUG_ARG
fprintf(stderr, "%g ", val);
#endif
break;
}
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE:
{
const char *str;
if (arg->type == GB_T_NULL)
str = "";
else
str = GB.ToZeroString((GB_STRING *)arg);
if (type == DBUS_TYPE_OBJECT_PATH && DBUS_validate_path(str, -1))
{
GB.Error("Invalid object path: &1", str);
goto __ERROR;
}
dbus_message_iter_append_basic(iter, type, &str);
#ifdef DEBUG_ARG
fprintf(stderr, "\"%s\" ", str);
#endif
break;
}
case DBUS_TYPE_ARRAY:
{
DBusMessageIter citer;
DBusMessageIter dict_entry_iter;
int i;
GB_VALUE value;
const char *contents_signature = &signature[1];
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, contents_signature, &citer);
if (contents_signature[0] == '{' && contents_signature[1] == 's')
{
GB_COLLECTION col = (GB_COLLECTION)(arg->_object.value);
char *key;
int len;
#ifdef DEBUG_ARG
fprintf(stderr, "{ ");
#endif
if (col)
{
GB_COLLECTION_ITER iter;
GB.Collection.Enum(col, &iter, NULL, NULL, NULL);
for(;;)
{
if (GB.Collection.Enum(col, &iter, (GB_VARIANT *)&value, &key, &len))
break;
key = GB.TempString(key, len);
dbus_message_iter_open_container(&citer, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter);
dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key);
#ifdef DEBUG_ARG
fprintf(stderr, "%s: ", key);
#endif
GB.BorrowValue(&value);
if (append_arg(&dict_entry_iter, &contents_signature[2], &value))
goto __ERROR;
dbus_message_iter_close_container(&citer, &dict_entry_iter);
}
}
#ifdef DEBUG_ARG
fprintf(stderr, "} ");
#endif
}
else
{
GB_ARRAY array = (GB_ARRAY)(arg->_object.value);
#ifdef DEBUG_ARG
fprintf(stderr, "[ ");
#endif
if (array)
{
for (i = 0; i < GB.Array.Count(array); i++)
{
value.type = GB.Array.Type(array);
GB.ReadValue(&value, GB.Array.Get(array, i), value.type);
GB.BorrowValue(&value);
if (append_arg(&citer, contents_signature, &value))
goto __ERROR;
}
}
}
dbus_message_iter_close_container(iter, &citer);
#ifdef DEBUG_ARG
fprintf(stderr, "] ");
#endif
break;
}
case DBUS_TYPE_STRUCT:
{
GB_ARRAY array;
DBusMessageIter citer;
DBusSignatureIter siter_contents;
int i;
GB_VALUE value;
array = (GB_ARRAY)(arg->_object.value);
dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &citer);
#ifdef DEBUG_ARG
fprintf(stderr, "( ");
#endif
dbus_signature_iter_recurse(&siter, &siter_contents);
if (array)
{
for (i = 0; i < GB.Array.Count(array); i++)
{
value.type = GB.Array.Type(array);
GB.ReadValue(&value, GB.Array.Get(array, i), value.type);
GB.BorrowValue(&value);
sign = dbus_signature_iter_get_signature(&siter_contents);
if (append_arg(&citer, sign, &value))
goto __ERROR_SIGN;
dbus_free(sign);
dbus_signature_iter_next(&siter_contents);
}
}
dbus_message_iter_close_container(iter, &citer);
#ifdef DEBUG_ARG
fprintf(stderr, ") ");
#endif
break;
}
case DBUS_TYPE_VARIANT:
{
DBusMessageIter citer;
const char *contents_signature;
#ifdef DEBUG_ARG
fprintf(stderr, "< ");
#endif
if (arg->type >= GB_T_OBJECT && GB.Is(arg->_object.value, CLASS_DBusVariant))
{
CDBUSVARIANT *dbusvariant = (CDBUSVARIANT *)arg->_object.value;
rarg.type = GB_T_VARIANT;
rarg._variant.value = dbusvariant->value;
contents_signature = CDBUSVARIANT_get_signature(dbusvariant);
//fprintf(stderr, "#1: %s\n", contents_signature);
GB.BorrowValue(&rarg);
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, contents_signature, &citer);
if (append_arg(&citer, contents_signature, &rarg))
goto __ERROR;
dbus_message_iter_close_container(iter, &citer);
}
else
{
contents_signature = to_dbus_type(arg);
//fprintf(stderr, "#3: %s\n", contents_signature);
if (!contents_signature)
{
GB.Error("Unsupported datatype in variant value");
goto __UNSUPPORTED;
}
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, contents_signature, &citer);
GB.BorrowValue(arg);
if (append_arg(&citer, contents_signature, arg))
goto __ERROR;
dbus_message_iter_close_container(iter, &citer);
}
#ifdef DEBUG_ARG
fprintf(stderr, "> ");
#endif
break;
}
default:
GB.Error("Unsupported signature: &1", sign);
goto __UNSUPPORTED;
}
GB.ReleaseValue(arg);
return FALSE;
__UNSUPPORTED:
//GB.Error("Unsupported datatype");
goto __ERROR;
__ERROR_SIGN:
dbus_free(sign);
__ERROR:
GB.ReleaseValue(arg);
return TRUE;
}
static void return_value_cb(GB_TYPE type, void *value, void *param)
{
if (type == GB_T_STRING)
type = GB_T_CSTRING;
GB.ReturnPtr(type, value);
}
static void add_value_cb(GB_TYPE type, void *value, void *param)
{
GB_ARRAY array = (GB_ARRAY)param;
GB_VALUE val;
if (type == GB_T_STRING)
type = GB_T_CSTRING;
GB.ReadValue(&val, value, type);
GB.BorrowValue(&val);
GB.Conv(&val, GB.Array.Type(array));
GB.Store(GB.Array.Type(array), &val, GB.Array.Add(array));
GB.ReleaseValue(&val);
}
static void add_collection_cb(GB_TYPE type, void *value, void *param)
{
COLLECTION_ADD *add = (COLLECTION_ADD *)param;
GB_VALUE val;
if (type == GB_T_STRING)
type = GB_T_CSTRING;
GB.ReadValue(&val, value, type);
GB.BorrowValue(&val);
GB.Conv(&val, GB_T_VARIANT);
//GB.Store(GB.Array.Type(array), &val, GB.Array.Add(array));
GB.Collection.Set(add->col, add->key, strlen(add->key), (GB_VARIANT *)&val);
GB.ReleaseValue(&val);
}
static bool retrieve_arg(DBusMessageIter *iter, RETRIEVE_CALLBACK cb, void *param)
{
char *signature = dbus_message_iter_get_signature(iter);
GB_TYPE gtype = from_dbus_type(signature);
int type = dbus_message_iter_get_arg_type(iter);
#ifdef DEBUG_ARG
fprintf(stderr, "retrieve_arg: signature = '%s'", signature);
if (gtype > GB_T_OBJECT) fprintf(stderr, " / %s", GB.GetClassName((void *)gtype));
fprintf(stderr, "\n");
#endif
dbus_free(signature);
if (dbus_type_is_basic(type))
{
#ifdef DEBUG_ARG
fprintf(stderr, "-> basic type\n");
#endif
char val[16];
dbus_message_iter_get_basic(iter, (void *)val);
(*cb)(gtype, val, param);
#ifdef DEBUG_ARG
fprintf(stderr, "<- basic type\n");
#endif
return FALSE;
}
switch(type)
{
case DBUS_TYPE_VARIANT:
{
DBusMessageIter iter_contents;
#ifdef DEBUG_ARG
fprintf(stderr, "-> variant\n");
#endif
dbus_message_iter_recurse(iter, &iter_contents);
if (retrieve_arg(&iter_contents, cb, param))
return TRUE;
#ifdef DEBUG_ARG
fprintf(stderr, "<- variant\n");
#endif
return FALSE;
}
case DBUS_TYPE_ARRAY:
case DBUS_TYPE_STRUCT:
{
char *signature_contents;
DBusMessageIter iter_contents;
DBusMessageIter dict_entry_contents;
dbus_message_iter_recurse(iter, &iter_contents);
signature_contents = dbus_message_iter_get_signature(&iter_contents);
if (signature_contents[0] == '{' && signature_contents[1] == 's')
{
GB_COLLECTION col;
COLLECTION_ADD add;
#ifdef DEBUG_ARG
fprintf(stderr, "-> collection\n");
#endif
GB.Collection.New(POINTER(&col), FALSE);
add.col = col;
for(;;)
{
if (dbus_message_iter_get_arg_type(&iter_contents) == DBUS_TYPE_INVALID)
break;
dbus_message_iter_recurse(&iter_contents, &dict_entry_contents);
// key
dbus_message_iter_get_basic(&dict_entry_contents, &add.key);
dbus_message_iter_next(&dict_entry_contents);
// value
if (dbus_message_iter_get_arg_type(&dict_entry_contents) != DBUS_TYPE_INVALID)
{
if (retrieve_arg(&dict_entry_contents, add_collection_cb, &add))
return TRUE;
}
dbus_message_iter_next(&iter_contents);
}
(*cb)(gtype, &col, param);
#ifdef DEBUG_ARG
fprintf(stderr, "<- collection\n");
#endif
return FALSE;
}
else
{
GB_ARRAY array = GB.New(gtype, NULL, NULL);
#ifdef DEBUG_ARG
fprintf(stderr, (type == DBUS_TYPE_ARRAY) ? "-> array" : "-> struct");
fprintf(stderr, " '%s' / %s\n", signature_contents, GB.GetClassName((void *)array));
#endif
dbus_free(signature_contents);
while (dbus_message_iter_get_arg_type(&iter_contents) != DBUS_TYPE_INVALID)
{
if (retrieve_arg(&iter_contents, add_value_cb, array))
return TRUE;
dbus_message_iter_next(&iter_contents);
}
(*cb)(gtype, &array, param);
#ifdef DEBUG_ARG
fprintf(stderr, (type == DBUS_TYPE_ARRAY) ? "<- array\n" : "<- struct\n");
#endif
return FALSE;
}
}
}
GB.Error("Unsupported DBus datatype");
return TRUE;
}
static bool define_arguments(DBusMessage *message, const char *signature, GB_ARRAY arguments)
{
int nparam;
int n;
GB_TYPE type;
GB_VALUE value;
DBusMessageIter iter;
DBusSignatureIter siter;
char *sign;
char *arg_sign;
bool err = FALSE;
if (arguments)
{
nparam = GB.Array.Count(arguments);
type = GB.Array.Type(arguments);
}
else
{
nparam = 0;
type = GB_T_NULL;
}
#ifdef DEBUG_ARG
fprintf(stderr, "define_arguments: %s %d %ld\n", signature, nparam, type);
#endif
if (!signature || !*signature)
{
if (nparam > 0)
{
GB.Error("Too many arguments");
return TRUE;
}
else
return FALSE;
}
sign = GB.NewZeroString(signature);
dbus_message_iter_init_append(message, &iter);
dbus_signature_iter_init(&siter, sign);
for (n = 0; n < nparam; n++)
{
value.type = type;
GB.ReadValue(&value, GB.Array.Get(arguments, n), type);
GB.BorrowValue(&value);
arg_sign = dbus_signature_iter_get_signature(&siter);
#ifdef DEBUG_ARG
fprintf(stderr, "[%d] %s: ", n, arg_sign);
#endif
err = append_arg(&iter, arg_sign, &value);
#ifdef DEBUG_ARG
fprintf(stderr, "\n");
#endif
dbus_free(arg_sign);
if (err)
goto __RETURN;
if (!dbus_signature_iter_next(&siter))
{
if (n < (nparam - 1))
{
GB.Error("Too many arguments");
err = TRUE;
}
goto __RETURN;
}
}
GB.Error("Not enough arguments");
err = TRUE;
__RETURN:
GB.FreeString(&sign);
return err;
}
bool DBUS_call_method(DBusConnection *connection, const char *application, const char *path, const char *interface, const char *method,
const char *signature_in, const char *signature_out, GB_ARRAY arguments)
{
DBusMessage *message;
//int n;
DBusMessageIter iter;
DBusMessage *reply;
DBusError error;
//DBusSignatureIter siter;
bool ret;
//GB_TYPE type;
//int nparam;
message = dbus_message_new_method_call(application, path, interface, method);
if (!message)
{
GB.Error("Couldn't allocate D-Bus message");
return TRUE;
}
ret = TRUE;
dbus_message_set_auto_start(message, TRUE);
if (define_arguments(message, signature_in, arguments))
goto __RETURN;
// Do not use asynchronous call, otherwise error message is lost.
/*if (!signature_out || !*signature_out)
{
dbus_connection_send(connection, message, NULL);
dbus_connection_flush(connection);
reply = NULL;
ret = FALSE;
}*/
dbus_error_init(&error);
reply = dbus_connection_send_with_reply_and_block(connection, message, -1, &error);
check_message(connection);
if (dbus_error_is_set(&error))
{
GB.Error("&1: &2", error.name, error.message);
if (reply)
dbus_message_unref(reply);
dbus_error_free(&error);
goto __RETURN;
}
if (!reply)
goto __RETURN;
dbus_message_iter_init(reply, &iter);
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INVALID)
{
GB.ReturnNull();
ret = FALSE;
}
else if (!dbus_message_iter_has_next(&iter))
{
ret = retrieve_arg(&iter, return_value_cb, NULL);
}
else
{
GB_ARRAY result;
GB.Array.New(POINTER(&result), GB_T_VARIANT, 0);
do
{
if (retrieve_arg(&iter, add_value_cb, result))
{
GB.Unref(&result);
goto __RETURN;
}
}
while (dbus_message_iter_next(&iter));
GB.ReturnObject(result);
ret = FALSE;
}
dbus_message_unref(reply);
__RETURN:
dbus_message_unref(message);
return ret;
}
bool DBUS_send_signal(DBusConnection *connection, const char *path, const char *interface, const char *signal, const char *signature_in, GB_ARRAY arguments)
{
DBusMessage *message;
bool ret;
message = dbus_message_new_signal(path, interface, signal);
if (!message)
{
GB.Error("Couldn't allocate D-Bus message");
return TRUE;
}
ret = TRUE;
//dbus_message_set_auto_start(message, TRUE);
if (define_arguments(message, signature_in, arguments))
goto __RETURN;
//dbus_error_init(&error);
dbus_connection_send(connection, message, NULL);
//dbus_connection_flush(connection);
check_message(connection);
ret = FALSE;
__RETURN:
dbus_message_unref(message);
return ret;
}
bool DBUS_retrieve_message_arguments(DBusMessage *message)
{
DBusMessageIter iter;
dbus_message_iter_init(message, &iter);
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INVALID)
{
GB.ReturnNull();
return FALSE;
}
else
{
GB_ARRAY result;
GB.Array.New(POINTER(&result), GB_T_VARIANT, 0);
do
{
if (retrieve_arg(&iter, add_value_cb, result))
{
GB.Unref(&result);
return TRUE;
}
}
while(dbus_message_iter_next(&iter));
GB.ReturnObject(result);
return FALSE;
}
}
bool DBUS_reply(DBusConnection *connection, DBusMessage *message, const char *signature, GB_ARRAY arguments)
{
bool ret = TRUE;
DBusMessage *reply;
//DBusError error;
dbus_uint32_t serial = 0;
reply = dbus_message_new_method_return(message);
if (signature && *signature && arguments)
{
if (define_arguments(reply, signature, arguments))
goto __RETURN;
}
if (!dbus_connection_send(connection, reply, &serial))
{
GB.Error("Cannot send reply");
goto __RETURN;
}
dbus_connection_flush(connection);
check_message(connection);
ret = FALSE;
__RETURN:
dbus_message_unref(reply);
return ret;
}
bool DBUS_error(DBusConnection *connection, DBusMessage *message, const char *type, const char *error)
{
bool ret = TRUE;
DBusMessage *reply;
dbus_uint32_t serial = 0;
char *full_type;
if (!error) error = "";
if (!type)
full_type = DBUS_ERROR_FAILED;
else
{
full_type = GB.NewZeroString("org.freedesktop.org.DBus.Error.");
full_type = GB.AddString(full_type, type, 0);
}
reply = dbus_message_new_error(message, full_type, error);
if (!dbus_connection_send(connection, reply, &serial))
{
GB.Error("Cannot send error");
goto __RETURN;
}
dbus_connection_flush(connection);
check_message(connection);
ret = FALSE;
__RETURN:
dbus_message_unref(reply);
return ret;
}
/*static const char *type_to_name (int message_type)
{
switch (message_type)
{
case DBUS_MESSAGE_TYPE_SIGNAL:
return "signal";
case DBUS_MESSAGE_TYPE_METHOD_CALL:
return "method call";
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
return "method return";
case DBUS_MESSAGE_TYPE_ERROR:
return "error";
default:
return "(unknown message type)";
}
}*/
char *DBUS_introspect(DBusConnection *connection, const char *application, const char *path)
{
DBusMessage *message;
DBusMessage *reply;
DBusError error;
DBusMessageIter iter;
int type;
char *signature = NULL;
message = dbus_message_new_method_call(application, path, "org.freedesktop.DBus.Introspectable", "Introspect");
if (!message)
{
GB.Error("Couldn't allocate D-Bus message");
return NULL;
}
dbus_message_set_auto_start(message, TRUE);
dbus_error_init(&error);
reply = dbus_connection_send_with_reply_and_block(connection, message, -1, &error);
check_message(connection);
if (dbus_error_is_set(&error))
{
GB.Error("&1: &2", error.name, error.message);
dbus_error_free(&error);
goto __RETURN;
}
if (!reply)
goto __RETURN;
dbus_message_iter_init(reply, &iter);
type = dbus_message_iter_get_arg_type(&iter);
if (type == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&iter, &signature);
dbus_message_unref (reply);
__RETURN:
dbus_message_unref(message);
return signature;
}
static bool check_filter(char *rule, const char *value)
{
if (!rule || !*rule || (rule[0] == '*' && rule[1] == 0))
return FALSE;
return (strcasecmp(rule, value) != 0);
}
static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *message, void *user_data)
{
CDBUSOBSERVER *obs;
bool found = FALSE;
for (obs = DBUS_observers; obs; obs = obs->next)
{
if (obs->type != dbus_message_get_type(message))
continue;
// Beware: "" means "only me" in DBusObserver, but everything there!
if (check_filter(obs->destination, dbus_message_get_destination(message)))
continue;
if (check_filter(obs->object, dbus_message_get_path(message)))
continue;
if (check_filter(obs->member, dbus_message_get_member(message)))
continue;
if (check_filter(obs->interface, dbus_message_get_interface(message)))
continue;
//print_message(message, FALSE);
found = TRUE;
obs->message = message;
obs->reply = FALSE;
DBUS_raise_observer(obs);
obs->message = NULL;
if (obs->reply)
return DBUS_HANDLER_RESULT_HANDLED;
}
if (!found && DBUS_Debug)
{
fprintf(stderr, "gb.dbus: warning: unhandled message: ");
print_message(message, FALSE);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
static bool get_socket(DBusConnection *connection, int *socket)
{
if (!dbus_connection_get_socket(connection, socket))
{
GB.Error("Unable to get DBus connection socket");
return TRUE;
}
else
return FALSE;
}
bool DBUS_watch(DBusConnection *connection, bool on)
{
int socket;
if (get_socket(connection, &socket))
return TRUE;
if (on)
{
if (_watch_count == 0)
{
if (!dbus_connection_add_filter(connection, filter_func, NULL, NULL))
{
GB.Error("Unable to watch the DBus connection");
return TRUE;
}
if (DBUS_Debug)
fprintf(stderr, "gb.dbus: start watching connection\n");
GB.Watch(socket, GB_WATCH_READ, (void *)handle_message, (intptr_t)connection);
check_message(connection);
}
_watch_count++;
}
else
{
_watch_count--;
if (_watch_count == 0)
{
if (DBUS_Debug)
fprintf(stderr, "gb.dbus: stop watching connection\n");
GB.Watch(socket, GB_WATCH_NONE, (void *)handle_message, (intptr_t)connection);
}
}
return FALSE;
}
bool DBUS_register(DBusConnection *connection, const char *name, bool unique)
{
DBusError error;
int ret;
dbus_error_init(&error);
//fprintf(stderr, "DBUS_register: %s / unique = %d\n", name, unique);
ret = dbus_bus_request_name(connection, name, unique ? DBUS_NAME_FLAG_DO_NOT_QUEUE : 0, &error);
if (dbus_error_is_set(&error))
{
GB.Error("Unable to register application name: &1", error.message);
dbus_error_free(&error);
return TRUE;
}
if (unique && ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
return TRUE;
return DBUS_watch(connection, TRUE);
}
bool DBUS_unregister(DBusConnection *connection, const char *name)
{
DBusError error;
dbus_error_init(&error);
dbus_bus_release_name(connection, name, &error);
if (dbus_error_is_set(&error))
{
GB.Error("Unable to unregister application name: &1", error.message);
dbus_error_free(&error);
return TRUE;
}
return DBUS_watch(connection, FALSE);
}
/***************************************************************************
Validation routines taken directly from the D-Bus source code.
***************************************************************************/
/**
* Determine wether the given character is valid as the first character
* in a name.
*/
#define VALID_INITIAL_NAME_CHARACTER(c) \
( ((c) >= 'A' && (c) <= 'Z') || \
((c) >= 'a' && (c) <= 'z') || \
((c) == '_') )
/**
* Determine wether the given character is valid as a second or later
* character in a name
*/
#define VALID_NAME_CHARACTER(c) \
( ((c) >= '0' && (c) <= '9') || \
((c) >= 'A' && (c) <= 'Z') || \
((c) >= 'a' && (c) <= 'z') || \
((c) == '_') )
bool DBUS_validate_path(const char *path, int len)
{
const unsigned char *s;
const unsigned char *end;
const unsigned char *last_slash;
if (len <= 0)
len = strlen(path);
s = (const unsigned char *)path;
end = s + (uint)len;
if (*s != '/')
return TRUE;
last_slash = s;
++s;
while (s != end)
{
if (*s == '/')
{
if ((s - last_slash) < 2)
return TRUE; /* no empty path components allowed */
last_slash = s;
}
else
{
if (!VALID_NAME_CHARACTER (*s))
return TRUE;
}
++s;
}
if ((end - last_slash) < 2 && len > 1)
return TRUE; /* trailing slash not allowed unless the string is "/" */
return FALSE;
}
bool DBUS_validate_interface (const char *interface, int len)
{
const unsigned char *s;
const unsigned char *end;
const unsigned char *last_dot;
if (!interface)
return FALSE;
if (len <= 0)
len = strlen(interface);
if (len > DBUS_MAXIMUM_NAME_LENGTH || len == 0)
return TRUE;
last_dot = NULL;
s = (const unsigned char *)interface;
end = s + (uint)len;
/* check special cases of first char so it doesn't have to be done
* in the loop. Note we know len > 0
*/
if (*s == '.') /* disallow starting with a . */
return TRUE;
else if (!VALID_INITIAL_NAME_CHARACTER (*s))
return TRUE;
else
++s;
while (s != end)
{
if (*s == '.')
{
if ((s + 1) == end)
return TRUE;
else if (!VALID_INITIAL_NAME_CHARACTER (*(s + 1)))
return TRUE;
last_dot = s;
++s; /* we just validated the next char, so skip two */
}
else if (!VALID_NAME_CHARACTER (*s))
{
return TRUE;
}
++s;
}
if (last_dot == NULL)
return TRUE;
return FALSE;
}
bool DBUS_validate_method(const char *method, int len)
{
const unsigned char *s;
const unsigned char *end;
if (len <= 0)
len = strlen(method);
if (len > DBUS_MAXIMUM_NAME_LENGTH || len == 0)
return TRUE;
s = (const unsigned char *)method;
end = s + (uint)len;
/* check special cases of first char so it doesn't have to be done
* in the loop. Note we know len > 0
*/
if (!VALID_INITIAL_NAME_CHARACTER (*s))
return TRUE;
else
++s;
while (s != end)
{
if (!VALID_NAME_CHARACTER (*s))
{
return TRUE;
}
++s;
}
return FALSE;
}
GB_ARRAY DBUS_split_signature(const char *signature)
{
GB_ARRAY result;
DBusSignatureIter siter;
char *sign;
GB.Array.New(POINTER(&result), GB_T_STRING, 0);
dbus_signature_iter_init(&siter, signature);
for(;;)
{
sign = dbus_signature_iter_get_signature(&siter);
*(char **)GB.Array.Add(result) = GB.NewZeroString(sign);
dbus_free(sign);
if (!dbus_signature_iter_next(&siter))
break;
}
return result;
}