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}