summaryrefslogtreecommitdiffstats
path: root/python/matchrules.py
blob: 9a7edb3e00439cbed7fd4778473589f294a54380 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from exceptions import *

class SignalMatchNode:
    def __init__(self):
        self.wildcard = None
        self.finite = {}
        self.rules = []
        
    def add(self, key, leaf=None):
        node = None
        
        if key:
            if self.finite.has_key(key):
                node = self.finite[key]
            else:
                node = SignalMatchNode()
                self.finite[key]  = node
        else:
            if self.wildcard:
                node = self.wildcard
            else:
                node = SignalMatchNode()
                self.wildcard = node
         
        node.rules.append(leaf)
        return node
    
    def get_matches(self, key):
        result = []
        if self.wildcard:
            result.append(self.wildcard)
        
        if self.finite.has_key(key):
            result.append(self.finite[key])
            
        return result
        
    def get_match(self, key):
        if key:
           if self.finite.has_key(key):
               return self.finite[key]
           else:
               return None
        
        return self.wildcard
        
    def has_children(self):
        if self.wildcard or len(self.finite.iterkeys()) > 0:
            return True
        return False

    def remove_child(self, child, key=None):
        if self.wildcard == child:
            self.wildcard = None
        elif self.finite.has_key(key):
            del self.finite[key]

class SignalMatchTree:
    """This class creates an ordered tree of SignalMatchRules
        to speed searchs.  Left branches are wildcard elements
        and all other branches are concreet elements.
    """
    def __init__(self):
        self._tree = SignalMatchNode()
    
    def add(self, rule):
        interface = self._tree.add(rule.sender)
        signal = interface.add(rule.dbus_interface)
        path = signal.add(rule.signal_name)
        path.add(rule.path, leaf=rule)
       
    def exec_matches(self, match_rule, message):
        sender_matches = self._tree.get_matches(match_rule.sender)
        for sender_node in sender_matches:
            interface_matches = sender_node.get_matches(match_rule.dbus_interface)
            for interface_node in interface_matches:
                signal_matches = interface_node.get_matches(match_rule.signal_name)
                for signal_node in signal_matches:
                    path_matches = signal_node.get_matches(match_rule.path)
                    for path_node in path_matches:
                        if(path_node.rules):
                            for rule in path_node.rules:
                                rule.execute(message)
            
    def remove(self, rule):
        try:
            sender = self._tree.get_match(rule.sender)
            interface = sender.get_match(rule.dbus_interface)
            signal = interface.get_match(rule.signal_name)
            path = signal.get_match(rule.path)
            
            rule_matches = []
            for _rule in path.rules:
                if _rule.is_match(rule):
                    rule_matches.append(_rule)
                    
            for _rule in rule_matches:
                path.rules.remove(_rule)
                
            #clean up tree
            if len(path.rules) == 0:
                signal.remove_child(path, key = rule.path)
                if not signal.has_children():
                    interface.remove_child(signal, key = rule.signal_name)
                    if not interface.has_children():
                        sender.remove_child(interface, key = rule.dbus_interface)
                        if not sender.has_children():
                            self._tree.remove_child(sender, key = rule.sender)
            
        except:
            raise DBusException ("Trying to remove unkown rule: %s"%str(rule))

class SignalMatchRule:
    """This class represents a dbus rule used to filter signals.
        When a rule matches a filter, the signal is propagated to the handler_funtions
    """
    def __init__(self, signal_name, dbus_interface, sender, path):
        self.handler_functions = []

        self.signal_name = signal_name
        self.dbus_interface = dbus_interface
        self.sender = sender
        self.path = path

    def execute(self, message):
        args = message.get_args_list()
        for handler in self.handler_functions:
            if getattr(handler, "_dbus_pass_message", False):
                keywords = {"dbus_message": message}
                handler(*args, **keywords)
            else:
                handler(*args)

    def add_handler(self, handler):
        self.handler_functions.append(handler)
        
    def is_match(self, rule):
        if (self.signal_name == rule.signal_name and
            self.dbus_interface == rule.dbus_interface and
            self.sender == rule.sender and
            self.path == rule.path):
                if rule.handler_functions == []:
                    return True
            
                _funcs_copy_a = self.handler_functions[0:]
                _funcs_copy_b = rule.handler_functions[0:]
                _funcs_copy_a.sort()
                _funcs_copy_b.sort()

                return _funcs_copy_a == _funcs_copy_b
 
        return False

    def __repr__(self):
        """Returns a custom representation of this DBusMatchRule that can
            be used with dbus_bindings
        """
        repr = "type='signal'"
        if (self.dbus_interface):
            repr = repr + ",interface='%s'" % (self.dbus_interface)

        if (self.sender):     
            repr = repr + ",sender='%s'" % (self.sender)
    
        if (self.path):
            repr = repr + ",path='%s'" % (self.path)
            
        if (self.signal_name):
            repr = repr + ",member='%s'" % (self.signal_name)
    
        return repr