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