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