summaryrefslogtreecommitdiff
path: root/src/net/i2p_package.rs
blob: 2776eea71ea15797b689f4de1c7086d1cc1305c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

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<bool, std::io::Error> {
        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)
    }
}