]> Untitled Git - lemmy.git/blob - server/src/apub/user.rs
Merge branch 'master' into federation
[lemmy.git] / server / src / apub / user.rs
1 use crate::{
2   apub::{
3     activities::send_activity,
4     create_apub_response,
5     extensions::signatures::PublicKey,
6     ActorType,
7     FromApub,
8     PersonExt,
9     ToApub,
10   },
11   convert_datetime,
12   db::{
13     activity::insert_activity,
14     user::{UserForm, User_},
15   },
16   naive_now,
17   routes::DbPoolParam,
18 };
19 use activitystreams::{
20   actor::{properties::ApActorProperties, Person},
21   context,
22   endpoint::EndpointProperties,
23   object::{properties::ObjectProperties, AnyImage, Image},
24   primitives::XsdAnyUri,
25 };
26 use activitystreams_ext::Ext2;
27 use activitystreams_new::{
28   activity::{Follow, Undo},
29   object::Tombstone,
30   prelude::*,
31 };
32 use actix_web::{body::Body, web::Path, HttpResponse, Result};
33 use diesel::PgConnection;
34 use failure::Error;
35 use serde::Deserialize;
36
37 #[derive(Deserialize)]
38 pub struct UserQuery {
39   user_name: String,
40 }
41
42 impl ToApub for User_ {
43   type Response = PersonExt;
44
45   // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
46   fn to_apub(&self, _conn: &PgConnection) -> Result<PersonExt, Error> {
47     // TODO go through all these to_string and to_owned()
48     let mut person = Person::default();
49     let oprops: &mut ObjectProperties = person.as_mut();
50     oprops
51       .set_context_xsd_any_uri(context())?
52       .set_id(self.actor_id.to_string())?
53       .set_name_xsd_string(self.name.to_owned())?
54       .set_published(convert_datetime(self.published))?;
55
56     if let Some(u) = self.updated {
57       oprops.set_updated(convert_datetime(u))?;
58     }
59
60     if let Some(i) = &self.preferred_username {
61       oprops.set_name_xsd_string(i.to_owned())?;
62     }
63
64     if let Some(avatar_url) = &self.avatar {
65       let mut image = Image::new();
66       image
67         .object_props
68         .set_url_xsd_any_uri(avatar_url.to_owned())?;
69       let any_image = AnyImage::from_concrete(image)?;
70       oprops.set_icon_any_image(any_image)?;
71     }
72
73     let mut endpoint_props = EndpointProperties::default();
74
75     endpoint_props.set_shared_inbox(self.get_shared_inbox_url())?;
76
77     let mut actor_props = ApActorProperties::default();
78
79     actor_props
80       .set_inbox(self.get_inbox_url())?
81       .set_outbox(self.get_outbox_url())?
82       .set_endpoints(endpoint_props)?
83       .set_followers(self.get_followers_url())?
84       .set_following(self.get_following_url())?
85       .set_liked(self.get_liked_url())?;
86
87     Ok(Ext2::new(person, actor_props, self.get_public_key_ext()))
88   }
89   fn to_tombstone(&self) -> Result<Tombstone, Error> {
90     unimplemented!()
91   }
92 }
93
94 impl ActorType for User_ {
95   fn actor_id(&self) -> String {
96     self.actor_id.to_owned()
97   }
98
99   fn public_key(&self) -> String {
100     self.public_key.to_owned().unwrap()
101   }
102
103   fn private_key(&self) -> String {
104     self.private_key.to_owned().unwrap()
105   }
106
107   /// As a given local user, send out a follow request to a remote community.
108   fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error> {
109     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
110     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
111     follow.set_context(context()).set_id(id.parse()?);
112     let to = format!("{}/inbox", follow_actor_id);
113
114     insert_activity(&conn, self.id, &follow, true)?;
115
116     send_activity(&follow, self, vec![to])?;
117     Ok(())
118   }
119
120   fn send_unfollow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error> {
121     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
122     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
123     follow.set_context(context()).set_id(id.parse()?);
124
125     let to = format!("{}/inbox", follow_actor_id);
126
127     // TODO
128     // Undo that fake activity
129     let undo_id = format!("{}/undo/follow/{}", self.actor_id, uuid::Uuid::new_v4());
130     let mut undo = Undo::new(self.actor_id.parse::<XsdAnyUri>()?, follow.into_any_base()?);
131     undo.set_context(context()).set_id(undo_id.parse()?);
132
133     insert_activity(&conn, self.id, &undo, true)?;
134
135     send_activity(&undo, self, vec![to])?;
136     Ok(())
137   }
138
139   fn send_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
140     unimplemented!()
141   }
142
143   fn send_undo_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
144     unimplemented!()
145   }
146
147   fn send_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
148     unimplemented!()
149   }
150
151   fn send_undo_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
152     unimplemented!()
153   }
154
155   fn send_accept_follow(&self, _follow: &Follow, _conn: &PgConnection) -> Result<(), Error> {
156     unimplemented!()
157   }
158
159   fn get_follower_inboxes(&self, _conn: &PgConnection) -> Result<Vec<String>, Error> {
160     unimplemented!()
161   }
162 }
163
164 impl FromApub for UserForm {
165   type ApubType = PersonExt;
166   /// Parse an ActivityPub person received from another instance into a Lemmy user.
167   fn from_apub(person: &PersonExt, _conn: &PgConnection) -> Result<Self, Error> {
168     let oprops = &person.inner.object_props;
169     let aprops = &person.ext_one;
170     let public_key: &PublicKey = &person.ext_two.public_key;
171
172     let avatar = match oprops.get_icon_any_image() {
173       Some(any_image) => any_image
174         .to_owned()
175         .into_concrete::<Image>()?
176         .object_props
177         .get_url_xsd_any_uri()
178         .map(|u| u.to_string()),
179       None => None,
180     };
181
182     Ok(UserForm {
183       name: oprops.get_name_xsd_string().unwrap().to_string(),
184       preferred_username: aprops.get_preferred_username().map(|u| u.to_string()),
185       password_encrypted: "".to_string(),
186       admin: false,
187       banned: false,
188       email: None,
189       avatar,
190       updated: oprops
191         .get_updated()
192         .map(|u| u.as_ref().to_owned().naive_local()),
193       show_nsfw: false,
194       theme: "".to_string(),
195       default_sort_type: 0,
196       default_listing_type: 0,
197       lang: "".to_string(),
198       show_avatars: false,
199       send_notifications_to_email: false,
200       matrix_user_id: None,
201       actor_id: oprops.get_id().unwrap().to_string(),
202       bio: oprops.get_summary_xsd_string().map(|s| s.to_string()),
203       local: false,
204       private_key: None,
205       public_key: Some(public_key.to_owned().public_key_pem),
206       last_refreshed_at: Some(naive_now()),
207     })
208   }
209 }
210
211 /// Return the user json over HTTP.
212 pub async fn get_apub_user_http(
213   info: Path<UserQuery>,
214   db: DbPoolParam,
215 ) -> Result<HttpResponse<Body>, Error> {
216   let user = User_::find_by_email_or_username(&&db.get()?, &info.user_name)?;
217   let u = user.to_apub(&db.get().unwrap())?;
218   Ok(create_apub_response(&u))
219 }