Skip to main content

tauri_plugin_updater/
config.rs

1// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5use std::{ffi::OsString, fmt::Display};
6
7use serde::{Deserialize, Deserializer};
8use url::Url;
9
10/// Install modes for the Windows update.
11#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
12#[serde(rename_all = "camelCase")]
13#[derive(Default)]
14pub enum WindowsUpdateInstallMode {
15    /// Specifies there's a basic UI during the installation process, including a final dialog box at the end.
16    BasicUi,
17    /// The quiet mode means there's no user interaction required.
18    /// Requires admin privileges if the installer does.
19    Quiet,
20    /// Specifies unattended mode, which means the installation only shows a progress bar.
21    #[default]
22    Passive,
23}
24
25impl WindowsUpdateInstallMode {
26    /// Returns the associated `msiexec.exe` arguments.
27    pub fn msiexec_args(&self) -> &'static [&'static str] {
28        match self {
29            Self::BasicUi => &["/qb+"],
30            Self::Quiet => &["/quiet"],
31            Self::Passive => &["/passive"],
32        }
33    }
34
35    /// Returns the associated nsis arguments.
36    pub fn nsis_args(&self) -> &'static [&'static str] {
37        // `/P`: Passive
38        // `/S`: Silent
39        // `/R`: Restart
40        match self {
41            Self::Passive => &["/P", "/R"],
42            Self::Quiet => &["/S", "/R"],
43            _ => &[],
44        }
45    }
46}
47
48impl Display for WindowsUpdateInstallMode {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        write!(
51            f,
52            "{}",
53            match self {
54                Self::BasicUi => "basicUi",
55                Self::Quiet => "quiet",
56                Self::Passive => "passive",
57            }
58        )
59    }
60}
61
62#[derive(Debug, Clone, Deserialize, Default)]
63#[serde(rename_all = "camelCase")]
64pub struct WindowsConfig {
65    /// Additional arguments given to the NSIS or WiX installer.
66    #[serde(
67        default,
68        alias = "installer-args",
69        deserialize_with = "deserialize_os_string"
70    )]
71    pub installer_args: Vec<OsString>,
72    /// Updating mode, defaults to `passive` mode.
73    ///
74    /// See [`WindowsUpdateInstallMode`] for more info.
75    #[serde(default, alias = "install-mode")]
76    pub install_mode: WindowsUpdateInstallMode,
77}
78
79fn deserialize_os_string<'de, D>(deserializer: D) -> Result<Vec<OsString>, D::Error>
80where
81    D: Deserializer<'de>,
82{
83    Ok(Vec::<String>::deserialize(deserializer)?
84        .into_iter()
85        .map(OsString::from)
86        .collect::<Vec<_>>())
87}
88
89/// Updater configuration.
90#[derive(Debug, Clone, Default)]
91pub struct Config {
92    /// Dangerously allow using insecure transport protocols for update endpoints.
93    pub dangerous_insecure_transport_protocol: bool,
94    /// Dangerously accept invalid TLS certificates for update requests.
95    pub dangerous_accept_invalid_certs: bool,
96    /// Dangerously accept invalid hostnames for TLS certificates for update requests.
97    pub dangerous_accept_invalid_hostnames: bool,
98    /// Updater endpoints.
99    pub endpoints: Vec<Url>,
100    /// Signature public key.
101    pub pubkey: String,
102    /// The Windows configuration for the updater.
103    pub windows: Option<WindowsConfig>,
104}
105
106impl<'de> Deserialize<'de> for Config {
107    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108    where
109        D: Deserializer<'de>,
110    {
111        #[derive(Deserialize)]
112        #[serde(rename_all = "camelCase")]
113        pub struct Config {
114            #[serde(default, alias = "dangerous-insecure-transport-protocol")]
115            pub dangerous_insecure_transport_protocol: bool,
116            #[serde(default, alias = "dangerous-accept-invalid-certs")]
117            pub dangerous_accept_invalid_certs: bool,
118            #[serde(default, alias = "dangerous-accept-invalid-hostnames")]
119            pub dangerous_accept_invalid_hostnames: bool,
120            #[serde(default)]
121            pub endpoints: Vec<Url>,
122            pub pubkey: String,
123            pub windows: Option<WindowsConfig>,
124        }
125
126        let config = Config::deserialize(deserializer)?;
127
128        validate_endpoints(
129            &config.endpoints,
130            config.dangerous_insecure_transport_protocol,
131        )
132        .map_err(serde::de::Error::custom)?;
133
134        Ok(Self {
135            dangerous_insecure_transport_protocol: config.dangerous_insecure_transport_protocol,
136            dangerous_accept_invalid_certs: config.dangerous_accept_invalid_certs,
137            dangerous_accept_invalid_hostnames: config.dangerous_accept_invalid_hostnames,
138            endpoints: config.endpoints,
139            pubkey: config.pubkey,
140            windows: config.windows,
141        })
142    }
143}
144
145pub(crate) fn validate_endpoints(
146    endpoints: &[Url],
147    dangerous_insecure_transport_protocol: bool,
148) -> crate::Result<()> {
149    if !dangerous_insecure_transport_protocol {
150        for url in endpoints {
151            if url.scheme() != "https" {
152                #[cfg(debug_assertions)]
153                {
154                    eprintln!("[\x1b[33mWARNING\x1b[0m] The updater endpoint \"{url}\" doesn't use `https` protocol. This is allowed in development but will fail in release builds.");
155                    eprintln!("[\x1b[33mWARNING\x1b[0m] if this is a desired behavior, you can enable `dangerousInsecureTransportProtocol` in the plugin configuration");
156                }
157                #[cfg(not(debug_assertions))]
158                return Err(crate::Error::InsecureTransportProtocol);
159            }
160        }
161    }
162
163    Ok(())
164}