1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* IBus - The Input Bus
4 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5 * Copyright (C) 2008-2010 Red Hat, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22
23 #include "matchrule.h"
24
25 #include <string.h>
26
27 #include "dbusimpl.h"
28
29 typedef enum {
30 MATCH_TYPE = 1 << 0,
31 MATCH_INTERFACE = 1 << 1,
32 MATCH_MEMBER = 1 << 2,
33 MATCH_SENDER = 1 << 3,
34 MATCH_DESTINATION = 1 << 4,
35 MATCH_PATH = 1 << 5,
36 MATCH_ARGS = 1 << 6,
37 } BusMatchFlags;
38
39 struct _BusMatchRule {
40 IBusObject parent;
41 /* instance members */
42 gint flags;
43 gint message_type;
44 gchar *interface;
45 gchar *member;
46 gchar *sender;
47 gchar *destination;
48 gchar *path;
49 GArray *args;
50 GList *recipients;
51 };
52
53 struct _BusMatchRuleClass {
54 IBusObjectClass parent;
55 /* class members */
56 };
57
58 typedef struct _BusRecipient BusRecipient;
59 struct _BusRecipient {
60 BusConnection *connection;
61 gint refcount;
62 };
63
64 static BusRecipient *bus_recipient_new (BusConnection *connection);
65 static void bus_recipient_free (BusRecipient *recipient);
66 static BusRecipient *bus_recipient_ref (BusRecipient *recipient);
67 static gboolean bus_recipient_unref (BusRecipient *recipient);
68 static void bus_match_rule_destroy (BusMatchRule *rule);
69 static void bus_match_rule_connection_destroy_cb
70 (BusConnection *connection,
71 BusMatchRule *rule);
72
73 static BusRecipient *
bus_recipient_new(BusConnection * connection)74 bus_recipient_new (BusConnection *connection)
75 {
76 BusRecipient *recipient = g_slice_new (BusRecipient);
77 g_object_ref (connection);
78 recipient->connection = connection;
79 recipient->refcount = 1;
80 return recipient;
81 }
82
83 static void
bus_recipient_free(BusRecipient * recipient)84 bus_recipient_free (BusRecipient *recipient)
85 {
86 g_object_unref (recipient->connection);
87 g_slice_free (BusRecipient, recipient);
88 }
89
90 static BusRecipient *
bus_recipient_ref(BusRecipient * recipient)91 bus_recipient_ref (BusRecipient *recipient)
92 {
93 recipient->refcount ++;
94 return recipient;
95 }
96
97 static gboolean
bus_recipient_unref(BusRecipient * recipient)98 bus_recipient_unref (BusRecipient *recipient)
99 {
100 recipient->refcount --;
101 if (recipient->refcount == 0) {
102 bus_recipient_free (recipient);
103 return TRUE;
104 }
105 return FALSE;
106 }
107
G_DEFINE_TYPE(BusMatchRule,bus_match_rule,IBUS_TYPE_OBJECT)108 G_DEFINE_TYPE (BusMatchRule, bus_match_rule, IBUS_TYPE_OBJECT)
109
110 static void
111 bus_match_rule_class_init (BusMatchRuleClass *class)
112 {
113 IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
114
115 ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_match_rule_destroy;
116 }
117
118 static void
bus_match_rule_init(BusMatchRule * rule)119 bus_match_rule_init (BusMatchRule *rule)
120 {
121 rule->flags = 0;
122 rule->message_type = G_DBUS_MESSAGE_TYPE_INVALID;
123 rule->interface = NULL;
124 rule->member = NULL;
125 rule->sender = NULL;
126 rule->destination = NULL;
127 rule->path = NULL;
128 rule->args = g_array_new (TRUE, TRUE, sizeof (gchar *));
129 }
130
131 static void
bus_match_rule_destroy(BusMatchRule * rule)132 bus_match_rule_destroy (BusMatchRule *rule)
133 {
134 g_free (rule->interface);
135 g_free (rule->member);
136 g_free (rule->sender);
137 g_free (rule->destination);
138 g_free (rule->path);
139
140 gint i;
141 for (i = 0; i < rule->args->len; i++) {
142 g_free (g_array_index (rule->args, gchar *, i));
143 }
144 g_array_free (rule->args, TRUE);
145
146 GList *p;
147 for (p = rule->recipients; p != NULL; p = p->next) {
148 BusRecipient *recipient = (BusRecipient *) p->data;
149 g_signal_handlers_disconnect_by_func (recipient->connection,
150 G_CALLBACK (bus_match_rule_connection_destroy_cb), rule);
151 bus_recipient_free (recipient);
152 }
153 g_list_free (rule->recipients);
154
155 IBUS_OBJECT_CLASS(bus_match_rule_parent_class)->destroy (IBUS_OBJECT (rule));
156 }
157
158
159 typedef struct _Token {
160 gchar *key;
161 gchar *value;
162 } Token;
163
164 #define SKIP_WHITE(a) \
165 while (*(a) == ' ' || *(a) == '\t' || *(a) == '\n') { (a)++; }
166 #define IS_ALPHA(a) \
167 ((*(a) >= 'a' && *(a) <= 'z') || (*(a) >= 'A' && *(a) <= 'Z'))
168 #define IS_NUMBER(a) \
169 (*(a) >= '0' && *(a) <= '9')
170
171 static gchar *
find_key(const gchar ** p)172 find_key (const gchar **p)
173 {
174 GString *text;
175
176 text = g_string_new ("");
177
178 SKIP_WHITE(*p)
179 if (!IS_ALPHA (*p))
180 goto failed;
181
182 g_string_append_c (text, **p);
183 (*p) ++;
184
185 while (IS_ALPHA (*p) || IS_NUMBER (*p)) {
186 g_string_append_c (text, **p);
187 (*p) ++;
188 }
189
190 return g_string_free (text, FALSE);
191
192 failed:
193 g_string_free (text, TRUE);
194 return NULL;
195
196 }
197
198 static gchar *
find_value(const gchar ** p)199 find_value (const gchar **p)
200 {
201 SKIP_WHITE (*p);
202
203 if (**p == '\'') {
204 GString *text = g_string_new ("");
205 (*p) ++;
206 while (**p != '\'') {
207 if (**p == '\0') {
208 g_string_free (text, TRUE);
209 return NULL;
210 }
211 if (**p == '\\')
212 (*p) ++;
213 g_string_append_c (text, **p);
214 (*p) ++;
215 }
216 (*p) ++;
217 return g_string_free (text, FALSE);
218 } else if (strncmp (*p, "true", 4) == 0) {
219 *p += 4;
220 return g_strdup ("true");
221 } else if (strncmp (*p, "false", 5) == 0) {
222 *p += 5;
223 return g_strdup ("false");
224 }
225
226 return NULL;
227 }
228
229 static Token *
tokenize_rule(const gchar * text)230 tokenize_rule (const gchar *text)
231 {
232 GArray *tokens;
233 Token token;
234 const gchar *p;
235 gint i;
236
237 tokens = g_array_new (TRUE, TRUE, sizeof (Token));
238
239 p = text;
240
241 while (*p != '\0') {
242 gchar *key;
243 gchar *value;
244
245 SKIP_WHITE (p);
246 key = find_key (&p);
247 if (key == NULL)
248 goto failed;
249 SKIP_WHITE (p);
250 if (*p != '=')
251 goto failed;
252 p ++;
253 SKIP_WHITE (p);
254 value = find_value (&p);
255 if (value == NULL) {
256 g_free (key);
257 goto failed;
258 }
259 SKIP_WHITE (p);
260 if (*p != ',' && *p != '\0') {
261 g_free (key);
262 g_free (value);
263 goto failed;
264 }
265
266 if (*p == ',')
267 p ++;
268 token.key = key;
269 token.value = value;
270 g_array_append_val (tokens, token);
271 }
272
273 return (Token *)g_array_free (tokens, FALSE);
274
275 failed:
276 for (i = 0; i < tokens->len; i++) {
277 Token *p = &g_array_index (tokens, Token, i);
278 g_free (p->key);
279 g_free (p->value);
280 }
281
282 g_array_free (tokens, TRUE);
283 return NULL;
284 }
285
286 static void
tokens_free(Token * tokens)287 tokens_free (Token *tokens)
288 {
289 Token *p;
290 p = tokens;
291
292 while (p != NULL && p->key != NULL) {
293 g_free (p->key);
294 g_free (p->value);
295 p ++;
296 }
297 g_free (tokens);
298 }
299
300 static gboolean
_atoi(const gchar * text,gint * i)301 _atoi (const gchar *text, gint *i)
302 {
303 const gchar *p = text;
304 *i = 0;
305 while (*p != '\0') {
306 if (!IS_NUMBER(p))
307 return FALSE;
308 *i = (*i) * 10 - '0' + *p;
309 p ++;
310 }
311 return TRUE;
312 }
313
314 BusMatchRule *
bus_match_rule_new(const gchar * text)315 bus_match_rule_new (const gchar *text)
316 {
317 g_assert (text != NULL);
318
319 Token *tokens, *p;
320 BusMatchRule *rule;
321
322 rule = BUS_MATCH_RULE (g_object_new (BUS_TYPE_MATCH_RULE, NULL));
323
324 /* parse rule */
325 tokens = tokenize_rule (text);
326
327 if (tokens == NULL)
328 goto failed;
329
330 for (p = tokens; p != NULL && p->key != 0; p++) {
331 if (g_strcmp0 (p->key, "type") == 0) {
332 if (g_strcmp0 (p->value, "signal") == 0) {
333 bus_match_rule_set_message_type (rule, G_DBUS_MESSAGE_TYPE_SIGNAL);
334 }
335 else if (g_strcmp0 (p->value, "method_call") == 0) {
336 bus_match_rule_set_message_type (rule, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
337 }
338 else if (g_strcmp0 (p->value, "method_return") == 0) {
339 bus_match_rule_set_message_type (rule, G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
340 }
341 else if (g_strcmp0 (p->value, "error") == 0) {
342 bus_match_rule_set_message_type (rule, G_DBUS_MESSAGE_TYPE_ERROR);
343 }
344 else
345 goto failed;
346 }
347 else if (g_strcmp0 (p->key, "sender") == 0) {
348 if (!g_dbus_is_name (p->value))
349 goto failed;
350 bus_match_rule_set_sender (rule, p->value);
351 }
352 else if (g_strcmp0 (p->key, "interface") == 0) {
353 if (!g_dbus_is_interface_name (p->value))
354 goto failed;
355 bus_match_rule_set_interface (rule, p->value);
356 }
357 else if (g_strcmp0 (p->key, "member") == 0) {
358 if (!g_dbus_is_member_name (p->value))
359 goto failed;
360 bus_match_rule_set_member (rule, p->value);
361 }
362 else if (g_strcmp0 (p->key, "path") == 0) {
363 bus_match_rule_set_path (rule, p->value);
364 }
365 else if (g_strcmp0 (p->key, "destination") == 0) {
366 if (!g_dbus_is_name (p->value))
367 goto failed;
368 bus_match_rule_set_destination (rule, p->value);
369 }
370 else if (strncmp (p->key, "arg", 3) == 0) {
371 gint i;
372 if (! _atoi (p->key + 3, &i))
373 goto failed;
374 bus_match_rule_set_arg (rule, i, p->value);
375 }
376 else if (g_strcmp0 (p->key, "eavesdrop") == 0) {
377 if (g_strcmp0 (p->value, "true") != 0 &&
378 g_strcmp0 (p->value, "false") != 0)
379 goto failed;
380 }
381 else
382 goto failed;
383 }
384
385 tokens_free (tokens);
386 return rule;
387
388 failed:
389 tokens_free (tokens);
390 g_object_unref (rule);
391 return NULL;
392 }
393
394 gboolean
bus_match_rule_set_message_type(BusMatchRule * rule,gint type)395 bus_match_rule_set_message_type (BusMatchRule *rule,
396 gint type)
397 {
398 g_assert (rule != NULL);
399 g_assert (type == G_DBUS_MESSAGE_TYPE_SIGNAL ||
400 type == G_DBUS_MESSAGE_TYPE_METHOD_CALL ||
401 type == G_DBUS_MESSAGE_TYPE_METHOD_RETURN ||
402 type == G_DBUS_MESSAGE_TYPE_ERROR);
403
404 rule->flags |= MATCH_TYPE;
405 rule->message_type = type;
406
407 return TRUE;
408 }
409
410 gboolean
bus_match_rule_set_sender(BusMatchRule * rule,const gchar * sender)411 bus_match_rule_set_sender (BusMatchRule *rule,
412 const gchar *sender)
413 {
414 g_assert (rule != NULL);
415 g_assert (sender != NULL);
416
417 rule->flags |= MATCH_SENDER;
418
419 g_free (rule->sender);
420 rule->sender = g_strdup (sender);
421
422 return TRUE;
423 }
424
425 gboolean
bus_match_rule_set_interface(BusMatchRule * rule,const gchar * interface)426 bus_match_rule_set_interface (BusMatchRule *rule,
427 const gchar *interface)
428 {
429 g_assert (rule != NULL);
430 g_assert (interface != NULL);
431
432 rule->flags |= MATCH_INTERFACE;
433
434 g_free (rule->interface);
435 rule->interface = g_strdup (interface);
436 return TRUE;
437 }
438
439 gboolean
bus_match_rule_set_member(BusMatchRule * rule,const gchar * member)440 bus_match_rule_set_member (BusMatchRule *rule,
441 const gchar *member)
442 {
443 g_assert (rule != NULL);
444 g_assert (member != NULL);
445
446 rule->flags |= MATCH_MEMBER;
447
448 g_free (rule->member);
449 rule->member = g_strdup (member);
450
451 return TRUE;
452 }
453
454 gboolean
bus_match_rule_set_path(BusMatchRule * rule,const gchar * path)455 bus_match_rule_set_path (BusMatchRule *rule,
456 const gchar *path)
457 {
458 g_assert (rule != NULL);
459 g_assert (path != NULL);
460
461 rule->flags |= MATCH_PATH;
462
463 g_free (rule->path);
464 rule->path = g_strdup (path);
465
466 return TRUE;
467 }
468
469 gboolean
bus_match_rule_set_destination(BusMatchRule * rule,const gchar * dest)470 bus_match_rule_set_destination (BusMatchRule *rule,
471 const gchar *dest)
472 {
473 g_assert (rule != NULL);
474 g_assert (dest != NULL);
475
476 rule->flags |= MATCH_DESTINATION;
477
478 g_free (rule->destination);
479 rule->destination = g_strdup (dest);
480
481 return TRUE;
482 }
483
484 gboolean
bus_match_rule_set_arg(BusMatchRule * rule,guint arg_i,const gchar * arg)485 bus_match_rule_set_arg (BusMatchRule *rule,
486 guint arg_i,
487 const gchar *arg)
488 {
489 g_assert (rule != NULL);
490 g_assert (arg != NULL);
491
492 rule->flags |= MATCH_ARGS;
493
494 if (arg_i >= rule->args->len) {
495 g_array_set_size (rule->args, arg_i + 1);
496 }
497
498 g_free (g_array_index (rule->args, gchar *, arg_i));
499 g_array_index (rule->args, gchar *, arg_i) = g_strdup (arg);
500 return TRUE;
501 }
502
503 static gboolean
bus_match_rule_match_name(const gchar * name,const gchar * match_name)504 bus_match_rule_match_name (const gchar *name,
505 const gchar *match_name)
506 {
507 if (g_dbus_is_unique_name (name) && !g_dbus_is_unique_name (match_name)) {
508 BusConnection *connection =
509 bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, match_name);
510 if (connection == NULL)
511 return FALSE;
512 return g_strcmp0 (name, bus_connection_get_unique_name (connection)) == 0;
513 }
514 return g_strcmp0 (name, match_name) == 0;
515 }
516
517 gboolean
bus_match_rule_match(BusMatchRule * rule,GDBusMessage * message)518 bus_match_rule_match (BusMatchRule *rule,
519 GDBusMessage *message)
520 {
521 g_assert (rule != NULL);
522 g_assert (message != NULL);
523
524 if (rule->flags & MATCH_TYPE) {
525 if (g_dbus_message_get_message_type (message) != rule->message_type)
526 return FALSE;
527 }
528
529 if (rule->flags & MATCH_INTERFACE) {
530 if (g_strcmp0 (g_dbus_message_get_interface (message), rule->interface) != 0)
531 return FALSE;
532 }
533
534 if (rule->flags & MATCH_MEMBER) {
535 if (g_strcmp0 (g_dbus_message_get_member (message), rule->member) != 0)
536 return FALSE;
537 }
538
539 if (rule->flags & MATCH_SENDER) {
540 if (!bus_match_rule_match_name (g_dbus_message_get_sender (message), rule->sender))
541 return FALSE;
542 }
543
544 if (rule->flags & MATCH_DESTINATION) {
545 if (!bus_match_rule_match_name (g_dbus_message_get_destination (message), rule->destination))
546 return FALSE;
547 }
548
549 if (rule->flags & MATCH_PATH) {
550 if (g_strcmp0 (g_dbus_message_get_path (message), rule->path) != 0)
551 return FALSE;
552 }
553
554 if (rule->flags & MATCH_ARGS) {
555 guint i;
556 GVariant *arguments = g_dbus_message_get_body (message);
557 if (arguments == NULL)
558 return FALSE;
559
560 for (i = 0; i < rule->args->len; i++) {
561 const gchar *arg = g_array_index (rule->args, const gchar *, i);
562 if (arg == NULL)
563 continue;
564 GVariant * variant = g_variant_get_child_value (arguments, i);
565 if (variant == NULL)
566 return FALSE;
567 switch (g_variant_classify (variant)) {
568 case G_VARIANT_CLASS_STRING:
569 case G_VARIANT_CLASS_OBJECT_PATH:
570 if (g_strcmp0 (arg, g_variant_get_string (variant, NULL)) == 0) {
571 g_variant_unref (variant);
572 continue;
573 }
574 default:
575 break;
576 }
577 g_variant_unref (variant);
578 return FALSE;
579 }
580 }
581 return TRUE;
582 }
583
584 gboolean
bus_match_rule_is_equal(BusMatchRule * a,BusMatchRule * b)585 bus_match_rule_is_equal (BusMatchRule *a,
586 BusMatchRule *b)
587 {
588 g_assert (a != NULL);
589 g_assert (b != NULL);
590
591 if (a->flags != b->flags)
592 return FALSE;
593
594 if (a->flags & MATCH_TYPE) {
595 if (a->message_type != b->message_type)
596 return FALSE;
597 }
598
599 if (a->flags & MATCH_INTERFACE) {
600 if (g_strcmp0 (a->interface, b->interface) != 0)
601 return FALSE;
602 }
603
604 if (a->flags & MATCH_MEMBER) {
605 if (g_strcmp0 (a->member, b->member) != 0)
606 return FALSE;
607 }
608
609 if (a->flags & MATCH_SENDER) {
610 if (g_strcmp0 (a->sender, b->sender) != 0)
611 return FALSE;
612 }
613
614 if (a->flags & MATCH_DESTINATION) {
615 if (g_strcmp0 (a->destination, b->destination) != 0)
616 return FALSE;
617 }
618
619 if (a->flags & MATCH_PATH) {
620 if (g_strcmp0 (a->path, b->path) != 0)
621 return FALSE;
622 }
623
624 if (a->flags & MATCH_ARGS) {
625 if (a->args->len != b->args->len)
626 return FALSE;
627
628 gint i;
629
630 for (i = 0; i < a->args->len; i++) {
631 if (g_strcmp0 (g_array_index (a->args, gchar *, i),
632 g_array_index (b->args, gchar *, i)) != 0)
633 return FALSE;
634 }
635 }
636
637 return TRUE;
638 }
639
640 static void
bus_match_rule_connection_destroy_cb(BusConnection * connection,BusMatchRule * rule)641 bus_match_rule_connection_destroy_cb (BusConnection *connection,
642 BusMatchRule *rule)
643 {
644 GList *p;
645 for (p = rule->recipients; p != NULL; p = p->next) {
646 BusRecipient *recipient = (BusRecipient *)p->data;
647
648 if (recipient->connection == connection) {
649 rule->recipients = g_list_remove_link (rule->recipients, p);
650 bus_recipient_free (recipient);
651 return;
652 }
653
654 if (rule->recipients == NULL) {
655 ibus_object_destroy (IBUS_OBJECT (rule));
656 }
657 }
658 g_assert_not_reached ();
659 }
660
661 void
bus_match_rule_add_recipient(BusMatchRule * rule,BusConnection * connection)662 bus_match_rule_add_recipient (BusMatchRule *rule,
663 BusConnection *connection)
664 {
665 g_assert (BUS_IS_MATCH_RULE (rule));
666 g_assert (BUS_IS_CONNECTION (connection));
667
668 GList *p;
669 for (p = rule->recipients; p != NULL; p = p->next) {
670 BusRecipient *recipient = (BusRecipient *) p->data;
671 if (connection == recipient->connection) {
672 bus_recipient_ref (recipient);
673 return;
674 }
675 }
676
677 /* alloc a new recipient */
678 BusRecipient *recipient = bus_recipient_new (connection);
679 rule->recipients = g_list_append (rule->recipients, recipient);
680 g_signal_connect (connection,
681 "destroy",
682 G_CALLBACK (bus_match_rule_connection_destroy_cb),
683 rule);
684 }
685
686 void
bus_match_rule_remove_recipient(BusMatchRule * rule,BusConnection * connection)687 bus_match_rule_remove_recipient (BusMatchRule *rule,
688 BusConnection *connection)
689 {
690 g_assert (BUS_IS_MATCH_RULE (rule));
691 g_assert (BUS_IS_CONNECTION (connection));
692
693 GList *p;
694 for (p = rule->recipients; p != NULL; p = p->next) {
695 BusRecipient *recipient = (BusRecipient *) p->data;
696 if (connection == recipient->connection) {
697 if (bus_recipient_unref (recipient)) {
698 rule->recipients = g_list_remove_link (rule->recipients, p);
699 g_signal_handlers_disconnect_by_func (connection,
700 G_CALLBACK (bus_match_rule_connection_destroy_cb),
701 rule);
702 }
703
704 if (rule->recipients == NULL ) {
705 ibus_object_destroy (IBUS_OBJECT(rule));
706 }
707 return;
708 }
709 }
710 g_return_if_reached ();
711 }
712
713 GList *
bus_match_rule_get_recipients(BusMatchRule * rule,GDBusMessage * message)714 bus_match_rule_get_recipients (BusMatchRule *rule,
715 GDBusMessage *message)
716 {
717 g_assert (BUS_IS_MATCH_RULE (rule));
718 g_assert (message != NULL);
719
720 GList *link;
721 GList *recipients = NULL;
722
723 if (!bus_match_rule_match (rule, message))
724 return FALSE;
725
726 for (link = rule->recipients; link != NULL; link = link->next) {
727 BusRecipient *recipient = (BusRecipient *) link->data;
728
729 recipients = g_list_append (recipients, recipient->connection);
730 }
731
732 return recipients;
733 }
734
735