xmpp-rs/tokio-xmpp/examples/contact_addr.rs
Jonas Schäfer fc8b581593 Clean up tls-related feature flags
This provides a clear and consistent interface for selecting TLS-related
features on both (xmpp and tokio-xmpp) crates. All feature checks have
been revamped. All working combinations have been tested (including a
connectivity test + --all-features docs build) using:

```
set -xeuo pipefail
features=(aws_lc_rs ring ktls,aws_lc_rs ktls,ring aws_lc_rs,ring aws_lc_rs,ring,ktls native-tls rustls-any-backend)
export RUSTFLAGS=" -D warnings"
cargo test --no-default-features
cargo test
for feature in ${features[@]}; do
  echo ">>> BUILDING with $feature" 2>&1
  # Running code or building examples cannot succeed with rustls-any-backend.
  features="starttls,$feature"
  if [ "$feature" != 'rustls-any-backend' ]; then
    if ! cargo test --no-default-features --features="$features"; then
      echo ">>> BUILD FAILED for tls feature set: $features" >&2
      exit 1
    fi
    set +e
    timeout -sINT -p -k 2 3 cargo run --no-default-features --features="$features" --example keep_connection -- test@hub.sotecware.net "$(pass xmpp/test@hub.sotecware.net)"
    status="$?"
    set -e
    if [ $status -ne 0 ]; then
      echo ">>> keep_connection did not shut down cleanly! (status: $status)" >&2
      exit 1
    fi
  else
    if ! cargo build --no-default-features --features="$feature"; then
      echo ">>> BUILD FAILED for tls feature set: $features" >&2
      exit 1
    fi
  fi
done
RUSTDOCFLAGS="--cfg docsrs" RUSTFLAGS="--cfg xmpprs_doc_build" cargo +nightly doc -Zrustdoc-map --all-features
```
2025-05-10 22:02:39 +02:00

130 lines
4.1 KiB
Rust

use futures::stream::StreamExt;
use std::env::args;
use std::process::exit;
use std::str::FromStr;
#[cfg(feature = "rustls-any-backend")]
use tokio_xmpp::rustls;
use tokio_xmpp::{Client, IqRequest, IqResponse};
use xmpp_parsers::{
disco::{DiscoInfoQuery, DiscoInfoResult},
jid::{BareJid, Jid},
ns,
server_info::ServerInfo,
};
#[cfg(all(
feature = "rustls-any-backend",
not(any(feature = "aws_lc_rs", feature = "ring"))
))]
compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
#[tokio::main]
async fn main() {
env_logger::init();
#[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("failed to install rustls crypto provider");
#[cfg(all(feature = "ring"))]
rustls::crypto::ring::default_provider()
.install_default()
.expect("failed to install rustls crypto provider");
let args: Vec<String> = args().collect();
if args.len() != 4 {
println!("Usage: {} <jid> <password> <target>", args[0]);
exit(1);
}
let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
let password = args[2].clone();
let target = Jid::from_str(&args[3]).expect(&format!("Invalid JID: {}", &args[3]));
// Client instance
let mut client = Client::new(jid, password);
let token = client
.send_iq(
Some(target),
IqRequest::Get(DiscoInfoQuery { node: None }.into()),
)
.await;
tokio::pin!(token);
// Main loop, processes events
loop {
tokio::select! {
response = &mut token => match response {
Ok(IqResponse::Result(Some(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);
}
}
}
}
break;
}
Ok(IqResponse::Result(None)) => {
panic!("disco#info response misses payload!");
}
Ok(IqResponse::Error(err)) => {
panic!("disco#info response is an error: {:?}", err);
}
Err(err) => {
panic!("disco#info request failed to send: {}", err);
}
},
event = client.next() => {
let Some(event) = event else {
println!("Client terminated");
break;
};
if event.is_online() {
println!("Online!");
}
},
}
}
client.send_end().await.expect("Stream shutdown unclean");
}
fn convert_field(field: Vec<String>) -> String {
field
.iter()
.fold((field.len(), String::new()), |(l, mut acc), s| {
acc.push('<');
acc.push_str(&s);
acc.push('>');
if l > 1 {
acc.push(',');
acc.push(' ');
}
(0, acc)
})
.1
}
fn print_server_info(server_info: ServerInfo) {
if server_info.abuse.len() != 0 {
println!("abuse: {}", convert_field(server_info.abuse));
}
if server_info.admin.len() != 0 {
println!("admin: {}", convert_field(server_info.admin));
}
if server_info.feedback.len() != 0 {
println!("feedback: {}", convert_field(server_info.feedback));
}
if server_info.sales.len() != 0 {
println!("sales: {}", convert_field(server_info.sales));
}
if server_info.security.len() != 0 {
println!("security: {}", convert_field(server_info.security));
}
if server_info.support.len() != 0 {
println!("support: {}", convert_field(server_info.support));
}
}