mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-17 07:41:23 +01:00
Add MxcData helper
This commit is contained in:
parent
64b3c357dd
commit
94204415ee
2 changed files with 78 additions and 28 deletions
|
|
@ -15,7 +15,12 @@ use ruma::{
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{service::media::FileMeta, services, utils, Ar, Error, Ra, Result};
|
use crate::{
|
||||||
|
service::media::FileMeta,
|
||||||
|
services,
|
||||||
|
utils::{self, MxcData},
|
||||||
|
Ar, Error, Ra, Result,
|
||||||
|
};
|
||||||
|
|
||||||
const MXC_LENGTH: usize = 32;
|
const MXC_LENGTH: usize = 32;
|
||||||
|
|
||||||
|
|
@ -133,16 +138,13 @@ pub(crate) async fn get_media_config_route(
|
||||||
pub(crate) async fn create_content_route(
|
pub(crate) async fn create_content_route(
|
||||||
body: Ar<create_content::v3::Request>,
|
body: Ar<create_content::v3::Request>,
|
||||||
) -> Result<Ra<create_content::v3::Response>> {
|
) -> Result<Ra<create_content::v3::Response>> {
|
||||||
let mxc = format!(
|
let media_id = utils::random_string(MXC_LENGTH);
|
||||||
"mxc://{}/{}",
|
let mxc = MxcData::new(services().globals.server_name(), &media_id)?;
|
||||||
services().globals.server_name(),
|
|
||||||
utils::random_string(MXC_LENGTH)
|
|
||||||
);
|
|
||||||
|
|
||||||
services()
|
services()
|
||||||
.media
|
.media
|
||||||
.create(
|
.create(
|
||||||
mxc.clone(),
|
mxc.to_string(),
|
||||||
body.filename
|
body.filename
|
||||||
.clone()
|
.clone()
|
||||||
.map(|filename| ContentDisposition {
|
.map(|filename| ContentDisposition {
|
||||||
|
|
@ -163,18 +165,16 @@ pub(crate) async fn create_content_route(
|
||||||
|
|
||||||
#[allow(deprecated)] // unauthenticated media
|
#[allow(deprecated)] // unauthenticated media
|
||||||
pub(crate) async fn get_remote_content(
|
pub(crate) async fn get_remote_content(
|
||||||
mxc: &str,
|
mxc: &MxcData<'_>,
|
||||||
server_name: &ruma::ServerName,
|
|
||||||
media_id: String,
|
|
||||||
) -> Result<legacy_media::get_content::v3::Response, Error> {
|
) -> Result<legacy_media::get_content::v3::Response, Error> {
|
||||||
let content_response = services()
|
let content_response = services()
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
server_name,
|
mxc.server_name,
|
||||||
legacy_media::get_content::v3::Request {
|
legacy_media::get_content::v3::Request {
|
||||||
allow_remote: false,
|
allow_remote: false,
|
||||||
server_name: server_name.to_owned(),
|
server_name: mxc.server_name.to_owned(),
|
||||||
media_id,
|
media_id: mxc.media_id.to_owned(),
|
||||||
timeout_ms: Duration::from_secs(20),
|
timeout_ms: Duration::from_secs(20),
|
||||||
allow_redirect: false,
|
allow_redirect: false,
|
||||||
},
|
},
|
||||||
|
|
@ -184,7 +184,7 @@ pub(crate) async fn get_remote_content(
|
||||||
services()
|
services()
|
||||||
.media
|
.media
|
||||||
.create(
|
.create(
|
||||||
mxc.to_owned(),
|
mxc.to_string(),
|
||||||
content_response.content_disposition.as_ref(),
|
content_response.content_disposition.as_ref(),
|
||||||
content_response.content_type.as_deref(),
|
content_response.content_type.as_deref(),
|
||||||
&content_response.file,
|
&content_response.file,
|
||||||
|
|
@ -225,13 +225,13 @@ pub(crate) async fn get_content_route(
|
||||||
async fn get_content_route_ruma(
|
async fn get_content_route_ruma(
|
||||||
body: Ar<legacy_media::get_content::v3::Request>,
|
body: Ar<legacy_media::get_content::v3::Request>,
|
||||||
) -> Result<legacy_media::get_content::v3::Response> {
|
) -> Result<legacy_media::get_content::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = MxcData::new(&body.server_name, &body.media_id)?;
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
content_type,
|
content_type,
|
||||||
file,
|
file,
|
||||||
..
|
..
|
||||||
}) = services().media.get(mxc.clone()).await?
|
}) = services().media.get(mxc.to_string()).await?
|
||||||
{
|
{
|
||||||
Ok(legacy_media::get_content::v3::Response {
|
Ok(legacy_media::get_content::v3::Response {
|
||||||
file,
|
file,
|
||||||
|
|
@ -245,9 +245,7 @@ async fn get_content_route_ruma(
|
||||||
} else if &*body.server_name != services().globals.server_name()
|
} else if &*body.server_name != services().globals.server_name()
|
||||||
&& body.allow_remote
|
&& body.allow_remote
|
||||||
{
|
{
|
||||||
let remote_content_response =
|
let remote_content_response = get_remote_content(&mxc).await?;
|
||||||
get_remote_content(&mxc, &body.server_name, body.media_id.clone())
|
|
||||||
.await?;
|
|
||||||
Ok(legacy_media::get_content::v3::Response {
|
Ok(legacy_media::get_content::v3::Response {
|
||||||
file: remote_content_response.file,
|
file: remote_content_response.file,
|
||||||
content_disposition: Some(content_disposition_for(
|
content_disposition: Some(content_disposition_for(
|
||||||
|
|
@ -288,13 +286,13 @@ pub(crate) async fn get_content_as_filename_route(
|
||||||
pub(crate) async fn get_content_as_filename_route_ruma(
|
pub(crate) async fn get_content_as_filename_route_ruma(
|
||||||
body: Ar<legacy_media::get_content_as_filename::v3::Request>,
|
body: Ar<legacy_media::get_content_as_filename::v3::Request>,
|
||||||
) -> Result<legacy_media::get_content_as_filename::v3::Response> {
|
) -> Result<legacy_media::get_content_as_filename::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = MxcData::new(&body.server_name, &body.media_id)?;
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
content_type,
|
content_type,
|
||||||
file,
|
file,
|
||||||
..
|
..
|
||||||
}) = services().media.get(mxc.clone()).await?
|
}) = services().media.get(mxc.to_string()).await?
|
||||||
{
|
{
|
||||||
Ok(legacy_media::get_content_as_filename::v3::Response {
|
Ok(legacy_media::get_content_as_filename::v3::Response {
|
||||||
file,
|
file,
|
||||||
|
|
@ -308,9 +306,7 @@ pub(crate) async fn get_content_as_filename_route_ruma(
|
||||||
} else if &*body.server_name != services().globals.server_name()
|
} else if &*body.server_name != services().globals.server_name()
|
||||||
&& body.allow_remote
|
&& body.allow_remote
|
||||||
{
|
{
|
||||||
let remote_content_response =
|
let remote_content_response = get_remote_content(&mxc).await?;
|
||||||
get_remote_content(&mxc, &body.server_name, body.media_id.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(legacy_media::get_content_as_filename::v3::Response {
|
Ok(legacy_media::get_content_as_filename::v3::Response {
|
||||||
content_disposition: Some(content_disposition_for(
|
content_disposition: Some(content_disposition_for(
|
||||||
|
|
@ -366,7 +362,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||||
async fn get_content_thumbnail_route_ruma(
|
async fn get_content_thumbnail_route_ruma(
|
||||||
body: Ar<legacy_media::get_content_thumbnail::v3::Request>,
|
body: Ar<legacy_media::get_content_thumbnail::v3::Request>,
|
||||||
) -> Result<legacy_media::get_content_thumbnail::v3::Response> {
|
) -> Result<legacy_media::get_content_thumbnail::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = MxcData::new(&body.server_name, &body.media_id)?;
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
content_type,
|
content_type,
|
||||||
|
|
@ -375,7 +371,7 @@ async fn get_content_thumbnail_route_ruma(
|
||||||
}) = services()
|
}) = services()
|
||||||
.media
|
.media
|
||||||
.get_thumbnail(
|
.get_thumbnail(
|
||||||
mxc.clone(),
|
mxc.to_string(),
|
||||||
body.width.try_into().map_err(|_| {
|
body.width.try_into().map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.")
|
Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.")
|
||||||
})?,
|
})?,
|
||||||
|
|
@ -414,7 +410,7 @@ async fn get_content_thumbnail_route_ruma(
|
||||||
services()
|
services()
|
||||||
.media
|
.media
|
||||||
.upload_thumbnail(
|
.upload_thumbnail(
|
||||||
mxc,
|
mxc.to_string(),
|
||||||
None,
|
None,
|
||||||
get_thumbnail_response.content_type.as_deref(),
|
get_thumbnail_response.content_type.as_deref(),
|
||||||
body.width.try_into().expect("all UInts are valid u32s"),
|
body.width.try_into().expect("all UInts are valid u32s"),
|
||||||
|
|
|
||||||
56
src/utils.rs
56
src/utils.rs
|
|
@ -13,9 +13,12 @@ use cmp::Ordering;
|
||||||
use rand::{prelude::*, rngs::OsRng};
|
use rand::{prelude::*, rngs::OsRng};
|
||||||
use ring::digest;
|
use ring::digest;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject,
|
api::client::error::ErrorKind, canonical_json::try_from_json_map,
|
||||||
|
CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError, OwnedMxcUri,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{Error, Result};
|
||||||
|
|
||||||
// Hopefully we have a better chat protocol in 530 years
|
// Hopefully we have a better chat protocol in 530 years
|
||||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
|
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
|
||||||
pub(crate) fn millis_since_unix_epoch() -> u64 {
|
pub(crate) fn millis_since_unix_epoch() -> u64 {
|
||||||
|
|
@ -243,6 +246,57 @@ pub(crate) fn dbg_truncate_str(s: &str, mut max_len: usize) -> Cow<'_, str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data that makes up an `mxc://` URL.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct MxcData<'a> {
|
||||||
|
pub(crate) server_name: &'a ruma::ServerName,
|
||||||
|
pub(crate) media_id: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MxcData<'a> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
server_name: &'a ruma::ServerName,
|
||||||
|
media_id: &'a str,
|
||||||
|
) -> Result<Self> {
|
||||||
|
if !media_id.bytes().all(|b| {
|
||||||
|
matches!(b,
|
||||||
|
b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'-' | b'_'
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Invalid MXC media id",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
server_name,
|
||||||
|
media_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MxcData<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "mxc://{}/{}", self.server_name, self.media_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MxcData<'_>> for OwnedMxcUri {
|
||||||
|
fn from(value: MxcData<'_>) -> Self {
|
||||||
|
value.to_string().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a MxcUri> for MxcData<'a> {
|
||||||
|
type Error = MxcUriError;
|
||||||
|
|
||||||
|
fn try_from(value: &'a MxcUri) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self::new(value.server_name()?, value.media_id()?)
|
||||||
|
.expect("validated MxcUri should always be valid MxcData"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::utils::dbg_truncate_str;
|
use crate::utils::dbg_truncate_str;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue