2023-02-28 21:14:11 +00:00
|
|
|
use url::Url;
|
2023-02-28 16:14:54 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct UrlRewriteRule {
|
2023-02-28 21:14:11 +00:00
|
|
|
pub host: String,
|
2023-02-28 16:14:54 +00:00
|
|
|
pub replacement: String,
|
|
|
|
}
|
|
|
|
|
2023-02-28 21:14:11 +00:00
|
|
|
impl UrlRewriteRule {
|
|
|
|
pub fn new(host: impl ToString, replacement: impl ToString) -> Self {
|
|
|
|
Self {
|
|
|
|
host: host.to_string(),
|
|
|
|
replacement: replacement.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct RuleSet {
|
2023-02-28 21:27:47 +00:00
|
|
|
name: String,
|
2023-02-28 21:14:11 +00:00
|
|
|
swf_domain_rewrite_rules: Vec<UrlRewriteRule>,
|
|
|
|
}
|
|
|
|
|
2023-02-28 16:24:27 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2023-02-28 16:14:54 +00:00
|
|
|
pub struct CompatibilityRules {
|
2023-02-28 21:14:11 +00:00
|
|
|
rule_sets: Vec<RuleSet>,
|
2023-02-28 16:14:54 +00:00
|
|
|
}
|
|
|
|
|
2023-02-28 16:24:27 +00:00
|
|
|
impl Default for CompatibilityRules {
|
|
|
|
#[cfg(feature = "default_compatibility_rules")]
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::builtin_rules()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "default_compatibility_rules"))]
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::empty()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-28 16:14:54 +00:00
|
|
|
impl CompatibilityRules {
|
|
|
|
pub fn empty() -> Self {
|
2023-02-28 21:14:11 +00:00
|
|
|
Self { rule_sets: vec![] }
|
2023-02-28 16:14:54 +00:00
|
|
|
}
|
|
|
|
|
2023-02-28 16:24:27 +00:00
|
|
|
/// Default rules for general SWF compatibility.
|
|
|
|
/// Rules that are added here must, to the best of our ability:
|
|
|
|
/// - Only affect content that cannot run anymore, such as requiring lost assets
|
|
|
|
/// - Not allow people to easily pirate or cheat games more than they can already
|
2023-02-28 16:14:54 +00:00
|
|
|
pub fn builtin_rules() -> Self {
|
2023-02-28 21:27:47 +00:00
|
|
|
// Replaces konggames.com domains with kongregate.com to fool old sitelocks that no longer work.
|
2023-02-28 21:14:11 +00:00
|
|
|
let kongregate_sitelock = RuleSet {
|
2023-02-28 21:27:47 +00:00
|
|
|
name: "kongregate_sitelock".to_string(),
|
2023-02-28 21:14:11 +00:00
|
|
|
swf_domain_rewrite_rules: vec![UrlRewriteRule::new(
|
|
|
|
"*.konggames.com",
|
|
|
|
"kongregate.com",
|
|
|
|
)],
|
|
|
|
};
|
|
|
|
|
2023-02-28 16:14:54 +00:00
|
|
|
Self {
|
2023-02-28 21:14:11 +00:00
|
|
|
rule_sets: vec![kongregate_sitelock],
|
2023-02-28 16:14:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rewrite_swf_url(&self, original_url: String) -> String {
|
2023-02-28 21:14:11 +00:00
|
|
|
let mut url = match Url::parse(&original_url) {
|
|
|
|
Ok(url) => url,
|
|
|
|
Err(e) => {
|
|
|
|
tracing::warn!("Couldn't rewrite swf url {original_url}: {e}");
|
|
|
|
return original_url;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for rule_set in &self.rule_sets {
|
|
|
|
for rule in &rule_set.swf_domain_rewrite_rules {
|
|
|
|
if let Some(host) = url.host_str() {
|
|
|
|
if domain_matches(&rule.host, host) {
|
2023-02-28 21:27:47 +00:00
|
|
|
tracing::info!(
|
|
|
|
"Rewriting swf url due to compatibility ruleset '{}'",
|
|
|
|
rule_set.name
|
|
|
|
);
|
2023-02-28 21:14:11 +00:00
|
|
|
if let Err(e) = url.set_host(Some(&rule.replacement)) {
|
|
|
|
tracing::warn!(
|
|
|
|
"Couldn't rewrite swf host to {}: {e}",
|
|
|
|
rule.replacement
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-28 16:14:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-28 21:14:11 +00:00
|
|
|
url.to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests that two domains match.
|
|
|
|
///
|
|
|
|
/// Expected string may start with `*.` to allow for any further subdomains.
|
|
|
|
pub fn domain_matches(expected: &str, actual: &str) -> bool {
|
|
|
|
let mut expected_parts = expected.rsplit('.').peekable();
|
|
|
|
let mut actual_parts = actual.rsplit('.');
|
|
|
|
let mut allow_subdomains = false;
|
|
|
|
|
|
|
|
while let Some(test) = expected_parts.next() {
|
|
|
|
if test == "*" && expected_parts.peek().is_none() {
|
|
|
|
allow_subdomains = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
match actual_parts.next() {
|
|
|
|
Some(actual) => {
|
|
|
|
if !test.eq_ignore_ascii_case(actual) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => return false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
allow_subdomains || actual_parts.next().is_none()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::compatibility_rules::domain_matches;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_domain_matches() {
|
2023-03-01 14:36:17 +00:00
|
|
|
assert!(domain_matches("foo.example.com", "foo.example.com"));
|
|
|
|
assert!(domain_matches("*.example.com", "foo.example.com"));
|
|
|
|
assert!(domain_matches("*.foo.example.com", "foo.example.com"));
|
|
|
|
assert!(domain_matches("*.com", "foo.example.com"));
|
|
|
|
assert!(domain_matches("*", "foo.example.com"));
|
|
|
|
assert!(!domain_matches("", "foo.example.com"));
|
|
|
|
assert!(!domain_matches("com", "foo.example.com"));
|
|
|
|
assert!(!domain_matches("example.com", "foo.example.com"));
|
|
|
|
assert!(!domain_matches("bar.example.com", "foo.example.com"));
|
|
|
|
assert!(!domain_matches("bar.foo.example.com", "foo.example.com"));
|
2023-02-28 16:14:54 +00:00
|
|
|
}
|
|
|
|
}
|