Use incoming endpoint instead of hardcoded localhost

This commit is contained in:
projectmoon 2024-03-31 12:20:57 +02:00
parent 8f6e7a0d02
commit c5eeb147a5
4 changed files with 49 additions and 25 deletions

View File

@ -13,7 +13,7 @@ pub(crate) async fn receive_c2s_gemention(
) -> Result<MentionResponse, GementionError> { ) -> Result<MentionResponse, GementionError> {
let mention_upload = C2SMentionRequest::try_from(&client)?.validate()?; let mention_upload = C2SMentionRequest::try_from(&client)?.validate()?;
let mut mention = Mention::try_from(mention_upload)?; let mut mention = Mention::try_from(mention_upload)?;
verify(&client.url, &mut mention).await?; verify(&mut mention).await?;
Ok(MentionResponse::from(mention)) Ok(MentionResponse::from(mention))
} }
@ -26,7 +26,7 @@ pub(crate) async fn receive_s2s_gemention(
) -> Result<MentionResponse, GementionError> { ) -> Result<MentionResponse, GementionError> {
let mention_upload = C2SMentionRequest::try_from(&client)?; let mention_upload = C2SMentionRequest::try_from(&client)?;
let mut mention = Mention::try_from(mention_upload)?; let mut mention = Mention::try_from(mention_upload)?;
verify(&client.url, &mut mention).await?; verify(&mut mention).await?;
Ok(MentionResponse::from(mention)) Ok(MentionResponse::from(mention))
} }

View File

