Simplify DBus notification assembly

This commit is contained in:
Benedikt Heine 2018-12-04 03:25:28 +01:00
parent a8faea725d
commit b10bb292c6
2 changed files with 136 additions and 136 deletions

View File

@ -142,96 +142,84 @@ static void on_get_capabilities(GDBusConnection *connection,
static struct notification *dbus_message_to_notification(const gchar *sender, GVariant *parameters) static struct notification *dbus_message_to_notification(const gchar *sender, GVariant *parameters)
{ {
/* Assert that the parameters' type is actually correct. Albeit usually DBus
* already rejects ill typed parameters, it may not be always the case. */
GVariantType *required_type = g_variant_type_new("(susssasa{sv}i)");
if (!g_variant_is_of_type(parameters, required_type)) {
g_variant_type_free(required_type);
return NULL;
}
struct notification *n = notification_create(); struct notification *n = notification_create();
n->dbus_client = g_strdup(sender); n->dbus_client = g_strdup(sender);
n->dbus_valid = true; n->dbus_valid = true;
{ GVariant *hints;
GVariantIter *iter = g_variant_iter_new(parameters); gchar **actions;
GVariant *content; int timeout;
GVariant *dict_value;
int idx = 0;
while ((content = g_variant_iter_next_value(iter))) {
switch (idx) { GVariantIter i;
case 0: g_variant_iter_init(&i, parameters);
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
n->appname = g_variant_dup_string(content, NULL);
break;
case 1:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_UINT32))
n->id = g_variant_get_uint32(content);
break;
case 2:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
n->icon = g_variant_dup_string(content, NULL);
break;
case 3:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
n->summary = g_variant_dup_string(content, NULL);
break;
case 4:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING))
n->body = g_variant_dup_string(content, NULL);
break;
case 5:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING_ARRAY)) {
gsize amount;
const gchar **out = g_variant_get_strv(content, &amount);
for(gsize i = 0; i+1 < amount; i+=2) g_variant_iter_next(&i, "s", &n->appname);
g_hash_table_insert(n->actions, g_strdup(out[i]), g_strdup(out[i+1])); g_variant_iter_next(&i, "u", &n->id);
g_variant_iter_next(&i, "s", &n->icon);
g_variant_iter_next(&i, "s", &n->summary);
g_variant_iter_next(&i, "s", &n->body);
g_variant_iter_next(&i, "^a&s", &actions);
g_variant_iter_next(&i, "@a{?*}", &hints);
g_variant_iter_next(&i, "i", &timeout);
g_free(out); gsize num = 0;
while (actions[num]) {
if (actions[num+1]) {
g_hash_table_insert(n->actions,
g_strdup(actions[num]),
g_strdup(actions[num+1]));
num+=2;
} else {
LOG_W("Odd length in actions array. Ignoring element: %s", actions[num]);
break;
}
} }
break;
case 6:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_DICTIONARY)) {
dict_value = g_variant_lookup_value(content, "urgency", G_VARIANT_TYPE_BYTE); GVariant *dict_value;
if (dict_value) { if ((dict_value = g_variant_lookup_value(hints, "urgency", G_VARIANT_TYPE_BYTE))) {
n->urgency = g_variant_get_byte(dict_value); n->urgency = g_variant_get_byte(dict_value);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
dict_value = g_variant_lookup_value(content, "fgcolor", G_VARIANT_TYPE_STRING); if ((dict_value = g_variant_lookup_value(hints, "fgcolor", G_VARIANT_TYPE_STRING))) {
if (dict_value) {
n->colors.fg = g_variant_dup_string(dict_value, NULL); n->colors.fg = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
dict_value = g_variant_lookup_value(content, "bgcolor", G_VARIANT_TYPE_STRING); if ((dict_value = g_variant_lookup_value(hints, "bgcolor", G_VARIANT_TYPE_STRING))) {
if (dict_value) {
n->colors.bg = g_variant_dup_string(dict_value, NULL); n->colors.bg = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
dict_value = g_variant_lookup_value(content, "frcolor", G_VARIANT_TYPE_STRING); if ((dict_value = g_variant_lookup_value(hints, "frcolor", G_VARIANT_TYPE_STRING))) {
if (dict_value) {
n->colors.frame = g_variant_dup_string(dict_value, NULL); n->colors.frame = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
dict_value = g_variant_lookup_value(content, "category", G_VARIANT_TYPE_STRING); if ((dict_value = g_variant_lookup_value(hints, "category", G_VARIANT_TYPE_STRING))) {
if (dict_value) {
n->category = g_variant_dup_string(dict_value, NULL); n->category = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
dict_value = g_variant_lookup_value(content, "image-path", G_VARIANT_TYPE_STRING); if ((dict_value = g_variant_lookup_value(hints, "image-path", G_VARIANT_TYPE_STRING))) {
if (dict_value) {
g_free(n->icon); g_free(n->icon);
n->icon = g_variant_dup_string(dict_value, NULL); n->icon = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
dict_value = g_variant_lookup_value(content, "image-data", G_VARIANT_TYPE("(iiibiiay)")); dict_value = g_variant_lookup_value(hints, "image-data", G_VARIANT_TYPE("(iiibiiay)"));
if (!dict_value) if (!dict_value)
dict_value = g_variant_lookup_value(content, "image_data", G_VARIANT_TYPE("(iiibiiay)")); dict_value = g_variant_lookup_value(hints, "image_data", G_VARIANT_TYPE("(iiibiiay)"));
if (!dict_value) if (!dict_value)
dict_value = g_variant_lookup_value(content, "icon_data", G_VARIANT_TYPE("(iiibiiay)")); dict_value = g_variant_lookup_value(hints, "icon_data", G_VARIANT_TYPE("(iiibiiay)"));
if (dict_value) { if (dict_value) {
n->raw_icon = get_raw_image_from_data_hint(dict_value); n->raw_icon = get_raw_image_from_data_hint(dict_value);
g_variant_unref(dict_value); g_variant_unref(dict_value);
@ -243,21 +231,21 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
* But notify-send does not support hints of type 'boolean'. * But notify-send does not support hints of type 'boolean'.
* So let's check for int and boolean until notify-send is fixed. * So let's check for int and boolean until notify-send is fixed.
*/ */
if((dict_value = g_variant_lookup_value(content, "transient", G_VARIANT_TYPE_BOOLEAN))) { if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_BOOLEAN))) {
n->transient = g_variant_get_boolean(dict_value); n->transient = g_variant_get_boolean(dict_value);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} else if((dict_value = g_variant_lookup_value(content, "transient", G_VARIANT_TYPE_UINT32))) { } else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_UINT32))) {
n->transient = g_variant_get_uint32(dict_value) > 0; n->transient = g_variant_get_uint32(dict_value) > 0;
g_variant_unref(dict_value); g_variant_unref(dict_value);
} else if((dict_value = g_variant_lookup_value(content, "transient", G_VARIANT_TYPE_INT32))) { } else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_INT32))) {
n->transient = g_variant_get_int32(dict_value) > 0; n->transient = g_variant_get_int32(dict_value) > 0;
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
if((dict_value = g_variant_lookup_value(content, "value", G_VARIANT_TYPE_INT32))) { if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_INT32))) {
n->progress = g_variant_get_int32(dict_value); n->progress = g_variant_get_int32(dict_value);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} else if((dict_value = g_variant_lookup_value(content, "value", G_VARIANT_TYPE_UINT32))) { } else if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_UINT32))) {
n->progress = g_variant_get_uint32(dict_value); n->progress = g_variant_get_uint32(dict_value);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
@ -267,27 +255,19 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
* Only accept to first one we find. * Only accept to first one we find.
*/ */
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i) { for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i) {
dict_value = g_variant_lookup_value(content, stack_tag_hints[i], G_VARIANT_TYPE_STRING); if ((dict_value = g_variant_lookup_value(hints, stack_tag_hints[i], G_VARIANT_TYPE_STRING))) {
if (dict_value) {
n->stack_tag = g_variant_dup_string(dict_value, NULL); n->stack_tag = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value); g_variant_unref(dict_value);
break; break;
} }
} }
} if (timeout >= 0)
break; n->timeout = timeout * 1000;
case 7:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_INT32))
n->timeout = g_variant_get_int32(content) * 1000;
break;
}
g_variant_unref(content);
idx++;
}
g_variant_iter_free(iter); g_variant_unref(hints);
} g_variant_type_free(required_type);
g_free(actions); // the strv is only a shallow copy
notification_init(n); notification_init(n);
return n; return n;
@ -299,6 +279,13 @@ static void on_notify(GDBusConnection *connection,
GDBusMethodInvocation *invocation) GDBusMethodInvocation *invocation)
{ {
struct notification *n = dbus_message_to_notification(sender, parameters); struct notification *n = dbus_message_to_notification(sender, parameters);
if (!n) {
g_dbus_method_invocation_return_dbus_error(
invocation,
"Cannot decode notification!",
"");
}
int id = queues_notification_insert(n); int id = queues_notification_insert(n);
GVariant *reply = g_variant_new("(u)", id); GVariant *reply = g_variant_new("(u)", id);

View File

@ -145,6 +145,18 @@ TEST test_dbus_teardown(void)
PASS(); PASS();
} }
TEST test_invalid_notification(void)
{
GVariant *faulty = g_variant_new_boolean(true);
ASSERT(NULL == dbus_message_to_notification(":123", faulty));
ASSERT(NULL == dbus_invoke("Notify", faulty));
g_variant_unref(faulty);
PASS();
}
TEST test_basic_notification(void) TEST test_basic_notification(void)
{ {
struct dbus_notification *n = dbus_notification_new(); struct dbus_notification *n = dbus_notification_new();
@ -210,6 +222,7 @@ gpointer run_threaded_tests(gpointer data)
RUN_TEST(test_dbus_init); RUN_TEST(test_dbus_init);
RUN_TEST(test_basic_notification); RUN_TEST(test_basic_notification);
RUN_TEST(test_invalid_notification);
RUN_TESTp(test_server_caps, MARKUP_FULL); RUN_TESTp(test_server_caps, MARKUP_FULL);
RUN_TESTp(test_server_caps, MARKUP_STRIP); RUN_TESTp(test_server_caps, MARKUP_STRIP);
RUN_TESTp(test_server_caps, MARKUP_NO); RUN_TESTp(test_server_caps, MARKUP_NO);