diff options
Diffstat (limited to 'src/realmd/rdcp_dbus.c')
-rw-r--r-- | src/realmd/rdcp_dbus.c | 2050 |
1 files changed, 2050 insertions, 0 deletions
diff --git a/src/realmd/rdcp_dbus.c b/src/realmd/rdcp_dbus.c new file mode 100644 index 0000000..b05e5c9 --- /dev/null +++ b/src/realmd/rdcp_dbus.c @@ -0,0 +1,2050 @@ +#include <konkret/konkret.h> + +#include "rdcp_error.h" +#include "realm-dbus-constants.h" + +#include "rdcp_dbus.h" +#include "rdcp_util.h" + +/*----------------------------------------------------------------------------*/ + +DBusConnection* system_bus = NULL; + +/*----------------------------------------------------------------------------*/ +#ifdef TRACE_VARIANT +static const char * +dbus_type_to_string(int type); +#endif + +static GError * +dbus_error_to_gerror(DBusError *dbus_error); + +static const char* +dbus_msg_type_to_string (int message_type); + +static void +dbus_message_print_indent(GString *string, int depth); + +static void +dbus_message_print_hex(GString *string, unsigned char *bytes, unsigned int len, int depth); + +static void +dbus_message_print_byte_array(GString *string, DBusMessageIter *iter, int depth); + +static void +dbus_message_print_iter(GString *string, DBusMessageIter *iter, int depth); + +static GString * +dbus_message_print_string(DBusMessage *message, GString *string, dbus_bool_t show_header); + +static gchar * +dbus_message_print(DBusMessage *message, GString *string, dbus_bool_t show_header); + +static gboolean +append_g_variant_to_dbus_msg_iter(DBusMessageIter *iter, GVariant *value, GError **g_error); + +static gboolean +append_g_variant_to_dbus_message(DBusMessage *message, GVariant *g_variant, GError **g_error); + +static gboolean +dbus_method_append_args_tuple(DBusMessage *message, GVariant *args, GError **g_error); + +static gboolean +marshal_dbus_string_variant(DBusMessageIter *iter, const char *value, GError **g_error); + +static gboolean +marshal_dbus_dict_string_entry(DBusMessageIter *array, const char *name, const char *value, GError **g_error); + +static gboolean +dbus_iter_to_variant(DBusMessageIter *iter, GVariant **g_variant_return, GError **g_error); + +static gboolean +dbus_message_to_g_variant(DBusMessage *msg, GVariant **g_variant_return, GError **g_error); + +static gboolean +dbus_method_reply_to_g_variant_tuple(DBusMessage *msg, GVariant **g_variant_return, GError **g_error); + +gboolean +get_dbus_string_property(DBusConnection *bus, const char *object_path, + const char* interface, const char *property, + char **value_return, GError **g_error); +gboolean +get_dbus_properties(DBusConnection *bus, const char *object_path, + const char* interface, GVariant **properties_return, + GError **g_error); + +static gboolean +dbus_discover_marshal(const char* target, GVariant *options, + DBusMessage **msg_return, GError **g_error); + +static gboolean +dbus_discover_unmarshal(DBusMessage *reply, gint32 *relevance_return, gchar ***paths_return, GError **g_error); + +/*----------------------------------------------------------------------------*/ + +#define RETURN_DBUS_ERROR(gerror, dbus_error) \ +{ \ + if (gerror) { \ + *gerror = dbus_error_to_gerror(&dbus_error); \ + } \ + dbus_error_free(&dbus_error); \ + return FALSE; \ +} + +GError * +dbus_error_to_gerror(DBusError *dbus_error) +{ + GError *gerror = NULL; + + if (dbus_error == NULL) { + g_set_error(&gerror, RDCP_ERROR, RDCP_ERROR_DBUS, + "dbus error not provided"); + } else { + g_set_error(&gerror, RDCP_ERROR, RDCP_ERROR_DBUS, + "dbus error (%s): %s", dbus_error->name, dbus_error->message); + } + + return gerror; +} + +/*----------------------------------------------------------------------------*/ + +/* + * The code to print a DBus message is based upon DBUS code in + * dbus/tools/dbus-print-message.c which is GPL. + */ + +static const char* +dbus_msg_type_to_string (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)"; + } +} + +#define INDENT_STRING " " +#define INDENT_WIDTH sizeof(INDENT_STRING) +#define PAGE_WIDTH 80 + +static void +dbus_message_print_indent(GString *string, int depth) +{ + while (depth-- > 0) + g_string_append(string, INDENT_STRING); +} + +static void +dbus_message_print_hex(GString *string, unsigned char *bytes, unsigned int len, int depth) +{ + unsigned int i, columns; + + g_string_append_printf(string, "array of bytes [\n"); + + dbus_message_print_indent (string, depth + 1); + + /* Each byte takes 3 cells (two hexits, and a space), except the last one. */ + columns = (PAGE_WIDTH - ((depth + 1) * INDENT_WIDTH)) / 3; + + if (columns < 8) + columns = 8; + + i = 0; + + while (i < len) { + g_string_append_printf(string, "%02x", bytes[i]); + i++; + + if (i != len) { + if (i % columns == 0) { + g_string_append(string, "\n"); + dbus_message_print_indent(string, depth + 1); + } else { + g_string_append(string, " "); + } + } + } + + g_string_append(string, "\n"); + dbus_message_print_indent(string, depth); + g_string_append(string, "]\n"); +} + +#define DEFAULT_SIZE 100 + +static void +dbus_message_print_byte_array(GString *string, DBusMessageIter *iter, int depth) +{ + unsigned char *bytes = malloc (DEFAULT_SIZE + 1); + unsigned int len = 0; + unsigned int max = DEFAULT_SIZE; + dbus_bool_t all_ascii = TRUE; + int current_type; + + while ((current_type = dbus_message_iter_get_arg_type (iter)) != DBUS_TYPE_INVALID) { + unsigned char val; + + dbus_message_iter_get_basic (iter, &val); + bytes[len] = val; + len++; + + if (val < 32 || val > 126) + all_ascii = FALSE; + + if (len == max) { + max *= 2; + bytes = realloc(bytes, max + 1); + } + + dbus_message_iter_next (iter); + } + + if (all_ascii) { + bytes[len] = '\0'; + g_string_append_printf(string, "array of bytes \"%s\"\n", bytes); + } else { + dbus_message_print_hex(string, bytes, len, depth); + } + + free (bytes); +} + +static void +dbus_message_print_iter(GString *string, DBusMessageIter *iter, int depth) +{ + int type; + + while ((type = dbus_message_iter_get_arg_type (iter)) != DBUS_TYPE_INVALID) { + + dbus_message_print_indent(string, depth); + + switch (type) { + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "boolean %s\n", val ? "true" : "false"); + } break; + + case DBUS_TYPE_BYTE: { + unsigned char val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "byte %d\n", val); + } break; + + case DBUS_TYPE_INT16: { + dbus_int16_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "int16 %" G_GINT16_FORMAT "\n", val); + } break; + + case DBUS_TYPE_UINT16: { + dbus_uint16_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "uint16 %" G_GUINT16_FORMAT "\n", val); + } break; + + case DBUS_TYPE_INT32: { + dbus_int32_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "int32 %" G_GINT32_FORMAT "\n", val); + } break; + + case DBUS_TYPE_UINT32: { + dbus_uint32_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "uint32 %" G_GUINT32_FORMAT "\n", val); + } break; + + case DBUS_TYPE_INT64: { + dbus_int64_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "int64 %" G_GINT64_FORMAT "\n", val); + } break; + + case DBUS_TYPE_UINT64: { + dbus_uint64_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "uint64 %" G_GUINT64_FORMAT "\n", val); + } break; + + case DBUS_TYPE_DOUBLE: { + double val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "double %g\n", val); + } break; + + case DBUS_TYPE_STRING: { + char *val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "string \"%s\"\n", val); + } break; + + case DBUS_TYPE_OBJECT_PATH: { + char *val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "object path \"%s\"\n", val); + } break; + + case DBUS_TYPE_SIGNATURE: { + char *val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "signature \"%s\"\n", val); + } break; + + case DBUS_TYPE_UNIX_FD: { + dbus_uint32_t val; + dbus_message_iter_get_basic (iter, &val); + g_string_append_printf(string, "Unix FD %" G_GUINT32_FORMAT "\n", val); + } break; + + case DBUS_TYPE_ARRAY: { + int current_type; + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + current_type = dbus_message_iter_get_arg_type (&subiter); + + if (current_type == DBUS_TYPE_BYTE) { + dbus_message_print_byte_array(string, &subiter, depth); + break; + } + + g_string_append(string, "array [\n"); + while (current_type != DBUS_TYPE_INVALID) { + dbus_message_print_iter(string, &subiter, depth+1); + + dbus_message_iter_next (&subiter); + current_type = dbus_message_iter_get_arg_type (&subiter); + + if (current_type != DBUS_TYPE_INVALID) + g_string_append(string, ","); + } + dbus_message_print_indent(string, depth); + g_string_append(string, "]\n"); + } break; + + case DBUS_TYPE_VARIANT: { + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + g_string_append(string, "variant "); + dbus_message_print_iter(string, &subiter, depth+1); + } break; + + case DBUS_TYPE_STRUCT: { + int current_type; + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + g_string_append(string, "struct {\n"); + while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) { + dbus_message_print_iter(string, &subiter, depth+1); + dbus_message_iter_next (&subiter); + if (dbus_message_iter_get_arg_type (&subiter) != DBUS_TYPE_INVALID) + g_string_append(string, ","); + } + dbus_message_print_indent(string, depth); + g_string_append(string, "}\n"); + } break; + + case DBUS_TYPE_DICT_ENTRY: { + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + g_string_append(string, "dict entry(\n"); + dbus_message_print_iter(string, &subiter, depth+1); + dbus_message_iter_next (&subiter); + dbus_message_print_iter(string, &subiter, depth+1); + dbus_message_print_indent(string, depth); + g_string_append(string, ")\n"); + } break; + + default: + g_string_append_printf(string, " (unknown arg type '%c')\n", type); + break; + } + dbus_message_iter_next(iter); + } +} + +/** + * dbus_message_print_string: + * @message The DBusMessage to format into a string. + * @string If non-NULL appends to this GString. + * @show_header If #TRUE the message header will be included. + * + * Formats a DBusMessage into a string. + * + * Returns: A GString which must be freed with g_string_free() + */ +static GString * +dbus_message_print_string(DBusMessage *message, GString *string, dbus_bool_t show_header) +{ + DBusMessageIter iter; + const char *sender; + const char *destination; + int message_type; + + g_return_val_if_fail (message != NULL, NULL); + + if (string == NULL) { + string = g_string_new(NULL); + } + + message_type = dbus_message_get_type (message); + sender = dbus_message_get_sender (message); + destination = dbus_message_get_destination (message); + + if (show_header) { + g_string_append_printf(string, "%s sender=%s -> dest=%s", + dbus_msg_type_to_string (message_type), + sender ? sender : "(null sender)", + destination ? destination : "(null destination)"); + + switch (message_type) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + case DBUS_MESSAGE_TYPE_SIGNAL: + g_string_append_printf(string, " serial=%u path=%s; interface=%s; member=%s\n", + dbus_message_get_serial (message), + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message)); + break; + + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + g_string_append_printf(string, " reply_serial=%u\n", + dbus_message_get_reply_serial (message)); + break; + + case DBUS_MESSAGE_TYPE_ERROR: + g_string_append_printf(string, " error_name=%s reply_serial=%u\n", + dbus_message_get_error_name (message), + dbus_message_get_reply_serial (message)); + break; + + default: + g_string_append(string, "\n"); + break; + } + } + + dbus_message_iter_init(message, &iter); + dbus_message_print_iter(string, &iter, 1); + + return string; +} + +/** + * dbus_message_print_string: + * @message The DBusMessage to format into a string. + * @string If non-NULL appends to this GString. + * @show_header If #TRUE the message header will be included. + * + * Formats a DBusMessage into a string. + * + * Returns: A simple malloc'ed string which must be freed with g_free(). + */ +static gchar * +dbus_message_print(DBusMessage *message, GString *string, dbus_bool_t show_header) +{ + g_return_val_if_fail (message != NULL, NULL); + + return g_string_free(dbus_message_print_string(message, NULL, show_header), FALSE); +} + +/*----------------------------------------------------------------------------*/ + +/** + * append_g_variant_to_dbus_msg_iter: + * @g_error initialized to error info when FALSE is returned. + * + * Helper routine for append_g_variant_to_dbus_message(). + * Performs the recusive descent into the GVariant appending + * values to the DBusMessage as it goes. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ +static gboolean +append_g_variant_to_dbus_msg_iter(DBusMessageIter *iter, GVariant *value, GError **g_error) +{ + GVariantClass class; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + class = g_variant_classify(value); + + switch (class) { + case G_VARIANT_CLASS_BOOLEAN: { + dbus_bool_t v = g_variant_get_boolean(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &v); + } break; + case G_VARIANT_CLASS_BYTE: { + guint8 v = g_variant_get_byte(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &v); + } break; + case G_VARIANT_CLASS_INT16: { + gint16 v = g_variant_get_int16 (value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &v); + } break; + case G_VARIANT_CLASS_UINT16: { + guint16 v = g_variant_get_uint16(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &v); + } break; + case G_VARIANT_CLASS_INT32: { + gint32 v = g_variant_get_int32(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &v); + } break; + case G_VARIANT_CLASS_UINT32: { + guint32 v = g_variant_get_uint32(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &v); + } break; + case G_VARIANT_CLASS_INT64: { + gint64 v = g_variant_get_int64(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &v); + } break; + case G_VARIANT_CLASS_UINT64: { + guint64 v = g_variant_get_uint64(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &v); + } break; + case G_VARIANT_CLASS_HANDLE: { + gint32 v = g_variant_get_handle(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &v); + } break; + case G_VARIANT_CLASS_DOUBLE: { + gdouble v = g_variant_get_double(value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &v); + } break; + case G_VARIANT_CLASS_STRING: { + const gchar *v = g_variant_get_string(value, NULL); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &v); + } break; + case G_VARIANT_CLASS_OBJECT_PATH: { + const gchar *v = g_variant_get_string(value, NULL); + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &v); + } break; + case G_VARIANT_CLASS_SIGNATURE: { + const gchar *v = g_variant_get_string(value, NULL); + dbus_message_iter_append_basic(iter, DBUS_TYPE_SIGNATURE, &v); + } break; + case G_VARIANT_CLASS_VARIANT: { + DBusMessageIter sub; + GVariant *child; + + child = g_variant_get_child_value(value, 0); + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + g_variant_get_type_string(child), + &sub); + if (!append_g_variant_to_dbus_msg_iter(&sub, child, g_error)) { + G_VARIANT_FREE(child); + goto fail; + } + dbus_message_iter_close_container(iter, &sub); + G_VARIANT_FREE(child); + } break; + case G_VARIANT_CLASS_MAYBE: { + GVariant *child; + + if (!g_variant_n_children(value)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "cannot serialize an empty GVariant MAYBE"); + goto fail; + } else { + child = g_variant_get_child_value(value, 0); + if (!append_g_variant_to_dbus_msg_iter(iter, child, g_error)) { + G_VARIANT_FREE(child); + goto fail; + } + G_VARIANT_FREE(child); + } + } break; + case G_VARIANT_CLASS_ARRAY: { + DBusMessageIter dbus_iter; + const gchar *type_string; + gsize n, i; + GVariant *child; + + type_string = g_variant_get_type_string(value); + type_string++; /* skip the 'a' */ + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + type_string, &dbus_iter); + + n = g_variant_n_children(value); + + for (i = 0; i < n; i++) { + child = g_variant_get_child_value(value, i); + if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, child, g_error)) { + G_VARIANT_FREE(child); + goto fail; + } + G_VARIANT_FREE(child); + } + + dbus_message_iter_close_container(iter, &dbus_iter); + } break; + case G_VARIANT_CLASS_TUPLE: { + DBusMessageIter dbus_iter; + gsize n, i; + GVariant *child; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, + NULL, &dbus_iter); + + n = g_variant_n_children(value); + + for (i = 0; i < n; i++) { + child = g_variant_get_child_value(value, i); + if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, child, g_error)) { + G_VARIANT_FREE(child); + goto fail; + } + G_VARIANT_FREE(child); + } + + dbus_message_iter_close_container(iter, &dbus_iter); + + } break; + case G_VARIANT_CLASS_DICT_ENTRY: { + DBusMessageIter dbus_iter; + GVariant *key, *val; + + dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, + NULL, &dbus_iter); + key = g_variant_get_child_value(value, 0); + if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, key, g_error)) { + G_VARIANT_FREE(key); + goto fail; + } + G_VARIANT_FREE(key); + + val = g_variant_get_child_value(value, 1); + if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, val, g_error)) { + G_VARIANT_FREE(val); + goto fail; + } + G_VARIANT_FREE(val); + + dbus_message_iter_close_container(iter, &dbus_iter); + } break; + default: { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "Error serializing GVariant with class '%c' to a D-Bus message", + class); + goto fail; + } break; + } + + return TRUE; + + fail: + return FALSE; +} + +/** + * append_g_variant_to_dbus_message: + * @message DBus message currently being built + * @g_variant The GVariant item to be appended to @message + * @g_error initialized to error info when FALSE is returned. + * + * Given a DBusMessage append the contents of the provied @g_variant to the message. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ +static gboolean +append_g_variant_to_dbus_message(DBusMessage *message, GVariant *g_variant, GError **g_error) +{ + DBusMessageIter iter; + + g_return_val_if_fail (message != NULL, FALSE); + g_return_val_if_fail (g_variant != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + dbus_message_iter_init_append(message, &iter); + if (!append_g_variant_to_dbus_msg_iter(&iter, g_variant, g_error)) { + return FALSE; + } + return TRUE; +} + +/** + * dbus_method_append_args_tuple: + * @message DBus message currently being built + * @args A GVariant tuple containing the method parameters to + * be appended to @message + * @g_error initialized to error info when FALSE is returned. + * + * Append the method parameters to a DBus method message. @args + * is a GVariant tuple representing the parameter list. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ +static gboolean +dbus_method_append_args_tuple(DBusMessage *message, GVariant *args, GError **g_error) +{ + DBusMessageIter iter; + gsize n, i; + GVariant *arg; + + g_return_val_if_fail (message != NULL, FALSE); + g_return_val_if_fail (args != NULL && g_variant_is_of_type(args, G_VARIANT_TYPE_TUPLE), FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + if ((n = g_variant_n_children(args))) { + + dbus_message_iter_init_append(message, &iter); + + for (i = 0; i < n; i++) { + arg = g_variant_get_child_value(args, i); + if (!append_g_variant_to_dbus_msg_iter(&iter, arg, g_error)) { + G_VARIANT_FREE(arg); + return FALSE; + } + G_VARIANT_FREE(arg); + } + } + + return TRUE; +} + +/** + * marshal_dbus_string_variant: + * @iter iterator into which the string variant will be inserted + * @value string value to insert as variant + * @g_error initialized to error info when FALSE is returned. + * + * Add a string variant while marshaling DBus protocol. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ +static gboolean +marshal_dbus_string_variant(DBusMessageIter *iter, const char *value, GError **g_error) +{ + DBusMessageIter variant; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "s", &variant)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "cannot open dbus variant string container, value=\"%s\"", value); + return FALSE; + } + + if (!dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &value)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "cannot append dbus variant string value, value=\"%s\"", value); + return FALSE; + } + + if (!dbus_message_iter_close_container(iter, &variant)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "cannot close dbus variant container, value=\"%s\"", value); + return FALSE; + } + + return TRUE; +} + +/** + * marshal_dbus_dict_string_entry: + * @array dictionary array into which entry is inserted + * @name entry's key + * @value entry's value + * @g_error initialized to error info when FALSE is returned. + * + * Adds a dictionary entry into an dictionary array whose key is a + * string and whose value is also a string while marshaling DBus protocol. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ +static gboolean +marshal_dbus_dict_string_entry(DBusMessageIter *array, const char *name, const char *value, GError **g_error) +{ + DBusMessageIter entry; + + g_return_val_if_fail (array != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + if (!dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "cannot open dbus dict entry container for option <%s=%s>", name, value); + return FALSE; + } + + if (!dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &name)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "cannot append option name for option <%s=%s>", name, value); + return FALSE; + } + + if (!marshal_dbus_string_variant(&entry, value, g_error)) { + return FALSE; + } + + if (!dbus_message_iter_close_container(array, &entry)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "cannot close dbus dict entry container for option <%s=%s>", name, value); + return FALSE; + } + + return TRUE; +} + + +/*----------------------------------------------------------------------------*/ + + +/** + * dbus_iter_to_variant: + * @msg DBusMessage which will be converted to a GVariant + * @g_variant_return Pointer to location where GVariant will be returned, + * will be NULL if error occurs + * @g_error initialized to error info when FALSE is returned. + * + * Helper routine for dbus_message_to_g_variant(), + * Performs the recusive descent into the DBusMessage appending + * values to the GVariant as it goes. + * + * Returns: return TRUE if successful, @g_variant_return will be non-NULL. + * FALSE if error with @g_error initialized, @g_variant_return will be NULL. + */ +static gboolean +dbus_iter_to_variant(DBusMessageIter *iter, GVariant **g_variant_return, GError **g_error) +{ + gboolean result = TRUE; + int arg_type; + GVariant *g_variant = NULL; + char *signature = NULL; + GVariantBuilder builder; + DBusMessageIter sub; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (g_variant_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *g_variant_return = NULL; + + arg_type = dbus_message_iter_get_arg_type(iter); + +#ifdef TRACE_VARIANT + signature = dbus_message_iter_get_signature(iter); + printf("dbus_iter_to_variant enter: type=%s signature=%s\n", + dbus_type_to_string(arg_type), signature); + g_free(signature); + signature = NULL; +#endif + + switch (arg_type) { + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_boolean(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant boolean value=%d", value); + result = FALSE; + } + + } break; + case DBUS_TYPE_BYTE: { + guint8 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_byte(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant byte value=%uc", value); + result = FALSE; + } + + } break; + case DBUS_TYPE_INT16: { + gint16 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_int16(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant int16 value=%" G_GINT16_FORMAT, value); + result = FALSE; + } + + } break; + case DBUS_TYPE_UINT16: { + guint16 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_uint16(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant uint16 value=%" G_GUINT16_FORMAT, value); + result = FALSE; + } + + } break; + case DBUS_TYPE_INT32: { + gint32 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_int32(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant int32 value=%" G_GINT32_FORMAT, value); + result = FALSE; + } + + } break; + case DBUS_TYPE_UINT32: { + guint32 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_uint32(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant uint32 value=%" G_GUINT32_FORMAT, value); + result = FALSE; + } + + } break; + case DBUS_TYPE_INT64: { + gint64 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_int64(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant int64 value=%" G_GINT64_FORMAT, value); + result = FALSE; + } + + } break; + case DBUS_TYPE_UINT64: { + guint64 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_uint64(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant uint64 value=%" G_GUINT64_FORMAT, value); + result = FALSE; + } + + } break; + case DBUS_TYPE_DOUBLE: { + gdouble value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_double(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant double value=%f", value); + result = FALSE; + } + + } break; + case DBUS_TYPE_STRING: { + gchar *value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_string(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant string value=\"%s\"", value); + result = FALSE; + } + + } break; + case DBUS_TYPE_OBJECT_PATH: { + gchar *value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_object_path(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant object path value=\"%s\"", value); + result = FALSE; + } + + } break; + case DBUS_TYPE_SIGNATURE: { + gchar *value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_signature(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant signature value=\"%s\"", value); + result = FALSE; + } + + } break; + case DBUS_TYPE_UNIX_FD: { + guint32 value; + + dbus_message_iter_get_basic(iter, &value); + if ((g_variant = g_variant_new_uint32(value)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant file descriptor value=%u", value); + result = FALSE; + } + + } break; + case DBUS_TYPE_ARRAY: { + GVariant *item; + + signature = dbus_message_iter_get_signature(iter); + + g_variant_builder_init(&builder, G_VARIANT_TYPE(signature)); + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + if (!dbus_iter_to_variant(&sub, &item, g_error)) { + g_variant_builder_clear(&builder); + result = FALSE; + goto exit; + } +#ifdef TRACE_VARIANT + { + gchar *variant_as_string = g_variant_print(item, TRUE); + printf("array item=%s\n", variant_as_string); + g_free(variant_as_string); + } +#endif + g_variant_builder_add_value(&builder, item); + dbus_message_iter_next(&sub); + } + + if ((g_variant = g_variant_builder_end(&builder)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant array"); + result = FALSE; + } + + } break; + case DBUS_TYPE_VARIANT: { + GVariant *item; + + signature = dbus_message_iter_get_signature(iter); + g_variant_builder_init(&builder, G_VARIANT_TYPE(signature)); + dbus_message_iter_recurse(iter, &sub); + + if (!dbus_iter_to_variant(&sub, &item, g_error)) { + g_variant_builder_clear(&builder); + result = FALSE; + goto exit; + } +#ifdef TRACE_VARIANT + { + gchar *variant_as_string = g_variant_print(item, TRUE); + printf("variant item=%s\n", variant_as_string); + g_free(variant_as_string); + } +#endif + + g_variant_builder_add_value(&builder, item); + + if ((g_variant = g_variant_builder_end(&builder)) == NULL) { + gchar *variant_as_string = g_variant_print(item, FALSE); + + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant variant for value=%s", variant_as_string); + g_free(variant_as_string); + result = FALSE; + } + } break; + case DBUS_TYPE_STRUCT: { + GVariant *item; + + signature = dbus_message_iter_get_signature(iter); + g_variant_builder_init(&builder, G_VARIANT_TYPE(signature)); + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + if (!dbus_iter_to_variant(&sub, &item, g_error)) { + g_variant_builder_clear(&builder); + result = FALSE; + goto exit; + } +#ifdef TRACE_VARIANT + { + gchar *variant_as_string = g_variant_print(item, TRUE); + printf("struct item=%s\n", variant_as_string); + g_free(variant_as_string); + } +#endif + g_variant_builder_add_value(&builder, item); + dbus_message_iter_next(&sub); + } + + if ((g_variant = g_variant_builder_end(&builder)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to create GVariant struct"); + result = FALSE; + } + + } break; + case DBUS_TYPE_DICT_ENTRY: { + GVariant *key, *value; + + dbus_message_iter_recurse(iter, &sub); + signature = dbus_message_iter_get_signature(iter); + + if (!dbus_iter_to_variant(&sub, &key, g_error)) { + g_prefix_error(g_error, "unable to create GVariant dict_entry key: "); + result = FALSE; + goto exit; + } + + dbus_message_iter_next(&sub); + + if (!dbus_iter_to_variant(&sub, &value, g_error)) { + g_prefix_error(g_error, "unable to create GVariant dict_entry value: "); + result = FALSE; + goto exit; + } +#ifdef TRACE_VARIANT + { + gchar *key_variant_as_string = g_variant_print(key, TRUE); + gchar *value_variant_as_string = g_variant_print(key, TRUE); + printf("dict_entry key=%s value=%s\n", g_variant_print(key, TRUE), g_variant_print(value, TRUE)); + g_free(key_variant_as_string); + g_free(value_variant_as_string); + } +#endif + + g_variant = g_variant_new_dict_entry(key, value); + + } break; + default: { + signature = dbus_message_iter_get_signature(iter); + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unknown DBus type=%d, signature=%s", arg_type, signature); + result = FALSE; + break; + } + } + + exit: + if (signature) dbus_free(signature); + *g_variant_return = g_variant; + +#ifdef TRACE_VARIANT + { + gchar *variant_as_string = NULL; + if (g_variant) { + variant_as_string = g_variant_print(g_variant, TRUE); + } else { + variant_as_string = "NULL"; + } + printf("dbus_iter_to_variant returns %s, variant=%s\n", + result ? "TRUE" : "FALSE", variant_as_string); + if (g_variant) { + g_free(variant_as_string); + } + } +#endif + + return result; +} + +/** + * dbus_message_to_g_variant: + * @msg DBusMessage which will be converted to a GVariant + * @g_variant_return Pointer to location where GVariant will be returned, + * will be NULL if error occurs + * @g_error initialized to error info when FALSE is returned. + * + * Converts a DBusMessage to a GVariant. + * + * Returns: return TRUE if successful, @g_variant_return will be non-NULL. + * FALSE if error with @g_error initialized, @g_variant_return will be NULL. + */ +static gboolean +dbus_message_to_g_variant(DBusMessage *msg, GVariant **g_variant_return, GError **g_error) +{ + DBusMessageIter iter; + + g_return_val_if_fail (msg != NULL, FALSE); + g_return_val_if_fail (g_variant_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *g_variant_return = NULL; + +#ifdef RDCP_DBUS_DEBUG + { + gchar *msg_as_string = dbus_message_print(msg, NULL, FALSE); + printf("dbus_message_to_g_variant: msg=\n%s", msg_as_string); + g_free(msg_as_string); + } +#endif + + if (!dbus_message_iter_init(msg, &iter)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "could not create iterator to parse DBus message"); + return FALSE; + } + + if (!dbus_iter_to_variant(&iter, g_variant_return, g_error)) { + g_prefix_error(g_error, "unable to convert dbus_message to GVariant: "); + return FALSE; + } + +#ifdef RDCP_DBUS_DEBUG + { + gchar *variant_as_string = NULL; + if (*g_variant_return) { + variant_as_string = g_variant_print(*g_variant_return, TRUE); + } else { + variant_as_string = "NULL"; + } + printf("dbus_message_to_g_variant returns variant=%s\n", variant_as_string); + if (*g_variant_return) { + g_free(variant_as_string); + } + } +#endif + + return TRUE; +} + +/** + * dbus_method_reply_to_g_variant_tuple: + * @msg DBus message reply which will be converted to a tuple of GVariant's + * @g_variant_return Pointer to location where GVariant will be returned, + * will be NULL if error occurs + * @g_error initialized to error info when FALSE is returned. + * + * A DBus method reply contains a sequence of zero or more OUT parameters. + * Parse the method reply and build a GVariant tuple whose members are + * the OUT parameters. Each tuple member will also be a GVariant. + * + * Returns: return TRUE if successful, @g_variant_return will be non-NULL. + * FALSE if error with @g_error initialized, @g_variant_return will be NULL. + */ +static gboolean +dbus_method_reply_to_g_variant_tuple(DBusMessage *msg, GVariant **g_variant_return, GError **g_error) +{ + DBusMessageIter iter; + GVariantBuilder builder; + + g_return_val_if_fail (msg != NULL, FALSE); + g_return_val_if_fail (g_variant_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *g_variant_return = NULL; + +#ifdef RDCP_DBUS_DEBUG + { + gchar *msg_as_string = dbus_message_print(msg, NULL, FALSE); + printf("dbus_method_reply_to_g_variant_tuple: msg=\n%s", msg_as_string); + g_free(msg_as_string); + } +#endif + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + + if (!dbus_message_iter_init(msg, &iter)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "could not create iterator to parse DBus message"); + return FALSE; + } + + while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) { + GVariant *g_variant = NULL; + + if (!dbus_iter_to_variant(&iter, &g_variant, g_error)) { + g_prefix_error(g_error, "unable to convert dbus_message to GVariant: "); + return FALSE; + } + + g_variant_builder_add_value(&builder, g_variant); + + dbus_message_iter_next (&iter); + } + + if ((*g_variant_return = g_variant_builder_end(&builder)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "unable to build GVariant options array"); + return FALSE; + } + + +#ifdef RDCP_DBUS_DEBUG + { + gchar *variant_as_string = NULL; + if (*g_variant_return) { + variant_as_string = g_variant_print(*g_variant_return, TRUE); + } else { + variant_as_string = "NULL"; + } + printf("dbus_method_reply_to_g_variant_tuple returns variant=%s\n", variant_as_string); + if (*g_variant_return) { + g_free(variant_as_string); + } + } +#endif + + return TRUE; +} + +/*----------------------------------------------------------------------------*/ + +/** + * get_dbus_string_property: + * @bus The DBus connection on which the query will be performed + * @object_path The DBus object path identifying the object + * @interface The DBus interface which provides the property + * @property The name of the proptery on the interface + * @value_return Pointer to where string value will be returned. + * Must be freed with g_free(), will be NULL if error occurs. + * @g_error initialized to error info when FALSE is returned. + * + * Retrieve a string valued property from an DBus object. + * + * Returns: return TRUE if successful, @value_return will be non-NULL. + * FALSE if error with @g_error initialized, @value_return will be NULL. + */ +gboolean +get_dbus_string_property(DBusConnection *bus, const char *object_path, + const char* interface, const char *property, + char **value_return, GError **g_error) +{ + const char *method = "Get"; + DBusMessage* msg = NULL; + DBusMessage* reply = NULL; + DBusError dbus_error; + const char *interface_ptr = interface; + const char *property_ptr = property; + DBusMessageIter iter, variant; + char *value = NULL; + char *signature; + + g_return_val_if_fail (bus != NULL, FALSE); + g_return_val_if_fail (object_path != NULL, FALSE); + g_return_val_if_fail (interface != NULL, FALSE); + g_return_val_if_fail (property != NULL, FALSE); + g_return_val_if_fail (value_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *value_return = NULL; + dbus_error_init(&dbus_error); + + if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, object_path, + DBUS_INTERFACE_PROPERTIES, method)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to create" + "DBus %s.%s() message, object_path=%s, interface=%s, property=%s", + DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property); + return FALSE; + } + + if (!dbus_message_append_args(msg, + DBUS_TYPE_STRING, &interface_ptr, + DBUS_TYPE_STRING, &property_ptr, + DBUS_TYPE_INVALID)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to add args to " + "DBus %s.%s() message, object_path=%s, interface=%s, property=%s", + DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property); + dbus_message_unref(msg); + return FALSE; + } + + if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) { + dbus_message_unref(msg); + RETURN_DBUS_ERROR(g_error, dbus_error); + } + dbus_message_unref(msg); + + if (!dbus_message_has_signature(reply, "v")) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "expected variant in DBus %s.%s() reply, object_path=%s, interface=%s, property=%s", + DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property); + dbus_message_unref(reply); + return FALSE; + } + + if (!dbus_message_iter_init(reply, &iter)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "could not create iterator to parse " + "DBus %s.%s() reply, object_path=%s, interface=%s, property=%s", + DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property); + dbus_message_unref(reply); + return FALSE; + } + + dbus_message_iter_recurse(&iter, &variant); + signature = dbus_message_iter_get_signature(&variant); + if (!g_str_equal(signature, DBUS_TYPE_STRING_AS_STRING)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "expected string type variant but got \"%s\" signature instead for " + "DBus %s.%s() reply, object_path=%s, interface=%s, property=%s", + signature, DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property); + dbus_free(signature); + dbus_message_unref(reply); + return FALSE; + } + dbus_free(signature); + dbus_message_iter_get_basic(&variant, &value); + *value_return = g_strdup(value); + + dbus_message_unref(reply); + return TRUE; +} + +/** + * get_dbus_properties: + * @bus The DBus connection on which the query will be performed + * @object_path The DBus object path identifying the object + * @interface The DBus interface which provides the property + * @properties_return Pointer to where a GVariant containing all + * the interface's properties for the object will be returned. + * Must be freed with g_variant_unref(), will be NULL if error occurs. + * @g_error initialized to error info when FALSE is returned. + * + * Retrieve all the interface properties from an DBus object. + * Returned as a GVariant dictionary. Use g_variant_lookup() to + * obtain a specific property in the dictionary. + * + * Returns: return TRUE if successful, @value_return will be non-NULL. + * FALSE if error with @g_error initialized, @value_return will be NULL. + */ +gboolean +get_dbus_properties(DBusConnection *bus, const char *object_path, + const char* interface, GVariant **properties_return, + GError **g_error) +{ + const char *method = "GetAll"; + DBusMessage* msg = NULL; + DBusMessage* reply = NULL; + DBusError dbus_error; + const char *interface_ptr = interface; + + g_return_val_if_fail (bus != NULL, FALSE); + g_return_val_if_fail (object_path != NULL, FALSE); + g_return_val_if_fail (interface != NULL, FALSE); + g_return_val_if_fail (properties_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *properties_return = NULL; + dbus_error_init(&dbus_error); + + if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, object_path, + DBUS_INTERFACE_PROPERTIES, method)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to create" + "DBus %s.%s() message, object_path=%s, interface=%s", + DBUS_INTERFACE_PROPERTIES, method, object_path, interface); + return FALSE; + } + + if (!dbus_message_append_args(msg, + DBUS_TYPE_STRING, &interface_ptr, + DBUS_TYPE_INVALID)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to add args to " + "DBus %s.%s() message, object_path=%s, interface=%s", + DBUS_INTERFACE_PROPERTIES, method, object_path, interface); + dbus_message_unref(msg); + return FALSE; + } + + if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) { + dbus_message_unref(msg); + RETURN_DBUS_ERROR(g_error, dbus_error); + } + dbus_message_unref(msg); + + if (!dbus_message_to_g_variant(reply, properties_return, g_error)) { + dbus_message_unref(reply); + return FALSE; + } + + + dbus_message_unref(reply); + return TRUE; +} + +/*----------------------------------------------------------------------------*/ + +/** + * dbus_discover_marshal: + * @target what to discover + * @options dictionary of option {key,values} + * @msg_return if successful DBus message returned here, + * if error then this will be NULL. + * @g_error initialized to error info when FALSE is returned. + * + * Marshal a realm Discover method call. + * + * Returns: return TRUE if successful, @msg_return will point to DBusMessage, + * FALSE if error with @g_error initialized. @msg_return will be NULL. + */ +static gboolean +dbus_discover_marshal(const char* target, GVariant *options, + DBusMessage **msg_return, GError **g_error) +{ + const char *method = "Discover"; + DBusMessage *msg = NULL; + DBusMessageIter iter; + + g_return_val_if_fail (target != NULL, FALSE); + g_return_val_if_fail (options != NULL, FALSE); + g_return_val_if_fail (msg_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *msg_return = NULL; + + if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, REALM_DBUS_SERVICE_PATH, + REALM_DBUS_PROVIDER_INTERFACE, method)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to create dbus method call %s.%s() message, object_path=%s", + REALM_DBUS_PROVIDER_INTERFACE, method, REALM_DBUS_SERVICE_PATH); + return FALSE; + } + + dbus_message_iter_init_append (msg, &iter); /* void return */ + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &target)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to add target parameter (%s)", target); + dbus_message_unref(msg); + return FALSE; + } + + if (!append_g_variant_to_dbus_message(msg, options, g_error)) { + g_prefix_error(g_error, "unable to append GVariant options dictionary into %s.%s() message", + REALM_DBUS_PROVIDER_INTERFACE, method); + dbus_message_unref(msg); + return FALSE; + } + + *msg_return = msg; + return TRUE; +} + +/** + * dbus_discover_unmarshal: + * @reply DBus method reply from Discover call + * @relevance_return Pointer to returned relevance value + * @paths_return Pointer to an array of object path strings, + * must be freed with g_strfreev(). + * @g_error initialized to error info when FALSE is returned. + * + * Parses the DBus reply message from the Discover call and unpacks + * the output paramters in the *_return parameters of this function. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ + +static gboolean +dbus_discover_unmarshal(DBusMessage *reply, gint32 *relevance_return, gchar ***paths_return, GError **g_error) +{ + GVariant *g_variant_reply = NULL; + + g_return_val_if_fail (reply != NULL, FALSE); + g_return_val_if_fail (relevance_return != NULL, FALSE); + g_return_val_if_fail (paths_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + if (!dbus_method_reply_to_g_variant_tuple(reply, &g_variant_reply, g_error)) { + gchar *reply_string = dbus_message_print(reply, NULL, FALSE); + + g_prefix_error(g_error, "unable convert reply (%s) to GVariant tuple: ", reply_string); + g_free(reply_string); + dbus_message_unref(reply); + return FALSE; + } + + g_variant_get(g_variant_reply, "(i^ao)", relevance_return, paths_return); + G_VARIANT_FREE(g_variant_reply); + + return TRUE; +} + +/** + * dbus_discover_call: + * @target what to discover + * @options dictionary of option {key,values} + * @relevance_return Pointer to returned relevance value + * @paths_return Pointer to an array of object path strings, + * must be freed with g_strfreev(). + * @g_error initialized to error info when FALSE is returned. + * + * Marshal a realm Discover method call, call it synchronously, + * unmarsh it's reply and return the OUT parameters. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized + */ +gboolean +dbus_discover_call(DBusConnection *bus, const char *target, GVariant *options, + gint32 *relevance_return, gchar ***paths_return, GError **g_error) +{ + DBusError dbus_error; + DBusMessage *msg = NULL; + DBusMessage* reply = NULL; + + g_return_val_if_fail (bus != NULL, FALSE); + g_return_val_if_fail (target != NULL, FALSE); + g_return_val_if_fail (options != NULL, FALSE); + g_return_val_if_fail (relevance_return != NULL, FALSE); + g_return_val_if_fail (paths_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + dbus_error_init(&dbus_error); + if (!dbus_discover_marshal(target, options, &msg, g_error)) { + RETURN_DBUS_ERROR(g_error, dbus_error); + } + + if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) { + dbus_message_unref(msg); + RETURN_DBUS_ERROR(g_error, dbus_error); + } + dbus_message_unref(msg); + + if (!dbus_discover_unmarshal(reply, relevance_return, paths_return, g_error)) { + dbus_message_unref(reply); + return FALSE; + } + dbus_message_unref(reply); + return TRUE; +} + +/*----------------------------------------------------------------------------*/ + +/** + * dbus_change_login_policy_marshal: + * @dbus_path The DBus object path of the object supporting the Realm + * interface on which the call will be made. + * @login_policy The new login policy, or an empty string. + * @permitted_add: An array of logins to permit. + * @permitted_remove: An array of logins to not permit. + * @options dictionary of option {key,values} + * @msg_return if successful DBus message returned here, + * if error then this will be NULL. + * @g_error initialized to error info when FALSE is returned. + * + * Marshal a realm ChangeLoginPolicy method call. + * + * Returns: return TRUE if successful, @msg_return will point to DBusMessage, + * FALSE if error with @g_error initialized. @msg_return will be NULL. + */ +static gboolean +dbus_change_login_policy_marshal(const gchar *dbus_path, const char *login_policy, + GVariant *permitted_add, GVariant *permitted_remove, + GVariant *options, DBusMessage **msg_return, GError **g_error) +{ + const char *method = "ChangeLoginPolicy"; + DBusMessage *msg = NULL; + DBusMessageIter iter; + + g_return_val_if_fail (dbus_path != NULL, FALSE); + g_return_val_if_fail (login_policy != NULL, FALSE); + g_return_val_if_fail (permitted_add != NULL, FALSE); + g_return_val_if_fail (permitted_remove != NULL, FALSE); + g_return_val_if_fail (options != NULL, FALSE); + g_return_val_if_fail (msg_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *msg_return = NULL; + + if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, dbus_path, + REALM_DBUS_REALM_INTERFACE, method)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to create dbus method call %s.%s() message, object_path=%s", + REALM_DBUS_PROVIDER_INTERFACE, method, REALM_DBUS_SERVICE_PATH); + return FALSE; + } + + dbus_message_iter_init_append (msg, &iter); /* void return */ + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &login_policy)) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to add login_policy parameter (%s)", login_policy); + dbus_message_unref(msg); + return FALSE; + } + + if (!append_g_variant_to_dbus_message(msg, permitted_add, g_error)) { + g_prefix_error(g_error, "unable to append GVariant permitted_add dictionary into %s.%s() message", + REALM_DBUS_PROVIDER_INTERFACE, method); + dbus_message_unref(msg); + return FALSE; + } + + if (!append_g_variant_to_dbus_message(msg, permitted_remove, g_error)) { + g_prefix_error(g_error, "unable to append GVariant permitted_remove dictionary into %s.%s() message", + REALM_DBUS_PROVIDER_INTERFACE, method); + dbus_message_unref(msg); + return FALSE; + } + + if (!append_g_variant_to_dbus_message(msg, options, g_error)) { + g_prefix_error(g_error, "unable to append GVariant options dictionary into %s.%s() message", + REALM_DBUS_PROVIDER_INTERFACE, method); + dbus_message_unref(msg); + return FALSE; + } + + *msg_return = msg; + return TRUE; +} + +/** + * dbus_change_login_policy_unmarshal: + * @reply DBus method reply from ChangeLoginPolicy call + * @g_error initialized to error info when FALSE is returned. + * + * Parses the DBus reply message from the ChangeLoginPolicy call. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ + +static gboolean +dbus_change_login_policy_unmarshal(DBusMessage *reply, GError **g_error) +{ + g_return_val_if_fail (reply != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + return TRUE; +} + +/** + * dbus_change_login_policy_call: + * @dbus_path The DBus object path of the object supporting the Realm + * interface on which the call will be made. + * @login_policy The new login policy, or an empty string. + * @permitted_add: An array of logins to permit. + * @permitted_remove: An array of logins to not permit. + * @options dictionary of option {key,values} + * @g_error initialized to error info when FALSE is returned. + * + * Marshal a realm ChangeLoginPolicy method call, call it synchronously, + * unmarsh it's reply and return the OUT parameters. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized + */ +gboolean +dbus_change_login_policy_call(DBusConnection *bus, const gchar *dbus_path, const char *login_policy, + GVariant *permitted_add, GVariant *permitted_remove, + GVariant *options, GError **g_error) +{ + DBusError dbus_error; + DBusMessage *msg = NULL; + DBusMessage* reply = NULL; + + g_return_val_if_fail (bus != NULL, FALSE); + g_return_val_if_fail (dbus_path != NULL, FALSE); + g_return_val_if_fail (login_policy != NULL, FALSE); + g_return_val_if_fail (permitted_add != NULL, FALSE); + g_return_val_if_fail (permitted_remove != NULL, FALSE); + g_return_val_if_fail (options != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + dbus_error_init(&dbus_error); + if (!dbus_change_login_policy_marshal(dbus_path, login_policy, + permitted_add, permitted_remove, + options, &msg, g_error)) { + RETURN_DBUS_ERROR(g_error, dbus_error); + } + + if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) { + dbus_message_unref(msg); + RETURN_DBUS_ERROR(g_error, dbus_error); + } + dbus_message_unref(msg); + + if (!dbus_change_login_policy_unmarshal(reply, g_error)) { + dbus_message_unref(reply); + return FALSE; + } + dbus_message_unref(reply); + return TRUE; +} + +/*----------------------------------------------------------------------------*/ + +/** + * dbus_join_leave_marshal: + * @dbus_path The DBus object path of the object supporting the kerberos + * membership interface on which the Join/Leave call will be made. + * @credentials A GVariant encoding the credentials according the the credential + * type. See the Realmd DBus interface for specifics. + * @options dictionary of option {key,values} + * @msg_return if successful DBus message returned here, + * if error then this will be NULL. + * @g_error initialized to error info when FALSE is returned. + * + * Since the Join() & Leave() methods share identical signatures (differing + * only in their method name) we use a common routine. + * + * Returns: return TRUE if successful, @msg_return will point to DBusMessage, + * FALSE if error with @g_error initialized. @msg_return will be NULL. + */ +static gboolean +dbus_join_leave_marshal(const char *method, const gchar* dbus_path, + GVariant *credentials, GVariant *options, + DBusMessage **msg_return, GError **g_error) +{ + DBusMessage *msg = NULL; + DBusMessageIter iter; + + g_return_val_if_fail (method != NULL, FALSE); + g_return_val_if_fail (dbus_path != NULL, FALSE); + g_return_val_if_fail (credentials != NULL, FALSE); + g_return_val_if_fail (options != NULL, FALSE); + g_return_val_if_fail (msg_return != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + *msg_return = NULL; + + if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, dbus_path, + REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE, method)) == NULL) { + g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS, + "failed to create dbus method call %s.%s() message, object_path=%s", + REALM_DBUS_PROVIDER_INTERFACE, method, REALM_DBUS_SERVICE_PATH); + return FALSE; + } + + dbus_message_iter_init_append (msg, &iter); /* void return */ + + if (!append_g_variant_to_dbus_message(msg, credentials, g_error)) { + g_prefix_error(g_error, "unable to append GVariant credentials into %s.%s() message", + REALM_DBUS_PROVIDER_INTERFACE, method); + dbus_message_unref(msg); + return FALSE; + } + + if (!append_g_variant_to_dbus_message(msg, options, g_error)) { + g_prefix_error(g_error, "unable to append GVariant options dictionary into %s.%s() message", + REALM_DBUS_PROVIDER_INTERFACE, method); + dbus_message_unref(msg); + return FALSE; + } + + *msg_return = msg; + return TRUE; +} + +/** + * dbus_join_leave_unmarshal: + * @reply DBus method reply from Join/Leave call + * @g_error initialized to error info when FALSE is returned. + * + * Since the Join() & Leave() methods share identical signatures (differing + * only in their method name) we use a common routine. + * + * Parses the DBus reply message from the Join/Leave call. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ + +static gboolean +dbus_join_leave_unmarshal(DBusMessage *reply, GError **g_error) +{ + + g_return_val_if_fail (reply != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + return TRUE; +} + +/** + * dbus_join_leave_call: + * @dbus_path The DBus object path of the object supporting the kerberos + * membership interface on which the Join/Leave call will be made. + * @credentials A GVariant encoding the credentials according the the credential + * type. See the Realmd DBus interface for specifics. + * @options dictionary of option {key,values} + * @g_error initialized to error info when FALSE is returned. + * + * Since the Join() & Leave() methods share identical signatures (differing + * only in their method name) we use a common routine. + * + * Marshal a realm Join/Leave method call, call it synchronously, + * unmarsh it's reply and return the OUT parameters. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized + */ +gboolean +dbus_join_leave_call(const char *method, DBusConnection *bus, const gchar *dbus_path, + GVariant *credentials, GVariant *options, GError **g_error) +{ + DBusError dbus_error; + DBusMessage *msg = NULL; + DBusMessage* reply = NULL; + + g_return_val_if_fail (method != NULL, FALSE); + g_return_val_if_fail (bus != NULL, FALSE); + g_return_val_if_fail (dbus_path != NULL, FALSE); + g_return_val_if_fail (credentials != NULL, FALSE); + g_return_val_if_fail (options != NULL, FALSE); + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + dbus_error_init(&dbus_error); + if (!dbus_join_leave_marshal(method, dbus_path, credentials, options, &msg, g_error)) { + RETURN_DBUS_ERROR(g_error, dbus_error); + } + + if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) { + dbus_message_unref(msg); + RETURN_DBUS_ERROR(g_error, dbus_error); + } + dbus_message_unref(msg); + + if (!dbus_join_leave_unmarshal(reply, g_error)) { + dbus_message_unref(reply); + return FALSE; + } + dbus_message_unref(reply); + return TRUE; +} + + +/*----------------------------------------------------------------------------*/ + +/** + * dbus_join_call: + * @dbus_path The DBus object path of the object supporting the kerberos + * membership interface on which the Join call will be made. + * @credentials A GVariant encoding the credentials according the the credential + * type. See the Realmd DBus interface for specifics. + * @options dictionary of option {key,values} + * @g_error initialized to error info when FALSE is returned. + * + * Marshal a realm Join method call, call it synchronously, + * unmarsh it's reply and return the OUT parameters. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized + */ +gboolean +dbus_join_call(DBusConnection *bus, const gchar *dbus_path, + GVariant *credentials, GVariant *options, GError **g_error) +{ + + return dbus_join_leave_call("Join", bus, dbus_path, credentials, + options, g_error); + +} + + +/** + * dbus_leave_call: + * @dbus_path The DBus object path of the object supporting the kerberos + * membership interface on which the Leave call will be made. + * @credentials A GVariant encoding the credentials according the the credential + * type. See the Realmd DBus interface for specifics. + * @options dictionary of option {key,values} + * @g_error initialized to error info when FALSE is returned. + * + * Marshal a realm Leave method call, call it synchronously, + * unmarsh it's reply and return the OUT parameters. + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized + */ +gboolean +dbus_leave_call(DBusConnection *bus, const gchar *dbus_path, + GVariant *credentials, GVariant *options, GError **g_error) +{ + + return dbus_join_leave_call("Leave", bus, dbus_path, credentials, + options, g_error); + +} + + +/*----------------------------------------------------------------------------*/ +/** + * get_short_dbus_interface_name + * @interface fully qualified DBus interface name + * + * Given a DBus interface name return a friendly short name + * appropriate for users to see. Currently the known short names are: + * + * * "Kerberos" + * * "KerberosMembership" + * * "Realm" + * * "Provider" + * * "Service" + * + * If the interface is not recognized the portion of the interface + * following the last period (".") will be returned. If there is no + * period in the interface name then the entire interface string is returned. + * If the interface is NULL then "(null)" is returned. + * + * Returns: pointer to string, must free with g_free() + */ + +char * +get_short_dbus_interface_name(const char *interface) +{ + char *token = NULL; + + if (interface == NULL) { + return g_strdup("(null)"); + } + + if (strcmp(interface, REALM_DBUS_KERBEROS_INTERFACE) == 0) { + return g_strdup("Kerberos"); + } + if (strcmp(interface, REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE) == 0) { + return g_strdup("KerberosMembership"); + } + if (strcmp(interface, REALM_DBUS_REALM_INTERFACE) == 0) { + return g_strdup("Realm"); + } + if (strcmp(interface, REALM_DBUS_PROVIDER_INTERFACE) == 0) { + return g_strdup("Provider"); + } + if (strcmp(interface, REALM_DBUS_SERVICE_INTERFACE) == 0) { + return g_strdup("Service"); + } + /* Return string which begins after last period */ + if ((token = rindex(interface, '.'))) { + token++; /* skip "." char */ + return g_strdup(token); + } else { + return g_strdup(interface); + } + +} + +/*----------------------------------------------------------------------------*/ + +/** + * rdcp_dbus_initialize: + * @g_error initialized to error info when FALSE is returned. + * + * Initializes the dbus module. + * + * - opens a connection the System Bus, exported as #system_bus + * + * Returns: return TRUE if successful, FALSE if error with @g_error initialized. + */ + +gboolean +rdcp_dbus_initialize(GError **g_error) +{ + DBusError dbus_error = DBUS_ERROR_INIT; + + dbus_error_init(&dbus_error); + + g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE); + + if (!system_bus) { + if ((system_bus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error)) == NULL) { + *g_error = dbus_error_to_gerror(&dbus_error); + g_prefix_error(g_error, "could not connect to System DBus"); + return FALSE; + } + } + + return TRUE; +} |