Add "Contact Address for XMPP Services" (0157) example

Maxime “pep” Buquet created

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>

Change summary

Cargo.lock               |   8 +
examples/contact_addr.rs | 130 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 135 insertions(+), 3 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1,3 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
 [[package]]
 name = "MacTypes-sys"
 version = "2.1.0"
@@ -1374,7 +1376,7 @@ dependencies = [
  "trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "xmpp-parsers 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xmpp-parsers 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1562,7 +1564,7 @@ dependencies = [
 
 [[package]]
 name = "xmpp-parsers"
-version = "0.12.2"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1755,4 +1757,4 @@ dependencies = [
 "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e"
 "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
 "checksum xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd7ebf0203c620906230ce22caa5df0b603c32b6fef72a275a48f6a2ae64b9"
-"checksum xmpp-parsers 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "58b4400e1ae0d246044db5fa7f2e693fdfe9cc6e8eaa72ef2a68c5dc1d3c96de"
+"checksum xmpp-parsers 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c467bb13e01718be793cb7a1c3d38d0e9ba45898db306aa43e70657a8aa3c2f2"

examples/contact_addr.rs 🔗

@@ -0,0 +1,130 @@
+use futures::{future, Sink, Stream};
+use std::env::args;
+use std::process::exit;
+use tokio::runtime::current_thread::Runtime;
+use tokio_xmpp::{Client, xmpp_codec::Packet};
+use xmpp_parsers::{
+    Element,
+    Jid,
+    TryFrom,
+    ns,
+    iq::{
+        Iq,
+        IqType,
+    },
+    disco::{
+        DiscoInfoResult,
+        DiscoInfoQuery,
+    },
+    server_info::ServerInfo,
+};
+
+fn main() {
+    let args: Vec<String> = args().collect();
+    if args.len() != 4 {
+        println!("Usage: {} <jid> <password> <target>", args[0]);
+        exit(1);
+    }
+    let jid = &args[1];
+    let password = &args[2];
+    let target = &args[3];
+
+    // tokio_core context
+    let mut rt = Runtime::new().unwrap();
+    // Client instance
+    let client = Client::new(jid, password).unwrap();
+
+    // Make the two interfaces for sending and receiving independent
+    // of each other so we can move one into a closure.
+    let (mut sink, stream) = client.split();
+    // Wrap sink in Option so that we can take() it for the send(self)
+    // to consume and return it back when ready.
+    let mut send = move |packet| {
+        sink.start_send(packet).expect("start_send");
+    };
+    // Main loop, processes events
+    let mut wait_for_stream_end = false;
+    let done = stream.for_each(|event| {
+        if wait_for_stream_end {
+            /* Do Nothing. */
+        } else if event.is_online() {
+            println!("Online!");
+
+            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));
+            send(Packet::Stanza(iq));
+        } 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);
+                                    wait_for_stream_end = true;
+                                    send(Packet::StreamEnd);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        Box::new(future::ok(()))
+    });
+
+    // Start polling `done`
+    match rt.block_on(done) {
+        Ok(_) => (),
+        Err(e) => {
+            println!("Fatal: {}", e);
+            ()
+        }
+    }
+}
+
+fn make_disco_iq(target: Jid) -> Element {
+    Iq::from_get("disco", DiscoInfoQuery { node: None })
+        .with_id(String::from("contact"))
+        .with_to(target)
+        .into()
+}
+
+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));
+    }
+}