]> Untitled Git - lemmy.git/blob - server/src/api/user.rs
Translated using Weblate (Italian)
[lemmy.git] / server / src / api / user.rs
1 use crate::{
2   api::{
3     check_slurs,
4     claims::Claims,
5     get_user_from_jwt,
6     get_user_from_jwt_opt,
7     is_admin,
8     APIError,
9     Oper,
10     Perform,
11   },
12   apub::ApubObjectType,
13   blocking,
14   captcha_espeak_wav_base64,
15   websocket::{
16     server::{CaptchaItem, CheckCaptcha, JoinUserRoom, SendAllMessage, SendUserRoomMessage},
17     UserOperation,
18     WebsocketInfo,
19   },
20   DbPool,
21   LemmyError,
22 };
23 use bcrypt::verify;
24 use captcha::{gen, Difficulty};
25 use chrono::Duration;
26 use lemmy_db::{
27   comment::*,
28   comment_view::*,
29   community::*,
30   community_view::*,
31   moderator::*,
32   naive_now,
33   password_reset_request::*,
34   post::*,
35   post_view::*,
36   private_message::*,
37   private_message_view::*,
38   site::*,
39   site_view::*,
40   user::*,
41   user_mention::*,
42   user_mention_view::*,
43   user_view::*,
44   Crud,
45   Followable,
46   Joinable,
47   ListingType,
48   SortType,
49 };
50 use lemmy_utils::{
51   generate_actor_keypair,
52   generate_random_string,
53   is_valid_username,
54   make_apub_endpoint,
55   naive_from_unix,
56   remove_slurs,
57   send_email,
58   settings::Settings,
59   EndpointType,
60 };
61 use log::error;
62 use serde::{Deserialize, Serialize};
63 use std::str::FromStr;
64
65 #[derive(Serialize, Deserialize, Debug)]
66 pub struct Login {
67   username_or_email: String,
68   password: String,
69 }
70
71 #[derive(Serialize, Deserialize)]
72 pub struct Register {
73   pub username: String,
74   pub email: Option<String>,
75   pub password: String,
76   pub password_verify: String,
77   pub admin: bool,
78   pub show_nsfw: bool,
79   pub captcha_uuid: Option<String>,
80   pub captcha_answer: Option<String>,
81 }
82
83 #[derive(Serialize, Deserialize)]
84 pub struct GetCaptcha {}
85
86 #[derive(Serialize, Deserialize)]
87 pub struct GetCaptchaResponse {
88   ok: Option<CaptchaResponse>,
89 }
90
91 #[derive(Serialize, Deserialize)]
92 pub struct CaptchaResponse {
93   png: String,         // A Base64 encoded png
94   wav: Option<String>, // A Base64 encoded wav audio
95   uuid: String,
96 }
97
98 #[derive(Serialize, Deserialize)]
99 pub struct SaveUserSettings {
100   show_nsfw: bool,
101   theme: String,
102   default_sort_type: i16,
103   default_listing_type: i16,
104   lang: String,
105   avatar: Option<String>,
106   email: Option<String>,
107   bio: Option<String>,
108   matrix_user_id: Option<String>,
109   new_password: Option<String>,
110   new_password_verify: Option<String>,
111   old_password: Option<String>,
112   show_avatars: bool,
113   send_notifications_to_email: bool,
114   auth: String,
115 }
116
117 #[derive(Serialize, Deserialize)]
118 pub struct LoginResponse {
119   pub jwt: String,
120 }
121
122 #[derive(Serialize, Deserialize)]
123 pub struct GetUserDetails {
124   user_id: Option<i32>,
125   username: Option<String>,
126   sort: String,
127   page: Option<i64>,
128   limit: Option<i64>,
129   community_id: Option<i32>,
130   saved_only: bool,
131   auth: Option<String>,
132 }
133
134 #[derive(Serialize, Deserialize)]
135 pub struct GetUserDetailsResponse {
136   user: UserView,
137   follows: Vec<CommunityFollowerView>,
138   moderates: Vec<CommunityModeratorView>,
139   comments: Vec<CommentView>,
140   posts: Vec<PostView>,
141 }
142
143 #[derive(Serialize, Deserialize)]
144 pub struct GetRepliesResponse {
145   replies: Vec<ReplyView>,
146 }
147
148 #[derive(Serialize, Deserialize)]
149 pub struct GetUserMentionsResponse {
150   mentions: Vec<UserMentionView>,
151 }
152
153 #[derive(Serialize, Deserialize)]
154 pub struct MarkAllAsRead {
155   auth: String,
156 }
157
158 #[derive(Serialize, Deserialize)]
159 pub struct AddAdmin {
160   user_id: i32,
161   added: bool,
162   auth: String,
163 }
164
165 #[derive(Serialize, Deserialize, Clone)]
166 pub struct AddAdminResponse {
167   admins: Vec<UserView>,
168 }
169
170 #[derive(Serialize, Deserialize)]
171 pub struct BanUser {
172   user_id: i32,
173   ban: bool,
174   reason: Option<String>,
175   expires: Option<i64>,
176   auth: String,
177 }
178
179 #[derive(Serialize, Deserialize, Clone)]
180 pub struct BanUserResponse {
181   user: UserView,
182   banned: bool,
183 }
184
185 #[derive(Serialize, Deserialize)]
186 pub struct GetReplies {
187   sort: String,
188   page: Option<i64>,
189   limit: Option<i64>,
190   unread_only: bool,
191   auth: String,
192 }
193
194 #[derive(Serialize, Deserialize)]
195 pub struct GetUserMentions {
196   sort: String,
197   page: Option<i64>,
198   limit: Option<i64>,
199   unread_only: bool,
200   auth: String,
201 }
202
203 #[derive(Serialize, Deserialize)]
204 pub struct MarkUserMentionAsRead {
205   user_mention_id: i32,
206   read: bool,
207   auth: String,
208 }
209
210 #[derive(Serialize, Deserialize, Clone)]
211 pub struct UserMentionResponse {
212   mention: UserMentionView,
213 }
214
215 #[derive(Serialize, Deserialize)]
216 pub struct DeleteAccount {
217   password: String,
218   auth: String,
219 }
220
221 #[derive(Serialize, Deserialize)]
222 pub struct PasswordReset {
223   email: String,
224 }
225
226 #[derive(Serialize, Deserialize, Clone)]
227 pub struct PasswordResetResponse {}
228
229 #[derive(Serialize, Deserialize)]
230 pub struct PasswordChange {
231   token: String,
232   password: String,
233   password_verify: String,
234 }
235
236 #[derive(Serialize, Deserialize)]
237 pub struct CreatePrivateMessage {
238   content: String,
239   pub recipient_id: i32,
240   auth: String,
241 }
242
243 #[derive(Serialize, Deserialize)]
244 pub struct EditPrivateMessage {
245   edit_id: i32,
246   content: String,
247   auth: String,
248 }
249
250 #[derive(Serialize, Deserialize)]
251 pub struct DeletePrivateMessage {
252   edit_id: i32,
253   deleted: bool,
254   auth: String,
255 }
256
257 #[derive(Serialize, Deserialize)]
258 pub struct MarkPrivateMessageAsRead {
259   edit_id: i32,
260   read: bool,
261   auth: String,
262 }
263
264 #[derive(Serialize, Deserialize)]
265 pub struct GetPrivateMessages {
266   unread_only: bool,
267   page: Option<i64>,
268   limit: Option<i64>,
269   auth: String,
270 }
271
272 #[derive(Serialize, Deserialize, Clone)]
273 pub struct PrivateMessagesResponse {
274   messages: Vec<PrivateMessageView>,
275 }
276
277 #[derive(Serialize, Deserialize, Clone)]
278 pub struct PrivateMessageResponse {
279   pub message: PrivateMessageView,
280 }
281
282 #[derive(Serialize, Deserialize, Debug)]
283 pub struct UserJoin {
284   auth: String,
285 }
286
287 #[derive(Serialize, Deserialize, Clone)]
288 pub struct UserJoinResponse {
289   pub user_id: i32,
290 }
291
292 #[async_trait::async_trait(?Send)]
293 impl Perform for Oper<Login> {
294   type Response = LoginResponse;
295
296   async fn perform(
297     &self,
298     pool: &DbPool,
299     _websocket_info: Option<WebsocketInfo>,
300   ) -> Result<LoginResponse, LemmyError> {
301     let data: &Login = &self.data;
302
303     // Fetch that username / email
304     let username_or_email = data.username_or_email.clone();
305     let user = match blocking(pool, move |conn| {
306       User_::find_by_email_or_username(conn, &username_or_email)
307     })
308     .await?
309     {
310       Ok(user) => user,
311       Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
312     };
313
314     // Verify the password
315     let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
316     if !valid {
317       return Err(APIError::err("password_incorrect").into());
318     }
319
320     // Return the jwt
321     Ok(LoginResponse {
322       jwt: Claims::jwt(user, Settings::get().hostname),
323     })
324   }
325 }
326
327 #[async_trait::async_trait(?Send)]
328 impl Perform for Oper<Register> {
329   type Response = LoginResponse;
330
331   async fn perform(
332     &self,
333     pool: &DbPool,
334     websocket_info: Option<WebsocketInfo>,
335   ) -> Result<LoginResponse, LemmyError> {
336     let data: &Register = &self.data;
337
338     // Make sure site has open registration
339     if let Ok(site) = blocking(pool, move |conn| SiteView::read(conn)).await? {
340       let site: SiteView = site;
341       if !site.open_registration {
342         return Err(APIError::err("registration_closed").into());
343       }
344     }
345
346     // Make sure passwords match
347     if data.password != data.password_verify {
348       return Err(APIError::err("passwords_dont_match").into());
349     }
350
351     // If its not the admin, check the captcha
352     if !data.admin && Settings::get().captcha.enabled {
353       match websocket_info {
354         Some(ws) => {
355           let check = ws
356             .chatserver
357             .send(CheckCaptcha {
358               uuid: data
359                 .captcha_uuid
360                 .to_owned()
361                 .unwrap_or_else(|| "".to_string()),
362               answer: data
363                 .captcha_answer
364                 .to_owned()
365                 .unwrap_or_else(|| "".to_string()),
366             })
367             .await?;
368           if !check {
369             return Err(APIError::err("captcha_incorrect").into());
370           }
371         }
372         None => return Err(APIError::err("captcha_incorrect").into()),
373       };
374     }
375
376     check_slurs(&data.username)?;
377
378     // Make sure there are no admins
379     let any_admins = blocking(pool, move |conn| {
380       UserView::admins(conn).map(|a| a.is_empty())
381     })
382     .await??;
383     if data.admin && !any_admins {
384       return Err(APIError::err("admin_already_created").into());
385     }
386
387     let user_keypair = generate_actor_keypair()?;
388     if !is_valid_username(&data.username) {
389       return Err(APIError::err("invalid_username").into());
390     }
391
392     // Register the new user
393     let user_form = UserForm {
394       name: data.username.to_owned(),
395       email: data.email.to_owned(),
396       matrix_user_id: None,
397       avatar: None,
398       password_encrypted: data.password.to_owned(),
399       preferred_username: None,
400       updated: None,
401       admin: data.admin,
402       banned: false,
403       show_nsfw: data.show_nsfw,
404       theme: "darkly".into(),
405       default_sort_type: SortType::Hot as i16,
406       default_listing_type: ListingType::Subscribed as i16,
407       lang: "browser".into(),
408       show_avatars: true,
409       send_notifications_to_email: false,
410       actor_id: make_apub_endpoint(EndpointType::User, &data.username).to_string(),
411       bio: None,
412       local: true,
413       private_key: Some(user_keypair.private_key),
414       public_key: Some(user_keypair.public_key),
415       last_refreshed_at: None,
416     };
417
418     // Create the user
419     let inserted_user = match blocking(pool, move |conn| User_::register(conn, &user_form)).await? {
420       Ok(user) => user,
421       Err(e) => {
422         let err_type = if e.to_string()
423           == "duplicate key value violates unique constraint \"user__email_key\""
424         {
425           "email_already_exists"
426         } else {
427           "user_already_exists"
428         };
429
430         return Err(APIError::err(err_type).into());
431       }
432     };
433
434     let main_community_keypair = generate_actor_keypair()?;
435
436     // Create the main community if it doesn't exist
437     let main_community = match blocking(pool, move |conn| Community::read(conn, 2)).await? {
438       Ok(c) => c,
439       Err(_e) => {
440         let default_community_name = "main";
441         let community_form = CommunityForm {
442           name: default_community_name.to_string(),
443           title: "The Default Community".to_string(),
444           description: Some("The Default Community".to_string()),
445           category_id: 1,
446           nsfw: false,
447           creator_id: inserted_user.id,
448           removed: None,
449           deleted: None,
450           updated: None,
451           actor_id: make_apub_endpoint(EndpointType::Community, default_community_name).to_string(),
452           local: true,
453           private_key: Some(main_community_keypair.private_key),
454           public_key: Some(main_community_keypair.public_key),
455           last_refreshed_at: None,
456           published: None,
457         };
458         blocking(pool, move |conn| Community::create(conn, &community_form)).await??
459       }
460     };
461
462     // Sign them up for main community no matter what
463     let community_follower_form = CommunityFollowerForm {
464       community_id: main_community.id,
465       user_id: inserted_user.id,
466     };
467
468     let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
469     if blocking(pool, follow).await?.is_err() {
470       return Err(APIError::err("community_follower_already_exists").into());
471     };
472
473     // If its an admin, add them as a mod and follower to main
474     if data.admin {
475       let community_moderator_form = CommunityModeratorForm {
476         community_id: main_community.id,
477         user_id: inserted_user.id,
478       };
479
480       let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
481       if blocking(pool, join).await?.is_err() {
482         return Err(APIError::err("community_moderator_already_exists").into());
483       }
484     }
485
486     // Return the jwt
487     Ok(LoginResponse {
488       jwt: Claims::jwt(inserted_user, Settings::get().hostname),
489     })
490   }
491 }
492
493 #[async_trait::async_trait(?Send)]
494 impl Perform for Oper<GetCaptcha> {
495   type Response = GetCaptchaResponse;
496
497   async fn perform(
498     &self,
499     _pool: &DbPool,
500     websocket_info: Option<WebsocketInfo>,
501   ) -> Result<Self::Response, LemmyError> {
502     let captcha_settings = Settings::get().captcha;
503
504     if !captcha_settings.enabled {
505       return Ok(GetCaptchaResponse { ok: None });
506     }
507
508     let captcha = match captcha_settings.difficulty.as_str() {
509       "easy" => gen(Difficulty::Easy),
510       "medium" => gen(Difficulty::Medium),
511       "hard" => gen(Difficulty::Hard),
512       _ => gen(Difficulty::Medium),
513     };
514
515     let answer = captcha.chars_as_string();
516
517     let png_byte_array = captcha.as_png().expect("failed to generate captcha");
518
519     let png = base64::encode(png_byte_array);
520
521     let uuid = uuid::Uuid::new_v4().to_string();
522
523     let wav = captcha_espeak_wav_base64(&answer).ok();
524
525     let captcha_item = CaptchaItem {
526       answer,
527       uuid: uuid.to_owned(),
528       expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
529     };
530
531     if let Some(ws) = websocket_info {
532       ws.chatserver.do_send(captcha_item);
533     }
534
535     Ok(GetCaptchaResponse {
536       ok: Some(CaptchaResponse { png, uuid, wav }),
537     })
538   }
539 }
540
541 #[async_trait::async_trait(?Send)]
542 impl Perform for Oper<SaveUserSettings> {
543   type Response = LoginResponse;
544
545   async fn perform(
546     &self,
547     pool: &DbPool,
548     _websocket_info: Option<WebsocketInfo>,
549   ) -> Result<LoginResponse, LemmyError> {
550     let data: &SaveUserSettings = &self.data;
551     let user = get_user_from_jwt(&data.auth, pool).await?;
552
553     let user_id = user.id;
554     let read_user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
555
556     let email = match &data.email {
557       Some(email) => Some(email.to_owned()),
558       None => read_user.email,
559     };
560
561     let bio = match &data.bio {
562       Some(bio) => {
563         if bio.chars().count() <= 300 {
564           Some(bio.to_owned())
565         } else {
566           return Err(APIError::err("bio_length_overflow").into());
567         }
568       }
569       None => read_user.bio,
570     };
571
572     let avatar = match &data.avatar {
573       Some(avatar) => Some(avatar.to_owned()),
574       None => read_user.avatar,
575     };
576
577     let password_encrypted = match &data.new_password {
578       Some(new_password) => {
579         match &data.new_password_verify {
580           Some(new_password_verify) => {
581             // Make sure passwords match
582             if new_password != new_password_verify {
583               return Err(APIError::err("passwords_dont_match").into());
584             }
585
586             // Check the old password
587             match &data.old_password {
588               Some(old_password) => {
589                 let valid: bool =
590                   verify(old_password, &read_user.password_encrypted).unwrap_or(false);
591                 if !valid {
592                   return Err(APIError::err("password_incorrect").into());
593                 }
594                 let new_password = new_password.to_owned();
595                 let user = blocking(pool, move |conn| {
596                   User_::update_password(conn, user_id, &new_password)
597                 })
598                 .await??;
599                 user.password_encrypted
600               }
601               None => return Err(APIError::err("password_incorrect").into()),
602             }
603           }
604           None => return Err(APIError::err("passwords_dont_match").into()),
605         }
606       }
607       None => read_user.password_encrypted,
608     };
609
610     let user_form = UserForm {
611       name: read_user.name,
612       email,
613       matrix_user_id: data.matrix_user_id.to_owned(),
614       avatar,
615       password_encrypted,
616       preferred_username: read_user.preferred_username,
617       updated: Some(naive_now()),
618       admin: read_user.admin,
619       banned: read_user.banned,
620       show_nsfw: data.show_nsfw,
621       theme: data.theme.to_owned(),
622       default_sort_type: data.default_sort_type,
623       default_listing_type: data.default_listing_type,
624       lang: data.lang.to_owned(),
625       show_avatars: data.show_avatars,
626       send_notifications_to_email: data.send_notifications_to_email,
627       actor_id: read_user.actor_id,
628       bio,
629       local: read_user.local,
630       private_key: read_user.private_key,
631       public_key: read_user.public_key,
632       last_refreshed_at: None,
633     };
634
635     let res = blocking(pool, move |conn| User_::update(conn, user_id, &user_form)).await?;
636     let updated_user: User_ = match res {
637       Ok(user) => user,
638       Err(e) => {
639         let err_type = if e.to_string()
640           == "duplicate key value violates unique constraint \"user__email_key\""
641         {
642           "email_already_exists"
643         } else {
644           "user_already_exists"
645         };
646
647         return Err(APIError::err(err_type).into());
648       }
649     };
650
651     // Return the jwt
652     Ok(LoginResponse {
653       jwt: Claims::jwt(updated_user, Settings::get().hostname),
654     })
655   }
656 }
657
658 #[async_trait::async_trait(?Send)]
659 impl Perform for Oper<GetUserDetails> {
660   type Response = GetUserDetailsResponse;
661
662   async fn perform(
663     &self,
664     pool: &DbPool,
665     _websocket_info: Option<WebsocketInfo>,
666   ) -> Result<GetUserDetailsResponse, LemmyError> {
667     let data: &GetUserDetails = &self.data;
668     let user = get_user_from_jwt_opt(&data.auth, pool).await?;
669
670     let show_nsfw = match &user {
671       Some(user) => user.show_nsfw,
672       None => false,
673     };
674
675     let sort = SortType::from_str(&data.sort)?;
676
677     let username = data
678       .username
679       .to_owned()
680       .unwrap_or_else(|| "admin".to_string());
681     let user_details_id = match data.user_id {
682       Some(id) => id,
683       None => {
684         let user = blocking(pool, move |conn| User_::read_from_name(conn, &username)).await?;
685         match user {
686           Ok(user) => user.id,
687           Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
688         }
689       }
690     };
691
692     let mut user_view = blocking(pool, move |conn| UserView::read(conn, user_details_id)).await??;
693
694     let page = data.page;
695     let limit = data.limit;
696     let saved_only = data.saved_only;
697     let community_id = data.community_id;
698     let user_id = user.map(|u| u.id);
699     let (posts, comments) = blocking(pool, move |conn| {
700       let mut posts_query = PostQueryBuilder::create(conn)
701         .sort(&sort)
702         .show_nsfw(show_nsfw)
703         .saved_only(saved_only)
704         .for_community_id(community_id)
705         .my_user_id(user_id)
706         .page(page)
707         .limit(limit);
708
709       let mut comments_query = CommentQueryBuilder::create(conn)
710         .sort(&sort)
711         .saved_only(saved_only)
712         .my_user_id(user_id)
713         .page(page)
714         .limit(limit);
715
716       // If its saved only, you don't care what creator it was
717       // Or, if its not saved, then you only want it for that specific creator
718       if !saved_only {
719         posts_query = posts_query.for_creator_id(user_details_id);
720         comments_query = comments_query.for_creator_id(user_details_id);
721       }
722
723       let posts = posts_query.list()?;
724       let comments = comments_query.list()?;
725
726       Ok((posts, comments)) as Result<_, LemmyError>
727     })
728     .await??;
729
730     let follows = blocking(pool, move |conn| {
731       CommunityFollowerView::for_user(conn, user_details_id)
732     })
733     .await??;
734     let moderates = blocking(pool, move |conn| {
735       CommunityModeratorView::for_user(conn, user_details_id)
736     })
737     .await??;
738
739     // If its not the same user, remove the email, and settings
740     // TODO an if let chain would be better here, but can't figure it out
741     // TODO separate out settings into its own thing
742     if user_id.is_none() || user_details_id != user_id.unwrap_or(0) {
743       user_view.email = None;
744     }
745
746     // Return the jwt
747     Ok(GetUserDetailsResponse {
748       user: user_view,
749       follows,
750       moderates,
751       comments,
752       posts,
753     })
754   }
755 }
756
757 #[async_trait::async_trait(?Send)]
758 impl Perform for Oper<AddAdmin> {
759   type Response = AddAdminResponse;
760
761   async fn perform(
762     &self,
763     pool: &DbPool,
764     websocket_info: Option<WebsocketInfo>,
765   ) -> Result<AddAdminResponse, LemmyError> {
766     let data: &AddAdmin = &self.data;
767     let user = get_user_from_jwt(&data.auth, pool).await?;
768
769     // Make sure user is an admin
770     is_admin(pool, user.id).await?;
771
772     let added = data.added;
773     let added_user_id = data.user_id;
774     let add_admin = move |conn: &'_ _| User_::add_admin(conn, added_user_id, added);
775     if blocking(pool, add_admin).await?.is_err() {
776       return Err(APIError::err("couldnt_update_user").into());
777     }
778
779     // Mod tables
780     let form = ModAddForm {
781       mod_user_id: user.id,
782       other_user_id: data.user_id,
783       removed: Some(!data.added),
784     };
785
786     blocking(pool, move |conn| ModAdd::create(conn, &form)).await??;
787
788     let site_creator_id =
789       blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
790
791     let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
792     let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
793     let creator_user = admins.remove(creator_index);
794     admins.insert(0, creator_user);
795
796     let res = AddAdminResponse { admins };
797
798     if let Some(ws) = websocket_info {
799       ws.chatserver.do_send(SendAllMessage {
800         op: UserOperation::AddAdmin,
801         response: res.clone(),
802         my_id: ws.id,
803       });
804     }
805
806     Ok(res)
807   }
808 }
809
810 #[async_trait::async_trait(?Send)]
811 impl Perform for Oper<BanUser> {
812   type Response = BanUserResponse;
813
814   async fn perform(
815     &self,
816     pool: &DbPool,
817     websocket_info: Option<WebsocketInfo>,
818   ) -> Result<BanUserResponse, LemmyError> {
819     let data: &BanUser = &self.data;
820     let user = get_user_from_jwt(&data.auth, pool).await?;
821
822     // Make sure user is an admin
823     is_admin(pool, user.id).await?;
824
825     let ban = data.ban;
826     let banned_user_id = data.user_id;
827     let ban_user = move |conn: &'_ _| User_::ban_user(conn, banned_user_id, ban);
828     if blocking(pool, ban_user).await?.is_err() {
829       return Err(APIError::err("couldnt_update_user").into());
830     }
831
832     // Mod tables
833     let expires = match data.expires {
834       Some(time) => Some(naive_from_unix(time)),
835       None => None,
836     };
837
838     let form = ModBanForm {
839       mod_user_id: user.id,
840       other_user_id: data.user_id,
841       reason: data.reason.to_owned(),
842       banned: Some(data.ban),
843       expires,
844     };
845
846     blocking(pool, move |conn| ModBan::create(conn, &form)).await??;
847
848     let user_id = data.user_id;
849     let user_view = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
850
851     let res = BanUserResponse {
852       user: user_view,
853       banned: data.ban,
854     };
855
856     if let Some(ws) = websocket_info {
857       ws.chatserver.do_send(SendAllMessage {
858         op: UserOperation::BanUser,
859         response: res.clone(),
860         my_id: ws.id,
861       });
862     }
863
864     Ok(res)
865   }
866 }
867
868 #[async_trait::async_trait(?Send)]
869 impl Perform for Oper<GetReplies> {
870   type Response = GetRepliesResponse;
871
872   async fn perform(
873     &self,
874     pool: &DbPool,
875     _websocket_info: Option<WebsocketInfo>,
876   ) -> Result<GetRepliesResponse, LemmyError> {
877     let data: &GetReplies = &self.data;
878     let user = get_user_from_jwt(&data.auth, pool).await?;
879
880     let sort = SortType::from_str(&data.sort)?;
881
882     let page = data.page;
883     let limit = data.limit;
884     let unread_only = data.unread_only;
885     let user_id = user.id;
886     let replies = blocking(pool, move |conn| {
887       ReplyQueryBuilder::create(conn, user_id)
888         .sort(&sort)
889         .unread_only(unread_only)
890         .page(page)
891         .limit(limit)
892         .list()
893     })
894     .await??;
895
896     Ok(GetRepliesResponse { replies })
897   }
898 }
899
900 #[async_trait::async_trait(?Send)]
901 impl Perform for Oper<GetUserMentions> {
902   type Response = GetUserMentionsResponse;
903
904   async fn perform(
905     &self,
906     pool: &DbPool,
907     _websocket_info: Option<WebsocketInfo>,
908   ) -> Result<GetUserMentionsResponse, LemmyError> {
909     let data: &GetUserMentions = &self.data;
910     let user = get_user_from_jwt(&data.auth, pool).await?;
911
912     let sort = SortType::from_str(&data.sort)?;
913
914     let page = data.page;
915     let limit = data.limit;
916     let unread_only = data.unread_only;
917     let user_id = user.id;
918     let mentions = blocking(pool, move |conn| {
919       UserMentionQueryBuilder::create(conn, user_id)
920         .sort(&sort)
921         .unread_only(unread_only)
922         .page(page)
923         .limit(limit)
924         .list()
925     })
926     .await??;
927
928     Ok(GetUserMentionsResponse { mentions })
929   }
930 }
931
932 #[async_trait::async_trait(?Send)]
933 impl Perform for Oper<MarkUserMentionAsRead> {
934   type Response = UserMentionResponse;
935
936   async fn perform(
937     &self,
938     pool: &DbPool,
939     _websocket_info: Option<WebsocketInfo>,
940   ) -> Result<UserMentionResponse, LemmyError> {
941     let data: &MarkUserMentionAsRead = &self.data;
942     let user = get_user_from_jwt(&data.auth, pool).await?;
943
944     let user_mention_id = data.user_mention_id;
945     let read_user_mention =
946       blocking(pool, move |conn| UserMention::read(conn, user_mention_id)).await??;
947
948     if user.id != read_user_mention.recipient_id {
949       return Err(APIError::err("couldnt_update_comment").into());
950     }
951
952     let user_mention_id = read_user_mention.id;
953     let read = data.read;
954     let update_mention = move |conn: &'_ _| UserMention::update_read(conn, user_mention_id, read);
955     if blocking(pool, update_mention).await?.is_err() {
956       return Err(APIError::err("couldnt_update_comment").into());
957     };
958
959     let user_mention_id = read_user_mention.id;
960     let user_id = user.id;
961     let user_mention_view = blocking(pool, move |conn| {
962       UserMentionView::read(conn, user_mention_id, user_id)
963     })
964     .await??;
965
966     Ok(UserMentionResponse {
967       mention: user_mention_view,
968     })
969   }
970 }
971
972 #[async_trait::async_trait(?Send)]
973 impl Perform for Oper<MarkAllAsRead> {
974   type Response = GetRepliesResponse;
975
976   async fn perform(
977     &self,
978     pool: &DbPool,
979     _websocket_info: Option<WebsocketInfo>,
980   ) -> Result<GetRepliesResponse, LemmyError> {
981     let data: &MarkAllAsRead = &self.data;
982     let user = get_user_from_jwt(&data.auth, pool).await?;
983
984     let user_id = user.id;
985     let replies = blocking(pool, move |conn| {
986       ReplyQueryBuilder::create(conn, user_id)
987         .unread_only(true)
988         .page(1)
989         .limit(999)
990         .list()
991     })
992     .await??;
993
994     // TODO: this should probably be a bulk operation
995     // Not easy to do as a bulk operation,
996     // because recipient_id isn't in the comment table
997     for reply in &replies {
998       let reply_id = reply.id;
999       let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
1000       if blocking(pool, mark_as_read).await?.is_err() {
1001         return Err(APIError::err("couldnt_update_comment").into());
1002       }
1003     }
1004
1005     // Mark all user mentions as read
1006     let update_user_mentions = move |conn: &'_ _| UserMention::mark_all_as_read(conn, user_id);
1007     if blocking(pool, update_user_mentions).await?.is_err() {
1008       return Err(APIError::err("couldnt_update_comment").into());
1009     }
1010
1011     // Mark all private_messages as read
1012     let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, user_id);
1013     if blocking(pool, update_pm).await?.is_err() {
1014       return Err(APIError::err("couldnt_update_private_message").into());
1015     }
1016
1017     Ok(GetRepliesResponse { replies: vec![] })
1018   }
1019 }
1020
1021 #[async_trait::async_trait(?Send)]
1022 impl Perform for Oper<DeleteAccount> {
1023   type Response = LoginResponse;
1024
1025   async fn perform(
1026     &self,
1027     pool: &DbPool,
1028     _websocket_info: Option<WebsocketInfo>,
1029   ) -> Result<LoginResponse, LemmyError> {
1030     let data: &DeleteAccount = &self.data;
1031     let user = get_user_from_jwt(&data.auth, pool).await?;
1032
1033     // Verify the password
1034     let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
1035     if !valid {
1036       return Err(APIError::err("password_incorrect").into());
1037     }
1038
1039     // Comments
1040     let user_id = user.id;
1041     let comments = blocking(pool, move |conn| {
1042       CommentQueryBuilder::create(conn)
1043         .for_creator_id(user_id)
1044         .limit(std::i64::MAX)
1045         .list()
1046     })
1047     .await??;
1048
1049     // TODO: this should probably be a bulk operation
1050     for comment in &comments {
1051       let comment_id = comment.id;
1052       let permadelete = move |conn: &'_ _| Comment::permadelete(conn, comment_id);
1053       if blocking(pool, permadelete).await?.is_err() {
1054         return Err(APIError::err("couldnt_update_comment").into());
1055       }
1056     }
1057
1058     // Posts
1059     let posts = blocking(pool, move |conn| {
1060       PostQueryBuilder::create(conn)
1061         .sort(&SortType::New)
1062         .for_creator_id(user_id)
1063         .limit(std::i64::MAX)
1064         .list()
1065     })
1066     .await??;
1067
1068     // TODO: this should probably be a bulk operation
1069     for post in &posts {
1070       let post_id = post.id;
1071       let permadelete = move |conn: &'_ _| Post::permadelete(conn, post_id);
1072       if blocking(pool, permadelete).await?.is_err() {
1073         return Err(APIError::err("couldnt_update_post").into());
1074       }
1075     }
1076
1077     Ok(LoginResponse {
1078       jwt: data.auth.to_owned(),
1079     })
1080   }
1081 }
1082
1083 #[async_trait::async_trait(?Send)]
1084 impl Perform for Oper<PasswordReset> {
1085   type Response = PasswordResetResponse;
1086
1087   async fn perform(
1088     &self,
1089     pool: &DbPool,
1090     _websocket_info: Option<WebsocketInfo>,
1091   ) -> Result<PasswordResetResponse, LemmyError> {
1092     let data: &PasswordReset = &self.data;
1093
1094     // Fetch that email
1095     let email = data.email.clone();
1096     let user = match blocking(pool, move |conn| User_::find_by_email(conn, &email)).await? {
1097       Ok(user) => user,
1098       Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
1099     };
1100
1101     // Generate a random token
1102     let token = generate_random_string();
1103
1104     // Insert the row
1105     let token2 = token.clone();
1106     let user_id = user.id;
1107     blocking(pool, move |conn| {
1108       PasswordResetRequest::create_token(conn, user_id, &token2)
1109     })
1110     .await??;
1111
1112     // Email the pure token to the user.
1113     // TODO no i18n support here.
1114     let user_email = &user.email.expect("email");
1115     let subject = &format!("Password reset for {}", user.name);
1116     let hostname = &format!("https://{}", Settings::get().hostname); //TODO add https for now.
1117     let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", user.name, hostname, &token);
1118     match send_email(subject, user_email, &user.name, html) {
1119       Ok(_o) => _o,
1120       Err(_e) => return Err(APIError::err(&_e).into()),
1121     };
1122
1123     Ok(PasswordResetResponse {})
1124   }
1125 }
1126
1127 #[async_trait::async_trait(?Send)]
1128 impl Perform for Oper<PasswordChange> {
1129   type Response = LoginResponse;
1130
1131   async fn perform(
1132     &self,
1133     pool: &DbPool,
1134     _websocket_info: Option<WebsocketInfo>,
1135   ) -> Result<LoginResponse, LemmyError> {
1136     let data: &PasswordChange = &self.data;
1137
1138     // Fetch the user_id from the token
1139     let token = data.token.clone();
1140     let user_id = blocking(pool, move |conn| {
1141       PasswordResetRequest::read_from_token(conn, &token).map(|p| p.user_id)
1142     })
1143     .await??;
1144
1145     // Make sure passwords match
1146     if data.password != data.password_verify {
1147       return Err(APIError::err("passwords_dont_match").into());
1148     }
1149
1150     // Update the user with the new password
1151     let password = data.password.clone();
1152     let updated_user = match blocking(pool, move |conn| {
1153       User_::update_password(conn, user_id, &password)
1154     })
1155     .await?
1156     {
1157       Ok(user) => user,
1158       Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
1159     };
1160
1161     // Return the jwt
1162     Ok(LoginResponse {
1163       jwt: Claims::jwt(updated_user, Settings::get().hostname),
1164     })
1165   }
1166 }
1167
1168 #[async_trait::async_trait(?Send)]
1169 impl Perform for Oper<CreatePrivateMessage> {
1170   type Response = PrivateMessageResponse;
1171
1172   async fn perform(
1173     &self,
1174     pool: &DbPool,
1175     websocket_info: Option<WebsocketInfo>,
1176   ) -> Result<PrivateMessageResponse, LemmyError> {
1177     let data: &CreatePrivateMessage = &self.data;
1178     let user = get_user_from_jwt(&data.auth, pool).await?;
1179
1180     let hostname = &format!("https://{}", Settings::get().hostname);
1181
1182     let content_slurs_removed = remove_slurs(&data.content.to_owned());
1183
1184     let private_message_form = PrivateMessageForm {
1185       content: content_slurs_removed.to_owned(),
1186       creator_id: user.id,
1187       recipient_id: data.recipient_id,
1188       deleted: None,
1189       read: None,
1190       updated: None,
1191       ap_id: "http://fake.com".into(),
1192       local: true,
1193       published: None,
1194     };
1195
1196     let inserted_private_message = match blocking(pool, move |conn| {
1197       PrivateMessage::create(conn, &private_message_form)
1198     })
1199     .await?
1200     {
1201       Ok(private_message) => private_message,
1202       Err(_e) => {
1203         return Err(APIError::err("couldnt_create_private_message").into());
1204       }
1205     };
1206
1207     let inserted_private_message_id = inserted_private_message.id;
1208     let updated_private_message = match blocking(pool, move |conn| {
1209       let apub_id = make_apub_endpoint(
1210         EndpointType::PrivateMessage,
1211         &inserted_private_message_id.to_string(),
1212       )
1213       .to_string();
1214       PrivateMessage::update_ap_id(&conn, inserted_private_message_id, apub_id)
1215     })
1216     .await?
1217     {
1218       Ok(private_message) => private_message,
1219       Err(_e) => return Err(APIError::err("couldnt_create_private_message").into()),
1220     };
1221
1222     updated_private_message
1223       .send_create(&user, &self.client, pool)
1224       .await?;
1225
1226     // Send notifications to the recipient
1227     let recipient_id = data.recipient_id;
1228     let recipient_user = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
1229     if recipient_user.send_notifications_to_email {
1230       if let Some(email) = recipient_user.email {
1231         let subject = &format!(
1232           "{} - Private Message from {}",
1233           Settings::get().hostname,
1234           user.name,
1235         );
1236         let html = &format!(
1237           "<h1>Private Message</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
1238           user.name, &content_slurs_removed, hostname
1239         );
1240         match send_email(subject, &email, &recipient_user.name, html) {
1241           Ok(_o) => _o,
1242           Err(e) => error!("{}", e),
1243         };
1244       }
1245     }
1246
1247     let message = blocking(pool, move |conn| {
1248       PrivateMessageView::read(conn, inserted_private_message.id)
1249     })
1250     .await??;
1251
1252     let res = PrivateMessageResponse { message };
1253
1254     if let Some(ws) = websocket_info {
1255       ws.chatserver.do_send(SendUserRoomMessage {
1256         op: UserOperation::CreatePrivateMessage,
1257         response: res.clone(),
1258         recipient_id: recipient_user.id,
1259         my_id: ws.id,
1260       });
1261     }
1262
1263     Ok(res)
1264   }
1265 }
1266
1267 #[async_trait::async_trait(?Send)]
1268 impl Perform for Oper<EditPrivateMessage> {
1269   type Response = PrivateMessageResponse;
1270
1271   async fn perform(
1272     &self,
1273     pool: &DbPool,
1274     websocket_info: Option<WebsocketInfo>,
1275   ) -> Result<PrivateMessageResponse, LemmyError> {
1276     let data: &EditPrivateMessage = &self.data;
1277     let user = get_user_from_jwt(&data.auth, pool).await?;
1278
1279     // Checking permissions
1280     let edit_id = data.edit_id;
1281     let orig_private_message =
1282       blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
1283     if user.id != orig_private_message.creator_id {
1284       return Err(APIError::err("no_private_message_edit_allowed").into());
1285     }
1286
1287     // Doing the update
1288     let content_slurs_removed = remove_slurs(&data.content);
1289     let edit_id = data.edit_id;
1290     let updated_private_message = match blocking(pool, move |conn| {
1291       PrivateMessage::update_content(conn, edit_id, &content_slurs_removed)
1292     })
1293     .await?
1294     {
1295       Ok(private_message) => private_message,
1296       Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1297     };
1298
1299     // Send the apub update
1300     updated_private_message
1301       .send_update(&user, &self.client, pool)
1302       .await?;
1303
1304     let edit_id = data.edit_id;
1305     let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
1306     let recipient_id = message.recipient_id;
1307
1308     let res = PrivateMessageResponse { message };
1309
1310     if let Some(ws) = websocket_info {
1311       ws.chatserver.do_send(SendUserRoomMessage {
1312         op: UserOperation::EditPrivateMessage,
1313         response: res.clone(),
1314         recipient_id,
1315         my_id: ws.id,
1316       });
1317     }
1318
1319     Ok(res)
1320   }
1321 }
1322
1323 #[async_trait::async_trait(?Send)]
1324 impl Perform for Oper<DeletePrivateMessage> {
1325   type Response = PrivateMessageResponse;
1326
1327   async fn perform(
1328     &self,
1329     pool: &DbPool,
1330     websocket_info: Option<WebsocketInfo>,
1331   ) -> Result<PrivateMessageResponse, LemmyError> {
1332     let data: &DeletePrivateMessage = &self.data;
1333     let user = get_user_from_jwt(&data.auth, pool).await?;
1334
1335     // Checking permissions
1336     let edit_id = data.edit_id;
1337     let orig_private_message =
1338       blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
1339     if user.id != orig_private_message.creator_id {
1340       return Err(APIError::err("no_private_message_edit_allowed").into());
1341     }
1342
1343     // Doing the update
1344     let edit_id = data.edit_id;
1345     let deleted = data.deleted;
1346     let updated_private_message = match blocking(pool, move |conn| {
1347       PrivateMessage::update_deleted(conn, edit_id, deleted)
1348     })
1349     .await?
1350     {
1351       Ok(private_message) => private_message,
1352       Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1353     };
1354
1355     // Send the apub update
1356     if data.deleted {
1357       updated_private_message
1358         .send_delete(&user, &self.client, pool)
1359         .await?;
1360     } else {
1361       updated_private_message
1362         .send_undo_delete(&user, &self.client, pool)
1363         .await?;
1364     }
1365
1366     let edit_id = data.edit_id;
1367     let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
1368     let recipient_id = message.recipient_id;
1369
1370     let res = PrivateMessageResponse { message };
1371
1372     if let Some(ws) = websocket_info {
1373       ws.chatserver.do_send(SendUserRoomMessage {
1374         op: UserOperation::DeletePrivateMessage,
1375         response: res.clone(),
1376         recipient_id,
1377         my_id: ws.id,
1378       });
1379     }
1380
1381     Ok(res)
1382   }
1383 }
1384
1385 #[async_trait::async_trait(?Send)]
1386 impl Perform for Oper<MarkPrivateMessageAsRead> {
1387   type Response = PrivateMessageResponse;
1388
1389   async fn perform(
1390     &self,
1391     pool: &DbPool,
1392     websocket_info: Option<WebsocketInfo>,
1393   ) -> Result<PrivateMessageResponse, LemmyError> {
1394     let data: &MarkPrivateMessageAsRead = &self.data;
1395     let user = get_user_from_jwt(&data.auth, pool).await?;
1396
1397     // Checking permissions
1398     let edit_id = data.edit_id;
1399     let orig_private_message =
1400       blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
1401     if user.id != orig_private_message.recipient_id {
1402       return Err(APIError::err("couldnt_update_private_message").into());
1403     }
1404
1405     // Doing the update
1406     let edit_id = data.edit_id;
1407     let read = data.read;
1408     match blocking(pool, move |conn| {
1409       PrivateMessage::update_read(conn, edit_id, read)
1410     })
1411     .await?
1412     {
1413       Ok(private_message) => private_message,
1414       Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1415     };
1416
1417     // No need to send an apub update
1418
1419     let edit_id = data.edit_id;
1420     let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
1421     let recipient_id = message.recipient_id;
1422
1423     let res = PrivateMessageResponse { message };
1424
1425     if let Some(ws) = websocket_info {
1426       ws.chatserver.do_send(SendUserRoomMessage {
1427         op: UserOperation::MarkPrivateMessageAsRead,
1428         response: res.clone(),
1429         recipient_id,
1430         my_id: ws.id,
1431       });
1432     }
1433
1434     Ok(res)
1435   }
1436 }
1437
1438 #[async_trait::async_trait(?Send)]
1439 impl Perform for Oper<GetPrivateMessages> {
1440   type Response = PrivateMessagesResponse;
1441
1442   async fn perform(
1443     &self,
1444     pool: &DbPool,
1445     _websocket_info: Option<WebsocketInfo>,
1446   ) -> Result<PrivateMessagesResponse, LemmyError> {
1447     let data: &GetPrivateMessages = &self.data;
1448     let user = get_user_from_jwt(&data.auth, pool).await?;
1449     let user_id = user.id;
1450
1451     let page = data.page;
1452     let limit = data.limit;
1453     let unread_only = data.unread_only;
1454     let messages = blocking(pool, move |conn| {
1455       PrivateMessageQueryBuilder::create(&conn, user_id)
1456         .page(page)
1457         .limit(limit)
1458         .unread_only(unread_only)
1459         .list()
1460     })
1461     .await??;
1462
1463     Ok(PrivateMessagesResponse { messages })
1464   }
1465 }
1466
1467 #[async_trait::async_trait(?Send)]
1468 impl Perform for Oper<UserJoin> {
1469   type Response = UserJoinResponse;
1470
1471   async fn perform(
1472     &self,
1473     pool: &DbPool,
1474     websocket_info: Option<WebsocketInfo>,
1475   ) -> Result<UserJoinResponse, LemmyError> {
1476     let data: &UserJoin = &self.data;
1477     let user = get_user_from_jwt(&data.auth, pool).await?;
1478
1479     if let Some(ws) = websocket_info {
1480       if let Some(id) = ws.id {
1481         ws.chatserver.do_send(JoinUserRoom {
1482           user_id: user.id,
1483           id,
1484         });
1485       }
1486     }
1487
1488     Ok(UserJoinResponse { user_id: user.id })
1489   }
1490 }