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