2020-03-05 00:25:24 +00:00
|
|
|
use futures::stream::StreamExt;
|
2019-01-28 21:39:54 +00:00
|
|
|
use std::env::args;
|
|
|
|
use std::process::exit;
|
2023-06-01 09:58:04 +00:00
|
|
|
use std::str::FromStr;
|
2024-08-10 13:05:42 +00:00
|
|
|
use tokio_xmpp::{Client, Stanza};
|
2019-01-28 21:39:54 +00:00
|
|
|
use xmpp_parsers::{
|
2019-10-22 23:32:41 +00:00
|
|
|
disco::{DiscoInfoQuery, DiscoInfoResult},
|
|
|
|
iq::{Iq, IqType},
|
2024-07-24 18:39:27 +00:00
|
|
|
jid::{BareJid, Jid},
|
2019-01-28 21:39:54 +00:00
|
|
|
ns,
|
|
|
|
server_info::ServerInfo,
|
|
|
|
};
|
|
|
|
|
2020-03-05 00:25:24 +00:00
|
|
|
#[tokio::main]
|
|
|
|
async fn main() {
|
2023-06-05 11:06:52 +00:00
|
|
|
env_logger::init();
|
|
|
|
|
2019-01-28 21:39:54 +00:00
|
|
|
let args: Vec<String> = args().collect();
|
|
|
|
if args.len() != 4 {
|
|
|
|
println!("Usage: {} <jid> <password> <target>", args[0]);
|
|
|
|
exit(1);
|
|
|
|
}
|
2023-06-01 09:58:04 +00:00
|
|
|
let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
|
2020-03-05 00:25:24 +00:00
|
|
|
let password = args[2].clone();
|
2019-01-28 21:39:54 +00:00
|
|
|
let target = &args[3];
|
|
|
|
|
|
|
|
// Client instance
|
2023-06-01 09:58:04 +00:00
|
|
|
let mut client = Client::new(jid, password);
|
2019-01-28 21:39:54 +00:00
|
|
|
|
|
|
|
// Main loop, processes events
|
2024-08-20 14:54:48 +00:00
|
|
|
while let Some(event) = client.next().await {
|
|
|
|
if event.is_online() {
|
|
|
|
println!("Online!");
|
2019-01-28 21:39:54 +00:00
|
|
|
|
2024-08-20 14:54:48 +00:00
|
|
|
let target_jid: Jid = target.clone().parse().unwrap();
|
|
|
|
let iq = make_disco_iq(target_jid);
|
|
|
|
println!("Sending disco#info request to {}", target.clone());
|
|
|
|
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);
|
2019-01-28 21:39:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-08-20 14:54:48 +00:00
|
|
|
break;
|
2019-01-28 21:39:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-08-20 14:54:48 +00:00
|
|
|
client.send_end().await.expect("Stream shutdown unclean");
|
2019-01-28 21:39:54 +00:00
|
|
|
}
|
|
|
|
|
2024-08-10 13:05:42 +00:00
|
|
|
fn make_disco_iq(target: Jid) -> Iq {
|
2019-01-28 21:39:54 +00:00
|
|
|
Iq::from_get("disco", DiscoInfoQuery { node: None })
|
|
|
|
.with_id(String::from("contact"))
|
|
|
|
.with_to(target)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn convert_field(field: Vec<String>) -> String {
|
2019-10-22 23:32:41 +00:00
|
|
|
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
|
2019-01-28 21:39:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|