From 7399a398a7a35831ac6148a9b3da8269f5491baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kl=C3=A4hn?= <39526136+Septias@users.noreply.github.com> Date: Thu, 26 Oct 2023 11:46:51 +0200 Subject: [PATCH] api: add mailto parse api (#4829) close #4620 This PR introduces a new core API to parse mailto links into a uniform data format. This could be used to unify the different implementations on the current platforms. To complete this PR we have to decide for which APIs we want to expose this (now) internal API (c, python, json-rpc, etc.), and if we want such an API at all as it doesn't have a corresponding UI-PR and is not _really_ needed. --- src/tools.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/src/tools.rs b/src/tools.rs index f81aa97dc2..20a73eff5f 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -20,6 +20,7 @@ use mailparse::headers::Headers; use mailparse::MailHeaderMap; use rand::{thread_rng, Rng}; use tokio::{fs, io}; +use url::Url; use crate::chat::{add_device_msg, add_device_msg_with_importance}; use crate::constants::{DC_ELLIPSIS, DC_OUTDATED_WARNING_DAYS}; @@ -481,7 +482,43 @@ pub(crate) fn time() -> i64 { .as_secs() as i64 } -/// Very simple email address wrapper. +/// Struct containing all mailto information +#[derive(Debug, Default, Eq, PartialEq)] +pub struct MailTo { + pub to: Vec, + pub subject: Option, + pub body: Option, +} + +/// Parse mailto urls +pub fn parse_mailto(mailto_url: &str) -> Option { + if let Ok(url) = Url::parse(mailto_url) { + if url.scheme() == "mailto" { + let mut mailto: MailTo = Default::default(); + // Extract the email address + url.path().split(',').for_each(|email| { + if let Ok(email) = EmailAddress::new(email) { + mailto.to.push(email); + } + }); + + // Extract query parameters + for (key, value) in url.query_pairs() { + if key == "subject" { + mailto.subject = Some(value.to_string()); + } else if key == "body" { + mailto.body = Some(value.to_string()); + } + } + Some(mailto) + } else { + None + } + } else { + None + } +} + /// /// Represents an email address, right now just the `name@domain` portion. /// @@ -1283,4 +1320,55 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true"; assert_eq!(remove_subject_prefix("Fwd: Subject"), "Subject"); assert_eq!(remove_subject_prefix("Fw: Subject"), "Subject"); } + + #[test] + fn test_parse_mailto() { + let mailto_url = "mailto:someone@example.com"; + let reps = parse_mailto(mailto_url); + assert_eq!( + Some(MailTo { + to: vec![EmailAddress { + local: "someone".to_string(), + domain: "example.com".to_string() + }], + subject: None, + body: None + }), + reps + ); + + let mailto_url = "mailto:someone@example.com?subject=Hello%20World"; + let reps = parse_mailto(mailto_url); + assert_eq!( + Some(MailTo { + to: vec![EmailAddress { + local: "someone".to_string(), + domain: "example.com".to_string() + }], + subject: Some("Hello World".to_string()), + body: None + }), + reps + ); + + let mailto_url = "mailto:someone@example.com,someoneelse@example.com?subject=Hello%20World&body=This%20is%20a%20test"; + let reps = parse_mailto(mailto_url); + assert_eq!( + Some(MailTo { + to: vec![ + EmailAddress { + local: "someone".to_string(), + domain: "example.com".to_string() + }, + EmailAddress { + local: "someoneelse".to_string(), + domain: "example.com".to_string() + } + ], + subject: Some("Hello World".to_string()), + body: Some("This is a test".to_string()) + }), + reps + ); + } }