1mod device;
2pub use device::*;
3mod password;
4pub use password::*;
5mod profile;
6pub use profile::*;
7mod filter;
8pub use filter::*;
9mod refresh_token;
10pub use refresh_token::*;
11mod data;
12pub use data::*;
13pub mod key;
14pub mod pusher;
15pub use key::*;
17pub mod key_backup;
18pub mod session;
19pub use key_backup::*;
20pub use session::*;
21pub mod presence;
22pub use presence::*;
23
24use std::collections::BTreeMap;
25use std::mem;
26use std::sync::{Arc, LazyLock, Mutex};
27
28use diesel::dsl::count_distinct;
29use diesel::prelude::*;
30
31use crate::core::client::sync_events;
32use crate::core::events::ignored_user_list::IgnoredUserListEvent;
33use crate::core::events::{AnyStrippedStateEvent, GlobalAccountDataEventType};
34use crate::core::identifiers::*;
35use crate::core::serde::{JsonValue, RawJson};
36use crate::core::Seqnum;
37use crate::core::{OwnedMxcUri, UnixMillis};
38use crate::schema::*;
39use crate::{connect, diesel_exists, DataError, DataResult};
40
41#[derive(Insertable, Identifiable, Queryable, Debug, Clone)]
42#[diesel(table_name = users)]
43pub struct DbUser {
44 pub id: OwnedUserId,
45 pub ty: Option<String>,
46 pub is_admin: bool,
47 pub is_guest: bool,
48 pub appservice_id: Option<String>,
49 pub shadow_banned: bool,
50 pub consent_at: Option<UnixMillis>,
51 pub consent_version: Option<String>,
52 pub consent_server_notice_sent: Option<String>,
53 pub approved_at: Option<UnixMillis>,
54 pub approved_by: Option<OwnedUserId>,
55 pub deactivated_at: Option<UnixMillis>,
56 pub deactivated_by: Option<OwnedUserId>,
57 pub locked_at: Option<UnixMillis>,
58 pub locked_by: Option<OwnedUserId>,
59 pub created_at: UnixMillis,
60}
61
62#[derive(Insertable, AsChangeset, Debug, Clone)]
63#[diesel(table_name = users)]
64pub struct NewDbUser {
65 pub id: OwnedUserId,
66 pub ty: Option<String>,
67 pub is_admin: bool,
68 pub is_guest: bool,
69 pub appservice_id: Option<String>,
70 pub created_at: UnixMillis,
71}
72
73impl DbUser {
74 pub fn is_deactivated(&self) -> bool {
75 self.deactivated_at.is_some()
76 }
77}
78
79#[derive(Identifiable, Queryable, Debug, Clone)]
80#[diesel(table_name = user_access_tokens)]
81pub struct DbAccessToken {
82 pub id: i64,
83 pub user_id: OwnedUserId,
84 pub device_id: OwnedDeviceId,
85 pub token: String,
86 pub puppets_user_id: Option<OwnedUserId>,
87 pub last_validated: Option<UnixMillis>,
88 pub refresh_token_id: Option<i64>,
89 pub is_used: bool,
90 pub expired_at: Option<UnixMillis>,
91 pub created_at: UnixMillis,
92}
93#[derive(Insertable, Debug, Clone)]
94#[diesel(table_name = user_access_tokens)]
95pub struct NewDbAccessToken {
96 pub user_id: OwnedUserId,
97 pub device_id: OwnedDeviceId,
98 pub token: String,
99 pub puppets_user_id: Option<OwnedUserId>,
100 pub last_validated: Option<UnixMillis>,
101 pub refresh_token_id: Option<i64>,
102 pub is_used: bool,
103 pub expired_at: Option<UnixMillis>,
104 pub created_at: UnixMillis,
105}
106
107impl NewDbAccessToken {
108 pub fn new(user_id: OwnedUserId, device_id: OwnedDeviceId, token: String) -> Self {
109 Self {
110 user_id,
111 device_id,
112 token,
113 puppets_user_id: None,
114 last_validated: None,
115 refresh_token_id: None,
116 is_used: false,
117 expired_at: None,
118 created_at: UnixMillis::now(),
119 }
120 }
121}
122
123pub fn joined_rooms(user_id: &UserId, since_sn: Seqnum) -> DataResult<Vec<OwnedRoomId>> {
125 room_users::table
126 .filter(room_users::user_id.eq(user_id))
127 .filter(room_users::membership.eq("join"))
128 .filter(room_users::event_sn.ge(since_sn))
129 .select(room_users::room_id)
130 .load(&mut connect()?)
131 .map_err(Into::into)
132}
133pub fn invited_rooms(
135 user_id: &UserId,
136 since_sn: i64,
137) -> DataResult<Vec<(OwnedRoomId, Vec<RawJson<AnyStrippedStateEvent>>)>> {
138 let list = room_users::table
139 .filter(room_users::user_id.eq(user_id))
140 .filter(room_users::membership.eq("invite"))
141 .filter(room_users::event_sn.ge(since_sn))
142 .select((room_users::room_id, room_users::state_data))
143 .load::<(OwnedRoomId, Option<JsonValue>)>(&mut *connect()?)?
144 .into_iter()
145 .filter_map(|(room_id, state_data)| {
146 if let Some(state_data) = state_data
147 .map(|state_data| serde_json::from_value(state_data).ok())
148 .flatten()
149 {
150 Some((room_id, state_data))
151 } else {
152 None
153 }
154 })
155 .collect();
156 Ok(list)
157}
158
159pub fn knocked_rooms(
160 user_id: &UserId,
161 since_sn: i64,
162) -> DataResult<Vec<(OwnedRoomId, Vec<RawJson<AnyStrippedStateEvent>>)>> {
163 let list = room_users::table
164 .filter(room_users::user_id.eq(user_id))
165 .filter(room_users::membership.eq("knock"))
166 .filter(room_users::event_sn.ge(since_sn))
167 .select((room_users::room_id, room_users::state_data))
168 .load::<(OwnedRoomId, Option<JsonValue>)>(&mut *connect()?)?
169 .into_iter()
170 .filter_map(|(room_id, state_data)| {
171 if let Some(state_data) = state_data
172 .map(|state_data| serde_json::from_value(state_data).ok())
173 .flatten()
174 {
175 Some((room_id, state_data))
176 } else {
177 None
178 }
179 })
180 .collect();
181 Ok(list)
182}
183
184pub fn user_exists(user_id: &UserId) -> DataResult<bool> {
186 let query = users::table.find(user_id);
187 diesel_exists!(query, &mut *connect()?).map_err(Into::into)
188}
189
190pub fn get_user(user_id: &UserId) -> DataResult<Option<DbUser>> {
191 users::table
192 .find(user_id)
193 .first::<DbUser>(&mut *connect()?)
194 .optional()
195 .map_err(Into::into)
196}
197
198pub fn count() -> DataResult<u64> {
200 let count = user_passwords::table
201 .select(count_distinct(user_passwords::user_id))
202 .first::<i64>(&mut *connect()?)?;
203 Ok(count as u64)
204}
205
206pub fn list_local_users() -> DataResult<Vec<OwnedUserId>> {
210 user_passwords::table
211 .select(user_passwords::user_id)
212 .load::<OwnedUserId>(&mut *connect()?)
213 .map_err(Into::into)
214}
215
216pub fn display_name(user_id: &UserId) -> DataResult<Option<String>> {
218 user_profiles::table
219 .filter(user_profiles::user_id.eq(user_id.as_str()))
220 .filter(user_profiles::room_id.is_null())
221 .select(user_profiles::display_name)
222 .first::<Option<String>>(&mut *connect()?)
223 .map_err(Into::into)
224}
225
226pub fn set_display_name(user_id: &UserId, display_name: Option<&str>) -> DataResult<()> {
227 diesel::update(
228 user_profiles::table
229 .filter(user_profiles::user_id.eq(user_id.as_str()))
230 .filter(user_profiles::room_id.is_null()),
231 )
232 .set(user_profiles::display_name.eq(display_name))
233 .execute(&mut connect()?)
234 .map(|_| ())
235 .map_err(Into::into)
236}
237
238pub fn avatar_url(user_id: &UserId) -> DataResult<Option<OwnedMxcUri>> {
240 user_profiles::table
241 .filter(user_profiles::user_id.eq(user_id.as_str()))
242 .filter(user_profiles::room_id.is_null())
243 .select(user_profiles::avatar_url)
244 .first::<Option<OwnedMxcUri>>(&mut *connect()?)
245 .optional()
246 .map(Option::flatten)
247 .map_err(Into::into)
248}
249
250pub fn blurhash(user_id: &UserId) -> DataResult<Option<String>> {
252 user_profiles::table
253 .filter(user_profiles::user_id.eq(user_id.as_str()))
254 .filter(user_profiles::room_id.is_null())
255 .select(user_profiles::blurhash)
256 .first::<Option<String>>(&mut *connect()?)
257 .optional()
258 .map(Option::flatten)
259 .map_err(Into::into)
260}
261
262pub fn clean_signatures<F: Fn(&UserId) -> bool>(
264 cross_signing_key: &mut serde_json::Value,
265 sender_id: Option<&UserId>,
266 user_id: &UserId,
267 allowed_signatures: F,
268) -> DataResult<()> {
269 if let Some(signatures) = cross_signing_key.get_mut("signatures").and_then(|v| v.as_object_mut()) {
270 let new_capacity = signatures.len() / 2;
273 for (user, signature) in mem::replace(signatures, serde_json::Map::with_capacity(new_capacity)) {
274 let sid =
275 <&UserId>::try_from(user.as_str()).map_err(|_| DataError::internal("Invalid user ID in database."))?;
276 if sender_id == Some(user_id) || sid == user_id || allowed_signatures(sid) {
277 signatures.insert(user, signature);
278 }
279 }
280 }
281
282 Ok(())
283}
284
285pub fn deactivate(user_id: &UserId, doer_id: &UserId) -> DataResult<()> {
286 diesel::update(users::table.find(user_id))
287 .set((
288 users::deactivated_at.eq(UnixMillis::now()),
289 users::deactivated_by.eq(doer_id.to_owned()),
290 ))
291 .execute(&mut connect()?)?;
292
293 diesel::delete(user_threepids::table.filter(user_threepids::user_id.eq(user_id))).execute(&mut connect()?)?;
294 diesel::delete(user_access_tokens::table.filter(user_access_tokens::user_id.eq(user_id)))
295 .execute(&mut connect()?)?;
296
297 Ok(())
298}
299
300pub fn user_is_ignored(sender_id: &UserId, recipient_id: &UserId) -> bool {
303 if let Ok(Some(ignored)) = crate::user::data::get_global_data::<IgnoredUserListEvent>(
304 recipient_id,
305 &GlobalAccountDataEventType::IgnoredUserList.to_string(),
306 ) {
307 ignored
308 .content
309 .ignored_users
310 .keys()
311 .any(|blocked_user| blocked_user == sender_id)
312 } else {
313 false
314 }
315}