1 | use core::panic; |
2 | use std::{ |
3 | convert::TryFrom, |
4 | fmt::{Display, Write}, |
5 | ops::Deref, |
6 | }; |
7 | |
8 | use serde::{de, Deserialize, Serialize}; |
9 | use static_assertions::assert_impl_all; |
10 | use zvariant::Structure; |
11 | |
12 | use crate::{ |
13 | names::{BusName, InterfaceName, MemberName, UniqueName}, |
14 | zvariant::{ObjectPath, Str, Type}, |
15 | Error, MatchRuleBuilder, MessageType, Result, |
16 | }; |
17 | |
18 | /// A bus match rule for subscribing to specific messages. |
19 | /// |
20 | /// This is mainly used by peer to subscribe to specific signals as by default the bus will not |
21 | /// send out most broadcasted signals. This API is intended to make it easy to create and parse |
22 | /// match rules. See the [match rules section of the D-Bus specification][mrs] for a description of |
23 | /// each possible element of a match rule. |
24 | /// |
25 | /// # Examples |
26 | /// |
27 | /// ``` |
28 | /// # fn main() -> Result<(), Box<dyn std::error::Error>> { |
29 | /// # use zbus::MatchRule; |
30 | /// use std::convert::TryFrom; |
31 | /// |
32 | /// // Let's take the most typical example of match rule to subscribe to properties' changes: |
33 | /// let rule = MatchRule::builder() |
34 | /// .msg_type(zbus::MessageType::Signal) |
35 | /// .sender("org.freedesktop.DBus" )? |
36 | /// .interface("org.freedesktop.DBus.Properties" )? |
37 | /// .member("PropertiesChanged" )? |
38 | /// .add_arg("org.zbus" )? |
39 | /// // Sometimes it's useful to match empty strings (null check). |
40 | /// .add_arg("" )? |
41 | /// .build(); |
42 | /// let rule_str = rule.to_string(); |
43 | /// assert_eq!( |
44 | /// rule_str, |
45 | /// "type='signal',\ |
46 | /// sender='org.freedesktop.DBus',\ |
47 | /// interface='org.freedesktop.DBus.Properties',\ |
48 | /// member='PropertiesChanged',\ |
49 | /// arg0='org.zbus',\ |
50 | /// arg1=''" , |
51 | /// ); |
52 | /// |
53 | /// // Let's parse it back. |
54 | /// let parsed_rule = MatchRule::try_from(rule_str.as_str())?; |
55 | /// assert_eq!(rule, parsed_rule); |
56 | /// |
57 | /// // Now for `ObjectManager::InterfacesAdded` signal. |
58 | /// let rule = MatchRule::builder() |
59 | /// .msg_type(zbus::MessageType::Signal) |
60 | /// .sender("org.zbus" )? |
61 | /// .interface("org.freedesktop.DBus.ObjectManager" )? |
62 | /// .member("InterfacesAdded" )? |
63 | /// .arg_path(0, "/org/zbus/NewPath" )? |
64 | /// .build(); |
65 | /// let rule_str = rule.to_string(); |
66 | /// assert_eq!( |
67 | /// rule_str, |
68 | /// "type='signal',\ |
69 | /// sender='org.zbus',\ |
70 | /// interface='org.freedesktop.DBus.ObjectManager',\ |
71 | /// member='InterfacesAdded',\ |
72 | /// arg0path='/org/zbus/NewPath'" , |
73 | /// ); |
74 | /// |
75 | /// // Let's parse it back. |
76 | /// let parsed_rule = MatchRule::try_from(rule_str.as_str())?; |
77 | /// assert_eq!(rule, parsed_rule); |
78 | /// |
79 | /// # Ok(()) |
80 | /// # } |
81 | /// ``` |
82 | /// |
83 | /// # Caveats |
84 | /// |
85 | /// The `PartialEq` implementation assumes arguments in both rules are in the same order. |
86 | /// |
87 | /// [mrs]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules |
88 | #[derive (Clone, Debug, PartialEq, Eq, Hash, Type)] |
89 | #[zvariant(signature = "s" )] |
90 | pub struct MatchRule<'m> { |
91 | pub(crate) msg_type: Option<MessageType>, |
92 | pub(crate) sender: Option<BusName<'m>>, |
93 | pub(crate) interface: Option<InterfaceName<'m>>, |
94 | pub(crate) member: Option<MemberName<'m>>, |
95 | pub(crate) path_spec: Option<MatchRulePathSpec<'m>>, |
96 | pub(crate) destination: Option<UniqueName<'m>>, |
97 | pub(crate) args: Vec<(u8, Str<'m>)>, |
98 | pub(crate) arg_paths: Vec<(u8, ObjectPath<'m>)>, |
99 | pub(crate) arg0namespace: Option<InterfaceName<'m>>, |
100 | pub(crate) arg0ns: Option<Str<'m>>, |
101 | } |
102 | |
103 | assert_impl_all!(MatchRule<'_>: Send, Sync, Unpin); |
104 | |
105 | impl<'m> MatchRule<'m> { |
106 | /// Create a builder for `MatchRuleBuilder`. |
107 | pub fn builder() -> MatchRuleBuilder<'m> { |
108 | MatchRuleBuilder::new() |
109 | } |
110 | |
111 | /// The sender, if set. |
112 | pub fn sender(&self) -> Option<&BusName<'_>> { |
113 | self.sender.as_ref() |
114 | } |
115 | |
116 | /// The message type, if set. |
117 | pub fn msg_type(&self) -> Option<MessageType> { |
118 | self.msg_type |
119 | } |
120 | |
121 | /// The interfac, if set. |
122 | pub fn interface(&self) -> Option<&InterfaceName<'_>> { |
123 | self.interface.as_ref() |
124 | } |
125 | |
126 | /// The member name if set. |
127 | pub fn member(&self) -> Option<&MemberName<'_>> { |
128 | self.member.as_ref() |
129 | } |
130 | |
131 | /// The path or path namespace, if set. |
132 | pub fn path_spec(&self) -> Option<&MatchRulePathSpec<'_>> { |
133 | self.path_spec.as_ref() |
134 | } |
135 | |
136 | /// The destination, if set. |
137 | pub fn destination(&self) -> Option<&UniqueName<'_>> { |
138 | self.destination.as_ref() |
139 | } |
140 | |
141 | /// The arguments. |
142 | pub fn args(&self) -> &[(u8, Str<'_>)] { |
143 | self.args.as_ref() |
144 | } |
145 | |
146 | /// The argument paths. |
147 | pub fn arg_paths(&self) -> &[(u8, ObjectPath<'_>)] { |
148 | self.arg_paths.as_ref() |
149 | } |
150 | |
151 | /// Match messages whose first argument is within the specified namespace. |
152 | /// |
153 | /// This function is deprecated because the choice of `InterfaceName` was too restrictive. |
154 | #[deprecated = "use arg0ns instead" ] |
155 | pub fn arg0namespace(&self) -> Option<&InterfaceName<'_>> { |
156 | self.arg0namespace.as_ref() |
157 | } |
158 | |
159 | /// Match messages whose first argument is within the specified namespace. |
160 | pub fn arg0ns(&self) -> Option<&Str<'m>> { |
161 | self.arg0ns.as_ref() |
162 | } |
163 | |
164 | /// Creates an owned clone of `self`. |
165 | pub fn to_owned(&self) -> MatchRule<'static> { |
166 | MatchRule { |
167 | msg_type: self.msg_type, |
168 | sender: self.sender.as_ref().map(|s| s.to_owned()), |
169 | interface: self.interface.as_ref().map(|i| i.to_owned()), |
170 | member: self.member.as_ref().map(|m| m.to_owned()), |
171 | path_spec: self.path_spec.as_ref().map(|p| p.to_owned()), |
172 | destination: self.destination.as_ref().map(|d| d.to_owned()), |
173 | args: self.args.iter().map(|(i, s)| (*i, s.to_owned())).collect(), |
174 | arg_paths: self |
175 | .arg_paths |
176 | .iter() |
177 | .map(|(i, p)| (*i, p.to_owned())) |
178 | .collect(), |
179 | arg0namespace: self.arg0namespace.as_ref().map(|a| a.to_owned()), |
180 | arg0ns: self.arg0ns.as_ref().map(|a| a.to_owned()), |
181 | } |
182 | } |
183 | |
184 | /// Creates an owned clone of `self`. |
185 | pub fn into_owned(self) -> MatchRule<'static> { |
186 | MatchRule { |
187 | msg_type: self.msg_type, |
188 | sender: self.sender.map(|s| s.into_owned()), |
189 | interface: self.interface.map(|i| i.into_owned()), |
190 | member: self.member.map(|m| m.into_owned()), |
191 | path_spec: self.path_spec.map(|p| p.into_owned()), |
192 | destination: self.destination.map(|d| d.into_owned()), |
193 | args: self |
194 | .args |
195 | .into_iter() |
196 | .map(|(i, s)| (i, s.into_owned())) |
197 | .collect(), |
198 | arg_paths: self |
199 | .arg_paths |
200 | .into_iter() |
201 | .map(|(i, p)| (i, p.into_owned())) |
202 | .collect(), |
203 | arg0namespace: self.arg0namespace.map(|a| a.into_owned()), |
204 | arg0ns: self.arg0ns.map(|a| a.into_owned()), |
205 | } |
206 | } |
207 | |
208 | /// Match the given message against this rule. |
209 | /// |
210 | /// # Caveats |
211 | /// |
212 | /// Since this method doesn't have any knowledge of names on the bus (or even connection to a |
213 | /// bus) matching always succeeds for: |
214 | /// |
215 | /// * `sender` in the rule (if set) that is a well-known name. The `sender` on a message is |
216 | /// always a unique name. |
217 | /// * `destination` in the rule when `destination` on the `msg` is a well-known name. The |
218 | /// `destination` on match rule is always a unique name. |
219 | pub fn matches(&self, msg: &zbus::Message) -> Result<bool> { |
220 | let hdr = msg.header()?; |
221 | |
222 | // Start with message type. |
223 | if let Some(msg_type) = self.msg_type() { |
224 | if msg_type != msg.message_type() { |
225 | return Ok(false); |
226 | } |
227 | } |
228 | |
229 | // Then check sender. |
230 | if let Some(sender) = self.sender() { |
231 | match sender { |
232 | BusName::Unique(name) if Some(name) != hdr.sender()? => { |
233 | return Ok(false); |
234 | } |
235 | BusName::Unique(_) => (), |
236 | // We can't match against a well-known name. |
237 | BusName::WellKnown(_) => (), |
238 | } |
239 | } |
240 | |
241 | // The interface. |
242 | if let Some(interface) = self.interface() { |
243 | match msg.interface().as_ref() { |
244 | Some(msg_interface) if interface != msg_interface => return Ok(false), |
245 | Some(_) => (), |
246 | None => return Ok(false), |
247 | } |
248 | } |
249 | |
250 | // The member. |
251 | if let Some(member) = self.member() { |
252 | match msg.member().as_ref() { |
253 | Some(msg_member) if member != msg_member => return Ok(false), |
254 | Some(_) => (), |
255 | None => return Ok(false), |
256 | } |
257 | } |
258 | |
259 | // The destination. |
260 | if let Some(destination) = self.destination() { |
261 | match hdr.destination()? { |
262 | Some(BusName::Unique(name)) if destination != name => { |
263 | return Ok(false); |
264 | } |
265 | Some(BusName::Unique(_)) | None => (), |
266 | // We can't match against a well-known name. |
267 | Some(BusName::WellKnown(_)) => (), |
268 | }; |
269 | } |
270 | |
271 | // The path. |
272 | if let Some(path_spec) = self.path_spec() { |
273 | let msg_path = match msg.path() { |
274 | Some(p) => p, |
275 | None => return Ok(false), |
276 | }; |
277 | match path_spec { |
278 | MatchRulePathSpec::Path(path) if path != &msg_path => return Ok(false), |
279 | MatchRulePathSpec::PathNamespace(path_ns) |
280 | if !msg_path.starts_with(path_ns.as_str()) => |
281 | { |
282 | return Ok(false); |
283 | } |
284 | MatchRulePathSpec::Path(_) | MatchRulePathSpec::PathNamespace(_) => (), |
285 | } |
286 | } |
287 | |
288 | // The arg0 namespace. |
289 | if let Some(arg0_ns) = self.arg0ns() { |
290 | if let Ok(arg0) = msg.body_unchecked::<BusName<'_>>() { |
291 | match arg0.strip_prefix(arg0_ns.as_str()) { |
292 | None => return Ok(false), |
293 | Some(s) if !s.is_empty() && !s.starts_with('.' ) => return Ok(false), |
294 | _ => (), |
295 | } |
296 | } else { |
297 | return Ok(false); |
298 | } |
299 | } |
300 | |
301 | // Args |
302 | if self.args().is_empty() && self.arg_paths().is_empty() { |
303 | return Ok(true); |
304 | } |
305 | let structure = match msg.body::<Structure<'_>>() { |
306 | Ok(s) => s, |
307 | Err(_) => return Ok(false), |
308 | }; |
309 | let args = structure.fields(); |
310 | |
311 | for (i, arg) in self.args() { |
312 | match args.get(*i as usize) { |
313 | Some(msg_arg) => match <&str>::try_from(msg_arg) { |
314 | Ok(msg_arg) if arg != msg_arg => return Ok(false), |
315 | Ok(_) => (), |
316 | Err(_) => return Ok(false), |
317 | }, |
318 | None => return Ok(false), |
319 | } |
320 | } |
321 | |
322 | // Path args |
323 | for (i, path) in self.arg_paths() { |
324 | match args.get(*i as usize) { |
325 | Some(msg_arg) => match <ObjectPath<'_>>::try_from(msg_arg) { |
326 | Ok(msg_arg) if *path != msg_arg => return Ok(false), |
327 | Ok(_) => (), |
328 | Err(_) => return Ok(false), |
329 | }, |
330 | None => return Ok(false), |
331 | } |
332 | } |
333 | |
334 | Ok(true) |
335 | } |
336 | } |
337 | |
338 | impl Display for MatchRule<'_> { |
339 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
340 | let mut first_component = true; |
341 | if let Some(msg_type) = self.msg_type() { |
342 | let type_str = match msg_type { |
343 | MessageType::Error => "error" , |
344 | MessageType::Invalid => panic!("invalid message type" ), |
345 | MessageType::MethodCall => "method_call" , |
346 | MessageType::MethodReturn => "method_return" , |
347 | MessageType::Signal => "signal" , |
348 | }; |
349 | write_match_rule_string_component(f, "type" , type_str, &mut first_component)?; |
350 | } |
351 | if let Some(sender) = self.sender() { |
352 | write_match_rule_string_component(f, "sender" , sender, &mut first_component)?; |
353 | } |
354 | if let Some(interface) = self.interface() { |
355 | write_match_rule_string_component(f, "interface" , interface, &mut first_component)?; |
356 | } |
357 | if let Some(member) = self.member() { |
358 | write_match_rule_string_component(f, "member" , member, &mut first_component)?; |
359 | } |
360 | if let Some(destination) = self.destination() { |
361 | write_match_rule_string_component(f, "destination" , destination, &mut first_component)?; |
362 | } |
363 | if let Some(path_spec) = self.path_spec() { |
364 | let (key, value) = match path_spec { |
365 | MatchRulePathSpec::Path(path) => ("path" , path), |
366 | MatchRulePathSpec::PathNamespace(ns) => ("path_namespace" , ns), |
367 | }; |
368 | write_match_rule_string_component(f, key, value, &mut first_component)?; |
369 | } |
370 | for (i, arg) in self.args() { |
371 | write_comma(f, &mut first_component)?; |
372 | write!(f, "arg {i}=' {arg}'" )?; |
373 | } |
374 | for (i, arg_path) in self.arg_paths() { |
375 | write_comma(f, &mut first_component)?; |
376 | write!(f, "arg {i}path=' {arg_path}'" )?; |
377 | } |
378 | if let Some(arg0namespace) = self.arg0ns() { |
379 | write_comma(f, &mut first_component)?; |
380 | write!(f, "arg0namespace=' {arg0namespace}'" )?; |
381 | } |
382 | |
383 | Ok(()) |
384 | } |
385 | } |
386 | |
387 | fn write_match_rule_string_component( |
388 | f: &mut std::fmt::Formatter<'_>, |
389 | key: &str, |
390 | value: &str, |
391 | first_component: &mut bool, |
392 | ) -> std::fmt::Result { |
393 | write_comma(f, first_component)?; |
394 | f.write_str(data:key)?; |
395 | f.write_str(data:"='" )?; |
396 | f.write_str(data:value)?; |
397 | f.write_char(' \'' )?; |
398 | |
399 | Ok(()) |
400 | } |
401 | |
402 | fn write_comma(f: &mut std::fmt::Formatter<'_>, first_component: &mut bool) -> std::fmt::Result { |
403 | if *first_component { |
404 | *first_component = false; |
405 | } else { |
406 | f.write_char(',' )?; |
407 | } |
408 | |
409 | Ok(()) |
410 | } |
411 | |
412 | impl<'m> TryFrom<&'m str> for MatchRule<'m> { |
413 | type Error = Error; |
414 | |
415 | fn try_from(s: &'m str) -> Result<Self> { |
416 | let components = s.split(',' ); |
417 | if components.clone().peekable().peek().is_none() { |
418 | return Err(Error::InvalidMatchRule); |
419 | } |
420 | let mut builder = MatchRule::builder(); |
421 | for component in components { |
422 | let (key, value) = component.split_once('=' ).ok_or(Error::InvalidMatchRule)?; |
423 | if key.is_empty() |
424 | || value.len() < 2 |
425 | || !value.starts_with(' \'' ) |
426 | || !value.ends_with(' \'' ) |
427 | { |
428 | return Err(Error::InvalidMatchRule); |
429 | } |
430 | let value = &value[1..value.len() - 1]; |
431 | builder = match key { |
432 | "type" => { |
433 | let msg_type = match value { |
434 | "error" => MessageType::Error, |
435 | "method_call" => MessageType::MethodCall, |
436 | "method_return" => MessageType::MethodReturn, |
437 | "signal" => MessageType::Signal, |
438 | _ => return Err(Error::InvalidMatchRule), |
439 | }; |
440 | builder.msg_type(msg_type) |
441 | } |
442 | "sender" => builder.sender(value)?, |
443 | "interface" => builder.interface(value)?, |
444 | "member" => builder.member(value)?, |
445 | "path" => builder.path(value)?, |
446 | "path_namespace" => builder.path_namespace(value)?, |
447 | "destination" => builder.destination(value)?, |
448 | "arg0namespace" => builder.arg0ns(value)?, |
449 | key if key.starts_with("arg" ) => { |
450 | if let Some(trailing_idx) = key.find("path" ) { |
451 | let idx = key[3..trailing_idx] |
452 | .parse::<u8>() |
453 | .map_err(|_| Error::InvalidMatchRule)?; |
454 | builder.arg_path(idx, value)? |
455 | } else { |
456 | let idx = key[3..] |
457 | .parse::<u8>() |
458 | .map_err(|_| Error::InvalidMatchRule)?; |
459 | builder.arg(idx, value)? |
460 | } |
461 | } |
462 | _ => return Err(Error::InvalidMatchRule), |
463 | }; |
464 | } |
465 | |
466 | Ok(builder.build()) |
467 | } |
468 | } |
469 | |
470 | impl<'de: 'm, 'm> Deserialize<'de> for MatchRule<'m> { |
471 | fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error> |
472 | where |
473 | D: serde::Deserializer<'de>, |
474 | { |
475 | let name: &str = <&str>::deserialize(deserializer)?; |
476 | |
477 | Self::try_from(name).map_err(|e: Error| de::Error::custom(msg:e.to_string())) |
478 | } |
479 | } |
480 | |
481 | impl Serialize for MatchRule<'_> { |
482 | fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> |
483 | where |
484 | S: serde::Serializer, |
485 | { |
486 | serializer.serialize_str(&self.to_string()) |
487 | } |
488 | } |
489 | |
490 | /// The path or path namespace. |
491 | #[derive (Clone, Debug, PartialEq, Eq, Hash)] |
492 | pub enum MatchRulePathSpec<'m> { |
493 | Path(ObjectPath<'m>), |
494 | PathNamespace(ObjectPath<'m>), |
495 | } |
496 | |
497 | assert_impl_all!(MatchRulePathSpec<'_>: Send, Sync, Unpin); |
498 | |
499 | impl<'m> MatchRulePathSpec<'m> { |
500 | /// Creates an owned clone of `self`. |
501 | fn to_owned(&self) -> MatchRulePathSpec<'static> { |
502 | match self { |
503 | MatchRulePathSpec::Path(path: &ObjectPath<'_>) => MatchRulePathSpec::Path(path.to_owned()), |
504 | MatchRulePathSpec::PathNamespace(ns: &ObjectPath<'_>) => MatchRulePathSpec::PathNamespace(ns.to_owned()), |
505 | } |
506 | } |
507 | |
508 | /// Creates an owned clone of `self`. |
509 | pub fn into_owned(self) -> MatchRulePathSpec<'static> { |
510 | match self { |
511 | MatchRulePathSpec::Path(path: ObjectPath<'_>) => MatchRulePathSpec::Path(path.into_owned()), |
512 | MatchRulePathSpec::PathNamespace(ns: ObjectPath<'_>) => { |
513 | MatchRulePathSpec::PathNamespace(ns.into_owned()) |
514 | } |
515 | } |
516 | } |
517 | } |
518 | |
519 | /// Owned sibling of [`MatchRule`]. |
520 | #[derive (Clone, Debug, Hash, PartialEq, Eq, Serialize, Type)] |
521 | pub struct OwnedMatchRule(#[serde(borrow)] MatchRule<'static>); |
522 | |
523 | assert_impl_all!(OwnedMatchRule: Send, Sync, Unpin); |
524 | |
525 | impl OwnedMatchRule { |
526 | /// Convert to the inner `MatchRule`, consuming `self`. |
527 | pub fn into_inner(self) -> MatchRule<'static> { |
528 | self.0 |
529 | } |
530 | |
531 | /// Get a reference to the inner `MatchRule`. |
532 | pub fn inner(&self) -> &MatchRule<'static> { |
533 | &self.0 |
534 | } |
535 | } |
536 | |
537 | impl Deref for OwnedMatchRule { |
538 | type Target = MatchRule<'static>; |
539 | |
540 | fn deref(&self) -> &Self::Target { |
541 | &self.0 |
542 | } |
543 | } |
544 | |
545 | impl From<OwnedMatchRule> for MatchRule<'static> { |
546 | fn from(o: OwnedMatchRule) -> Self { |
547 | o.into_inner() |
548 | } |
549 | } |
550 | |
551 | impl<'unowned, 'owned: 'unowned> From<&'owned OwnedMatchRule> for MatchRule<'unowned> { |
552 | fn from(rule: &'owned OwnedMatchRule) -> Self { |
553 | rule.inner().clone() |
554 | } |
555 | } |
556 | |
557 | impl From<MatchRule<'_>> for OwnedMatchRule { |
558 | fn from(rule: MatchRule<'_>) -> Self { |
559 | OwnedMatchRule(rule.into_owned()) |
560 | } |
561 | } |
562 | |
563 | impl TryFrom<&'_ str> for OwnedMatchRule { |
564 | type Error = Error; |
565 | |
566 | fn try_from(value: &str) -> Result<Self> { |
567 | Ok(Self::from(MatchRule::try_from(value)?)) |
568 | } |
569 | } |
570 | |
571 | impl<'de> Deserialize<'de> for OwnedMatchRule { |
572 | fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> |
573 | where |
574 | D: de::Deserializer<'de>, |
575 | { |
576 | String::deserialize(deserializer) |
577 | .and_then(|r| { |
578 | MatchRule::try_from(r.as_str()) |
579 | .map(|r| r.to_owned()) |
580 | .map_err(|e| de::Error::custom(e.to_string())) |
581 | }) |
582 | .map(Self) |
583 | } |
584 | } |
585 | |
586 | impl PartialEq<MatchRule<'_>> for OwnedMatchRule { |
587 | fn eq(&self, other: &MatchRule<'_>) -> bool { |
588 | self.0 == *other |
589 | } |
590 | } |
591 | |