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