contact_addr.rs

  1use futures::stream::StreamExt;
  2use std::env::args;
  3use std::process::exit;
  4use std::str::FromStr;
  5use tokio_xmpp::rustls;
  6use tokio_xmpp::{Client, IqRequest, IqResponse};
  7use xmpp_parsers::{
  8    disco::{DiscoInfoQuery, DiscoInfoResult},
  9    jid::{BareJid, Jid},
 10    ns,
 11    server_info::ServerInfo,
 12};
 13
 14#[tokio::main]
 15async fn main() {
 16    env_logger::init();
 17
 18    rustls::crypto::aws_lc_rs::default_provider()
 19        .install_default()
 20        .expect("failed to install rustls crypto provider");
 21
 22    let args: Vec<String> = args().collect();
 23    if args.len() != 4 {
 24        println!("Usage: {} <jid> <password> <target>", args[0]);
 25        exit(1);
 26    }
 27    let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
 28    let password = args[2].clone();
 29    let target = Jid::from_str(&args[3]).expect(&format!("Invalid JID: {}", &args[3]));
 30
 31    // Client instance
 32    let mut client = Client::new(jid, password);
 33
 34    let token = client
 35        .send_iq(
 36            Some(target),
 37            IqRequest::Get(DiscoInfoQuery { node: None }.into()),
 38        )
 39        .await;
 40    tokio::pin!(token);
 41
 42    // Main loop, processes events
 43    loop {
 44        tokio::select! {
 45            response = &mut token => match response {
 46                Ok(IqResponse::Result(Some(payload))) => {
 47                    if payload.is("query", ns::DISCO_INFO) {
 48                        if let Ok(disco_info) = DiscoInfoResult::try_from(payload) {
 49                            for ext in disco_info.extensions {
 50                                if let Ok(server_info) = ServerInfo::try_from(ext) {
 51                                    print_server_info(server_info);
 52                                }
 53                            }
 54                        }
 55                    }
 56                    break;
 57                }
 58                Ok(IqResponse::Result(None)) => {
 59                    panic!("disco#info response misses payload!");
 60                }
 61                Ok(IqResponse::Error(err)) => {
 62                    panic!("disco#info response is an error: {:?}", err);
 63                }
 64                Err(err) => {
 65                    panic!("disco#info request failed to send: {}", err);
 66                }
 67            },
 68            event = client.next() => {
 69                let Some(event) = event else {
 70                    println!("Client terminated");
 71                    break;
 72                };
 73                if event.is_online() {
 74                    println!("Online!");
 75                }
 76            },
 77        }
 78    }
 79    client.send_end().await.expect("Stream shutdown unclean");
 80}
 81
 82fn convert_field(field: Vec<String>) -> String {
 83    field
 84        .iter()
 85        .fold((field.len(), String::new()), |(l, mut acc), s| {
 86            acc.push('<');
 87            acc.push_str(&s);
 88            acc.push('>');
 89            if l > 1 {
 90                acc.push(',');
 91                acc.push(' ');
 92            }
 93            (0, acc)
 94        })
 95        .1
 96}
 97
 98fn print_server_info(server_info: ServerInfo) {
 99    if server_info.abuse.len() != 0 {
100        println!("abuse: {}", convert_field(server_info.abuse));
101    }
102    if server_info.admin.len() != 0 {
103        println!("admin: {}", convert_field(server_info.admin));
104    }
105    if server_info.feedback.len() != 0 {
106        println!("feedback: {}", convert_field(server_info.feedback));
107    }
108    if server_info.sales.len() != 0 {
109        println!("sales: {}", convert_field(server_info.sales));
110    }
111    if server_info.security.len() != 0 {
112        println!("security: {}", convert_field(server_info.security));
113    }
114    if server_info.support.len() != 0 {
115        println!("support: {}", convert_field(server_info.support));
116    }
117}