1 #![allow(deprecated)]
2 
3 use super::{Connection, Message, MessageItem, Error, TypeSig};
4 use std::collections::BTreeMap;
5 use std::rc::Rc;
6 use std::cell::{Cell, RefCell};
7 use std::borrow::Cow;
8 
9 /// a Method has a list of Arguments.
10 pub struct Argument<'a> {
11     name: &'a str,
12     sig: TypeSig<'a>,
13 }
14 
15 impl<'a> Argument<'a> {
16     /// Create a new Argument.
new<T: Into<Cow<'a, str>>>(name: &'a str, sig: T) -> Argument<'a>17     pub fn new<T: Into<Cow<'a, str>>>(name: &'a str, sig: T) -> Argument<'a> {
18         Argument { name: name, sig: sig.into() }
19     }
20 }
21 
22 struct Annotation {
23     name: String,
24     value: String,
25 }
26 
27 struct ISignal<'a> {
28     args: Vec<Argument<'a>>,
29     anns: Vec<Annotation>,
30 }
31 
32 /// Declares that an Interface can send this signal
33 pub struct Signal<'a> {
34     name: String,
35     i: ISignal<'a>,
36 }
37 
38 impl<'a> Signal<'a> {
39     /// Create a new Signal.
new<N: ToString>(name: N, args: Vec<Argument<'a>>) -> Signal<'a>40     pub fn new<N: ToString>(name: N, args: Vec<Argument<'a>>) -> Signal<'a> {
41         Signal { name: name.to_string(), i: ISignal { args: args, anns: vec![] } }
42     }
43 
44     /// Add an Annotation to the Signal.
annotate<N: ToString, V: ToString>(&mut self, name: N, value: V)45     pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) {
46         self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() });
47     }
48 }
49 
50 /// A method returns either a list of MessageItems, or an error - the tuple
51 /// represents the name and message of the Error.
52 pub type MethodResult = Result<Vec<MessageItem>, (&'static str, String)>;
53 /// Contains the retrieved MessageItem or an error tuple containing the
54 /// name and message of the error.
55 pub type PropertyGetResult = Result<MessageItem, (&'static str, String)>;
56 /// Contains () or an error tuple containing the name and message of
57 /// the error.
58 pub type PropertySetResult = Result<(), (&'static str, String)>;
59 
60 /// A boxed closure for dynamic dispatch. It is called when the method is
61 /// called by a remote application.
62 pub type MethodHandler<'a> = Box<FnMut(&mut Message) -> MethodResult + 'a>;
63 
64 struct IMethod<'a> {
65     in_args: Vec<Argument<'a>>,
66     out_args: Vec<Argument<'a>>,
67     cb: Rc<RefCell<MethodHandler<'a>>>,
68     anns: Vec<Annotation>,
69 }
70 
71 /// a method that can be called from another application
72 pub struct Method<'a> {
73     name: String,
74     i: IMethod<'a>
75 }
76 
77 impl<'a> Method<'a> {
78     /// Create a new Method.
79     #[deprecated(note="please use `tree` module instead")]
new<N: ToString>(name: N, in_args: Vec<Argument<'a>>, out_args: Vec<Argument<'a>>, cb: MethodHandler<'a>) -> Method<'a>80     pub fn new<N: ToString>(name: N, in_args: Vec<Argument<'a>>,
81             out_args: Vec<Argument<'a>>, cb: MethodHandler<'a>) -> Method<'a> {
82         Method { name: name.to_string(), i: IMethod {
83             in_args: in_args, out_args: out_args, cb: Rc::new(RefCell::new(cb)), anns: vec![] }
84         }
85     }
86 
87     /// Add an Annotation to the Method.
annotate<N: ToString, V: ToString>(&mut self, name: N, value: V)88     pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) {
89         self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() });
90     }
91 }
92 
93 /// A read/write property handler.
94 pub trait PropertyRWHandler {
95     /// Get a property's value.
get(&self) -> PropertyGetResult96     fn get(&self) -> PropertyGetResult;
97     /// Set a property's value.
set(&self, &MessageItem) -> PropertySetResult98     fn set(&self, &MessageItem) -> PropertySetResult;
99 }
100 
101 /// A read-only property handler.
102 pub trait PropertyROHandler {
103     /// Get a property's value.
get(&self) -> PropertyGetResult104     fn get(&self) -> PropertyGetResult;
105 }
106 
107 /// A write-only property handler.
108 pub trait PropertyWOHandler {
109     /// Set a property's value.
set(&self, &MessageItem) -> PropertySetResult110     fn set(&self, &MessageItem) -> PropertySetResult;
111 }
112 
113 /// Types of access to a Property.
114 pub enum PropertyAccess<'a> {
115     RO(Box<PropertyROHandler+'a>),
116     RW(Box<PropertyRWHandler+'a>),
117     WO(Box<PropertyWOHandler+'a>),
118 }
119 
120 struct IProperty<'a> {
121     sig: TypeSig<'a>,
122     access: PropertyAccess<'a>,
123     anns: Vec<Annotation>,
124 }
125 
126 /// Properties that a remote application can get/set.
127 pub struct Property<'a> {
128     name: String,
129     i: IProperty<'a>
130 }
131 
132 impl<'a> Property<'a> {
new<N: ToString>(name: N, sig: TypeSig<'a>, a: PropertyAccess<'a>) -> Property<'a>133     fn new<N: ToString>(name: N, sig: TypeSig<'a>, a: PropertyAccess<'a>) -> Property<'a> {
134         Property { name: name.to_string(), i: IProperty { sig: sig, access: a, anns: vec![] } }
135     }
136     /// Creates a new read-only Property
new_ro<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyROHandler+'a>) -> Property<'a>137     pub fn new_ro<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyROHandler+'a>) -> Property<'a> {
138         Property::new(name, sig, PropertyAccess::RO(h))
139     }
140     /// Creates a new read-write Property
new_rw<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyRWHandler+'a>) -> Property<'a>141     pub fn new_rw<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyRWHandler+'a>) -> Property<'a> {
142         Property::new(name, sig, PropertyAccess::RW(h))
143     }
144     /// Creates a new write-only Property
new_wo<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyWOHandler+'a>) -> Property<'a>145     pub fn new_wo<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyWOHandler+'a>) -> Property<'a> {
146         Property::new(name, sig, PropertyAccess::WO(h))
147     }
148     /// Add an annotation to the Property
annotate<N: ToString, V: ToString>(&mut self, name: N, value: V)149     pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) {
150         self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() })
151     }
152 }
153 
154 /// Interfaces can contain Methods, Properties, and Signals.
155 pub struct Interface<'a> {
156     methods: BTreeMap<String, IMethod<'a>>,
157     properties: BTreeMap<String, IProperty<'a>>,
158     signals: BTreeMap<String, ISignal<'a>>,
159 }
160 
161 impl<'a> Interface<'a> {
162     /// Create a new Interface.
163     #[deprecated(note="please use `tree` module instead")]
new(m: Vec<Method<'a>>, p: Vec<Property<'a>>, s: Vec<Signal<'a>>) -> Interface<'a>164     pub fn new(m: Vec<Method<'a>>, p: Vec<Property<'a>>, s: Vec<Signal<'a>>) -> Interface<'a> {
165         Interface {
166            methods: m.into_iter().map(|m| (m.name, m.i)).collect(),
167            properties: p.into_iter().map(|p| (p.name, p.i)).collect(),
168            signals: s.into_iter().map(|s| (s.name, s.i)).collect(),
169         }
170     }
171 }
172 
173 struct IObjectPath<'a> {
174     conn: &'a Connection,
175     path: String,
176     registered: Cell<bool>,
177     interfaces: RefCell<BTreeMap<String, Interface<'a>>>,
178 }
179 
180 /// Represents a D-Bus object path, which can in turn contain Interfaces.
181 pub struct ObjectPath<'a> {
182     // We need extra references for the introspector and property handlers, hence this extra boxing
183     i: Rc<IObjectPath<'a>>,
184 }
185 
186 impl<'a> Drop for ObjectPath<'a> {
drop(&mut self)187     fn drop(&mut self) {
188         let _ = self.i.set_registered(false);
189         self.i.interfaces.borrow_mut().clear(); // This should remove all the other references to i
190     }
191 }
192 
introspect_args(args: &Vec<Argument>, indent: &str, dir: &str) -> String193 fn introspect_args(args: &Vec<Argument>, indent: &str, dir: &str) -> String {
194     args.iter().fold("".to_string(), |aa, az| {
195         format!("{}{}<arg name=\"{}\" type=\"{}\"{}/>\n", aa, indent, az.name, az.sig, dir)
196     })
197 }
198 
introspect_anns(anns: &Vec<Annotation>, indent: &str) -> String199 fn introspect_anns(anns: &Vec<Annotation>, indent: &str) -> String {
200     anns.iter().fold("".to_string(), |aa, az| {
201         format!("{}{}<annotation name=\"{}\" value=\"{}\"/>\n", aa, indent, az.name, az.value)
202     })
203 }
204 
introspect_map<T, C: Fn(&T) -> (String, String)> (h: &BTreeMap<String, T>, name: &str, indent: &str, func: C) -> String205 fn introspect_map<T, C: Fn(&T) -> (String, String)>
206     (h: &BTreeMap<String, T>, name: &str, indent: &str, func: C) -> String {
207 
208     h.iter().fold("".to_string(), |a, (k, v)| {
209         let (params, contents) = func(v);
210         format!("{}{}<{} name=\"{}\"{}{}>\n",
211             a, indent, name, k, params, if contents.len() > 0 {
212                 format!(">\n{}{}</{}", contents, indent, name)
213             }
214             else { format!("/") }
215         )
216     })
217 }
218 
219 impl<'a> IObjectPath<'a> {
220 
set_registered(&self, register: bool) -> Result<(), Error>221     fn set_registered(&self, register: bool) -> Result<(), Error> {
222         if register == self.registered.get() { return Ok(()) };
223         if register {
224             try!(self.conn.register_object_path(&self.path));
225         } else {
226             self.conn.unregister_object_path(&self.path);
227         }
228         self.registered.set(register);
229         Ok(())
230     }
231 
introspect(&self, _: &mut Message) -> MethodResult232     fn introspect(&self, _: &mut Message) -> MethodResult {
233         let ifacestr = introspect_map(&self.interfaces.borrow(), "interface", "  ", |iv|
234             (format!(""), format!("{}{}{}",
235                 introspect_map(&iv.methods, "method", "    ", |m| (format!(""), format!("{}{}{}",
236                     introspect_args(&m.in_args, "      ", " direction=\"in\""),
237                     introspect_args(&m.out_args, "      ", " direction=\"out\""),
238                     introspect_anns(&m.anns, "      ")
239                 ))),
240                 introspect_map(&iv.properties, "property", "    ", |p| (
241                     format!(" type=\"{}\" access=\"{}\"", p.sig, match p.access {
242                         PropertyAccess::RO(_) => "read",
243                         PropertyAccess::RW(_) => "readwrite",
244                         PropertyAccess::WO(_) => "write",
245                     }),
246                     introspect_anns(&p.anns, "      ")
247                 )),
248                 introspect_map(&iv.signals, "signal", "    ", |s| (format!(""), format!("{}{}",
249                     introspect_args(&s.args, "      ", ""),
250                     introspect_anns(&s.anns, "      ")
251                 )))
252             ))
253         );
254         let childstr = self.conn.list_registered_object_paths(&self.path).iter().fold("".to_string(), |na, n|
255             format!(r##"{}  <node name="{}"/>
256 "##, na, n)
257         );
258         let nodestr = format!(r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
259 <node name="{}">
260 {}{}</node>"##, self.path, ifacestr, childstr);
261 
262         Ok(vec!(MessageItem::Str(nodestr)))
263     }
264 
property_get(&self, msg: &mut Message) -> MethodResult265     fn property_get(&self, msg: &mut Message) -> MethodResult {
266         let items = msg.get_items();
267         let iface_name = try!(parse_msg_str(items.get(0)));
268         let prop_name = try!(parse_msg_str(items.get(1)));
269 
270         let is = self.interfaces.borrow();
271         let i = try!(is.get(iface_name).ok_or_else(||
272             ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name))));
273         let p = try!(i.properties.get(prop_name).ok_or_else(||
274             ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name))));
275         let v = try!(match p.access {
276             PropertyAccess::RO(ref cb) => cb.get(),
277             PropertyAccess::RW(ref cb) => cb.get(),
278             PropertyAccess::WO(_) => {
279                 return Err(("org.freedesktop.DBus.Error.Failed", format!("Property {} is write only", prop_name)))
280             }
281         });
282         Ok(vec!(MessageItem::Variant(Box::new(v))))
283     }
284 
property_getall(&self, msg: &mut Message) -> MethodResult285     fn property_getall(&self, msg: &mut Message) -> MethodResult {
286         let items = msg.get_items();
287         let iface_name = try!(parse_msg_str(items.get(0)));
288 
289         let is = self.interfaces.borrow();
290         let i = try!(is.get(iface_name).ok_or_else(||
291             ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name))));
292         let mut result = Vec::new();
293         result.push(try!(MessageItem::from_dict(i.properties.iter().filter_map(|(pname, pv)| {
294             let v = match pv.access {
295                 PropertyAccess::RO(ref cb) => cb.get(),
296                 PropertyAccess::RW(ref cb) => cb.get(),
297                 PropertyAccess::WO(_) => { return None }
298             };
299             Some(v.map(|vv| (pname.clone(),vv)))
300         }))));
301         Ok(result)
302     }
303 
property_set(&self, msg: &mut Message) -> MethodResult304     fn property_set(&self, msg: &mut Message) -> MethodResult {
305         let items = msg.get_items();
306         let iface_name = try!(parse_msg_str(items.get(0)));
307         let prop_name = try!(parse_msg_str(items.get(1)));
308         let value = try!(parse_msg_variant(items.get(2)));
309 
310         let is = self.interfaces.borrow();
311         let i = try!(is.get(iface_name).ok_or_else(||
312             ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name))));
313         let p = try!(i.properties.get(prop_name).ok_or_else(||
314             ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name))));
315         try!(match p.access {
316             PropertyAccess::WO(ref cb) => cb.set(value),
317             PropertyAccess::RW(ref cb) => cb.set(value),
318             PropertyAccess::RO(_) => {
319                 return Err(("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", prop_name)))
320             }
321         });
322         Ok(vec!())
323     }
324 }
325 
parse_msg_str(a: Option<&MessageItem>) -> Result<&str,(&'static str, String)>326 fn parse_msg_str(a: Option<&MessageItem>) -> Result<&str,(&'static str, String)> {
327     let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))));
328     name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))
329 }
330 
parse_msg_variant(a: Option<&MessageItem>) -> Result<&MessageItem,(&'static str, String)>331 fn parse_msg_variant(a: Option<&MessageItem>) -> Result<&MessageItem,(&'static str, String)> {
332     let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))));
333     name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))
334 }
335 
336 impl PropertyROHandler for MessageItem {
get(&self) -> PropertyGetResult337     fn get(&self) -> PropertyGetResult {
338         Ok(self.clone())
339     }
340 }
341 
342 impl<'a> ObjectPath<'a> {
343     /// Create a new ObjectPath.
344     #[deprecated(note="please use `tree` module instead")]
new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a>345     pub fn new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a> {
346         let i = IObjectPath {
347             conn: conn,
348             path: path.to_string(),
349             registered: Cell::new(false),
350             interfaces: RefCell::new(BTreeMap::new()),
351         };
352         let mut o = ObjectPath { i: Rc::new(i) };
353 
354         if introspectable {
355             let o_cl = o.i.clone();
356             let i = Interface::new(vec!(
357                 Method::new("Introspect", vec!(), vec!(Argument::new("xml_data", "s")),
358                     Box::new(move |m| { o_cl.introspect(m) }))), vec!(), vec!());
359             o.insert_interface("org.freedesktop.DBus.Introspectable", i);
360         }
361         o
362     }
363 
add_property_handler(&mut self)364     fn add_property_handler(&mut self) {
365         if self.i.interfaces.borrow().contains_key("org.freedesktop.DBus.Properties") { return };
366         let (cl1, cl2, cl3) = (self.i.clone(), self.i.clone(), self.i.clone());
367         let i = Interface::new(vec!(
368             Method::new("Get",
369                 vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s")),
370                 vec!(Argument::new("value", "v")),
371                 Box::new(move |m| cl1.property_get(m))),
372             Method::new("GetAll",
373                 vec!(Argument::new("interface_name", "s")),
374                 vec!(Argument::new("props", "a{sv}")),
375                 Box::new(move |m| cl2.property_getall(m))),
376             Method::new("Set",
377                 vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s"),
378                     Argument::new("value", "v")),
379                 vec!(),
380                 Box::new(move |m| cl3.property_set(m)))),
381             vec!(), vec!());
382         self.insert_interface("org.freedesktop.DBus.Properties", i);
383     }
384 
385     /// Add an Interface to this ObjectPath.
insert_interface<N: ToString>(&mut self, name: N, i: Interface<'a>)386     pub fn insert_interface<N: ToString>(&mut self, name: N, i: Interface<'a>) {
387         if !i.properties.is_empty() {
388             self.add_property_handler();
389         }
390         self.i.interfaces.borrow_mut().insert(name.to_string(), i);
391     }
392 
393     /// Returns if the ObjectPath is registered.
is_registered(&self) -> bool394     pub fn is_registered(&self) -> bool {
395         self.i.registered.get()
396     }
397 
398     /// Changes the registration status of the ObjectPath.
set_registered(&mut self, register: bool) -> Result<(), Error>399     pub fn set_registered(&mut self, register: bool) -> Result<(), Error> {
400         self.i.set_registered(register)
401     }
402 
403     /// Handles a method call if the object path matches.
404     /// Return value: None => not handled (no match),
405     /// Some(Err(())) => message reply send failed,
406     /// Some(Ok()) => message reply send ok */
handle_message(&mut self, msg: &mut Message) -> Option<Result<(), ()>>407     pub fn handle_message(&mut self, msg: &mut Message) -> Option<Result<(), ()>> {
408         let (_, path, iface, method) = msg.headers();
409         if path.is_none() || path.unwrap() != self.i.path { return None; }
410         if iface.is_none() { return None; }
411 
412         let method = {
413             // This is because we don't want to hold the refcell lock when we call the
414             // callback - maximum flexibility for clients.
415             if let Some(i) = self.i.interfaces.borrow().get(&iface.unwrap()) {
416                 if let Some(Some(m)) = method.map(|m| i.methods.get(&m)) {
417                     m.cb.clone()
418                 } else {
419                     return Some(self.i.conn.send(Message::new_error(
420                         msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method").unwrap()).map(|_| ()));
421                 }
422             } else {
423                 return Some(self.i.conn.send(Message::new_error(msg,
424                     "org.freedesktop.DBus.Error.UnknownInterface", "Unknown interface").unwrap()).map(|_| ()));
425             }
426         };
427 
428         let r = {
429             // Now call it
430             let mut m = method.borrow_mut();
431             (&mut **m)(msg)
432         };
433 
434         let reply = match r {
435             Ok(r) => {
436                 let mut z = Message::new_method_return(msg).unwrap();
437                 z.append_items(&r);
438                 z
439             },
440             Err((aa,bb)) => Message::new_error(msg, aa, &bb).unwrap(),
441         };
442 
443         Some(self.i.conn.send(reply).map(|_| ()))
444     }
445 }
446 
447 #[cfg(test)]
make_objpath<'a>(c: &'a Connection) -> ObjectPath<'a>448 fn make_objpath<'a>(c: &'a Connection) -> ObjectPath<'a> {
449     let mut o = ObjectPath::new(c, "/echo", true);
450     o.insert_interface("com.example.echo", Interface::new(
451         vec!(Method::new("Echo",
452             vec!(Argument::new("request", "s")),
453             vec!(Argument::new("reply", "s")), Box::new(|_| { Err(("dummy", "dummy".to_string())) } ))),
454         vec!(Property::new_ro("EchoCount", MessageItem::Int32(7).type_sig(), Box::new(MessageItem::Int32(7)))),
455         vec!(Signal::new("Echoed", vec!(Argument::new("data", "s"))))));
456     o
457 }
458 
459 #[test]
test_objpath()460 fn test_objpath() {
461     let c = Connection::get_private(super::BusType::Session).unwrap();
462     let mut o = make_objpath(&c);
463     o.set_registered(true).unwrap();
464     let busname = format!("com.example.objpath.test.test_objpath");
465     assert_eq!(c.register_name(&busname, super::NameFlag::ReplaceExisting as u32).unwrap(), super::RequestNameReply::PrimaryOwner);
466 
467     let thread = ::std::thread::spawn(move || {
468         let c = Connection::get_private(super::BusType::Session).unwrap();
469         let pr = super::Props::new(&c, &*busname, "/echo", "com.example.echo", 5000);
470         assert_eq!(pr.get("EchoCount").unwrap(), 7i32.into());
471         let m = pr.get_all().unwrap();
472         assert_eq!(m.get("EchoCount").unwrap(), &7i32.into());
473     });
474 
475     let mut i = 0;
476     for n in c.iter(1000) {
477         println!("objpath msg {:?}", n);
478         if let super::ConnectionItem::MethodCall(mut m) = n {
479             if let Some(msg) = o.handle_message(&mut m) {
480                 msg.unwrap();
481                 i += 1;
482                 if i >= 2 { break };
483             }
484         }
485     }
486 
487     thread.join().unwrap();
488 }
489 
490 
491 /// Currently commented out because it requires feature(alloc)
492 /*
493 #[test]
494 fn test_refcount() {
495     let c = Connection::get_private(super::BusType::Session).unwrap();
496     let i = {
497         let o = make_objpath(&c);
498         o.i.clone()
499     };
500     assert!(::std::rc::is_unique(&i));
501 }
502 */
503 
504 #[test]
test_introspect()505 fn test_introspect() {
506     let c = Connection::get_private(super::BusType::Session).unwrap();
507     let mut o = make_objpath(&c);
508     o.set_registered(true).unwrap();
509     let mut o2 = ObjectPath::new(&c, "/echo/subpath", true);
510     o2.set_registered(true).unwrap();
511     let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Introspectable", "Introspect").unwrap();
512     println!("Introspect result: {}", parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap());
513 
514     let result = r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
515 <node name="/echo">
516   <interface name="com.example.echo">
517     <method name="Echo">
518       <arg name="request" type="s" direction="in"/>
519       <arg name="reply" type="s" direction="out"/>
520     </method>
521     <property name="EchoCount" type="i" access="read"/>
522     <signal name="Echoed">
523       <arg name="data" type="s"/>
524     </signal>
525   </interface>
526   <interface name="org.freedesktop.DBus.Introspectable">
527     <method name="Introspect">
528       <arg name="xml_data" type="s" direction="out"/>
529     </method>
530   </interface>
531   <interface name="org.freedesktop.DBus.Properties">
532     <method name="Get">
533       <arg name="interface_name" type="s" direction="in"/>
534       <arg name="property_name" type="s" direction="in"/>
535       <arg name="value" type="v" direction="out"/>
536     </method>
537     <method name="GetAll">
538       <arg name="interface_name" type="s" direction="in"/>
539       <arg name="props" type="a{sv}" direction="out"/>
540     </method>
541     <method name="Set">
542       <arg name="interface_name" type="s" direction="in"/>
543       <arg name="property_name" type="s" direction="in"/>
544       <arg name="value" type="v" direction="in"/>
545     </method>
546   </interface>
547   <node name="subpath"/>
548 </node>"##;
549 
550     assert_eq!(result, parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap());
551 
552 }
553 
554