mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
refactor: Ruma upstreaming, half-baked edition
Co-authored-by: Jade Ellis <jade@ellis.link>
This commit is contained in:
@@ -6,7 +6,9 @@ use conduwuit::{
|
||||
};
|
||||
use database::{Database, Interfix, Map};
|
||||
use futures::StreamExt;
|
||||
use ruma::{Mxc, OwnedMxcUri, UserId, http_headers::ContentDisposition};
|
||||
use ruma::{OwnedMxcUri, OwnedUserId, UserId, http_headers::ContentDisposition};
|
||||
|
||||
use crate::media::mxc::Mxc;
|
||||
|
||||
use super::{preview::UrlPreviewData, thumbnail::Dim};
|
||||
|
||||
@@ -41,7 +43,7 @@ impl Data {
|
||||
content_type: Option<&str>,
|
||||
) -> Result<Vec<u8>> {
|
||||
let dim: &[u32] = &[dim.width, dim.height];
|
||||
let key = (mxc, dim, content_disposition, content_type);
|
||||
let key = (mxc, dim, content_disposition.map(ToString::to_string), content_type);
|
||||
let key = database::serialize_key(key)?;
|
||||
self.mediaid_file.insert(&key, []);
|
||||
if let Some(user) = user {
|
||||
@@ -146,7 +148,7 @@ impl Data {
|
||||
self.mediaid_user
|
||||
.stream()
|
||||
.ignore_err()
|
||||
.ready_filter_map(|(key, user): (&str, &UserId)| {
|
||||
.ready_filter_map(|(key, user): (&str, OwnedUserId)| {
|
||||
(user == user_id).then(|| key.into())
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod blurhash;
|
||||
pub mod mxc;
|
||||
mod data;
|
||||
pub(super) mod migrations;
|
||||
mod preview;
|
||||
@@ -17,7 +18,7 @@ use conduwuit::{
|
||||
},
|
||||
warn,
|
||||
};
|
||||
use ruma::{Mxc, OwnedMxcUri, UserId, http_headers::ContentDisposition};
|
||||
use ruma::{OwnedMxcUri, UserId, http_headers::ContentDisposition};
|
||||
use tokio::{
|
||||
fs,
|
||||
io::{AsyncReadExt, AsyncWriteExt, BufReader},
|
||||
@@ -25,7 +26,7 @@ use tokio::{
|
||||
|
||||
use self::data::{Data, Metadata};
|
||||
pub use self::thumbnail::Dim;
|
||||
use crate::{Dep, client, globals, moderation, sending};
|
||||
use crate::{Dep, client, globals, media::mxc::Mxc, moderation, sending};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileMeta {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
use std::fmt;
|
||||
|
||||
use ruma::{MxcUri, MxcUriError, OwnedMxcUri, ServerName};
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
/// A structured, valid MXC URI
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Mxc<'a> {
|
||||
/// ServerName part of the MXC URI
|
||||
pub server_name: &'a ServerName,
|
||||
|
||||
/// MediaId part of the MXC URI
|
||||
pub media_id: &'a str,
|
||||
}
|
||||
|
||||
impl fmt::Display for Mxc<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "mxc://{}/{}", self.server_name, self.media_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a MxcUri> for Mxc<'a> {
|
||||
type Error = MxcUriError;
|
||||
|
||||
fn try_from(s: &'a MxcUri) -> Result<Self, Self::Error> {
|
||||
let (server_name, media_id) = s.parts()?;
|
||||
|
||||
Ok(Self { server_name, media_id })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Mxc<'a> {
|
||||
type Error = MxcUriError;
|
||||
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
let s: &MxcUri = s.into();
|
||||
s.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a OwnedMxcUri> for Mxc<'a> {
|
||||
type Error = MxcUriError;
|
||||
|
||||
fn try_from(s: &'a OwnedMxcUri) -> Result<Self, Self::Error> {
|
||||
let s: &MxcUri = s.as_ref();
|
||||
s.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Mxc<'_> {
|
||||
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
||||
s.serialize_str(self.to_string().as_str())
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,8 @@ pub async fn download_image(
|
||||
) -> Result<UrlPreviewData> {
|
||||
use conduwuit::utils::random_string;
|
||||
use image::ImageReader;
|
||||
use ruma::Mxc;
|
||||
|
||||
use crate::media::mxc::Mxc;
|
||||
|
||||
let mut preview_data = preview_data.unwrap_or_default();
|
||||
|
||||
|
||||
+60
-62
@@ -6,18 +6,16 @@ use conduwuit::{
|
||||
};
|
||||
use http::header::{CONTENT_DISPOSITION, CONTENT_TYPE, HeaderValue};
|
||||
use ruma::{
|
||||
Mxc, ServerName, UserId,
|
||||
api::{
|
||||
OutgoingRequest,
|
||||
client::{
|
||||
ServerName, UserId, api::{
|
||||
Metadata, OutgoingRequest, auth_scheme::NoAuthentication, client::{
|
||||
error::ErrorKind::{NotFound, Unrecognized},
|
||||
media,
|
||||
},
|
||||
federation,
|
||||
federation::authenticated_media::{Content, FileOrLocation},
|
||||
},
|
||||
}, federation::{self, authenticated_media::{Content, FileOrLocation}, authentication::ServerSignatures}, path_builder::PathBuilder
|
||||
}
|
||||
};
|
||||
|
||||
use crate::{federation::FederationPathBuilderInput, media::mxc::Mxc};
|
||||
|
||||
use super::{Dim, FileMeta};
|
||||
|
||||
#[implement(super::Service)]
|
||||
@@ -87,14 +85,10 @@ async fn fetch_thumbnail_authenticated(
|
||||
) -> Result<FileMeta> {
|
||||
use federation::authenticated_media::get_content_thumbnail::v1::{Request, Response};
|
||||
|
||||
let request = Request {
|
||||
media_id: mxc.media_id.into(),
|
||||
method: dim.method.clone().into(),
|
||||
width: dim.width.into(),
|
||||
height: dim.height.into(),
|
||||
animated: true.into(),
|
||||
timeout_ms,
|
||||
};
|
||||
let mut request = Request::new(mxc.media_id.into(), dim.width.into(), dim.height.into());
|
||||
request.method = Some(dim.method.clone());
|
||||
request.animated = Some(true);
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
let Response { content, .. } = self.federation_request(mxc, server, request).await?;
|
||||
|
||||
@@ -102,6 +96,7 @@ async fn fetch_thumbnail_authenticated(
|
||||
| FileOrLocation::File(content) =>
|
||||
self.handle_thumbnail_file(mxc, user, dim, content).await,
|
||||
| FileOrLocation::Location(location) => self.handle_location(mxc, user, &location).await,
|
||||
| _ => Err!("Unknown content in response"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,16 +110,15 @@ async fn fetch_content_authenticated(
|
||||
) -> Result<FileMeta> {
|
||||
use federation::authenticated_media::get_content::v1::{Request, Response};
|
||||
|
||||
let request = Request {
|
||||
media_id: mxc.media_id.into(),
|
||||
timeout_ms,
|
||||
};
|
||||
let mut request = Request::new(mxc.media_id.into());
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
let Response { content, .. } = self.federation_request(mxc, server, request).await?;
|
||||
|
||||
match content {
|
||||
| FileOrLocation::File(content) => self.handle_content_file(mxc, user, content).await,
|
||||
| FileOrLocation::Location(location) => self.handle_location(mxc, user, &location).await,
|
||||
| _ => Err!("Unknown content in response"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,23 +134,18 @@ async fn fetch_thumbnail_unauthenticated(
|
||||
) -> Result<FileMeta> {
|
||||
use media::get_content_thumbnail::v3::{Request, Response};
|
||||
|
||||
let request = Request {
|
||||
allow_remote: true,
|
||||
allow_redirect: true,
|
||||
animated: true.into(),
|
||||
method: dim.method.clone().into(),
|
||||
width: dim.width.into(),
|
||||
height: dim.height.into(),
|
||||
server_name: mxc.server_name.into(),
|
||||
media_id: mxc.media_id.into(),
|
||||
timeout_ms,
|
||||
};
|
||||
let mut request = Request::new(mxc.media_id.into(), mxc.server_name.into(), dim.width.into(), dim.height.into());
|
||||
request.allow_redirect = true;
|
||||
request.allow_remote = true;
|
||||
request.animated = Some(true);
|
||||
request.method = Some(dim.method.clone());
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
let Response {
|
||||
file, content_type, content_disposition, ..
|
||||
} = self.federation_request(mxc, server, request).await?;
|
||||
} = self.federation_request_unauthenticated(mxc, server, request).await?;
|
||||
|
||||
let content = Content { file, content_type, content_disposition };
|
||||
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
||||
|
||||
self.handle_thumbnail_file(mxc, user, dim, content).await
|
||||
}
|
||||
@@ -172,19 +161,16 @@ async fn fetch_content_unauthenticated(
|
||||
) -> Result<FileMeta> {
|
||||
use media::get_content::v3::{Request, Response};
|
||||
|
||||
let request = Request {
|
||||
allow_remote: true,
|
||||
allow_redirect: true,
|
||||
server_name: mxc.server_name.into(),
|
||||
media_id: mxc.media_id.into(),
|
||||
timeout_ms,
|
||||
};
|
||||
let mut request = Request::new(mxc.media_id.into(), mxc.server_name.into());
|
||||
request.allow_remote = true;
|
||||
request.allow_redirect = true;
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
let Response {
|
||||
file, content_type, content_disposition, ..
|
||||
} = self.federation_request(mxc, server, request).await?;
|
||||
} = self.federation_request_unauthenticated(mxc, server, request).await?;
|
||||
|
||||
let content = Content { file, content_type, content_disposition };
|
||||
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
||||
|
||||
self.handle_content_file(mxc, user, content).await
|
||||
}
|
||||
@@ -307,14 +293,14 @@ async fn location_request(&self, location: &str) -> Result<FileMeta> {
|
||||
}
|
||||
|
||||
#[implement(super::Service)]
|
||||
async fn federation_request<Request>(
|
||||
async fn federation_request<'i, Request>(
|
||||
&self,
|
||||
mxc: &Mxc<'_>,
|
||||
server: Option<&ServerName>,
|
||||
request: Request,
|
||||
) -> Result<Request::IncomingResponse>
|
||||
where
|
||||
Request: OutgoingRequest + Send + Debug,
|
||||
Request: OutgoingRequest::<Authentication = ServerSignatures, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Debug + Send,
|
||||
{
|
||||
self.services
|
||||
.sending
|
||||
@@ -322,6 +308,22 @@ where
|
||||
.await
|
||||
}
|
||||
|
||||
#[implement(super::Service)]
|
||||
async fn federation_request_unauthenticated<'i, Request>(
|
||||
&self,
|
||||
mxc: &Mxc<'_>,
|
||||
server: Option<&ServerName>,
|
||||
request: Request,
|
||||
) -> Result<Request::IncomingResponse>
|
||||
where
|
||||
Request: OutgoingRequest::<Authentication = NoAuthentication, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Debug + Send,
|
||||
{
|
||||
self.services
|
||||
.sending
|
||||
.send_unauthenticated_request(server.unwrap_or(mxc.server_name), request)
|
||||
.await
|
||||
}
|
||||
|
||||
#[implement(super::Service)]
|
||||
#[allow(deprecated)]
|
||||
pub async fn fetch_remote_thumbnail_legacy(
|
||||
@@ -333,22 +335,19 @@ pub async fn fetch_remote_thumbnail_legacy(
|
||||
media_id: &body.media_id,
|
||||
};
|
||||
|
||||
let mut request = media::get_content_thumbnail::v3::Request::new(body.media_id.clone(), body.server_name.clone(), body.width, body.height);
|
||||
request.method = body.method.clone();
|
||||
request.allow_remote = body.allow_remote;
|
||||
request.allow_redirect = body.allow_redirect;
|
||||
request.animated = body.animated;
|
||||
request.timeout_ms = body.timeout_ms;
|
||||
|
||||
self.check_legacy_freeze()?;
|
||||
self.check_fetch_authorized(&mxc)?;
|
||||
let response = self
|
||||
.services
|
||||
.sending
|
||||
.send_federation_request(mxc.server_name, media::get_content_thumbnail::v3::Request {
|
||||
allow_remote: body.allow_remote,
|
||||
height: body.height,
|
||||
width: body.width,
|
||||
method: body.method.clone(),
|
||||
server_name: body.server_name.clone(),
|
||||
media_id: body.media_id.clone(),
|
||||
timeout_ms: body.timeout_ms,
|
||||
allow_redirect: body.allow_redirect,
|
||||
animated: body.animated,
|
||||
})
|
||||
.send_unauthenticated_request(mxc.server_name, request)
|
||||
.await?;
|
||||
|
||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||
@@ -373,18 +372,17 @@ pub async fn fetch_remote_content_legacy(
|
||||
allow_redirect: bool,
|
||||
timeout_ms: Duration,
|
||||
) -> Result<media::get_content::v3::Response, Error> {
|
||||
let mut request = media::get_content::v3::Request::new(mxc.media_id.into(), mxc.server_name.into());
|
||||
request.allow_remote = true;
|
||||
request.allow_redirect = allow_redirect;
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
self.check_legacy_freeze()?;
|
||||
self.check_fetch_authorized(mxc)?;
|
||||
let response = self
|
||||
.services
|
||||
.sending
|
||||
.send_federation_request(mxc.server_name, media::get_content::v3::Request {
|
||||
allow_remote: true,
|
||||
server_name: mxc.server_name.into(),
|
||||
media_id: mxc.media_id.into(),
|
||||
timeout_ms,
|
||||
allow_redirect,
|
||||
})
|
||||
.send_unauthenticated_request(mxc.server_name, request)
|
||||
.await?;
|
||||
|
||||
let content_disposition = make_content_disposition(
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
use std::{cmp, num::Saturating as Sat};
|
||||
|
||||
use conduwuit::{Result, checked, err, implement};
|
||||
use ruma::{Mxc, UInt, UserId, http_headers::ContentDisposition, media::Method};
|
||||
use ruma::{UInt, UserId, http_headers::ContentDisposition, media::Method};
|
||||
use tokio::{
|
||||
fs,
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
};
|
||||
|
||||
use crate::media::mxc::Mxc;
|
||||
|
||||
use super::{FileMeta, data::Metadata};
|
||||
|
||||
/// Dimension specification for a thumbnail.
|
||||
|
||||
Reference in New Issue
Block a user