1 use actix_web::{web, web::Data};
2 use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
3 use lemmy_utils::{ConnectionId, LemmyError};
4 use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
5 use serde::Deserialize;
6 use std::{env, process::Command};
19 #[async_trait::async_trait(?Send)]
21 type Response: serde::ser::Serialize + Send;
25 context: &Data<LemmyContext>,
26 websocket_id: Option<ConnectionId>,
27 ) -> Result<Self::Response, LemmyError>;
30 pub async fn match_websocket_operation(
31 context: LemmyContext,
35 ) -> Result<String, LemmyError> {
36 //TODO: handle commented out actions in crud crate
40 UserOperation::Login => {
41 //do_websocket_operation::<Login>(context, id, op, data).await
44 UserOperation::Register => {
45 //do_websocket_operation::<Register>(context, id, op, data).await
48 UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
49 UserOperation::GetPersonDetails => {
50 //do_websocket_operation::<GetPersonDetails>(context, id, op, data).await
53 UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
54 UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
55 UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
56 UserOperation::GetPersonMentions => {
57 //do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
60 UserOperation::MarkPersonMentionAsRead => {
61 do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
63 UserOperation::MarkAllAsRead => {
64 do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
66 UserOperation::DeleteAccount => {
67 //do_websocket_operation::<DeleteAccount>(context, id, op, data).await
70 UserOperation::PasswordReset => {
71 do_websocket_operation::<PasswordReset>(context, id, op, data).await
73 UserOperation::PasswordChange => {
74 do_websocket_operation::<PasswordChange>(context, id, op, data).await
76 UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
77 UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
78 UserOperation::CommunityJoin => {
79 do_websocket_operation::<CommunityJoin>(context, id, op, data).await
81 UserOperation::ModJoin => do_websocket_operation::<ModJoin>(context, id, op, data).await,
82 UserOperation::SaveUserSettings => {
83 do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
85 UserOperation::GetReportCount => {
86 do_websocket_operation::<GetReportCount>(context, id, op, data).await
89 // Private Message ops
90 UserOperation::CreatePrivateMessage => {
91 //do_websocket_operation::<CreatePrivateMessage>(context, id, op, data).await
94 UserOperation::EditPrivateMessage => {
95 //do_websocket_operation::<EditPrivateMessage>(context, id, op, data).await
98 UserOperation::DeletePrivateMessage => {
99 //do_websocket_operation::<DeletePrivateMessage>(context, id, op, data).await
102 UserOperation::MarkPrivateMessageAsRead => {
103 do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
105 UserOperation::GetPrivateMessages => {
106 //do_websocket_operation::<GetPrivateMessages>(context, id, op, data).await
111 UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
112 UserOperation::CreateSite => {
113 //do_websocket_operation::<CreateSite>(context, id, op, data).await
116 UserOperation::EditSite => {
117 //do_websocket_operation::<EditSite>(context, id, op, data).await
120 UserOperation::GetSite => {
121 //do_websocket_operation::<GetSite>(context, id, op, data).await
124 UserOperation::GetSiteConfig => {
125 do_websocket_operation::<GetSiteConfig>(context, id, op, data).await
127 UserOperation::SaveSiteConfig => {
128 do_websocket_operation::<SaveSiteConfig>(context, id, op, data).await
130 UserOperation::Search => do_websocket_operation::<Search>(context, id, op, data).await,
131 UserOperation::TransferCommunity => {
132 do_websocket_operation::<TransferCommunity>(context, id, op, data).await
134 UserOperation::TransferSite => {
135 do_websocket_operation::<TransferSite>(context, id, op, data).await
139 UserOperation::GetCommunity => {
140 //do_websocket_operation::<GetCommunity>(context, id, op, data).await
143 UserOperation::ListCommunities => {
144 //do_websocket_operation::<ListCommunities>(context, id, op, data).await
147 UserOperation::CreateCommunity => {
148 //do_websocket_operation::<CreateCommunity>(context, id, op, data).await
151 UserOperation::EditCommunity => {
152 //do_websocket_operation::<EditCommunity>(context, id, op, data).await
155 UserOperation::DeleteCommunity => {
156 //do_websocket_operation::<DeleteCommunity>(context, id, op, data).await
159 UserOperation::RemoveCommunity => {
160 //do_websocket_operation::<RemoveCommunity>(context, id, op, data).await
163 UserOperation::FollowCommunity => {
164 do_websocket_operation::<FollowCommunity>(context, id, op, data).await
166 UserOperation::GetFollowedCommunities => {
167 do_websocket_operation::<GetFollowedCommunities>(context, id, op, data).await
169 UserOperation::BanFromCommunity => {
170 do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
172 UserOperation::AddModToCommunity => {
173 do_websocket_operation::<AddModToCommunity>(context, id, op, data).await
177 UserOperation::CreatePost => {
178 //do_websocket_operation::<CreatePost>(context, id, op, data).await
181 UserOperation::GetPost => {
182 //do_websocket_operation::<GetPost>(context, id, op, data).await
185 UserOperation::GetPosts => {
186 //do_websocket_operation::<GetPosts>(context, id, op, data).await
189 UserOperation::EditPost => {
190 //do_websocket_operation::<EditPost>(context, id, op, data).await
193 UserOperation::DeletePost => {
194 //do_websocket_operation::<DeletePost>(context, id, op, data).await
197 UserOperation::RemovePost => {
198 //do_websocket_operation::<RemovePost>(context, id, op, data).await
201 UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
202 UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
203 UserOperation::CreatePostLike => {
204 do_websocket_operation::<CreatePostLike>(context, id, op, data).await
206 UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
207 UserOperation::CreatePostReport => {
208 do_websocket_operation::<CreatePostReport>(context, id, op, data).await
210 UserOperation::ListPostReports => {
211 do_websocket_operation::<ListPostReports>(context, id, op, data).await
213 UserOperation::ResolvePostReport => {
214 do_websocket_operation::<ResolvePostReport>(context, id, op, data).await
218 UserOperation::CreateComment => {
219 //do_websocket_operation::<CreateComment>(context, id, op, data).await
222 UserOperation::EditComment => {
223 //do_websocket_operation::<EditComment>(context, id, op, data).await
226 UserOperation::DeleteComment => {
227 //do_websocket_operation::<DeleteComment>(context, id, op, data).await
230 UserOperation::RemoveComment => {
231 //do_websocket_operation::<RemoveComment>(context, id, op, data).await
234 UserOperation::MarkCommentAsRead => {
235 do_websocket_operation::<MarkCommentAsRead>(context, id, op, data).await
237 UserOperation::SaveComment => {
238 do_websocket_operation::<SaveComment>(context, id, op, data).await
240 UserOperation::GetComments => {
241 //do_websocket_operation::<GetComments>(context, id, op, data).await
244 UserOperation::CreateCommentLike => {
245 do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
247 UserOperation::CreateCommentReport => {
248 do_websocket_operation::<CreateCommentReport>(context, id, op, data).await
250 UserOperation::ListCommentReports => {
251 do_websocket_operation::<ListCommentReports>(context, id, op, data).await
253 UserOperation::ResolveCommentReport => {
254 do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await
259 async fn do_websocket_operation<'a, 'b, Data>(
260 context: LemmyContext,
264 ) -> Result<String, LemmyError>
266 for<'de> Data: Deserialize<'de> + 'a,
269 let parsed_data: Data = serde_json::from_str(&data)?;
270 let res = parsed_data
271 .perform(&web::Data::new(context), Some(id))
273 serialize_websocket_message(&op, &res)
276 pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyError> {
277 let mut built_text = String::new();
279 // Building proper speech text for espeak
280 for mut c in captcha.chars() {
281 let new_str = if c.is_alphabetic() {
282 if c.is_lowercase() {
283 c.make_ascii_uppercase();
284 format!("lower case {} ... ", c)
286 c.make_ascii_uppercase();
287 format!("capital {} ... ", c)
293 built_text.push_str(&new_str);
296 espeak_wav_base64(&built_text)
299 pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
300 // Make a temp file path
301 let uuid = uuid::Uuid::new_v4().to_string();
302 let file_path = format!(
303 "{}/lemmy_espeak_{}.wav",
304 env::temp_dir().to_string_lossy(),
308 // Write the wav file
309 Command::new("espeak")
315 // Read the wav file bytes
316 let bytes = std::fs::read(&file_path)?;
319 std::fs::remove_file(file_path)?;
322 let base64 = base64::encode(bytes);
329 use crate::{captcha_espeak_wav_base64, check_validator_time};
330 use lemmy_api_common::check_validator_time;
331 use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
332 use lemmy_db_schema::source::{
333 local_user::{LocalUser, LocalUserForm},
334 person::{Person, PersonForm},
336 use lemmy_utils::claims::Claims;
339 fn test_should_not_validate_user_token_after_password_change() {
340 let conn = establish_unpooled_connection();
342 let new_person = PersonForm {
343 name: "Gerry9812".into(),
344 preferred_username: None,
356 last_refreshed_at: None,
358 shared_inbox_url: None,
361 let inserted_person = Person::create(&conn, &new_person).unwrap();
363 let local_user_form = LocalUserForm {
364 person_id: inserted_person.id,
366 matrix_user_id: None,
367 password_encrypted: "123456".to_string(),
371 default_sort_type: None,
372 default_listing_type: None,
375 send_notifications_to_email: None,
378 let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap();
380 let jwt = Claims::jwt(inserted_local_user.id.0).unwrap();
381 let claims = Claims::decode(&jwt).unwrap().claims;
382 let check = check_validator_time(&inserted_local_user.validator_time, &claims);
383 assert!(check.is_ok());
385 // The check should fail, since the validator time is now newer than the jwt issue time
386 let updated_local_user =
387 LocalUser::update_password(&conn, inserted_local_user.id, &"password111").unwrap();
388 let check_after = check_validator_time(&updated_local_user.validator_time, &claims);
389 assert!(check_after.is_err());
391 let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
392 assert_eq!(1, num_deleted);
397 assert!(captcha_espeak_wav_base64("WxRt2l").is_ok())