sdague-ruby-dbus 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,391 @@
1
+ # dbus.rb - Module containing the low-level D-Bus implementation
2
+ #
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+
11
+ require 'socket'
12
+
13
+ # = D-Bus main module
14
+ #
15
+ # Module containing all the D-Bus modules and classes.
16
+ module DBus
17
+ # Exception raised when an invalid packet is encountered.
18
+ class InvalidPacketException < Exception
19
+ end
20
+
21
+ # = D-Bus packet unmarshaller class
22
+ #
23
+ # Class that handles the conversion (unmarshalling) of payload data
24
+ # to Array.
25
+ class PacketUnmarshaller
26
+ # Index pointer that points to the byte in the data that is
27
+ # currently being processed.
28
+ #
29
+ # Used to kown what part of the buffer has been consumed by unmarshalling.
30
+ # FIXME: Maybe should be accessed with a "consumed_size" method.
31
+ attr_reader :idx
32
+
33
+ # Create a new unmarshaller for the given data _buffer_ and _endianness_.
34
+ def initialize(buffer, endianness)
35
+ @buffy, @endianness = buffer.dup, endianness
36
+ if @endianness == BIG_END
37
+ @uint32 = "N"
38
+ @uint16 = "n"
39
+ @double = "G"
40
+ elsif @endianness == LIL_END
41
+ @uint32 = "V"
42
+ @uint16 = "v"
43
+ @double = "E"
44
+ else
45
+ # FIXME: shouldn't a more special exception be raised here?
46
+ # yes, idea for a good name ? :)
47
+ raise Exception, "Incorrect endianness"
48
+ end
49
+ @idx = 0
50
+ end
51
+
52
+ # Unmarshall the buffer for a given _signature_ and length _len_.
53
+ # Return an array of unmarshalled objects
54
+ def unmarshall(signature, len = nil)
55
+ if len != nil
56
+ if @buffy.size < @idx + len
57
+ raise IncompleteBufferException
58
+ end
59
+ end
60
+ sigtree = Type::Parser.new(signature).parse
61
+ ret = Array.new
62
+ sigtree.each do |elem|
63
+ ret << do_parse(elem)
64
+ end
65
+ ret
66
+ end
67
+
68
+ # Align the pointer index on a byte index of _a_, where a
69
+ # must be 1, 2, 4 or 8.
70
+ def align(a)
71
+ case a
72
+ when 1
73
+ when 2, 4, 8
74
+ bits = a - 1
75
+ @idx = @idx + bits & ~bits
76
+ raise IncompleteBufferException if @idx > @buffy.size
77
+ else
78
+ raise "Unsupported alignment #{a}"
79
+ end
80
+ end
81
+
82
+ ###############################################################
83
+ # FIXME: does anyone except the object itself call the above methods?
84
+ # Yes : Message marshalling code needs to align "body" to 8 byte boundary
85
+ private
86
+
87
+ # Retrieve the next _nbytes_ number of bytes from the buffer.
88
+ def get(nbytes)
89
+ raise IncompleteBufferException if @idx + nbytes > @buffy.size
90
+ ret = @buffy.slice(@idx, nbytes)
91
+ @idx += nbytes
92
+ ret
93
+ end
94
+
95
+ # Retrieve the series of bytes until the next NULL (\0) byte.
96
+ def get_nul_terminated
97
+ raise IncompleteBufferException if not @buffy[@idx..-1] =~ /^([^\0]*)\0/
98
+ str = $1
99
+ raise IncompleteBufferException if @idx + str.size + 1 > @buffy.size
100
+ @idx += str.size + 1
101
+ str
102
+ end
103
+
104
+ # Get the string length and string itself from the buffer.
105
+ # Return the string.
106
+ def get_string
107
+ align(4)
108
+ str_sz = get(4).unpack(@uint32)[0]
109
+ ret = @buffy.slice(@idx, str_sz)
110
+ raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.size
111
+ @idx += str_sz
112
+ if @buffy[@idx] != 0
113
+ raise InvalidPacketException, "String is not nul-terminated"
114
+ end
115
+ @idx += 1
116
+ # no exception, see check above
117
+ ret
118
+ end
119
+
120
+ # Get the signature length and signature itself from the buffer.
121
+ # Return the signature.
122
+ def get_signature
123
+ str_sz = get(1).unpack('C')[0]
124
+ ret = @buffy.slice(@idx, str_sz)
125
+ raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.size
126
+ @idx += str_sz
127
+ if @buffy[@idx] != 0
128
+ raise InvalidPacketException, "Type is not nul-terminated"
129
+ end
130
+ @idx += 1
131
+ # no exception, see check above
132
+ ret
133
+ end
134
+
135
+ # Based on the _signature_ type, retrieve a packet from the buffer
136
+ # and return it.
137
+ def do_parse(signature)
138
+ packet = nil
139
+ case signature.sigtype
140
+ when Type::BYTE
141
+ packet = get(1).unpack("C")[0]
142
+ when Type::UINT16
143
+ align(2)
144
+ packet = get(2).unpack(@uint16)[0]
145
+ when Type::INT16
146
+ align(4)
147
+ packet = get(4).unpack(@uint16)[0]
148
+ if (packet & 0x8000) != 0
149
+ packet -= 0x10000
150
+ end
151
+ when Type::UINT32
152
+ align(4)
153
+ packet = get(4).unpack(@uint32)[0]
154
+ when Type::INT32
155
+ align(4)
156
+ packet = get(4).unpack(@uint32)[0]
157
+ if (packet & 0x80000000) != 0
158
+ packet -= 0x100000000
159
+ end
160
+ when Type::UINT64
161
+ align(8)
162
+ packet_l = get(4).unpack(@uint32)[0]
163
+ packet_h = get(4).unpack(@uint32)[0]
164
+ if @endianness == LIL_END
165
+ packet = packet_l + packet_h * 2**32
166
+ else
167
+ packet = packet_l * 2**32 + packet_h
168
+ end
169
+ when Type::INT64
170
+ align(8)
171
+ packet_l = get(4).unpack(@uint32)[0]
172
+ packet_h = get(4).unpack(@uint32)[0]
173
+ if @endianness == LIL_END
174
+ packet = packet_l + packet_h * 2**32
175
+ else
176
+ packet = packet_l * 2**32 + packet_h
177
+ end
178
+ if (packet & 0x8000000000000000) != 0
179
+ packet -= 0x10000000000000000
180
+ end
181
+ when Type::DOUBLE
182
+ align(8)
183
+ packet = get(8).unpack(@double)[0]
184
+ when Type::BOOLEAN
185
+ align(4)
186
+ v = get(4).unpack(@uint32)[0]
187
+ raise InvalidPacketException if not [0, 1].member?(v)
188
+ packet = (v == 1)
189
+ when Type::ARRAY
190
+ align(4)
191
+ # checks please
192
+ array_sz = get(4).unpack(@uint32)[0]
193
+ raise InvalidPacketException if array_sz > 67108864
194
+
195
+ align(signature.child.alignment)
196
+ raise IncompleteBufferException if @idx + array_sz > @buffy.size
197
+
198
+ packet = Array.new
199
+ start_idx = @idx
200
+ while @idx - start_idx < array_sz
201
+ packet << do_parse(signature.child)
202
+ end
203
+
204
+ if signature.child.sigtype == Type::DICT_ENTRY then
205
+ packet = packet.inject(Hash.new) do |hash, pair|
206
+ hash[pair[0]] = pair[1]
207
+ hash
208
+ end
209
+ end
210
+ when Type::STRUCT
211
+ align(8)
212
+ packet = Array.new
213
+ signature.members.each do |elem|
214
+ packet << do_parse(elem)
215
+ end
216
+ when Type::VARIANT
217
+ string = get_signature
218
+ # error checking please
219
+ sig = Type::Parser.new(string).parse[0]
220
+ align(sig.alignment)
221
+ packet = do_parse(sig)
222
+ when Type::OBJECT_PATH
223
+ packet = get_string
224
+ when Type::STRING
225
+ packet = get_string
226
+ when Type::SIGNATURE
227
+ packet = get_signature
228
+ when Type::DICT_ENTRY
229
+ align(8)
230
+ key = do_parse(signature.members[0])
231
+ value = do_parse(signature.members[1])
232
+ packet = [key, value]
233
+ else
234
+ raise NotImplementedError,
235
+ "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
236
+ end
237
+ packet
238
+ end # def do_parse
239
+ end # class PacketUnmarshaller
240
+
241
+ # D-Bus packet marshaller class
242
+ #
243
+ # Class that handles the conversion (unmarshalling) of Ruby objects to
244
+ # (binary) payload data.
245
+ class PacketMarshaller
246
+ # The current or result packet.
247
+ # FIXME: allow access only when marshalling is finished
248
+ attr_reader :packet
249
+
250
+ # Create a new marshaller, setting the current packet to the
251
+ # empty packet.
252
+ def initialize
253
+ @packet = ""
254
+ end
255
+
256
+ # Align the buffer with NULL (\0) bytes on a byte length of _a_.
257
+ def align(a)
258
+ case a
259
+ when 1
260
+ when 2, 4, 8
261
+ bits = a - 1
262
+ @packet = @packet.ljust(@packet.length + bits & ~bits, 0.chr)
263
+ else
264
+ raise "Unsupported alignment"
265
+ end
266
+ end
267
+
268
+ # Append the the string _str_ itself to the packet.
269
+ def append_string(str)
270
+ align(4)
271
+ @packet += [str.length].pack("L") + str + "\0"
272
+ end
273
+
274
+ # Append the the signature _signature_ itself to the packet.
275
+ def append_signature(str)
276
+ @packet += str.length.chr + str + "\0"
277
+ end
278
+
279
+ # Append the array type _type_ to the packet and allow for appending
280
+ # the child elements.
281
+ def array(type)
282
+ # Thanks to Peter Rullmann for this line
283
+ align(4)
284
+ sizeidx = @packet.size
285
+ @packet += "ABCD"
286
+ align(type.alignment)
287
+ contentidx = @packet.size
288
+ yield
289
+ sz = @packet.size - contentidx
290
+ raise InvalidPacketException if sz > 67108864
291
+ @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
292
+ end
293
+
294
+ # Align and allow for appending struct fields.
295
+ def struct
296
+ align(8)
297
+ yield
298
+ end
299
+
300
+ # Append a string of bytes without type.
301
+ def append_simple_string(s)
302
+ @packet += s + "\0"
303
+ end
304
+
305
+ # Append a value _val_ to the packet based on its _type_.
306
+ def append(type, val)
307
+ type = type.chr if type.kind_of?(Fixnum)
308
+ type = Type::Parser.new(type).parse[0] if type.kind_of?(String)
309
+ case type.sigtype
310
+ when Type::BYTE
311
+ @packet += val.chr
312
+ when Type::UINT32
313
+ align(4)
314
+ @packet += [val].pack("L")
315
+ when Type::UINT64
316
+ align(8)
317
+ @packet += [val].pack("Q")
318
+ when Type::INT64
319
+ align(8)
320
+ @packet += [val].pack("q")
321
+ when Type::INT32
322
+ align(4)
323
+ @packet += [val].pack("l")
324
+ when Type::UINT16
325
+ align(2)
326
+ @packet += [val].pack("S")
327
+ when Type::INT16
328
+ align(2)
329
+ @packet += [val].pack("s")
330
+ when Type::DOUBLE
331
+ align(8)
332
+ @packet += [val].pack("d")
333
+ when Type::BOOLEAN
334
+ align(4)
335
+ if val
336
+ @packet += [1].pack("L")
337
+ else
338
+ @packet += [0].pack("L")
339
+ end
340
+ when Type::OBJECT_PATH
341
+ append_string(val)
342
+ when Type::STRING
343
+ append_string(val)
344
+ when Type::SIGNATURE
345
+ append_signature(val)
346
+ when Type::VARIANT
347
+ if not val.kind_of?(Array)
348
+ raise TypeException
349
+ end
350
+ vartype, vardata = val
351
+ vartype = Type::Parser.new(vartype).parse[0] if vartype.kind_of?(String)
352
+ append_signature(vartype.to_s)
353
+ align(vartype.alignment)
354
+ sub = PacketMarshaller.new
355
+ sub.append(vartype, vardata)
356
+ @packet += sub.packet
357
+ when Type::ARRAY
358
+ if val.kind_of?(Hash)
359
+ raise TypeException if type.child.sigtype != Type::DICT_ENTRY
360
+ # Damn ruby rocks here
361
+ val = val.to_a
362
+ end
363
+ if not val.kind_of?(Array)
364
+ raise TypeException
365
+ end
366
+ array(type.child) do
367
+ val.each do |elem|
368
+ append(type.child, elem)
369
+ end
370
+ end
371
+ when Type::STRUCT, Type::DICT_ENTRY
372
+ raise TypeException if not val.kind_of?(Array)
373
+ if type.sigtype == Type::DICT_ENTRY and val.size != 2
374
+ raise TypeException
375
+ end
376
+ struct do
377
+ idx = 0
378
+ while val[idx] != nil
379
+ type.members.each do |subtype|
380
+ raise TypeException if val[idx] == nil
381
+ append(subtype, val[idx])
382
+ idx += 1
383
+ end
384
+ end
385
+ end
386
+ else
387
+ raise NotImplementedError
388
+ end
389
+ end # def append
390
+ end # class PacketMarshaller
391
+ end # module DBus
@@ -0,0 +1,98 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License, version 2.1 as published by the Free Software Foundation.
7
+ # See the file "COPYING" for the exact licensing terms.
8
+
9
+ module DBus
10
+ # Exception raised when an erroneous match rule type is encountered.
11
+ class MatchRuleException < Exception
12
+ end
13
+
14
+ # = D-Bus match rule class
15
+ #
16
+ # FIXME
17
+ class MatchRule
18
+ # The list of possible match filters.
19
+ FILTERS = [:sender, :interface, :member, :path, :destination, :type]
20
+ # The sender filter.
21
+ attr_accessor :sender
22
+ # The interface filter.
23
+ attr_accessor :interface
24
+ # The member filter.
25
+ attr_accessor :member
26
+ # The path filter.
27
+ attr_accessor :path
28
+ # The destination filter.
29
+ attr_accessor :destination
30
+ # The type type that is matched.
31
+ attr_reader :type
32
+
33
+ # Create a new match rule
34
+ def initialize
35
+ @sender = @interface = @member = @path = @destination = @type = nil
36
+ end
37
+
38
+ # Set the message types to filter to type _t_.
39
+ # Possible message types are: signal, method_call, method_return, and error.
40
+ def type=(t)
41
+ if not ['signal', 'method_call', 'method_return', 'error'].member?(t)
42
+ raise MatchRuleException
43
+ end
44
+ @type = t
45
+ end
46
+
47
+ # Returns a match rule string version of the object.
48
+ # E.g.: "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',path='/bar/foo',destination=':452345.34',arg2='bar'"
49
+ def to_s
50
+ FILTERS.select do |sym|
51
+ not method(sym).call.nil?
52
+ end.collect do |sym|
53
+ "#{sym.to_s}='#{method(sym).call}'"
54
+ end.join(",")
55
+ end
56
+
57
+ # Parses a match rule string _s_ and sets the filters on the object.
58
+ def from_s(str)
59
+ s.split(",").each do |eq|
60
+ if eq =~ /^(.*)='([^']*)'$/
61
+ name = $1
62
+ val = $1
63
+ if FILTERS.member?(name.to_sym)
64
+ method(name + "=").call(val)
65
+ else
66
+ raise MatchRuleException
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # Sets the match rule to filter for the given _signal_ and the
73
+ # given interface _intf_.
74
+ def from_signal(intf, signal)
75
+ signal = signal.name unless signal.is_a?(String)
76
+ self.type = "signal"
77
+ self.interface = intf.name
78
+ self.member = signal
79
+ self.path = intf.object.path
80
+ self
81
+ end
82
+
83
+ # Determines whether a message _msg_ matches the match rule.
84
+ def match(msg)
85
+ if @type
86
+ if {Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
87
+ Message::METHOD_RETURN => "method_return",
88
+ Message::ERROR => "error"}[msg.message_type] != @type
89
+ return false
90
+ end
91
+ end
92
+ return false if @interface and @interface != msg.interface
93
+ return false if @member and @member != msg.member
94
+ return false if @path and @path != msg.path
95
+ true
96
+ end
97
+ end # class MatchRule
98
+ end # module D-Bus