1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use super::{Connection, Message, MessageItem, Error};
use std::collections::BTreeMap;

/// Client side properties - get and set properties on a remote application.
pub struct Props<'a> {
    name: String,
    path: String,
    interface: String,
    timeout_ms: i32,
    conn: &'a Connection,
}

impl<'a> Props<'a> {
    pub fn new(conn: &'a Connection, name: &str, path: &str, interface: &str, timeout_ms: i32) -> Props<'a> {
        Props {
            name: name.to_string(),
            path: path.to_string(),
            interface: interface.to_string(),
            timeout_ms: timeout_ms,
            conn: conn,
        }
    }

    pub fn get(&self, propname: &str) -> Result<MessageItem, Error> {
        let mut m = Message::new_method_call(&self.name, &self.path,
            "org.freedesktop.DBus.Properties", "Get").unwrap();
        m.append_items(&[self.interface.clone().into(), propname.to_string().into()]);
        let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
        let reply = try!(r.as_result()).get_items();
        if reply.len() == 1 {
            if let &MessageItem::Variant(ref v) = &reply[0] {
                return Ok((**v).clone())
            }
       }
       let f = format!("Invalid reply for property get {}: '{:?}'", propname, reply);
       return Err(Error::new_custom("InvalidReply", &f));
    }

    pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> {
        let mut m = Message::new_method_call(&self.name, &self.path,
            "org.freedesktop.DBus.Properties", "Set").unwrap();
        m.append_items(&[self.interface.clone().into(), propname.to_string().into(), Box::new(value).into()]);
        let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
        try!(r.as_result());
        Ok(())
    }

    pub fn get_all(&self) -> Result<BTreeMap<String, MessageItem>, Error> {
        let mut m = Message::new_method_call(&self.name, &self.path,
            "org.freedesktop.DBus.Properties", "GetAll").unwrap();
        m.append_items(&[self.interface.clone().into()]);
        let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
        let reply = try!(r.as_result()).get_items();

        (|| {
            if reply.len() != 1 { return Err(()) };
            let mut t = BTreeMap::new();
            let a: &[MessageItem] = try!(reply[0].inner());
            for p in a.iter() {
                let (k, v) = try!(p.inner());
                let (k, v): (&String, &MessageItem) = (try!(k.inner()), try!(v.inner()));
                t.insert(k.clone(), v.clone());
            }
            Ok(t)
        })().map_err(|_| {
            let f = format!("Invalid reply for property GetAll: '{:?}'", reply);
            Error::new_custom("InvalidReply", &f)
        })
    }
}

/// Wrapper around Props that keeps a map of fetched properties.
pub struct PropHandler<'a> {
    p: Props<'a>,
    map: BTreeMap<String, MessageItem>,
}

impl<'a> PropHandler<'a> {
    pub fn new(p: Props) -> PropHandler {
        PropHandler { p: p, map: BTreeMap::new() }
    }

    pub fn get_all(&mut self) -> Result<(), Error> {
        self.map = try!(self.p.get_all());
        Ok(())
    }

    pub fn map_mut(&mut self) -> &mut BTreeMap<String, MessageItem> { &mut self.map }
    pub fn map(&self) -> &BTreeMap<String, MessageItem> { &self.map }

    pub fn get(&mut self, propname: &str) -> Result<&MessageItem, Error> {
        let v = try!(self.p.get(propname));
        self.map.insert(propname.to_string(), v);
        Ok(self.map.get(propname).unwrap())
    }

    pub fn set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error> {
        try!(self.p.set(propname, value.clone()));
        self.map.insert(propname.to_string(), value);
        Ok(())
    }
}


/* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but PolicyKit should be around on most distros. */
#[test]
fn test_get_policykit_version() {
    use super::BusType;
    let c = Connection::get_private(BusType::System).unwrap();
    let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority",
        "org.freedesktop.PolicyKit1.Authority", 10000);

    /* Let's use both the get and getall methods and see if we get the same result */
    let v = p.get("BackendVersion").unwrap();
    let vall = p.get_all().unwrap();
    let v2 = vall.get("BackendVersion").unwrap();

    assert_eq!(&v, &*v2);
    match v {
        MessageItem::Str(ref s) => { println!("Policykit Backend version is {}", s); }
        _ => { panic!("Invalid Get: {:?}", v); }
    };
}