]> Untitled Git - lemmy.git/blob - server/src/apub/user.rs
Refactor inbox, simplify and split into multiple files
[lemmy.git] / server / src / apub / user.rs
1 use crate::{
2   api::claims::Claims,
3   apub::{
4     activities::send_activity,
5     create_apub_response,
6     insert_activity,
7     ActorType,
8     FromApub,
9     PersonExt,
10     ToApub,
11   },
12   blocking,
13   routes::DbPoolParam,
14   DbPool,
15   LemmyError,
16 };
17 use activitystreams_ext::Ext1;
18 use activitystreams_new::{
19   activity::{Follow, Undo},
20   actor::{ApActor, Endpoints, Person},
21   context,
22   object::{Image, Tombstone},
23   prelude::*,
24 };
25 use actix_web::{body::Body, client::Client, web, HttpResponse};
26 use lemmy_db::{
27   naive_now,
28   user::{UserForm, User_},
29 };
30 use lemmy_utils::convert_datetime;
31 use serde::Deserialize;
32 use url::Url;
33
34 #[derive(Deserialize)]
35 pub struct UserQuery {
36   user_name: String,
37 }
38
39 #[async_trait::async_trait(?Send)]
40 impl ToApub for User_ {
41   type Response = PersonExt;
42
43   // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
44   async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
45     // TODO go through all these to_string and to_owned()
46     let mut person = Person::new();
47     person
48       .set_context(context())
49       .set_id(Url::parse(&self.actor_id)?)
50       .set_name(self.name.to_owned())
51       .set_published(convert_datetime(self.published));
52
53     if let Some(u) = self.updated {
54       person.set_updated(convert_datetime(u));
55     }
56
57     if let Some(avatar_url) = &self.avatar {
58       let mut image = Image::new();
59       image.set_url(avatar_url.to_owned());
60       person.set_icon(image.into_any_base()?);
61     }
62
63     let mut ap_actor = ApActor::new(self.get_inbox_url().parse()?, person);
64     ap_actor
65       .set_outbox(self.get_outbox_url().parse()?)
66       .set_followers(self.get_followers_url().parse()?)
67       .set_following(self.get_following_url().parse()?)
68       .set_liked(self.get_liked_url().parse()?)
69       .set_endpoints(Endpoints {
70         shared_inbox: Some(self.get_shared_inbox_url().parse()?),
71         ..Default::default()
72       });
73
74     if let Some(i) = &self.preferred_username {
75       ap_actor.set_preferred_username(i.to_owned());
76     }
77
78     Ok(Ext1::new(ap_actor, self.get_public_key_ext()))
79   }
80   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
81     unimplemented!()
82   }
83 }
84
85 #[async_trait::async_trait(?Send)]
86 impl ActorType for User_ {
87   fn actor_id_str(&self) -> String {
88     self.actor_id.to_owned()
89   }
90
91   fn public_key(&self) -> String {
92     self.public_key.to_owned().unwrap()
93   }
94
95   fn private_key(&self) -> String {
96     self.private_key.to_owned().unwrap()
97   }
98
99   /// As a given local user, send out a follow request to a remote community.
100   async fn send_follow(
101     &self,
102     follow_actor_id: &str,
103     client: &Client,
104     pool: &DbPool,
105   ) -> Result<(), LemmyError> {
106     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
107     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
108     follow.set_context(context()).set_id(id.parse()?);
109     let to = format!("{}/inbox", follow_actor_id);
110
111     insert_activity(self.id, follow.clone(), true, pool).await?;
112
113     send_activity(client, &follow.into_any_base()?, self, vec![to]).await?;
114     Ok(())
115   }
116
117   async fn send_unfollow(
118     &self,
119     follow_actor_id: &str,
120     client: &Client,
121     pool: &DbPool,
122   ) -> Result<(), LemmyError> {
123     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
124     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
125     follow.set_context(context()).set_id(id.parse()?);
126
127     let to = format!("{}/inbox", follow_actor_id);
128
129     // TODO
130     // Undo that fake activity
131     let undo_id = format!("{}/undo/follow/{}", self.actor_id, uuid::Uuid::new_v4());
132     let mut undo = Undo::new(Url::parse(&self.actor_id)?, follow.into_any_base()?);
133     undo.set_context(context()).set_id(undo_id.parse()?);
134
135     insert_activity(self.id, undo.clone(), true, pool).await?;
136
137     send_activity(client, &undo.into_any_base()?, self, vec![to]).await?;
138     Ok(())
139   }
140
141   async fn send_delete(
142     &self,
143     _creator: &User_,
144     _client: &Client,
145     _pool: &DbPool,
146   ) -> Result<(), LemmyError> {
147     unimplemented!()
148   }
149
150   async fn send_undo_delete(
151     &self,
152     _creator: &User_,
153     _client: &Client,
154     _pool: &DbPool,
155   ) -> Result<(), LemmyError> {
156     unimplemented!()
157   }
158
159   async fn send_remove(
160     &self,
161     _creator: &User_,
162     _client: &Client,
163     _pool: &DbPool,
164   ) -> Result<(), LemmyError> {
165     unimplemented!()
166   }
167
168   async fn send_undo_remove(
169     &self,
170     _creator: &User_,
171     _client: &Client,
172     _pool: &DbPool,
173   ) -> Result<(), LemmyError> {
174     unimplemented!()
175   }
176
177   async fn send_accept_follow(
178     &self,
179     _follow: Follow,
180     _client: &Client,
181     _pool: &DbPool,
182   ) -> Result<(), LemmyError> {
183     unimplemented!()
184   }
185
186   async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<String>, LemmyError> {
187     unimplemented!()
188   }
189
190   fn user_id(&self) -> i32 {
191     self.id
192   }
193 }
194
195 #[async_trait::async_trait(?Send)]
196 impl FromApub for UserForm {
197   type ApubType = PersonExt;
198   /// Parse an ActivityPub person received from another instance into a Lemmy user.
199   async fn from_apub(
200     person: &PersonExt,
201     _: &Client,
202     _: &DbPool,
203     actor_id: &Url,
204   ) -> Result<Self, LemmyError> {
205     let avatar = match person.icon() {
206       Some(any_image) => Image::from_any_base(any_image.as_one().unwrap().clone())
207         .unwrap()
208         .unwrap()
209         .url()
210         .unwrap()
211         .as_single_xsd_any_uri()
212         .map(|u| u.to_string()),
213       None => None,
214     };
215
216     Ok(UserForm {
217       name: person
218         .name()
219         .unwrap()
220         .one()
221         .unwrap()
222         .as_xsd_string()
223         .unwrap()
224         .to_string(),
225       preferred_username: person.inner.preferred_username().map(|u| u.to_string()),
226       password_encrypted: "".to_string(),
227       admin: false,
228       banned: false,
229       email: None,
230       avatar,
231       updated: person.updated().map(|u| u.to_owned().naive_local()),
232       show_nsfw: false,
233       theme: "".to_string(),
234       default_sort_type: 0,
235       default_listing_type: 0,
236       lang: "".to_string(),
237       show_avatars: false,
238       send_notifications_to_email: false,
239       matrix_user_id: None,
240       actor_id: person.id(actor_id.domain().unwrap())?.unwrap().to_string(),
241       bio: person
242         .inner
243         .summary()
244         .map(|s| s.as_single_xsd_string().unwrap().into()),
245       local: false,
246       private_key: None,
247       public_key: Some(person.ext_one.public_key.to_owned().public_key_pem),
248       last_refreshed_at: Some(naive_now()),
249     })
250   }
251 }
252
253 /// Return the user json over HTTP.
254 pub async fn get_apub_user_http(
255   info: web::Path<UserQuery>,
256   db: DbPoolParam,
257 ) -> Result<HttpResponse<Body>, LemmyError> {
258   let user_name = info.into_inner().user_name;
259   let user = blocking(&db, move |conn| {
260     Claims::find_by_email_or_username(conn, &user_name)
261   })
262   .await??;
263   let u = user.to_apub(&db).await?;
264   Ok(create_apub_response(&u))
265 }