From e1f014ea85fb8cd953ea6c3bbf83ec147606fd83 Mon Sep 17 00:00:00 2001 From: Marco Bartoli Date: Sat, 25 May 2024 00:07:36 +0200 Subject: [PATCH] desktop: Add HTTP Referer and Cookie (#16424) --- desktop/assets/texts/en-US/settings.ftl | 2 + desktop/src/cli.rs | 9 ++++ desktop/src/gui/dialogs/open_dialog.rs | 60 ++++++++++++++++++++++++ desktop/src/player.rs | 4 ++ frontend-utils/src/backends/navigator.rs | 19 +++++++- frontend-utils/src/player_options.rs | 4 ++ 6 files changed, 97 insertions(+), 1 deletion(-) diff --git a/desktop/assets/texts/en-US/settings.ftl b/desktop/assets/texts/en-US/settings.ftl index fdbb8b3f5..1ad36f41f 100644 --- a/desktop/assets/texts/en-US/settings.ftl +++ b/desktop/assets/texts/en-US/settings.ftl @@ -5,6 +5,8 @@ movie-parameters = Movie Parameters custom-base-url = Custom Base URL spoof-swf-url = Spoof SWF URL +referer-url = Referer URL +cookie = Cookie proxy = Proxy diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs index ad9eac0d1..262ad6858 100644 --- a/desktop/src/cli.rs +++ b/desktop/src/cli.rs @@ -149,6 +149,15 @@ pub struct Opt { #[clap(long, value_parser)] pub spoof_url: Option, + /// Spoofs the HTTP referer header. + #[clap(long, value_parser)] + pub referer: Option, + + /// Spoofs the HTTP cookie header. + /// This is a string of the form "name1=value1; name2=value2". + #[clap(long)] + pub cookie: Option, + /// The version of the player to emulate #[clap(long)] pub player_version: Option, diff --git a/desktop/src/gui/dialogs/open_dialog.rs b/desktop/src/gui/dialogs/open_dialog.rs index e7af826a9..90fa98fa1 100644 --- a/desktop/src/gui/dialogs/open_dialog.rs +++ b/desktop/src/gui/dialogs/open_dialog.rs @@ -23,6 +23,8 @@ pub struct OpenDialog { // These are outside of PlayerOptions as it can be an invalid value (ie URL) during typing, // and we don't want to clear the value if the user, ie, toggles the checkbox. spoof_url: OptionalField, + referer: OptionalField, + cookie: OptionalField, base_url: OptionalField, proxy_url: OptionalField, path: PathOrUrlField, @@ -53,6 +55,14 @@ impl OpenDialog { defaults.player.spoof_url.as_ref().map(Url::to_string), UrlField::new("https://example.org/game.swf"), ); + let referer = OptionalField::new( + defaults.player.referer.as_ref().map(Url::to_string), + UrlField::new("https://example.org"), + ); + let cookie = OptionalField::new( + defaults.player.cookie.clone(), + CookieField::new("value1=cookie1; value2=cookie2"), + ); let base_url = OptionalField::new( defaults.player.base.as_ref().map(Url::to_string), UrlField::new("https://example.org"), @@ -242,6 +252,8 @@ impl OpenDialog { options: defaults, event_loop, spoof_url, + referer, + cookie, base_url, proxy_url, path, @@ -354,6 +366,20 @@ impl OpenDialog { .is_valid(); ui.end_row(); + ui.label(text(locale, "referer-url")); + is_valid &= self + .referer + .ui(ui, &mut self.options.player.referer, locale) + .is_valid(); + ui.end_row(); + + ui.label(text(locale, "cookie")); + is_valid &= self + .cookie + .ui(ui, &mut self.options.player.cookie, locale) + .is_valid(); + ui.end_row(); + ui.label(text(locale, "proxy")); is_valid &= self .proxy_url @@ -594,6 +620,40 @@ impl InnerField for UrlField { } } +struct CookieField { + hint: &'static str, +} + +impl CookieField { + pub fn new(hint: &'static str) -> Self { + Self { hint } + } +} + +impl InnerField for CookieField { + type Value = String; + type Result = String; + + fn value_if_missing(&self) -> Self::Value { + String::new() + } + + fn ui(&self, ui: &mut Ui, value: &mut Self::Value, error: bool, _locale: &LanguageIdentifier) { + TextEdit::singleline(value) + .hint_text(self.hint) + .text_color_opt(if error { + Some(ui.style().visuals.error_fg_color) + } else { + None + }) + .ui(ui); + } + + fn value_to_result(&self, value: &Self::Value) -> Result { + Ok(value.clone()) + } +} + struct DurationField { range: RangeInclusive, default_seconds: f64, diff --git a/desktop/src/player.rs b/desktop/src/player.rs index 2d449f93f..53eef00f4 100644 --- a/desktop/src/player.rs +++ b/desktop/src/player.rs @@ -75,6 +75,8 @@ impl From<&GlobalPreferences> for LaunchOptions { load_behavior: value.cli.load_behavior, letterbox: value.cli.letterbox, spoof_url: value.cli.spoof_url.clone(), + referer: value.cli.referer.clone(), + cookie: value.cli.cookie.clone(), player_version: value.cli.player_version, player_runtime: value.cli.player_runtime, frame_rate: value.cli.frame_rate, @@ -192,6 +194,8 @@ impl ActivePlayer { .base .to_owned() .unwrap_or_else(|| movie_url.clone()), + opt.player.referer.clone(), + opt.player.cookie.clone(), future_spawner, opt.proxy.clone(), opt.player.upgrade_to_https.unwrap_or_default(), diff --git a/frontend-utils/src/backends/navigator.rs b/frontend-utils/src/backends/navigator.rs index f51abdaf2..e4a376ac4 100644 --- a/frontend-utils/src/backends/navigator.rs +++ b/frontend-utils/src/backends/navigator.rs @@ -9,7 +9,7 @@ use async_net::TcpStream; use futures::future::select; use futures::{AsyncReadExt, AsyncWriteExt}; use futures_lite::FutureExt; -use reqwest::Proxy; +use reqwest::{cookie, header, Proxy}; use ruffle_core::backend::navigator::{ async_return, create_fetch_error, ErrorResponse, NavigationMethod, NavigatorBackend, OpenURLMode, OwnedFuture, Request, SocketMode, SuccessResponse, @@ -70,6 +70,8 @@ impl ExternalNavigatorBackend { #[allow(clippy::too_many_arguments)] pub fn new( mut base_url: Url, + referer: Option, + cookie: Option, future_spawner: F, proxy: Option, upgrade_to_https: bool, @@ -81,6 +83,19 @@ impl ExternalNavigatorBackend { ) -> Self { let mut builder = reqwest::ClientBuilder::new().cookie_store(true); + if let Some(referer) = referer { + let mut headers = header::HeaderMap::new(); + headers.insert(header::REFERER, referer.to_string().parse().unwrap()); + builder = builder.default_headers(headers); + } + + if let Some(cookie) = cookie { + let cookie_jar = cookie::Jar::default(); + cookie_jar.add_cookie_str(&cookie, &base_url); + let cookie_store = std::sync::Arc::new(cookie_jar); + builder = builder.cookie_provider(cookie_store) + } + if let Some(proxy) = proxy { match Proxy::all(proxy.clone()) { Ok(proxy) => { @@ -535,6 +550,8 @@ mod tests { let url = Url::parse("https://example.com/path/").unwrap(); ExternalNavigatorBackend::new( url.clone(), + None, + None, TestFutureSpawner, None, false, diff --git a/frontend-utils/src/player_options.rs b/frontend-utils/src/player_options.rs index b9e0ba58d..5335e507b 100644 --- a/frontend-utils/src/player_options.rs +++ b/frontend-utils/src/player_options.rs @@ -21,6 +21,8 @@ pub struct PlayerOptions { pub load_behavior: Option, pub letterbox: Option, pub spoof_url: Option, + pub referer: Option, + pub cookie: Option, pub player_version: Option, pub player_runtime: Option, pub frame_rate: Option, @@ -44,6 +46,8 @@ impl PlayerOptions { load_behavior: self.load_behavior.or(other.load_behavior), letterbox: self.letterbox.or(other.letterbox), spoof_url: self.spoof_url.clone().or_else(|| other.spoof_url.clone()), + referer: self.referer.clone().or_else(|| other.referer.clone()), + cookie: self.cookie.clone().or_else(|| other.cookie.clone()), player_version: self.player_version.or(other.player_version), player_runtime: self.player_runtime.or(other.player_runtime), frame_rate: self.frame_rate.or(other.frame_rate),