ruby-dbus 0.17.0 → 0.18.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +38 -0
- data/README.md +1 -1
- data/Rakefile +3 -11
- data/VERSION +1 -1
- data/doc/Reference.md +10 -3
- data/examples/doc/_extract_examples +2 -0
- data/examples/gdbus/gdbus +11 -5
- data/examples/no-introspect/nm-test.rb +2 -0
- data/examples/no-introspect/tracker-test.rb +3 -1
- data/examples/rhythmbox/playpause.rb +2 -1
- data/examples/service/call_service.rb +1 -0
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +1 -1
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +2 -1
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +1 -0
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +12 -7
- data/lib/dbus/bus.rb +114 -70
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/data.rb +744 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +30 -18
- data/lib/dbus/logger.rb +3 -1
- data/lib/dbus/marshall.rb +229 -293
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +44 -37
- data/lib/dbus/message_queue.rb +10 -5
- data/lib/dbus/object.rb +36 -15
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/proxy_object.rb +18 -4
- data/lib/dbus/proxy_object_factory.rb +11 -7
- data/lib/dbus/proxy_object_interface.rb +26 -22
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +164 -80
- data/lib/dbus/xml.rb +28 -17
- data/lib/dbus.rb +13 -7
- data/ruby-dbus.gemspec +4 -2
- data/spec/async_spec.rb +2 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +2 -0
- data/spec/bus_driver_spec.rb +2 -0
- data/spec/bus_name_spec.rb +3 -1
- data/spec/bus_spec.rb +2 -0
- data/spec/byte_array_spec.rb +2 -0
- data/spec/client_robustness_spec.rb +4 -2
- data/spec/data/marshall.yaml +1639 -0
- data/spec/data_spec.rb +353 -0
- data/spec/err_msg_spec.rb +2 -0
- data/spec/introspect_xml_parser_spec.rb +2 -0
- data/spec/introspection_spec.rb +2 -0
- data/spec/main_loop_spec.rb +2 -0
- data/spec/node_spec.rb +23 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/packet_marshaller_spec.rb +34 -0
- data/spec/packet_unmarshaller_spec.rb +262 -0
- data/spec/property_spec.rb +60 -2
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/server_robustness_spec.rb +2 -0
- data/spec/server_spec.rb +2 -0
- data/spec/service_newapi.rb +37 -4
- data/spec/session_bus_spec.rb +3 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +2 -0
- data/spec/spec_helper.rb +19 -3
- data/spec/thread_safety_spec.rb +2 -0
- data/spec/type_spec.rb +69 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +16 -7
data/lib/dbus/marshall.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# dbus.rb - Module containing the low-level D-Bus implementation
|
2
4
|
#
|
3
5
|
# This file is part of the ruby-dbus project
|
@@ -10,6 +12,8 @@
|
|
10
12
|
|
11
13
|
require "socket"
|
12
14
|
|
15
|
+
require_relative "../dbus/type"
|
16
|
+
|
13
17
|
# = D-Bus main module
|
14
18
|
#
|
15
19
|
# Module containing all the D-Bus modules and classes.
|
@@ -21,211 +25,134 @@ module DBus
|
|
21
25
|
# = D-Bus packet unmarshaller class
|
22
26
|
#
|
23
27
|
# Class that handles the conversion (unmarshalling) of payload data
|
24
|
-
# to
|
28
|
+
# to #{::Object}s (in **plain** mode) or to {Data::Base} (in **exact** mode)
|
29
|
+
#
|
30
|
+
# Spelling note: this codebase always uses a double L
|
31
|
+
# in the "marshall" word and its inflections.
|
25
32
|
class PacketUnmarshaller
|
26
|
-
#
|
27
|
-
#
|
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_.
|
33
|
+
# Create a new unmarshaller for the given data *buffer*.
|
34
|
+
# @param buffer [String]
|
35
|
+
# @param endianness [:little,:big]
|
34
36
|
def initialize(buffer, endianness)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@uint16 = "n"
|
40
|
-
@double = "G"
|
41
|
-
elsif @endianness == LIL_END
|
42
|
-
@uint32 = "V"
|
43
|
-
@uint16 = "v"
|
44
|
-
@double = "E"
|
45
|
-
else
|
46
|
-
raise InvalidPacketException, "Incorrect endianness #{@endianness}"
|
47
|
-
end
|
48
|
-
@idx = 0
|
37
|
+
# TODO: this dup can be avoided if we can prove
|
38
|
+
# that an IncompleteBufferException leaves the original *buffer* intact
|
39
|
+
buffer = buffer.dup
|
40
|
+
@raw_msg = RawMessage.new(buffer, endianness)
|
49
41
|
end
|
50
42
|
|
51
43
|
# Unmarshall the buffer for a given _signature_ and length _len_.
|
52
|
-
# Return an array of unmarshalled objects
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
44
|
+
# Return an array of unmarshalled objects.
|
45
|
+
# @param signature [Signature]
|
46
|
+
# @param len [Integer,nil] if given, and there is not enough data
|
47
|
+
# in the buffer, raise {IncompleteBufferException}
|
48
|
+
# @param mode [:plain,:exact]
|
49
|
+
# @return [Array<::Object,DBus::Data::Base>]
|
50
|
+
# Objects in `:plain` mode, {DBus::Data::Base} in `:exact` mode
|
51
|
+
# The array size corresponds to the number of types in *signature*.
|
52
|
+
# @raise IncompleteBufferException
|
53
|
+
# @raise InvalidPacketException
|
54
|
+
def unmarshall(signature, len = nil, mode: :plain)
|
55
|
+
@raw_msg.want!(len) if len
|
56
|
+
|
59
57
|
sigtree = Type::Parser.new(signature).parse
|
60
58
|
ret = []
|
61
59
|
sigtree.each do |elem|
|
62
|
-
ret << do_parse(elem)
|
60
|
+
ret << do_parse(elem, mode: mode)
|
63
61
|
end
|
64
62
|
ret
|
65
63
|
end
|
66
64
|
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
case a
|
71
|
-
when 1
|
72
|
-
nil
|
73
|
-
when 2, 4, 8
|
74
|
-
bits = a - 1
|
75
|
-
@idx = @idx + bits & ~bits
|
76
|
-
raise IncompleteBufferException if @idx > @buffy.bytesize
|
77
|
-
else
|
78
|
-
raise "Unsupported alignment #{a}"
|
79
|
-
end
|
65
|
+
# after the headers, the body starts 8-aligned
|
66
|
+
def align_body
|
67
|
+
@raw_msg.align(8)
|
80
68
|
end
|
81
69
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
# Retrieve the next _nbytes_ number of bytes from the buffer.
|
88
|
-
def read(nbytes)
|
89
|
-
raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
|
90
|
-
ret = @buffy.slice(@idx, nbytes)
|
91
|
-
@idx += nbytes
|
92
|
-
ret
|
70
|
+
# @return [Integer]
|
71
|
+
def consumed_size
|
72
|
+
@raw_msg.pos
|
93
73
|
end
|
94
74
|
|
95
|
-
|
96
|
-
# Return the string.
|
97
|
-
def read_string
|
98
|
-
align(4)
|
99
|
-
str_sz = read(4).unpack(@uint32)[0]
|
100
|
-
ret = @buffy.slice(@idx, str_sz)
|
101
|
-
raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
|
102
|
-
@idx += str_sz
|
103
|
-
if @buffy[@idx].ord != 0
|
104
|
-
raise InvalidPacketException, "String is not nul-terminated"
|
105
|
-
end
|
106
|
-
@idx += 1
|
107
|
-
# no exception, see check above
|
108
|
-
ret
|
109
|
-
end
|
75
|
+
private
|
110
76
|
|
111
|
-
#
|
112
|
-
#
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
@idx += str_sz
|
118
|
-
if @buffy[@idx].ord != 0
|
119
|
-
raise InvalidPacketException, "Type is not nul-terminated"
|
120
|
-
end
|
121
|
-
@idx += 1
|
122
|
-
# no exception, see check above
|
123
|
-
ret
|
77
|
+
# @param data_class [Class] a subclass of Data::Base (specific?)
|
78
|
+
# @return [::Integer,::Float]
|
79
|
+
def aligned_read_value(data_class)
|
80
|
+
@raw_msg.align(data_class.alignment)
|
81
|
+
bytes = @raw_msg.read(data_class.alignment)
|
82
|
+
bytes.unpack1(data_class.format[@raw_msg.endianness])
|
124
83
|
end
|
125
84
|
|
126
85
|
# Based on the _signature_ type, retrieve a packet from the buffer
|
127
86
|
# and return it.
|
128
|
-
|
87
|
+
# @param signature [Type]
|
88
|
+
# @param mode [:plain,:exact]
|
89
|
+
# @return [Data::Base]
|
90
|
+
def do_parse(signature, mode: :plain)
|
91
|
+
# FIXME: better naming for packet vs value
|
129
92
|
packet = nil
|
130
|
-
|
131
|
-
when Type::BYTE
|
132
|
-
packet = read(1).unpack("C")[0]
|
133
|
-
when Type::UINT16
|
134
|
-
align(2)
|
135
|
-
packet = read(2).unpack(@uint16)[0]
|
136
|
-
when Type::INT16
|
137
|
-
align(2)
|
138
|
-
packet = read(2).unpack(@uint16)[0]
|
139
|
-
if (packet & 0x8000) != 0
|
140
|
-
packet -= 0x10000
|
141
|
-
end
|
142
|
-
when Type::UINT32, Type::UNIX_FD
|
143
|
-
align(4)
|
144
|
-
packet = read(4).unpack(@uint32)[0]
|
145
|
-
when Type::INT32
|
146
|
-
align(4)
|
147
|
-
packet = read(4).unpack(@uint32)[0]
|
148
|
-
if (packet & 0x80000000) != 0
|
149
|
-
packet -= 0x100000000
|
150
|
-
end
|
151
|
-
when Type::UINT64
|
152
|
-
align(8)
|
153
|
-
packet_l = read(4).unpack(@uint32)[0]
|
154
|
-
packet_h = read(4).unpack(@uint32)[0]
|
155
|
-
packet = if @endianness == LIL_END
|
156
|
-
packet_l + packet_h * 2**32
|
157
|
-
else
|
158
|
-
packet_l * 2**32 + packet_h
|
159
|
-
end
|
160
|
-
when Type::INT64
|
161
|
-
align(8)
|
162
|
-
packet_l = read(4).unpack(@uint32)[0]
|
163
|
-
packet_h = read(4).unpack(@uint32)[0]
|
164
|
-
packet = if @endianness == LIL_END
|
165
|
-
packet_l + packet_h * 2**32
|
166
|
-
else
|
167
|
-
packet_l * 2**32 + packet_h
|
168
|
-
end
|
169
|
-
if (packet & 0x8000000000000000) != 0
|
170
|
-
packet -= 0x10000000000000000
|
171
|
-
end
|
172
|
-
when Type::DOUBLE
|
173
|
-
align(8)
|
174
|
-
packet = read(8).unpack(@double)[0]
|
175
|
-
when Type::BOOLEAN
|
176
|
-
align(4)
|
177
|
-
v = read(4).unpack(@uint32)[0]
|
178
|
-
raise InvalidPacketException if ![0, 1].member?(v)
|
179
|
-
packet = (v == 1)
|
180
|
-
when Type::ARRAY
|
181
|
-
align(4)
|
182
|
-
# checks please
|
183
|
-
array_sz = read(4).unpack(@uint32)[0]
|
184
|
-
raise InvalidPacketException if array_sz > 67_108_864
|
185
|
-
|
186
|
-
align(signature.child.alignment)
|
187
|
-
raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize
|
188
|
-
|
189
|
-
packet = []
|
190
|
-
start_idx = @idx
|
191
|
-
while @idx - start_idx < array_sz
|
192
|
-
packet << do_parse(signature.child)
|
193
|
-
end
|
93
|
+
data_class = Data::BY_TYPE_CODE[signature.sigtype]
|
194
94
|
|
195
|
-
|
196
|
-
packet = Hash[packet]
|
197
|
-
end
|
198
|
-
when Type::STRUCT
|
199
|
-
align(8)
|
200
|
-
packet = []
|
201
|
-
signature.members.each do |elem|
|
202
|
-
packet << do_parse(elem)
|
203
|
-
end
|
204
|
-
when Type::VARIANT
|
205
|
-
string = read_signature
|
206
|
-
# error checking please
|
207
|
-
sig = Type::Parser.new(string).parse[0]
|
208
|
-
align(sig.alignment)
|
209
|
-
packet = do_parse(sig)
|
210
|
-
when Type::OBJECT_PATH
|
211
|
-
packet = read_string
|
212
|
-
when Type::STRING
|
213
|
-
packet = read_string
|
214
|
-
packet.force_encoding("UTF-8")
|
215
|
-
when Type::SIGNATURE
|
216
|
-
packet = read_signature
|
217
|
-
when Type::DICT_ENTRY
|
218
|
-
align(8)
|
219
|
-
key = do_parse(signature.members[0])
|
220
|
-
value = do_parse(signature.members[1])
|
221
|
-
packet = [key, value]
|
222
|
-
else
|
95
|
+
if data_class.nil?
|
223
96
|
raise NotImplementedError,
|
224
97
|
"sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
|
225
98
|
end
|
99
|
+
|
100
|
+
if data_class.fixed?
|
101
|
+
value = aligned_read_value(data_class)
|
102
|
+
packet = data_class.from_raw(value, mode: mode)
|
103
|
+
elsif data_class.basic?
|
104
|
+
size = aligned_read_value(data_class.size_class)
|
105
|
+
# @raw_msg.align(data_class.alignment)
|
106
|
+
# ^ is not necessary because we've just read a suitably-aligned *size*
|
107
|
+
value = @raw_msg.read(size)
|
108
|
+
nul = @raw_msg.read(1)
|
109
|
+
if nul != "\u0000"
|
110
|
+
raise InvalidPacketException, "#{data_class} is not NUL-terminated"
|
111
|
+
end
|
112
|
+
|
113
|
+
packet = data_class.from_raw(value, mode: mode)
|
114
|
+
else
|
115
|
+
@raw_msg.align(data_class.alignment)
|
116
|
+
case signature.sigtype
|
117
|
+
when Type::STRUCT, Type::DICT_ENTRY
|
118
|
+
values = signature.members.map do |child_sig|
|
119
|
+
do_parse(child_sig, mode: mode)
|
120
|
+
end
|
121
|
+
packet = data_class.from_items(values, mode: mode, member_types: signature.members)
|
122
|
+
|
123
|
+
when Type::VARIANT
|
124
|
+
data_sig = do_parse(Data::Signature.type, mode: :exact) # -> Data::Signature
|
125
|
+
types = Type::Parser.new(data_sig.value).parse # -> Array<Type>
|
126
|
+
unless types.size == 1
|
127
|
+
raise InvalidPacketException, "VARIANT must contain 1 value, #{types.size} found"
|
128
|
+
end
|
129
|
+
|
130
|
+
type = types.first
|
131
|
+
value = do_parse(type, mode: mode)
|
132
|
+
packet = data_class.from_items(value, mode: mode, member_type: type)
|
133
|
+
|
134
|
+
when Type::ARRAY
|
135
|
+
array_bytes = aligned_read_value(Data::UInt32)
|
136
|
+
if array_bytes > 67_108_864
|
137
|
+
raise InvalidPacketException, "ARRAY body longer than 64MiB"
|
138
|
+
end
|
139
|
+
|
140
|
+
# needed here because of empty arrays
|
141
|
+
@raw_msg.align(signature.child.alignment)
|
142
|
+
|
143
|
+
items = []
|
144
|
+
end_pos = @raw_msg.pos + array_bytes
|
145
|
+
while @raw_msg.pos < end_pos
|
146
|
+
item = do_parse(signature.child, mode: mode)
|
147
|
+
items << item
|
148
|
+
end
|
149
|
+
is_hash = signature.child.sigtype == Type::DICT_ENTRY
|
150
|
+
packet = data_class.from_items(items, mode: mode, member_type: signature.child, hash: is_hash)
|
151
|
+
end
|
152
|
+
end
|
226
153
|
packet
|
227
|
-
end
|
228
|
-
end
|
154
|
+
end
|
155
|
+
end
|
229
156
|
|
230
157
|
# D-Bus packet marshaller class
|
231
158
|
#
|
@@ -234,40 +161,35 @@ module DBus
|
|
234
161
|
class PacketMarshaller
|
235
162
|
# The current or result packet.
|
236
163
|
# FIXME: allow access only when marshalling is finished
|
164
|
+
# @return [String]
|
237
165
|
attr_reader :packet
|
238
166
|
|
167
|
+
# @return [:little,:big]
|
168
|
+
attr_reader :endianness
|
169
|
+
|
239
170
|
# Create a new marshaller, setting the current packet to the
|
240
171
|
# empty packet.
|
241
|
-
def initialize(offset = 0)
|
172
|
+
def initialize(offset = 0, endianness: HOST_ENDIANNESS)
|
173
|
+
@endianness = endianness
|
242
174
|
@packet = ""
|
243
175
|
@offset = offset # for correct alignment of nested marshallers
|
244
176
|
end
|
245
177
|
|
246
|
-
# Round
|
247
|
-
def num_align(
|
248
|
-
case
|
178
|
+
# Round _num_ up to the specified power of two, _alignment_
|
179
|
+
def num_align(num, alignment)
|
180
|
+
case alignment
|
249
181
|
when 1, 2, 4, 8
|
250
|
-
bits =
|
251
|
-
|
182
|
+
bits = alignment - 1
|
183
|
+
num + bits & ~bits
|
252
184
|
else
|
253
|
-
raise "Unsupported alignment"
|
185
|
+
raise ArgumentError, "Unsupported alignment #{alignment}"
|
254
186
|
end
|
255
187
|
end
|
256
188
|
|
257
|
-
# Align the buffer with NULL (\0) bytes on a byte length of
|
258
|
-
def align(
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
# Append the the string _str_ itself to the packet.
|
263
|
-
def append_string(str)
|
264
|
-
align(4)
|
265
|
-
@packet += [str.bytesize].pack("L") + [str].pack("Z*")
|
266
|
-
end
|
267
|
-
|
268
|
-
# Append the the signature _signature_ itself to the packet.
|
269
|
-
def append_signature(str)
|
270
|
-
@packet += str.bytesize.chr + str + "\0"
|
189
|
+
# Align the buffer with NULL (\0) bytes on a byte length of _alignment_.
|
190
|
+
def align(alignment)
|
191
|
+
pad_count = num_align(@offset + @packet.bytesize, alignment) - @offset
|
192
|
+
@packet = @packet.ljust(pad_count, 0.chr)
|
271
193
|
end
|
272
194
|
|
273
195
|
# Append the array type _type_ to the packet and allow for appending
|
@@ -282,7 +204,9 @@ module DBus
|
|
282
204
|
yield
|
283
205
|
sz = @packet.bytesize - contentidx
|
284
206
|
raise InvalidPacketException if sz > 67_108_864
|
285
|
-
|
207
|
+
|
208
|
+
sz_data = Data::UInt32.new(sz)
|
209
|
+
@packet[sizeidx...sizeidx + 4] = sz_data.marshall(endianness)
|
286
210
|
end
|
287
211
|
|
288
212
|
# Align and allow for appending struct fields.
|
@@ -294,110 +218,122 @@ module DBus
|
|
294
218
|
# Append a value _val_ to the packet based on its _type_.
|
295
219
|
#
|
296
220
|
# Host native endianness is used, declared in Message#marshall
|
221
|
+
#
|
222
|
+
# @param type [SingleCompleteType] (or Integer or {Type})
|
223
|
+
# @param val [::Object]
|
297
224
|
def append(type, val)
|
298
225
|
raise TypeException, "Cannot send nil" if val.nil?
|
299
226
|
|
300
227
|
type = type.chr if type.is_a?(Integer)
|
301
228
|
type = Type::Parser.new(type).parse[0] if type.is_a?(String)
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
align(
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
@packet += [val].pack("
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
parsed = Type::Parser.new(val[0]).parse
|
347
|
-
vartype = parsed[0] if parsed.size == 1
|
348
|
-
vardata = val[1]
|
349
|
-
rescue Type::SignatureException
|
350
|
-
# no assignment
|
229
|
+
# type is [Type] now
|
230
|
+
data_class = Data::BY_TYPE_CODE[type.sigtype]
|
231
|
+
if data_class.nil?
|
232
|
+
raise NotImplementedError,
|
233
|
+
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
234
|
+
end
|
235
|
+
|
236
|
+
if data_class.fixed?
|
237
|
+
align(data_class.alignment)
|
238
|
+
data = data_class.new(val)
|
239
|
+
@packet += data.marshall(endianness)
|
240
|
+
elsif data_class.basic?
|
241
|
+
val = val.value if val.is_a?(Data::Basic)
|
242
|
+
align(data_class.size_class.alignment)
|
243
|
+
size_data = data_class.size_class.new(val.bytesize)
|
244
|
+
@packet += size_data.marshall(endianness)
|
245
|
+
# Z* makes a binary string, as opposed to interpolation
|
246
|
+
@packet += [val].pack("Z*")
|
247
|
+
else
|
248
|
+
case type.sigtype
|
249
|
+
|
250
|
+
when Type::VARIANT
|
251
|
+
append_variant(val)
|
252
|
+
when Type::ARRAY
|
253
|
+
append_array(type.child, val)
|
254
|
+
when Type::STRUCT, Type::DICT_ENTRY
|
255
|
+
val = val.value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry)
|
256
|
+
unless val.is_a?(Array) || val.is_a?(Struct)
|
257
|
+
type_name = Type::TYPE_MAPPING[type.sigtype].first
|
258
|
+
raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
|
259
|
+
end
|
260
|
+
|
261
|
+
if type.sigtype == Type::DICT_ENTRY && val.size != 2
|
262
|
+
raise TypeException, "DICT_ENTRY expects a pair"
|
263
|
+
end
|
264
|
+
|
265
|
+
if type.members.size != val.size
|
266
|
+
type_name = Type::TYPE_MAPPING[type.sigtype].first
|
267
|
+
raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}"
|
268
|
+
end
|
269
|
+
|
270
|
+
struct do
|
271
|
+
type.members.zip(val).each do |t, v|
|
272
|
+
append(t, v)
|
351
273
|
end
|
352
274
|
end
|
275
|
+
else
|
276
|
+
raise NotImplementedError,
|
277
|
+
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
353
278
|
end
|
354
|
-
|
355
|
-
|
356
|
-
vartype = Type::Parser.new(vartype).parse[0]
|
357
|
-
end
|
279
|
+
end
|
280
|
+
end
|
358
281
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
array(type.child) do
|
378
|
-
val.each do |elem|
|
379
|
-
append(type.child, elem)
|
282
|
+
def append_variant(val)
|
283
|
+
vartype = nil
|
284
|
+
if val.is_a?(DBus::Data::Base)
|
285
|
+
vartype = val.type # FIXME: box or unbox another variant?
|
286
|
+
vardata = val.value
|
287
|
+
elsif val.is_a?(Array) && val.size == 2
|
288
|
+
case val[0]
|
289
|
+
when Type
|
290
|
+
vartype, vardata = val
|
291
|
+
# Ambiguous but easy to use, because Type
|
292
|
+
# cannot construct "as" "a{sv}" easily
|
293
|
+
when String
|
294
|
+
begin
|
295
|
+
parsed = Type::Parser.new(val[0]).parse
|
296
|
+
vartype = parsed[0] if parsed.size == 1
|
297
|
+
vardata = val[1]
|
298
|
+
rescue Type::SignatureException
|
299
|
+
# no assignment
|
380
300
|
end
|
381
301
|
end
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
302
|
+
end
|
303
|
+
if vartype.nil?
|
304
|
+
vartype, vardata = PacketMarshaller.make_variant(val)
|
305
|
+
vartype = Type::Parser.new(vartype).parse[0]
|
306
|
+
end
|
307
|
+
|
308
|
+
append(Data::Signature.type, vartype.to_s)
|
309
|
+
align(vartype.alignment)
|
310
|
+
sub = PacketMarshaller.new(@offset + @packet.bytesize, endianness: endianness)
|
311
|
+
sub.append(vartype, vardata)
|
312
|
+
@packet += sub.packet
|
313
|
+
end
|
314
|
+
|
315
|
+
# @param child_type [Type]
|
316
|
+
def append_array(child_type, val)
|
317
|
+
if val.is_a?(Hash)
|
318
|
+
raise TypeException, "Expected an Array but got a Hash" if child_type.sigtype != Type::DICT_ENTRY
|
319
|
+
|
320
|
+
# Damn ruby rocks here
|
321
|
+
val = val.to_a
|
322
|
+
end
|
323
|
+
# If string is recieved and ay is expected, explode the string
|
324
|
+
if val.is_a?(String) && child_type.sigtype == Type::BYTE
|
325
|
+
val = val.bytes
|
326
|
+
end
|
327
|
+
if !val.is_a?(Enumerable)
|
328
|
+
raise TypeException, "Expected an Enumerable of #{child_type.inspect} but got a #{val.class}"
|
329
|
+
end
|
330
|
+
|
331
|
+
array(child_type) do
|
332
|
+
val.each do |elem|
|
333
|
+
append(child_type, elem)
|
395
334
|
end
|
396
|
-
else
|
397
|
-
raise NotImplementedError,
|
398
|
-
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
399
335
|
end
|
400
|
-
end
|
336
|
+
end
|
401
337
|
|
402
338
|
# Make a [signature, value] pair for a variant
|
403
339
|
def self.make_variant(value)
|
@@ -429,5 +365,5 @@ module DBus
|
|
429
365
|
end
|
430
366
|
end
|
431
367
|
end
|
432
|
-
end
|
433
|
-
end
|
368
|
+
end
|
369
|
+
end
|
data/lib/dbus/matchrule.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file is part of the ruby-dbus project
|
2
4
|
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
3
5
|
#
|
@@ -27,7 +29,7 @@ module DBus
|
|
27
29
|
attr_accessor :path
|
28
30
|
# The destination filter.
|
29
31
|
attr_accessor :destination
|
30
|
-
# The type type that is matched.
|
32
|
+
# @return [String] The type type that is matched.
|
31
33
|
attr_reader :type
|
32
34
|
|
33
35
|
# Create a new match rule
|
@@ -35,13 +37,14 @@ module DBus
|
|
35
37
|
@sender = @interface = @member = @path = @destination = @type = nil
|
36
38
|
end
|
37
39
|
|
38
|
-
# Set the message types to filter to type
|
40
|
+
# Set the message types to filter to type _typ_.
|
39
41
|
# Possible message types are: signal, method_call, method_return, and error.
|
40
|
-
def type=(
|
41
|
-
if !["signal", "method_call", "method_return", "error"].member?(
|
42
|
-
raise MatchRuleException,
|
42
|
+
def type=(typ)
|
43
|
+
if !["signal", "method_call", "method_return", "error"].member?(typ)
|
44
|
+
raise MatchRuleException, typ
|
43
45
|
end
|
44
|
-
|
46
|
+
|
47
|
+
@type = typ
|
45
48
|
end
|
46
49
|
|
47
50
|
# Returns a match rule string version of the object. E.g.:
|
@@ -58,11 +61,13 @@ module DBus
|
|
58
61
|
def from_s(str)
|
59
62
|
str.split(",").each do |eq|
|
60
63
|
next unless eq =~ /^(.*)='([^']*)'$/
|
64
|
+
|
61
65
|
# "
|
62
66
|
name = Regexp.last_match(1)
|
63
67
|
val = Regexp.last_match(2)
|
64
68
|
raise MatchRuleException, name unless FILTERS.member?(name.to_sym)
|
65
|
-
|
69
|
+
|
70
|
+
method("#{name}=").call(val)
|
66
71
|
end
|
67
72
|
self
|
68
73
|
end
|
@@ -80,18 +85,13 @@ module DBus
|
|
80
85
|
|
81
86
|
# Determines whether a message _msg_ matches the match rule.
|
82
87
|
def match(msg)
|
83
|
-
if @type
|
84
|
-
if { Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
|
85
|
-
Message::METHOD_RETURN => "method_return",
|
86
|
-
Message::ERROR => "error" }[msg.message_type] != @type
|
87
|
-
return false
|
88
|
-
end
|
89
|
-
end
|
88
|
+
return false if @type && @type != msg.message_type_s
|
90
89
|
return false if @interface && @interface != msg.interface
|
91
90
|
return false if @member && @member != msg.member
|
92
91
|
return false if @path && @path != msg.path
|
92
|
+
|
93
93
|
# FIXME: sender and destination are ignored
|
94
94
|
true
|
95
95
|
end
|
96
|
-
end
|
97
|
-
end
|
96
|
+
end
|
97
|
+
end
|