tuwunel_service/pusher/
send.rs1use ipaddress::IPAddress;
2use ruma::{
3 UInt, UserId,
4 api::{
5 client::push::{Pusher, PusherKind},
6 push_gateway::send_event_notification::{
7 self,
8 v1::{Device, Notification, NotificationCounts, NotificationPriority},
9 },
10 },
11 events::TimelineEventType,
12 push::{Action, PushFormat, Ruleset, Tweak},
13 uint,
14};
15use tuwunel_core::{Err, Result, err, implement, matrix::Event};
16
17#[implement(super::Service)]
18#[tracing::instrument(level = "debug", skip_all)]
19pub async fn send_push_notice<E>(
20 &self,
21 user_id: &UserId,
22 pusher: &Pusher,
23 ruleset: &Ruleset,
24 event: &E,
25) -> Result
26where
27 E: Event,
28{
29 let mut notify = None;
30 let mut tweaks = Vec::new();
31
32 let power_levels = self
33 .services
34 .state_accessor
35 .get_power_levels(event.room_id())
36 .await
37 .ok();
38
39 let serialized = event.to_format();
40 let actions = self
41 .get_actions(user_id, ruleset, power_levels.as_ref(), &serialized, event.room_id())
42 .await;
43
44 for action in actions {
45 let n = match action {
46 | Action::Notify => true,
47 | Action::SetTweak(tweak) => {
48 tweaks.push(tweak.clone());
49 continue;
50 },
51 | _ => false,
52 };
53
54 if notify.is_some() {
55 return Err!(Request(BadJson(
56 r#"Malformed pushrule contains more than one of these actions: ["dont_notify", "notify", "coalesce"]"#
57 )));
58 }
59
60 notify = Some(n);
61 }
62
63 if notify == Some(true) || self.services.config.push_everything {
64 let unread: UInt = self
65 .services
66 .pusher
67 .notification_count(user_id, event.room_id())
68 .await
69 .try_into()
70 .unwrap_or_else(|_| uint!(1));
71
72 self.send_notice(unread, pusher, tweaks, event)
73 .await?;
74 }
75
76 Ok(())
77}
78
79#[implement(super::Service)]
80#[tracing::instrument(level = "debug", skip_all)]
81async fn send_notice<Pdu: Event>(
82 &self,
83 unread: UInt,
84 pusher: &Pusher,
85 tweaks: Vec<Tweak>,
86 event: &Pdu,
87) -> Result {
88 match &pusher.kind {
90 | PusherKind::Http(http) => {
91 let url = &http.url;
92 let url = url::Url::parse(&http.url).map_err(|e| {
93 err!(Request(InvalidParam(
94 warn!(%url, "HTTP pusher URL is not a valid URL: {e}")
95 )))
96 })?;
97
98 if ["http", "https"]
99 .iter()
100 .all(|&scheme| !scheme.eq_ignore_ascii_case(url.scheme()))
101 {
102 return Err!(Request(InvalidParam(
103 warn!(%url, "HTTP pusher URL is not a valid HTTP/HTTPS URL")
104 )));
105 }
106
107 if let Ok(ip) = IPAddress::parse(url.host_str().expect("URL previously validated"))
108 && !self.services.client.valid_cidr_range(&ip)
109 {
110 return Err!(Request(InvalidParam(
111 warn!(%url, "HTTP pusher URL is a forbidden remote address")
112 )));
113 }
114
115 let event_id_only = http.format == Some(PushFormat::EventIdOnly);
117
118 let mut device = Device::new(pusher.ids.app_id.clone(), pusher.ids.pushkey.clone());
119 device.data.data.clone_from(&http.data);
120 device.data.format.clone_from(&http.format);
121
122 if !event_id_only {
124 device.tweaks.clone_from(&tweaks);
125 }
126
127 let d = vec![device];
128 let mut notify = Notification::new(d);
129
130 notify.event_id = Some(event.event_id().to_owned());
131 notify.room_id = Some(event.room_id().to_owned());
132 if http
133 .data
134 .get("org.matrix.msc4076.disable_badge_count")
135 .is_none() && http.data.get("disable_badge_count").is_none()
136 {
137 notify.counts = NotificationCounts::new(unread, uint!(0));
138 } else {
139 notify.counts = NotificationCounts::default();
142 }
143
144 if !event_id_only {
145 if *event.kind() == TimelineEventType::RoomEncrypted
146 || tweaks.iter().any(|t| {
147 matches!(
148 t,
149 Tweak::Highlight(ruma::push::HighlightTweakValue::Yes)
150 | Tweak::Sound(_)
151 )
152 }) {
153 notify.prio = NotificationPriority::High;
154 } else {
155 notify.prio = NotificationPriority::Low;
156 }
157 notify.sender = Some(event.sender().to_owned());
158 notify.event_type = Some(event.kind().to_owned());
159 notify.content = serde_json::value::to_raw_value(event.content()).ok();
160
161 if *event.kind() == TimelineEventType::RoomMember {
162 notify.user_is_target = event.state_key() == Some(event.sender().as_str());
163 }
164
165 notify.sender_display_name = self
166 .services
167 .users
168 .displayname(event.sender())
169 .await
170 .ok();
171
172 notify.room_name = self
173 .services
174 .state_accessor
175 .get_name(event.room_id())
176 .await
177 .ok();
178
179 notify.room_alias = self
180 .services
181 .state_accessor
182 .get_canonical_alias(event.room_id())
183 .await
184 .ok();
185 }
186
187 self.send_request(&http.url, send_event_notification::v1::Request::new(notify))
188 .await?;
189
190 Ok(())
191 },
192 | _ => Ok(()),
195 }
196}