diff options
| author | A Farzat <a@farzat.xyz> | 2026-03-01 22:05:20 +0300 |
|---|---|---|
| committer | A Farzat <a@farzat.xyz> | 2026-03-01 22:06:24 +0300 |
| commit | c6ffa62423659cf7d82802b9e0343f149c780de3 (patch) | |
| tree | 8b5299ff9c89dbd9906d4abdceea95f0744da718 /src | |
| parent | b0e66713529bd1cdac39d73fd06d37731b58882c (diff) | |
| download | oreilly-epub-c6ffa62423659cf7d82802b9e0343f149c780de3.tar.gz oreilly-epub-c6ffa62423659cf7d82802b9e0343f149c780de3.zip | |
Add authentication using cookies
Diffstat (limited to 'src')
| -rw-r--r-- | src/http_client.rs | 33 | ||||
| -rw-r--r-- | src/main.rs | 29 |
2 files changed, 57 insertions, 5 deletions
diff --git a/src/http_client.rs b/src/http_client.rs new file mode 100644 index 0000000..6e06474 --- /dev/null +++ b/src/http_client.rs @@ -0,0 +1,33 @@ +use anyhow::{Context, Result}; +use reqwest::{Client, cookie::Jar}; +use std::{collections::HashMap, fs, sync::Arc}; + +/// Reads the cookies.json file and builds an authenticated reqwest client. +pub fn build_authenticated_client(cookies_path: &str) -> Result<Client> { + // Read the JSON file. + let cookies_content = fs::read_to_string(cookies_path) + .with_context(|| format!("Failed to read cookies file from: {}", cookies_path))?; + + // Parse the JSON into a Rust HashMap. + let cookies_map: HashMap<String, String> = serde_json::from_str(&cookies_content) + .context("Failed to parse cookies file. Ensure it is a flat key-value JSON object.")?; + + // Create a Cookie Jar. + let jar = Arc::new(Jar::default()); + // `reqwest` needs a URL to associate the cookies with. + let url = "https://learning.oreilly.com".parse::<reqwest::Url>()?; + for (key, value) in cookies_map { + // `reqwest` expects cookies in the standard "key=value" string format. + let cookie_str = format!("{}={}", key, value); + jar.add_cookie_str(&cookie_str, &url); + } + + // Build the client with the cookie provider and a standard User-Agent. + let client = Client::builder() + .cookie_provider(Arc::clone(&jar)) + .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") + .build() + .context("Failed to build the HTTP client")?; + + Ok(client) +} diff --git a/src/main.rs b/src/main.rs index 998cc1f..33c8d82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,8 @@ +mod http_client; + +use anyhow::Result; use clap::Parser; +use http_client::build_authenticated_client; /// Download and generate an EPUB from Safari Books Online. #[derive(Parser, Debug)] @@ -7,22 +11,37 @@ struct Args { /// The Book digits ID that you want to download. #[arg(required = true)] bookid: String, - + /// Path to the cookies.json file. + #[arg(long, default_value = "cookies.json")] + cookies: String, /// Do not delete the log file on success. #[arg(long = "preserve-log")] preserve_log: bool, } -fn main() { +#[tokio::main] +async fn main() -> Result<()> { // Parse the command line arguments let args = Args::parse(); println!("Welcome to SafariBooks Rust Port!"); println!("Target Book ID: {}", args.bookid); - if args.preserve_log { - println!("Logs will be preserved"); + // Initialise the HTTP client. + println!("Loading cookies and initialising the HTTP client..."); + let client = build_authenticated_client(&args.cookies)?; + + // Quick test request to verify authentication. + let profile_url = "https://learning.oreilly.com/profile/"; + let response = client.get(profile_url).send().await?; + if response.status().is_success() { + println!("Successfully authenticated!"); + } else { + println!( + "Authentication might have failed. Status code: {}", + response.status() + ); } - // TODO: Proceed to load cookies and setup the HTTP client... + Ok(()) } |
