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			|| global_ruleset
60				.get(Override, PredefinedOverrideRuleId::Reply.as_str())
61				.is_none()
62		{
63			global_ruleset
64				.remove(Override, PredefinedOverrideRuleId::ContainsDisplayName)
65				.ok();
66			global_ruleset
67				.remove(Override, PredefinedOverrideRuleId::RoomNotif)
68				.ok();
69			global_ruleset
70				.remove(Content, PredefinedContentRuleId::ContainsUserName)
71				.ok();
72
73			global_ruleset.update_with_server_default(Ruleset::server_default(sender_user));
74
75			let ty = GlobalAccountDataEventType::PushRules;
76			let event = PushRulesEvent {
77				content: PushRulesEventContent { global: global_ruleset.clone() },
78			};
79
80			services
81				.account_data
82				.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(event)?)
83				.await?;
84		}
85	};
86
87	Ok(get_pushrules_all::v3::Response { global: global_ruleset })
88}
89
90/// user somehow has bad push rules, these must always exist per spec.
91/// so recreate it and return server default silently
92async fn recreate_push_rules_and_return(
93	services: &Services,
94	sender_user: &ruma::UserId,
95) -> Result<get_pushrules_all::v3::Response> {
96	let ty = GlobalAccountDataEventType::PushRules;
97	let event = PushRulesEvent {
98		content: PushRulesEventContent {
99			global: Ruleset::server_default(sender_user),
100		},
101	};
102
103	services
104		.account_data
105		.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(event)?)
106		.await?;
107
108	Ok(get_pushrules_all::v3::Response {
109		global: Ruleset::server_default(sender_user),
110	})
111}