demirten-ruby-dbus 0.2.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,396 @@
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(offset = 0)
253
+ @packet = ""
254
+ @offset = offset # for correct alignment of nested marshallers
255
+ end
256
+
257
+ # Round _n_ up to the specified power of two, _a_
258
+ def num_align(n, a)
259
+ case a
260
+ when 1, 2, 4, 8
261
+ bits = a - 1
262
+ n + bits & ~bits
263
+ else
264
+ raise "Unsupported alignment"
265
+ end
266
+ end
267
+
268
+ # Align the buffer with NULL (\0) bytes on a byte length of _a_.
269
+ def align(a)
270
+ @packet = @packet.ljust(num_align(@offset + @packet.length, a) - @offset, 0.chr)
271
+ end
272
+
273
+ # Append the the string _str_ itself to the packet.
274
+ def append_string(str)
275
+ align(4)
276
+ @packet += [str.length].pack("L") + str + "\0"
277
+ end
278
+
279
+ # Append the the signature _signature_ itself to the packet.
280
+ def append_signature(str)
281
+ @packet += str.length.chr + str + "\0"
282
+ end
283
+
284
+ # Append the array type _type_ to the packet and allow for appending
285
+ # the child elements.
286
+ def array(type)
287
+ # Thanks to Peter Rullmann for this line
288
+ align(4)
289
+ sizeidx = @packet.size
290
+ @packet += "ABCD"
291
+ align(type.alignment)
292
+ contentidx = @packet.size
293
+ yield
294
+ sz = @packet.size - contentidx
295
+ raise InvalidPacketException if sz > 67108864
296
+ @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
297
+ end
298
+
299
+ # Align and allow for appending struct fields.
300
+ def struct
301
+ align(8)
302
+ yield
303
+ end
304
+
305
+ # Append a string of bytes without type.
306
+ def append_simple_string(s)
307
+ @packet += s + "\0"
308
+ end
309
+
310
+ # Append a value _val_ to the packet based on its _type_.
311
+ def append(type, val)
312
+ type = type.chr if type.kind_of?(Fixnum)
313
+ type = Type::Parser.new(type).parse[0] if type.kind_of?(String)
314
+ case type.sigtype
315
+ when Type::BYTE
316
+ @packet += val.chr
317
+ when Type::UINT32
318
+ align(4)
319
+ @packet += [val].pack("L")
320
+ when Type::UINT64
321
+ align(8)
322
+ @packet += [val].pack("Q")
323
+ when Type::INT64
324
+ align(8)
325
+ @packet += [val].pack("q")
326
+ when Type::INT32
327
+ align(4)
328
+ @packet += [val].pack("l")
329
+ when Type::UINT16
330
+ align(2)
331
+ @packet += [val].pack("S")
332
+ when Type::INT16
333
+ align(2)
334
+ @packet += [val].pack("s")
335
+ when Type::DOUBLE
336
+ align(8)
337
+ @packet += [val].pack("d")
338
+ when Type::BOOLEAN
339
+ align(4)
340
+ if val
341
+ @packet += [1].pack("L")
342
+ else
343
+ @packet += [0].pack("L")
344
+ end
345
+ when Type::OBJECT_PATH
346
+ append_string(val)
347
+ when Type::STRING
348
+ append_string(val)
349
+ when Type::SIGNATURE
350
+ append_signature(val)
351
+ when Type::VARIANT
352
+ if not val.kind_of?(Array)
353
+ raise TypeException
354
+ end
355
+ vartype, vardata = val
356
+ vartype = Type::Parser.new(vartype).parse[0] if vartype.kind_of?(String)
357
+ append_signature(vartype.to_s)
358
+ align(vartype.alignment)
359
+ sub = PacketMarshaller.new(@offset + @packet.length)
360
+ sub.append(vartype, vardata)
361
+ @packet += sub.packet
362
+ when Type::ARRAY
363
+ if val.kind_of?(Hash)
364
+ raise TypeException if type.child.sigtype != Type::DICT_ENTRY
365
+ # Damn ruby rocks here
366
+ val = val.to_a
367
+ end
368
+ if not val.kind_of?(Array)
369
+ raise TypeException
370
+ end
371
+ array(type.child) do
372
+ val.each do |elem|
373
+ append(type.child, elem)
374
+ end
375
+ end
376
+ when Type::STRUCT, Type::DICT_ENTRY
377
+ raise TypeException if not val.kind_of?(Array)
378
+ if type.sigtype == Type::DICT_ENTRY and val.size != 2
379
+ raise TypeException
380
+ end
381
+ struct do
382
+ idx = 0
383
+ while val[idx] != nil
384
+ type.members.each do |subtype|
385
+ raise TypeException if val[idx] == nil
386
+ append(subtype, val[idx])
387
+ idx += 1
388
+ end
389
+ end
390
+ end
391
+ else
392
+ raise NotImplementedError
393
+ end
394
+ end # def append
395
+ end # class PacketMarshaller
396
+ 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