diff --git a/src/comments.rs b/src/comments.rs index 1584f23..87823c8 100644 --- a/src/comments.rs +++ b/src/comments.rs @@ -13,7 +13,7 @@ pub(crate) async fn receive_c2s_gemention( ) -> Result { let mention_upload = C2SMentionRequest::try_from(&client)?.validate()?; let mut mention = Mention::try_from(mention_upload)?; - verify(&client.url, &mut mention).await?; + verify(&mut mention).await?; Ok(MentionResponse::from(mention)) } @@ -26,7 +26,7 @@ pub(crate) async fn receive_s2s_gemention( ) -> Result { let mention_upload = C2SMentionRequest::try_from(&client)?; let mut mention = Mention::try_from(mention_upload)?; - verify(&client.url, &mut mention).await?; + verify(&mut mention).await?; Ok(MentionResponse::from(mention)) } diff --git a/src/models/mentions.rs b/src/models/mentions.rs index 9217d3e..00161f7 100644 --- a/src/models/mentions.rs +++ b/src/models/mentions.rs @@ -33,7 +33,7 @@ fn parse_content_body(raw_body: &[u8]) -> Result<&str, GementionError> { /// gemention. pub(crate) struct C2SMentionRequest<'a> { client: &'a Client, - receiving_endpoint: &'a Url, + accessed_url: &'a Url, } impl<'a> C2SMentionRequest<'a> { @@ -41,7 +41,7 @@ impl<'a> C2SMentionRequest<'a> { if let Some(_) = client.titan { Ok(Self { client: &client, - receiving_endpoint: &client.url, + accessed_url: &client.url, }) } else { Err(GementionError::NotTitanResource) @@ -84,8 +84,9 @@ impl<'a> C2SMentionRequest<'a> { parse_content_body(self.raw_body()) } - pub fn receiving_endpoint(&self) -> &Url { - self.receiving_endpoint + /// The full URL that was accessed for the incoming request. + pub fn accessed_url(&self) -> &Url { + self.accessed_url } } @@ -105,6 +106,7 @@ pub(crate) enum MentionType { #[derive(Debug)] pub(crate) struct Mention { + receiving_endpoint: Url, mention_type: MentionType, target: String, user: String, @@ -117,6 +119,10 @@ impl Mention { &self.target } + pub fn receiving_endpoint(&self) -> &Url { + &self.receiving_endpoint + } + pub fn target_url(&self) -> Url { Url::parse(&format!("gemini://{}", &self.target)).unwrap() } @@ -223,6 +229,15 @@ impl<'a> TryFrom> for Mention { .username() .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 = if !content.is_empty() { Some(content) @@ -235,6 +250,7 @@ impl<'a> TryFrom> for Mention { mention_type, content, target, + receiving_endpoint, verification_status: VerificationStatus::NotYetVerified, }) } else { diff --git a/src/models/verification.rs b/src/models/verification.rs index 3b28baf..1f27a76 100644 --- a/src/models/verification.rs +++ b/src/models/verification.rs @@ -48,7 +48,7 @@ pub(crate) enum VerificationFailureReason { /// One or more mention links exist, but they are not to this /// endpoint, or for this page. - MentionLinkIncorrect, + MentionLinkIncorrect(String), /// There was an error during the verification process. Error(GementionError), @@ -58,7 +58,9 @@ impl ToString for VerificationFailureReason { fn to_string(&self) -> String { match self { 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(), } } diff --git a/src/verification/mod.rs b/src/verification/mod.rs index 021a7aa..ec69855 100644 --- a/src/verification/mod.rs +++ b/src/verification/mod.rs @@ -7,13 +7,12 @@ use crate::error::GementionError; use crate::models::mentions::Mention; use crate::models::verification::*; -const OUR_ENDPOINT: &'static str = "titan://localhost/receive/"; - -fn is_mention_link(gemtext_link: &str) -> bool { - gemtext_link.starts_with(OUR_ENDPOINT) +fn is_mention_link(endpoint: &Url, gemtext_link: &str) -> bool { + gemtext_link.starts_with(endpoint.as_str()) } fn scan_for_gemention_endpoints( + endpoint: &Url, meta: GeminiMetadata, ast: GeminiAst, ) -> (VerificationSource, Vec) { @@ -29,7 +28,7 @@ fn scan_for_gemention_endpoints( .inner() .into_iter() .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, }) .cloned() @@ -38,14 +37,17 @@ fn scan_for_gemention_endpoints( (VerificationSource::Page, endpoints) } -fn verify_mentions>( - expected_link: S, +fn verify_mentions( + mention: &mut Mention, source_and_endpoints: (VerificationSource, Vec), -) -> VerificationStatus { +) -> Result { 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(); - if mentions.len() > 0 { + let verification_status = if mentions.len() > 0 { // We have links that go to our endpoint. Scan links for the // one we expect (i.e. for the target), otherwise we say // incorrect link. @@ -62,16 +64,19 @@ fn verify_mentions>( } }) .unwrap_or(VerificationStatus::Invalid( - VerificationFailureReason::MentionLinkIncorrect, + VerificationFailureReason::MentionLinkIncorrect( + mention.receiving_endpoint().to_string(), + ), )) } else { 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()))?; - println!("expected link is {}", expected_link); let resp = germ_request(&url).await?; 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)?; let ast = GeminiAst::from_string(content); - let source_and_endpoints = scan_for_gemention_endpoints(meta, ast); - let status = verify_mentions(expected_link, source_and_endpoints); + let 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); Ok(()) } -pub(crate) async fn verify(expected_url: &Url, mention: &mut Mention) -> Result<(), GementionError> { - verify_mention(expected_url, mention).await +pub(crate) async fn verify(mention: &mut Mention) -> Result<(), GementionError> { + verify_mention(mention).await }