mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
Compare commits
7 Commits
321f458263
...
bf7d794bd2
Author | SHA1 | Date |
---|---|---|
Paul Fariello | bf7d794bd2 | |
Emmanuel Gil Peyrot | f725994fe0 | |
Emmanuel Gil Peyrot | 48f77acb69 | |
Jonas Schäfer | fcadccfbab | |
Astro | 6c3081d656 | |
Astro | 598ffdbecf | |
Paul Fariello | ce493e82d2 |
|
@ -32,4 +32,6 @@ serde_test = "1"
|
|||
jid = { path = ".", features = [ "serde" ] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
quote = ["dep:quote", "dep:proc-macro2"]
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
|
||||
/// An error that signifies that a `Jid` cannot be parsed from a string.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -49,6 +50,7 @@ pub enum Error {
|
|||
ResourceInBareJid,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl StdError for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#![no_std]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
//! Represents XMPP addresses, also known as JabberIDs (JIDs) for the [XMPP](https://xmpp.org/)
|
||||
|
@ -30,13 +31,22 @@
|
|||
//! - stringprep error: some characters were invalid according to the stringprep algorithm, such as
|
||||
//! mixing left-to-write and right-to-left characters
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate std;
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::mem;
|
||||
use core::num::NonZeroU16;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use core::ops::Deref;
|
||||
use core::str::FromStr;
|
||||
|
||||
use memchr::memchr;
|
||||
|
||||
|
@ -364,13 +374,13 @@ impl Jid {
|
|||
Ok(unsafe {
|
||||
// SAFETY: FullJid is #[repr(transparent)] of Jid
|
||||
// SOUNDNESS: we asserted that self.slash is set above
|
||||
std::mem::transmute::<&Jid, &FullJid>(self)
|
||||
mem::transmute::<&Jid, &FullJid>(self)
|
||||
})
|
||||
} else {
|
||||
Err(unsafe {
|
||||
// SAFETY: BareJid is #[repr(transparent)] of Jid
|
||||
// SOUNDNESS: we asserted that self.slash is unset above
|
||||
std::mem::transmute::<&Jid, &BareJid>(self)
|
||||
mem::transmute::<&Jid, &BareJid>(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -383,13 +393,13 @@ impl Jid {
|
|||
Ok(unsafe {
|
||||
// SAFETY: FullJid is #[repr(transparent)] of Jid
|
||||
// SOUNDNESS: we asserted that self.slash is set above
|
||||
std::mem::transmute::<&mut Jid, &mut FullJid>(self)
|
||||
mem::transmute::<&mut Jid, &mut FullJid>(self)
|
||||
})
|
||||
} else {
|
||||
Err(unsafe {
|
||||
// SAFETY: BareJid is #[repr(transparent)] of Jid
|
||||
// SOUNDNESS: we asserted that self.slash is unset above
|
||||
std::mem::transmute::<&mut Jid, &mut BareJid>(self)
|
||||
mem::transmute::<&mut Jid, &mut BareJid>(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -865,10 +875,11 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::vec::Vec;
|
||||
|
||||
macro_rules! assert_size (
|
||||
($t:ty, $sz:expr) => (
|
||||
assert_eq!(::std::mem::size_of::<$t>(), $sz);
|
||||
assert_eq!(::core::mem::size_of::<$t>(), $sz);
|
||||
);
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use std::borrow::{Borrow, Cow};
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use alloc::borrow::{Cow, ToOwned};
|
||||
use alloc::string::{String, ToString};
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt;
|
||||
use core::mem;
|
||||
use core::ops::Deref;
|
||||
use core::str::FromStr;
|
||||
|
||||
use stringprep::{nameprep, nodeprep, resourceprep};
|
||||
|
||||
|
@ -162,7 +165,7 @@ macro_rules! def_part_types {
|
|||
pub(crate) fn from_str_unchecked(s: &str) -> &Self {
|
||||
// SAFETY: repr(transparent) thing can be transmuted to/from
|
||||
// its inner.
|
||||
unsafe { std::mem::transmute(s) }
|
||||
unsafe { mem::transmute(s) }
|
||||
}
|
||||
|
||||
/// Access the contents as [`str`] slice.
|
||||
|
|
|
@ -14,7 +14,7 @@ license = "MPL-2.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.21"
|
||||
base64 = "0.22"
|
||||
digest = "0.10"
|
||||
sha1 = "0.10"
|
||||
sha2 = "0.10"
|
||||
|
|
|
@ -87,6 +87,9 @@ pub mod rsm;
|
|||
/// XEP-0060: Publish-Subscribe
|
||||
pub mod pubsub;
|
||||
|
||||
/// XEP-0066: OOB
|
||||
pub mod oob;
|
||||
|
||||
/// XEP-0071: XHTML-IM
|
||||
pub mod xhtml;
|
||||
|
||||
|
|
|
@ -63,6 +63,9 @@ pub const PUBSUB_OWNER: &str = "http://jabber.org/protocol/pubsub#owner";
|
|||
/// XEP-0060: Publish-Subscribe node configuration
|
||||
pub const PUBSUB_CONFIGURE: &str = "http://jabber.org/protocol/pubsub#node_config";
|
||||
|
||||
/// XEP-0066: Out of Band Data
|
||||
pub const OOB: &str = "jabber:x:oob";
|
||||
|
||||
/// XEP-0071: XHTML-IM
|
||||
pub const XHTML_IM: &str = "http://jabber.org/protocol/xhtml-im";
|
||||
/// XEP-0071: XHTML-IM
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) 2024 Paul Fariello <xmpp-parsers@fariello.eu>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::message::MessagePayload;
|
||||
|
||||
generate_element!(
|
||||
/// Defines associated out of band url.
|
||||
Oob, "x", OOB,
|
||||
children: [
|
||||
/// The associated URL.
|
||||
url: Required<String> = ("url", OOB) => String,
|
||||
/// An optionnal description of the out of band data.
|
||||
desc: Option<String> = ("desc", OOB) => String,
|
||||
]
|
||||
);
|
||||
|
||||
impl MessagePayload for Oob {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::util::error::Error;
|
||||
use crate::Element;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert_size!(Oob, 24);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert_size!(Oob, 48);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<x xmlns='jabber:x:oob'><url>http://example.org</url></x>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Oob::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_desc() {
|
||||
let elem: Element =
|
||||
"<x xmlns='jabber:x:oob'><url>http://example.org</url><desc>Example website</desc></x>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Oob::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<x xmlns='jabber:x:oob'></x>".parse().unwrap();
|
||||
let error = Oob::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Missing child url in x element.");
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
//! see [`vcard_update`][crate::vcard_update] module.
|
||||
|
||||
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||
use crate::util::text_node_codecs::{Base64, Codec, Text};
|
||||
use crate::util::text_node_codecs::{Codec, Text, WhitespaceAwareBase64};
|
||||
use crate::{ns, Error};
|
||||
use minidom::Element;
|
||||
|
||||
|
@ -44,7 +44,7 @@ generate_element!(
|
|||
Binval, "BINVAL", VCARD,
|
||||
text: (
|
||||
/// The actual data.
|
||||
data: Base64
|
||||
data: WhitespaceAwareBase64
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -131,4 +131,34 @@ mod tests {
|
|||
assert_eq!(photo.type_.data, "image/jpeg".to_string());
|
||||
assert_eq!(photo.binval.data, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vcard_with_linebreaks() {
|
||||
// Test xml stolen from https://xmpp.org/extensions/xep-0153.html#example-5
|
||||
// extended to use a multi-line base64 string as is allowed as per RFC 2426
|
||||
let test_vcard = r"<vCard xmlns='vcard-temp'>
|
||||
<BDAY>1476-06-09</BDAY>
|
||||
<ADR>
|
||||
<CTRY>Italy</CTRY>
|
||||
<LOCALITY>Verona</LOCALITY>
|
||||
<HOME/>
|
||||
</ADR>
|
||||
<NICKNAME/>
|
||||
<N><GIVEN>Juliet</GIVEN><FAMILY>Capulet</FAMILY></N>
|
||||
<EMAIL>jcapulet@shakespeare.lit</EMAIL>
|
||||
<PHOTO>
|
||||
<TYPE>image/jpeg</TYPE>
|
||||
<BINVAL>Zm9v
|
||||
Cg==</BINVAL>
|
||||
</PHOTO>
|
||||
</vCard>";
|
||||
|
||||
let test_vcard = Element::from_str(&test_vcard).expect("Failed to parse XML");
|
||||
let test_vcard = VCard::try_from(test_vcard).expect("Failed to parse vCard");
|
||||
|
||||
let photo = test_vcard.photo.expect("No photo found");
|
||||
|
||||
assert_eq!(photo.type_.data, "image/jpeg".to_string());
|
||||
assert_eq!(photo.binval.data, b"foo\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ scram = ["base64", "getrandom", "sha-1", "sha2", "hmac", "pbkdf2"]
|
|||
anonymous = ["getrandom"]
|
||||
|
||||
[dependencies]
|
||||
base64 = { version = "0.21", optional = true }
|
||||
base64 = { version = "0.22", optional = true }
|
||||
getrandom = { version = "0.2", optional = true }
|
||||
sha-1 = { version = "0.10", optional = true }
|
||||
sha2 = { version = "0.10", optional = true }
|
||||
|
|
|
@ -29,13 +29,13 @@ xmpp-parsers = { version = "0.20", path = "../parsers" }
|
|||
|
||||
# these are only needed for starttls ServerConnector support
|
||||
hickory-resolver = { version = "0.24", optional = true}
|
||||
idna = { version = "0.4", optional = true}
|
||||
idna = { version = "0.5", optional = true}
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
tokio-native-tls = { version = "0.3", optional = true }
|
||||
tokio-rustls = { version = "0.24", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.10", default-features = false, features = ["auto-color", "humantime"] }
|
||||
env_logger = { version = "0.11", default-features = false, features = ["auto-color", "humantime"] }
|
||||
# this is needed for echo-component example
|
||||
tokio-xmpp = { path = ".", features = ["insecure-tcp"]}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use super::error::{ConnectorError, Error};
|
||||
use hickory_resolver::{IntoName, TokioAsyncResolver};
|
||||
use futures::{future::select_ok, FutureExt};
|
||||
use hickory_resolver::{
|
||||
config::LookupIpStrategy, name_server::TokioConnectionProvider, IntoName, TokioAsyncResolver,
|
||||
};
|
||||
use log::debug;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::TcpStream;
|
||||
|
@ -13,19 +16,24 @@ pub async fn connect_to_host(domain: &str, port: u16) -> Result<TcpStream, Error
|
|||
.map_err(|e| Error::from(crate::Error::Io(e)))?);
|
||||
}
|
||||
|
||||
let resolver = TokioAsyncResolver::tokio_from_system_conf().map_err(ConnectorError::Resolve)?;
|
||||
let (config, mut options) =
|
||||
hickory_resolver::system_conf::read_system_conf().map_err(ConnectorError::Resolve)?;
|
||||
options.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||
let resolver = TokioAsyncResolver::new(config, options, TokioConnectionProvider::default());
|
||||
|
||||
let ips = resolver
|
||||
.lookup_ip(ascii_domain)
|
||||
.await
|
||||
.map_err(ConnectorError::Resolve)?;
|
||||
for ip in ips.iter() {
|
||||
match TcpStream::connect(&SocketAddr::new(ip, port)).await {
|
||||
Ok(stream) => return Ok(stream),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
Err(crate::Error::Disconnected.into())
|
||||
// Happy Eyeballs: connect to all records in parallel, return the
|
||||
// first to succeed
|
||||
select_ok(
|
||||
ips.into_iter()
|
||||
.map(|ip| TcpStream::connect(SocketAddr::new(ip, port)).boxed()),
|
||||
)
|
||||
.await
|
||||
.map(|(result, _)| result)
|
||||
.map_err(|_| crate::Error::Disconnected.into())
|
||||
}
|
||||
|
||||
pub async fn connect_with_srv(
|
||||
|
|
|
@ -18,13 +18,13 @@ chrono = "0.4"
|
|||
futures = "0.3"
|
||||
tokio = { version = "1", features = ["fs"] }
|
||||
log = "0.4"
|
||||
reqwest = { version = "0.11.8", features = ["stream"] }
|
||||
reqwest = { version = "0.12", features = ["stream"] }
|
||||
tokio-util = { version = "0.7", features = ["codec"] }
|
||||
# same repository dependencies
|
||||
tokio-xmpp = { version = "3.4", path = "../tokio-xmpp", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.10", default-features = false, features = ["auto-color", "humantime"] }
|
||||
env_logger = { version = "0.11", default-features = false, features = ["auto-color", "humantime"] }
|
||||
|
||||
[[example]]
|
||||
name = "hello_bot"
|
||||
|
|
Loading…
Reference in New Issue