Skip to main content

tauri_plugin_keygen_rs2/
machine.rs

1use std::{
2    fs::{self, File},
3    io::Write,
4    path::PathBuf,
5};
6
7use keygen_rs::{machine::MachineCheckoutOpts, machine_file::MachineFile};
8use tauri::{webview_version, AppHandle, Runtime};
9#[cfg(mobile)]
10use tauri_plugin_machine_uid::MachineUidExt;
11
12use crate::{error::Error, utils::get_app_keygen_path, AppHandleExt, Result};
13
14#[cfg(mobile)]
15static ENGINE_NAME: &str = "WRY";
16
17#[cfg(target_os = "linux")]
18static ENGINE_NAME: &str = "WebKit";
19
20#[cfg(target_os = "macos")]
21static ENGINE_NAME: &str = "WebKit";
22
23#[cfg(target_os = "windows")]
24static ENGINE_NAME: &str = "WebView2";
25
26#[derive(Debug, Clone)]
27pub struct MachineState {
28    pub name: String,
29    pub fingerprint: String,
30    pub platform: String,
31    pub user_agent: String,
32}
33
34impl MachineState {
35    #[cfg(desktop)]
36    pub fn get_fingerprint() -> String {
37        machine_uid::get().unwrap_or_default()
38    }
39
40    #[cfg(desktop)]
41    pub fn get_fingerprint_app<R: Runtime>(_app_handle: &AppHandle<R>) -> String {
42        Self::get_fingerprint()
43    }
44
45    #[cfg(mobile)]
46    pub fn get_fingerprint() -> String {
47        panic!(
48            r#"On mobile, you should use the `get_fingerprint_app` method instead of `get_fingerprint`."#
49        );
50    }
51
52    #[cfg(mobile)]
53    pub fn get_fingerprint_app<R: Runtime>(app_handle: &AppHandle<R>) -> String {
54        let Some(state) = app_handle.try_machine_uid() else {
55            panic!(
56                r#"tauri-plugin-machine-uid is not initialized but is required to get machine fingerprints on mobile.
57You should add it to your application and initialize it before tauri-plugin-keygen-rs2."#
58            );
59        };
60
61        state
62            .get_machine_uid()
63            .ok()
64            .and_then(|uid| uid.id)
65            .unwrap_or_default()
66    }
67
68    pub(crate) fn new<R: Runtime>(
69        app_handle: &AppHandle<R>,
70        app_name: String,
71        app_version: String,
72    ) -> Self {
73        let fingerprint = Self::get_fingerprint_app(app_handle);
74        let name = whoami::devicename();
75
76        let os_name = format!("{}", whoami::platform());
77        let os_version = whoami::distro().to_string();
78        let arch = format!("{}", whoami::arch());
79        let platform = format!("{} - {} - {}", os_name, os_version, arch);
80
81        let engine_name = ENGINE_NAME.to_string();
82        let engine_version = webview_version().unwrap_or_default();
83        let locale = sys_locale::get_locale().unwrap_or_default();
84        let user_agent = format!(
85            "{}/{} {}/{} {}/{} {}",
86            app_name, app_version, os_name, os_version, engine_name, engine_version, locale
87        );
88
89        Self {
90            name,
91            fingerprint,
92            platform,
93            user_agent,
94        }
95    }
96
97    pub async fn checkout<R: Runtime>(
98        &self,
99        app_handle: &AppHandle<R>,
100        options: &MachineCheckoutOpts,
101    ) -> Result<MachineFile> {
102        let config_state = app_handle.get_keygen_config();
103        let config = config_state.lock().await;
104        let license_state = app_handle.get_license_state();
105        let license_state = license_state.lock().await;
106        if let Some(license) = &license_state.license {
107            log::info!("Checking out machine file: {}", self.fingerprint);
108            let machine = license
109                .clone()
110                .with_config(config.clone())
111                .machine(&self.fingerprint)
112                .await?;
113            let machine_file = machine
114                .with_config(config.clone())
115                .checkout(options)
116                .await?;
117            Self::save_machine_file(app_handle, &machine_file)?;
118            Ok(machine_file)
119        } else {
120            Err(Error::NoLicenseError)
121        }
122    }
123
124    pub fn load_machine_file<R: Runtime>(
125        app_handle: &AppHandle<R>,
126        key: &str,
127    ) -> Result<Option<MachineFile>> {
128        let path = Self::get_machine_file_path(app_handle)?;
129        if !path.exists() {
130            return Ok(None);
131        }
132        let content = fs::read_to_string(path)?;
133        let machine_file = MachineFile::from_cert(key, &content)?;
134        Ok(Some(machine_file))
135    }
136
137    pub fn remove_machine_file<R: Runtime>(app_handle: &AppHandle<R>) -> Result<()> {
138        let path = Self::get_machine_file_path(app_handle)?;
139        if path.exists() {
140            fs::remove_file(path)?;
141        }
142        Ok(())
143    }
144
145    fn get_machine_file_path<R: Runtime>(app_handle: &AppHandle<R>) -> Result<PathBuf> {
146        let data_dir = get_app_keygen_path(app_handle)?;
147        let path = data_dir.join("machine.lic");
148        Ok(path)
149    }
150
151    fn save_machine_file<R: Runtime>(
152        app_handle: &AppHandle<R>,
153        machine_file: &MachineFile,
154    ) -> Result<()> {
155        let path = Self::get_machine_file_path(app_handle)?;
156        let mut file = File::create(path)?;
157        file.write_all(machine_file.certificate.as_bytes())?;
158        Ok(())
159    }
160}