@ -33,7 +33,7 @@ fn parse_content_body(raw_body: &[u8]) -> Result<&str, GementionError> {
/// gemention. /// gemention.
pub(crate) struct C2SMentionRequest<'a> { pub(crate) struct C2SMentionRequest<'a> {
client: &'a Client, client: &'a Client,
receiving_endpoint: &'a Url, accessed_url: &'a Url,
} }
impl<'a> C2SMentionRequest<'a> { impl<'a> C2SMentionRequest<'a> {
@ -41,7 +41,7 @@ impl<'a> C2SMentionRequest<'a> {
if let Some(_) = client.titan { if let Some(_) = client.titan {
Ok(Self { Ok(Self {
client: &client, client: &client,
receiving_endpoint: &client.url, accessed_url: &client.url,
}) })
} else { } else {
Err(GementionError::NotTitanResource) Err(GementionError::NotTitanResource)
@ -84,8 +84,9 @@ impl<'a> C2SMentionRequest<'a> {
parse_content_body(self.raw_body()) parse_content_body(self.raw_body())
} }
pub fn receiving_endpoint(&self) -> &Url { /// The full URL that was accessed for the incoming request.
self.receiving_endpoint pub fn accessed_url(&self) -> &Url {
self.accessed_url
} }
} }
@ -105,6 +106,7 @@ pub(crate) enum MentionType {
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Mention { pub(crate) struct Mention {
receiving_endpoint: Url,
mention_type: MentionType, mention_type: MentionType,
target: String, target: String,
user: String, user: String,
@ -117,6 +119,10 @@ impl Mention {
&self.target &self.target
} }
pub fn receiving_endpoint(&self) -> &Url {
&self.receiving_endpoint
}
pub fn target_url(&self) -> Url { pub fn target_url(&self) -> Url {
Url::parse(&format!("gemini://{}", &self.target)).unwrap() Url::parse(&format!("gemini://{}", &self.target)).unwrap()
} }
@ -223,6 +229,15 @@ impl<'a> TryFrom<C2SMentionRequest<'a>> for Mention {
.username() .username()
.ok_or(GementionError::UsernameNotProvided)?; .ok_or(GementionError::UsernameNotProvided)?;
let receiving_endpoint = Url::parse(
resource
.accessed_url()
.as_str()
.split(&target)
.next()
.ok_or(GementionError::InvalidBody)?,
)?;
let content = resource.content()?.to_owned(); let content = resource.content()?.to_owned();
let content = if !content.is_empty() { let content = if !content.is_empty() {
Some(content) Some(content)
@ -235,6 +250,7 @@ impl<'a> TryFrom<C2SMentionRequest<'a>> for Mention {
mention_type, mention_type,
content, content,
target, target,
receiving_endpoint,
verification_status: VerificationStatus::NotYetVerified, verification_status: VerificationStatus::NotYetVerified,
}) })
} else { } else {

View File

@ -48,7 +48,7 @@ pub(crate) enum VerificationFailureReason {
/// One or more mention links exist, but they are not to this /// One or more mention links exist, but they are not to this
/// endpoint, or for this page. /// endpoint, or for this page.
MentionLinkIncorrect, MentionLinkIncorrect(String),
/// There was an error during the verification process. /// There was an error during the verification process.
Error(GementionError), Error(GementionError),
@ -58,7 +58,9 @@ impl ToString for VerificationFailureReason {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
Self::NoMentionLinkFound => String::from("No mention link found"), Self::NoMentionLinkFound => String::from("No mention link found"),
Self::MentionLinkIncorrect => String::from("Mention link points to wrong target"), Self::MentionLinkIncorrect(endpoint) => {
format!("Target URL does not accept gementions from endpoint: {}", endpoint)
}
Self::Error(err) => err.to_string(), Self::Error(err) => err.to_string(),
} }
} }

View File

@ -7,13 +7,12 @@ use crate::error::GementionError;
use crate::models::mentions::Mention; use crate::models::mentions::Mention;
use crate::models::verification::*; use crate::models::verification::*;
const OUR_ENDPOINT: &'static str = "titan://localhost/receive/"; fn is_mention_link(endpoint: &Url, gemtext_link: &str) -> bool {
gemtext_link.starts_with(endpoint.as_str())
fn is_mention_link(gemtext_link: &str) -> bool {
gemtext_link.starts_with(OUR_ENDPOINT)
} }
fn scan_for_gemention_endpoints( fn scan_for_gemention_endpoints(
endpoint: &Url,
meta: GeminiMetadata, meta: GeminiMetadata,
ast: GeminiAst, ast: GeminiAst,
) -> (VerificationSource, Vec<String>) { ) -> (VerificationSource, Vec<String>) {
@ -29,7 +28,7 @@ fn scan_for_gemention_endpoints(
.inner() .inner()
.into_iter() .into_iter()
.filter_map(|node| match node { .filter_map(|node| match node {
GemtextNode::Link { ref to, .. } if is_mention_link(to) => Some(to), GemtextNode::Link { ref to, .. } if is_mention_link(endpoint, to) => Some(to),
_ => None, _ => None,
}) })
.cloned() .cloned()
@ -38,14 +37,17 @@ fn scan_for_gemention_endpoints(
(VerificationSource::Page, endpoints) (VerificationSource::Page, endpoints)
} }
fn verify_mentions<S: AsRef<str>>( fn verify_mentions(
expected_link: S, mention: &mut Mention,
source_and_endpoints: (VerificationSource, Vec<String>), source_and_endpoints: (VerificationSource, Vec<String>),
) -> VerificationStatus { ) -> Result<VerificationStatus, GementionError> {
let (verification_source, mentions) = source_and_endpoints; let (verification_source, mentions) = source_and_endpoints;
// TODO need to normalize url from page as well as ours, to make
// sure things like ports being in url or not are handled.
let expected_link = mention.receiving_endpoint().join(mention.target())?;
let expected_link = expected_link.as_ref(); let expected_link = expected_link.as_ref();
if mentions.len() > 0 { let verification_status = if mentions.len() > 0 {
// We have links that go to our endpoint. Scan links for the // We have links that go to our endpoint. Scan links for the
// one we expect (i.e. for the target), otherwise we say // one we expect (i.e. for the target), otherwise we say
// incorrect link. // incorrect link.
@ -62,16 +64,19 @@ fn verify_mentions<S: AsRef<str>>(
} }
}) })
.unwrap_or(VerificationStatus::Invalid( .unwrap_or(VerificationStatus::Invalid(
VerificationFailureReason::MentionLinkIncorrect, VerificationFailureReason::MentionLinkIncorrect(
mention.receiving_endpoint().to_string(),
),
)) ))
} else { } else {
VerificationStatus::Invalid(VerificationFailureReason::NoMentionLinkFound) VerificationStatus::Invalid(VerificationFailureReason::NoMentionLinkFound)
} };
Ok(verification_status)
} }
async fn verify_mention(expected_link: &Url, mention: &mut Mention) -> Result<(), GementionError> { async fn verify_mention(mention: &mut Mention) -> Result<(), GementionError> {
let url = Url::parse(&format!("gemini://{}", mention.target()))?; let url = Url::parse(&format!("gemini://{}", mention.target()))?;
println!("expected link is {}", expected_link);
let resp = germ_request(&url).await?; let resp = germ_request(&url).await?;
let meta = GeminiMetadata::from_string(resp.meta()); let meta = GeminiMetadata::from_string(resp.meta());
@ -82,13 +87,14 @@ async fn verify_mention(expected_link: &Url, mention: &mut Mention) -> Result<()
.ok_or(GementionError::NoContentFoundForTarget)?; .ok_or(GementionError::NoContentFoundForTarget)?;
let ast = GeminiAst::from_string(content); let ast = GeminiAst::from_string(content);
let source_and_endpoints = scan_for_gemention_endpoints(meta, ast); let source_and_endpoints =
let status = verify_mentions(expected_link, source_and_endpoints); scan_for_gemention_endpoints(mention.receiving_endpoint(), meta, ast);
let status = verify_mentions(mention, source_and_endpoints)?;
mention.set_verify_status(status); mention.set_verify_status(status);
Ok(()) Ok(())
} }
pub(crate) async fn verify(expected_url: &Url, mention: &mut Mention) -> Result<(), GementionError> { pub(crate) async fn verify(mention: &mut Mention) -> Result<(), GementionError> {
verify_mention(expected_url, mention).await verify_mention(mention).await
} }