diff --git a/src/lib.rs b/src/lib.rs
index 7be94346..79b7d1da 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,8 @@ pub mod ns;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod message;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
+pub mod presence;
+/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod body;
/// XEP-0004: Data Forms
diff --git a/src/presence.rs b/src/presence.rs
new file mode 100644
index 00000000..51e5854e
--- /dev/null
+++ b/src/presence.rs
@@ -0,0 +1,179 @@
+use std::str::FromStr;
+
+use minidom::Element;
+use minidom::IntoAttributeValue;
+
+use jid::Jid;
+
+use error::Error;
+
+use ns;
+
+use delay;
+
+/// Lists every known payload of a ``.
+#[derive(Debug, Clone)]
+pub enum PresencePayload {
+ Delay(delay::Delay),
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum PresenceType {
+ /// This value is not an acceptable 'type' attribute, it is only used
+ /// internally to signal the absence of 'type'.
+ Available,
+ Error,
+ Probe,
+ Subscribe,
+ Subscribed,
+ Unavailable,
+ Unsubscribe,
+ Unsubscribed,
+}
+
+impl Default for PresenceType {
+ fn default() -> PresenceType {
+ PresenceType::Available
+ }
+}
+
+impl FromStr for PresenceType {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result {
+ Ok(match s {
+ "error" => PresenceType::Error,
+ "probe" => PresenceType::Probe,
+ "subscribe" => PresenceType::Subscribe,
+ "subscribed" => PresenceType::Subscribed,
+ "unavailable" => PresenceType::Unavailable,
+ "unsubscribe" => PresenceType::Unsubscribe,
+ "unsubscribed" => PresenceType::Unsubscribed,
+
+ _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")),
+ })
+ }
+}
+
+impl IntoAttributeValue for PresenceType {
+ fn into_attribute_value(self) -> Option {
+ Some(match self {
+ PresenceType::Available => return None,
+
+ PresenceType::Error => "error",
+ PresenceType::Probe => "probe",
+ PresenceType::Subscribe => "subscribe",
+ PresenceType::Subscribed => "subscribed",
+ PresenceType::Unavailable => "unavailable",
+ PresenceType::Unsubscribe => "unsubscribe",
+ PresenceType::Unsubscribed => "unsubscribed",
+ }.to_owned())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum PresencePayloadType {
+ XML(Element),
+ Parsed(PresencePayload),
+}
+
+#[derive(Debug, Clone)]
+pub struct Presence {
+ pub from: Option,
+ pub to: Option,
+ pub id: Option,
+ pub type_: PresenceType,
+ pub payloads: Vec,
+}
+
+pub fn parse_presence(root: &Element) -> Result {
+ if !root.is("presence", ns::JABBER_CLIENT) {
+ return Err(Error::ParseError("This is not a presence element."));
+ }
+ let from = root.attr("from")
+ .and_then(|value| value.parse().ok());
+ let to = root.attr("to")
+ .and_then(|value| value.parse().ok());
+ let id = root.attr("id")
+ .and_then(|value| value.parse().ok());
+ let type_ = match root.attr("type") {
+ Some(type_) => type_.parse()?,
+ None => Default::default(),
+ };
+ let mut payloads = vec!();
+ for elem in root.children() {
+ let payload = if let Ok(delay) = delay::parse_delay(elem) {
+ Some(PresencePayload::Delay(delay))
+ } else {
+ None
+ };
+ payloads.push(match payload {
+ Some(payload) => PresencePayloadType::Parsed(payload),
+ None => PresencePayloadType::XML(elem.clone()),
+ });
+ }
+ Ok(Presence {
+ from: from,
+ to: to,
+ id: id,
+ type_: type_,
+ payloads: payloads,
+ })
+}
+
+pub fn serialise_payload(payload: &PresencePayload) -> Element {
+ match *payload {
+ PresencePayload::Delay(ref delay) => delay::serialise(delay),
+ }
+}
+
+pub fn serialise(presence: &Presence) -> Element {
+ let mut stanza = Element::builder("presence")
+ .ns(ns::JABBER_CLIENT)
+ .attr("from", presence.from.clone().and_then(|value| Some(String::from(value))))
+ .attr("to", presence.to.clone().and_then(|value| Some(String::from(value))))
+ .attr("id", presence.id.clone())
+ .attr("type", presence.type_.clone())
+ .build();
+ for child in presence.payloads.clone() {
+ let elem = match child {
+ PresencePayloadType::XML(elem) => elem,
+ PresencePayloadType::Parsed(payload) => serialise_payload(&payload),
+ };
+ stanza.append_child(elem);
+ }
+ stanza
+}
+
+#[cfg(test)]
+mod tests {
+ use minidom::Element;
+ use presence;
+
+ #[test]
+ fn test_simple() {
+ let elem: Element = "".parse().unwrap();
+ let presence = presence::parse_presence(&elem).unwrap();
+ assert_eq!(presence.from, None);
+ assert_eq!(presence.to, None);
+ assert_eq!(presence.id, None);
+ assert_eq!(presence.type_, presence::PresenceType::Available);
+ assert!(presence.payloads.is_empty());
+ }
+
+ #[test]
+ fn test_serialise() {
+ let elem: Element = "".parse().unwrap();
+ let presence = presence::parse_presence(&elem).unwrap();
+ let presence2 = presence::Presence {
+ from: None,
+ to: None,
+ id: None,
+ type_: presence::PresenceType::Unavailable,
+ payloads: vec!(),
+ };
+ let elem2 = presence::serialise(&presence2);
+ assert_eq!(elem, elem2);
+ println!("{:#?}", presence);
+ }
+}