mod.rs

  1// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use crate::{
  8    Agent, Event, RoomNick,
  9    jid::{BareJid, Jid},
 10    minidom::Element,
 11    muc::room::{JoinRoomSettings, LeaveRoomSettings},
 12    parsers::{
 13        bookmarks2, ns,
 14        pubsub::{self, pubsub::PubSub},
 15    },
 16};
 17
 18use std::str::FromStr;
 19
 20#[cfg(feature = "avatars")]
 21pub(crate) mod avatar;
 22
 23pub(crate) async fn handle_event(
 24    #[cfg_attr(not(feature = "avatars"), allow(unused_variables))] from: &Jid,
 25    elem: Element,
 26    #[cfg_attr(not(feature = "avatars"), allow(unused_variables))] agent: &mut Agent,
 27) -> Vec<Event> {
 28    // We allow the useless mut warning for no-default-features,
 29    // since for now only avatars pushes events here.
 30    #[allow(unused_mut)]
 31    let mut events = Vec::new();
 32
 33    let event = pubsub::Event::try_from(elem);
 34    trace!("PubSub event: {:#?}", event);
 35    match event {
 36        Ok(pubsub::Event {
 37            payload:
 38                pubsub::event::Payload::Items {
 39                    node,
 40                    published,
 41                    retracted,
 42                },
 43        }) => {
 44            match node.0 {
 45                #[cfg(feature = "avatars")]
 46                ref node if node == ns::AVATAR_METADATA => {
 47                    // TODO: Also handle retracted!
 48                    let new_events =
 49                        avatar::handle_metadata_pubsub_event(&from, agent, published).await;
 50                    events.extend(new_events);
 51                }
 52                ref node if node == ns::BOOKMARKS2 => {
 53                    let config = agent.get_config().await;
 54                    // TODO: Check that our bare JID is the sender.
 55                    if let [item] = &published[..] {
 56                        let jid = BareJid::from_str(&item.id.clone().unwrap().0).unwrap();
 57                        let payload = item.payload.clone().unwrap();
 58                        match bookmarks2::Conference::try_from(payload) {
 59                            Ok(conference) => {
 60                                if conference.autojoin {
 61                                    if !agent.rooms_joined.contains_key(&jid) {
 62                                        agent
 63                                            .join_room(JoinRoomSettings {
 64                                                nick: conference.nick.map(RoomNick::new),
 65                                                password: conference.password,
 66                                                ..JoinRoomSettings::new(jid)
 67                                            })
 68                                            .await;
 69                                    } else {
 70                                        if config.bookmarks_autojoin {
 71                                            // So maybe another client of ours left the room... let's leave it too
 72                                            agent.leave_room(LeaveRoomSettings::new(jid)).await;
 73                                        }
 74                                    }
 75                                }
 76                            }
 77                            Err(err) => println!("not bookmark: {}", err),
 78                        }
 79                    } else if let [item] = &retracted[..] {
 80                        if config.bookmarks_autojoin {
 81                            let jid = BareJid::from_str(&item.0).unwrap();
 82
 83                            agent.leave_room(LeaveRoomSettings::new(jid)).await;
 84                        }
 85                    } else {
 86                        error!("No published or retracted item in pubsub event!");
 87                    }
 88                }
 89                ref node => unimplemented!("node {}", node),
 90            }
 91        }
 92        Ok(pubsub::Event {
 93            payload: pubsub::event::Payload::Purge { node },
 94        }) => match node.0 {
 95            ref node if node == ns::BOOKMARKS2 => {
 96                warn!("The bookmarks2 PEP node was deleted!");
 97            }
 98            ref node => unimplemented!("node {}", node),
 99        },
100        Err(e) => {
101            error!("Error parsing PubSub event: {}", e);
102        }
103        _ => unimplemented!("PubSub event: {:#?}", event),
104    }
105    events
106}
107
108pub(crate) async fn handle_iq_result(
109    #[cfg_attr(not(feature = "avatars"), allow(unused_variables))] from: &Jid,
110    elem: Element,
111    agent: &mut Agent,
112) -> impl IntoIterator<Item = Event> {
113    // We allow the useless mut warning for no-default-features,
114    // since for now only avatars pushes events here.
115    #[allow(unused_mut)]
116    let mut events = Vec::new();
117
118    let pubsub = PubSub::try_from(elem).unwrap();
119    trace!("PubSub: {:#?}", pubsub);
120    if let PubSub::Items(items) = pubsub {
121        match items.node.0.clone() {
122            #[cfg(feature = "avatars")]
123            ref node if node == ns::AVATAR_DATA => {
124                let new_events = avatar::handle_data_pubsub_iq(&from, &items);
125                events.extend(new_events);
126            }
127            ref node if node == ns::BOOKMARKS2 => {
128                let config = agent.get_config().await;
129                // Keep track of the new added/removed rooms in the bookmarks2 list.
130                // The rooms we joined which are no longer in the list should be left ASAP.
131                let mut new_room_list: Vec<BareJid> = Vec::new();
132
133                for item in items.items {
134                    let jid = BareJid::from_str(&item.id.clone().unwrap().0).unwrap();
135                    let payload = item.payload.clone().unwrap();
136                    match bookmarks2::Conference::try_from(payload) {
137                        Ok(conference) => {
138                            // This room was either marked for join or leave, but it was still in the bookmarks.
139                            // Keep track in new_room_list.
140                            new_room_list.push(jid.clone());
141
142                            if conference.autojoin {
143                                if !agent.rooms_joined.contains_key(&jid) {
144                                    agent
145                                        .join_room(JoinRoomSettings {
146                                            nick: conference.nick.map(RoomNick::new),
147                                            password: conference.password,
148                                            ..JoinRoomSettings::new(jid)
149                                        })
150                                        .await;
151                                }
152                            } else {
153                                if config.bookmarks_autojoin {
154                                    // Leave the room that is no longer autojoin
155                                    agent.leave_room(LeaveRoomSettings::new(jid)).await;
156                                }
157                            }
158                        }
159                        Err(err) => {
160                            warn!("Wrong payload type in bookmarks2 item: {}", err);
161                        }
162                    }
163                }
164
165                if config.bookmarks_autojoin {
166                    // Now we leave the rooms that are no longer in the bookmarks
167                    let mut rooms_to_leave: Vec<BareJid> = Vec::new();
168                    for (room, _nick) in &agent.rooms_joined {
169                        if !new_room_list.contains(&room) {
170                            rooms_to_leave.push(room.clone());
171                        }
172                    }
173
174                    for room in rooms_to_leave {
175                        agent.leave_room(LeaveRoomSettings::new(room)).await;
176                    }
177                }
178            }
179            _ => unimplemented!(),
180        }
181    }
182    events
183}