205 lines
6.3 KiB
C
205 lines
6.3 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"
|
||
|
|
||
|
void match_rule_free(match_rule_t *rule)
|
||
|
{
|
||
|
if (rule) {
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
match_rule_t *match_rule_from_string(char *d)
|
||
|
{
|
||
|
char *key = NULL;
|
||
|
char acc[MATCH_RULE_MAX];
|
||
|
int acci = 0;
|
||
|
match_rule_t *rule;
|
||
|
|
||
|
if (strlen(d) >= MATCH_RULE_MAX) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
rule = calloc(sizeof(match_rule_t), 1);
|
||
|
if (!rule) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
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_rule_check(match_rule_t *rule, wire_message_t *msg, wire_context_t *ctx)
|
||
|
{
|
||
|
if (rule->type && msg->type != rule->type) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
_check_header_field_str(sender, DBUS_HEADER_FIELD_SENDER);
|
||
|
_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);
|
||
|
|
||
|
wire_message_body_string_t strings[MATCH_RULE_MAX_ARG];
|
||
|
memset(strings, 0, sizeof(strings));
|
||
|
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;
|
||
|
}
|