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
125
126
127
128
129
130
131
132
133
134
135
136
use super::{Connection, Message, MessageItem, Error, Path, Interface, BusName};
use std::collections::BTreeMap;

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

impl<'a> Props<'a> {
    /// Create a new Props.
    pub fn 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>> {
        Props {
            name: name.into(),
            path: path.into(),
            interface: interface.into(),
            timeout_ms: timeout_ms,
            conn: conn,
        }
    }

    /// Get a single property's value.
    pub fn get(&self, propname: &str) -> Result<MessageItem, Error> {
        let mut m = Message::method_call(&self.name, &self.path,
            &"org.freedesktop.DBus.Properties".into(), &"Get".into());
        m.append_items(&[self.interface.to_string().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));
    }

    /// Set a single property's value.
    pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> {
        let mut m = Message::method_call(&self.name, &self.path,
            &"org.freedesktop.DBus.Properties".into(), &"Set".into());
        m.append_items(&[self.interface.to_string().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(())
    }

    /// Get a map of all the properties' names and their values.
    pub fn get_all(&self) -> Result<BTreeMap<String, MessageItem>, Error> {
        let mut m = Message::method_call(&self.name, &self.path,
            &"org.freedesktop.DBus.Properties".into(), &"GetAll".into());
        m.append_items(&[self.interface.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 { 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> {
    /// Create a new PropHandler from a Props.
    pub fn new(p: Props) -> PropHandler {
        PropHandler { p: p, map: BTreeMap::new() }
    }

    /// Get a map of all the properties' names and their values.
    pub fn get_all(&mut self) -> Result<(), Error> {
        self.map = try!(self.p.get_all());
        Ok(())
    }

    /// Get a mutable reference to the PropHandler's fetched properties.
    pub fn map_mut(&mut self) -> &mut BTreeMap<String, MessageItem> { &mut self.map }

    /// Get a reference to the PropHandler's fetched properties.
    pub fn map(&self) -> &BTreeMap<String, MessageItem> { &self.map }

    /// Get a single property's value.
    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())
    }

    /// Set a single property's value.
    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); }
    };
}