use crate::cfg::config::Config; use tokio; /* use emissary_core::runtime::{ AsyncRead, AsyncWrite, }; */ use std::{fs::File, path::Path, io::Write}; // use emissary_core::Profile; // use emissary_core::i2np::Message; use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader}; use yosemite::SessionOptions; use yosemite::{Session, style::Stream}; use url; pub struct I2PPackage { config: Config, } impl I2PPackage { /// Creates a new I2P object with the given configuration. /// /// # Returns /// /// A new I2P object with the given configuration. The session is initially set to None and the connected status is set to false. pub fn new(config: Config) -> Self { I2PPackage{ config: config, } } /// Downloads the INDEX.tar.gz file from the configured repository /// and stores it in the configured cache directory. /// /// # Errors /// /// Returns an error if the request fails, if the response status is not successful, or if there's an issue while reading or writing the file. pub async fn fetch_index(&mut self) -> Result { let repo_url_str = &self.config.repo.repo_url; let cache_dir = &self.config.paths.cache_dir; let url = url::Url::parse(repo_url_str) .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid repo URL"))?; let host = url.host_str() .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidInput, "No host in URL"))?; let request_path = url.path(); let request_path = if request_path.ends_with(".tar.gz") { request_path.to_string() } else { format!("{}/INDEX.tar.gz", request_path.trim_end_matches('/')) }; let sam_host = "127.0.0.1"; let sam_port = 7656; let session_options = SessionOptions::default(); let mut session = Session::new(session_options).await .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create SAM session: {}", e)))?; let mut stream = session.connect(host).await .map_err(|e| std::io::Error::new(std::io::ErrorKind::ConnectionAborted, format!("Failed to connect: {}", e)))?; let request = format!( "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", request_path, host ); stream.write_all(request.as_bytes()).await .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to write request: {}", e)))?; let mut reader = BufReader::new(stream); let mut response_buffer = Vec::new(); reader.read_to_end(&mut response_buffer).await .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to read response: {}", e)))?; let headers_end = response_buffer .windows(4) .position(|window| window == b"\r\n\r\n") .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid response: no headers end"))?; let headers_str = std::str::from_utf8(&response_buffer[..headers_end]) .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid header encoding"))?; if !headers_str.starts_with("HTTP/1.1 200") && !headers_str.starts_with("HTTP/1.0 200") { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("HTTP Error: {}", headers_str.lines().next().unwrap_or("Unknown")), )); } let body_start = headers_end + 4; let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); let mut file = File::create(&file_path) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create file: {}", e)))?; file.write_all(&response_buffer[body_start..]) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to write file: {}", e)))?; file.flush() .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to flush file: {}", e)))?; Ok(true) } }