00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "signals.h"
00024 #include "services.h"
00025 #include "utils.h"
00026
00027 struct BusMatchRule
00028 {
00029 int refcount;
00031 DBusConnection *matches_go_to;
00033 unsigned int flags;
00035 int message_type;
00036 char *interface;
00037 char *member;
00038 char *sender;
00039 char *destination;
00040 char *path;
00041 };
00042
00043 BusMatchRule*
00044 bus_match_rule_new (DBusConnection *matches_go_to)
00045 {
00046 BusMatchRule *rule;
00047
00048 rule = dbus_new0 (BusMatchRule, 1);
00049 if (rule == NULL)
00050 return NULL;
00051
00052 rule->refcount = 1;
00053 rule->matches_go_to = matches_go_to;
00054
00055 return rule;
00056 }
00057
00058 void
00059 bus_match_rule_ref (BusMatchRule *rule)
00060 {
00061 _dbus_assert (rule->refcount > 0);
00062
00063 rule->refcount += 1;
00064 }
00065
00066 void
00067 bus_match_rule_unref (BusMatchRule *rule)
00068 {
00069 _dbus_assert (rule->refcount > 0);
00070
00071 rule->refcount -= 1;
00072 if (rule->refcount == 0)
00073 {
00074 dbus_free (rule->interface);
00075 dbus_free (rule->member);
00076 dbus_free (rule->sender);
00077 dbus_free (rule->destination);
00078 dbus_free (rule->path);
00079 dbus_free (rule);
00080 }
00081 }
00082
00083 #ifdef DBUS_ENABLE_VERBOSE_MODE
00084 static char*
00085 match_rule_to_string (BusMatchRule *rule)
00086 {
00087 DBusString str;
00088 char *ret;
00089
00090 if (!_dbus_string_init (&str))
00091 {
00092 char *s;
00093 while ((s = _dbus_strdup ("nomem")) == NULL)
00094 ;
00095 return s;
00096 }
00097
00098 if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
00099 {
00100
00101 if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
00102 goto nomem;
00103 }
00104
00105 if (rule->flags & BUS_MATCH_INTERFACE)
00106 {
00107 if (_dbus_string_get_length (&str) > 0)
00108 {
00109 if (!_dbus_string_append (&str, ","))
00110 goto nomem;
00111 }
00112
00113 if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface))
00114 goto nomem;
00115 }
00116
00117 if (rule->flags & BUS_MATCH_MEMBER)
00118 {
00119 if (_dbus_string_get_length (&str) > 0)
00120 {
00121 if (!_dbus_string_append (&str, ","))
00122 goto nomem;
00123 }
00124
00125 if (!_dbus_string_append_printf (&str, "member='%s'", rule->member))
00126 goto nomem;
00127 }
00128
00129 if (rule->flags & BUS_MATCH_PATH)
00130 {
00131 if (_dbus_string_get_length (&str) > 0)
00132 {
00133 if (!_dbus_string_append (&str, ","))
00134 goto nomem;
00135 }
00136
00137 if (!_dbus_string_append_printf (&str, "path='%s'", rule->path))
00138 goto nomem;
00139 }
00140
00141 if (rule->flags & BUS_MATCH_SENDER)
00142 {
00143 if (_dbus_string_get_length (&str) > 0)
00144 {
00145 if (!_dbus_string_append (&str, ","))
00146 goto nomem;
00147 }
00148
00149 if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender))
00150 goto nomem;
00151 }
00152
00153 if (rule->flags & BUS_MATCH_DESTINATION)
00154 {
00155 if (_dbus_string_get_length (&str) > 0)
00156 {
00157 if (!_dbus_string_append (&str, ","))
00158 goto nomem;
00159 }
00160
00161 if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination))
00162 goto nomem;
00163 }
00164
00165 if (!_dbus_string_steal_data (&str, &ret))
00166 goto nomem;
00167
00168 _dbus_string_free (&str);
00169 return ret;
00170
00171 nomem:
00172 _dbus_string_free (&str);
00173 {
00174 char *s;
00175 while ((s = _dbus_strdup ("nomem")) == NULL)
00176 ;
00177 return s;
00178 }
00179 }
00180 #endif
00181
00182 dbus_bool_t
00183 bus_match_rule_set_message_type (BusMatchRule *rule,
00184 int type)
00185 {
00186 rule->flags |= BUS_MATCH_MESSAGE_TYPE;
00187
00188 rule->message_type = type;
00189
00190 return TRUE;
00191 }
00192
00193 dbus_bool_t
00194 bus_match_rule_set_interface (BusMatchRule *rule,
00195 const char *interface)
00196 {
00197 char *new;
00198
00199 _dbus_assert (interface != NULL);
00200
00201 new = _dbus_strdup (interface);
00202 if (new == NULL)
00203 return FALSE;
00204
00205 rule->flags |= BUS_MATCH_INTERFACE;
00206 dbus_free (rule->interface);
00207 rule->interface = new;
00208
00209 return TRUE;
00210 }
00211
00212 dbus_bool_t
00213 bus_match_rule_set_member (BusMatchRule *rule,
00214 const char *member)
00215 {
00216 char *new;
00217
00218 _dbus_assert (member != NULL);
00219
00220 new = _dbus_strdup (member);
00221 if (new == NULL)
00222 return FALSE;
00223
00224 rule->flags |= BUS_MATCH_MEMBER;
00225 dbus_free (rule->member);
00226 rule->member = new;
00227
00228 return TRUE;
00229 }
00230
00231 dbus_bool_t
00232 bus_match_rule_set_sender (BusMatchRule *rule,
00233 const char *sender)
00234 {
00235 char *new;
00236
00237 _dbus_assert (sender != NULL);
00238
00239 new = _dbus_strdup (sender);
00240 if (new == NULL)
00241 return FALSE;
00242
00243 rule->flags |= BUS_MATCH_SENDER;
00244 dbus_free (rule->sender);
00245 rule->sender = new;
00246
00247 return TRUE;
00248 }
00249
00250 dbus_bool_t
00251 bus_match_rule_set_destination (BusMatchRule *rule,
00252 const char *destination)
00253 {
00254 char *new;
00255
00256 _dbus_assert (destination != NULL);
00257
00258 new = _dbus_strdup (destination);
00259 if (new == NULL)
00260 return FALSE;
00261
00262 rule->flags |= BUS_MATCH_DESTINATION;
00263 dbus_free (rule->destination);
00264 rule->destination = new;
00265
00266 return TRUE;
00267 }
00268
00269 dbus_bool_t
00270 bus_match_rule_set_path (BusMatchRule *rule,
00271 const char *path)
00272 {
00273 char *new;
00274
00275 _dbus_assert (path != NULL);
00276
00277 new = _dbus_strdup (path);
00278 if (new == NULL)
00279 return FALSE;
00280
00281 rule->flags |= BUS_MATCH_PATH;
00282 dbus_free (rule->path);
00283 rule->path = new;
00284
00285 return TRUE;
00286 }
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 BusMatchRule*
00297 bus_match_rule_parse (DBusConnection *matches_go_to,
00298 const DBusString *rule_text,
00299 DBusError *error)
00300 {
00301 BusMatchRule *rule;
00302
00303 rule = bus_match_rule_new (matches_go_to);
00304 if (rule == NULL)
00305 goto oom;
00306
00307
00308
00309 if (!bus_match_rule_set_message_type (rule,
00310 DBUS_MESSAGE_TYPE_SIGNAL))
00311 goto oom;
00312
00313 return rule;
00314
00315 oom:
00316 if (rule)
00317 bus_match_rule_unref (rule);
00318 BUS_SET_OOM (error);
00319 return NULL;
00320 }
00321
00322 struct BusMatchmaker
00323 {
00324 int refcount;
00325
00326 DBusList *all_rules;
00327 };
00328
00329 BusMatchmaker*
00330 bus_matchmaker_new (void)
00331 {
00332 BusMatchmaker *matchmaker;
00333
00334 matchmaker = dbus_new0 (BusMatchmaker, 1);
00335 if (matchmaker == NULL)
00336 return NULL;
00337
00338 matchmaker->refcount = 1;
00339
00340 return matchmaker;
00341 }
00342
00343 void
00344 bus_matchmaker_ref (BusMatchmaker *matchmaker)
00345 {
00346 _dbus_assert (matchmaker->refcount > 0);
00347
00348 matchmaker->refcount += 1;
00349 }
00350
00351 void
00352 bus_matchmaker_unref (BusMatchmaker *matchmaker)
00353 {
00354 _dbus_assert (matchmaker->refcount > 0);
00355
00356 matchmaker->refcount -= 1;
00357 if (matchmaker->refcount == 0)
00358 {
00359 while (matchmaker->all_rules != NULL)
00360 {
00361 BusMatchRule *rule;
00362
00363 rule = matchmaker->all_rules->data;
00364 bus_match_rule_unref (rule);
00365 _dbus_list_remove_link (&matchmaker->all_rules,
00366 matchmaker->all_rules);
00367 }
00368
00369 dbus_free (matchmaker);
00370 }
00371 }
00372
00373
00374 dbus_bool_t
00375 bus_matchmaker_add_rule (BusMatchmaker *matchmaker,
00376 BusMatchRule *rule)
00377 {
00378 _dbus_assert (bus_connection_is_active (rule->matches_go_to));
00379
00380 if (!_dbus_list_append (&matchmaker->all_rules, rule))
00381 return FALSE;
00382
00383 if (!bus_connection_add_match_rule (rule->matches_go_to, rule))
00384 {
00385 _dbus_list_remove_last (&matchmaker->all_rules, rule);
00386 return FALSE;
00387 }
00388
00389 bus_match_rule_ref (rule);
00390
00391 #ifdef DBUS_ENABLE_VERBOSE_MODE
00392 {
00393 char *s = match_rule_to_string (rule);
00394
00395 _dbus_verbose ("Added match rule %s to connection %p\n",
00396 s, rule->matches_go_to);
00397 dbus_free (s);
00398 }
00399 #endif
00400
00401 return TRUE;
00402 }
00403
00404 static dbus_bool_t
00405 match_rule_equal (BusMatchRule *a,
00406 BusMatchRule *b)
00407 {
00408 if (a->flags != b->flags)
00409 return FALSE;
00410
00411 if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
00412 a->message_type != b->message_type)
00413 return FALSE;
00414
00415 if ((a->flags & BUS_MATCH_MEMBER) &&
00416 strcmp (a->member, b->member) != 0)
00417 return FALSE;
00418
00419 if ((a->flags & BUS_MATCH_PATH) &&
00420 strcmp (a->path, b->path) != 0)
00421 return FALSE;
00422
00423 if ((a->flags & BUS_MATCH_INTERFACE) &&
00424 strcmp (a->interface, b->interface) != 0)
00425 return FALSE;
00426
00427 if ((a->flags & BUS_MATCH_SENDER) &&
00428 strcmp (a->sender, b->sender) != 0)
00429 return FALSE;
00430
00431 if ((a->flags & BUS_MATCH_DESTINATION) &&
00432 strcmp (a->destination, b->destination) != 0)
00433 return FALSE;
00434
00435 return TRUE;
00436 }
00437
00438 static void
00439 bus_matchmaker_remove_rule_link (BusMatchmaker *matchmaker,
00440 DBusList *link)
00441 {
00442 BusMatchRule *rule = link->data;
00443
00444 bus_connection_remove_match_rule (rule->matches_go_to, rule);
00445 _dbus_list_remove_link (&matchmaker->all_rules, link);
00446
00447 #ifdef DBUS_ENABLE_VERBOSE_MODE
00448 {
00449 char *s = match_rule_to_string (rule);
00450
00451 _dbus_verbose ("Removed match rule %s for connection %p\n",
00452 s, rule->matches_go_to);
00453 dbus_free (s);
00454 }
00455 #endif
00456
00457 bus_match_rule_unref (rule);
00458 }
00459
00460 void
00461 bus_matchmaker_remove_rule (BusMatchmaker *matchmaker,
00462 BusMatchRule *rule)
00463 {
00464 bus_connection_remove_match_rule (rule->matches_go_to, rule);
00465 _dbus_list_remove (&matchmaker->all_rules, rule);
00466
00467 #ifdef DBUS_ENABLE_VERBOSE_MODE
00468 {
00469 char *s = match_rule_to_string (rule);
00470
00471 _dbus_verbose ("Removed match rule %s for connection %p\n",
00472 s, rule->matches_go_to);
00473 dbus_free (s);
00474 }
00475 #endif
00476
00477 bus_match_rule_unref (rule);
00478 }
00479
00480
00481 dbus_bool_t
00482 bus_matchmaker_remove_rule_by_value (BusMatchmaker *matchmaker,
00483 BusMatchRule *value,
00484 DBusError *error)
00485 {
00486
00487
00488 DBusList *link;
00489
00490
00491
00492
00493 link = _dbus_list_get_last_link (&matchmaker->all_rules);
00494 while (link != NULL)
00495 {
00496 BusMatchRule *rule;
00497 DBusList *prev;
00498
00499 rule = link->data;
00500 prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link);
00501
00502 if (match_rule_equal (rule, value))
00503 {
00504 bus_matchmaker_remove_rule_link (matchmaker, link);
00505 break;
00506 }
00507
00508 link = prev;
00509 }
00510
00511 if (link == NULL)
00512 {
00513 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
00514 "The given match rule wasn't found and can't be removed");
00515 return FALSE;
00516 }
00517
00518 return TRUE;
00519 }
00520
00521 void
00522 bus_matchmaker_disconnected (BusMatchmaker *matchmaker,
00523 DBusConnection *disconnected)
00524 {
00525 DBusList *link;
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536 _dbus_assert (bus_connection_is_active (disconnected));
00537
00538 link = _dbus_list_get_first_link (&matchmaker->all_rules);
00539 while (link != NULL)
00540 {
00541 BusMatchRule *rule;
00542 DBusList *next;
00543
00544 rule = link->data;
00545 next = _dbus_list_get_next_link (&matchmaker->all_rules, link);
00546
00547 if (rule->matches_go_to == disconnected)
00548 {
00549 bus_matchmaker_remove_rule_link (matchmaker, link);
00550 }
00551 else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') ||
00552 ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':'))
00553 {
00554
00555
00556
00557
00558 const char *name;
00559
00560 name = bus_connection_get_name (disconnected);
00561 _dbus_assert (name != NULL);
00562
00563 if (((rule->flags & BUS_MATCH_SENDER) &&
00564 strcmp (rule->sender, name) == 0) ||
00565 ((rule->flags & BUS_MATCH_DESTINATION) &&
00566 strcmp (rule->destination, name) == 0))
00567 {
00568 bus_matchmaker_remove_rule_link (matchmaker, link);
00569 }
00570 }
00571
00572 link = next;
00573 }
00574 }
00575
00576 static dbus_bool_t
00577 connection_is_primary_owner (DBusConnection *connection,
00578 const char *service_name)
00579 {
00580 BusService *service;
00581 DBusString str;
00582 BusRegistry *registry;
00583
00584 registry = bus_connection_get_registry (connection);
00585
00586 _dbus_string_init_const (&str, service_name);
00587 service = bus_registry_lookup (registry, &str);
00588
00589 if (service == NULL)
00590 return FALSE;
00591
00592 return bus_service_get_primary_owner (service) == connection;
00593 }
00594
00595 static dbus_bool_t
00596 match_rule_matches (BusMatchRule *rule,
00597 BusConnections *connections,
00598 DBusConnection *sender,
00599 DBusConnection *addressed_recipient,
00600 DBusMessage *message)
00601 {
00602
00603
00604
00605
00606 if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
00607 {
00608 _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
00609
00610 if (rule->message_type != dbus_message_get_type (message))
00611 return FALSE;
00612 }
00613
00614 if (rule->flags & BUS_MATCH_INTERFACE)
00615 {
00616 const char *iface;
00617
00618 _dbus_assert (rule->interface != NULL);
00619
00620 iface = dbus_message_get_interface (message);
00621 if (iface == NULL)
00622 return FALSE;
00623
00624 if (strcmp (iface, rule->interface) != 0)
00625 return FALSE;
00626 }
00627
00628 if (rule->flags & BUS_MATCH_MEMBER)
00629 {
00630 const char *member;
00631
00632 _dbus_assert (rule->member != NULL);
00633
00634 member = dbus_message_get_member (message);
00635 if (member == NULL)
00636 return FALSE;
00637
00638 if (strcmp (member, rule->member) != 0)
00639 return FALSE;
00640 }
00641
00642 if (rule->flags & BUS_MATCH_SENDER)
00643 {
00644 _dbus_assert (rule->sender != NULL);
00645
00646 if (!connection_is_primary_owner (sender, rule->sender))
00647 return FALSE;
00648 }
00649
00650 if (rule->flags & BUS_MATCH_DESTINATION)
00651 {
00652 const char *destination;
00653
00654 _dbus_assert (rule->destination != NULL);
00655
00656 if (addressed_recipient == NULL)
00657 return FALSE;
00658
00659 destination = dbus_message_get_destination (message);
00660 if (destination == NULL)
00661 return FALSE;
00662
00663 if (!connection_is_primary_owner (addressed_recipient, rule->destination))
00664 return FALSE;
00665 }
00666
00667 if (rule->flags & BUS_MATCH_PATH)
00668 {
00669 const char *path;
00670
00671 _dbus_assert (rule->path != NULL);
00672
00673 path = dbus_message_get_path (message);
00674 if (path == NULL)
00675 return FALSE;
00676
00677 if (strcmp (path, rule->path) != 0)
00678 return FALSE;
00679 }
00680
00681 return TRUE;
00682 }
00683
00684 dbus_bool_t
00685 bus_matchmaker_get_recipients (BusMatchmaker *matchmaker,
00686 BusConnections *connections,
00687 DBusConnection *sender,
00688 DBusConnection *addressed_recipient,
00689 DBusMessage *message,
00690 DBusList **recipients_p)
00691 {
00692
00693
00694
00695
00696
00697
00698 DBusList *link;
00699
00700 _dbus_assert (*recipients_p == NULL);
00701
00702
00703
00704
00705
00706 bus_connections_increment_stamp (connections);
00707
00708
00709
00710
00711
00712 if (addressed_recipient != NULL)
00713 bus_connection_mark_stamp (addressed_recipient);
00714
00715 link = _dbus_list_get_first_link (&matchmaker->all_rules);
00716 while (link != NULL)
00717 {
00718 BusMatchRule *rule;
00719
00720 rule = link->data;
00721
00722 #ifdef DBUS_ENABLE_VERBOSE_MODE
00723 {
00724 char *s = match_rule_to_string (rule);
00725
00726 _dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
00727 s, rule->matches_go_to);
00728 dbus_free (s);
00729 }
00730 #endif
00731
00732 if (match_rule_matches (rule, connections,
00733 sender, addressed_recipient, message))
00734 {
00735 _dbus_verbose ("Rule matched\n");
00736
00737
00738 if (bus_connection_mark_stamp (rule->matches_go_to))
00739 {
00740 if (!_dbus_list_append (recipients_p, rule->matches_go_to))
00741 goto nomem;
00742 }
00743 #ifdef DBUS_ENABLE_VERBOSE_MODE
00744 else
00745 {
00746 _dbus_verbose ("Connection already receiving this message, so not adding again\n");
00747 }
00748 #endif
00749 }
00750
00751 link = _dbus_list_get_next_link (&matchmaker->all_rules, link);
00752 }
00753
00754 return TRUE;
00755
00756 nomem:
00757 _dbus_list_clear (recipients_p);
00758 return FALSE;
00759 }
00760
00761 #ifdef DBUS_BUILD_TESTS
00762 #include "test.h"
00763
00764 dbus_bool_t
00765 bus_signals_test (const DBusString *test_data_dir)
00766 {
00767 BusMatchmaker *matchmaker;
00768
00769 matchmaker = bus_matchmaker_new ();
00770 bus_matchmaker_ref (matchmaker);
00771 bus_matchmaker_unref (matchmaker);
00772 bus_matchmaker_unref (matchmaker);
00773
00774 return TRUE;
00775 }
00776
00777 #endif
00778