Port crates to use new XSO-based xmlstream

This commit is contained in:
Jonas Schäfer 2024-08-10 15:05:42 +02:00
commit ab10e30ac0
26 changed files with 623 additions and 973 deletions

View file

@ -1,9 +1,8 @@
use futures::stream::StreamExt;
use minidom::Element;
use std::env::args;
use std::process::exit;
use std::str::FromStr;
use tokio_xmpp::Client;
use tokio_xmpp::{Client, Stanza};
use xmpp_parsers::{
disco::{DiscoInfoQuery, DiscoInfoResult},
iq::{Iq, IqType},
@ -41,24 +40,21 @@ async fn main() {
let target_jid: Jid = target.clone().parse().unwrap();
let iq = make_disco_iq(target_jid);
println!("Sending disco#info request to {}", target.clone());
println!(">> {}", String::from(&iq));
client.send_stanza(iq).await.unwrap();
} else if let Some(stanza) = event.into_stanza() {
if stanza.is("iq", "jabber:client") {
let iq = Iq::try_from(stanza).unwrap();
if let IqType::Result(Some(payload)) = iq.payload {
if payload.is("query", ns::DISCO_INFO) {
if let Ok(disco_info) = DiscoInfoResult::try_from(payload) {
for ext in disco_info.extensions {
if let Ok(server_info) = ServerInfo::try_from(ext) {
print_server_info(server_info);
}
println!(">> {:?}", iq);
client.send_stanza(iq.into()).await.unwrap();
} else if let Some(Stanza::Iq(iq)) = event.into_stanza() {
if let IqType::Result(Some(payload)) = iq.payload {
if payload.is("query", ns::DISCO_INFO) {
if let Ok(disco_info) = DiscoInfoResult::try_from(payload) {
for ext in disco_info.extensions {
if let Ok(server_info) = ServerInfo::try_from(ext) {
print_server_info(server_info);
}
}
}
wait_for_stream_end = true;
client.send_end().await.unwrap();
}
wait_for_stream_end = true;
client.send_end().await.unwrap();
}
}
} else {
@ -67,11 +63,10 @@ async fn main() {
}
}
fn make_disco_iq(target: Jid) -> Element {
fn make_disco_iq(target: Jid) -> Iq {
Iq::from_get("disco", DiscoInfoQuery { node: None })
.with_id(String::from("contact"))
.with_to(target)
.into()
}
fn convert_field(field: Vec<String>) -> String {

View file

@ -1,11 +1,10 @@
use futures::stream::StreamExt;
use minidom::Element;
use std::env::args;
use std::fs::{create_dir_all, File};
use std::io::{self, Write};
use std::process::exit;
use std::str::FromStr;
use tokio_xmpp::Client;
use tokio_xmpp::{Client, Stanza};
use xmpp_parsers::{
avatar::{Data as AvatarData, Metadata as AvatarMetadata},
caps::{compute_disco, hash_caps, Caps},
@ -13,7 +12,6 @@ use xmpp_parsers::{
hashes::Algo,
iq::{Iq, IqType},
jid::{BareJid, Jid},
message::Message,
ns,
presence::{Presence, Type as PresenceType},
pubsub::{
@ -55,100 +53,107 @@ async fn main() {
let presence = make_presence(caps);
client.send_stanza(presence.into()).await.unwrap();
} else if let Some(stanza) = event.into_stanza() {
if stanza.is("iq", "jabber:client") {
let iq = Iq::try_from(stanza).unwrap();
if let IqType::Get(payload) = iq.payload {
if payload.is("query", ns::DISCO_INFO) {
let query = DiscoInfoQuery::try_from(payload);
match query {
Ok(query) => {
let mut disco = disco_info.clone();
disco.node = query.node;
let iq = Iq::from_result(iq.id, Some(disco))
.with_to(iq.from.unwrap());
client.send_stanza(iq.into()).await.unwrap();
match stanza {
Stanza::Iq(iq) => {
if let IqType::Get(payload) = iq.payload {
if payload.is("query", ns::DISCO_INFO) {
let query = DiscoInfoQuery::try_from(payload);
match query {
Ok(query) => {
let mut disco = disco_info.clone();
disco.node = query.node;
let iq = Iq::from_result(iq.id, Some(disco))
.with_to(iq.from.unwrap());
client.send_stanza(iq.into()).await.unwrap();
}
Err(err) => client
.send_stanza(
make_error(
iq.from.unwrap(),
iq.id,
ErrorType::Modify,
DefinedCondition::BadRequest,
&format!("{}", err),
)
.into(),
)
.await
.unwrap(),
}
Err(err) => client
.send_stanza(make_error(
} else {
// We MUST answer unhandled get iqs with a service-unavailable error.
client
.send_stanza(
make_error(
iq.from.unwrap(),
iq.id,
ErrorType::Cancel,
DefinedCondition::ServiceUnavailable,
"No handler defined for this kind of iq.",
)
.into(),
)
.await
.unwrap();
}
} else if let IqType::Result(Some(payload)) = iq.payload {
if payload.is("pubsub", ns::PUBSUB) {
let pubsub = PubSub::try_from(payload).unwrap();
let from = iq.from.clone().unwrap_or(jid.clone().into());
handle_iq_result(pubsub, &from);
}
} else if let IqType::Set(_) = iq.payload {
// We MUST answer unhandled set iqs with a service-unavailable error.
client
.send_stanza(
make_error(
iq.from.unwrap(),
iq.id,
ErrorType::Modify,
DefinedCondition::BadRequest,
&format!("{}", err),
))
.await
.unwrap(),
}
} else {
// We MUST answer unhandled get iqs with a service-unavailable error.
client
.send_stanza(make_error(
iq.from.unwrap(),
iq.id,
ErrorType::Cancel,
DefinedCondition::ServiceUnavailable,
"No handler defined for this kind of iq.",
))
ErrorType::Cancel,
DefinedCondition::ServiceUnavailable,
"No handler defined for this kind of iq.",
)
.into(),
)
.await
.unwrap();
}
} else if let IqType::Result(Some(payload)) = iq.payload {
if payload.is("pubsub", ns::PUBSUB) {
let pubsub = PubSub::try_from(payload).unwrap();
let from = iq.from.clone().unwrap_or(jid.clone().into());
handle_iq_result(pubsub, &from);
}
} else if let IqType::Set(_) = iq.payload {
// We MUST answer unhandled set iqs with a service-unavailable error.
client
.send_stanza(make_error(
iq.from.unwrap(),
iq.id,
ErrorType::Cancel,
DefinedCondition::ServiceUnavailable,
"No handler defined for this kind of iq.",
))
.await
.unwrap();
}
} else if stanza.is("message", "jabber:client") {
let message = Message::try_from(stanza).unwrap();
let from = message.from.clone().unwrap();
if let Some(body) = message.get_best_body(vec!["en"]) {
if body.0 == "die" {
println!("Secret die command triggered by {}", from);
wait_for_stream_end = true;
client.send_end().await.unwrap();
Stanza::Message(message) => {
let from = message.from.clone().unwrap();
if let Some(body) = message.get_best_body(vec!["en"]) {
if body.0 == "die" {
println!("Secret die command triggered by {}", from);
wait_for_stream_end = true;
client.send_end().await.unwrap();
}
}
}
for child in message.payloads {
if child.is("event", ns::PUBSUB_EVENT) {
let event = PubSubEvent::try_from(child).unwrap();
if let PubSubEvent::PublishedItems { node, items } = event {
if node.0 == ns::AVATAR_METADATA {
for item in items.into_iter() {
let payload = item.payload.clone().unwrap();
if payload.is("metadata", ns::AVATAR_METADATA) {
// TODO: do something with these metadata.
let _metadata =
AvatarMetadata::try_from(payload).unwrap();
println!(
"{} has published an avatar, downloading...",
from.clone()
);
let iq = download_avatar(from.clone());
client.send_stanza(iq.into()).await.unwrap();
for child in message.payloads {
if child.is("event", ns::PUBSUB_EVENT) {
let event = PubSubEvent::try_from(child).unwrap();
if let PubSubEvent::PublishedItems { node, items } = event {
if node.0 == ns::AVATAR_METADATA {
for item in items.into_iter() {
let payload = item.payload.clone().unwrap();
if payload.is("metadata", ns::AVATAR_METADATA) {
// TODO: do something with these metadata.
let _metadata =
AvatarMetadata::try_from(payload).unwrap();
println!(
"{} has published an avatar, downloading...",
from.clone()
);
let iq = download_avatar(from.clone());
client.send_stanza(iq.into()).await.unwrap();
}
}
}
}
}
}
}
} else if stanza.is("presence", "jabber:client") {
// Nothing to do here.
()
} else {
panic!("Unknown stanza: {}", String::from(&stanza));
Stanza::Presence(_) => (),
}
}
} else {
@ -164,10 +169,9 @@ fn make_error(
type_: ErrorType,
condition: DefinedCondition,
text: &str,
) -> Element {
) -> Iq {
let error = StanzaError::new(type_, condition, "en", text);
let iq = Iq::from_error(id, error).with_to(to);
iq.into()
Iq::from_error(id, error).with_to(to)
}
fn make_disco() -> DiscoInfoResult {

View file

@ -1,5 +1,4 @@
use futures::stream::StreamExt;
use minidom::Element;
use std::env::args;
use std::process::exit;
use std::str::FromStr;
@ -40,7 +39,7 @@ async fn main() {
println!("Online at {}", jid);
let presence = make_presence();
client.send_stanza(presence).await.unwrap();
client.send_stanza(presence.into()).await.unwrap();
} else if let Some(message) = event
.into_stanza()
.and_then(|stanza| Message::try_from(stanza).ok())
@ -55,7 +54,7 @@ async fn main() {
if message.type_ != MessageType::Error {
// This is a message we'll echo
let reply = make_reply(from.clone(), &body.0);
client.send_stanza(reply).await.unwrap();
client.send_stanza(reply.into()).await.unwrap();
}
}
_ => {}
@ -69,18 +68,18 @@ async fn main() {
}
// Construct a <presence/>
fn make_presence() -> Element {
fn make_presence() -> Presence {
let mut presence = Presence::new(PresenceType::None);
presence.show = Some(PresenceShow::Chat);
presence
.statuses
.insert(String::from("en"), String::from("Echoing messages."));
presence.into()
presence
}
// Construct a chat <message/>
fn make_reply(to: Jid, body: &str) -> Element {
fn make_reply(to: Jid, body: &str) -> Message {
let mut message = Message::new(Some(to));
message.bodies.insert(String::new(), Body(body.to_owned()));
message.into()
message
}

View file

@ -1,5 +1,4 @@
use futures::stream::StreamExt;
use minidom::Element;
use std::env::args;
use std::process::exit;
use std::str::FromStr;
@ -45,7 +44,7 @@ async fn main() {
Jid::from_str("test@component.linkmauve.fr/coucou").unwrap(),
Jid::from_str("linkmauve@linkmauve.fr").unwrap(),
);
component.send_stanza(presence).await.unwrap();
component.send_stanza(presence.into()).await.unwrap();
// Main loop, processes events
loop {
@ -56,7 +55,7 @@ async fn main() {
(Some(from), Some(body)) => {
if message.type_ != MessageType::Error {
let reply = make_reply(from, &body.0);
component.send_stanza(reply).await.unwrap();
component.send_stanza(reply.into()).await.unwrap();
}
}
_ => (),
@ -69,7 +68,7 @@ async fn main() {
}
// Construct a <presence/>
fn make_presence(from: Jid, to: Jid) -> Element {
fn make_presence(from: Jid, to: Jid) -> Presence {
let mut presence = Presence::new(PresenceType::None);
presence.from = Some(from);
presence.to = Some(to);
@ -77,12 +76,12 @@ fn make_presence(from: Jid, to: Jid) -> Element {
presence
.statuses
.insert(String::from("en"), String::from("Echoing messages."));
presence.into()
presence
}
// Construct a chat <message/>
fn make_reply(to: Jid, body: &str) -> Element {
fn make_reply(to: Jid, body: &str) -> Message {
let mut message = Message::new(Some(to));
message.bodies.insert(String::new(), Body(body.to_owned()));
message.into()
message
}

View file

@ -1,8 +1,8 @@
use futures::{SinkExt, StreamExt};
use tokio::{self, io, net::TcpSocket};
use tokio_util::codec::Framed;
use tokio_xmpp::proto::XmppCodec;
use tokio_xmpp::parsers::stream_features::StreamFeatures;
use tokio_xmpp::xmlstream::{accept_stream, StreamHeader};
#[tokio::main]
async fn main() -> Result<(), io::Error> {
@ -16,16 +16,22 @@ async fn main() -> Result<(), io::Error> {
// Main loop, accepts incoming connections
loop {
let (stream, _addr) = listener.accept().await?;
// Use the `XMPPCodec` to encode and decode frames
let mut framed = Framed::new(stream, XmppCodec::new());
let stream = accept_stream(
tokio::io::BufStream::new(stream),
tokio_xmpp::parsers::ns::DEFAULT_NS,
)
.await?;
let stream = stream.send_header(StreamHeader::default()).await?;
let mut stream = stream
.send_features::<minidom::Element>(&StreamFeatures::default())
.await?;
tokio::spawn(async move {
while let Some(packet) = framed.next().await {
while let Some(packet) = stream.next().await {
match packet {
Ok(packet) => {
println!("Received packet: {:?}", packet);
framed.send(packet).await.unwrap();
stream.send(&packet).await.unwrap();
}
Err(e) => {
eprintln!("Error: {:?}", e);