Skip to main content

tuwunel_api/client/push/
pushrules.rs

1use axum::extract::State;
2use ruma::{
3	CanonicalJsonObject, CanonicalJsonValue,
4	api::client::push::get_pushrules_all,
5	events::{
6		GlobalAccountDataEventType,
7		push_rules::{PushRulesEvent, PushRulesEventContent},
8	},
9	push::{PredefinedContentRuleId, PredefinedOverrideRuleId, Ruleset},
10};
11use tuwunel_core::{Result, err};
12use tuwunel_service::Services;
13
14use crate::Ruma;
15
16/// # `GET /_matrix/client/r0/pushrules/`
17///
18/// Retrieves the push rules event for this user.
19pub(crate) async fn get_pushrules_all_route(
20	State(services): State<crate::State>,
21	body: Ruma<get_pushrules_all::v3::Request>,
22) -> Result<get_pushrules_all::v3::Response> {
23	let sender_user = body.sender_user();
24
25	let Some(content_value) = services
26		.account_data
27		.get_global::<CanonicalJsonObject>(sender_user, GlobalAccountDataEventType::PushRules)
28		.await
29		.ok()
30		.and_then(|event| event.get("content").cloned())
31		.filter(CanonicalJsonValue::is_object)
32	else {
33		// user somehow has non-existent push rule event. recreate it and return server
34		// default silently
35		return recreate_push_rules_and_return(&services, sender_user).await;
36	};
37
38	let account_data_content =
39		serde_json::from_value::<PushRulesEventContent>(content_value.into()).map_err(|e| {
40			err!(Database(warn!("Invalid push rules account data event in database: {e}")))
41		})?;
42
43	let mut global_ruleset = account_data_content.global;
44
45	// remove old deprecated mentions push rules as per MSC4210
46	// and update the stored server default push rules
47	#[expect(deprecated)]
48	{
49		use ruma::push::RuleKind::*;
50		if global_ruleset
51			.get(Override, PredefinedOverrideRuleId::ContainsDisplayName.as_str())
52			.is_some()
53			|| global_ruleset
54				.get(Override, PredefinedOverrideRuleId::RoomNotif.as_str())
55				.is_some()
56			|| global_ruleset
57				.get(Content, PredefinedContentRuleId::ContainsUserName.as_str())
58				.is_some()
59		{
60			global_ruleset
61				.remove(Override, PredefinedOverrideRuleId::ContainsDisplayName)
62				.ok();
63			global_ruleset
64				.remove(Override, PredefinedOverrideRuleId::RoomNotif)
65				.ok();
66			global_ruleset
67				.remove(Content, PredefinedContentRuleId::ContainsUserName)
68				.ok();
69
70			global_ruleset.update_with_server_default(Ruleset::server_default(sender_user));
71
72			let ty = GlobalAccountDataEventType::PushRules;
73			let event = PushRulesEvent {
74				content: PushRulesEventContent { global: global_ruleset.clone() },
75			};
76
77			services
78				.account_data
79				.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(event)?)
80				.await?;
81		}
82	};
83
84	Ok(get_pushrules_all::v3::Response { global: global_ruleset })
85}
86
87/// user somehow has bad push rules, these must always exist per spec.
88/// so recreate it and return server default silently
89async fn recreate_push_rules_and_return(
90	services: &Services,
91	sender_user: &ruma::UserId,
92) -> Result<get_pushrules_all::v3::Response> {
93	let ty = GlobalAccountDataEventType::PushRules;
94	let event = PushRulesEvent {
95		content: PushRulesEventContent {
96			global: Ruleset::server_default(sender_user),
97		},
98	};
99
100	services
101		.account_data
102		.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(event)?)
103		.await?;
104
105	Ok(get_pushrules_all::v3::Response {
106		global: Ruleset::server_default(sender_user),
107	})
108}