mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2024-12-22 06:39:21 +00:00
update: implement tor/arti, tls, basic metadata fetch
This commit is contained in:
parent
5a686aaa1d
commit
bf81b2c57c
8 changed files with 2611 additions and 109 deletions
2153
Cargo.lock
generated
2153
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
18
Cargo.toml
18
Cargo.toml
|
@ -4,27 +4,41 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
dirs = "4.0.0"
|
||||
anyhow = "1.0.65"
|
||||
arti-client = "0.7.0"
|
||||
arti-hyper = "0.7.0"
|
||||
bytes = "1.2.1"
|
||||
chrono = "0.4.22"
|
||||
dirs = "4.0.0"
|
||||
eframe = "0.19.0"
|
||||
egui = "0.19.0"
|
||||
egui_extras = { version = "0.19.0", features = ["image"] }
|
||||
env_logger = "0.9.1"
|
||||
figment = { version = "0.10.8", features = ["toml"] }
|
||||
flate2 = "1.0"
|
||||
hex-literal = "0.3.4"
|
||||
hyper = "0.14.20"
|
||||
hyper-tls = "0.5.0"
|
||||
image = { version = "0.24.4", features = ["png"] }
|
||||
log = "0.4.17"
|
||||
monero = "0.18.0"
|
||||
num_cpus = "1.13.1"
|
||||
num-format = "0.4.0"
|
||||
openssl = { version = "*", features = ["vendored"] }
|
||||
rand = "0.8.5"
|
||||
regex = "1.6.0"
|
||||
reqwest = { version = "0.11.12", features = ["blocking", "json"] }
|
||||
serde = "1.0.145"
|
||||
serde_derive = "1.0.145"
|
||||
serde_json = "1.0"
|
||||
sha2 = "0.10.6"
|
||||
tar = "0.4.38"
|
||||
tls-api = "0.9.0"
|
||||
tls-api-native-tls = "0.9.0"
|
||||
tokio = { version = "1.21.2", features = ["full"] }
|
||||
toml = "0.5.9"
|
||||
openssl = { version = "*", features = ["vendored"] }
|
||||
tor-rtcompat = "0.7.0"
|
||||
walkdir = "2.3.2"
|
||||
|
||||
[profile.optimized]
|
||||
codegen-units = 1
|
||||
|
|
|
@ -26,11 +26,6 @@ pub const P2POOL_BASE_ARGS: &'static str = "";
|
|||
pub const XMRIG_BASE_ARGS: &'static str = "--http-host=127.0.0.1 --http-port=18088 --algo=rx/0 --coin=Monero --randomx-cache-qos";
|
||||
pub const HORIZONTAL: &'static str = "--------------------------------------------";
|
||||
|
||||
// Update data
|
||||
pub const GITHUB_METADATA_GUPAX: &'static str = "https://api.github.com/repos/hinto-janaiyo/gupax/releases/latest";
|
||||
pub const GITHUB_METADATA_P2POOL: &'static str = "https://api.github.com/repos/SChernykh/p2pool/releases/latest";
|
||||
pub const GITHUB_METADATA_XMRIG: &'static str = "https://api.github.com/repos/xmrig/xmrig/releases/latest";
|
||||
|
||||
// OS specific
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const OS: &'static str = " Windows";
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::App;
|
|||
use egui::WidgetType::Button;
|
||||
use crate::constants::*;
|
||||
use crate::state::Gupax;
|
||||
use crate::update::*;
|
||||
|
||||
impl Gupax {
|
||||
pub fn show(state: &mut Gupax, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||
|
|
43
src/main.rs
43
src/main.rs
|
@ -50,7 +50,8 @@ mod status;
|
|||
mod gupax;
|
||||
mod p2pool;
|
||||
mod xmrig;
|
||||
use {ferris::*,constants::*,node::*,state::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*};
|
||||
mod update;
|
||||
use {ferris::*,constants::*,node::*,state::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*,update::*};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Struct + Impl
|
||||
// The state of the outer main [App].
|
||||
|
@ -66,15 +67,11 @@ pub struct App {
|
|||
node: Arc<Mutex<NodeStruct>>, // Data on community nodes
|
||||
width: f32, // Top-level width
|
||||
height: f32, // Top-level height
|
||||
// State:
|
||||
// og = Old state to compare against
|
||||
// state = Working state (current settings)
|
||||
// Instead of comparing [og == state] every frame,
|
||||
// the [diff] bool will be the signal for [Reset/Save].
|
||||
og: State,
|
||||
state: State,
|
||||
// update: Update, // State for update data [update.rs]
|
||||
diff: bool,
|
||||
// State
|
||||
og: State, // og = Old state to compare against
|
||||
state: State, // state = Working state (current settings)
|
||||
update: Update, // State for update data [update.rs]
|
||||
diff: bool, // Instead of comparing [og == state] every frame, this bool indicates changes
|
||||
// Process/update state:
|
||||
// Doesn't make sense to save this on disk
|
||||
// so it's represented as a bool here.
|
||||
|
@ -105,6 +102,7 @@ impl App {
|
|||
}
|
||||
|
||||
fn new() -> Self {
|
||||
let throwaway = State::default();
|
||||
let app = Self {
|
||||
tab: Tab::default(),
|
||||
quit: false,
|
||||
|
@ -116,7 +114,7 @@ impl App {
|
|||
node: Arc::new(Mutex::new(NodeStruct::default())),
|
||||
og: State::default(),
|
||||
state: State::default(),
|
||||
// update: Update::default(),
|
||||
update: Update::new(&throwaway.gupax.absolute_p2pool_path, &throwaway.gupax.absolute_xmrig_path, true),
|
||||
diff: false,
|
||||
p2pool: false,
|
||||
xmrig: false,
|
||||
|
@ -133,7 +131,7 @@ impl App {
|
|||
};
|
||||
// Apply arg state
|
||||
let mut app = parse_args(app);
|
||||
// Get exe path + random tmp folder
|
||||
// Get exe path + random tmp folder
|
||||
app.exe = match get_exe_dir() {
|
||||
Ok(exe) => exe,
|
||||
Err(err) => { panic_app(err.to_string()); exit(1); },
|
||||
|
@ -149,6 +147,7 @@ impl App {
|
|||
app.og.xmrig.max_threads = num_cpus::get();
|
||||
if app.og.xmrig.current_threads > app.og.xmrig.max_threads { app.og.xmrig.current_threads = app.og.xmrig.max_threads; }
|
||||
app.state = app.og.clone();
|
||||
app.update = Update::new(&app.og.gupax.absolute_p2pool_path, &app.og.gupax.absolute_xmrig_path, app.og.gupax.update_via_tor);
|
||||
app
|
||||
}
|
||||
}
|
||||
|
@ -273,14 +272,23 @@ fn parse_args(mut app: App) -> App {
|
|||
app
|
||||
}
|
||||
|
||||
fn get_exe_dir() -> Result<String, std::io::Error> {
|
||||
// Get absolute [Gupax] binary path
|
||||
pub fn get_exe() -> Result<String, std::io::Error> {
|
||||
match std::env::current_exe() {
|
||||
Ok(mut path) => { path.pop(); Ok(path.into_os_string().into_string().unwrap()) },
|
||||
Ok(mut path) => { Ok(path.display().to_string()) },
|
||||
Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) },
|
||||
}
|
||||
}
|
||||
|
||||
fn get_rand_tmp(path: &String) -> String {
|
||||
// Get absolute [Gupax] directory path
|
||||
pub fn get_exe_dir() -> Result<String, std::io::Error> {
|
||||
match std::env::current_exe() {
|
||||
Ok(mut path) => { path.pop(); Ok(path.display().to_string()) },
|
||||
Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rand_tmp(path: &String) -> String {
|
||||
use rand::{thread_rng, Rng};
|
||||
use rand::distributions::Alphanumeric;
|
||||
let rand: String = thread_rng()
|
||||
|
@ -361,6 +369,11 @@ impl eframe::App for App {
|
|||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
// *-------*
|
||||
// | DEBUG |
|
||||
// *-------*
|
||||
self.update.start();
|
||||
thread::sleep;
|
||||
// This sets the top level Ui dimensions.
|
||||
// Used as a reference for other uis.
|
||||
egui::CentralPanel::default().show(ctx, |ui| { self.width = ui.available_width(); self.height = ui.available_height(); });
|
||||
|
|
33
src/node.rs
33
src/node.rs
|
@ -103,7 +103,7 @@ impl NodeStruct {
|
|||
// find the fastest/slowest one for the user.
|
||||
// The process:
|
||||
// - Send [get_info] JSON-RPC requests over HTTP
|
||||
// - To prevent fingerprinting, randomly send [1-5] calls
|
||||
// - To prevent fingerprinting, randomly send [2-4] calls
|
||||
// - Measure each request in milliseconds as [u128]
|
||||
// - Timeout on requests over 5 seconds
|
||||
// - Calculate average time
|
||||
|
@ -121,16 +121,26 @@ impl NodeStruct {
|
|||
// default = GRAY
|
||||
pub fn ping() -> PingResult {
|
||||
info!("Starting community node pings...");
|
||||
// Get node list
|
||||
let mut nodes = NodeStruct::default();
|
||||
|
||||
// Create JSON request
|
||||
let mut get_info = HashMap::new();
|
||||
get_info.insert("jsonrpc", "2.0");
|
||||
get_info.insert("id", "0");
|
||||
get_info.insert("method", "get_info");
|
||||
|
||||
// Misc Settings
|
||||
let mut vec: Vec<(u128, NodeEnum)> = Vec::new();
|
||||
let fastest = false;
|
||||
|
||||
// Create HTTP Client
|
||||
let timeout_sec = Duration::from_millis(5000);
|
||||
let client = reqwest::blocking::ClientBuilder::new();
|
||||
let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec);
|
||||
let client = reqwest::blocking::ClientBuilder::build(client).unwrap();
|
||||
|
||||
for ip in NODE_IPS.iter() {
|
||||
// Match IP
|
||||
let id = match *ip {
|
||||
C3POOL => C3pool,
|
||||
CAKE => Cake,
|
||||
|
@ -143,27 +153,28 @@ impl NodeStruct {
|
|||
SETH => Seth,
|
||||
SUPPORTXMR => SupportXmr,
|
||||
SUPPORTXMR_IR => SupportXmrIr,
|
||||
// XMRVSBEAST => XmrVsBeast,
|
||||
_ => XmrVsBeast,
|
||||
};
|
||||
// Misc
|
||||
let mut timeout = false;
|
||||
let mut mid = Duration::new(0, 0);
|
||||
let max = rand::thread_rng().gen_range(1..5);
|
||||
let max = rand::thread_rng().gen_range(2..4);
|
||||
|
||||
// Start JSON-RPC request
|
||||
for i in 1..=max {
|
||||
let client = reqwest::blocking::ClientBuilder::new();
|
||||
let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec);
|
||||
let client = reqwest::blocking::ClientBuilder::build(client).unwrap();
|
||||
let http = "http://".to_owned() + &**ip + "/json_rpc";
|
||||
let now = Instant::now();
|
||||
let http = "http://".to_owned() + &**ip + "/json_rpc";
|
||||
match client.post(http).json(&get_info).send() {
|
||||
Ok(r) => mid += now.elapsed(),
|
||||
Ok(_) => mid += now.elapsed(),
|
||||
Err(err) => {
|
||||
error!("Timeout on [{:#?}: {}] (over 5 seconds)", id, ip);
|
||||
error!("Timeout on [{:#?}: {}] (over 5 seconds) | {}", id, ip, err);
|
||||
mid += timeout_sec;
|
||||
timeout = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate average
|
||||
let ms = mid.as_millis() / 3;
|
||||
vec.push((ms, id));
|
||||
info!("{}ms ... {} calls ... {}", ms, max, ip);
|
||||
|
@ -192,6 +203,8 @@ impl NodeStruct {
|
|||
XmrVsBeast => { nodes.xmrvsbeast.ms = ms; nodes.xmrvsbeast.color = color; },
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate fastest out of all nodes
|
||||
let mut best_ms: u128 = vec[0].0;
|
||||
let mut fastest: NodeEnum = vec[0].1;
|
||||
for (ms, id) in vec.iter() {
|
||||
|
|
|
@ -51,6 +51,7 @@ impl State {
|
|||
auto_node: true,
|
||||
ask_before_quit: true,
|
||||
save_before_quit: true,
|
||||
update_via_tor: true,
|
||||
p2pool_path: DEFAULT_P2POOL_PATH.to_string(),
|
||||
xmrig_path: DEFAULT_XMRIG_PATH.to_string(),
|
||||
absolute_p2pool_path: Self::into_absolute_path(DEFAULT_P2POOL_PATH.to_string()).unwrap(),
|
||||
|
@ -300,6 +301,7 @@ pub struct Gupax {
|
|||
pub auto_node: bool,
|
||||
pub ask_before_quit: bool,
|
||||
pub save_before_quit: bool,
|
||||
pub update_via_tor: bool,
|
||||
pub p2pool_path: String,
|
||||
pub xmrig_path: String,
|
||||
pub absolute_p2pool_path: PathBuf,
|
||||
|
|
465
src/update.rs
465
src/update.rs
|
@ -14,54 +14,455 @@
|
|||
//// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::State;
|
||||
//---------------------------------------------------------------------------------------------------- Imports
|
||||
use serde_derive::{Serialize,Deserialize};
|
||||
use tokio::task::JoinHandle;
|
||||
use std::time::Duration;
|
||||
use std::sync::{Arc,Mutex};
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::io::{Read,Write};
|
||||
//use crate::{Name::*,State};
|
||||
use rand::{thread_rng, Rng};
|
||||
use rand::distributions::Alphanumeric;
|
||||
use anyhow::Error;
|
||||
use arti_hyper::*;
|
||||
use arti_client::{TorClient,TorClientConfig};
|
||||
use tokio::io::{AsyncReadExt,AsyncWriteExt};
|
||||
use tls_api::{TlsConnector, TlsConnectorBuilder};
|
||||
use hyper::header::HeaderValue;
|
||||
use hyper::{Client,Body,Request};
|
||||
use hyper_tls::HttpsConnector;
|
||||
use arti_hyper::*;
|
||||
use log::*;
|
||||
use crate::update::Name::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
struct Update {
|
||||
new_gupax: String,
|
||||
new_p2pool: String,
|
||||
new_xmrig: String,
|
||||
path_gupax: String,
|
||||
path_p2pool: String,
|
||||
path_xmrig: String,
|
||||
updating: Arc<Mutex<bool>> // Is the update in progress?
|
||||
update_prog: u8, // Not an [f32] because [Eq] doesn't work
|
||||
// use tls_api_native_tls::{TlsConnector,TlsConnectorBuilder};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Constants
|
||||
// Package naming schemes:
|
||||
// gupax | gupax-vX.X.X-(windows|macos|linux)-x64.(zip|tar.gz)
|
||||
// p2pool | p2pool-vX.X.X-(windows|macos|linux)-x64.(zip|tar.gz)
|
||||
// xmrig | xmrig-X.X.X-(msvc-win64|macos-x64|linux-static-x64).(zip|tar.gz)
|
||||
//
|
||||
// Download link = PREFIX + Version (found at runtime) + SUFFIX + Version + EXT
|
||||
// Example: https://github.com/hinto-janaiyo/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-standalone-x64
|
||||
//
|
||||
// Exceptions (there are always exceptions...):
|
||||
// - XMRig doesn't have a [v], so it is [xmrig-6.18.0-...]
|
||||
// - XMRig separates the hash and signature
|
||||
// - P2Pool hashes are in UPPERCASE
|
||||
// - Gupax will be downloaded as a standalone binary (no decompression/extraction needed)
|
||||
|
||||
const GUPAX_METADATA: &'static str = "https://api.github.com/repos/hinto-janaiyo/gupax/releases/latest";
|
||||
const P2POOL_METADATA: &'static str = "https://api.github.com/repos/SChernykh/p2pool/releases/latest";
|
||||
const XMRIG_METADATA: &'static str = "https://api.github.com/repos/xmrig/xmrig/releases/latest";
|
||||
|
||||
const GUPAX_PREFIX: &'static str = "https://github.com/hinto-janaiyo/gupax/releases/download/";
|
||||
const P2POOL_PREFIX: &'static str = "https://github.com/SChernykh/p2pool/releases/download/";
|
||||
const XMRIG_PREFIX: &'static str = "https://github.com/xmrig/xmrig/releases/download/";
|
||||
|
||||
const GUPAX_SUFFIX: &'static str = "/gupax-";
|
||||
const P2POOL_SUFFIX: &'static str = "/p2pool-";
|
||||
const XMRIG_SUFFIX: &'static str = "/xmrig-";
|
||||
|
||||
const GUPAX_HASH: &'static str = "SHA256SUMS";
|
||||
const P2POOL_HASH: &'static str = "sha256sums.txt.asc";
|
||||
const XMRIG_HASH: &'static str = "SHA256SUMS";
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const GUPAX_EXTENSION: &'static str = "-windows-standalone-x64.exe";
|
||||
#[cfg(target_os = "windows")]
|
||||
const P2POOL_EXTENSION: &'static str = "-windows-x64.zip";
|
||||
#[cfg(target_os = "windows")]
|
||||
const XMRIG_EXTENSION: &'static str = "-msvc-win64.zip";
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const GUPAX_EXTENSION: &'static str = "-macos-standalone-x64";
|
||||
#[cfg(target_os = "macos")]
|
||||
const P2POOL_EXTENSION: &'static str = "-macos-x64.tar.gz";
|
||||
#[cfg(target_os = "macos")]
|
||||
const XMRIG_EXTENSION: &'static str = "-macos-x64.tar.gz";
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
const GUPAX_EXTENSION: &'static str = "-linux-standalone-x64";
|
||||
#[cfg(target_os = "linux")]
|
||||
const P2POOL_EXTENSION: &'static str = "-linux-x64.tar.gz";
|
||||
#[cfg(target_os = "linux")]
|
||||
const XMRIG_EXTENSION: &'static str = "-linux-static-x64.tar.gz";
|
||||
|
||||
// Some fake Curl/Wget user-agents because GitHub API requires one and a Tor browser
|
||||
// user-agent might be fingerprintable without all the associated headers.
|
||||
const FAKE_USER_AGENT: [&'static str; 50] = [
|
||||
"Wget/1.16.3",
|
||||
"Wget/1.17",
|
||||
"Wget/1.17.1",
|
||||
"Wget/1.18",
|
||||
"Wget/1.18",
|
||||
"Wget/1.19",
|
||||
"Wget/1.19.1",
|
||||
"Wget/1.19.2",
|
||||
"Wget/1.19.3",
|
||||
"Wget/1.19.4",
|
||||
"Wget/1.19.5",
|
||||
"Wget/1.20",
|
||||
"Wget/1.20.1",
|
||||
"Wget/1.20.2",
|
||||
"Wget/1.20.3",
|
||||
"Wget/1.21",
|
||||
"Wget/1.21.1",
|
||||
"Wget/1.21.2",
|
||||
"Wget/1.21.3",
|
||||
"curl/7.64.1",
|
||||
"curl/7.65.0",
|
||||
"curl/7.65.1",
|
||||
"curl/7.65.2",
|
||||
"curl/7.65.3",
|
||||
"curl/7.66.0",
|
||||
"curl/7.67.0",
|
||||
"curl/7.68.0",
|
||||
"curl/7.69.0",
|
||||
"curl/7.69.1",
|
||||
"curl/7.70.0",
|
||||
"curl/7.70.1",
|
||||
"curl/7.71.0",
|
||||
"curl/7.71.1",
|
||||
"curl/7.72.0",
|
||||
"curl/7.73.0",
|
||||
"curl/7.74.0",
|
||||
"curl/7.75.0",
|
||||
"curl/7.76.0",
|
||||
"curl/7.76.1",
|
||||
"curl/7.77.0",
|
||||
"curl/7.78.0",
|
||||
"curl/7.79.0",
|
||||
"curl/7.79.1",
|
||||
"curl/7.80.0",
|
||||
"curl/7.81.0",
|
||||
"curl/7.82.0",
|
||||
"curl/7.83.0",
|
||||
"curl/7.83.1",
|
||||
"curl/7.84.0",
|
||||
"curl/7.85.0",
|
||||
];
|
||||
|
||||
const MSG_START: &'static str = "Starting update...";
|
||||
const MSG_TMP: &'static str = "Creating temporary directory";
|
||||
const MSG_PKG: &'static str = "Creating package list";
|
||||
const MSG_TOR: &'static str = "Creating Tor+HTTPS client";
|
||||
const MSG_HTTPS: &'static str = "Creating HTTPS client";
|
||||
const MSG_METADATA: &'static str = "Fetching package metadata";
|
||||
const MSG_ARCHIVE: &'static str = "Downloading packages";
|
||||
|
||||
// These two are sequential and not async so no need for a constant message.
|
||||
// The package in question will be known at runtime, so that will be printed.
|
||||
//const MSG_EXTRACT: &'static str = "Extracting packages";
|
||||
//const MSG_UPGRADE: &'static str = "Upgrading packages";
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Update struct/impl
|
||||
// Contains values needed during update
|
||||
// Progress bar structure:
|
||||
// 5% | Create tmp directory
|
||||
// 5% | Create package list
|
||||
// 10% | Create Tor/HTTPS client
|
||||
// 20% | Download Metadata (x3)
|
||||
// 30% | Download Archive (x3)
|
||||
// 15% | Extract (x3)
|
||||
// 15% | Upgrade (x3)
|
||||
|
||||
pub struct Update {
|
||||
path_gupax: String, // Full path to current gupax
|
||||
path_p2pool: String, // Full path to current p2pool
|
||||
path_xmrig: String, // Full path to current xmrig
|
||||
tmp_dir: String, // Full path to temporary directory
|
||||
updating: Arc<Mutex<bool>>, // Is an update in progress?
|
||||
prog: Arc<Mutex<u8>>, // Holds the 0-100% progress bar number
|
||||
msg: Arc<Mutex<String>>, // Message to display on [Gupax] tab while updating
|
||||
tor: bool, // Is Tor enabled or not?
|
||||
}
|
||||
|
||||
impl Update {
|
||||
fn new(path_p2pool: String, path_xmrig: String) -> Result<Self, Error> {
|
||||
let path_gupax = std::env::current_exe()?;
|
||||
// Takes in current paths from [State]
|
||||
pub fn new(path_p2pool: &PathBuf, path_xmrig: &PathBuf, tor: bool) -> Self {
|
||||
Self {
|
||||
new_gupax: "?".to_string(),
|
||||
new_p2pool: "?".to_string(),
|
||||
new_xmrig: "?".to_string(),
|
||||
path_gupax,
|
||||
path_p2pool,
|
||||
path_xmrig,
|
||||
updating: Arc::new(Mutex::new(false)),
|
||||
update_prog: 0,
|
||||
path_gupax: crate::get_exe().unwrap(),
|
||||
path_p2pool: path_p2pool.display().to_string(),
|
||||
path_xmrig: path_xmrig.display().to_string(),
|
||||
tmp_dir: "".to_string(),
|
||||
updating: Arc::new(Mutex::new(true)),
|
||||
prog: Arc::new(Mutex::new(0)),
|
||||
msg: Arc::new(Mutex::new("".to_string())),
|
||||
tor,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(state: &mut State) -> Result((), Error) {
|
||||
|
||||
// Get a temporary random folder
|
||||
// for package download contents
|
||||
// Will look like [/tmp/gupax_A1m98FN3fa/] on Unix
|
||||
pub fn get_tmp_dir() -> String {
|
||||
let rand_string: String = thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(10)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
let tmp = std::env::temp_dir();
|
||||
let tmp = format!("{}{}{}{}", tmp.display(), "/gupax_", rand_string, "/");
|
||||
info!("Update | TMP directory ... {}", tmp);
|
||||
tmp
|
||||
}
|
||||
|
||||
// The HTTPS client created when Tor is enabled:
|
||||
// TLS implementation | tls-api -> native-tls
|
||||
// Tor implementatoin | arti
|
||||
pub async fn get_tor_client() -> Result<ClientEnum, anyhow::Error> {
|
||||
info!("Update | Creating Tor+HTTPS client...");
|
||||
let tor = TorClient::create_bootstrapped(TorClientConfig::default()).await?;
|
||||
let tls = tls_api_native_tls::TlsConnector::builder()?.build()?;
|
||||
let http = ArtiHttpConnector::new(tor, tls);
|
||||
let client = ClientEnum::Tor(Client::builder().build(http));
|
||||
info!("Update | Tor client ... OK");
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
// The HTTPS client created when Tor is disabled:
|
||||
// TLS implementation | hyper-tls
|
||||
pub async fn get_client() -> Result<ClientEnum, anyhow::Error> {
|
||||
info!("Update | Creating HTTPS client...");
|
||||
let mut https = hyper_tls::HttpsConnector::new();
|
||||
https.https_only(true);
|
||||
let client = ClientEnum::Https(Client::builder().build(https));
|
||||
info!("Update | HTTPS client ... OK");
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
// Download process:
|
||||
// 0. setup tor, client, http, etc
|
||||
// 1. fill vector with all enums
|
||||
// 2. loop over vec, download metadata
|
||||
// 3. if current == version, remove from vec
|
||||
// 4. loop over vec, download links
|
||||
// 5. extract, upgrade
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn start(&mut self) -> Result<(), anyhow::Error> {
|
||||
// Set progress bar
|
||||
*self.msg.lock().unwrap() = MSG_START.to_string();
|
||||
*self.prog.lock().unwrap() = 0;
|
||||
|
||||
// Get temporary directory
|
||||
*self.msg.lock().unwrap() = MSG_TMP.to_string();
|
||||
let tmp_dir = Self::get_tmp_dir();
|
||||
*self.prog.lock().unwrap() = 5;
|
||||
|
||||
// Make Pkg vector
|
||||
*self.msg.lock().unwrap() = MSG_PKG.to_string();
|
||||
let vec = vec![
|
||||
Pkg::new(Gupax, &tmp_dir, self.prog.clone(), self.msg.clone()),
|
||||
Pkg::new(P2pool, &tmp_dir, self.prog.clone(), self.msg.clone()),
|
||||
Pkg::new(Xmrig, &tmp_dir, self.prog.clone(), self.msg.clone()),
|
||||
];
|
||||
let mut handles: Vec<JoinHandle<()>> = vec![];
|
||||
*self.prog.lock().unwrap() = 5;
|
||||
|
||||
// Create Tor/HTTPS client
|
||||
let mut client: ClientEnum;
|
||||
if self.tor {
|
||||
*self.msg.lock().unwrap() = MSG_TOR.to_string();
|
||||
client = Self::get_tor_client().await?;
|
||||
} else {
|
||||
*self.msg.lock().unwrap() = MSG_HTTPS.to_string();
|
||||
client = Self::get_client().await?;
|
||||
}
|
||||
*self.prog.lock().unwrap() = 10;
|
||||
|
||||
// loop for metadata
|
||||
for pkg in vec.iter() {
|
||||
info!("Update | Metadata | Starting ... {}", pkg.name);
|
||||
let name = pkg.name.clone();
|
||||
let version = Arc::clone(&pkg.version);
|
||||
let request = hyper::Request::builder()
|
||||
.method("GET")
|
||||
.uri(pkg.link_metadata)
|
||||
.header(hyper::header::USER_AGENT, hyper::header::HeaderValue::from_static("Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"))
|
||||
.body(Body::empty())?;
|
||||
let client = client.clone();
|
||||
let handle: JoinHandle<()> = tokio::spawn(async move {
|
||||
Pkg::get_tor_response(name, version, client, request).await;
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
for handle in handles {
|
||||
handle.await?;
|
||||
}
|
||||
info!("Update | Metadata ... OK\n");
|
||||
Ok(())
|
||||
|
||||
//----------------------------------------------
|
||||
//
|
||||
// // loop for download
|
||||
// let mut handles: Vec<JoinHandle<()>> = vec![];
|
||||
// for pkg in vec.iter() {
|
||||
// let name = pkg.name.clone();
|
||||
// let bytes = Arc::clone(&pkg.bytes);
|
||||
// let version = pkg.version.lock().unwrap();
|
||||
// let link;
|
||||
// if pkg.name == Name::Xmrig {
|
||||
// link = pkg.link_prefix.clone() + &version + &pkg.link_suffix_1 + &version[1..] + &pkg.link_suffix_2;
|
||||
// } else {
|
||||
// link = pkg.link_prefix.clone() + &version + &pkg.link_suffix_1 + &version + &pkg.link_suffix_2;
|
||||
// }
|
||||
// println!("download: {:#?} | {}", pkg.name, link);
|
||||
// let request = Client::get(&client.clone(), &link);
|
||||
// let handle: JoinHandle<()> = tokio::spawn(async move {
|
||||
// get_bytes(request, bytes, name).await;
|
||||
// });
|
||||
// handles.push(handle);
|
||||
// }
|
||||
// for handle in handles {
|
||||
// handle.await.unwrap();
|
||||
// }
|
||||
// println!("download ... OK\n");
|
||||
//
|
||||
// // extract
|
||||
// let TMP = num();
|
||||
// std::fs::create_dir(&TMP).unwrap();
|
||||
// for pkg in vec.iter() {
|
||||
// let name = TMP.to_string() + &pkg.name.to_string();
|
||||
// println!("extract: {:#?} | {}", pkg.name, name);
|
||||
// if pkg.name == Name::Gupax {
|
||||
// std::fs::OpenOptions::new().mode(0o700).create(true).write(true).open(&name);
|
||||
// std::fs::write(name, pkg.bytes.lock().unwrap().as_ref()).unwrap();
|
||||
// } else {
|
||||
// std::fs::create_dir(&name).unwrap();
|
||||
// tar::Archive::new(flate2::read::GzDecoder::new(pkg.bytes.lock().unwrap().as_ref())).unpack(name).unwrap();
|
||||
// }
|
||||
// }
|
||||
// println!("extract ... OK");
|
||||
//
|
||||
//async fn get_bytes(request: RequestBuilder, bytes: Arc<Mutex<bytes::Bytes>>, name: Name) {
|
||||
// *bytes.lock().unwrap() = request.send().await.unwrap().bytes().await.unwrap();
|
||||
// println!("{} download ... OK", name);
|
||||
//}
|
||||
//
|
||||
//async fn func(request: RequestBuilder, version: Arc<Mutex<String>>, name: Name) {
|
||||
// let response = request.send().await.unwrap().bytes().await.unwrap();
|
||||
//
|
||||
// let mut bytes = flate2::read::GzDecoder::new(response.as_ref());
|
||||
// let mut response = String::new();
|
||||
// bytes.read_to_string(&mut response).unwrap();
|
||||
//
|
||||
// let response: Version = serde_json::from_str(&response).unwrap();
|
||||
// *version.lock().unwrap() = response.tag_name.clone();
|
||||
// println!("{} {} ... OK", name, response.tag_name);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper type around Tor/HTTPS client
|
||||
enum ClientEnum {
|
||||
Tor(Client<ArtiHttpConnector<tor_rtcompat::PreferredRuntime, tls_api_native_tls::TlsConnector>>),
|
||||
Https(Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>),
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Pkg struct/impl
|
||||
#[derive(Debug)]
|
||||
pub struct Pkg {
|
||||
name: Name,
|
||||
link_metadata: &'static str,
|
||||
link_prefix: &'static str,
|
||||
link_suffix: &'static str,
|
||||
link_extension: &'static str,
|
||||
tmp_dir: String,
|
||||
update_prog: Arc<Mutex<u8>>,
|
||||
update_msg: Arc<Mutex<String>>,
|
||||
bytes: Arc<Mutex<bytes::Bytes>>,
|
||||
version: Arc<Mutex<String>>,
|
||||
}
|
||||
|
||||
impl Pkg {
|
||||
pub fn new(name: Name, tmp_dir: &String, update_prog: Arc<Mutex<u8>>, update_msg: Arc<Mutex<String>>) -> Self {
|
||||
let link_metadata = match name {
|
||||
Gupax => GUPAX_METADATA,
|
||||
P2pool => P2POOL_METADATA,
|
||||
Xmrig => XMRIG_METADATA,
|
||||
};
|
||||
let link_prefix = match name {
|
||||
Gupax => GUPAX_PREFIX,
|
||||
P2pool => P2POOL_PREFIX,
|
||||
Xmrig => XMRIG_PREFIX,
|
||||
};
|
||||
let link_suffix = match name {
|
||||
Gupax => GUPAX_SUFFIX,
|
||||
P2pool => P2POOL_SUFFIX,
|
||||
Xmrig => XMRIG_SUFFIX,
|
||||
};
|
||||
let link_extension = match name {
|
||||
Gupax => GUPAX_EXTENSION,
|
||||
P2pool => P2POOL_EXTENSION,
|
||||
Xmrig => XMRIG_EXTENSION,
|
||||
};
|
||||
Self {
|
||||
name,
|
||||
link_metadata,
|
||||
link_prefix,
|
||||
link_suffix,
|
||||
link_extension,
|
||||
tmp_dir: tmp_dir.to_string(),
|
||||
update_prog,
|
||||
update_msg,
|
||||
bytes: Arc::new(Mutex::new(bytes::Bytes::new())),
|
||||
version: Arc::new(Mutex::new(String::new())),
|
||||
}
|
||||
}
|
||||
|
||||
// Generate GET request based off input URI + fake user agent
|
||||
pub async fn get_request(self) -> Result<Request<Body>, anyhow::Error> {
|
||||
let user_agent = FAKE_USER_AGENT[thread_rng().gen_range(0..50)];
|
||||
let request = Request::builder()
|
||||
.method("GET")
|
||||
.uri(self.link_metadata)
|
||||
.header(hyper::header::USER_AGENT, HeaderValue::from_static(user_agent))
|
||||
.body(Body::empty())?;
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
// Get response using [Tor client] + [request]
|
||||
// and change the [version] under an Arc<Mutex>
|
||||
pub async fn get_tor_response(name: Name, version: Arc<Mutex<String>>, client: Client<ArtiHttpConnector<tor_rtcompat::PreferredRuntime, tls_api_native_tls::TlsConnector>>, request: Request<Body>) -> Result<(), Error> {
|
||||
let mut response = client.request(request).await?;
|
||||
let body = hyper::body::to_bytes(response.body_mut()).await?;
|
||||
let body: Version = serde_json::from_slice(&body)?;
|
||||
*version.lock().unwrap() = body.tag_name.clone();
|
||||
info!("Update | Metadata | {} {} ... OK", name, body.tag_name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Same thing, but without Tor.
|
||||
pub async fn get_response(name: Name, version: Arc<Mutex<String>>, client: Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>, request: Request<Body>) -> Result<(), Error> {
|
||||
let mut response = client.request(request).await?;
|
||||
let body = hyper::body::to_bytes(response.body_mut()).await?;
|
||||
let body: Version = serde_json::from_slice(&body)?;
|
||||
*version.lock().unwrap() = body.tag_name.clone();
|
||||
info!("Update | Metadata | {} {} ... OK", name, body.tag_name);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// This inherits the value of [tag_name] from GitHub's JSON API
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct TagName {
|
||||
struct Version {
|
||||
tag_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum Error {
|
||||
Io(std::io::Error),
|
||||
Serialize(toml::ser::Error),
|
||||
Deserialize(toml::de::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum Package {
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum Name {
|
||||
Gupax,
|
||||
P2pool,
|
||||
Xmrig,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue