81 lines
2.4 KiB
Rust
81 lines
2.4 KiB
Rust
use std::time::UNIX_EPOCH;
|
|
|
|
use axum::{
|
|
Form,
|
|
extract::{FromRequest, Request},
|
|
http::{HeaderValue, StatusCode, Uri, header::CONTENT_TYPE},
|
|
response::{IntoResponse, Response},
|
|
};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Default, Deserialize)]
|
|
pub struct PasteInput {
|
|
#[serde(alias = "href")]
|
|
pub content: String,
|
|
pub language: Option<String>,
|
|
pub expires: Option<i64>,
|
|
pub max_views: Option<i64>,
|
|
}
|
|
|
|
impl PasteInput {
|
|
pub fn validate_url(&mut self) -> Result<(), StatusCode> {
|
|
self.content = self.content.trim().to_owned();
|
|
let uri: Uri = self.content.parse().map_err(|_| StatusCode::BAD_REQUEST)?;
|
|
if !matches!(uri.scheme_str(), Some("http" | "https"))
|
|
|| uri.host().is_none()
|
|
|| HeaderValue::from_str(&self.content).is_err()
|
|
{
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn expiration(&self) -> Result<Option<i64>, StatusCode> {
|
|
self.expires
|
|
.map(|expires| {
|
|
let now = UNIX_EPOCH
|
|
.elapsed()
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
.as_secs() as i64;
|
|
now.checked_add(expires).ok_or(StatusCode::BAD_REQUEST)
|
|
})
|
|
.transpose()
|
|
}
|
|
}
|
|
|
|
impl<S: Send + Sync> FromRequest<S> for PasteInput {
|
|
type Rejection = Response;
|
|
|
|
async fn from_request(request: Request, state: &S) -> Result<Self, Self::Rejection> {
|
|
let form = request
|
|
.headers()
|
|
.get(CONTENT_TYPE)
|
|
.and_then(|value| value.to_str().ok())
|
|
.and_then(|value| value.split(';').next())
|
|
.is_some_and(|value| {
|
|
value
|
|
.trim()
|
|
.eq_ignore_ascii_case("application/x-www-form-urlencoded")
|
|
});
|
|
let input = if form {
|
|
Form::<Self>::from_request(request, state)
|
|
.await
|
|
.map_err(IntoResponse::into_response)?
|
|
.0
|
|
} else {
|
|
Self {
|
|
content: String::from_request(request, state)
|
|
.await
|
|
.map_err(IntoResponse::into_response)?,
|
|
..Self::default()
|
|
}
|
|
};
|
|
if input.expires.is_some_and(|expires| expires < 0)
|
|
|| input.max_views.is_some_and(|max| max < 1)
|
|
{
|
|
return Err(StatusCode::BAD_REQUEST.into_response());
|
|
}
|
|
Ok(input)
|
|
}
|
|
}
|