use once_cell::sync::Lazy;
use regex::Regex;
use crate::RXingResult;
use super::{ParsedClientResult, ResultParser, URIParsedRXingResult};
static ALLOWED_URI_CHARS: Lazy<Regex> = Lazy::new(|| {
Regex::new(ALLOWED_URI_CHARS_PATTERN).expect("Regex patterns should always copile")
});
static USER_IN_HOST: Lazy<Regex> =
Lazy::new(|| Regex::new(":/*([^/@]+)@[^/]+").expect("Regex patterns should always copile"));
static URL_WITH_PROTOCOL_PATTERN: Lazy<Regex> =
Lazy::new(|| Regex::new("[a-zA-Z][a-zA-Z0-9+-.]+:").unwrap());
static URL_WITHOUT_PROTOCOL_PATTERN: Lazy<Regex> =
Lazy::new(|| Regex::new("([a-zA-Z0-9\\-]+\\.){1,6}[a-zA-Z]{2,}(:\\d{1,5})?(/|\\?|$)").unwrap());
const ALLOWED_URI_CHARS_PATTERN: &str = "[-._~:/?#\\[\\]@!$&'()*+,;=%A-Za-z0-9]+";
pub fn parse(result: &RXingResult) -> Option<ParsedClientResult> {
let raw_text = ResultParser::getMassagedText(result);
if raw_text.starts_with("URL:") || raw_text.starts_with("URI:") {
return Some(ParsedClientResult::URIResult(URIParsedRXingResult::new(
raw_text[4..].trim().to_owned(),
String::default(),
)));
}
let raw_text = raw_text.trim();
if !is_basically_valid_uri(raw_text) || is_possibly_malicious_uri(raw_text) {
return None;
}
Some(ParsedClientResult::URIResult(URIParsedRXingResult::new(
raw_text.to_owned(),
String::default(),
)))
}
pub fn is_possibly_malicious_uri(uri: &str) -> bool {
let allowed = if let Some(fnd) = ALLOWED_URI_CHARS.find(uri) {
fnd.start() == 0 && fnd.end() == uri.len()
} else {
false
};
let user = USER_IN_HOST.is_match(uri);
!allowed || user
}
pub fn is_basically_valid_uri(uri: &str) -> bool {
if uri.contains(' ') {
return false;
}
if let Some(found) = URL_WITH_PROTOCOL_PATTERN.find(uri) {
if found.start() == 0 {
return true;
}
}
if let Some(found) = URL_WITHOUT_PROTOCOL_PATTERN.find(uri) {
found.start() == 0
} else {
false
}
}