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")]
13pub enum WindowsUpdateInstallMode {
14    /// Specifies there's a basic UI during the installation process, including a final dialog box at the end.
15    BasicUi,
16    /// The quiet mode means there's no user interaction required.
17    /// Requires admin privileges if the installer does.
18    Quiet,
19    /// Specifies unattended mode, which means the installation only shows a progress bar.
20    Passive,
21}
22
23impl WindowsUpdateInstallMode {
24    /// Returns the associated `msiexec.exe` arguments.
25    pub fn msiexec_args(&self) -> &'static [&'static str] {
26        match self {
27            Self::BasicUi => &["/qb+"],
28            Self::Quiet => &["/quiet"],
29            Self::Passive => &["/passive"],
30        }
31    }
32
33    /// Returns the associated nsis arguments.
34    pub fn nsis_args(&self) -> &'static [&'static str] {
35        // `/P`: Passive
36        // `/S`: Silent
37        // `/R`: Restart
38        match self {
39            Self::Passive => &["/P", "/R"],
40            Self::Quiet => &["/S", "/R"],
41            _ => &[],
42        }
43    }
44}
45
46impl Display for WindowsUpdateInstallMode {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(
49            f,
50            "{}",
51            match self {
52                Self::BasicUi => "basicUi",
53                Self::Quiet => "quiet",
54                Self::Passive => "passive",
55            }
56        )
57    }
58}
59
60impl Default for WindowsUpdateInstallMode {
61    fn default() -> Self {
62        Self::Passive
63    }
64}
65
66#[derive(Debug, Clone, Deserialize, Default)]
67#[serde(rename_all = "camelCase")]
68pub struct WindowsConfig {
69    /// Additional arguments given to the NSIS or WiX installer.
70    #[serde(
71        default,
72        alias = "installer-args",
73        deserialize_with = "deserialize_os_string"
74    )]
75    pub installer_args: Vec<OsString>,
76    /// Updating mode, defaults to `passive` mode.
77    ///
78    /// See [`WindowsUpdateInstallMode`] for more info.
79    #[serde(default, alias = "install-mode")]
80    pub install_mode: WindowsUpdateInstallMode,
81}
82
83fn deserialize_os_string<'de, D>(deserializer: D) -> Result<Vec<OsString>, D::Error>
84where
85    D: Deserializer<'de>,
86{
87    Ok(Vec::<String>::deserialize(deserializer)?
88        .into_iter()
89        .map(OsString::from)
90        .collect::<Vec<_>>())
91}
92
93/// Updater configuration.
94#[derive(Debug, Clone, Default)]
95pub struct Config {
96    /// Dangerously allow using insecure transport protocols for update endpoints.
97    pub dangerous_insecure_transport_protocol: 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)]
117            pub endpoints: Vec<Url>,
118            pub pubkey: String,
119            pub windows: Option<WindowsConfig>,
120        }
121
122        let config = Config::deserialize(deserializer)?;
123
124        validate_endpoints(
125            &config.endpoints,
126            config.dangerous_insecure_transport_protocol,
127        )
128        .map_err(serde::de::Error::custom)?;
129
130        Ok(Self {
131            dangerous_insecure_transport_protocol: config.dangerous_insecure_transport_protocol,
132            endpoints: config.endpoints,
133            pubkey: config.pubkey,
134            windows: config.windows,
135        })
136    }
137}
138
139pub(crate) fn validate_endpoints(
140    endpoints: &[Url],
141    dangerous_insecure_transport_protocol: bool,
142) -> crate::Result<()> {
143    if !dangerous_insecure_transport_protocol {
144        for url in endpoints {
145            if url.scheme() != "https" {
146                #[cfg(debug_assertions)]
147                {
148                    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.");
149                    eprintln!("[\x1b[33mWARNING\x1b[0m] if this is a desired behavior, you can enable `dangerousInsecureTransportProtocol` in the plugin configuration");
150                }
151                #[cfg(not(debug_assertions))]
152                return Err(crate::Error::InsecureTransportProtocol);
153            }
154        }
155    }
156
157    Ok(())
158}