Skip to main content

why2_chat/
misc.rs

1/*
2This is part of WHY2
3Copyright (C) 2022-2026 Václav Šmejkal
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19use std::
20{
21    fs,
22    path::Path,
23    time::Duration,
24};
25
26use ureq::{ Error, Agent };
27
28use serde_json::Value;
29
30use semver::Version;
31
32use crate::consts;
33
34#[cfg(feature = "client")]
35use std::sync::mpsc::Sender;
36
37#[cfg(feature = "client")]
38use crate::network::client::ClientEvent;
39
40//PRIVATE
41fn get_dir(dir: &str) -> String
42{
43    dir.replace("{HOME}", dirs::home_dir().expect("Could not determine home directory").to_str().expect("Invalid home directory"))
44}
45
46//PUBLIC
47pub fn get_version<'a>() -> &'a str //GET COMPILED PACKAGE VERSION
48{
49    env!("CARGO_PKG_VERSION")
50}
51
52pub fn get_identifier() -> String //GET IDENTIFIER OF PACKAGE VERSION [WHY2/VERSION]
53{
54    format!("WHY2/{}", get_version())
55}
56
57pub fn fetch_data(url: &str) -> Result<String, Error> //FETCH DATA USING REQWEST
58{
59    //CREATE CUSTOM CLIENT (WITH TIMEOUT)
60    let agent: Agent = Agent::config_builder()
61        .timeout_global(Some(Duration::from_millis(consts::FETCH_TIMEOUT)))
62        .build()
63        .into();
64
65    //FETCH DATA
66    agent.get(url)
67        .header("User-Agent", &get_identifier())
68        .call()?
69        .body_mut()
70        .read_to_string()
71}
72
73pub fn check_version(#[cfg(feature = "client")] tx: &Sender<ClientEvent>) //CHECK FOR LATEST WHY2 VERSION
74{
75    //FETCH METADATA (USE CUSTOM User-Agent, FOR CRATES.IO TO WORK)
76    let metadata_raw = match fetch_data(consts::METADATA_URL)
77    {
78        Ok(m) => m,
79        Err(_) =>
80        {
81            #[cfg(feature = "client")]
82            {
83                tx.send(ClientEvent::VersionFailed).unwrap();
84            }
85
86            #[cfg(feature = "server")]
87            {
88                log::warn!("Fetching versions failed, this release could be unsafe!");
89            }
90
91            return;
92        }
93    };
94
95    //PARSE METADATA TO JSON
96    let metadata: Value = serde_json::from_str(&metadata_raw).expect("Parsing versions failed"); //PARSE
97    let newest_version = metadata.get("crate") //GET LATEST VERSION
98        .and_then(|c| c.get("newest_version"))
99        .and_then(|v| v.as_str())
100        .unwrap();
101
102    //OUTDATED VERSION, CALCULATE HOW MANY NEWER VERSIONS EXIST
103    let current_version = get_version();
104    if current_version != newest_version
105    {
106        //GET ARRAY OF VERSIONS
107        let versions = metadata.get("versions").and_then(|v| v.as_array()).unwrap();
108        let mut newer_versions = 0usize;
109        let current_version = Version::parse(current_version).expect("Invalid version");
110
111        //CALCULATE
112        for version in versions
113        {
114            //FOUND NEWER VERSION
115            if Version::parse(version.get("num").and_then(|n| n.as_str()).unwrap()).unwrap() > current_version
116            {
117                //INCREMENT COUNTER
118                newer_versions += 1;
119            }
120        }
121
122        #[cfg(feature = "client")]
123        {
124            tx.send(ClientEvent::UnsafeVersion(newer_versions, current_version, newest_version.to_owned())).unwrap();
125        }
126
127        #[cfg(feature = "server")]
128        {
129            log::warn!("This release could be unsafe! You are {newer_versions} versions behind! ({current_version}/{newest_version})");
130        }
131    }
132}
133
134pub fn get_why2_dir() -> String //RETURN PATH TO WHY2 CONFIG DIRECTORY
135{
136    get_dir(consts::CONFIG_DIR)
137}
138
139pub fn check_directory() //CREATE WHY2 CONFIG DIRECTORY
140{
141    let config = get_why2_dir();
142
143    //CREATE WHY2 CONFIG DIRECTORY
144    if !Path::new(&config).is_dir()
145    {
146        fs::create_dir_all(config).expect("Failed to create WHY2 config directory");
147    }
148}