Detailed changes
@@ -1,7 +1,7 @@
[package]
name = "xmpp"
version = "0.2.0"
-authors = ["lumi <lumi@pew.im>"]
+authors = ["lumi <lumi@pew.im>", "Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>"]
description = "A type-safe rust XMPP library. Still under development."
homepage = "https://gitlab.com/lumi/xmpp-rs"
repository = "https://gitlab.com/lumi/xmpp-rs"
@@ -20,7 +20,7 @@ xmpp-parsers = "0.3.0"
openssl = "0.9.12"
base64 = "0.5.2"
minidom = "0.4.1"
-jid = "0.2.0"
+jid = "0.2.1"
sasl = "0.4.0"
sha-1 = "0.3.3"
@@ -6,6 +6,7 @@ use xmpp::plugins::stanza::StanzaPlugin;
use xmpp::plugins::unhandled_iq::UnhandledIqPlugin;
use xmpp::plugins::messaging::{MessagingPlugin, MessageEvent};
use xmpp::plugins::presence::{PresencePlugin, Type};
+use xmpp::plugins::ibb::IbbPlugin;
use xmpp::plugins::ping::{PingPlugin, PingEvent};
use xmpp::event::{Priority, Propagation};
@@ -22,6 +23,7 @@ fn main() {
client.register_plugin(UnhandledIqPlugin::new());
client.register_plugin(MessagingPlugin::new());
client.register_plugin(PresencePlugin::new());
+ client.register_plugin(IbbPlugin::new());
client.register_plugin(PingPlugin::new());
client.register_handler(Priority::Max, |e: &MessageEvent| {
println!("{:?}", e);
@@ -1,3 +1 @@
-pub mod stanza_error;
pub mod sasl_error;
-//pub mod stream_error;
@@ -1,172 +0,0 @@
-use ns;
-use minidom::Element;
-use util::{FromElement, FromParentElement};
-use std::str::FromStr;
-
-#[derive(Copy, Clone, Debug)]
-pub enum ErrorType {
- Auth,
- Cancel,
- Continue,
- Modify,
- Wait,
-}
-
-impl FromStr for ErrorType {
- type Err = ();
-
- fn from_str(s: &str) -> Result<ErrorType, ()> {
- Ok(match s {
- "auth" => ErrorType::Auth,
- "cancel" => ErrorType::Cancel,
- "continue" => ErrorType::Continue,
- "modify" => ErrorType::Modify,
- "wait" => ErrorType::Wait,
- _ => { return Err(()); },
- })
- }
-}
-
-#[derive(Clone, Debug)]
-pub enum Condition {
- BadRequest,
- Conflict,
- FeatureNotImplemented,
- Forbidden,
- Gone(Option<String>),
- InternalServerError,
- ItemNotFound,
- JidMalformed,
- NotAcceptable,
- NotAllowed,
- NotAuthorized,
- PolicyViolation,
- RecipientUnavailable,
- Redirect(Option<String>),
- RegistrationRequired,
- RemoteServerNotFound,
- RemoteServerTimeout,
- ResourceConstraint,
- ServiceUnavailable,
- SubscriptionRequired,
- UndefinedCondition,
- UnexpectedRequest,
-}
-
-impl FromParentElement for Condition {
- type Err = ();
-
- fn from_parent_element(elem: &Element) -> Result<Condition, ()> {
- if elem.has_child("bad-request", ns::STANZAS) {
- Ok(Condition::BadRequest)
- }
- else if elem.has_child("conflict", ns::STANZAS) {
- Ok(Condition::Conflict)
- }
- else if elem.has_child("feature-not-implemented", ns::STANZAS) {
- Ok(Condition::FeatureNotImplemented)
- }
- else if elem.has_child("forbidden", ns::STANZAS) {
- Ok(Condition::Forbidden)
- }
- else if let Some(alt) = elem.get_child("gone", ns::STANZAS) {
- let text = alt.text();
- let inner = if text == "" { None } else { Some(text) };
- Ok(Condition::Gone(inner))
- }
- else if elem.has_child("internal-server-error", ns::STANZAS) {
- Ok(Condition::InternalServerError)
- }
- else if elem.has_child("item-not-found", ns::STANZAS) {
- Ok(Condition::ItemNotFound)
- }
- else if elem.has_child("jid-malformed", ns::STANZAS) {
- Ok(Condition::JidMalformed)
- }
- else if elem.has_child("not-acceptable", ns::STANZAS) {
- Ok(Condition::NotAcceptable)
- }
- else if elem.has_child("not-allowed", ns::STANZAS) {
- Ok(Condition::NotAllowed)
- }
- else if elem.has_child("not-authorized", ns::STANZAS) {
- Ok(Condition::NotAuthorized)
- }
- else if elem.has_child("policy-violation", ns::STANZAS) {
- Ok(Condition::PolicyViolation)
- }
- else if elem.has_child("recipient-unavailable", ns::STANZAS) {
- Ok(Condition::RecipientUnavailable)
- }
- else if let Some(alt) = elem.get_child("redirect", ns::STANZAS) {
- let text = alt.text();
- let inner = if text == "" { None } else { Some(text) };
- Ok(Condition::Redirect(inner))
- }
- else if elem.has_child("registration-required", ns::STANZAS) {
- Ok(Condition::RegistrationRequired)
- }
- else if elem.has_child("remote-server-not-found", ns::STANZAS) {
- Ok(Condition::RemoteServerNotFound)
- }
- else if elem.has_child("remote-server-timeout", ns::STANZAS) {
- Ok(Condition::RemoteServerTimeout)
- }
- else if elem.has_child("resource-constraint", ns::STANZAS) {
- Ok(Condition::ResourceConstraint)
- }
- else if elem.has_child("service-unavailable", ns::STANZAS) {
- Ok(Condition::ServiceUnavailable)
- }
- else if elem.has_child("subscription-required", ns::STANZAS) {
- Ok(Condition::SubscriptionRequired)
- }
- else if elem.has_child("undefined-condition", ns::STANZAS) {
- Ok(Condition::UndefinedCondition)
- }
- else if elem.has_child("unexpected-request", ns::STANZAS) {
- Ok(Condition::UnexpectedRequest)
- }
- else {
- Err(())
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct StanzaError {
- error_type: ErrorType,
- text: Option<String>,
- condition: Condition,
-}
-
-impl StanzaError {
- pub fn new(error_type: ErrorType, text: Option<String>, condition: Condition) -> StanzaError {
- StanzaError {
- error_type: error_type,
- text: text,
- condition: condition,
- }
- }
-}
-
-impl FromElement for StanzaError {
- type Err = ();
-
- fn from_element(elem: &Element) -> Result<StanzaError, ()> {
- if elem.is("error", ns::STANZAS) {
- let error_type = elem.attr("type").ok_or(())?;
- let err: ErrorType = error_type.parse().map_err(|_| ())?;
- let condition: Condition = Condition::from_parent_element(elem)?;
- let text = elem.get_child("text", ns::STANZAS).map(|c| c.text());
- Ok(StanzaError {
- error_type: err,
- text: text,
- condition: condition,
- })
- }
- else {
- Err(())
- }
- }
-}
@@ -0,0 +1,175 @@
+use std::collections::{HashMap, BTreeMap};
+use std::collections::hash_map::Entry;
+use std::convert::TryFrom;
+use std::sync::{Mutex, Arc};
+
+use plugin::PluginProxy;
+use event::{Event, Priority, Propagation};
+use jid::Jid;
+
+use plugins::stanza::Iq;
+use xmpp_parsers::iq::{IqType, IqPayload};
+use xmpp_parsers::ibb::{IBB, Stanza};
+use xmpp_parsers::stanza_error::{StanzaError, ErrorType, DefinedCondition};
+
+#[derive(Debug, Clone)]
+pub struct Session {
+ stanza: Stanza,
+ block_size: u16,
+ cur_seq: u16,
+}
+
+#[derive(Debug)]
+pub struct IbbOpen {
+ pub session: Session,
+}
+
+#[derive(Debug)]
+pub struct IbbData {
+ pub session: Session,
+ pub data: Vec<u8>,
+}
+
+#[derive(Debug)]
+pub struct IbbClose {
+ pub session: Session,
+}
+
+impl Event for IbbOpen {}
+impl Event for IbbData {}
+impl Event for IbbClose {}
+
+fn generate_error(type_: ErrorType, defined_condition: DefinedCondition, text: &str) -> StanzaError {
+ StanzaError {
+ type_: type_,
+ defined_condition: defined_condition,
+ texts: {
+ let mut texts = BTreeMap::new();
+ texts.insert(String::new(), String::from(text));
+ texts
+ },
+ by: None,
+ other: None,
+ }
+}
+
+pub struct IbbPlugin {
+ proxy: PluginProxy,
+ sessions: Arc<Mutex<HashMap<(Jid, String), Session>>>,
+}
+
+impl IbbPlugin {
+ pub fn new() -> IbbPlugin {
+ IbbPlugin {
+ proxy: PluginProxy::new(),
+ sessions: Arc::new(Mutex::new(HashMap::new())),
+ }
+ }
+
+ fn handle_ibb(&self, from: Jid, ibb: IBB) -> Result<(), StanzaError> {
+ let mut sessions = self.sessions.lock().unwrap();
+ match ibb {
+ IBB::Open { block_size, sid, stanza } => {
+ match sessions.entry((from.clone(), sid.clone())) {
+ Entry::Vacant(_) => Ok(()),
+ Entry::Occupied(_) => Err(generate_error(
+ ErrorType::Cancel,
+ DefinedCondition::NotAcceptable,
+ "This session is already open."
+ )),
+ }?;
+ let session = Session {
+ stanza,
+ block_size,
+ cur_seq: 65535u16,
+ };
+ sessions.insert((from, sid), session.clone());
+ self.proxy.dispatch(IbbOpen {
+ session: session,
+ });
+ },
+ IBB::Data { seq, sid, data } => {
+ let entry = match sessions.entry((from, sid)) {
+ Entry::Occupied(entry) => Ok(entry),
+ Entry::Vacant(_) => Err(generate_error(
+ ErrorType::Cancel,
+ DefinedCondition::ItemNotFound,
+ "This session doesnβt exist."
+ )),
+ }?;
+ let mut session = entry.into_mut();
+ if session.stanza != Stanza::Iq {
+ return Err(generate_error(
+ ErrorType::Cancel,
+ DefinedCondition::NotAcceptable,
+ "Wrong stanza type."
+ ))
+ }
+ let cur_seq = session.cur_seq.wrapping_add(1);
+ if seq != cur_seq {
+ return Err(generate_error(
+ ErrorType::Cancel,
+ DefinedCondition::NotAcceptable,
+ "Wrong seq number."
+ ))
+ }
+ session.cur_seq = cur_seq;
+ self.proxy.dispatch(IbbData {
+ session: session.clone(),
+ data,
+ });
+ },
+ IBB::Close { sid } => {
+ let entry = match sessions.entry((from, sid)) {
+ Entry::Occupied(entry) => Ok(entry),
+ Entry::Vacant(_) => Err(generate_error(
+ ErrorType::Cancel,
+ DefinedCondition::ItemNotFound,
+ "This session doesnβt exist."
+ )),
+ }?;
+ let session = entry.remove();
+ self.proxy.dispatch(IbbClose {
+ session,
+ });
+ },
+ }
+ Ok(())
+ }
+
+ fn handle_iq(&self, iq: &Iq) -> Propagation {
+ let iq = iq.clone();
+ if let IqType::Set(payload) = iq.payload {
+ let from = iq.from.unwrap();
+ let id = iq.id.unwrap();
+ // TODO: use an intermediate plugin to parse this payload.
+ let payload = match IqPayload::try_from(payload) {
+ Ok(IqPayload::IBB(ibb)) => {
+ match self.handle_ibb(from.clone(), ibb) {
+ Ok(_) => IqType::Result(None),
+ Err(error) => IqType::Error(error),
+ }
+ },
+ Err(err) => IqType::Error(generate_error(
+ ErrorType::Cancel,
+ DefinedCondition::NotAcceptable,
+ format!("{:?}", err).as_ref()
+ )),
+ Ok(_) => return Propagation::Continue,
+ };
+ self.proxy.send(Iq {
+ from: None,
+ to: Some(from),
+ id: Some(id),
+ payload: payload,
+ }.into());
+ Propagation::Stop
+ } else {
+ Propagation::Continue
+ }
+ }
+}
+
+impl_plugin!(IbbPlugin, proxy, [
+ (Iq, Priority::Default) => handle_iq,
+]);
@@ -1,6 +1,7 @@
pub mod messaging;
pub mod presence;
pub mod ping;
+pub mod ibb;
pub mod stanza;
pub mod stanza_debug;
pub mod unhandled_iq;