diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bb7bf..10cd79c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Replace post attachments and other related objects when processing `Update(Note)` activity. - Append attachment URL to post content if attachment size exceeds limit. - Added `/api/v1/custom_emojis` endpoint. +- Added `limits` parameter group to configuration. +- Made file size limit adjustable with `limits.media.file_size_limit` configuration option. ### Changed diff --git a/contrib/mitra_config.yaml b/contrib/mitra_config.yaml index 2c2ed60..0a5c8be 100644 --- a/contrib/mitra_config.yaml +++ b/contrib/mitra_config.yaml @@ -30,6 +30,11 @@ registration: # Proxy for outgoing requests #proxy_url: 'socks5h://127.0.0.1:9050' +# Limits +#limits: +# media: +# file_size_limit: 20M + # List of blocked domains #blocked_instances: [] diff --git a/src/activitypub/handlers/create.rs b/src/activitypub/handlers/create.rs index 7115516..92d2eda 100644 --- a/src/activitypub/handlers/create.rs +++ b/src/activitypub/handlers/create.rs @@ -108,8 +108,6 @@ pub fn create_content_link(url: String) -> String { ) } -const ATTACHMENT_MAX_SIZE: usize = 20 * 1000 * 1000; // 20 MB - fn is_gnu_social_link(author_id: &str, attachment: &Attachment) -> bool { if !author_id.contains("/index.php/user/") { return false; @@ -162,7 +160,7 @@ pub async fn get_object_attachments( &instance, &attachment_url, attachment.media_type.as_deref(), - ATTACHMENT_MAX_SIZE, + config.limits.media.file_size_limit, &media_dir, ).await { Ok(file) => file, diff --git a/src/config/limits.rs b/src/config/limits.rs new file mode 100644 index 0000000..64e60d6 --- /dev/null +++ b/src/config/limits.rs @@ -0,0 +1,81 @@ +use regex::Regex; +use serde::{ + Deserialize, + Deserializer, + de::{Error as DeserializerError}, +}; + +const FILE_SIZE_RE: &str = r#"^(?i)(?P\d+)(?P[kmg]?)b?$"#; + +#[derive(thiserror::Error, Debug)] +#[error("{0}")] +struct ConfigError(&'static str); + +fn parse_file_size(value: &str) -> Result { + let file_size_re = Regex::new(FILE_SIZE_RE) + .expect("regexp should be valid"); + let caps = file_size_re.captures(value) + .ok_or(ConfigError("invalid file size"))?; + let size: usize = caps["size"].to_string().parse() + .map_err(|_| ConfigError("invalid file size"))?; + let unit = caps["unit"].to_string().to_lowercase(); + let multiplier = match unit.as_str() { + "k" => usize::pow(10, 3), + "m" => usize::pow(10, 6), + "g" => usize::pow(10, 9), + "" => 1, + _ => return Err(ConfigError("invalid file size unit")), + }; + Ok(size * multiplier) +} + +fn deserialize_file_size<'de, D>( + deserializer: D, +) -> Result + where D: Deserializer<'de> +{ + let file_size_str = String::deserialize(deserializer)?; + let file_size = parse_file_size(&file_size_str) + .map_err(DeserializerError::custom)?; + Ok(file_size) +} + +const fn default_file_size_limit() -> usize { 20_000_000 } // 20 MB + +#[derive(Clone, Deserialize)] +pub struct MediaLimits { + #[serde( + default = "default_file_size_limit", + deserialize_with = "deserialize_file_size", + )] + pub file_size_limit: usize, +} + +impl Default for MediaLimits { + fn default() -> Self { + Self { + file_size_limit: default_file_size_limit(), + } + } +} + +#[derive(Clone, Default, Deserialize)] +pub struct Limits { + #[serde(default)] + pub media: MediaLimits, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_file_size() { + let file_size = parse_file_size("1234").unwrap(); + assert_eq!(file_size, 1234); + let file_size = parse_file_size("89kB").unwrap(); + assert_eq!(file_size, 89_000); + let file_size = parse_file_size("12M").unwrap(); + assert_eq!(file_size, 12_000_000); + } +} diff --git a/src/config/main.rs b/src/config/main.rs index c95d7a5..0dabdeb 100644 --- a/src/config/main.rs +++ b/src/config/main.rs @@ -16,6 +16,7 @@ use crate::utils::urls::guess_protocol; use super::blockchain::BlockchainConfig; use super::environment::Environment; +use super::limits::Limits; use super::MITRA_VERSION; #[derive(Clone, PartialEq)] @@ -103,6 +104,9 @@ pub struct Config { proxy_url: Option, + #[serde(default)] + pub limits: Limits, + #[serde(default)] pub blocked_instances: Vec, diff --git a/src/config/mod.rs b/src/config/mod.rs index ac75e8f..c29c10d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,5 +1,6 @@ mod blockchain; mod environment; +mod limits; mod loader; mod main;