Workaround prosody bug 1664

xmppftw created

https://issues.prosody.im/1664

Change summary

xmpp/src/lib.rs | 118 ++++++++++++++++++++++++++++++++------------------
1 file changed, 75 insertions(+), 43 deletions(-)

Detailed changes

xmpp/src/lib.rs 🔗

@@ -33,6 +33,7 @@ use tokio_xmpp::parsers::{
     pubsub::pubsub::{Items, PubSub},
     roster::{Item as RosterItem, Roster},
     stanza_error::{DefinedCondition, ErrorType, StanzaError},
+    Error as ParsersError,
 };
 use tokio_xmpp::{AsyncClient as TokioXmppClient, Event as TokioXmppEvent};
 pub use tokio_xmpp::{BareJid, Element, FullJid, Jid};
@@ -404,49 +405,7 @@ impl Agent {
                     }
                 }
             } else if payload.is("query", ns::DISCO_INFO) {
-                match DiscoInfoResult::try_from(payload) {
-                    Ok(disco) => {
-                        // Safe unwrap because no DISCO is received when we are not online
-                        if from == self.client.bound_jid().unwrap().to_bare()
-                            && self.awaiting_disco_bookmarks_type
-                        {
-                            // Trigger bookmarks query
-                            // TODO: only send this when the JoinRooms feature is enabled.
-                            self.awaiting_disco_bookmarks_type = false;
-                            let mut perform_bookmarks2 = false;
-                            for feature in disco.features {
-                                if feature.var == "urn:xmpp:bookmarks:1#compat" {
-                                    perform_bookmarks2 = true;
-                                }
-                            }
-
-                            if perform_bookmarks2 {
-                                // XEP-0402 bookmarks (modern)
-                                let iq = Iq::from_get(
-                                    "bookmarks",
-                                    PubSub::Items(Items::new(ns::BOOKMARKS2)),
-                                )
-                                .into();
-                                let _ = self.client.send_stanza(iq).await;
-                            } else {
-                                // XEP-0048 v1.0 bookmarks (legacy)
-                                let iq = Iq::from_get(
-                                    "bookmarks-legacy",
-                                    PrivateXMLQuery {
-                                        storage: bookmarks::Storage::new(),
-                                    },
-                                )
-                                .into();
-                                let _ = self.client.send_stanza(iq).await;
-                            }
-                        } else {
-                            unimplemented!("Ignored disco#info response from {}", from);
-                        }
-                    }
-                    Err(e) => {
-                        panic!("Wrong disco#info format: {}", e);
-                    }
-                }
+                self.handle_disco_info_result_payload(payload, from).await;
             }
         } else if let IqType::Set(_) = iq.payload {
             // We MUST answer unhandled set iqs with a service-unavailable error.
@@ -464,6 +423,79 @@ impl Agent {
         events
     }
 
+    // This method is a workaround due to prosody bug https://issues.prosody.im/1664
+    // FIXME: To be removed in the future
+    // The server doesn't return disco#info feature when querying the account
+    // so we add it manually because we know it's true
+    async fn handle_disco_info_result_payload(&mut self, payload: Element, from: Jid) {
+        match DiscoInfoResult::try_from(payload.clone()) {
+            Ok(disco) => {
+                self.handle_disco_info_result(disco, from).await;
+            }
+            Err(e) => match e {
+                ParsersError::ParseError(reason) => {
+                    if reason == "disco#info feature not present in disco#info." {
+                        let mut payload = payload.clone();
+                        let disco_feature =
+                            Feature::new("http://jabber.org/protocol/disco#info").into();
+                        payload.append_child(disco_feature);
+                        match DiscoInfoResult::try_from(payload) {
+                            Ok(disco) => {
+                                self.handle_disco_info_result(disco, from).await;
+                            }
+                            Err(e) => {
+                                panic!("Wrong disco#info format after workaround: {}", e)
+                            }
+                        }
+                    } else {
+                        panic!(
+                            "Wrong disco#info format (reason cannot be worked around): {}",
+                            e
+                        )
+                    }
+                }
+                _ => panic!("Wrong disco#info format: {}", e),
+            },
+        }
+    }
+
+    async fn handle_disco_info_result(&mut self, disco: DiscoInfoResult, from: Jid) {
+        // Safe unwrap because no DISCO is received when we are not online
+        if from == self.client.bound_jid().unwrap().to_bare() && self.awaiting_disco_bookmarks_type
+        {
+            info!("Received disco info about bookmarks type");
+            // Trigger bookmarks query
+            // TODO: only send this when the JoinRooms feature is enabled.
+            self.awaiting_disco_bookmarks_type = false;
+            let mut perform_bookmarks2 = false;
+            info!("{:#?}", disco.features);
+            for feature in disco.features {
+                if feature.var == "urn:xmpp:bookmarks:1#compat" {
+                    perform_bookmarks2 = true;
+                }
+            }
+
+            if perform_bookmarks2 {
+                // XEP-0402 bookmarks (modern)
+                let iq =
+                    Iq::from_get("bookmarks", PubSub::Items(Items::new(ns::BOOKMARKS2))).into();
+                let _ = self.client.send_stanza(iq).await;
+            } else {
+                // XEP-0048 v1.0 bookmarks (legacy)
+                let iq = Iq::from_get(
+                    "bookmarks-legacy",
+                    PrivateXMLQuery {
+                        storage: bookmarks::Storage::new(),
+                    },
+                )
+                .into();
+                let _ = self.client.send_stanza(iq).await;
+            }
+        } else {
+            unimplemented!("Ignored disco#info response from {}", from);
+        }
+    }
+
     async fn handle_message(&mut self, message: Message) -> Vec<Event> {
         let mut events = vec![];
         let from = message.from.clone().unwrap();