Separate object URL handling from content validation

This commit is contained in:
silverpill 2023-01-31 18:03:24 +00:00
parent bc19a524c4
commit 01c894da9d
2 changed files with 37 additions and 30 deletions

View file

@ -19,7 +19,7 @@ use crate::activitypub::{
}; };
use crate::config::Config; use crate::config::Config;
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::{ConversionError, ValidationError}; use crate::errors::ValidationError;
use crate::models::{ use crate::models::{
attachments::queries::create_attachment, attachments::queries::create_attachment,
emojis::queries::{ emojis::queries::{
@ -68,23 +68,22 @@ fn get_object_attributed_to(object: &Object)
Ok(author_id) Ok(author_id)
} }
fn parse_object_url(value: &JsonValue) -> Result<String, ConversionError> { pub fn get_object_url(object: &Object) -> Result<String, ValidationError> {
let object_url = match value { let maybe_object_url = match &object.url {
JsonValue::String(string) => string.to_owned(), Some(JsonValue::String(string)) => Some(string.to_owned()),
other_value => { Some(other_value) => {
let links: Vec<Link> = parse_property_value(other_value)?; let links: Vec<Link> = parse_property_value(other_value)
if let Some(link) = links.get(0) { .map_err(|_| ValidationError("invalid object URL"))?;
link.href.clone() links.get(0).map(|link| link.href.clone())
} else {
return Err(ConversionError);
}
}, },
None => None,
}; };
let object_url = maybe_object_url.unwrap_or(object.id.clone());
Ok(object_url) Ok(object_url)
} }
pub fn get_object_content(object: &Object) -> Result<String, ValidationError> { pub fn get_object_content(object: &Object) -> Result<String, ValidationError> {
let mut content = if let Some(ref content) = object.content { let content = if let Some(ref content) = object.content {
if object.media_type == Some("text/markdown".to_string()) { if object.media_type == Some("text/markdown".to_string()) {
format!("<p>{}</p>", content) format!("<p>{}</p>", content)
} else { } else {
@ -95,19 +94,6 @@ pub fn get_object_content(object: &Object) -> Result<String, ValidationError> {
// Lemmy pages and PeerTube videos have "name" property // Lemmy pages and PeerTube videos have "name" property
object.name.as_deref().unwrap_or("").to_string() object.name.as_deref().unwrap_or("").to_string()
}; };
if object.object_type != NOTE {
// Append link to object
let object_url = if let Some(ref value) = object.url {
parse_object_url(value)
.map_err(|_| ValidationError("invalid object URL"))?
} else {
object.id.clone()
};
content += &format!(
r#"<p><a href="{0}">{0}</a></p>"#,
object_url,
);
};
if content.len() > CONTENT_MAX_SIZE { if content.len() > CONTENT_MAX_SIZE {
return Err(ValidationError("content is too long")); return Err(ValidationError("content is too long"));
}; };
@ -115,6 +101,13 @@ pub fn get_object_content(object: &Object) -> Result<String, ValidationError> {
Ok(content_safe) Ok(content_safe)
} }
pub fn create_content_link(url: String) -> String {
format!(
r#"<p><a href="{0}" rel="noopener">{0}</a></p>"#,
url,
)
}
const ATTACHMENT_MAX_SIZE: usize = 20 * 1000 * 1000; // 20 MB const ATTACHMENT_MAX_SIZE: usize = 20 * 1000 * 1000; // 20 MB
fn is_gnu_social_link(author_id: &str, attachment: &Attachment) -> bool { fn is_gnu_social_link(author_id: &str, attachment: &Attachment) -> bool {
@ -498,9 +491,13 @@ pub async fn handle_note(
log::warn!("failed to import {} ({})", author_id, err); log::warn!("failed to import {} ({})", author_id, err);
err err
})?; })?;
let content = get_object_content(&object)?;
let created_at = object.published.unwrap_or(Utc::now());
let mut content = get_object_content(&object)?;
if object.object_type != NOTE {
// Append link to object
let object_url = get_object_url(&object)?;
content += &create_content_link(object_url);
};
let attachments = get_object_attachments( let attachments = get_object_attachments(
config, config,
db_client, db_client,
@ -556,6 +553,7 @@ pub async fn handle_note(
author.username, author.username,
); );
}; };
let created_at = object.published.unwrap_or(Utc::now());
let post_data = PostCreateData { let post_data = PostCreateData {
content: content, content: content,
in_reply_to_id, in_reply_to_id,
@ -638,7 +636,9 @@ mod tests {
}])), }])),
..Default::default() ..Default::default()
}; };
let content = get_object_content(&object).unwrap(); let mut content = get_object_content(&object).unwrap();
let object_url = get_object_url(&object).unwrap();
content += &create_content_link(object_url);
assert_eq!( assert_eq!(
content, content,
r#"test-content<p><a href="https://example.org/xyz" rel="noopener">https://example.org/xyz</a></p>"#, r#"test-content<p><a href="https://example.org/xyz" rel="noopener">https://example.org/xyz</a></p>"#,

View file

@ -10,9 +10,11 @@ use crate::activitypub::{
types::Actor, types::Actor,
}, },
handlers::create::{ handlers::create::{
create_content_link,
get_object_attachments, get_object_attachments,
get_object_content, get_object_content,
get_object_tags, get_object_tags,
get_object_url,
}, },
types::Object, types::Object,
vocabulary::{NOTE, PERSON}, vocabulary::{NOTE, PERSON},
@ -46,8 +48,12 @@ async fn handle_update_note(
Err(DatabaseError::NotFound(_)) => return Ok(None), Err(DatabaseError::NotFound(_)) => return Ok(None),
Err(other_error) => return Err(other_error.into()), Err(other_error) => return Err(other_error.into()),
}; };
let content = get_object_content(&object)?; let mut content = get_object_content(&object)?;
let updated_at = object.updated.unwrap_or(Utc::now()); if object.object_type != NOTE {
// Append link to object
let object_url = get_object_url(&object)?;
content += &create_content_link(object_url);
};
let attachments = get_object_attachments( let attachments = get_object_attachments(
config, config,
db_client, db_client,
@ -63,6 +69,7 @@ async fn handle_update_note(
&object, &object,
&HashMap::new(), &HashMap::new(),
).await?; ).await?;
let updated_at = object.updated.unwrap_or(Utc::now());
let post_data = PostUpdateData { let post_data = PostUpdateData {
content, content,
attachments, attachments,