tuwunel_service/appservice/
mod.rs1mod append;
2mod keys;
3mod namespace_regex;
4mod ping;
5mod registration_info;
6pub(crate) mod request;
7mod thirdparty;
8
9use std::{
10 collections::BTreeMap,
11 ffi::OsStr,
12 fs::{self, read_dir},
13 sync::Arc,
14};
15
16use async_trait::async_trait;
17use futures::{FutureExt, Stream, TryStreamExt};
18use ruma::{RoomAliasId, RoomId, UserId, api::appservice::Registration};
19use tokio::sync::{RwLock, RwLockReadGuard, SetOnce};
20use tuwunel_core::{Err, Result, defer, err, utils::stream::IterStream};
21use tuwunel_database::Map;
22
23pub use self::{namespace_regex::NamespaceRegex, registration_info::RegistrationInfo};
24
25pub struct Service {
26 registration_info: RwLock<Registrations>,
27 loaded: SetOnce<()>,
28 services: Arc<crate::services::OnceServices>,
29 db: Data,
30}
31
32struct Data {
33 id_appserviceregistrations: Arc<Map>,
34}
35
36type Registrations = BTreeMap<String, RegistrationInfo>;
37
38#[async_trait]
39impl crate::Service for Service {
40 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
41 Ok(Arc::new(Self {
42 registration_info: RwLock::new(BTreeMap::new()),
43 loaded: SetOnce::new(),
44 services: args.services.clone(),
45 db: Data {
46 id_appserviceregistrations: args.db["id_appserviceregistrations"].clone(),
47 },
48 }))
49 }
50
51 async fn worker(self: Arc<Self>) -> Result {
52 defer! {{
53 self.loaded.set(()).ok();
54 }}
55
56 self.load().await
57 }
58
59 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
60}
61
62impl Service {
63 async fn load(&self) -> Result {
68 for (id, mut appservice) in self.services.config.appservice.clone() {
69 if appservice.id.is_empty() {
70 appservice.id = id.clone();
71 }
72
73 if *id != appservice.id {
74 return Err!(Config(
75 "id",
76 "Registration ID {:?} does not match the configured {id:?}",
77 appservice.id
78 ));
79 }
80
81 self.load_appservice(appservice.into()).await?;
82 }
83
84 if let Some(appservice_dir) = &self.services.config.appservice_dir {
85 let entries = read_dir(appservice_dir).map_err(|e| {
86 err!(Config("appservice_dir", "Failed to read {appservice_dir:?}: {e}"))
87 })?;
88
89 for dir_entry in entries {
90 let path = dir_entry?.path();
91
92 if !path.is_file()
93 || !path
94 .extension()
95 .and_then(OsStr::to_str)
96 .is_some_and(|ext| matches!(ext, "yaml" | "yml"))
97 {
98 continue;
99 }
100
101 let bytes = fs::read(path)?;
102 let registration: Registration = serde_yaml::from_slice(&bytes)?;
103
104 self.load_appservice(registration).await?;
105 }
106 }
107
108 self.iter_db_ids()
109 .try_for_each(|registration| self.load_appservice(registration))
110 .await?;
111
112 Ok(())
113 }
114
115 pub async fn load_appservice(&self, registration: Registration) -> Result {
116 let registration_info =
119 RegistrationInfo::new(registration, self.services.globals.server_name())?;
120
121 let id = ®istration_info.registration.id;
122
123 let mut registrations = self.registration_info.write().await;
124
125 for loaded_registration_info in registrations.values() {
126 let loaded_id = &loaded_registration_info.registration.id;
127
128 if loaded_id == id {
129 return Err!("Duplicate id: {id}");
130 }
131
132 if loaded_registration_info.registration.as_token
133 == registration_info.registration.as_token
134 {
135 return Err!("Duplicate as_token: {loaded_id} {id}");
136 }
137 }
138
139 let appservice_user = ®istration_info.sender;
140
141 if !self.services.users.exists(appservice_user).await {
142 self.services
143 .users
144 .create(appservice_user, None, None)
145 .await?;
146 }
147
148 registrations.insert(id.clone(), registration_info);
149
150 Ok(())
151 }
152
153 pub async fn register_appservice(&self, registration: Registration) -> Result {
154 self.loaded().await;
155
156 let id = registration.id.clone();
157
158 let appservice_yaml = serde_yaml::to_string(®istration)?;
159
160 self.load_appservice(registration).await?;
161
162 self.db
163 .id_appserviceregistrations
164 .insert(&id, appservice_yaml);
165
166 Ok(())
167 }
168
169 pub async fn unregister_appservice(&self, appservice_id: &str) -> Result {
170 self.loaded().await;
171
172 let mut registrations = self.registration_info.write().await;
173
174 if !registrations.contains_key(appservice_id) {
175 return Err!("Appservice not found");
176 }
177
178 if self
179 .db
180 .id_appserviceregistrations
181 .exists(appservice_id)
182 .await
183 .is_err()
184 {
185 return Err!("Cannot unregister config appservice");
186 }
187
188 registrations
190 .remove(appservice_id)
191 .ok_or_else(|| err!("Appservice not found"))?;
192
193 self.db
195 .id_appserviceregistrations
196 .remove(appservice_id);
197
198 self.services
201 .sending
202 .cleanup_events(Some(appservice_id), None, None)
203 .await
204 }
205
206 pub async fn get_registration(&self, id: &str) -> Option<Registration> {
207 self.registration_info
208 .read()
209 .await
210 .get(id)
211 .cloned()
212 .map(|info| info.registration)
213 }
214
215 pub async fn get_registration_info(&self, id: &str) -> Option<RegistrationInfo> {
218 self.registration_info
219 .read()
220 .await
221 .get(id)
222 .cloned()
223 }
224
225 pub async fn find_from_access_token(&self, token: &str) -> Result<RegistrationInfo> {
226 self.read()
227 .await
228 .values()
229 .find(|info| info.registration.as_token == token)
230 .cloned()
231 .ok_or_else(|| err!(Request(NotFound("Missing or invalid appservice token"))))
232 }
233
234 pub async fn is_exclusive_user_id(&self, user_id: &UserId) -> bool {
236 self.read()
237 .await
238 .values()
239 .any(|info| info.is_exclusive_user_match(user_id))
240 }
241
242 pub async fn is_interested_in_user(&self, user_id: &UserId) -> bool {
244 self.read()
245 .await
246 .values()
247 .any(|info| info.is_user_match(user_id))
248 }
249
250 pub async fn is_exclusive_alias(&self, alias: &RoomAliasId) -> bool {
252 self.read()
253 .await
254 .values()
255 .any(|info| info.aliases.is_exclusive_match(alias.as_str()))
256 }
257
258 pub async fn is_exclusive_room_id(&self, room_id: &RoomId) -> bool {
262 self.read()
263 .await
264 .values()
265 .any(|info| info.rooms.is_exclusive_match(room_id.as_str()))
266 }
267
268 pub fn iter_ids(&self) -> impl Stream<Item = String> + Send {
269 self.read()
270 .map(|info| info.keys().cloned().collect::<Vec<_>>())
271 .map(IntoIterator::into_iter)
272 .map(IterStream::stream)
273 .flatten_stream()
274 }
275
276 pub fn iter_db_ids(&self) -> impl Stream<Item = Result<Registration>> + Send {
277 self.db
278 .id_appserviceregistrations
279 .keys()
280 .and_then(async move |id: &str| Ok(self.get_db_registration(id).await?))
281 }
282
283 pub async fn get_db_registration(&self, id: &str) -> Result<Registration> {
284 self.db
285 .id_appserviceregistrations
286 .get(id)
287 .await
288 .and_then(|ref bytes| serde_yaml::from_slice(bytes).map_err(Into::into))
289 .map_err(|e| err!(Database("Invalid appservice {id:?} registration: {e:?}")))
290 }
291
292 pub fn read(&self) -> impl Future<Output = RwLockReadGuard<'_, Registrations>> + Send {
293 self.registration_info.read()
294 }
295
296 #[inline]
302 pub async fn loaded(&self) { self.loaded.wait().await; }
303}