mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-26 13:31:02 +00:00
Rmove FromId
This commit is contained in:
parent
2165c286ae
commit
2804f44a06
12 changed files with 107 additions and 929 deletions
|
@ -318,72 +318,6 @@ fn get_id(json: serde_json::Value) -> Option<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for ActivityPub objects that can be retrieved or constructed from ID.
|
|
||||||
///
|
|
||||||
/// The two functions to implement are `from_activity` to create (and save) a new object
|
|
||||||
/// of this type from its AP representation, and `from_db` to try to find it in the database
|
|
||||||
/// using its ID.
|
|
||||||
///
|
|
||||||
/// When dealing with the "object" field of incoming activities, `Inbox` will try to see if it is
|
|
||||||
/// a full object, and if so, save it with `from_activity`. If it is only an ID, it will try to find
|
|
||||||
/// it in the database with `from_db`, and otherwise dereference (fetch) the full object and parse it
|
|
||||||
/// with `from_activity`.
|
|
||||||
pub trait FromId<C>: Sized {
|
|
||||||
/// The type representing a failure
|
|
||||||
type Error: From<InboxError<Self::Error>> + Debug;
|
|
||||||
|
|
||||||
/// The ActivityPub object type representing Self
|
|
||||||
type Object: activitypub::Object;
|
|
||||||
|
|
||||||
/// Tries to get an instance of `Self` from an ActivityPub ID.
|
|
||||||
///
|
|
||||||
/// # Parameters
|
|
||||||
///
|
|
||||||
/// - `ctx`: a context to get this instance (= a database in which to search)
|
|
||||||
/// - `id`: the ActivityPub ID of the object to find
|
|
||||||
/// - `object`: optional object that will be used if the object was not found in the database
|
|
||||||
/// If absent, the ID will be dereferenced.
|
|
||||||
fn from_id(
|
|
||||||
ctx: &C,
|
|
||||||
id: &str,
|
|
||||||
object: Option<Self::Object>,
|
|
||||||
proxy: Option<&reqwest::Proxy>,
|
|
||||||
) -> Result<Self, (Option<serde_json::Value>, Self::Error)> {
|
|
||||||
match Self::from_db(ctx, id) {
|
|
||||||
Ok(x) => Ok(x),
|
|
||||||
_ => match object {
|
|
||||||
Some(o) => Self::from_activity(ctx, o).map_err(|e| (None, e)),
|
|
||||||
None => Self::from_activity(ctx, Self::deref(id, proxy.cloned())?)
|
|
||||||
.map_err(|e| (None, e)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dereferences an ID
|
|
||||||
fn deref(
|
|
||||||
id: &str,
|
|
||||||
proxy: Option<reqwest::Proxy>,
|
|
||||||
) -> Result<Self::Object, (Option<serde_json::Value>, Self::Error)> {
|
|
||||||
request::get(id, Self::get_sender(), proxy)
|
|
||||||
.map_err(|_| (None, InboxError::DerefError))
|
|
||||||
.and_then(|mut r| {
|
|
||||||
let json: serde_json::Value = r
|
|
||||||
.json()
|
|
||||||
.map_err(|_| (None, InboxError::InvalidObject(None)))?;
|
|
||||||
serde_json::from_value(json.clone())
|
|
||||||
.map_err(|_| (Some(json), InboxError::InvalidObject(None)))
|
|
||||||
})
|
|
||||||
.map_err(|(json, e)| (json, e.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a `Self` from its ActivityPub representation
|
|
||||||
fn from_activity(ctx: &C, activity: Self::Object) -> Result<Self, Self::Error>;
|
|
||||||
|
|
||||||
/// Tries to find a `Self` with a given ID (`id`), using `ctx` (a database)
|
|
||||||
fn from_db(ctx: &C, id: &str) -> Result<Self, Self::Error>;
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer;
|
|
||||||
}
|
|
||||||
/// A trait for ActivityPub objects that can be retrieved or constructed from ID.
|
/// A trait for ActivityPub objects that can be retrieved or constructed from ID.
|
||||||
///
|
///
|
||||||
/// The two functions to implement are `from_activity` to create (and save) a new object
|
/// The two functions to implement are `from_activity` to create (and save) a new object
|
||||||
|
@ -808,22 +742,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyActor;
|
struct MyActor;
|
||||||
impl FromId<()> for MyActor {
|
|
||||||
type Error = ();
|
|
||||||
type Object = Person;
|
|
||||||
|
|
||||||
fn from_db(_: &(), _id: &str) -> Result<Self, Self::Error> {
|
|
||||||
Ok(MyActor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
|
|
||||||
Ok(MyActor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
&*MY_SIGNER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FromId07<()> for MyActor {
|
impl FromId07<()> for MyActor {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
type Object = Person07;
|
type Object = Person07;
|
||||||
|
@ -852,22 +770,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyObject;
|
struct MyObject;
|
||||||
impl FromId<()> for MyObject {
|
|
||||||
type Error = ();
|
|
||||||
type Object = Note;
|
|
||||||
|
|
||||||
fn from_db(_: &(), _id: &str) -> Result<Self, Self::Error> {
|
|
||||||
Ok(MyObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(_: &(), _obj: Note) -> Result<Self, Self::Error> {
|
|
||||||
Ok(MyObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
&*MY_SIGNER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsObject<MyActor, Create, &()> for MyObject {
|
impl AsObject<MyActor, Create, &()> for MyObject {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
@ -1017,15 +919,6 @@ mod tests {
|
||||||
act
|
act
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inbox_basic() {
|
|
||||||
let act = serde_json::to_value(build_create()).unwrap();
|
|
||||||
let res: Result<(), ()> = Inbox::handle(&(), act)
|
|
||||||
.with::<MyActor, Create, MyObject>(None)
|
|
||||||
.done();
|
|
||||||
assert!(res.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inbox_basic07() {
|
fn test_inbox_basic07() {
|
||||||
let act = serde_json::to_value(build_create07()).unwrap();
|
let act = serde_json::to_value(build_create07()).unwrap();
|
||||||
|
@ -1035,18 +928,6 @@ mod tests {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inbox_multi_handlers() {
|
|
||||||
let act = serde_json::to_value(build_create()).unwrap();
|
|
||||||
let res: Result<(), ()> = Inbox::handle(&(), act)
|
|
||||||
.with::<MyActor, Announce, MyObject>(None)
|
|
||||||
.with::<MyActor, Delete, MyObject>(None)
|
|
||||||
.with::<MyActor, Create, MyObject>(None)
|
|
||||||
.with::<MyActor, Like, MyObject>(None)
|
|
||||||
.done();
|
|
||||||
assert!(res.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inbox_multi_handlers07() {
|
fn test_inbox_multi_handlers07() {
|
||||||
let act = serde_json::to_value(build_create()).unwrap();
|
let act = serde_json::to_value(build_create()).unwrap();
|
||||||
|
@ -1059,17 +940,6 @@ mod tests {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inbox_failure() {
|
|
||||||
let act = serde_json::to_value(build_create()).unwrap();
|
|
||||||
// Create is not handled by this inbox
|
|
||||||
let res: Result<(), ()> = Inbox::handle(&(), act)
|
|
||||||
.with::<MyActor, Announce, MyObject>(None)
|
|
||||||
.with::<MyActor, Like, MyObject>(None)
|
|
||||||
.done();
|
|
||||||
assert!(res.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inbox_failure07() {
|
fn test_inbox_failure07() {
|
||||||
let act = serde_json::to_value(build_create07()).unwrap();
|
let act = serde_json::to_value(build_create07()).unwrap();
|
||||||
|
@ -1082,22 +952,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FailingActor;
|
struct FailingActor;
|
||||||
impl FromId<()> for FailingActor {
|
|
||||||
type Error = ();
|
|
||||||
type Object = Person;
|
|
||||||
|
|
||||||
fn from_db(_: &(), _id: &str) -> Result<Self, Self::Error> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
&*MY_SIGNER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsActor<&()> for FailingActor {
|
impl AsActor<&()> for FailingActor {
|
||||||
fn get_inbox_url(&self) -> String {
|
fn get_inbox_url(&self) -> String {
|
||||||
String::from("https://test.ap/failing-actor/inbox")
|
String::from("https://test.ap/failing-actor/inbox")
|
||||||
|
@ -1155,22 +1009,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inbox_actor_failure() {
|
|
||||||
let act = serde_json::to_value(build_create()).unwrap();
|
|
||||||
|
|
||||||
let res: Result<(), ()> = Inbox::handle(&(), act.clone())
|
|
||||||
.with::<FailingActor, Create, MyObject>(None)
|
|
||||||
.done();
|
|
||||||
assert!(res.is_err());
|
|
||||||
|
|
||||||
let res: Result<(), ()> = Inbox::handle(&(), act.clone())
|
|
||||||
.with::<FailingActor, Create, MyObject>(None)
|
|
||||||
.with::<MyActor, Create, MyObject>(None)
|
|
||||||
.done();
|
|
||||||
assert!(res.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inbox_actor_failure07() {
|
fn test_inbox_actor_failure07() {
|
||||||
let act = serde_json::to_value(build_create07()).unwrap();
|
let act = serde_json::to_value(build_create07()).unwrap();
|
||||||
|
|
|
@ -27,11 +27,10 @@ use openssl::{
|
||||||
sign::{Signer, Verifier},
|
sign::{Signer, Verifier},
|
||||||
};
|
};
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
inbox::{AsActor, FromId, FromId07},
|
inbox::{AsActor, FromId07},
|
||||||
sign, ActivityStream, ApSignature, ApSignature07, CustomGroup as CustomGroup07, Id, IntoId,
|
sign, ActivityStream, ApSignature, ApSignature07, CustomGroup as CustomGroup07, Id, IntoId,
|
||||||
PublicKey, PublicKey07, Source, SourceProperty, ToAsString, ToAsUri,
|
PublicKey, PublicKey07, Source, SourceProperty, ToAsString, ToAsUri,
|
||||||
};
|
};
|
||||||
use url::Url;
|
|
||||||
use webfinger::*;
|
use webfinger::*;
|
||||||
|
|
||||||
pub type CustomGroup = CustomObject<ApSignature, Group>;
|
pub type CustomGroup = CustomObject<ApSignature, Group>;
|
||||||
|
@ -161,7 +160,7 @@ impl Blog {
|
||||||
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
||||||
.ok_or(Error::Webfinger)
|
.ok_or(Error::Webfinger)
|
||||||
.and_then(|l| {
|
.and_then(|l| {
|
||||||
Blog::from_id(
|
Blog::from_id07(
|
||||||
conn,
|
conn,
|
||||||
&l.href.ok_or(Error::MissingApProperty)?,
|
&l.href.ok_or(Error::MissingApProperty)?,
|
||||||
None,
|
None,
|
||||||
|
@ -471,110 +470,6 @@ impl IntoId for Blog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Blog {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = CustomGroup;
|
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
|
||||||
Self::find_by_ap_url(conn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result<Self> {
|
|
||||||
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
|
||||||
let inst = url.host_str().ok_or(Error::Url)?;
|
|
||||||
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
|
||||||
Instance::insert(
|
|
||||||
conn,
|
|
||||||
NewInstance {
|
|
||||||
public_domain: inst.to_owned(),
|
|
||||||
name: inst.to_owned(),
|
|
||||||
local: false,
|
|
||||||
// We don't really care about all the following for remote instances
|
|
||||||
long_description: SafeString::new(""),
|
|
||||||
short_description: SafeString::new(""),
|
|
||||||
default_license: String::new(),
|
|
||||||
open_registrations: true,
|
|
||||||
short_description_html: String::new(),
|
|
||||||
long_description_html: String::new(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let icon_id = acct
|
|
||||||
.object
|
|
||||||
.object_props
|
|
||||||
.icon_image()
|
|
||||||
.ok()
|
|
||||||
.and_then(|icon| {
|
|
||||||
let owner = icon.object_props.attributed_to_link::<Id>().ok()?;
|
|
||||||
Media::save_remote(
|
|
||||||
conn,
|
|
||||||
icon.object_props.url_string().ok()?,
|
|
||||||
&User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?,
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.map(|m| m.id);
|
|
||||||
|
|
||||||
let banner_id = acct
|
|
||||||
.object
|
|
||||||
.object_props
|
|
||||||
.image_image()
|
|
||||||
.ok()
|
|
||||||
.and_then(|banner| {
|
|
||||||
let owner = banner.object_props.attributed_to_link::<Id>().ok()?;
|
|
||||||
Media::save_remote(
|
|
||||||
conn,
|
|
||||||
banner.object_props.url_string().ok()?,
|
|
||||||
&User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?,
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.map(|m| m.id);
|
|
||||||
|
|
||||||
let name = acct.object.ap_actor_props.preferred_username_string()?;
|
|
||||||
if name.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
|
||||||
return Err(Error::InvalidValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
Blog::insert(
|
|
||||||
conn,
|
|
||||||
NewBlog {
|
|
||||||
actor_id: name.clone(),
|
|
||||||
title: acct.object.object_props.name_string().unwrap_or(name),
|
|
||||||
outbox_url: acct.object.ap_actor_props.outbox_string()?,
|
|
||||||
inbox_url: acct.object.ap_actor_props.inbox_string()?,
|
|
||||||
summary: acct
|
|
||||||
.object
|
|
||||||
.ap_object_props
|
|
||||||
.source_object::<Source>()
|
|
||||||
.map(|s| s.content)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
instance_id: instance.id,
|
|
||||||
ap_url: acct.object.object_props.id_string()?,
|
|
||||||
public_key: acct
|
|
||||||
.custom_props
|
|
||||||
.public_key_publickey()?
|
|
||||||
.public_key_pem_string()?,
|
|
||||||
private_key: None,
|
|
||||||
banner_id,
|
|
||||||
icon_id,
|
|
||||||
summary_html: SafeString::new(
|
|
||||||
&acct
|
|
||||||
.object
|
|
||||||
.object_props
|
|
||||||
.summary_string()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
),
|
|
||||||
theme: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn sign::Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for Blog {
|
impl FromId07<DbConn> for Blog {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = CustomGroup07;
|
type Object = CustomGroup07;
|
||||||
|
@ -648,7 +543,7 @@ impl FromId07<DbConn> for Blog {
|
||||||
Media::save_remote(
|
Media::save_remote(
|
||||||
conn,
|
conn,
|
||||||
banner.url()?.to_as_uri()?,
|
banner.url()?.to_as_uri()?,
|
||||||
&User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?,
|
&User::from_id07(conn, &owner, None, CONFIG.proxy()).ok()?,
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
|
@ -1130,33 +1025,6 @@ pub(crate) mod tests {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn self_federation() {
|
|
||||||
let conn = &db();
|
|
||||||
conn.test_transaction::<_, (), _>(|| {
|
|
||||||
let (_users, blogs) = fill_database(&conn);
|
|
||||||
|
|
||||||
let ap_repr = blogs[0].to_activity(&conn).unwrap();
|
|
||||||
blogs[0].delete(&conn).unwrap();
|
|
||||||
let blog = Blog::from_activity(&conn, ap_repr).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(blog.actor_id, blogs[0].actor_id);
|
|
||||||
assert_eq!(blog.title, blogs[0].title);
|
|
||||||
assert_eq!(blog.summary, blogs[0].summary);
|
|
||||||
assert_eq!(blog.outbox_url, blogs[0].outbox_url);
|
|
||||||
assert_eq!(blog.inbox_url, blogs[0].inbox_url);
|
|
||||||
assert_eq!(blog.instance_id, blogs[0].instance_id);
|
|
||||||
assert_eq!(blog.ap_url, blogs[0].ap_url);
|
|
||||||
assert_eq!(blog.public_key, blogs[0].public_key);
|
|
||||||
assert_eq!(blog.fqn, blogs[0].fqn);
|
|
||||||
assert_eq!(blog.summary_html, blogs[0].summary_html);
|
|
||||||
assert_eq!(blog.icon_url(&conn), blogs[0].icon_url(&conn));
|
|
||||||
assert_eq!(blog.banner_url(&conn), blogs[0].banner_url(&conn));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn self_federation07() {
|
fn self_federation07() {
|
||||||
let conn = &db();
|
let conn = &db();
|
||||||
|
|
|
@ -30,7 +30,7 @@ use chrono::{self, NaiveDateTime, TimeZone, Utc};
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{
|
activity_pub::{
|
||||||
inbox::{AsActor, AsObject, AsObject07, FromId, FromId07},
|
inbox::{AsActor, AsObject, AsObject07, FromId07},
|
||||||
sign::Signer,
|
sign::Signer,
|
||||||
Id, IntoId, ToAsString, ToAsUri, PUBLIC_VISIBILITY,
|
Id, IntoId, ToAsString, ToAsUri, PUBLIC_VISIBILITY,
|
||||||
},
|
},
|
||||||
|
@ -294,141 +294,6 @@ impl Comment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Comment {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = Note;
|
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
|
||||||
Self::find_by_ap_url(conn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, note: Note) -> Result<Self> {
|
|
||||||
let comm = {
|
|
||||||
let previous_url = note
|
|
||||||
.object_props
|
|
||||||
.in_reply_to
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(Error::MissingApProperty)?
|
|
||||||
.as_str()
|
|
||||||
.ok_or(Error::MissingApProperty)?;
|
|
||||||
let previous_comment = Comment::find_by_ap_url(conn, previous_url);
|
|
||||||
|
|
||||||
let is_public = |v: &Option<serde_json::Value>| match v
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&serde_json::Value::Null)
|
|
||||||
{
|
|
||||||
serde_json::Value::Array(v) => v
|
|
||||||
.iter()
|
|
||||||
.filter_map(serde_json::Value::as_str)
|
|
||||||
.any(|s| s == PUBLIC_VISIBILITY),
|
|
||||||
serde_json::Value::String(s) => s == PUBLIC_VISIBILITY,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let public_visibility = is_public(¬e.object_props.to)
|
|
||||||
|| is_public(¬e.object_props.bto)
|
|
||||||
|| is_public(¬e.object_props.cc)
|
|
||||||
|| is_public(¬e.object_props.bcc);
|
|
||||||
|
|
||||||
let comm = Comment::insert(
|
|
||||||
conn,
|
|
||||||
NewComment {
|
|
||||||
content: SafeString::new(¬e.object_props.content_string()?),
|
|
||||||
spoiler_text: note.object_props.summary_string().unwrap_or_default(),
|
|
||||||
ap_url: note.object_props.id_string().ok(),
|
|
||||||
in_response_to_id: previous_comment.iter().map(|c| c.id).next(),
|
|
||||||
post_id: previous_comment.map(|c| c.post_id).or_else(|_| {
|
|
||||||
Ok(Post::find_by_ap_url(conn, previous_url)?.id) as Result<i32>
|
|
||||||
})?,
|
|
||||||
author_id: User::from_id(
|
|
||||||
conn,
|
|
||||||
¬e.object_props.attributed_to_link::<Id>()?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)?
|
|
||||||
.id,
|
|
||||||
sensitive: note.object_props.summary_string().is_ok(),
|
|
||||||
public_visibility,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// save mentions
|
|
||||||
if let Some(serde_json::Value::Array(tags)) = note.object_props.tag.clone() {
|
|
||||||
for tag in tags {
|
|
||||||
serde_json::from_value::<link::Mention>(tag)
|
|
||||||
.map_err(Error::from)
|
|
||||||
.and_then(|m| {
|
|
||||||
let author = &Post::get(conn, comm.post_id)?.get_authors(conn)?[0];
|
|
||||||
let not_author = m.link_props.href_string()? != author.ap_url.clone();
|
|
||||||
Mention::from_activity(conn, &m, comm.id, false, not_author)
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
comm
|
|
||||||
};
|
|
||||||
|
|
||||||
if !comm.public_visibility {
|
|
||||||
let receivers_ap_url = |v: Option<serde_json::Value>| {
|
|
||||||
let filter = |e: serde_json::Value| {
|
|
||||||
if let serde_json::Value::String(s) = e {
|
|
||||||
Some(s)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match v.unwrap_or(serde_json::Value::Null) {
|
|
||||||
serde_json::Value::Array(v) => v,
|
|
||||||
v => vec![v],
|
|
||||||
}
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(filter)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut note = note;
|
|
||||||
|
|
||||||
let to = receivers_ap_url(note.object_props.to.take());
|
|
||||||
let cc = receivers_ap_url(note.object_props.cc.take());
|
|
||||||
let bto = receivers_ap_url(note.object_props.bto.take());
|
|
||||||
let bcc = receivers_ap_url(note.object_props.bcc.take());
|
|
||||||
|
|
||||||
let receivers_ap_url = to
|
|
||||||
.chain(cc)
|
|
||||||
.chain(bto)
|
|
||||||
.chain(bcc)
|
|
||||||
.collect::<HashSet<_>>() // remove duplicates (don't do a query more than once)
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|v| {
|
|
||||||
if let Ok(user) = User::from_id(conn, &v, None, CONFIG.proxy()) {
|
|
||||||
vec![user]
|
|
||||||
} else {
|
|
||||||
vec![] // TODO try to fetch collection
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(|u| u.get_instance(conn).map(|i| i.local).unwrap_or(false))
|
|
||||||
.collect::<HashSet<User>>(); //remove duplicates (prevent db error)
|
|
||||||
|
|
||||||
for user in &receivers_ap_url {
|
|
||||||
CommentSeers::insert(
|
|
||||||
conn,
|
|
||||||
NewCommentSeers {
|
|
||||||
comment_id: comm.id,
|
|
||||||
user_id: user.id,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
comm.notify(conn)?;
|
|
||||||
Ok(comm)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for Comment {
|
impl FromId07<DbConn> for Comment {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = Note07;
|
type Object = Note07;
|
||||||
|
@ -484,7 +349,7 @@ impl FromId07<DbConn> for Comment {
|
||||||
post_id: previous_comment.map(|c| c.post_id).or_else(|_| {
|
post_id: previous_comment.map(|c| c.post_id).or_else(|_| {
|
||||||
Ok(Post::find_by_ap_url(conn, previous_url.as_str())?.id) as Result<i32>
|
Ok(Post::find_by_ap_url(conn, previous_url.as_str())?.id) as Result<i32>
|
||||||
})?,
|
})?,
|
||||||
author_id: User::from_id(
|
author_id: User::from_id07(
|
||||||
conn,
|
conn,
|
||||||
¬e
|
¬e
|
||||||
.attributed_to()
|
.attributed_to()
|
||||||
|
@ -537,7 +402,7 @@ impl FromId07<DbConn> for Comment {
|
||||||
let receivers_ap_url = receiver_ids
|
let receivers_ap_url = receiver_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|v| {
|
.flat_map(|v| {
|
||||||
if let Ok(user) = User::from_id(conn, v.as_ref(), None, CONFIG.proxy()) {
|
if let Ok(user) = User::from_id07(conn, v.as_ref(), None, CONFIG.proxy()) {
|
||||||
vec![user]
|
vec![user]
|
||||||
} else {
|
} else {
|
||||||
vec![] // TODO try to fetch collection
|
vec![] // TODO try to fetch collection
|
||||||
|
|
|
@ -12,7 +12,7 @@ use activitystreams::{
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
broadcast, broadcast07,
|
broadcast, broadcast07,
|
||||||
inbox::{AsActor, AsObject, AsObject07, FromId, FromId07},
|
inbox::{AsActor, AsObject, AsObject07, FromId07},
|
||||||
sign::Signer,
|
sign::Signer,
|
||||||
Id, IntoId, PUBLIC_VISIBILITY,
|
Id, IntoId, PUBLIC_VISIBILITY,
|
||||||
};
|
};
|
||||||
|
@ -272,38 +272,6 @@ impl AsObject07<User, FollowAct07, &DbConn> for User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Follow {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = FollowAct;
|
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
|
||||||
Follow::find_by_ap_url(conn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, follow: FollowAct) -> Result<Self> {
|
|
||||||
let actor = User::from_id(
|
|
||||||
conn,
|
|
||||||
&follow.follow_props.actor_link::<Id>()?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)?;
|
|
||||||
|
|
||||||
let target = User::from_id(
|
|
||||||
conn,
|
|
||||||
&follow.follow_props.object_link::<Id>()?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)?;
|
|
||||||
Follow::accept_follow(conn, &actor, &target, follow, actor.id, target.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for Follow {
|
impl FromId07<DbConn> for Follow {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = FollowAct07;
|
type Object = FollowAct07;
|
||||||
|
|
|
@ -12,7 +12,7 @@ use activitystreams::{
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
inbox::{AsActor, AsObject, AsObject07, FromId, FromId07},
|
inbox::{AsActor, AsObject, AsObject07, FromId07},
|
||||||
sign::Signer,
|
sign::Signer,
|
||||||
Id, IntoId, PUBLIC_VISIBILITY,
|
Id, IntoId, PUBLIC_VISIBILITY,
|
||||||
};
|
};
|
||||||
|
@ -158,46 +158,6 @@ impl AsObject07<User, Like07, &DbConn> for Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Like {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = activity::Like;
|
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
|
||||||
Like::find_by_ap_url(conn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, act: activity::Like) -> Result<Self> {
|
|
||||||
let res = Like::insert(
|
|
||||||
conn,
|
|
||||||
NewLike {
|
|
||||||
post_id: Post::from_id(
|
|
||||||
conn,
|
|
||||||
&act.like_props.object_link::<Id>()?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)?
|
|
||||||
.id,
|
|
||||||
user_id: User::from_id(
|
|
||||||
conn,
|
|
||||||
&act.like_props.actor_link::<Id>()?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)?
|
|
||||||
.id,
|
|
||||||
ap_url: act.object_props.id_string()?,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
res.notify(conn)?;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for Like {
|
impl FromId07<DbConn> for Like {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = Like07;
|
type Object = Like07;
|
||||||
|
@ -210,7 +170,7 @@ impl FromId07<DbConn> for Like {
|
||||||
let res = Like::insert(
|
let res = Like::insert(
|
||||||
conn,
|
conn,
|
||||||
NewLike {
|
NewLike {
|
||||||
post_id: Post::from_id(
|
post_id: Post::from_id07(
|
||||||
conn,
|
conn,
|
||||||
act.object_field_ref()
|
act.object_field_ref()
|
||||||
.as_single_id()
|
.as_single_id()
|
||||||
|
|
|
@ -7,7 +7,7 @@ use activitystreams::{object::Image as Image07, prelude::*};
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use guid_create::GUID;
|
use guid_create::GUID;
|
||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{inbox::FromId, request, Id, ToAsString, ToAsUri},
|
activity_pub::{inbox::FromId07, request, Id, ToAsString, ToAsUri},
|
||||||
utils::{escape, MediaProcessor},
|
utils::{escape, MediaProcessor},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -222,7 +222,7 @@ impl Media {
|
||||||
// TODO: conditional GET
|
// TODO: conditional GET
|
||||||
request::get(
|
request::get(
|
||||||
remote_url.as_str(),
|
remote_url.as_str(),
|
||||||
User::get_sender(),
|
User::get_sender07(),
|
||||||
CONFIG.proxy().cloned(),
|
CONFIG.proxy().cloned(),
|
||||||
)?
|
)?
|
||||||
.copy_to(&mut dest)?;
|
.copy_to(&mut dest)?;
|
||||||
|
@ -275,7 +275,7 @@ impl Media {
|
||||||
remote_url: None,
|
remote_url: None,
|
||||||
sensitive: image.object_props.summary_string().is_ok(),
|
sensitive: image.object_props.summary_string().is_ok(),
|
||||||
content_warning: image.object_props.summary_string().ok(),
|
content_warning: image.object_props.summary_string().ok(),
|
||||||
owner_id: User::from_id(
|
owner_id: User::from_id07(
|
||||||
conn,
|
conn,
|
||||||
image
|
image
|
||||||
.object_props
|
.object_props
|
||||||
|
@ -311,7 +311,7 @@ impl Media {
|
||||||
// TODO: conditional GET
|
// TODO: conditional GET
|
||||||
request::get(
|
request::get(
|
||||||
remote_url.as_str(),
|
remote_url.as_str(),
|
||||||
User::get_sender(),
|
User::get_sender07(),
|
||||||
CONFIG.proxy().cloned(),
|
CONFIG.proxy().cloned(),
|
||||||
)?
|
)?
|
||||||
.copy_to(&mut dest)?;
|
.copy_to(&mut dest)?;
|
||||||
|
@ -362,7 +362,7 @@ impl Media {
|
||||||
remote_url: None,
|
remote_url: None,
|
||||||
sensitive: summary.is_some(),
|
sensitive: summary.is_some(),
|
||||||
content_warning: summary,
|
content_warning: summary,
|
||||||
owner_id: User::from_id(
|
owner_id: User::from_id07(
|
||||||
conn,
|
conn,
|
||||||
&image
|
&image
|
||||||
.attributed_to()
|
.attributed_to()
|
||||||
|
|
|
@ -26,7 +26,7 @@ use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{
|
activity_pub::{
|
||||||
inbox::{AsActor, AsObject, AsObject07, FromId, FromId07},
|
inbox::{AsActor, AsObject, AsObject07, FromId07},
|
||||||
sign::Signer,
|
sign::Signer,
|
||||||
Hashtag, Hashtag07, HashtagType07, Id, IntoId, Licensed, Licensed07,
|
Hashtag, Hashtag07, HashtagType07, Id, IntoId, Licensed, Licensed07,
|
||||||
LicensedArticle as LicensedArticle07, Source, SourceProperty, ToAsString, ToAsUri,
|
LicensedArticle as LicensedArticle07, Source, SourceProperty, ToAsString, ToAsUri,
|
||||||
|
@ -863,160 +863,6 @@ impl Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Post {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = LicensedArticle;
|
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
|
||||||
Self::find_by_ap_url(conn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, article: LicensedArticle) -> Result<Self> {
|
|
||||||
let conn = conn;
|
|
||||||
let license = article.custom_props.license_string().unwrap_or_default();
|
|
||||||
let article = article.object;
|
|
||||||
|
|
||||||
let (blog, authors) = article
|
|
||||||
.object_props
|
|
||||||
.attributed_to_link_vec::<Id>()?
|
|
||||||
.into_iter()
|
|
||||||
.fold((None, vec![]), |(blog, mut authors), link| {
|
|
||||||
let url = link;
|
|
||||||
match User::from_id(conn, &url, None, CONFIG.proxy()) {
|
|
||||||
Ok(u) => {
|
|
||||||
authors.push(u);
|
|
||||||
(blog, authors)
|
|
||||||
}
|
|
||||||
Err(_) => (
|
|
||||||
blog.or_else(|| Blog::from_id(conn, &url, None, CONFIG.proxy()).ok()),
|
|
||||||
authors,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let cover = article
|
|
||||||
.object_props
|
|
||||||
.icon_object::<Image>()
|
|
||||||
.ok()
|
|
||||||
.and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id));
|
|
||||||
|
|
||||||
let title = article.object_props.name_string()?;
|
|
||||||
let ap_url = article
|
|
||||||
.object_props
|
|
||||||
.url_string()
|
|
||||||
.or_else(|_| article.object_props.id_string())?;
|
|
||||||
let post = Post::from_db(conn, &ap_url)
|
|
||||||
.and_then(|mut post| {
|
|
||||||
let mut updated = false;
|
|
||||||
|
|
||||||
let slug = Self::slug(&title);
|
|
||||||
let content = SafeString::new(&article.object_props.content_string()?);
|
|
||||||
let subtitle = article.object_props.summary_string()?;
|
|
||||||
let source = article.ap_object_props.source_object::<Source>()?.content;
|
|
||||||
if post.slug != slug {
|
|
||||||
post.slug = slug.to_string();
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
if post.title != title {
|
|
||||||
post.title = title.clone();
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
if post.content != content {
|
|
||||||
post.content = content;
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
if post.license != license {
|
|
||||||
post.license = license.clone();
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
if post.subtitle != subtitle {
|
|
||||||
post.subtitle = subtitle;
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
if post.source != source {
|
|
||||||
post.source = source;
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
if post.cover_id != cover {
|
|
||||||
post.cover_id = cover;
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if updated {
|
|
||||||
post.update(conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(post)
|
|
||||||
})
|
|
||||||
.or_else(|_| {
|
|
||||||
Post::insert(
|
|
||||||
conn,
|
|
||||||
NewPost {
|
|
||||||
blog_id: blog.ok_or(Error::NotFound)?.id,
|
|
||||||
slug: Self::slug(&title).to_string(),
|
|
||||||
title,
|
|
||||||
content: SafeString::new(&article.object_props.content_string()?),
|
|
||||||
published: true,
|
|
||||||
license,
|
|
||||||
// FIXME: This is wrong: with this logic, we may use the display URL as the AP ID. We need two different fields
|
|
||||||
ap_url,
|
|
||||||
creation_date: Some(article.object_props.published_utctime()?.naive_utc()),
|
|
||||||
subtitle: article.object_props.summary_string()?,
|
|
||||||
source: article.ap_object_props.source_object::<Source>()?.content,
|
|
||||||
cover_id: cover,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.and_then(|post| {
|
|
||||||
for author in authors {
|
|
||||||
PostAuthor::insert(
|
|
||||||
conn,
|
|
||||||
NewPostAuthor {
|
|
||||||
post_id: post.id,
|
|
||||||
author_id: author.id,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(post)
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// save mentions and tags
|
|
||||||
let mut hashtags = md_to_html(&post.source, None, false, None)
|
|
||||||
.2
|
|
||||||
.into_iter()
|
|
||||||
.collect::<HashSet<_>>();
|
|
||||||
if let Some(serde_json::Value::Array(tags)) = article.object_props.tag {
|
|
||||||
for tag in tags {
|
|
||||||
serde_json::from_value::<link::Mention>(tag.clone())
|
|
||||||
.map(|m| Mention::from_activity(conn, &m, post.id, true, true))
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
serde_json::from_value::<Hashtag>(tag.clone())
|
|
||||||
.map_err(Error::from)
|
|
||||||
.and_then(|t| {
|
|
||||||
let tag_name = t.name_string()?;
|
|
||||||
Ok(Tag::from_activity(
|
|
||||||
conn,
|
|
||||||
&t,
|
|
||||||
post.id,
|
|
||||||
hashtags.remove(&tag_name),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timeline::add_to_all_timelines(conn, &post, Kind::Original)?;
|
|
||||||
|
|
||||||
Ok(post)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for Post {
|
impl FromId07<DbConn> for Post {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = LicensedArticle07;
|
type Object = LicensedArticle07;
|
||||||
|
@ -1273,43 +1119,6 @@ pub struct PostUpdate {
|
||||||
pub tags: Option<serde_json::Value>,
|
pub tags: Option<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for PostUpdate {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = LicensedArticle;
|
|
||||||
|
|
||||||
fn from_db(_: &DbConn, _: &str) -> Result<Self> {
|
|
||||||
// Always fail because we always want to deserialize the AP object
|
|
||||||
Err(Error::NotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, updated: LicensedArticle) -> Result<Self> {
|
|
||||||
Ok(PostUpdate {
|
|
||||||
ap_url: updated.object.object_props.id_string()?,
|
|
||||||
title: updated.object.object_props.name_string().ok(),
|
|
||||||
subtitle: updated.object.object_props.summary_string().ok(),
|
|
||||||
content: updated.object.object_props.content_string().ok(),
|
|
||||||
cover: updated
|
|
||||||
.object
|
|
||||||
.object_props
|
|
||||||
.icon_object::<Image>()
|
|
||||||
.ok()
|
|
||||||
.and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id)),
|
|
||||||
source: updated
|
|
||||||
.object
|
|
||||||
.ap_object_props
|
|
||||||
.source_object::<Source>()
|
|
||||||
.ok()
|
|
||||||
.map(|x| x.content),
|
|
||||||
license: updated.custom_props.license_string().ok(),
|
|
||||||
tags: updated.object.object_props.tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for PostUpdate {
|
impl FromId07<DbConn> for PostUpdate {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = LicensedArticle07;
|
type Object = LicensedArticle07;
|
||||||
|
@ -1373,7 +1182,7 @@ impl AsObject<User, Update, &DbConn> for PostUpdate {
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||||
let mut post =
|
let mut post =
|
||||||
Post::from_id(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
Post::from_id07(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
||||||
|
|
||||||
if !post.is_author(conn, actor.id)? {
|
if !post.is_author(conn, actor.id)? {
|
||||||
// TODO: maybe the author was added in the meantime
|
// TODO: maybe the author was added in the meantime
|
||||||
|
@ -1445,7 +1254,7 @@ impl AsObject07<User, Update07, &DbConn> for PostUpdate {
|
||||||
|
|
||||||
fn activity07(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity07(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
||||||
let mut post =
|
let mut post =
|
||||||
Post::from_id(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
Post::from_id07(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
||||||
|
|
||||||
if !post.is_author(conn, actor.id)? {
|
if !post.is_author(conn, actor.id)? {
|
||||||
// TODO: maybe the author was added in the meantime
|
// TODO: maybe the author was added in the meantime
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db_conn::{DbConn, DbPool},
|
db_conn::{DbConn, DbPool},
|
||||||
follows,
|
follows,
|
||||||
posts::{LicensedArticle, Post},
|
posts::Post,
|
||||||
users::{User, UserEvent},
|
users::{User, UserEvent},
|
||||||
ACTOR_SYS, CONFIG, USER_CHAN,
|
ACTOR_SYS, CONFIG, USER_CHAN,
|
||||||
};
|
};
|
||||||
use activitypub::activity::Create;
|
use activitystreams::{
|
||||||
use plume_common::activity_pub::inbox::FromId;
|
activity::{ActorAndObjectRef, Create as Create07},
|
||||||
|
base::AnyBase,
|
||||||
|
object::kind::ArticleType,
|
||||||
|
};
|
||||||
|
use plume_common::activity_pub::{inbox::FromId07, LicensedArticle as LicensedArticle07};
|
||||||
use riker::actors::{Actor, ActorFactoryArgs, ActorRefFactory, Context, Sender, Subscribe, Tell};
|
use riker::actors::{Actor, ActorFactoryArgs, ActorRefFactory, Context, Sender, Subscribe, Tell};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
@ -64,17 +68,23 @@ impl ActorFactoryArgs<DbPool> for RemoteFetchActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_and_cache_articles(user: &Arc<User>, conn: &DbConn) {
|
fn fetch_and_cache_articles(user: &Arc<User>, conn: &DbConn) {
|
||||||
let create_acts = user.fetch_outbox::<Create>();
|
let create_acts = user.fetch_outbox07::<Create07>();
|
||||||
match create_acts {
|
match create_acts {
|
||||||
Ok(create_acts) => {
|
Ok(create_acts) => {
|
||||||
for create_act in create_acts {
|
for create_act in create_acts {
|
||||||
match create_act.create_props.object_object::<LicensedArticle>() {
|
match create_act
|
||||||
Ok(article) => {
|
.object_field_ref()
|
||||||
Post::from_activity(conn, article)
|
.as_single_base()
|
||||||
|
.and_then(|base| {
|
||||||
|
let any_base = AnyBase::from_base(base.clone()); // FIXME: Don't clone()
|
||||||
|
any_base.extend::<LicensedArticle07, ArticleType>().ok()
|
||||||
|
}) {
|
||||||
|
Some(Some(article)) => {
|
||||||
|
Post::from_activity07(conn, article)
|
||||||
.expect("Article from remote user couldn't be saved");
|
.expect("Article from remote user couldn't be saved");
|
||||||
info!("Fetched article from remote user");
|
info!("Fetched article from remote user");
|
||||||
}
|
}
|
||||||
Err(e) => warn!("Error while fetching articles in background: {:?}", e),
|
_ => warn!("Error while fetching articles in background"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +99,7 @@ fn fetch_and_cache_followers(user: &Arc<User>, conn: &DbConn) {
|
||||||
match follower_ids {
|
match follower_ids {
|
||||||
Ok(user_ids) => {
|
Ok(user_ids) => {
|
||||||
for user_id in user_ids {
|
for user_id in user_ids {
|
||||||
let follower = User::from_id(conn, &user_id, None, CONFIG.proxy());
|
let follower = User::from_id07(conn, &user_id, None, CONFIG.proxy());
|
||||||
match follower {
|
match follower {
|
||||||
Ok(follower) => {
|
Ok(follower) => {
|
||||||
let inserted = follows::Follow::insert(
|
let inserted = follows::Follow::insert(
|
||||||
|
|
|
@ -12,7 +12,7 @@ use activitystreams::{
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
inbox::{AsActor, AsObject, AsObject07, FromId, FromId07},
|
inbox::{AsActor, AsObject, AsObject07, FromId07},
|
||||||
sign::Signer,
|
sign::Signer,
|
||||||
Id, IntoId, PUBLIC_VISIBILITY,
|
Id, IntoId, PUBLIC_VISIBILITY,
|
||||||
};
|
};
|
||||||
|
@ -186,46 +186,6 @@ impl AsObject07<User, Announce07, &DbConn> for Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Reshare {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = Announce;
|
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
|
||||||
Reshare::find_by_ap_url(conn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, act: Announce) -> Result<Self> {
|
|
||||||
let res = Reshare::insert(
|
|
||||||
conn,
|
|
||||||
NewReshare {
|
|
||||||
post_id: Post::from_id(
|
|
||||||
conn,
|
|
||||||
&act.announce_props.object_link::<Id>()?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)?
|
|
||||||
.id,
|
|
||||||
user_id: User::from_id(
|
|
||||||
conn,
|
|
||||||
&act.announce_props.actor_link::<Id>()?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)?
|
|
||||||
.id,
|
|
||||||
ap_url: act.object_props.id_string()?,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
res.notify(conn)?;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for Reshare {
|
impl FromId07<DbConn> for Reshare {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = Announce07;
|
type Object = Announce07;
|
||||||
|
@ -238,7 +198,7 @@ impl FromId07<DbConn> for Reshare {
|
||||||
let res = Reshare::insert(
|
let res = Reshare::insert(
|
||||||
conn,
|
conn,
|
||||||
NewReshare {
|
NewReshare {
|
||||||
post_id: Post::from_id(
|
post_id: Post::from_id07(
|
||||||
conn,
|
conn,
|
||||||
act.object_field_ref()
|
act.object_field_ref()
|
||||||
.as_single_id()
|
.as_single_id()
|
||||||
|
@ -249,7 +209,7 @@ impl FromId07<DbConn> for Reshare {
|
||||||
)
|
)
|
||||||
.map_err(|(_, e)| e)?
|
.map_err(|(_, e)| e)?
|
||||||
.id,
|
.id,
|
||||||
user_id: User::from_id(
|
user_id: User::from_id07(
|
||||||
conn,
|
conn,
|
||||||
act.actor_field_ref()
|
act.actor_field_ref()
|
||||||
.as_single_id()
|
.as_single_id()
|
||||||
|
|
|
@ -13,7 +13,7 @@ use activitypub::{
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::Delete as Delete07,
|
activity::Delete as Delete07,
|
||||||
actor::AsApActor,
|
actor::{ApActor, AsApActor},
|
||||||
actor::{ApActor as ApActor07, Endpoints as Endpoints07, Person as Person07},
|
actor::{ApActor as ApActor07, Endpoints as Endpoints07, Person as Person07},
|
||||||
base::{AnyBase, Base},
|
base::{AnyBase, Base},
|
||||||
collection::{
|
collection::{
|
||||||
|
@ -21,7 +21,7 @@ use activitystreams::{
|
||||||
},
|
},
|
||||||
iri_string::types::IriString,
|
iri_string::types::IriString,
|
||||||
markers::Activity as Activity07,
|
markers::Activity as Activity07,
|
||||||
object::{AsObject as _, Image as Image07, Tombstone as Tombstone07},
|
object::{kind::ImageType, AsObject as _, Image as Image07, Tombstone as Tombstone07},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
|
@ -35,7 +35,7 @@ use openssl::{
|
||||||
};
|
};
|
||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{
|
activity_pub::{
|
||||||
inbox::{AsActor, AsObject, AsObject07, FromId, FromId07},
|
inbox::{AsActor, AsObject, AsObject07, FromId07},
|
||||||
request::get,
|
request::get,
|
||||||
sign::{gen_keypair, Error as SignError, Result as SignResult, Signer},
|
sign::{gen_keypair, Error as SignError, Result as SignResult, Signer},
|
||||||
ActivityStream, ApSignature, ApSignature07, CustomPerson as CustomPerson07, Id, IntoId,
|
ActivityStream, ApSignature, ApSignature07, CustomPerson as CustomPerson07, Id, IntoId,
|
||||||
|
@ -53,7 +53,6 @@ use std::{
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use url::Url;
|
|
||||||
use webfinger::*;
|
use webfinger::*;
|
||||||
|
|
||||||
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
||||||
|
@ -238,7 +237,7 @@ impl User {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
||||||
.ok_or(Error::Webfinger)?;
|
.ok_or(Error::Webfinger)?;
|
||||||
User::from_id(
|
User::from_id07(
|
||||||
conn,
|
conn,
|
||||||
link.href.as_ref().ok_or(Error::Webfinger)?,
|
link.href.as_ref().ok_or(Error::Webfinger)?,
|
||||||
None,
|
None,
|
||||||
|
@ -256,53 +255,84 @@ impl User {
|
||||||
.ok_or(Error::Webfinger)
|
.ok_or(Error::Webfinger)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(url: &str) -> Result<CustomPerson> {
|
fn fetch(url: &str) -> Result<CustomPerson07> {
|
||||||
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let mut res = get(url, Self::get_sender07(), CONFIG.proxy().cloned())?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
// without this workaround, publicKey is not correctly deserialized
|
// without this workaround, publicKey is not correctly deserialized
|
||||||
let ap_sign = serde_json::from_str::<ApSignature>(text)?;
|
let ap_sign = serde_json::from_str::<ApSignature07>(text)?;
|
||||||
let mut json = serde_json::from_str::<CustomPerson>(text)?;
|
let person = serde_json::from_str::<Person07>(text)?;
|
||||||
json.custom_props = ap_sign;
|
let json = CustomPerson07::new(
|
||||||
|
ApActor::new(
|
||||||
|
person
|
||||||
|
.clone()
|
||||||
|
.id_unchecked()
|
||||||
|
.ok_or(Error::MissingApProperty)?
|
||||||
|
.to_owned(),
|
||||||
|
person,
|
||||||
|
),
|
||||||
|
ap_sign,
|
||||||
|
); // FIXME: Don't clone()
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_from_url(conn: &DbConn, url: &str) -> Result<User> {
|
pub fn fetch_from_url(conn: &DbConn, url: &str) -> Result<User> {
|
||||||
User::fetch(url).and_then(|json| User::from_activity(conn, json))
|
User::fetch(url).and_then(|json| User::from_activity07(conn, json))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refetch(&self, conn: &Connection) -> Result<()> {
|
pub fn refetch(&self, conn: &Connection) -> Result<()> {
|
||||||
User::fetch(&self.ap_url.clone()).and_then(|json| {
|
User::fetch(&self.ap_url.clone()).and_then(|json| {
|
||||||
let avatar = Media::save_remote(
|
let avatar = Media::save_remote(
|
||||||
conn,
|
conn,
|
||||||
json.object
|
json.ap_actor_ref()
|
||||||
.object_props
|
.icon()
|
||||||
.icon_image()? // FIXME: Fails when icon is not set
|
.ok_or(Error::MissingApProperty)? // FIXME: Fails when icon is not set
|
||||||
.object_props
|
.iter()
|
||||||
.url_string()?,
|
.next()
|
||||||
|
.and_then(|i| {
|
||||||
|
i.clone()
|
||||||
|
.extend::<Image07, ImageType>() // FIXME: Don't clone()
|
||||||
|
.ok()?
|
||||||
|
.and_then(|url| Some(url.id_unchecked()?.to_string()))
|
||||||
|
})
|
||||||
|
.ok_or(Error::MissingApProperty)?,
|
||||||
self,
|
self,
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
|
let pub_key = &json.ext_one.public_key.public_key_pem;
|
||||||
diesel::update(self)
|
diesel::update(self)
|
||||||
.set((
|
.set((
|
||||||
users::username.eq(json.object.ap_actor_props.preferred_username_string()?),
|
users::username.eq(json
|
||||||
users::display_name.eq(json.object.object_props.name_string()?),
|
.ap_actor_ref()
|
||||||
users::outbox_url.eq(json.object.ap_actor_props.outbox_string()?),
|
.preferred_username()
|
||||||
users::inbox_url.eq(json.object.ap_actor_props.inbox_string()?),
|
.ok_or(Error::MissingApProperty)?),
|
||||||
|
users::display_name.eq(json
|
||||||
|
.ap_actor_ref()
|
||||||
|
.name()
|
||||||
|
.ok_or(Error::MissingApProperty)?
|
||||||
|
.to_as_string()
|
||||||
|
.ok_or(Error::MissingApProperty)?),
|
||||||
|
users::outbox_url.eq(json
|
||||||
|
.ap_actor_ref()
|
||||||
|
.outbox()?
|
||||||
|
.ok_or(Error::MissingApProperty)?
|
||||||
|
.as_str()),
|
||||||
|
users::inbox_url.eq(json.ap_actor_ref().inbox()?.as_str()),
|
||||||
users::summary.eq(SafeString::new(
|
users::summary.eq(SafeString::new(
|
||||||
&json
|
&json
|
||||||
.object
|
.ap_actor_ref()
|
||||||
.object_props
|
.summary()
|
||||||
.summary_string()
|
.and_then(|summary| summary.to_as_string())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)),
|
)),
|
||||||
users::followers_endpoint.eq(json.object.ap_actor_props.followers_string()?),
|
users::followers_endpoint.eq(json
|
||||||
|
.ap_actor_ref()
|
||||||
|
.followers()?
|
||||||
|
.ok_or(Error::MissingApProperty)?
|
||||||
|
.as_str()),
|
||||||
users::avatar_id.eq(avatar.map(|a| a.id)),
|
users::avatar_id.eq(avatar.map(|a| a.id)),
|
||||||
users::last_fetched_date.eq(Utc::now().naive_utc()),
|
users::last_fetched_date.eq(Utc::now().naive_utc()),
|
||||||
users::public_key.eq(json
|
users::public_key.eq(pub_key),
|
||||||
.custom_props
|
|
||||||
.public_key_publickey()?
|
|
||||||
.public_key_pem_string()?),
|
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
|
@ -548,7 +578,7 @@ impl User {
|
||||||
Ok(coll)
|
Ok(coll)
|
||||||
}
|
}
|
||||||
fn fetch_outbox_page<T: Activity>(&self, url: &str) -> Result<(Vec<T>, Option<String>)> {
|
fn fetch_outbox_page<T: Activity>(&self, url: &str) -> Result<(Vec<T>, Option<String>)> {
|
||||||
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let mut res = get(url, Self::get_sender07(), CONFIG.proxy().cloned())?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
let json: serde_json::Value = serde_json::from_str(text)?;
|
let json: serde_json::Value = serde_json::from_str(text)?;
|
||||||
let items = json["items"]
|
let items = json["items"]
|
||||||
|
@ -565,7 +595,7 @@ impl User {
|
||||||
&self,
|
&self,
|
||||||
url: &str,
|
url: &str,
|
||||||
) -> Result<(Vec<T>, Option<String>)> {
|
) -> Result<(Vec<T>, Option<String>)> {
|
||||||
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let mut res = get(url, Self::get_sender07(), CONFIG.proxy().cloned())?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
let json: serde_json::Value = serde_json::from_str(text)?;
|
let json: serde_json::Value = serde_json::from_str(text)?;
|
||||||
let items = json["items"]
|
let items = json["items"]
|
||||||
|
@ -581,7 +611,7 @@ impl User {
|
||||||
pub fn fetch_outbox<T: Activity>(&self) -> Result<Vec<T>> {
|
pub fn fetch_outbox<T: Activity>(&self) -> Result<Vec<T>> {
|
||||||
let mut res = get(
|
let mut res = get(
|
||||||
&self.outbox_url[..],
|
&self.outbox_url[..],
|
||||||
Self::get_sender(),
|
Self::get_sender07(),
|
||||||
CONFIG.proxy().cloned(),
|
CONFIG.proxy().cloned(),
|
||||||
)?;
|
)?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
|
@ -617,7 +647,7 @@ impl User {
|
||||||
pub fn fetch_outbox07<T: Activity07 + serde::de::DeserializeOwned>(&self) -> Result<Vec<T>> {
|
pub fn fetch_outbox07<T: Activity07 + serde::de::DeserializeOwned>(&self) -> Result<Vec<T>> {
|
||||||
let mut res = get(
|
let mut res = get(
|
||||||
&self.outbox_url[..],
|
&self.outbox_url[..],
|
||||||
Self::get_sender(),
|
Self::get_sender07(),
|
||||||
CONFIG.proxy().cloned(),
|
CONFIG.proxy().cloned(),
|
||||||
)?;
|
)?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
|
@ -653,7 +683,7 @@ impl User {
|
||||||
pub fn fetch_followers_ids(&self) -> Result<Vec<String>> {
|
pub fn fetch_followers_ids(&self) -> Result<Vec<String>> {
|
||||||
let mut res = get(
|
let mut res = get(
|
||||||
&self.followers_endpoint[..],
|
&self.followers_endpoint[..],
|
||||||
Self::get_sender(),
|
Self::get_sender07(),
|
||||||
CONFIG.proxy().cloned(),
|
CONFIG.proxy().cloned(),
|
||||||
)?;
|
)?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
|
@ -1097,110 +1127,6 @@ impl IntoId for User {
|
||||||
|
|
||||||
impl Eq for User {}
|
impl Eq for User {}
|
||||||
|
|
||||||
impl FromId<DbConn> for User {
|
|
||||||
type Error = Error;
|
|
||||||
type Object = CustomPerson;
|
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
|
||||||
Self::find_by_ap_url(conn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
|
|
||||||
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
|
||||||
let inst = url.host_str().ok_or(Error::Url)?;
|
|
||||||
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
|
||||||
Instance::insert(
|
|
||||||
conn,
|
|
||||||
NewInstance {
|
|
||||||
name: inst.to_owned(),
|
|
||||||
public_domain: inst.to_owned(),
|
|
||||||
local: false,
|
|
||||||
// We don't really care about all the following for remote instances
|
|
||||||
long_description: SafeString::new(""),
|
|
||||||
short_description: SafeString::new(""),
|
|
||||||
default_license: String::new(),
|
|
||||||
open_registrations: true,
|
|
||||||
short_description_html: String::new(),
|
|
||||||
long_description_html: String::new(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let username = acct.object.ap_actor_props.preferred_username_string()?;
|
|
||||||
|
|
||||||
if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
|
||||||
return Err(Error::InvalidValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
let fqn = if instance.local {
|
|
||||||
username.clone()
|
|
||||||
} else {
|
|
||||||
format!("{}@{}", username, instance.public_domain)
|
|
||||||
};
|
|
||||||
|
|
||||||
let user = User::insert(
|
|
||||||
conn,
|
|
||||||
NewUser {
|
|
||||||
display_name: acct
|
|
||||||
.object
|
|
||||||
.object_props
|
|
||||||
.name_string()
|
|
||||||
.unwrap_or_else(|_| username.clone()),
|
|
||||||
username,
|
|
||||||
outbox_url: acct.object.ap_actor_props.outbox_string()?,
|
|
||||||
inbox_url: acct.object.ap_actor_props.inbox_string()?,
|
|
||||||
role: 2,
|
|
||||||
summary: acct
|
|
||||||
.object
|
|
||||||
.object_props
|
|
||||||
.summary_string()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
summary_html: SafeString::new(
|
|
||||||
&acct
|
|
||||||
.object
|
|
||||||
.object_props
|
|
||||||
.summary_string()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
),
|
|
||||||
email: None,
|
|
||||||
hashed_password: None,
|
|
||||||
instance_id: instance.id,
|
|
||||||
ap_url: acct.object.object_props.id_string()?,
|
|
||||||
public_key: acct
|
|
||||||
.custom_props
|
|
||||||
.public_key_publickey()?
|
|
||||||
.public_key_pem_string()?,
|
|
||||||
private_key: None,
|
|
||||||
shared_inbox_url: acct
|
|
||||||
.object
|
|
||||||
.ap_actor_props
|
|
||||||
.endpoints_endpoint()
|
|
||||||
.and_then(|e| e.shared_inbox_string())
|
|
||||||
.ok(),
|
|
||||||
followers_endpoint: acct.object.ap_actor_props.followers_string()?,
|
|
||||||
fqn,
|
|
||||||
avatar_id: None,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Ok(icon) = acct.object.object_props.icon_image() {
|
|
||||||
if let Ok(url) = icon.object_props.url_string() {
|
|
||||||
let avatar = Media::save_remote(conn, url, &user);
|
|
||||||
|
|
||||||
if let Ok(avatar) = avatar {
|
|
||||||
user.set_avatar(conn, avatar.id)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromId07<DbConn> for User {
|
impl FromId07<DbConn> for User {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = CustomPerson07;
|
type Object = CustomPerson07;
|
||||||
|
@ -1690,32 +1616,6 @@ pub(crate) mod tests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn self_federation() {
|
|
||||||
let conn = db();
|
|
||||||
conn.test_transaction::<_, (), _>(|| {
|
|
||||||
let users = fill_database(&conn);
|
|
||||||
|
|
||||||
let ap_repr = users[0].to_activity(&conn).unwrap();
|
|
||||||
users[0].delete(&conn).unwrap();
|
|
||||||
let user = User::from_activity(&conn, ap_repr).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(user.username, users[0].username);
|
|
||||||
assert_eq!(user.display_name, users[0].display_name);
|
|
||||||
assert_eq!(user.outbox_url, users[0].outbox_url);
|
|
||||||
assert_eq!(user.inbox_url, users[0].inbox_url);
|
|
||||||
assert_eq!(user.instance_id, users[0].instance_id);
|
|
||||||
assert_eq!(user.ap_url, users[0].ap_url);
|
|
||||||
assert_eq!(user.public_key, users[0].public_key);
|
|
||||||
assert_eq!(user.shared_inbox_url, users[0].shared_inbox_url);
|
|
||||||
assert_eq!(user.followers_endpoint, users[0].followers_endpoint);
|
|
||||||
assert_eq!(user.avatar_url(&conn), users[0].avatar_url(&conn));
|
|
||||||
assert_eq!(user.fqn, users[0].fqn);
|
|
||||||
assert_eq!(user.summary_html, users[0].summary_html);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn self_federation07() {
|
fn self_federation07() {
|
||||||
let conn = db();
|
let conn = db();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
inbox::FromId,
|
inbox::FromId07,
|
||||||
request::Digest,
|
request::Digest,
|
||||||
sign::{verify_http_headers, Signable},
|
sign::{verify_http_headers, Signable},
|
||||||
};
|
};
|
||||||
|
@ -26,7 +26,7 @@ pub fn handle_incoming(
|
||||||
.or_else(|| activity["actor"]["id"].as_str())
|
.or_else(|| activity["actor"]["id"].as_str())
|
||||||
.ok_or(status::BadRequest(Some("Missing actor id for activity")))?;
|
.ok_or(status::BadRequest(Some("Missing actor id for activity")))?;
|
||||||
|
|
||||||
let actor = User::from_id(&conn, actor_id, None, CONFIG.proxy())
|
let actor = User::from_id07(&conn, actor_id, None, CONFIG.proxy())
|
||||||
.expect("instance::shared_inbox: user error");
|
.expect("instance::shared_inbox: user error");
|
||||||
if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) {
|
if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) {
|
||||||
// maybe we just know an old key?
|
// maybe we just know an old key?
|
||||||
|
|
|
@ -11,7 +11,7 @@ use validator::{Validate, ValidationErrors};
|
||||||
use crate::inbox;
|
use crate::inbox;
|
||||||
use crate::routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page, RespondOrRedirect};
|
use crate::routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page, RespondOrRedirect};
|
||||||
use crate::template_utils::{IntoContext, Ructe};
|
use crate::template_utils::{IntoContext, Ructe};
|
||||||
use plume_common::activity_pub::{broadcast, inbox::FromId};
|
use plume_common::activity_pub::{broadcast, inbox::FromId07};
|
||||||
use plume_models::{
|
use plume_models::{
|
||||||
admin::*,
|
admin::*,
|
||||||
blocklisted_emails::*,
|
blocklisted_emails::*,
|
||||||
|
@ -404,7 +404,7 @@ pub fn interact(conn: DbConn, user: Option<User>, target: String) -> Option<Redi
|
||||||
return Some(Redirect::to(uri!(super::user::details: name = target)));
|
return Some(Redirect::to(uri!(super::user::details: name = target)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(post) = Post::from_id(&conn, &target, None, CONFIG.proxy()) {
|
if let Ok(post) = Post::from_id07(&conn, &target, None, CONFIG.proxy()) {
|
||||||
return Some(Redirect::to(uri!(
|
return Some(Redirect::to(uri!(
|
||||||
super::posts::details: blog = post.get_blog(&conn).expect("Can't retrieve blog").fqn,
|
super::posts::details: blog = post.get_blog(&conn).expect("Can't retrieve blog").fqn,
|
||||||
slug = &post.slug,
|
slug = &post.slug,
|
||||||
|
@ -412,7 +412,7 @@ pub fn interact(conn: DbConn, user: Option<User>, target: String) -> Option<Redi
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(comment) = Comment::from_id(&conn, &target, None, CONFIG.proxy()) {
|
if let Ok(comment) = Comment::from_id07(&conn, &target, None, CONFIG.proxy()) {
|
||||||
if comment.can_see(&conn, user.as_ref()) {
|
if comment.can_see(&conn, user.as_ref()) {
|
||||||
let post = comment.get_post(&conn).expect("Can't retrieve post");
|
let post = comment.get_post(&conn).expect("Can't retrieve post");
|
||||||
return Some(Redirect::to(uri!(
|
return Some(Redirect::to(uri!(
|
||||||
|
|
Loading…
Reference in a new issue