jitterbug/match.c
2023-01-19 03:47:49 +02:00

237 lines
7.2 KiB
C

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "match.h"
#include "util.h"
#include "wire.h"
#include "try.h"
#include "server.h"
void match_rule_free(MatchRule *rule)
{
if (rule) {
free(rule->rule_string);
free(rule->sender);
free(rule->interface);
free(rule->member);
free(rule->path);
free(rule->path_namespace);
free(rule->destination);
free(rule->arg0namespace);
for (int i = 0; i < MATCH_RULE_MAX_ARG; i++) {
free(rule->arg[i]);
free(rule->arg_path[i]);
}
free(rule);
}
}
MatchRule *match_rule_from_string(char *d)
{
char *key = NULL;
char acc[MATCH_RULE_MAX];
int acci = 0;
MatchRule *rule;
if (strlen(d) >= MATCH_RULE_MAX) {
return NULL;
}
rule = calloc(sizeof(MatchRule), 1);
if (!rule) {
return NULL;
}
rule->rule_string = string_dup(d);
while (*d) {
/* key */
key = d;
while (*d && *d != '=') d++;
if (*d != '=') {
goto fail;
}
*d = '\0';
d++;
if (!*d) {
goto fail;
}
/* value */
bool quote = false;
acci = 0;
acc[acci] = '\0';
for (;;) {
if (*d == '\'') {
/* '\'' starts a quoted region */
quote = !quote;
d++;
} else if (!quote && *d == ',') {
/* ',' outside quoted region means next key-value pair */
acc[acci] = '\0';
acci = 0;
d++;
break;
} else if (!quote && *d == '\\') {
/* '\\' outside quoted region means escaped character */
/* TODO: not entirely to spec */
d++;
if (*d != '\'') {
goto fail;
}
if (acci >= MATCH_RULE_MAX) {
goto fail;
}
acc[acci++] = *d;
d++;
} else if (!*d) {
/* end of string */
if (quote) {
/* end of string, yet we were insinde a quoted region */
/* unexpected end of input */
goto fail;
}
acc[acci] = '\0';
acci = 0;
break;
} else if (quote) {
if (acci >= MATCH_RULE_MAX) {
goto fail;
}
acc[acci++] = *d;
d++;
} else {
goto fail;
}
}
if (strcmp(key, "type") == 0 && !rule->type) {
if (strcmp(acc, "signal") == 0) rule->type = DBUS_MESSAGE_SIGNAL;
else if (strcmp(acc, "method_call") == 0) rule->type = DBUS_MESSAGE_METHOD_CALL;
else if (strcmp(acc, "method_return") == 0) rule->type = DBUS_MESSAGE_METHOD_RETURN;
else if (strcmp(acc, "error") == 0) rule->type = DBUS_MESSAGE_ERROR;
else goto fail;
} else if (strcmp(key, "sender") == 0 && !rule->sender) {
rule->sender = string_dup(acc);
} else if (strcmp(key, "interface") == 0 && !rule->interface) {
rule->interface = string_dup(acc);
} else if (strcmp(key, "member") == 0 && !rule->member) {
rule->member = string_dup(acc);
} else if (strcmp(key, "path") == 0 && !rule->path && !rule->path_namespace) {
rule->path = string_dup(acc);
} else if (strcmp(key, "path_namespace") == 0 && !rule->path && !rule->path_namespace) {
rule->path_namespace = string_dup(acc);
} else if (strcmp(key, "destination") == 0 && !rule->destination) {
rule->destination = string_dup(acc);
} else if (strcmp(key, "arg0namespace") == 0 && !rule->arg0namespace) {
rule->arg0namespace = string_dup(acc);
} else if (strncmp(key, "arg", 3) == 0) {
char *part = key + 3;
if (!*part) {
goto fail;
}
// thanks: https://github.com/bus1/dbus-broker/blob/55fe5443d88ee2b93afa340b82ad89508ff017e2/src/bus/match.c#L105
size_t partn = strlen(part);
uint32_t index = 0;
for (unsigned int i = 0; i < 2 && partn; i++, part++, --partn) {
if (*part >= '0' && *part <= '9') {
index = index * 10 + *part - '0';
} else {
break;
}
}
if (index == 0 && rule->arg0namespace) {
goto fail;
}
if (index >= MATCH_RULE_MAX_ARG) {
goto fail;
}
if (rule->arg[index] || rule->arg_path[index]) {
goto fail;
}
if (*part == '\0') {
rule->arg[index] = string_dup(acc);
} else if (strcmp(part, "path") == 0) {
rule->arg_path[index] = string_dup(acc);
} else {
goto fail;
}
} else {
goto fail;
}
}
return rule;
fail:
match_rule_free(rule);
return NULL;
}
#define _check_header_field_str(M_rule_field, M_header_field) \
do { \
if (rule->M_rule_field && (!msg->fields[M_header_field].present || strcmp(rule->M_rule_field, msg->fields[M_header_field].t.str) != 0)) { \
return -1; \
} \
} while(0)
int match_check_sender(BusClient *sender_client, MatchRule *rule)
{
if (rule->sender) {
if (sender_client->unique_name) {
if (strcmp(rule->sender, sender_client->unique_name->name) == 0) {
return 1;
}
}
for (int i = 0; i < BUS_NAMES_PER_CLIENT; i++) {
if (sender_client->owned_names[i] && strcmp(rule->sender, sender_client->owned_names[i]->name) == 0) {
return 1;
}
}
}
return -1;
}
int match_rule_check(BusClient *sender_client, MatchRule *rule, WireMsg *msg, WireCtx *ctx)
{
if (rule->type && msg->type != rule->type) {
return -1;
}
if (!sender_client) {
// if the sender is negative, we assume the message is coming from the message bus
if (rule->sender && strcmp(rule->sender, "org.freedesktop.DBus") != 0) {
return -1;
}
} else {
if (match_check_sender(sender_client, rule) < 0) {
return -1;
}
}
_check_header_field_str(interface, DBUS_HEADER_FIELD_INTERFACE);
_check_header_field_str(member, DBUS_HEADER_FIELD_MEMBER);
_check_header_field_str(path, DBUS_HEADER_FIELD_PATH);
/* todo: path_namespace */
_check_header_field_str(destination, DBUS_HEADER_FIELD_DESTINATION);
WireMsgBodyString strings[MATCH_RULE_MAX_ARG];
memset(strings, 0, sizeof(strings));
/* TODO: handle failure */
TRYST(wire_collect_strings(ctx, msg, strings, MATCH_RULE_MAX_ARG));
for (int i = 0; i < MATCH_RULE_MAX_ARG; i++) {
if (rule->arg[i] && (!strings[i].str || strcmp(strings[i].str, rule->arg[i]) != 0)) {
return -1;
}
/* todo: arg_path */
}
/* todo: arg0namespace */
return 0;
}