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