use crate::cfg::config::Config; use tokio; use emissary_core::runtime::{ AsyncRead, AsyncWrite, }; use std::{io, fs::File, path::Path, io::Write}; // use emissary_core::Profile; // use emissary_core::i2np::Message; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use yosemite::SessionOptions; use yosemite::{Session, style::Stream}; /* use i2p_client::ClientType; use i2p_client::I2PClient; use i2p_client::SessionStyle::Stream; use i2p_client::Session; struct I2PStatus { Connected: bool, } impl I2PStatus { pub fn connect(&self) -> Result { let config: Config = Config::parse().unwrap(); let client= I2PClient::new(true, "MeskPKG-manager".to_string(), "2.0", "2.58.0", 10); // let destination = Session::r#gen(&mut self, SigType::EdDsaSha512Ed25519) let session = Session::create(config.repo.repo_url, &config.repo.destination.0, "MeskPKG-manager", Stream, "2.0", "2.58"); Ok(true) } } */ struct I2P { session: Option>, connected: bool, config: Config, } impl I2P { /// 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 { I2P { session: None, connected: false, config: config, } } /// Fetches the list of packages from the repository specified in the configuration. /// /// This function connects to the repository specified in the configuration, sends a GET request for the repository list and returns true if the request is successful, false otherwise. /// /// # Errors /// /// Returns an error if the repository URL is invalid or if the request fails. /// 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 repo_pos = repo_url_str.find("/repo").ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidData, "URL does not contain '/repo'") })?; // BaseURL let base_url = &repo_url_str[..repo_pos]; // Url after /repo let path_suffix = &repo_url_str[repo_pos + "/repo".len()..]; // HTTP path let request_path = format!("/repo{}{}", path_suffix, if path_suffix.ends_with(".tar.gz") { "" } else { "/INDEX.tar.gz" }); let mut opts = SessionOptions::default(); // FIXME: Make sure, opts setted to I2P // opts.set_host("127.0.0.1"); // opts.set_port(7656); let mut session = Session::::new(opts).await.map_err(|e| { io::Error::new(io::ErrorKind::Other, format!("Failed to create session: {}", e)) })?; let mut stream = session.connect(base_url).await.map_err(|e| { io::Error::new(io::ErrorKind::ConnectionAborted, format!("Failed to connect: {}", e)) })?; let request = format!("GET {} HTTP/1.1\r\nHost: {}\r\n\r\n", request_path, base_url); stream.write_all(request.as_bytes()).await.map_err(|e| { io::Error::new(io::ErrorKind::Other, format!("Failed to write request: {}", e)) })?; let mut response_buffer = Vec::new(); let mut chunk = [0u8; 1024]; loop { // FIXME: Check docs and make suro stream allows to AsyncReadExt let bytes_read = stream.read(&mut chunk).await.map_err(|e| { io::Error::new(io::ErrorKind::Other, format!("Failed to read response: {}", e)) })?; if bytes_read == 0 { break; } response_buffer.extend_from_slice(&chunk[..bytes_read]); if let Some(headers_end) = Self::find_double_crlf(&response_buffer) { let body_start = headers_end + 4; let headers_str = std::str::from_utf8(&response_buffer[..headers_end]) .map_err(|_| io::Error::new(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(io::Error::new(io::ErrorKind::Other, format!("HTTP Error: {}", headers_str.lines().next().unwrap_or("Unknown")))); } let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); let mut file = File::create(&file_path)?; file.write_all(&response_buffer[body_start..])?; while let Ok(bytes_read) = stream.read(&mut chunk).await { if bytes_read == 0 { break; } file.write_all(&chunk[..bytes_read])?; } file.flush()?; break; } } Ok(true) } fn find_double_crlf(buf: &[u8]) -> Option { for i in 0..buf.len().saturating_sub(3) { if buf[i] == b'\r' && buf[i+1] == b'\n' && buf[i+2] == b'\r' && buf[i+3] == b'\n' { return Some(i); } } None } }