1 use super::{Connection, Message, MessageItem, Error, Path, Interface, BusName};
2 use std::collections::BTreeMap;
3 
4 /// Client side properties - get and set properties on a remote application.
5 pub struct Props<'a> {
6     name: BusName<'a>,
7     path: Path<'a>,
8     interface: Interface<'a>,
9     timeout_ms: i32,
10     conn: &'a Connection,
11 }
12 
13 impl<'a> Props<'a> {
14     /// Create a new Props.
new<N, P, I>(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a> where N: Into<BusName<'a>>, P: Into<Path<'a>>, I: Into<Interface<'a>>15     pub fn new<N, P, I>(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a>
16     where N: Into<BusName<'a>>, P: Into<Path<'a>>, I: Into<Interface<'a>> {
17         Props {
18             name: name.into(),
19             path: path.into(),
20             interface: interface.into(),
21             timeout_ms: timeout_ms,
22             conn: conn,
23         }
24     }
25 
26     /// Get a single property's value.
get(&self, propname: &str) -> Result<MessageItem, Error>27     pub fn get(&self, propname: &str) -> Result<MessageItem, Error> {
28         let mut m = Message::method_call(&self.name, &self.path,
29             &"org.freedesktop.DBus.Properties".into(), &"Get".into());
30         m.append_items(&[self.interface.to_string().into(), propname.to_string().into()]);
31         let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
32         let reply = try!(r.as_result()).get_items();
33         if reply.len() == 1 {
34             if let &MessageItem::Variant(ref v) = &reply[0] {
35                 return Ok((**v).clone())
36             }
37        }
38        let f = format!("Invalid reply for property get {}: '{:?}'", propname, reply);
39        return Err(Error::new_custom("InvalidReply", &f));
40     }
41 
42     /// Set a single property's value.
set(&self, propname: &str, value: MessageItem) -> Result<(), Error>43     pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> {
44         let mut m = Message::method_call(&self.name, &self.path,
45             &"org.freedesktop.DBus.Properties".into(), &"Set".into());
46         m.append_items(&[self.interface.to_string().into(), propname.to_string().into(), Box::new(value).into()]);
47         let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
48         try!(r.as_result());
49         Ok(())
50     }
51 
52     /// Get a map of all the properties' names and their values.
get_all(&self) -> Result<BTreeMap<String, MessageItem>, Error>53     pub fn get_all(&self) -> Result<BTreeMap<String, MessageItem>, Error> {
54         let mut m = Message::method_call(&self.name, &self.path,
55             &"org.freedesktop.DBus.Properties".into(), &"GetAll".into());
56         m.append_items(&[self.interface.to_string().into()]);
57         let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
58         let reply = try!(r.as_result()).get_items();
59 
60         (|| {
61             if reply.len() != 1 { return Err(()) };
62             let mut t = BTreeMap::new();
63             let a: &[MessageItem] = try!(reply[0].inner());
64             for p in a.iter() {
65                 let (k, v) = try!(p.inner());
66                 let (k, v): (&String, &MessageItem) = (try!(k.inner()), try!(v.inner()));
67                 t.insert(k.clone(), v.clone());
68             }
69             Ok(t)
70         })().map_err(|_| {
71             let f = format!("Invalid reply for property GetAll: '{:?}'", reply);
72             Error::new_custom("InvalidReply", &f)
73         })
74     }
75 }
76 
77 /// Wrapper around Props that keeps a map of fetched properties.
78 pub struct PropHandler<'a> {
79     p: Props<'a>,
80     map: BTreeMap<String, MessageItem>,
81 }
82 
83 impl<'a> PropHandler<'a> {
84     /// Create a new PropHandler from a Props.
new(p: Props) -> PropHandler85     pub fn new(p: Props) -> PropHandler {
86         PropHandler { p: p, map: BTreeMap::new() }
87     }
88 
89     /// Get a map of all the properties' names and their values.
get_all(&mut self) -> Result<(), Error>90     pub fn get_all(&mut self) -> Result<(), Error> {
91         self.map = try!(self.p.get_all());
92         Ok(())
93     }
94 
95     /// Get a mutable reference to the PropHandler's fetched properties.
map_mut(&mut self) -> &mut BTreeMap<String, MessageItem>96     pub fn map_mut(&mut self) -> &mut BTreeMap<String, MessageItem> { &mut self.map }
97 
98     /// Get a reference to the PropHandler's fetched properties.
map(&self) -> &BTreeMap<String, MessageItem>99     pub fn map(&self) -> &BTreeMap<String, MessageItem> { &self.map }
100 
101     /// Get a single property's value.
get(&mut self, propname: &str) -> Result<&MessageItem, Error>102     pub fn get(&mut self, propname: &str) -> Result<&MessageItem, Error> {
103         let v = try!(self.p.get(propname));
104         self.map.insert(propname.to_string(), v);
105         Ok(self.map.get(propname).unwrap())
106     }
107 
108     /// Set a single property's value.
set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error>109     pub fn set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error> {
110         try!(self.p.set(propname, value.clone()));
111         self.map.insert(propname.to_string(), value);
112         Ok(())
113     }
114 }
115 
116 
117 /* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but PolicyKit should be around on most distros. */
118 #[test]
test_get_policykit_version()119 fn test_get_policykit_version() {
120     use super::BusType;
121     let c = Connection::get_private(BusType::System).unwrap();
122     let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority",
123         "org.freedesktop.PolicyKit1.Authority", 10000);
124 
125     /* Let's use both the get and getall methods and see if we get the same result */
126     let v = p.get("BackendVersion").unwrap();
127     let vall = p.get_all().unwrap();
128     let v2 = vall.get("BackendVersion").unwrap();
129 
130     assert_eq!(&v, &*v2);
131     match v {
132         MessageItem::Str(ref s) => { println!("Policykit Backend version is {}", s); }
133         _ => { panic!("Invalid Get: {:?}", v); }
134     };
135 }
136 
137