From 7d3bc3f8c361bc2f82bb26927a879309df582815 Mon Sep 17 00:00:00 2001 From: Albin Date: Tue, 9 Apr 2024 14:58:12 +0200 Subject: [PATCH 001/214] Add empty android changelog with instructions The instructions has been duplicated from the main CHANGELOG.md in the root of the repository. --- android/CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 android/CHANGELOG.md diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md new file mode 100644 index 000000000000..ba2715d5e8dc --- /dev/null +++ b/android/CHANGELOG.md @@ -0,0 +1,22 @@ +# Android changelog +All changes to the software that can be noticed from the users' perspective should have an entry in +this file. Except very minor things that will not affect functionality, such as log message changes +and minor GUI adjustments. + +### Format + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). + +Entries should have the imperative form, just like commit messages. Start each entry with words like +add, fix, increase, force etc.. Not added, fixed, increased, forced etc. + +Line wrap the file at 100 chars. That is over here -> | + +### Categories each change fall into + +* **Added**: for new features. +* **Changed**: for changes in existing functionality. +* **Deprecated**: for soon-to-be removed features. +* **Removed**: for now removed features. +* **Fixed**: for any bug fixes. +* **Security**: in case of vulnerabilities. From 661bdec3fe8fd06c6f1eece34a9e55a78043278d Mon Sep 17 00:00:00 2001 From: Albin Date: Tue, 9 Apr 2024 15:17:33 +0200 Subject: [PATCH 002/214] Move android changes to android changelog --- CHANGELOG.md | 378 ++----------------------------------------- android/CHANGELOG.md | 351 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 368 insertions(+), 361 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9788d9e826b2..4217f6079f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,10 +27,6 @@ Line wrap the file at 100 chars. Th dropped packets and does not take fragmentation into account. - Add ability to import server IP overrides in GUI. -#### Android -- Add the ability to create and manage custom lists of relays. -- Add Server IP overrides feature. - ### Changed - Change default obfuscation setting to `auto`. - Migrate obfuscation settings for existing users from `off` to `auto`. @@ -46,12 +42,6 @@ Line wrap the file at 100 chars. Th - Fix incorrectly showing "App lost contact with system service" notification on suspend. -## [android/2024.1] - 2024-04-05 -### Fixed -#### Android -- Fix 3D map animation distance calculation. - - ## [2024.1] - 2024-03-21 ### Fixed - Fix map scaling issues when changing display scale settings and when running under Wayland on @@ -63,37 +53,6 @@ Line wrap the file at 100 chars. Th gracefully. -## [android/2024.1-beta1] - 2024-03-18 -### Added -#### Android -- Add 3D map to the connect screen. -- Add support for all screen orientations. -- Add possibility to filter locations by provider. -- Add toggle for enabling or disabling split tunneling. -- Add auto-connect and lockdown guide on platforms with system vpn settings. - -### Changed -#### Android -- Migrate to Compose Navigation which also improves screen transition animations. -- Increase focus highlight opacity. -- Set auto-connect setting as legacy on platforms with system vpn settings. -- Change default obfuscation setting to `auto`. -- Migrate obfuscation settings for existing users from `off` to `auto`. -- Update support email address to new email address, support@mullvadvpn.net. - -### Fixed -#### Android -- Improve DPAD navigation. -- Upgrade wireguard-go. This might improve connectivity on some devices such as chromebooks. -- Fix connectivity issues that would occur when using quantum-resistant tunnels with an incorrectly - configured MTU. - -### Security -#### Android -- Change from singleTask to singleInstance to fix Task Affinity Vulnerability in Android 8. -- Add protection against some tapjacking vulnerabilities. - - ## [2024.1-beta2] - 2024-02-19 ### Added - Add account UUID to verbose 'mullvad account get -v' output. @@ -143,10 +102,6 @@ Line wrap the file at 100 chars. Th with VMWare. -## [android/2023.10] - 2023-12-14 -Identical to `android/2023.10-beta1`. - - ## [2024.1-beta1] - 2023-12-14 ### Added - Add CLI support for applying patches to the settings with `mullvad import-settings`. @@ -168,18 +123,6 @@ Identical to `android/2023.10-beta1`. - Fix stable releases being considered a downgrade from a beta version by `dnf`. -## [android/2023.10-beta1] - 2023-12-11 -### Fixed -#### Android -- Fix relay selector attempting to connect to OpenVPN relays in some circumstances. - - -## [android/2023.9] - 2023-12-06 -### Added -#### Android -- Add missing translations for in-app purchases and a few settings. - - ## [2023.6] - 2023-12-06 ### Changed - Update OpenVPN to 2.6.8 from 2.6.0. @@ -189,20 +132,6 @@ Identical to `android/2023.10-beta1`. peer. -## [android/2023.8] - 2023-11-28 -Identical to `android/2023.8-beta2`. - - -## [android/2023.8-beta2] - 2023-11-27 -### Fixed -#### Android -- Fix top bar flickering in some scrollable views. -- Fix welcome screen sometimes showing on app restart after adding time. -- Fix inconsistencies with the account history in the login view. -- Fix OS crash when sharing long logs by instead sharing the log content as a file. -- Improve in-app purchase and verification flow in some circumstances. - - ## [2023.6-beta1] - 2023-11-23 ### Added - Add customizable relay lists to the CLI on desktop. Custom lists can be managed through @@ -241,26 +170,6 @@ Identical to `android/2023.8-beta2`. the directory in ProgramData (CVE-2023-50446). -## [android/2023.8-beta1] - 2023-11-20 -### Changed -#### Android -- Add Google Play in-app purchases to the build distributed via Google Play. -- Add social media content blocker. -- Add support for setting per-app language in system settings. -- Improve device and expiry information throughout the app. -- Migrate remaining views to Compose and MVVM (welcome, out-of-time, login, problem report, logs - voucher dialog, in-app notifications). -- Add share button to the log view which can be used to copy or in other ways share the log text. - This was partially added to due to limitations in Compose which result in it not being possible to - select and copy text in the log view. - - -## [android/2023.7] - 2023-10-11 -### Changed -#### Android -- Minor addition to problem report logs to aid debugging of user issues. - - ## [2023.5] - 2023-10-10 ### Added #### Linux @@ -271,14 +180,6 @@ Identical to `android/2023.8-beta2`. - Fix connectivity issues when switching between networks or disconnecting. -## [android/2023.6] - 2023-09-25 -### Fixed -#### Android -- Fix inconsistent dialog corner radius. -- Fix missing scrolling in the changes dialog. -- Fix unused bundled relay list. - - ## [2023.5-beta2] - 2023-09-20 ### Fixed #### macOS @@ -290,49 +191,6 @@ Identical to `android/2023.8-beta2`. - Fix inability to switch from a network to a higher-priority network without the tunnel timing out. -## [android/2023.6-beta2] - 2023-09-13 -### Fixed -#### Android -- Fix tunnel state and connection details sometimes getting stuck showing the wrong information. -- Fix MTU dismiss behavior. -- Fix DNS input crash. -- Fix inconsistent dialog padding. - - -## [android/2023.6-beta1] - 2023-08-29 -### Added -#### Android -- Add quantum resistant tunneling. -- Add UDP-over-TCP WireGuard obfuscation. -- Improve how the Android firewall handles incoming connections on Android 11+ devices. -- Add search bar to the Select location view. -- Add settings entry to configure WireGuard port by either using a predefined or custom port. - -### Changed -#### Android -- Combine the "Preferences" and "Account" settings sub-menus into a single one called - "VPN Settings". -- Make "Split tunneling" more accessible by placing it directly in the main settings menu. -- Migrate multiple views to Compose and MVVM (Settings, Account, Split tunneling, Select location). - -### Fixed -#### Android -- Reduce flickering in the main/connect view. - - -## [android/2023.5] - 2023-08-02 -### Changed -#### Android -- New fancy version number in order to try to resolve Google Play distribution issues. Otherwise - same as `android/2023.4`. - - -## [android/2023.4] - 2023-07-18 -### Changed -#### Android -- Prevent opening download page in Google Play builds. - - ## [2023.5-beta1] - 2023-07-13 ### Added - Add `--help` and `--version` options to the desktop GUI application. @@ -374,59 +232,6 @@ Identical to `android/2023.8-beta2`. - Fix misaligned read in `shadowsocks` leading to a panic on some platforms. -## [android/2023.3] - 2023-06-27 -### Changed -#### Android -- Change so that all links and texts leading to the mullvad webpage display a modified version of - the webpage that does not include links to the account page in order to comply with - the Google Play payment policies. This doesn't apply to F-Droid builds. -- Hide the FAQs and Guides button for Google Play users. - - -## [android/2023.2] - 2023-05-22 -### Changed -#### Android -- Change so that all links and texts leading to the account web page (which also includes a payment - flow) are either hidden or leads to the app itself (notification actions) in order to comply with - the Google Play payment policies. This doesn't apply to F-Droid builds. - - -## [android/2023.1] - 2023-05-16 -### Fixed -#### Android -- Fix DNS input keyboard type. - - -## [android/2023.1-beta2] - 2023-05-09 -### Added -#### Android -- Add "Manage account" button to the account view. - -### Fixed -#### Android -- Fix missing payment info in out-of-time view. - - -## [android/2023.1-beta1] - 2023-05-03 -### Added -#### Android -- Add themed icon. -- Add DNS content blockers. - -### Changed -#### Android -- Clarify some of the error messages throughout the app. -- Increase WireGuard key rotation interval to 14 days. -- Change the DNS/MTU input to rely on dialogs in order to improve the UX on some devices. -- Hide "Buy more credit" buttons in the default release build published to Google Play, our website - and GitHub. The buttons are still visible for F-Droid builds. - -### Fixed -#### Android -- Fix adaptive app icon which previously had a displaced nose and some other oddities. -- Fix app version sometimes missing in the settings menu. - - ## [2023.4-beta1] - 2023-05-02 ### Added - Log select settings on each connection attempt. @@ -573,17 +378,6 @@ Identical to `android/2023.8-beta2`. queries to servers that would normally be blocked. -## [android/2022.3] - 2022-11-14 -### Added -#### Android -- Add privacy policy link in settings. -- Add initial privacy consent which is showed on each start until approved. - - -## [android/2022.2] - 2022-10-17 -Identical to android/2022.2-beta2 except for updated translations. - - ## [2022.5] - 2022-10-14 ### Fixed #### Linux @@ -675,22 +469,6 @@ Identical to android/2022.2-beta2 except for updated translations. interface. -## [android/2022.2-beta2] - 2022-09-09 -### Changed -#### Android -- Refresh device data when opening the account view to ensure the local data is up-to-date and that - the device hasn't been revoked. -- Disable settings button during login. - -### Fixed -#### Android -- Fix crash sometimes occurring during account creation. -- Fix tunnel info expansion state not remembered during pause and resume. -- Fix crash during some view transitions. -- Fix disabled login button on login failure. Instead, the login button will now still be enabled - on login failures to let the user re-attempt the login. - - ## [2022.4] - 2022-08-19 ### Added #### Windows @@ -707,36 +485,6 @@ Identical to android/2022.2-beta2 except for updated translations. actually transport. -## [android/2022.2-beta1] - 2022-08-11 -### Added -#### Android -- Add device management to the Android app. This simplifies knowing which device is which and adds - the option to log other devices out when the account already has five devices. - -### Changed -#### Android -- Lowered default MTU to 1280 on Android. -- Disable app icon badge for tunnel state notification/status. - -### Removed -#### Android -- Remove WireGuard view as it's no longer needed with the new way of managing devices. - -### Fixed -#### Android -- Fix unused dependencies loaded in the service/tile DI graph. -- Fix missing IPC message unregistration causing multiple copies of some messages to be received. -- Fix quick settings tile being unresponsive and causing crashes on some devices. -- Fix quick settings tile not working when the device is locked. It will now prompt the user to - unlock the device before attempting to toggle the tunnel state. -- Fix crash when clicking in-app URL notifications. - -### Security -#### Android -- Prevent location request responses from being received outside the tunnel when in the connected - state. - - ## [2022.3] - 2022-08-10 This release is for desktop only. @@ -874,10 +622,6 @@ This release is identical to 2022.2-beta2. one application. -## [android/2022.1] - 2022-03-01 -Identical to android/2022.1-beta3 except for a few updated translations. - - ## [2022.1] - 2022-03-01 This release is for desktop only. @@ -974,62 +718,6 @@ This release is identical to 2022.1-beta2 except that it has translations for ne whereas on Linux and macOS only root processes are able to reach the API. -## [android/2022.1-beta3] - 2022-02-08 -### Fixed -#### Android -- Fix app crash caused by quick settings tile. - - -## [android/2022.1-beta2] - 2022-01-27 -### Fixed -#### Android -- Fix app sometimes crashing during startup on Android TVs. - - -## [android/2022.1-beta1] - 2022-01-26 -### Added -#### Android -- Add toggle for Split tunneling view to be able to show system apps -- Add support of adaptive icons (available only from Android 8). - -### Changed -- Gradually increase the WireGuard connectivity check timeout, lowering the timeout for the first - few attempts. - -#### Android -- Improve stability by running the UI and the tunnel management logic in separate processes. -- Remove dialog warning that only custom local DNS servers are supported, since public custom DNS - servers are now supported. -- Drop support for Android 7/7.1 (Android 8/API level 26 or later is now required). -- Change so that swiping the notification no longer kills the service since that isn't a common way - of handling the lifecycle in Android. Instead rely on the following mechanisms to kill the - service: - * Swiping to remove app from the Recents/Overview screen. - * Android Background Execution Limits. - * The System Settings way of killing apps ("Force Stop"). -- Change Quick Settings tile label to reflect the action of clicking the tile. Also add a subtitle - on supported Android versions (Q and above) to reflect the state. -- Hide the tunnel state notification from the lock screen. - -### Fixed -#### Android -- Fix banner sometimes incorrectly showing (e.g. "BLOCKING INTERNET"). -- Fix tunnel state notification sometimes re-appearing after being dismissed. -- Fix invalid URLs. Rely on browser locale rather than app/system language. -- Automatically disable custom DNS when no servers have been added. -- Fix issue where erasing wireguard MTU value did not clear its setting. -- Fix initial state of Split tunneling excluded apps list. Previously it was not notified the daemon -properly after initialization. -- Fix UI sometimes not updating correctly while no split screen or after having a dialog from - another app appear on top. -- Fix request to connect from notification or quick-settings tile not connecting if VPN permission - isn't granted to the app. The app will now show the UI to ask for the permission and correctly - connect after it is granted. -- Fix quick-settings tile sometimes showing the wrong tunnel state. -- Fix TV-only apps not appearing in the Split Tunneling screen. -- Fix status bar having the wrong color after logging out. - - ## [2021.6] - 2021-11-17 ### Fixed - Fix the font for Russian. Issue introduced in 2021.6-beta1. @@ -1249,57 +937,16 @@ So there might be warnings when installing this for a while. ## [2021.3-beta1] - 2021-04-13 This release is for desktop only. -### Added -- Preserve log of old daemon instance when upgrading on Desktop. - -#### Linux -- Always enable `src_valid_mark` config option when connecting to allow policty based routing. - -### Changed -- Allow whitespace in account token in CLI. -- Read account token from standard input unless given as an argument in CLI. -- Make WireGuard automatic key rotation interval mandatory and between 1 and 7 days. -- Show default, minimum, and maximum key rotation intervals in CLI. -- Attempt to send problem reports using other endpoints if using the primary one fails. -- Upgrade wireguard-go to version 20210225140808 (Windows: v0.3.8) -- Settings format updated to `v3`. - -### Fixed -- Fix GUI not showing correct view if disconnected from the daemon during app startup. -- Fix incorrectly displayed "inconsistent version" text in settings if disconnected from daemon on - startup. - -#### Linux -- Further improve offline monitor to properly receive `ENETUNREACH`. - -### Security -- Always reconnect appropriately after an upgrade. Previously, installing the app twice in - succession, with auto-connect disabled, would cause it to re-launch in the disconnected state. - - -## [android/2021.1] - 2021-05-04 -This release is for Android only. - -This release is identical to android/2021.1-beta1. -This is our first non beta release for the Android platform! - - -## [android/2021.1-beta1] - 2021-04-06 -This release is for Android only. From now on, Android releases will have this new header format -that is the same as the git tag they receive: `android/`. - ### Added - Enable isolation of the Electron renderer process to protect against potentially malicious third party dependencies. +- Preserve log of old daemon instance when upgrading on Desktop. - Add 51820 to list of WireGuard ports in app settings. - Add option to connect to WireGuard relays over IPv6. - Add Burmese translations. -#### Android -- Allow reaching the API server when connecting, disconnecting or in a blocked state. -- Add FAQs & Guides menu entry to the Settings screen. -- Add TV banner for better user experience and requirements. -- Style StatucBar and NavigationBar to make our app a bit more beautiful. +#### Linux +- Always enable `src_valid_mark` config option when connecting to allow policty based routing. ### Changed - Update Electron from 11.0.2 to 11.2.1 which includes a newer Chromium version and @@ -1308,9 +955,13 @@ that is the same as the git tag they receive: `android/`. - Only download a new relay list if it has been modified. - Connect to the API only via TLS 1.3 - Shrink account history capactity from 3 account entries to 1. - -#### Android -- WireGuard key is now rotated sooner: every four days instead of seven. +- Allow whitespace in account token in CLI. +- Read account token from standard input unless given as an argument in CLI. +- Make WireGuard automatic key rotation interval mandatory and between 1 and 7 days. +- Show default, minimum, and maximum key rotation intervals in CLI. +- Attempt to send problem reports using other endpoints if using the primary one fails. +- Upgrade wireguard-go to version 20210225140808 (Windows: v0.3.8) +- Settings format updated to `v3`. #### Windows - Upgrade Wintun from 0.9.2 to 0.10.1. @@ -1318,6 +969,9 @@ that is the same as the git tag they receive: `android/`. ### Fixed - Fix delay in showing/hiding update notification when toggling beta program. - Improve responsiveness when reconnecting after some failed connection attempts. +- Fix GUI not showing correct view if disconnected from the daemon during app startup. +- Fix incorrectly displayed "inconsistent version" text in settings if disconnected from daemon on + startup. #### Windows - Fix "cannot find the file" error while creating a Wintun adapter by upgrading Wintun. @@ -1327,9 +981,11 @@ that is the same as the git tag they receive: `android/`. - Stop using NM for managing DNS if it's newer than 1.26. - Fix DNS issues where NM would overwrite Mullvad tunnel's DNS config in systemd-resolved. - Fix issues with hosts where the firewall is doing reverse path filtering. +- Further improve offline monitor to properly receive `ENETUNREACH`. -#### Android -- Fix input area sometimes disappearing when returning to the Login screen. +### Security +- Always reconnect appropriately after an upgrade. Previously, installing the app twice in + succession, with auto-connect disabled, would cause it to re-launch in the disconnected state. ## [2021.2] - 2021-02-18 diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index ba2715d5e8dc..77ca045f8f8a 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -20,3 +20,354 @@ Line wrap the file at 100 chars. Th * **Removed**: for now removed features. * **Fixed**: for any bug fixes. * **Security**: in case of vulnerabilities. + +## [Unreleased] +### Added +- Add the ability to create and manage custom lists of relays. +- Add Server IP overrides feature. + + +## [android/2024.1] - 2024-04-05 +### Fixed +#### Android +- Fix 3D map animation distance calculation. + + +## [android/2024.1-beta1] - 2024-03-18 +### Added +#### Android +- Add 3D map to the connect screen. +- Add support for all screen orientations. +- Add possibility to filter locations by provider. +- Add toggle for enabling or disabling split tunneling. +- Add auto-connect and lockdown guide on platforms with system vpn settings. + +### Changed +#### Android +- Migrate to Compose Navigation which also improves screen transition animations. +- Increase focus highlight opacity. +- Set auto-connect setting as legacy on platforms with system vpn settings. +- Change default obfuscation setting to `auto`. +- Migrate obfuscation settings for existing users from `off` to `auto`. +- Update support email address to new email address, support@mullvadvpn.net. + +### Fixed +#### Android +- Improve DPAD navigation. +- Upgrade wireguard-go. This might improve connectivity on some devices such as chromebooks. +- Fix connectivity issues that would occur when using quantum-resistant tunnels with an incorrectly + configured MTU. + +### Security +#### Android +- Change from singleTask to singleInstance to fix Task Affinity Vulnerability in Android 8. +- Add protection against some tapjacking vulnerabilities. + + +## [android/2023.10] - 2023-12-14 +Identical to `android/2023.10-beta1`. + + +## [android/2023.10-beta1] - 2023-12-11 +### Fixed +#### Android +- Fix relay selector attempting to connect to OpenVPN relays in some circumstances. + + +## [android/2023.9] - 2023-12-06 +### Added +#### Android +- Add missing translations for in-app purchases and a few settings. + + +## [android/2023.8] - 2023-11-28 +Identical to `android/2023.8-beta2`. + + +## [android/2023.8-beta2] - 2023-11-27 +### Fixed +#### Android +- Fix top bar flickering in some scrollable views. +- Fix welcome screen sometimes showing on app restart after adding time. +- Fix inconsistencies with the account history in the login view. +- Fix OS crash when sharing long logs by instead sharing the log content as a file. +- Improve in-app purchase and verification flow in some circumstances. + + +## [android/2023.8-beta1] - 2023-11-20 +### Changed +#### Android +- Add Google Play in-app purchases to the build distributed via Google Play. +- Add social media content blocker. +- Add support for setting per-app language in system settings. +- Improve device and expiry information throughout the app. +- Migrate remaining views to Compose and MVVM (welcome, out-of-time, login, problem report, logs + voucher dialog, in-app notifications). +- Add share button to the log view which can be used to copy or in other ways share the log text. + This was partially added to due to limitations in Compose which result in it not being possible to + select and copy text in the log view. + + +## [android/2023.7] - 2023-10-11 +### Changed +#### Android +- Minor addition to problem report logs to aid debugging of user issues. + + +## [android/2023.6] - 2023-09-25 +### Fixed +#### Android +- Fix inconsistent dialog corner radius. +- Fix missing scrolling in the changes dialog. +- Fix unused bundled relay list. + + +## [android/2023.6-beta2] - 2023-09-13 +### Fixed +#### Android +- Fix tunnel state and connection details sometimes getting stuck showing the wrong information. +- Fix MTU dismiss behavior. +- Fix DNS input crash. +- Fix inconsistent dialog padding. + + +## [android/2023.6-beta1] - 2023-08-29 +### Added +#### Android +- Add quantum resistant tunneling. +- Add UDP-over-TCP WireGuard obfuscation. +- Improve how the Android firewall handles incoming connections on Android 11+ devices. +- Add search bar to the Select location view. +- Add settings entry to configure WireGuard port by either using a predefined or custom port. + +### Changed +#### Android +- Combine the "Preferences" and "Account" settings sub-menus into a single one called + "VPN Settings". +- Make "Split tunneling" more accessible by placing it directly in the main settings menu. +- Migrate multiple views to Compose and MVVM (Settings, Account, Split tunneling, Select location). + +### Fixed +#### Android +- Reduce flickering in the main/connect view. + + +## [android/2023.5] - 2023-08-02 +### Changed +#### Android +- New fancy version number in order to try to resolve Google Play distribution issues. Otherwise + same as `android/2023.4`. + + +## [android/2023.4] - 2023-07-18 +### Changed +#### Android +- Prevent opening download page in Google Play builds. + + +## [android/2023.3] - 2023-06-27 +### Changed +#### Android +- Change so that all links and texts leading to the mullvad webpage display a modified version of + the webpage that does not include links to the account page in order to comply with + the Google Play payment policies. This doesn't apply to F-Droid builds. +- Hide the FAQs and Guides button for Google Play users. + + +## [android/2023.2] - 2023-05-22 +### Changed +#### Android +- Change so that all links and texts leading to the account web page (which also includes a payment + flow) are either hidden or leads to the app itself (notification actions) in order to comply with + the Google Play payment policies. This doesn't apply to F-Droid builds. + + +## [android/2023.1] - 2023-05-16 +### Fixed +#### Android +- Fix DNS input keyboard type. + + +## [android/2023.1-beta2] - 2023-05-09 +### Added +#### Android +- Add "Manage account" button to the account view. + +### Fixed +#### Android +- Fix missing payment info in out-of-time view. + + +## [android/2023.1-beta1] - 2023-05-03 +### Added +#### Android +- Add themed icon. +- Add DNS content blockers. + +### Changed +#### Android +- Clarify some of the error messages throughout the app. +- Increase WireGuard key rotation interval to 14 days. +- Change the DNS/MTU input to rely on dialogs in order to improve the UX on some devices. +- Hide "Buy more credit" buttons in the default release build published to Google Play, our website + and GitHub. The buttons are still visible for F-Droid builds. + +### Fixed +#### Android +- Fix adaptive app icon which previously had a displaced nose and some other oddities. +- Fix app version sometimes missing in the settings menu. + + +## [android/2022.3] - 2022-11-14 +### Added +#### Android +- Add privacy policy link in settings. +- Add initial privacy consent which is showed on each start until approved. + + +## [android/2022.2] - 2022-10-17 +Identical to android/2022.2-beta2 except for updated translations. + + +## [android/2022.2-beta2] - 2022-09-09 +### Changed +#### Android +- Refresh device data when opening the account view to ensure the local data is up-to-date and that + the device hasn't been revoked. +- Disable settings button during login. + +### Fixed +#### Android +- Fix crash sometimes occurring during account creation. +- Fix tunnel info expansion state not remembered during pause and resume. +- Fix crash during some view transitions. +- Fix disabled login button on login failure. Instead, the login button will now still be enabled + on login failures to let the user re-attempt the login. + + +## [android/2022.2-beta1] - 2022-08-11 +### Added +#### Android +- Add device management to the Android app. This simplifies knowing which device is which and adds + the option to log other devices out when the account already has five devices. + +### Changed +#### Android +- Lowered default MTU to 1280 on Android. +- Disable app icon badge for tunnel state notification/status. + +### Removed +#### Android +- Remove WireGuard view as it's no longer needed with the new way of managing devices. + +### Fixed +#### Android +- Fix unused dependencies loaded in the service/tile DI graph. +- Fix missing IPC message unregistration causing multiple copies of some messages to be received. +- Fix quick settings tile being unresponsive and causing crashes on some devices. +- Fix quick settings tile not working when the device is locked. It will now prompt the user to + unlock the device before attempting to toggle the tunnel state. +- Fix crash when clicking in-app URL notifications. + +### Security +#### Android +- Prevent location request responses from being received outside the tunnel when in the connected + state. + + +## [android/2022.1] - 2022-03-01 +Identical to android/2022.1-beta3 except for a few updated translations. + + +## [android/2022.1-beta3] - 2022-02-08 +### Fixed +#### Android +- Fix app crash caused by quick settings tile. + + +## [android/2022.1-beta2] - 2022-01-27 +### Fixed +#### Android +- Fix app sometimes crashing during startup on Android TVs. + + +## [android/2022.1-beta1] - 2022-01-26 +### Added +#### Android +- Add toggle for Split tunneling view to be able to show system apps +- Add support of adaptive icons (available only from Android 8). + +### Changed +- Gradually increase the WireGuard connectivity check timeout, lowering the timeout for the first + few attempts. + +#### Android +- Improve stability by running the UI and the tunnel management logic in separate processes. +- Remove dialog warning that only custom local DNS servers are supported, since public custom DNS + servers are now supported. +- Drop support for Android 7/7.1 (Android 8/API level 26 or later is now required). +- Change so that swiping the notification no longer kills the service since that isn't a common way + of handling the lifecycle in Android. Instead rely on the following mechanisms to kill the + service: + * Swiping to remove app from the Recents/Overview screen. + * Android Background Execution Limits. + * The System Settings way of killing apps ("Force Stop"). +- Change Quick Settings tile label to reflect the action of clicking the tile. Also add a subtitle + on supported Android versions (Q and above) to reflect the state. +- Hide the tunnel state notification from the lock screen. + +### Fixed +#### Android +- Fix banner sometimes incorrectly showing (e.g. "BLOCKING INTERNET"). +- Fix tunnel state notification sometimes re-appearing after being dismissed. +- Fix invalid URLs. Rely on browser locale rather than app/system language. +- Automatically disable custom DNS when no servers have been added. +- Fix issue where erasing wireguard MTU value did not clear its setting. +- Fix initial state of Split tunneling excluded apps list. Previously it was not notified the daemon +properly after initialization. +- Fix UI sometimes not updating correctly while no split screen or after having a dialog from + another app appear on top. +- Fix request to connect from notification or quick-settings tile not connecting if VPN permission + isn't granted to the app. The app will now show the UI to ask for the permission and correctly + connect after it is granted. +- Fix quick-settings tile sometimes showing the wrong tunnel state. +- Fix TV-only apps not appearing in the Split Tunneling screen. +- Fix status bar having the wrong color after logging out. + + +## [android/2021.1] - 2021-05-04 +This release is for Android only. + +This release is identical to android/2021.1-beta1. +This is our first non beta release for the Android platform! + + +## [android/2021.1-beta1] - 2021-04-06 +This release is for Android only. From now on, Android releases will have this new header format +that is the same as the git tag they receive: `android/`. + +### Added +- Add 51820 to list of WireGuard ports in app settings. +- Add option to connect to WireGuard relays over IPv6. +- Add Burmese translations. + +#### Android +- Allow reaching the API server when connecting, disconnecting or in a blocked state. +- Add FAQs & Guides menu entry to the Settings screen. +- Add TV banner for better user experience and requirements. +- Style StatucBar and NavigationBar to make our app a bit more beautiful. + +### Changed +- Allow provider constraint to specify multiple hosting providers. +- Only download a new relay list if it has been modified. +- Connect to the API only via TLS 1.3 +- Shrink account history capactity from 3 account entries to 1. + +#### Android +- WireGuard key is now rotated sooner: every four days instead of seven. + +### Fixed +- Improve responsiveness when reconnecting after some failed connection attempts. + +#### Android +- Fix input area sometimes disappearing when returning to the Login screen. From ef302ac53a0416af6fb6a3e14176ab6f14498e6b Mon Sep 17 00:00:00 2001 From: Albin Date: Tue, 9 Apr 2024 17:02:13 +0200 Subject: [PATCH 003/214] Add link to main changelog --- android/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 77ca045f8f8a..286f38e4b77c 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -371,3 +371,5 @@ that is the same as the git tag they receive: `android/`. #### Android - Fix input area sometimes disappearing when returning to the Login screen. + +For older non-stable releases, see the main changelog ../CHANGELOG.md From ab33e15563acc9baaf38e2c1fb8a27384cbb40d6 Mon Sep 17 00:00:00 2001 From: Albin Date: Tue, 9 Apr 2024 17:25:40 +0200 Subject: [PATCH 004/214] Strip android headers in android changelog --- android/CHANGELOG.md | 47 +------------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 286f38e4b77c..dcb2a01618a2 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -29,13 +29,11 @@ Line wrap the file at 100 chars. Th ## [android/2024.1] - 2024-04-05 ### Fixed -#### Android - Fix 3D map animation distance calculation. ## [android/2024.1-beta1] - 2024-03-18 ### Added -#### Android - Add 3D map to the connect screen. - Add support for all screen orientations. - Add possibility to filter locations by provider. @@ -43,7 +41,6 @@ Line wrap the file at 100 chars. Th - Add auto-connect and lockdown guide on platforms with system vpn settings. ### Changed -#### Android - Migrate to Compose Navigation which also improves screen transition animations. - Increase focus highlight opacity. - Set auto-connect setting as legacy on platforms with system vpn settings. @@ -52,14 +49,12 @@ Line wrap the file at 100 chars. Th - Update support email address to new email address, support@mullvadvpn.net. ### Fixed -#### Android - Improve DPAD navigation. - Upgrade wireguard-go. This might improve connectivity on some devices such as chromebooks. - Fix connectivity issues that would occur when using quantum-resistant tunnels with an incorrectly configured MTU. ### Security -#### Android - Change from singleTask to singleInstance to fix Task Affinity Vulnerability in Android 8. - Add protection against some tapjacking vulnerabilities. @@ -70,13 +65,11 @@ Identical to `android/2023.10-beta1`. ## [android/2023.10-beta1] - 2023-12-11 ### Fixed -#### Android - Fix relay selector attempting to connect to OpenVPN relays in some circumstances. ## [android/2023.9] - 2023-12-06 ### Added -#### Android - Add missing translations for in-app purchases and a few settings. @@ -86,7 +79,6 @@ Identical to `android/2023.8-beta2`. ## [android/2023.8-beta2] - 2023-11-27 ### Fixed -#### Android - Fix top bar flickering in some scrollable views. - Fix welcome screen sometimes showing on app restart after adding time. - Fix inconsistencies with the account history in the login view. @@ -96,7 +88,6 @@ Identical to `android/2023.8-beta2`. ## [android/2023.8-beta1] - 2023-11-20 ### Changed -#### Android - Add Google Play in-app purchases to the build distributed via Google Play. - Add social media content blocker. - Add support for setting per-app language in system settings. @@ -110,13 +101,11 @@ Identical to `android/2023.8-beta2`. ## [android/2023.7] - 2023-10-11 ### Changed -#### Android - Minor addition to problem report logs to aid debugging of user issues. ## [android/2023.6] - 2023-09-25 ### Fixed -#### Android - Fix inconsistent dialog corner radius. - Fix missing scrolling in the changes dialog. - Fix unused bundled relay list. @@ -124,7 +113,6 @@ Identical to `android/2023.8-beta2`. ## [android/2023.6-beta2] - 2023-09-13 ### Fixed -#### Android - Fix tunnel state and connection details sometimes getting stuck showing the wrong information. - Fix MTU dismiss behavior. - Fix DNS input crash. @@ -133,7 +121,6 @@ Identical to `android/2023.8-beta2`. ## [android/2023.6-beta1] - 2023-08-29 ### Added -#### Android - Add quantum resistant tunneling. - Add UDP-over-TCP WireGuard obfuscation. - Improve how the Android firewall handles incoming connections on Android 11+ devices. @@ -141,33 +128,28 @@ Identical to `android/2023.8-beta2`. - Add settings entry to configure WireGuard port by either using a predefined or custom port. ### Changed -#### Android - Combine the "Preferences" and "Account" settings sub-menus into a single one called "VPN Settings". - Make "Split tunneling" more accessible by placing it directly in the main settings menu. - Migrate multiple views to Compose and MVVM (Settings, Account, Split tunneling, Select location). ### Fixed -#### Android - Reduce flickering in the main/connect view. ## [android/2023.5] - 2023-08-02 ### Changed -#### Android - New fancy version number in order to try to resolve Google Play distribution issues. Otherwise same as `android/2023.4`. ## [android/2023.4] - 2023-07-18 ### Changed -#### Android - Prevent opening download page in Google Play builds. ## [android/2023.3] - 2023-06-27 ### Changed -#### Android - Change so that all links and texts leading to the mullvad webpage display a modified version of the webpage that does not include links to the account page in order to comply with the Google Play payment policies. This doesn't apply to F-Droid builds. @@ -176,7 +158,6 @@ Identical to `android/2023.8-beta2`. ## [android/2023.2] - 2023-05-22 ### Changed -#### Android - Change so that all links and texts leading to the account web page (which also includes a payment flow) are either hidden or leads to the app itself (notification actions) in order to comply with the Google Play payment policies. This doesn't apply to F-Droid builds. @@ -184,28 +165,23 @@ Identical to `android/2023.8-beta2`. ## [android/2023.1] - 2023-05-16 ### Fixed -#### Android - Fix DNS input keyboard type. ## [android/2023.1-beta2] - 2023-05-09 ### Added -#### Android - Add "Manage account" button to the account view. ### Fixed -#### Android - Fix missing payment info in out-of-time view. ## [android/2023.1-beta1] - 2023-05-03 ### Added -#### Android - Add themed icon. - Add DNS content blockers. ### Changed -#### Android - Clarify some of the error messages throughout the app. - Increase WireGuard key rotation interval to 14 days. - Change the DNS/MTU input to rely on dialogs in order to improve the UX on some devices. @@ -213,14 +189,12 @@ Identical to `android/2023.8-beta2`. and GitHub. The buttons are still visible for F-Droid builds. ### Fixed -#### Android - Fix adaptive app icon which previously had a displaced nose and some other oddities. - Fix app version sometimes missing in the settings menu. ## [android/2022.3] - 2022-11-14 ### Added -#### Android - Add privacy policy link in settings. - Add initial privacy consent which is showed on each start until approved. @@ -231,13 +205,11 @@ Identical to android/2022.2-beta2 except for updated translations. ## [android/2022.2-beta2] - 2022-09-09 ### Changed -#### Android - Refresh device data when opening the account view to ensure the local data is up-to-date and that the device hasn't been revoked. - Disable settings button during login. ### Fixed -#### Android - Fix crash sometimes occurring during account creation. - Fix tunnel info expansion state not remembered during pause and resume. - Fix crash during some view transitions. @@ -247,21 +219,17 @@ Identical to android/2022.2-beta2 except for updated translations. ## [android/2022.2-beta1] - 2022-08-11 ### Added -#### Android - Add device management to the Android app. This simplifies knowing which device is which and adds the option to log other devices out when the account already has five devices. ### Changed -#### Android - Lowered default MTU to 1280 on Android. - Disable app icon badge for tunnel state notification/status. ### Removed -#### Android - Remove WireGuard view as it's no longer needed with the new way of managing devices. ### Fixed -#### Android - Fix unused dependencies loaded in the service/tile DI graph. - Fix missing IPC message unregistration causing multiple copies of some messages to be received. - Fix quick settings tile being unresponsive and causing crashes on some devices. @@ -270,7 +238,6 @@ Identical to android/2022.2-beta2 except for updated translations. - Fix crash when clicking in-app URL notifications. ### Security -#### Android - Prevent location request responses from being received outside the tunnel when in the connected state. @@ -281,27 +248,22 @@ Identical to android/2022.1-beta3 except for a few updated translations. ## [android/2022.1-beta3] - 2022-02-08 ### Fixed -#### Android - Fix app crash caused by quick settings tile. ## [android/2022.1-beta2] - 2022-01-27 ### Fixed -#### Android - Fix app sometimes crashing during startup on Android TVs. ## [android/2022.1-beta1] - 2022-01-26 ### Added -#### Android - Add toggle for Split tunneling view to be able to show system apps - Add support of adaptive icons (available only from Android 8). ### Changed - Gradually increase the WireGuard connectivity check timeout, lowering the timeout for the first few attempts. - -#### Android - Improve stability by running the UI and the tunnel management logic in separate processes. - Remove dialog warning that only custom local DNS servers are supported, since public custom DNS servers are now supported. @@ -317,14 +279,13 @@ Identical to android/2022.1-beta3 except for a few updated translations. - Hide the tunnel state notification from the lock screen. ### Fixed -#### Android - Fix banner sometimes incorrectly showing (e.g. "BLOCKING INTERNET"). - Fix tunnel state notification sometimes re-appearing after being dismissed. - Fix invalid URLs. Rely on browser locale rather than app/system language. - Automatically disable custom DNS when no servers have been added. - Fix issue where erasing wireguard MTU value did not clear its setting. - Fix initial state of Split tunneling excluded apps list. Previously it was not notified the daemon -properly after initialization. + properly after initialization. - Fix UI sometimes not updating correctly while no split screen or after having a dialog from another app appear on top. - Fix request to connect from notification or quick-settings tile not connecting if VPN permission @@ -350,8 +311,6 @@ that is the same as the git tag they receive: `android/`. - Add 51820 to list of WireGuard ports in app settings. - Add option to connect to WireGuard relays over IPv6. - Add Burmese translations. - -#### Android - Allow reaching the API server when connecting, disconnecting or in a blocked state. - Add FAQs & Guides menu entry to the Settings screen. - Add TV banner for better user experience and requirements. @@ -362,14 +321,10 @@ that is the same as the git tag they receive: `android/`. - Only download a new relay list if it has been modified. - Connect to the API only via TLS 1.3 - Shrink account history capactity from 3 account entries to 1. - -#### Android - WireGuard key is now rotated sooner: every four days instead of seven. ### Fixed - Improve responsiveness when reconnecting after some failed connection attempts. - -#### Android - Fix input area sometimes disappearing when returning to the Login screen. For older non-stable releases, see the main changelog ../CHANGELOG.md From c53b8eb393f240d72517869039504c7563906443 Mon Sep 17 00:00:00 2001 From: Albin Date: Tue, 9 Apr 2024 17:40:29 +0200 Subject: [PATCH 005/214] Use android changelog in release script --- prepare-release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prepare-release.sh b/prepare-release.sh index 2f0e1b7157cb..91829b142a98 100755 --- a/prepare-release.sh +++ b/prepare-release.sh @@ -56,7 +56,7 @@ if [[ "$DESKTOP" == "true" && $(grep "^## \\[$PRODUCT_VERSION\\] - " CHANGELOG.m exit 1 fi -if [[ "$ANDROID" == "true" && $(grep "^## \\[android/$PRODUCT_VERSION\\] - " CHANGELOG.md) == "" ]]; then +if [[ "$ANDROID" == "true" && $(grep "^## \\[android/$PRODUCT_VERSION\\] - " android/CHANGELOG.md) == "" ]]; then echo "It looks like you did not add $PRODUCT_VERSION to the changelog?" echo "Please make sure the changelog is up to date and correct before you proceed." exit 1 From 912e5d1e9b128c674d81f0a78c03a9114f4ba2fd Mon Sep 17 00:00:00 2001 From: Albin Date: Wed, 10 Apr 2024 09:47:52 +0200 Subject: [PATCH 006/214] Update audit changelog reference --- audits/2022-10-14-atredis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audits/2022-10-14-atredis.md b/audits/2022-10-14-atredis.md index 39a466142b5f..053e742119e9 100644 --- a/audits/2022-10-14-atredis.md +++ b/audits/2022-10-14-atredis.md @@ -10,7 +10,7 @@ For the desktop app, version [2022.4] was audited. On Android, version [2022.2-b was audited and on iOS the Test Flight version [2022.3 (build 1)] was audited. [2022.4]: ../CHANGELOG.md#20224---2022-08-19 -[2022.2-beta1]: ../CHANGELOG.md#android20222-beta1---2022-08-11 +[2022.2-beta1]: ../android/CHANGELOG.md#android20222-beta1---2022-08-11 [2022.3 (build 1)]: https://github.com/mullvad/mullvadvpn-app/commit/b05f9c588f5c88e98a9d36af84765bbd1254be43 Quoting the key conclusions of the report: From 6355b4228b8802be7b7b9c0ddf94fb7f20cb9db4 Mon Sep 17 00:00:00 2001 From: Albin Date: Wed, 10 Apr 2024 09:51:53 +0200 Subject: [PATCH 007/214] Add CI check for android and ios changelogs --- .github/workflows/check-changelog.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-changelog.yml b/.github/workflows/check-changelog.yml index 838629e685dc..8925a21074d0 100644 --- a/.github/workflows/check-changelog.yml +++ b/.github/workflows/check-changelog.yml @@ -4,15 +4,20 @@ on: pull_request: paths: - 'CHANGELOG.md' + - 'ios/CHANGELOG.md' + - 'android/CHANGELOG.md' env: LINE_LIMIT: 100 jobs: check-changelog: runs-on: ubuntu-latest + strategy: + matrix: + changelog: [CHANGELOG.md, ios/CHANGELOG.md, android/CHANGELOG.md] steps: - name: Checkout repository uses: actions/checkout@v3 - name: No lines must exceed ${{ env.LINE_LIMIT }} characters run: | awk 'length($0) > '$LINE_LIMIT' { print NR ": Line exceeds '$LINE_LIMIT' chars: " $0; found=1 } \ - END { if(found) exit 1 }' CHANGELOG.md + END { if(found) exit 1 }' ${{ matrix.changelog }} From 810ac5ef426648577a4e4fc0b5d45685b726963c Mon Sep 17 00:00:00 2001 From: mojganii Date: Wed, 10 Apr 2024 11:28:56 +0200 Subject: [PATCH 008/214] Change action sheet tint color --- ios/MullvadVPN/Coordinators/LocationCoordinator.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift index 0f808438a3ee..e146a7cd06a3 100644 --- a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift @@ -197,6 +197,9 @@ extension LocationCoordinator: LocationViewControllerDelegate { preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet ) + actionSheet.overrideUserInterfaceStyle = .dark + actionSheet.view.tintColor = UIColor(red: 0.0, green: 0.59, blue: 1.0, alpha: 1) + actionSheet.addAction(UIAlertAction( title: NSLocalizedString( "CUSTOM_LIST_ACTION_SHEET_ADD_LIST_BUTTON", @@ -236,9 +239,6 @@ extension LocationCoordinator: LocationViewControllerDelegate { handler: nil )) - actionSheet.overrideUserInterfaceStyle = .dark - actionSheet.view.tintColor = .white - presentationContext.present(actionSheet, animated: true) } } From 77f066bde558008ce86d422ffd7226e118a05171 Mon Sep 17 00:00:00 2001 From: Niklas Berglund Date: Tue, 9 Apr 2024 09:45:15 +0200 Subject: [PATCH 009/214] Fix for screenshot tests failing --- ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift b/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift index bae6c14b748b..18b53f563249 100644 --- a/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift +++ b/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift @@ -74,8 +74,8 @@ class MullvadVPNScreenshots: XCTestCase { if cityCell.exists { cityCell.tap() } else { - _ = countryCell.buttons[AccessibilityIdentifier.collapseButton.rawValue].waitForExistence(timeout: 5) - countryCell.buttons[AccessibilityIdentifier.collapseButton.rawValue].tap() + _ = countryCell.buttons[AccessibilityIdentifier.expandButton.rawValue].waitForExistence(timeout: 5) + countryCell.buttons[AccessibilityIdentifier.expandButton.rawValue].tap() cityCell.tap() } @@ -87,7 +87,7 @@ class MullvadVPNScreenshots: XCTestCase { // Re-open Select location controller (iPhone only) if case .phone = UIDevice.current.userInterfaceIdiom { app.buttons[AccessibilityIdentifier.selectLocationButton.rawValue].tap() - cityCell.buttons[AccessibilityIdentifier.collapseButton.rawValue].tap() + cityCell.buttons[AccessibilityIdentifier.expandButton.rawValue].tap() snapshot("SelectLocation") // Tap the "Filter" button and expand each relay filter From b79afa24fbfa57585aa28deb0252173b6bea22fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls?= Date: Wed, 10 Apr 2024 11:43:10 +0200 Subject: [PATCH 010/214] Add custom lists to iOS changelog --- ios/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index b0359289427c..6c4d8544a8ea 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -22,10 +22,12 @@ Line wrap the file at 100 chars. Th * **Security**: in case of vulnerabilities. ## [Unreleased] +### Added +- Add ability to create custom lists. ## [2024.2 - 2024-02-26] ### Added -- Add IP Overrides +- Add IP Overrides. ## [2024.1 - 2024-01-06] ### Added From 8beace5e3051f04021214a9fb2a5c19c8e3b0d46 Mon Sep 17 00:00:00 2001 From: mojganii Date: Wed, 10 Apr 2024 11:20:26 +0200 Subject: [PATCH 011/214] Revise navigation for empty Custom Lists --- .../CustomLists/ListCustomListCoordinator.swift | 14 ++++++++------ .../ListCustomListViewController.swift | 17 ----------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift index fbdab2fba849..2b238dd1e5de 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift @@ -62,9 +62,7 @@ class ListCustomListCoordinator: Coordinator, Presentable, Presenting { guard let self else { return } popToList() editCustomListCoordinator.removeFromParent() - self.updateRelayConstraints(for: action, in: list) - self.listViewController.updateDataSource(reloadExisting: action == .save) } coordinator.start() @@ -97,9 +95,13 @@ class ListCustomListCoordinator: Coordinator, Presentable, Presenting { } private func popToList() { - guard let listController = navigationController.viewControllers - .first(where: { $0 is ListCustomListViewController }) else { return } - - navigationController.popToViewController(listController, animated: true) + if interactor.fetchAll().isEmpty { + navigationController.dismiss(animated: true) + didFinish?(self) + } else if let listController = navigationController.viewControllers + .first(where: { $0 is ListCustomListViewController }) { + navigationController.popToViewController(listController, animated: true) + listViewController.updateDataSource(reloadExisting: true, animated: false) + } } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift index 78a14a298ca5..74b185169ca3 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift @@ -34,22 +34,6 @@ class ListCustomListViewController: UIViewController { private var dataSource: DataSource? private var fetchedItems: [CustomList] = [] private var tableView = UITableView(frame: .zero, style: .plain) - - private let emptyListLabel: UILabel = { - let textLabel = UILabel() - textLabel.font = .preferredFont(forTextStyle: .title2) - textLabel.textColor = .secondaryTextColor - textLabel.textAlignment = .center - textLabel.numberOfLines = .zero - textLabel.lineBreakStrategy = [] - textLabel.text = NSLocalizedString( - "CustomList", - value: "No custom list to display", - comment: "" - ) - return textLabel - }() - var didSelectItem: ((CustomList) -> Void)? var didFinish: (() -> Void)? @@ -75,7 +59,6 @@ class ListCustomListViewController: UIViewController { func updateDataSource(reloadExisting: Bool, animated: Bool = true) { fetchedItems = interactor.fetchAll() - tableView.backgroundView = fetchedItems.isEmpty ? emptyListLabel : nil var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.default]) From 127b3369d5277883c850a3d5f6d2a619b9e3de5f Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Wed, 20 Mar 2024 08:56:34 +0100 Subject: [PATCH 012/214] Move the title 'Server IP Override' up --- ios/MullvadVPN.xcodeproj/project.pbxproj | 4 + .../UINavigationBar+Appearance.swift | 2 +- .../Cells/ListCellContentConfiguration.swift | 7 +- .../Cells/TextCellContentConfiguration.swift | 2 +- .../List/ListAccessMethodCoordinator.swift | 5 + .../IPOverride/IPOverrideCoordinator.swift | 49 ++++++++ .../IPOverride/IPOverrideHeaderView.swift | 115 ++++++++++++++++++ .../IPOverride/IPOverrideViewController.swift | 82 ++----------- .../IPOverrideViewControllerDelegate.swift | 1 + .../UI appearance/UIColor+Palette.swift | 2 +- .../VPNSettings/VPNSettingsDataSource.swift | 10 +- 11 files changed, 199 insertions(+), 80 deletions(-) create mode 100644 ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index fcd39ee55e3b..7879654647b1 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -482,6 +482,7 @@ 7A1A26492A29D48A00B978AA /* RelayFilterCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26482A29D48A00B978AA /* RelayFilterCellFactory.swift */; }; 7A21DACF2A30AA3700A787A9 /* UITextField+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */; }; 7A28826A2BA8336600FD9F20 /* VPNSettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */; }; + 7A28826D2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */; }; 7A2960F62A963F7500389B82 /* AlertCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */; }; 7A2960FD2A964BB700389B82 /* AlertPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */; }; 7A307AD92A8CD8DA0017618B /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A307AD82A8CD8DA0017618B /* Duration.swift */; }; @@ -1750,6 +1751,7 @@ 7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableSettingsCell.swift; sourceTree = ""; }; 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Appearance.swift"; sourceTree = ""; }; 7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNSettingsCoordinator.swift; sourceTree = ""; }; + 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideHeaderView.swift; sourceTree = ""; }; 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertCoordinator.swift; sourceTree = ""; }; 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresentation.swift; sourceTree = ""; }; 7A307AD82A8CD8DA0017618B /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = ""; }; @@ -3502,6 +3504,7 @@ isa = PBXGroup; children = ( 7A5869AA2B55527C00640D27 /* IPOverrideCoordinator.swift */, + 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */, 7AB4CCBA2B691BBB006037F5 /* IPOverrideInteractor.swift */, 7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */, 7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */, @@ -5333,6 +5336,7 @@ F0C6FA852A6A733700F521F0 /* InAppPurchaseInteractor.swift in Sources */, 58CEB2F92AFD136E00E6E088 /* UIBackgroundConfiguration+Extensions.swift in Sources */, 5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */, + 7A28826D2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift in Sources */, A98502032B627B120061901E /* LocalNetworkProbe.swift in Sources */, 583FE01029C0F532006E85F9 /* CustomSplitViewController.swift in Sources */, 7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */, diff --git a/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift b/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift index b7011599478d..68d503a0766d 100644 --- a/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift +++ b/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift @@ -32,7 +32,7 @@ extension UINavigationBar { private func makeNavigationBarAppearance(isTransparent: Bool) -> UINavigationBarAppearance { let backIndicatorImage = UIImage(named: "IconBack")?.withTintColor( - UIColor.NavigationBar.backButtonIndicatorColor, + UIColor.NavigationBar.buttonColor, renderingMode: .alwaysOriginal ) let backIndicatorTransitionMask = UIImage(named: "IconBackTransitionMask") diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift index 891927c51086..7055407625a6 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift @@ -38,7 +38,12 @@ struct ListCellContentConfiguration: UIContentConfiguration, Equatable { let tertiaryTextProperties = TertiaryTextProperties() /// Content view layout margins. - var directionalLayoutMargins: NSDirectionalEdgeInsets = UIMetrics.SettingsCell.apiAccessInsetLayoutMargins + var directionalLayoutMargins = NSDirectionalEdgeInsets( + top: 8, + leading: 24, + bottom: 8, + trailing: 24 + ) func makeContentView() -> UIView & UIContentView { return ListCellContentView(configuration: self) diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift index 2eddbf041069..0f652febe7c4 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift @@ -28,7 +28,7 @@ struct TextCellContentConfiguration: UIContentConfiguration, Equatable { /// The editing events configuration. var editingEvents = EditingEvents() - /// The text properties confgiuration. + /// The text properties configuration. var textProperties = TextProperties() /// The text field properties configuration. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift index 748b3286aaee..f5383c6d19ee 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift @@ -76,17 +76,20 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin private func about() { let header = NSLocalizedString( "ABOUT_API_ACCESS_HEADER", + tableName: "APIAccess", value: "API access", comment: "" ) let preamble = NSLocalizedString( "ABOUT_API_ACCESS_PREAMBLE", + tableName: "APIAccess", value: "Manage default and setup custom methods to access the Mullvad API.", comment: "" ) let body = [ NSLocalizedString( "ABOUT_API_ACCESS_BODY_1", + tableName: "APIAccess", value: """ The app needs to communicate with a Mullvad API server to log you in, fetch server lists, \ and other critical operations. @@ -95,6 +98,7 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin ), NSLocalizedString( "ABOUT_API_ACCESS_BODY_2", + tableName: "APIAccess", value: """ On some networks, where various types of censorship are being used, the API servers might \ not be directly reachable. @@ -103,6 +107,7 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin ), NSLocalizedString( "ABOUT_API_ACCESS_BODY_3", + tableName: "APIAccess", value: """ This feature allows you to circumvent that censorship by adding custom ways to access the \ API via proxies and similar methods. diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift index e4d6f504d25e..b2f9fe51645f 100644 --- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift @@ -47,4 +47,53 @@ extension IPOverrideCoordinator: IPOverrideViewControllerDelegate { presentationContext.present(customNavigationController, animated: true) } + + func presentAbout() { + let header = NSLocalizedString( + "IP_OVERRIDE_HEADER", + tableName: "IPOverride", + value: "IP Override", + comment: "" + ) + let body = [ + NSLocalizedString( + "IP_OVERRIDE_BODY_1", + tableName: "IPOverride", + value: """ + On some networks, where various types of censorship are being used, our server IP addresses are \ + sometimes blocked. + """, + comment: "" + ), + NSLocalizedString( + "IP_OVERRIDE_BODY_2", + tableName: "IPOverride", + value: """ + To circumvent this you can import a file or a text, provided by our support team, \ + with new IP addresses that override the default addresses of the servers in the Select location view. + """, + comment: "" + ), + NSLocalizedString( + "IP_OVERRIDE_BODY_3", + tableName: "IPOverride", + value: """ + If you are having issues connecting to VPN servers, please contact support. + """, + comment: "" + ), + ] + + let aboutController = AboutViewController(header: header, preamble: nil, body: body) + let aboutNavController = UINavigationController(rootViewController: aboutController) + + aboutController.navigationItem.rightBarButtonItem = UIBarButtonItem( + systemItem: .done, + primaryAction: UIAction { [weak aboutNavController] _ in + aboutNavController?.dismiss(animated: true) + } + ) + + navigationController.present(aboutNavController, animated: true) + } } diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift new file mode 100644 index 000000000000..16c751f121d1 --- /dev/null +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift @@ -0,0 +1,115 @@ +// +// IPOverrideHeaderView.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-03-20. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +/// Header view pinned at the top of ``IPOverrideViewController``. +class IPOverrideHeaderView: UIView, UITextViewDelegate { + /// Event handler invoked when user taps on the link to learn more about API access. + var onAbout: (() -> Void)? + + private let textView = UITextView() + + override init(frame: CGRect) { + super.init(frame: frame) + + textView.backgroundColor = .clear + textView.dataDetectorTypes = .link + textView.isSelectable = true + textView.isEditable = false + textView.isScrollEnabled = false + textView.contentInset = .zero + textView.textContainerInset = .zero + textView.attributedText = makeAttributedString() + textView.linkTextAttributes = defaultLinkAttributes + textView.textContainer.lineFragmentPadding = 0 + textView.delegate = self + + directionalLayoutMargins = .zero + + addSubviews() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private let defaultTextAttributes: [NSAttributedString.Key: Any] = [ + .font: UIFont.systemFont(ofSize: 13), + .foregroundColor: UIColor.ContentHeading.textColor, + ] + + private let defaultLinkAttributes: [NSAttributedString.Key: Any] = [ + .font: UIFont.systemFont(ofSize: 13), + .foregroundColor: UIColor.ContentHeading.linkColor, + ] + + private func makeAttributedString() -> NSAttributedString { + let body = NSLocalizedString( + "IP_OVERRIDE_HEADER_BODY", + tableName: "IPOverride", + value: "Import files or text with new IP addresses for the servers in the Select location view.", + comment: "" + ) + let link = NSLocalizedString( + "IP_OVERRIDE_HEADER_LINK", + tableName: "IPOverride", + value: "About IP override...", + comment: "" + ) + + var linkAttributes = defaultLinkAttributes + linkAttributes[.link] = "#" + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineBreakMode = .byWordWrapping + + let attributedString = NSMutableAttributedString() + attributedString.append(NSAttributedString(string: body, attributes: defaultTextAttributes)) + attributedString.append(NSAttributedString(string: " ", attributes: defaultTextAttributes)) + attributedString.append(NSAttributedString(string: link, attributes: linkAttributes)) + attributedString.addAttribute( + .paragraphStyle, + value: paragraphStyle, + range: NSRange(location: 0, length: attributedString.length) + ) + return attributedString + } + + private func addSubviews() { + addConstrainedSubviews([textView]) { + textView.pinEdgesToSuperviewMargins() + } + } + + func textView( + _ textView: UITextView, + shouldInteractWith URL: URL, + in characterRange: NSRange, + interaction: UITextItemInteraction + ) -> Bool { + onAbout?() + return false + } + + @available(iOS 17.0, *) + func textView(_ textView: UITextView, menuConfigurationFor textItem: UITextItem, defaultMenu: UIMenu) -> UITextItem + .MenuConfiguration? { + return nil + } + + @available(iOS 17.0, *) + func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? { + if case .link = textItem.content { + return UIAction { [weak self] _ in + self?.onAbout?() + } + } + return nil + } +} diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift index 1088a86a2de5..be9116823d27 100644 --- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift @@ -13,6 +13,7 @@ class IPOverrideViewController: UIViewController { private let interactor: IPOverrideInteractor private var cancellables = Set() private let alertPresenter: AlertPresenter + private let headerView = IPOverrideHeaderView() weak var delegate: IPOverrideViewControllerDelegate? @@ -51,11 +52,11 @@ class IPOverrideViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - navigationController?.navigationItem.largeTitleDisplayMode = .never view.backgroundColor = .secondaryColor + view.directionalLayoutMargins = UIMetrics.contentHeadingLayoutMargins - addHeader() - addPreamble() + configureNavigation() + addHeaderView() addImportButtons() addStatusLabel() @@ -70,44 +71,21 @@ class IPOverrideViewController: UIViewController { }.store(in: &cancellables) } - private func addHeader() { - let label = UILabel() - label.font = .systemFont(ofSize: 32, weight: .bold) - label.textColor = .white - label.text = NSLocalizedString( + private func configureNavigation() { + title = NSLocalizedString( "IP_OVERRIDE_HEADER", tableName: "IPOverride", value: "Server IP override", comment: "" ) - - let infoButton = UIButton(type: .custom) - infoButton.tintColor = .white - infoButton.setImage(UIImage(resource: .iconInfo), for: .normal) - infoButton.addTarget(self, action: #selector(didTapInfoButton), for: .touchUpInside) - infoButton.heightAnchor.constraint(equalToConstant: 24).isActive = true - infoButton.widthAnchor.constraint(equalTo: infoButton.heightAnchor, multiplier: 1).isActive = true - - let headerView = UIStackView(arrangedSubviews: [label, infoButton, UIView()]) - headerView.spacing = 8 - - containerView.addArrangedSubview(headerView) - containerView.setCustomSpacing(14, after: headerView) } - private func addPreamble() { - let label = UILabel() - label.font = .systemFont(ofSize: 12, weight: .semibold) - label.textColor = .white.withAlphaComponent(0.6) - label.numberOfLines = 0 - label.text = NSLocalizedString( - "IP_OVERRIDE_PREAMBLE", - tableName: "IPOverride", - value: "Import files or text with new IP addresses for the servers in the Select location view.", - comment: "" - ) + private func addHeaderView() { + headerView.onAbout = { [weak self] in + self?.delegate?.presentAbout() + } - containerView.addArrangedSubview(label) + containerView.addArrangedSubview(headerView) } private func addImportButtons() { @@ -140,44 +118,6 @@ class IPOverrideViewController: UIViewController { containerView.addArrangedSubview(statusView) } - @objc private func didTapInfoButton() { - let message = NSLocalizedString( - "IP_OVERRIDE_DIALOG_MESSAGE", - tableName: "IPOverride", - value: """ - On some networks, where various types of censorship are being used, our server IP addresses are \ - sometimes blocked. - To circumvent this you can import a file or a text, provided by our support team, \ - with new IP addresses that override the default addresses of the servers in the Select location view. - If you are having issues connecting to VPN servers, please contact support. - """, - comment: "" - ) - - let presentation = AlertPresentation( - id: "ip-override-info-alert", - icon: .info, - title: NSLocalizedString( - "IP_OVERRIDE_INFO_DIALOG_TITLE", - tableName: "IPOverride", - value: "Server IP override", - comment: "" - ), - message: message, - buttons: [AlertAction( - title: NSLocalizedString( - "IP_OVERRIDE_INFO_DIALOG_OK_BUTTON", - tableName: "IPOverride", - value: "Got it!", - comment: "" - ), - style: .default - )] - ) - - alertPresenter.showAlert(presentation: presentation, animated: true) - } - @objc private func didTapClearButton() { let presentation = AlertPresentation( id: "ip-override-clear-alert", diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift index d71de543c4bf..5f24c056c409 100644 --- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift @@ -10,4 +10,5 @@ import Foundation protocol IPOverrideViewControllerDelegate: AnyObject { func presentImportTextController() + func presentAbout() } diff --git a/ios/MullvadVPN/UI appearance/UIColor+Palette.swift b/ios/MullvadVPN/UI appearance/UIColor+Palette.swift index 63495b44a874..5cc9a562a111 100644 --- a/ios/MullvadVPN/UI appearance/UIColor+Palette.swift +++ b/ios/MullvadVPN/UI appearance/UIColor+Palette.swift @@ -72,7 +72,7 @@ extension UIColor { // Navigation bars enum NavigationBar { - static let backButtonIndicatorColor = UIColor(white: 1.0, alpha: 0.8) + static let buttonColor = UIColor(white: 1.0, alpha: 0.8) static let backButtonTitleColor = UIColor.white static let titleColor = UIColor.white static let promptColor = UIColor.white diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift index a3f5b16747e6..ed4f2727b3d0 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift @@ -377,11 +377,11 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { let sectionIdentifier = snapshot().sectionIdentifiers[section] - switch sectionIdentifier { - case .ipOverrides: - return 10 - default: - return 0.5 + return switch sectionIdentifier { + // 0 due to there already being a separator between .dnsSettings and .ipOverrides. + case .dnsSettings: 0 + case .ipOverrides: 10 + default: 0.5 } } From 81c966ce5d9f2e6c57156a76c910fc2cd2e21c34 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Mon, 8 Apr 2024 16:11:56 +0200 Subject: [PATCH 013/214] Fix wireguard rotation test The test was flaky because if a race condition which made the key rotation missable. --- mullvad-management-interface/src/client.rs | 4 +- test/test-manager/src/tests/account.rs | 59 ++++++++++++++-------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 847150c47731..fd6c56e19fb2 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -16,8 +16,7 @@ use mullvad_types::{ version::AppVersionInfo, wireguard::{PublicKey, QuantumResistantState, RotationInterval}, }; -use std::path::Path; -use std::str::FromStr; +use std::{path::Path, str::FromStr}; #[cfg(target_os = "windows")] use talpid_types::split_tunnel::ExcludedProcess; use tonic::{Code, Status}; @@ -29,6 +28,7 @@ pub type Result = std::result::Result; #[derive(Debug, Clone)] pub struct MullvadProxyClient(crate::ManagementServiceClient); +#[derive(Debug)] pub enum DaemonEvent { TunnelState(TunnelState), Settings(Settings), diff --git a/test/test-manager/src/tests/account.rs b/test/test-manager/src/tests/account.rs index 94374dba2020..69f605a29094 100644 --- a/test/test-manager/src/tests/account.rs +++ b/test/test-manager/src/tests/account.rs @@ -288,50 +288,69 @@ pub async fn test_automatic_wireguard_rotation( .device .pubkey; - // Stop daemon + log::info!("Old wireguard key: {old_key}"); + + log::info!("Stopping daemon"); rpc.stop_mullvad_daemon() .await .expect("Could not stop system service"); - // Open device.json and change created field to more than 7 days ago + log::info!("Changing created field of `device.json` to more than 7 days ago"); rpc.make_device_json_old() .await .expect("Could not change device.json to have an old created timestamp"); - // Start daemon + log::info!("Starting daemon"); rpc.start_mullvad_daemon() .await .expect("Could not start system service"); // NOTE: Need to create a new `mullvad_client` here after the restart otherwise we can't // communicate with the daemon + log::info!("Reconnecting to daemon"); drop(mullvad_client); let mut mullvad_client = ctx.rpc_provider.new_client().await; - // Verify rotation has happened after a minute - const KEY_ROTATION_TIMEOUT: Duration = Duration::from_secs(100); + log::info!("Verifying that wireguard key has change"); + + // Check if the key rotation has already occurred when connected to the daemon, otherwise + // listen for device daemon events until we observe the change. We have to register the event + // listener before polling the current key to be sure we don't miss the change. + let event_listener = mullvad_client.events_listen().await.unwrap(); + let new_key = mullvad_client + .get_device() + .await + .unwrap() + .into_device() + .expect("Could not get device") + .device + .pubkey; - let new_key = tokio::time::timeout( - KEY_ROTATION_TIMEOUT, - helpers::find_daemon_event( - mullvad_client.events_listen().await.unwrap(), - |daemon_event| match daemon_event { + // If key has not yet been updated, listen for changes to it + if new_key == old_key { + log::info!("Listening for device daemon event"); + // Verify rotation has happened within 100 seconds - if the key hasn't been rotated after + // that, the rotation probably won't happen anytime soon. + let device_event = tokio::task::spawn(tokio::time::timeout( + Duration::from_secs(100), + helpers::find_daemon_event(event_listener, |daemon_event| match daemon_event { DaemonEvent::Device(device_event) => Some(device_event), _ => None, - }, - ), - ) - .await - .map_err(|_error| Error::Daemon(String::from("Tunnel event listener timed out")))? - .map(|device_event| { - device_event + }), + )) + .await + .unwrap() + .map_err(|_error| Error::Daemon(String::from("Tunnel event listener timed out")))??; + + let new_key = device_event .new_state .into_device() .expect("Could not get device") .device - .pubkey - })?; + .pubkey; + + assert_ne!(old_key, new_key); + } - assert_ne!(old_key, new_key); Ok(()) } From 92f7b5bffca9bd1e16da162c35611d6e936f28d6 Mon Sep 17 00:00:00 2001 From: Albin Date: Thu, 11 Apr 2024 10:32:11 +0200 Subject: [PATCH 014/214] Rename F-Droid screenshots This is done to address F-Droid not purging old screenshots: https://gitlab.com/fdroid/fdroidserver/-/issues/490 --- .../phone-screenshots/{01_connected.png => 01.png} | Bin .../{02_free_the_internet.png => 02.png} | Bin .../phone-screenshots/{03_account.png => 03.png} | Bin .../phone-screenshots/{04_locations.png => 04.png} | Bin .../phone-screenshots/{05_settings.png => 05.png} | Bin .../{06_split tunneling.png => 06.png} | Bin .../phone-screenshots/{07_audits.png => 07.png} | Bin 7 files changed, 0 insertions(+), 0 deletions(-) rename android/src/main/play/listings/en-US/graphics/phone-screenshots/{01_connected.png => 01.png} (100%) rename android/src/main/play/listings/en-US/graphics/phone-screenshots/{02_free_the_internet.png => 02.png} (100%) rename android/src/main/play/listings/en-US/graphics/phone-screenshots/{03_account.png => 03.png} (100%) rename android/src/main/play/listings/en-US/graphics/phone-screenshots/{04_locations.png => 04.png} (100%) rename android/src/main/play/listings/en-US/graphics/phone-screenshots/{05_settings.png => 05.png} (100%) rename android/src/main/play/listings/en-US/graphics/phone-screenshots/{06_split tunneling.png => 06.png} (100%) rename android/src/main/play/listings/en-US/graphics/phone-screenshots/{07_audits.png => 07.png} (100%) diff --git a/android/src/main/play/listings/en-US/graphics/phone-screenshots/01_connected.png b/android/src/main/play/listings/en-US/graphics/phone-screenshots/01.png similarity index 100% rename from android/src/main/play/listings/en-US/graphics/phone-screenshots/01_connected.png rename to android/src/main/play/listings/en-US/graphics/phone-screenshots/01.png diff --git a/android/src/main/play/listings/en-US/graphics/phone-screenshots/02_free_the_internet.png b/android/src/main/play/listings/en-US/graphics/phone-screenshots/02.png similarity index 100% rename from android/src/main/play/listings/en-US/graphics/phone-screenshots/02_free_the_internet.png rename to android/src/main/play/listings/en-US/graphics/phone-screenshots/02.png diff --git a/android/src/main/play/listings/en-US/graphics/phone-screenshots/03_account.png b/android/src/main/play/listings/en-US/graphics/phone-screenshots/03.png similarity index 100% rename from android/src/main/play/listings/en-US/graphics/phone-screenshots/03_account.png rename to android/src/main/play/listings/en-US/graphics/phone-screenshots/03.png diff --git a/android/src/main/play/listings/en-US/graphics/phone-screenshots/04_locations.png b/android/src/main/play/listings/en-US/graphics/phone-screenshots/04.png similarity index 100% rename from android/src/main/play/listings/en-US/graphics/phone-screenshots/04_locations.png rename to android/src/main/play/listings/en-US/graphics/phone-screenshots/04.png diff --git a/android/src/main/play/listings/en-US/graphics/phone-screenshots/05_settings.png b/android/src/main/play/listings/en-US/graphics/phone-screenshots/05.png similarity index 100% rename from android/src/main/play/listings/en-US/graphics/phone-screenshots/05_settings.png rename to android/src/main/play/listings/en-US/graphics/phone-screenshots/05.png diff --git a/android/src/main/play/listings/en-US/graphics/phone-screenshots/06_split tunneling.png b/android/src/main/play/listings/en-US/graphics/phone-screenshots/06.png similarity index 100% rename from android/src/main/play/listings/en-US/graphics/phone-screenshots/06_split tunneling.png rename to android/src/main/play/listings/en-US/graphics/phone-screenshots/06.png diff --git a/android/src/main/play/listings/en-US/graphics/phone-screenshots/07_audits.png b/android/src/main/play/listings/en-US/graphics/phone-screenshots/07.png similarity index 100% rename from android/src/main/play/listings/en-US/graphics/phone-screenshots/07_audits.png rename to android/src/main/play/listings/en-US/graphics/phone-screenshots/07.png From ab25e130af693d7a27fcff061fdaa3cfdbc4541a Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 08:03:54 +0200 Subject: [PATCH 015/214] Only reset bridge location when normal is selected --- gui/src/main/settings.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/gui/src/main/settings.ts b/gui/src/main/settings.ts index 16996eebd9b4..8bf60d9108e2 100644 --- a/gui/src/main/settings.ts +++ b/gui/src/main/settings.ts @@ -1,6 +1,5 @@ import fs from 'fs/promises'; -import BridgeSettingsBuilder from '../shared/bridge-settings-builder'; import { ISettings } from '../shared/daemon-rpc-types'; import { ICurrentAppVersionInfo } from '../shared/ipc-types'; import log from '../shared/logging'; @@ -44,9 +43,15 @@ export default class Settings implements Readonly { IpcMainEventChannel.settings.handleSetBridgeState(async (bridgeState) => { await this.daemonRpc.setBridgeState(bridgeState); - // Reset bridge constraints to `any` when the state is set to auto or off - if (bridgeState === 'auto' || bridgeState === 'off') { - await this.daemonRpc.setBridgeSettings(new BridgeSettingsBuilder().location.any().build()); + // Reset bridge constraints to `any` when the state is set to auto or off if not custom + if ( + (bridgeState === 'auto' || bridgeState === 'off') && + this.bridgeSettings.type === 'normal' + ) { + await this.daemonRpc.setBridgeSettings({ + ...this.bridgeSettings, + normal: { ...this.bridgeSettings.normal, location: 'any' }, + }); } }); IpcMainEventChannel.settings.handleSetOpenVpnMssfix((mssfix?: number) => From cc37b2033af9e4e1665fabf195ca345a6e7061a7 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 08:10:06 +0200 Subject: [PATCH 016/214] Add provider and ownership to bridgesettings --- gui/src/renderer/app.tsx | 2 ++ gui/src/renderer/redux/settings/reducers.ts | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index 5e2c574d792c..a8c1901ae85a 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -657,6 +657,8 @@ export default class AppRenderer { type: bridgeSettings.type, normal: { location: liftConstraint(bridgeSettings.normal.location), + providers: bridgeSettings.normal.providers, + ownership: bridgeSettings.normal.ownership, }, custom: bridgeSettings.custom, }); diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts index 07502ab280fb..210ef380105e 100644 --- a/gui/src/renderer/redux/settings/reducers.ts +++ b/gui/src/renderer/redux/settings/reducers.ts @@ -42,6 +42,9 @@ export type NormalRelaySettingsRedux = { export type NormalBridgeSettingsRedux = { location: LiftedConstraint; + // Providers and ownership are used to filter bridges and as bridge constraints for the daemon. + providers: string[]; + ownership: Ownership; }; export type RelaySettingsRedux = @@ -150,6 +153,8 @@ const initialState: ISettingsReduxState = { type: 'normal', normal: { location: 'any', + providers: [], + ownership: Ownership.any, }, custom: undefined, }, From 0f2f673890121a8aada58580dda89c222ef1de36 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 08:12:00 +0200 Subject: [PATCH 017/214] Add possibility to left-align SmallButtons --- gui/src/renderer/components/SmallButton.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gui/src/renderer/components/SmallButton.tsx b/gui/src/renderer/components/SmallButton.tsx index 181a557fd29f..ba82f15cd4a6 100644 --- a/gui/src/renderer/components/SmallButton.tsx +++ b/gui/src/renderer/components/SmallButton.tsx @@ -37,6 +37,11 @@ const StyledSmallButton = styled.button<{ $color?: SmallButtonColor; disabled?: borderRadius: '4px', marginLeft: '12px', + [`${SmallButtonGroupStart} &&`]: { + marginLeft: 0, + marginRight: '12px', + }, + [`${StyledSmallButtonGrid} &&`]: { marginLeft: 0, }, @@ -67,6 +72,12 @@ export const SmallButtonGroup = styled.div<{ $noMarginTop?: boolean }>((props) = marginTop: props.$noMarginTop ? 0 : '30px', })); +export const SmallButtonGroupStart = styled(SmallButtonGroup)({ + flex: 1, + justifyContent: 'start', + margin: 0, +}); + const StyledSmallButtonGrid = styled.div<{ $columns: number }>((props) => ({ display: 'grid', gridTemplateColumns: `repeat(${props.$columns}, 1fr)`, From d8ead1aa606fe9fec9cc0875dc86dd2829324971 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 08:12:34 +0200 Subject: [PATCH 018/214] Add support for info dialog titles for Selectors --- gui/src/renderer/components/cell/Selector.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx index debcdbef3860..e527017435d3 100644 --- a/gui/src/renderer/components/cell/Selector.tsx +++ b/gui/src/renderer/components/cell/Selector.tsx @@ -25,6 +25,7 @@ interface CommonSelectorProps { value: T | U; selectedCellRef?: React.Ref; className?: string; + infoTitle?: string; details?: React.ReactElement; expandable?: { expandable: boolean; id: string }; disabled?: boolean; @@ -82,7 +83,7 @@ export default function Selector(props: SelectorProps) { {props.details && ( - {props.details} + {props.details} )} From abd2912d013df44c6aea983205c4bc173f6f748b Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 08:18:49 +0200 Subject: [PATCH 019/214] Move location type state to redux --- .../select-location/SelectLocationContainer.tsx | 14 +++++++++----- gui/src/renderer/redux/userinterface/actions.ts | 17 ++++++++++++++++- .../renderer/redux/userinterface/reducers.ts | 9 +++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/gui/src/renderer/components/select-location/SelectLocationContainer.tsx b/gui/src/renderer/components/select-location/SelectLocationContainer.tsx index 5843d5e6f498..1d0e5251f7aa 100644 --- a/gui/src/renderer/components/select-location/SelectLocationContainer.tsx +++ b/gui/src/renderer/components/select-location/SelectLocationContainer.tsx @@ -1,5 +1,8 @@ import React, { useContext, useMemo, useState } from 'react'; +import useActions from '../../lib/actionsHook'; +import { useSelector } from '../../redux/store'; +import userInterface from '../../redux/userinterface/actions'; import { RelayListContextProvider } from './RelayListContext'; import { ScrollPositionContextProvider } from './ScrollPositionContext'; import { LocationType } from './select-location-types'; @@ -20,13 +23,14 @@ export function useSelectLocationContext() { } export default function SelectLocationContainer() { - const [locationType, setLocationType] = useState(LocationType.exit); + const locationType = useSelector((state) => state.userInterface.selectLocationView); + const { setSelectLocationView } = useActions(userInterface); const [searchTerm, setSearchTerm] = useState(''); - const value = useMemo(() => ({ locationType, setLocationType, searchTerm, setSearchTerm }), [ - locationType, - searchTerm, - ]); + const value = useMemo( + () => ({ locationType, setLocationType: setSelectLocationView, searchTerm, setSearchTerm }), + [locationType, searchTerm], + ); return ( diff --git a/gui/src/renderer/redux/userinterface/actions.ts b/gui/src/renderer/redux/userinterface/actions.ts index b4b588537012..cc439909801e 100644 --- a/gui/src/renderer/redux/userinterface/actions.ts +++ b/gui/src/renderer/redux/userinterface/actions.ts @@ -1,5 +1,6 @@ import { MacOsScrollbarVisibility } from '../../../shared/ipc-schema'; import { IChangelog } from '../../../shared/ipc-types'; +import { LocationType } from '../../components/select-location/select-location-types'; export interface IUpdateLocaleAction { type: 'UPDATE_LOCALE'; @@ -50,6 +51,11 @@ export interface ISetIsPerformingPostUpgrade { isPerformingPostUpgrade: boolean; } +export interface ISetSelectLocationView { + type: 'SET_SELECT_LOCATION_VIEW'; + selectLocationView: LocationType; +} + export type UserInterfaceAction = | IUpdateLocaleAction | IUpdateWindowArrowPositionAction @@ -60,7 +66,8 @@ export type UserInterfaceAction = | ISetDaemonAllowed | ISetChangelog | ISetForceShowChanges - | ISetIsPerformingPostUpgrade; + | ISetIsPerformingPostUpgrade + | ISetSelectLocationView; function updateLocale(locale: string): IUpdateLocaleAction { return { @@ -133,6 +140,13 @@ function setIsPerformingPostUpgrade(isPerformingPostUpgrade: boolean): ISetIsPer }; } +function setSelectLocationView(selectLocationView: LocationType): ISetSelectLocationView { + return { + type: 'SET_SELECT_LOCATION_VIEW', + selectLocationView, + }; +} + export default { updateLocale, updateWindowArrowPosition, @@ -144,4 +158,5 @@ export default { setChangelog, setForceShowChanges, setIsPerformingPostUpgrade, + setSelectLocationView, }; diff --git a/gui/src/renderer/redux/userinterface/reducers.ts b/gui/src/renderer/redux/userinterface/reducers.ts index f9ecc6fdadcb..622b7814aadf 100644 --- a/gui/src/renderer/redux/userinterface/reducers.ts +++ b/gui/src/renderer/redux/userinterface/reducers.ts @@ -1,5 +1,6 @@ import { MacOsScrollbarVisibility } from '../../../shared/ipc-schema'; import { IChangelog } from '../../../shared/ipc-types'; +import { LocationType } from '../../components/select-location/select-location-types'; import { ReduxAction } from '../store'; export interface IUserInterfaceReduxState { @@ -13,6 +14,7 @@ export interface IUserInterfaceReduxState { changelog: IChangelog; forceShowChanges: boolean; isPerformingPostUpgrade: boolean; + selectLocationView: LocationType; } const initialState: IUserInterfaceReduxState = { @@ -25,6 +27,7 @@ const initialState: IUserInterfaceReduxState = { changelog: [], forceShowChanges: false, isPerformingPostUpgrade: false, + selectLocationView: LocationType.exit, }; export default function ( @@ -71,6 +74,12 @@ export default function ( isPerformingPostUpgrade: action.isPerformingPostUpgrade, }; + case 'SET_SELECT_LOCATION_VIEW': + return { + ...state, + selectLocationView: action.selectLocationView, + }; + default: return state; } From d5dc077984ecf64f9fe7ab31cd32d2a3d881aeea Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 08:22:36 +0200 Subject: [PATCH 020/214] Move Location row button styles to separate file --- .../select-location/CustomLists.tsx | 2 +- .../select-location/LocationRow.tsx | 135 ++-------------- .../select-location/LocationRowStyles.tsx | 150 ++++++++++++++++++ 3 files changed, 160 insertions(+), 127 deletions(-) create mode 100644 gui/src/renderer/components/select-location/LocationRowStyles.tsx diff --git a/gui/src/renderer/components/select-location/CustomLists.tsx b/gui/src/renderer/components/select-location/CustomLists.tsx index 737d66bf89cd..ca4638360c29 100644 --- a/gui/src/renderer/components/select-location/CustomLists.tsx +++ b/gui/src/renderer/components/select-location/CustomLists.tsx @@ -12,7 +12,7 @@ import * as Cell from '../cell'; import { measurements } from '../common-styles'; import { BackAction } from '../KeyboardNavigation'; import SimpleInput from '../SimpleInput'; -import { StyledLocationRowIcon } from './LocationRow'; +import { StyledLocationRowIcon } from './LocationRowStyles'; import { useRelayListContext } from './RelayListContext'; import RelayLocationList from './RelayLocationList'; import { useScrollPositionContext } from './ScrollPositionContext'; diff --git a/gui/src/renderer/components/select-location/LocationRow.tsx b/gui/src/renderer/components/select-location/LocationRow.tsx index 79b44d372a66..f76b5d3f612a 100644 --- a/gui/src/renderer/components/select-location/LocationRow.tsx +++ b/gui/src/renderer/components/select-location/LocationRow.tsx @@ -1,8 +1,6 @@ import React, { useCallback, useRef } from 'react'; import { sprintf } from 'sprintf-js'; -import styled from 'styled-components'; -import { colors } from '../../../config.json'; import { compareRelayLocation, compareRelayLocationGeographical, @@ -16,10 +14,17 @@ import { useSelector } from '../../redux/store'; import Accordion from '../Accordion'; import * as Cell from '../cell'; import ChevronButton from '../ChevronButton'; -import { measurements, normalText } from '../common-styles'; -import ImageView from '../ImageView'; import RelayStatusIndicator from '../RelayStatusIndicator'; import { AddToListDialog, DeleteConfirmDialog, EditListDialog } from './CustomListDialogs'; +import { + getButtonColor, + StyledHoverIcon, + StyledHoverIconButton, + StyledLocationRowButton, + StyledLocationRowContainer, + StyledLocationRowIcon, + StyledLocationRowLabel, +} from './LocationRowStyles'; import { CitySpecification, CountrySpecification, @@ -28,110 +33,6 @@ import { RelaySpecification, } from './select-location-types'; -interface IButtonColorProps { - $backgroundColor: string; - $backgroundColorHover: string; -} - -const buttonColor = (props: IButtonColorProps) => { - return { - backgroundColor: props.$backgroundColor, - '&&:not(:disabled):hover': { - backgroundColor: props.$backgroundColorHover, - }, - }; -}; - -export const StyledLocationRowContainer = styled(Cell.Container)({ - display: 'flex', - padding: 0, - background: 'none', -}); - -export const StyledLocationRowButton = styled(Cell.Row)( - buttonColor, - (props) => { - const paddingLeft = (props.$level + 1) * 16 + 2; - - return { - display: 'flex', - flex: 1, - overflow: 'hidden', - border: 'none', - padding: `0 10px 0 ${paddingLeft}px`, - margin: 0, - }; - }, -); - -export const StyledLocationRowIcon = styled.button(buttonColor, { - position: 'relative', - alignSelf: 'stretch', - paddingLeft: measurements.viewMargin, - paddingRight: measurements.viewMargin, - - '&&::before': { - content: '""', - position: 'absolute', - margin: 'auto', - top: 0, - left: 0, - bottom: 0, - height: '50%', - width: '1px', - backgroundColor: colors.darkBlue, - }, -}); - -export const StyledLocationRowLabel = styled(Cell.Label)(normalText, { - flex: 1, - minWidth: 0, - fontWeight: 400, - lineHeight: '24px', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', -}); - -const StyledHoverIconButton = styled.button( - buttonColor, - (props) => ({ - flex: 0, - display: 'none', - padding: '0 10px', - paddingRight: props.$isLast ? '17px' : '10px', - margin: 0, - border: 0, - height: measurements.rowMinHeight, - appearance: 'none', - - '&&:last-child': { - paddingRight: '25px', - }, - - '&&:not(:disabled):hover': { - backgroundColor: props.$backgroundColor, - }, - [`${StyledLocationRowContainer}:hover &&`]: { - display: 'block', - }, - [`${StyledLocationRowButton}:hover ~ &&`]: { - backgroundColor: props.$backgroundColorHover, - }, - }), -); - -const StyledHoverIcon = styled(ImageView).attrs({ - width: 18, - height: 18, - tintColor: colors.white60, - tintHoverColor: colors.white, -})({ - [`${StyledHoverIconButton}:hover &&`]: { - backgroundColor: colors.white, - }, -}); - interface IProps { source: C; level: number; @@ -336,24 +237,6 @@ function LocationRow(props: IProps) { // a lot more work than necessary export default React.memo(LocationRow, compareProps); -export function getButtonColor(selected: boolean, level: number, disabled?: boolean) { - let backgroundColor = colors.blue60; - if (selected) { - backgroundColor = colors.green; - } else if (level === 1) { - backgroundColor = colors.blue40; - } else if (level === 2) { - backgroundColor = colors.blue20; - } else if (level === 3) { - backgroundColor = colors.blue10; - } - - return { - $backgroundColor: backgroundColor, - $backgroundColorHover: selected || disabled ? backgroundColor : colors.blue80, - }; -} - function compareProps( oldProps: IProps, nextProps: IProps, diff --git a/gui/src/renderer/components/select-location/LocationRowStyles.tsx b/gui/src/renderer/components/select-location/LocationRowStyles.tsx new file mode 100644 index 000000000000..6a159f405455 --- /dev/null +++ b/gui/src/renderer/components/select-location/LocationRowStyles.tsx @@ -0,0 +1,150 @@ +import styled from 'styled-components'; +import { Styles } from 'styled-components/dist/types'; + +import { colors } from '../../../config.json'; +import * as Cell from '../cell'; +import { measurements, normalText } from '../common-styles'; +import ImageView from '../ImageView'; +import InfoButton from '../InfoButton'; + +interface ButtonColorProps { + $backgroundColor: string; + $backgroundColorHover: string; +} + +export const buttonColor = (props: ButtonColorProps) => { + return { + backgroundColor: props.$backgroundColor, + '&&:not(:disabled):hover': { + backgroundColor: props.$backgroundColorHover, + }, + }; +}; + +export const StyledLocationRowContainer = styled(Cell.Container)({ + display: 'flex', + padding: 0, + background: 'none', +}); + +export const StyledLocationRowContainerWithMargin = styled(StyledLocationRowContainer)({ + marginBottom: 1, +}); + +export const StyledLocationRowLabel = styled(Cell.Label)(normalText, { + flex: 1, + minWidth: 0, + fontWeight: 400, + lineHeight: '24px', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', +}); + +export const StyledLocationRowButton = styled(Cell.Row)( + buttonColor, + (props) => { + const paddingLeft = (props.$level + 1) * 16 + 2; + + return { + display: 'flex', + flex: 1, + overflow: 'hidden', + border: 'none', + padding: `0 10px 0 ${paddingLeft}px`, + margin: 0, + }; + }, +); + +export const StyledLocationRowIcon = styled.button(buttonColor, { + position: 'relative', + alignSelf: 'stretch', + paddingLeft: measurements.viewMargin, + paddingRight: measurements.viewMargin, + border: 0, + + '&&::before': { + content: '""', + position: 'absolute', + margin: 'auto', + top: 0, + left: 0, + bottom: 0, + height: '50%', + width: '1px', + backgroundColor: colors.darkBlue, + }, +}); + +interface HoverButtonProps { + $isLast?: boolean; +} + +const hoverButton = ( + props: ButtonColorProps & HoverButtonProps, +): Styles< + React.DetailedHTMLProps, HTMLButtonElement> +> => ({ + flex: 0, + display: 'none', + padding: '0 10px', + paddingRight: props.$isLast ? '17px' : '10px', + margin: 0, + border: 0, + height: measurements.rowMinHeight, + appearance: 'none', + + '&&:last-child': { + paddingRight: '25px', + }, + + '&&:not(:disabled):hover': { + backgroundColor: props.$backgroundColor, + }, + [`${StyledLocationRowContainer}:hover &&`]: { + display: 'block', + }, + [`${StyledLocationRowButton}:hover ~ &&`]: { + backgroundColor: props.$backgroundColorHover, + }, +}); + +export const StyledHoverIconButton = styled.button( + buttonColor, + hoverButton, +); + +export const StyledHoverIcon = styled(ImageView).attrs({ + width: 18, + height: 18, + tintColor: colors.white60, + tintHoverColor: colors.white, +})({ + [`${StyledHoverIconButton}:hover &&`]: { + backgroundColor: colors.white, + }, +}); + +export const StyledHoverInfoButton = styled(InfoButton)( + buttonColor, + hoverButton, +); + +export function getButtonColor(selected: boolean, level: number, disabled?: boolean) { + let backgroundColor = colors.blue60; + if (selected) { + backgroundColor = colors.green; + } else if (level === 1) { + backgroundColor = colors.blue40; + } else if (level === 2) { + backgroundColor = colors.blue20; + } else if (level === 3) { + backgroundColor = colors.blue10; + } + + return { + $backgroundColor: backgroundColor, + $backgroundColorHover: selected || disabled ? backgroundColor : colors.blue80, + }; +} From f3221b144564dd70ad0d32354d9451009bb0c14d Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 12:10:56 +0200 Subject: [PATCH 021/214] Improve custom proxy daemon rpc code and types --- gui/src/main/daemon-rpc.ts | 187 +++++++------------- gui/src/renderer/redux/settings/reducers.ts | 4 +- gui/src/shared/daemon-rpc-types.ts | 69 ++------ 3 files changed, 84 insertions(+), 176 deletions(-) diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 703ad027ccbb..531ec80ced55 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -17,6 +17,7 @@ import { ApiAccessMethodSettings, AuthFailedError, BridgeSettings, + BridgesMethod, BridgeState, BridgeType, ConnectionConfig, @@ -27,6 +28,7 @@ import { DaemonEvent, DeviceEvent, DeviceState, + DirectMethod, EndpointObfuscationType, ErrorState, ErrorStateCause, @@ -82,7 +84,6 @@ const NETWORK_CALL_TIMEOUT = 10000; const CHANNEL_STATE_TIMEOUT = 1000 * 60 * 60; const noConnectionError = new Error('No connection established to daemon'); -const configNotSupported = new Error('Setting custom settings is not supported'); const invalidErrorStateCause = new Error( 'VPN_PERMISSION_DENIED is not a valid error state cause on desktop', ); @@ -347,51 +348,17 @@ export class DaemonRpc { public async setBridgeSettings(bridgeSettings: BridgeSettings): Promise { const grpcBridgeSettings = new grpcTypes.BridgeSettings(); - if (bridgeSettings.type === 'custom') { - throw configNotSupported; - } - - grpcBridgeSettings.setBridgeType(grpcTypes.BridgeSettings.BridgeType.NORMAL); + grpcBridgeSettings.setBridgeType( + bridgeSettings.type === 'normal' + ? grpcTypes.BridgeSettings.BridgeType.NORMAL + : grpcTypes.BridgeSettings.BridgeType.CUSTOM, + ); const normalSettings = convertToNormalBridgeSettings(bridgeSettings.normal); grpcBridgeSettings.setNormal(normalSettings); if (bridgeSettings.custom) { - const customProxy = new grpcTypes.CustomProxy(); - - const customSettings = bridgeSettings.custom; - - if ('local' in customSettings) { - const local = customSettings.local; - const socks5Local = new grpcTypes.Socks5Local(); - socks5Local.setLocalPort(local.localPort); - socks5Local.setRemoteIp(local.remoteIp); - socks5Local.setRemotePort(local.remotePort); - customProxy.setSocks5local(socks5Local); - } - if ('remote' in customSettings) { - const remote = customSettings.remote; - const socks5Remote = new grpcTypes.Socks5Remote(); - if (remote.auth) { - const auth = new grpcTypes.SocksAuth(); - auth.setUsername(remote.auth.username); - auth.setPassword(remote.auth.password); - socks5Remote.setAuth(auth); - } - socks5Remote.setIp(remote.ip); - socks5Remote.setPort(remote.port); - customProxy.setSocks5remote(socks5Remote); - } - if ('shadowsocks' in customSettings) { - const shadowsocks = customSettings.shadowsocks; - const shadowOut = new grpcTypes.Shadowsocks(); - shadowOut.setCipher(shadowsocks.cipher); - shadowOut.setIp(shadowsocks.ip); - shadowOut.setPort(shadowsocks.port); - shadowOut.setPassword(shadowsocks.password); - customProxy.setShadowsocks(shadowOut); - } - + const customProxy = convertToCustomProxy(bridgeSettings.custom); grpcBridgeSettings.setCustom(customProxy); } @@ -1276,41 +1243,8 @@ function convertFromBridgeSettings(bridgeSettings: grpcTypes.BridgeSettings): Br ownership, }; - let custom = undefined; - - if (bridgeSettingsObject.custom) { - const localSettings = bridgeSettingsObject.custom.socks5local; - if (localSettings) { - custom = { - local: { - localPort: localSettings.localPort, - remoteIp: localSettings.remoteIp, - remotePort: localSettings.remotePort, - }, - }; - } - const remoteSettings = bridgeSettingsObject.custom.socks5remote; - if (remoteSettings) { - custom = { - remote: { - ip: remoteSettings.ip, - port: remoteSettings.port, - auth: remoteSettings.auth && { ...remoteSettings.auth }, - }, - }; - } - const shadowsocksSettings = bridgeSettingsObject.custom.shadowsocks; - if (shadowsocksSettings) { - custom = { - shadowsocks: { - ip: shadowsocksSettings.ip, - port: shadowsocksSettings.port, - password: shadowsocksSettings.password, - cipher: shadowsocksSettings.cipher, - }, - }; - } - } + const grpcCustom = bridgeSettings.getCustom(); + const custom = grpcCustom ? convertFromCustomProxy(grpcCustom) : undefined; return { type, normal, custom }; } @@ -1910,15 +1844,16 @@ function convertFromApiAccessMethodSettings( ): ApiAccessMethodSettings { const direct = convertFromApiAccessMethodSetting( ensureExists(accessMethods.getDirect(), "no 'Direct' access method was found"), - ); + ) as AccessMethodSetting; const bridges = convertFromApiAccessMethodSetting( ensureExists(accessMethods.getMullvadBridges(), "no 'Mullvad Bridges' access method was found"), - ); - const custom = - accessMethods - .getCustomList() - .filter((setting) => setting.hasId() && setting.hasAccessMethod()) - .map(convertFromApiAccessMethodSetting) ?? []; + ) as AccessMethodSetting; + const custom = accessMethods + .getCustomList() + .filter((setting) => setting.hasId() && setting.hasAccessMethod()) + .map(convertFromApiAccessMethodSetting) + // The last filter helps TypeScript infer the custom proxy type. + .filter(isCustomProxy); return { direct, @@ -1927,6 +1862,12 @@ function convertFromApiAccessMethodSettings( }; } +function isCustomProxy( + accessMethod: AccessMethodSetting, +): accessMethod is AccessMethodSetting { + return accessMethod.type !== 'direct' && accessMethod.type !== 'bridges'; +} + function convertFromApiAccessMethodSetting( setting: grpcTypes.AccessMethodSetting, ): AccessMethodSetting { @@ -1948,52 +1889,52 @@ function convertFromAccessMethod(method: grpcTypes.AccessMethod): AccessMethod { case grpcTypes.AccessMethod.AccessMethodCase.BRIDGES: return { type: 'bridges' }; case grpcTypes.AccessMethod.AccessMethodCase.CUSTOM: { - const proxy = method.getCustom()!; - switch (proxy.getProxyMethodCase()) { - case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5LOCAL: { - const socks5Local = proxy.getSocks5local()!; - return { - type: 'socks5-local', - remoteIp: socks5Local.getRemoteIp(), - remotePort: socks5Local.getRemotePort(), - remoteTransportProtocol: convertFromTransportProtocol( - socks5Local.getRemoteTransportProtocol(), - ), - localPort: socks5Local.getLocalPort(), - }; - } - case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5REMOTE: { - const socks5Remote = proxy.getSocks5remote()!; - const auth = socks5Remote.getAuth(); - return { - type: 'socks5-remote', - ip: socks5Remote.getIp(), - port: socks5Remote.getPort(), - authentication: auth === undefined ? undefined : convertFromSocksAuth(auth), - }; - } - case grpcTypes.CustomProxy.ProxyMethodCase.SHADOWSOCKS: { - const shadowsocks = proxy.getShadowsocks()!; - return { - type: 'shadowsocks', - ip: shadowsocks.getIp(), - port: shadowsocks.getPort(), - password: shadowsocks.getPassword(), - cipher: shadowsocks.getCipher(), - }; - } - case grpcTypes.CustomProxy.ProxyMethodCase.PROXY_METHOD_NOT_SET: - throw new Error('Custom method not set, which should always be set'); - } - // This break is required to prevent eslint from complainting about fallthrough, even though - // all cases are covered above. - break; + return convertFromCustomProxy(method.getCustom()!); } case grpcTypes.AccessMethod.AccessMethodCase.ACCESS_METHOD_NOT_SET: throw new Error('Access method not set, which should always be set'); } } +function convertFromCustomProxy(proxy: grpcTypes.CustomProxy): CustomProxy { + switch (proxy.getProxyMethodCase()) { + case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5LOCAL: { + const socks5Local = proxy.getSocks5local()!; + return { + type: 'socks5-local', + remoteIp: socks5Local.getRemoteIp(), + remotePort: socks5Local.getRemotePort(), + remoteTransportProtocol: convertFromTransportProtocol( + socks5Local.getRemoteTransportProtocol(), + ), + localPort: socks5Local.getLocalPort(), + }; + } + case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5REMOTE: { + const socks5Remote = proxy.getSocks5remote()!; + const auth = socks5Remote.getAuth(); + return { + type: 'socks5-remote', + ip: socks5Remote.getIp(), + port: socks5Remote.getPort(), + authentication: auth === undefined ? undefined : convertFromSocksAuth(auth), + }; + } + case grpcTypes.CustomProxy.ProxyMethodCase.SHADOWSOCKS: { + const shadowsocks = proxy.getShadowsocks()!; + return { + type: 'shadowsocks', + ip: shadowsocks.getIp(), + port: shadowsocks.getPort(), + password: shadowsocks.getPassword(), + cipher: shadowsocks.getCipher(), + }; + } + case grpcTypes.CustomProxy.ProxyMethodCase.PROXY_METHOD_NOT_SET: + throw new Error('Custom method not set, which should always be set'); + } +} + function convertFromSocksAuth(auth: grpcTypes.SocksAuth): SocksAuth { return { username: auth.getUsername(), diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts index 210ef380105e..e64b0d63237f 100644 --- a/gui/src/renderer/redux/settings/reducers.ts +++ b/gui/src/renderer/redux/settings/reducers.ts @@ -6,6 +6,7 @@ import { BridgeState, BridgeType, CustomLists, + CustomProxy, IDnsOptions, IpVersion, IWireguardEndpointData, @@ -13,7 +14,6 @@ import { ObfuscationSettings, ObfuscationType, Ownership, - ProxySettings, RelayEndpointType, RelayLocation, RelayOverride, @@ -62,7 +62,7 @@ export type RelaySettingsRedux = export type BridgeSettingsRedux = { type: BridgeType; normal: NormalBridgeSettingsRedux; - custom?: ProxySettings; + custom?: CustomProxy; }; export interface IRelayLocationRelayRedux { diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index e24c124f4c58..fb1a88cff66e 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -352,35 +352,6 @@ export interface IDnsOptions { }; } -export type ProxySettings = - | { local: ILocalProxySettings } - | { remote: IRemoteProxySettings } - | { shadowsocks: IShadowsocksProxySettings }; - -export interface ILocalProxySettings { - localPort: number; - remoteIp: string; - remotePort: number; -} - -export interface IRemoteProxySettings { - ip: string; - port: number; - auth?: IRemoteProxyAuth; -} - -export interface IRemoteProxyAuth { - username: string; - password: string; -} - -export interface IShadowsocksProxySettings { - ip: string; - port: number; - password: string; - cipher: string; -} - export interface IAppVersionInfo { supported: boolean; suggestedUpgrade?: string; @@ -471,7 +442,7 @@ export type BridgeType = 'normal' | 'custom'; export interface BridgeSettings { type: BridgeType; normal: IBridgeConstraints; - custom?: ProxySettings; + custom?: CustomProxy; } export interface ISocketAddress { @@ -488,7 +459,7 @@ export interface SocksAuth { password: string; } -export type Socks5LocalAccessMethod = { +export type Socks5LocalCustomProxy = { type: 'socks5-local'; remoteIp: string; remotePort: number; @@ -496,14 +467,14 @@ export type Socks5LocalAccessMethod = { localPort: number; }; -export type Socks5RemoteAccessMethod = { +export type Socks5RemoteCustomProxy = { type: 'socks5-remote'; ip: string; port: number; authentication?: SocksAuth; }; -export type ShadowsocksAccessMethod = { +export type ShadowsocksCustomProxy = { type: 'shadowsocks'; ip: string; port: number; @@ -511,33 +482,29 @@ export type ShadowsocksAccessMethod = { cipher: string; }; -export type CustomProxy = - | Socks5LocalAccessMethod - | Socks5RemoteAccessMethod - | ShadowsocksAccessMethod; +export type CustomProxy = Socks5LocalCustomProxy | Socks5RemoteCustomProxy | ShadowsocksCustomProxy; +export type NamedCustomProxy = CustomProxy & { name: string }; -export type AccessMethod = - | { - type: 'direct'; - } - | { - type: 'bridges'; - } - | CustomProxy; +export type DirectMethod = { type: 'direct' }; +export type BridgesMethod = { type: 'bridges' }; +export type AccessMethod = DirectMethod | BridgesMethod | CustomProxy; -export type NewAccessMethodSetting = AccessMethod & { - name: string; +export type NamedAccessMethod = T & { name: string }; + +export type NewAccessMethodSetting = NamedAccessMethod & { enabled: boolean; }; -export type AccessMethodSetting = NewAccessMethodSetting & { +export type AccessMethodSetting< + T extends AccessMethod = AccessMethod +> = NewAccessMethodSetting & { id: string; }; export type ApiAccessMethodSettings = { - direct: AccessMethodSetting; - mullvadBridges: AccessMethodSetting; - custom: Array; + direct: AccessMethodSetting; + mullvadBridges: AccessMethodSetting; + custom: Array>; }; export interface RelayOverride { From 49c21c0a391e1bb48e8400451263b07f31e97c1f Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 16:56:37 +0200 Subject: [PATCH 022/214] Respect custom bridge when displaying location as selected --- .../select-location/RelayListContext.tsx | 27 ++++++++++++++++--- .../select-location/custom-list-helpers.ts | 12 +++++++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/gui/src/renderer/components/select-location/RelayListContext.tsx b/gui/src/renderer/components/select-location/RelayListContext.tsx index d0ee5d6e8565..86dd7e54676a 100644 --- a/gui/src/renderer/components/select-location/RelayListContext.tsx +++ b/gui/src/renderer/components/select-location/RelayListContext.tsx @@ -135,6 +135,15 @@ function useRelayList( const selectedLocation = useSelectedLocation(); const disabledLocation = useDisabledLocation(); + const preventDueToCustomBridgeSelected = usePreventDueToCustomBridgeSelected(); + + const isLocationSelected = useCallback( + (location: RelayLocation) => { + return preventDueToCustomBridgeSelected ? false : isSelected(location, selectedLocation); + }, + [preventDueToCustomBridgeSelected, selectedLocation], + ); + return useMemo(() => { return relayList .map((country) => { @@ -149,7 +158,7 @@ function useRelayList( disabled: countryDisabledReason !== undefined, disabledReason: countryDisabledReason, expanded: isExpanded(countryLocation, expandedLocations), - selected: isSelected(countryLocation, selectedLocation), + selected: isLocationSelected(countryLocation), cities: country.cities .map((city) => { const cityLocation: RelayLocation = { country: country.code, city: city.code }; @@ -164,7 +173,7 @@ function useRelayList( disabled: cityDisabledReason !== undefined, disabledReason: cityDisabledReason, expanded: isExpanded(cityLocation, expandedLocations), - selected: isSelected(cityLocation, selectedLocation), + selected: isLocationSelected(cityLocation), relays: city.relays .map((relay) => { const relayLocation: RelayLocation = { @@ -183,7 +192,7 @@ function useRelayList( location: relayLocation, disabled: relayDisabledReason !== undefined, disabledReason: relayDisabledReason, - selected: isSelected(relayLocation, selectedLocation), + selected: isLocationSelected(relayLocation), }; }) .sort((a, b) => a.hostname.localeCompare(b.hostname, locale, { numeric: true })), @@ -193,7 +202,17 @@ function useRelayList( }; }) .sort((a, b) => a.label.localeCompare(b.label, locale)); - }, [locale, expandedLocations, relayList, selectedLocation, disabledLocation]); + }, [locale, expandedLocations, relayList, disabledLocation, isLocationSelected]); +} + +export function usePreventDueToCustomBridgeSelected(): boolean { + const relaySettings = useNormalRelaySettings(); + const { locationType } = useSelectLocationContext(); + const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); + const isBridgeSelection = + relaySettings?.tunnelProtocol === 'openvpn' && locationType === LocationType.entry; + + return isBridgeSelection && bridgeSettings.type === 'custom'; } // Return all RelayLocations that should be expanded diff --git a/gui/src/renderer/components/select-location/custom-list-helpers.ts b/gui/src/renderer/components/select-location/custom-list-helpers.ts index 799deb8ed3f8..5cb6d695b8d7 100644 --- a/gui/src/renderer/components/select-location/custom-list-helpers.ts +++ b/gui/src/renderer/components/select-location/custom-list-helpers.ts @@ -4,7 +4,11 @@ import { ICustomList, RelayLocation } from '../../../shared/daemon-rpc-types'; import { hasValue } from '../../../shared/utils'; import { searchMatch } from '../../lib/filter-locations'; import { useSelector } from '../../redux/store'; -import { useDisabledLocation, useSelectedLocation } from './RelayListContext'; +import { + useDisabledLocation, + usePreventDueToCustomBridgeSelected, + useSelectedLocation, +} from './RelayListContext'; import { isCustomListDisabled, isExpanded, isSelected } from './select-location-helpers'; import { CitySpecification, @@ -26,6 +30,8 @@ export function useCustomListsRelayList( const { searchTerm } = useSelectLocationContext(); const customLists = useSelector((state) => state.settings.customLists); + const preventDueToCustomBridgeSelected = usePreventDueToCustomBridgeSelected(); + // Populate all custom lists with the real location trees for the list locations. return useMemo( () => @@ -34,6 +40,7 @@ export function useCustomListsRelayList( list, relayList, searchTerm, + preventDueToCustomBridgeSelected, selectedLocation, disabledLocation, expandedLocations, @@ -48,6 +55,7 @@ function prepareCustomList( list: ICustomList, fullRelayList: GeographicalRelayList, searchTerm: string, + preventDueToCustomBridgeSelected: boolean, selectedLocation?: RelayLocation, disabledLocation?: { location: RelayLocation; reason: DisabledReason }, expandedLocations?: Array, @@ -64,7 +72,7 @@ function prepareCustomList( disabled: disabledReason !== undefined, disabledReason, expanded: isExpanded(location, expandedLocations), - selected: isSelected(location, selectedLocation), + selected: preventDueToCustomBridgeSelected ? false : isSelected(location, selectedLocation), visible: searchMatch(searchTerm, list.name), locations, }; From ea2c847d41183eed302c1c9994a06bd55454e252 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 16:58:19 +0200 Subject: [PATCH 023/214] Display custom bridge correctly in ConnectionPanel --- gui/src/renderer/components/ConnectionPanel.tsx | 16 +++++++++++----- gui/src/shared/daemon-rpc-types.ts | 10 ---------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/gui/src/renderer/components/ConnectionPanel.tsx b/gui/src/renderer/components/ConnectionPanel.tsx index 07547363a07e..a99140bb431d 100644 --- a/gui/src/renderer/components/ConnectionPanel.tsx +++ b/gui/src/renderer/components/ConnectionPanel.tsx @@ -6,7 +6,6 @@ import { colors } from '../../config.json'; import { EndpointObfuscationType, ProxyType, - proxyTypeToString, RelayProtocol, TunnelType, tunnelTypeToString, @@ -164,10 +163,9 @@ export default class ConnectionPanel extends React.Component { entry: this.props.entryHostname, }, ); - } else if (this.props.bridgeInfo?.ip) { - return sprintf(messages.pgettext('connection-info', '%(relay)s via %(entry)s'), { + } else if (this.props.bridgeInfo !== undefined) { + return sprintf(messages.pgettext('connection-info', '%(relay)s via Custom bridge'), { relay: this.props.hostname, - entry: this.props.bridgeInfo.ip, }); } else { return this.props.hostname || ''; @@ -181,7 +179,7 @@ export default class ConnectionPanel extends React.Component { const tunnelType = tunnelTypeToString(inAddress.tunnelType); if (bridgeInfo) { - const bridgeType = proxyTypeToString(bridgeInfo.bridgeType); + const bridgeType = this.bridgeType(); return sprintf( // TRANSLATORS: The tunnel type line displayed below the hostname line on the main screen @@ -201,4 +199,12 @@ export default class ConnectionPanel extends React.Component { return ''; } } + + private bridgeType() { + if (this.props.bridgeHostname && this.props.bridgeInfo?.bridgeType === 'shadowsocks') { + return 'Shadowsocks bridge'; + } else { + return 'Custom bridge'; + } + } } diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index fb1a88cff66e..738eef5e9561 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -128,16 +128,6 @@ export function wrapConstraint( } export type ProxyType = 'shadowsocks' | 'custom'; -export function proxyTypeToString(proxy: ProxyType): string { - switch (proxy) { - case 'shadowsocks': - return 'Shadowsocks bridge'; - case 'custom': - return 'custom bridge'; - default: - return ''; - } -} export enum Ownership { any, From c4d6f31c41d19133f8e23701de487846cc7ec362 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 9 Apr 2024 08:03:57 +0200 Subject: [PATCH 024/214] Add bridge settings updater functions --- gui/src/renderer/lib/constraint-updater.ts | 77 ++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/gui/src/renderer/lib/constraint-updater.ts b/gui/src/renderer/lib/constraint-updater.ts index 054f06346504..73fbdd1336a7 100644 --- a/gui/src/renderer/lib/constraint-updater.ts +++ b/gui/src/renderer/lib/constraint-updater.ts @@ -1,6 +1,8 @@ import { useCallback } from 'react'; import { + BridgeSettings, + IBridgeConstraints, IOpenVpnConstraints, IRelaySettingsNormal, IWireguardConstraints, @@ -8,7 +10,12 @@ import { wrapConstraint, } from '../../shared/daemon-rpc-types'; import { useAppContext } from '../context'; -import { NormalRelaySettingsRedux } from '../redux/settings/reducers'; +import { + BridgeSettingsRedux, + NormalBridgeSettingsRedux, + NormalRelaySettingsRedux, +} from '../redux/settings/reducers'; +import { useSelector } from '../redux/store'; import { useNormalRelaySettings } from './utilityHooks'; export function wrapRelaySettingsOrDefault( @@ -59,7 +66,7 @@ export function wrapRelaySettingsOrDefault( }; } -type UpdateFunction = ( +type RelaySettingsUpdateFunction = ( settings: IRelaySettingsNormal, ) => IRelaySettingsNormal; @@ -67,7 +74,7 @@ export function useRelaySettingsModifier() { const relaySettings = useNormalRelaySettings(); return useCallback( - (fn: UpdateFunction) => { + (fn: RelaySettingsUpdateFunction) => { const settings = wrapRelaySettingsOrDefault(relaySettings); return fn(settings); }, @@ -80,10 +87,72 @@ export function useRelaySettingsUpdater() { const modifyRelaySettings = useRelaySettingsModifier(); return useCallback( - async (fn: UpdateFunction) => { + async (fn: RelaySettingsUpdateFunction) => { const modifiedSettings = modifyRelaySettings(fn); await setRelaySettings({ normal: modifiedSettings }); }, [setRelaySettings, modifyRelaySettings], ); } + +export function wrapBridgeSettingsOrDefault(bridgeSettings?: BridgeSettingsRedux): BridgeSettings { + if (bridgeSettings) { + return { + type: bridgeSettings.type, + normal: wrapNormalBridgeSettingsOrDefault(bridgeSettings.normal), + custom: bridgeSettings.custom, + }; + } + + return { + type: 'normal', + normal: wrapNormalBridgeSettingsOrDefault(), + }; +} + +function wrapNormalBridgeSettingsOrDefault( + bridgeSettings?: NormalBridgeSettingsRedux, +): IBridgeConstraints { + if (bridgeSettings) { + const location = wrapConstraint(bridgeSettings.location); + + return { + location, + providers: [...bridgeSettings.providers], + ownership: bridgeSettings.ownership, + }; + } + + return { + location: 'any', + providers: [], + ownership: Ownership.any, + }; +} + +type BridgeSettingsUpdateFunction = (settings: BridgeSettings) => BridgeSettings; + +export function useBridgeSettingsModifier() { + const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); + + return useCallback( + (fn: BridgeSettingsUpdateFunction) => { + const settings = wrapBridgeSettingsOrDefault(bridgeSettings); + return fn(settings); + }, + [bridgeSettings], + ); +} + +export function useBridgeSettingsUpdater() { + const { updateBridgeSettings } = useAppContext(); + const modifyBridgeSettings = useBridgeSettingsModifier(); + + return useCallback( + async (fn: BridgeSettingsUpdateFunction) => { + const modifiedSettings = modifyBridgeSettings(fn); + await updateBridgeSettings(modifiedSettings); + }, + [updateBridgeSettings, modifyBridgeSettings], + ); +} From a1fa168418366e350be10b338c69bd1fda0d83b5 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 9 Apr 2024 08:04:29 +0200 Subject: [PATCH 025/214] Remove bridge settings builder --- gui/src/shared/bridge-settings-builder.ts | 28 ---------------- gui/src/shared/relay-location-builder.ts | 41 ----------------------- 2 files changed, 69 deletions(-) delete mode 100644 gui/src/shared/bridge-settings-builder.ts delete mode 100644 gui/src/shared/relay-location-builder.ts diff --git a/gui/src/shared/bridge-settings-builder.ts b/gui/src/shared/bridge-settings-builder.ts deleted file mode 100644 index 2ee546970786..000000000000 --- a/gui/src/shared/bridge-settings-builder.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BridgeSettings, IBridgeConstraints, Ownership } from './daemon-rpc-types'; -import makeLocationBuilder, { ILocationBuilder } from './relay-location-builder'; - -export default class BridgeSettingsBuilder { - private payload: Partial = {}; - - public build(): BridgeSettings { - if (this.payload.location) { - return { - type: 'normal', - normal: { - location: this.payload.location, - providers: this.payload.providers ?? [], - ownership: this.payload.ownership ?? Ownership.any, - }, - custom: undefined, - }; - } else { - throw new Error('Unsupported configuration'); - } - } - - get location(): ILocationBuilder { - return makeLocationBuilder(this, (location) => { - this.payload.location = location; - }); - } -} diff --git a/gui/src/shared/relay-location-builder.ts b/gui/src/shared/relay-location-builder.ts deleted file mode 100644 index 7e585f9eafde..000000000000 --- a/gui/src/shared/relay-location-builder.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Constraint, LiftedConstraint, RelayLocation } from './daemon-rpc-types'; - -export interface ILocationBuilder { - country(country: string): Self; - city(country: string, city: string): Self; - hostname(country: string, city: string, hostname: string): Self; - any(): Self; - fromRaw(location: LiftedConstraint): Self; -} - -export default function makeLocationBuilder( - context: T, - receiver: (constraint: Constraint) => void, -): ILocationBuilder { - return { - country: (country: string) => { - receiver({ only: { country } }); - return context; - }, - city: (country: string, city: string) => { - receiver({ only: { country, city } }); - return context; - }, - hostname: (country: string, city: string, hostname: string) => { - receiver({ only: { country, city, hostname } }); - return context; - }, - any: () => { - receiver('any'); - return context; - }, - fromRaw(location: LiftedConstraint) { - if (location === 'any') { - return this.any(); - } else { - receiver({ only: location }); - return context; - } - }, - }; -} From b7da31389077d62200a932a729429d3118ab00d8 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 08:11:23 +0200 Subject: [PATCH 026/214] Add more bridge info to OpenVPN settings --- .../renderer/components/OpenVpnSettings.tsx | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/gui/src/renderer/components/OpenVpnSettings.tsx b/gui/src/renderer/components/OpenVpnSettings.tsx index 679794d8312f..dee1e6408968 100644 --- a/gui/src/renderer/components/OpenVpnSettings.tsx +++ b/gui/src/renderer/components/OpenVpnSettings.tsx @@ -18,13 +18,12 @@ import { useHistory } from '../lib/history'; import { formatHtml } from '../lib/html-formatter'; import { useBoolean } from '../lib/utilityHooks'; import { useSelector } from '../redux/store'; -import * as AppButton from './AppButton'; import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup'; import * as Cell from './cell'; import Selector, { SelectorItem } from './cell/Selector'; import { BackAction } from './KeyboardNavigation'; import { Layout, SettingsContainer } from './Layout'; -import { ModalAlert, ModalAlertType } from './Modal'; +import { ModalAlert, ModalAlertType, ModalMessage } from './Modal'; import { NavigationBar, NavigationContainer, @@ -33,6 +32,7 @@ import { TitleBarItem, } from './NavigationBar'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; +import { SmallButton } from './SmallButton'; const MIN_MSSFIX_VALUE = 1000; const MAX_MSSFIX_VALUE = 1450; @@ -308,6 +308,8 @@ function BridgeModeSelector() { await setBridgeState('on'); }, [hideConfirmationDialog, setBridgeState]); + const footerText = bridgeModeFooterText(bridgeState === 'on', tunnelProtocol, transportProtocol); + return ( <> @@ -317,31 +319,57 @@ function BridgeModeSelector() { // TRANSLATORS: The title for the shadowsocks bridge selector section. messages.pgettext('openvpn-settings-view', 'Bridge mode') } + infoTitle={messages.pgettext('openvpn-settings-view', 'Bridge mode')} + details={ + <> + + {sprintf( + // TRANSLATORS: This is used as a description for the bridge mode + // TRANSLATORS: setting. + // TRANSLATORS: Available placeholders: + // TRANSLATORS: %(openvpn)s - will be replaced with OpenVPN + messages.pgettext( + 'openvpn-settings-view', + 'Helps circumvent censorship, by routing your traffic through a bridge server before reaching an %(openvpn)s server. Obfuscation is added to make fingerprinting harder.', + ), + { openvpn: strings.openvpn }, + )} + + + {messages.gettext('This setting increases latency. Use only if needed.')} + + + } items={options} value={bridgeState} onSelect={onSelectBridgeState} automaticValue={'auto' as const} /> - - - - {bridgeModeFooterText(tunnelProtocol, transportProtocol)} - - - + {footerText !== undefined && ( + + + {footerText} + + + )} - {messages.gettext('Enable anyway')} - , - - {messages.gettext('Back')} - , + title={messages.pgettext('openvpn-settings-view', 'Enable bridge mode?')} + message={ + // TRANSLATORS: Warning shown in dialog to users when they enable setting that increases + // TRANSLATORS: network latency (decreases performance). + messages.gettext('This setting increases latency. Use only if needed.') + } + gridButtons={[ + + {messages.gettext('Cancel')} + , + + {messages.gettext('Enable')} + , ]} close={hideConfirmationDialog} /> @@ -350,10 +378,18 @@ function BridgeModeSelector() { } function bridgeModeFooterText( + bridgeModeOn: boolean, tunnelProtocol: TunnelProtocol | null, transportProtocol: RelayProtocol | null, -) { - if (tunnelProtocol !== 'openvpn') { +): React.ReactNode | void { + if (bridgeModeOn) { + // TRANSLATORS: This text is shown beneath the bridge mode setting to instruct users how to + // TRANSLATORS: configure the feature further. + return messages.pgettext( + 'openvpn-settings-view', + 'To select a specific bridge server, go to the Select location view.', + ); + } else if (tunnelProtocol !== 'openvpn') { return formatHtml( sprintf( // TRANSLATORS: This is used to instruct users how to make the bridge mode setting @@ -391,18 +427,6 @@ function bridgeModeFooterText( }, ), ); - } else { - return sprintf( - // TRANSLATORS: This is used as a description for the bridge mode - // TRANSLATORS: setting. - // TRANSLATORS: Available placeholders: - // TRANSLATORS: %(openvpn)s - will be replaced with OpenVPN - messages.pgettext( - 'openvpn-settings-view', - 'Helps circumvent censorship, by routing your traffic through a bridge server before reaching an %(openvpn)s server. Obfuscation is added to make fingerprinting harder.', - ), - { openvpn: strings.openvpn }, - ); } } From 9e7b4bf6638c1b57d5291305a838367865ce8f76 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 9 Apr 2024 08:09:15 +0200 Subject: [PATCH 027/214] Add Custom bridge to Select location view --- .../select-location/SelectLocation.tsx | 50 +++---- .../select-location/SpecialLocationList.tsx | 122 ++++++++++++++---- .../select-location/select-location-hooks.ts | 44 +++++-- .../select-location/select-location-types.ts | 11 +- 4 files changed, 154 insertions(+), 73 deletions(-) diff --git a/gui/src/renderer/components/select-location/SelectLocation.tsx b/gui/src/renderer/components/select-location/SelectLocation.tsx index 6bf0fcff6fe2..5ba02039395f 100644 --- a/gui/src/renderer/components/select-location/SelectLocation.tsx +++ b/gui/src/renderer/components/select-location/SelectLocation.tsx @@ -9,7 +9,7 @@ import { filterSpecialLocations } from '../../lib/filter-locations'; import { useHistory } from '../../lib/history'; import { formatHtml } from '../../lib/html-formatter'; import { RoutePath } from '../../lib/routes'; -import { useNormalBridgeSettings, useNormalRelaySettings } from '../../lib/utilityHooks'; +import { useNormalRelaySettings } from '../../lib/utilityHooks'; import { useSelector } from '../../redux/store'; import * as Cell from '../cell'; import { useFilteredProviders } from '../Filter'; @@ -34,12 +34,7 @@ import { useOnSelectEntryLocation, useOnSelectExitLocation, } from './select-location-hooks'; -import { - LocationType, - SpecialBridgeLocationType, - SpecialLocation, - SpecialLocationIcon, -} from './select-location-types'; +import { LocationType, SpecialBridgeLocationType, SpecialLocation } from './select-location-types'; import { useSelectLocationContext } from './SelectLocationContainer'; import { StyledClearFilterButton, @@ -54,6 +49,11 @@ import { StyledSearchBar, } from './SelectLocationStyles'; import { SpacePreAllocationView } from './SpacePreAllocationView'; +import { + AutomaticLocationRow, + CustomBridgeLocationRow, + CustomExitLocationRow, +} from './SpecialLocationList'; export default function SelectLocation() { const history = useHistory(); @@ -260,22 +260,21 @@ function SelectLocationContent() { const [onSelectBridgeRelay, onSelectBridgeSpecial] = useOnSelectBridgeLocation(); const relaySettings = useNormalRelaySettings(); - const bridgeSettings = useNormalBridgeSettings(); + const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); const allowAddToCustomList = useSelector((state) => state.settings.customLists.length > 0); if (locationType === LocationType.exit) { // Add "Custom" item if a custom relay is selected - const specialList: Array> = - relaySettings === undefined - ? [ - { - label: messages.gettext('Custom'), - value: undefined, - selected: true, - }, - ] - : []; + const specialList: Array> = []; + if (relaySettings === undefined) { + specialList.push({ + label: messages.gettext('Custom'), + value: undefined, + selected: true, + component: CustomExitLocationRow, + }); + } const specialLocations = filterSpecialLocations(searchTerm, specialList); return ( @@ -319,15 +318,18 @@ function SelectLocationContent() { } else { // Add the "Automatic" item const specialList: Array> = [ + { + label: messages.pgettext('select-location-view', 'Custom bridge'), + value: SpecialBridgeLocationType.custom, + selected: bridgeSettings?.type === 'custom', + disabled: bridgeSettings?.custom === undefined, + component: CustomBridgeLocationRow, + }, { label: messages.gettext('Automatic'), - icon: SpecialLocationIcon.geoLocation, - info: messages.pgettext( - 'select-location-view', - 'The app selects a random bridge server, but servers have a higher probability the closer they are to you.', - ), value: SpecialBridgeLocationType.closestToExit, - selected: bridgeSettings?.location === 'any', + selected: bridgeSettings?.type === 'normal' && bridgeSettings.normal?.location === 'any', + component: AutomaticLocationRow, }, ]; diff --git a/gui/src/renderer/components/select-location/SpecialLocationList.tsx b/gui/src/renderer/components/select-location/SpecialLocationList.tsx index 030520fc73b3..2e658c60463d 100644 --- a/gui/src/renderer/components/select-location/SpecialLocationList.tsx +++ b/gui/src/renderer/components/select-location/SpecialLocationList.tsx @@ -3,16 +3,22 @@ import styled from 'styled-components'; import { colors } from '../../../config.json'; import { messages } from '../../../shared/gettext'; +import { useHistory } from '../../lib/history'; +import { RoutePath } from '../../lib/routes'; +import { useSelector } from '../../redux/store'; import * as Cell from '../cell'; +import ImageView from '../ImageView'; import InfoButton from '../InfoButton'; +import RelayStatusIndicator from '../RelayStatusIndicator'; import { getButtonColor, + StyledHoverInfoButton, StyledLocationRowButton, - StyledLocationRowContainer, + StyledLocationRowContainerWithMargin, StyledLocationRowIcon, StyledLocationRowLabel, -} from './LocationRow'; -import { SpecialLocation } from './select-location-types'; +} from './LocationRowStyles'; +import { SpecialBridgeLocationType, SpecialLocation } from './select-location-types'; interface SpecialLocationsProps { source: Array>; @@ -30,21 +36,14 @@ export default function SpecialLocationList({ source, ...props }: SpecialLoca ); } -const StyledLocationRowContainerWithMargin = styled(StyledLocationRowContainer)({ - marginBottom: 1, -}); - const StyledSpecialLocationIcon = styled(Cell.Icon)({ flex: 0, marginLeft: '2px', marginRight: '8px', }); -const StyledSpecialLocationInfoButton = styled(InfoButton)({ - margin: 0, - padding: '0 25px', - backgroundColor: colors.blue, -}); +const StyledSpecialLocationInfoButton = styled(InfoButton)({ padding: '0 25px', margin: 0 }); +const StyledSpecialLocationSideButton = styled(ImageView)({ padding: '0 3px' }); interface SpecialLocationRowProps { source: SpecialLocation; @@ -59,30 +58,97 @@ function SpecialLocationRow(props: SpecialLocationRowProps) { } }, [props.source.selected, props.onSelect, props.source.value]); - const icon = props.source.selected ? 'icon-tick' : props.source.icon ?? undefined; + const innerProps: SpecialLocationRowInnerProps = { + ...props, + onSelect, + }; + return ; +} + +export interface SpecialLocationRowInnerProps + extends Omit, 'onSelect'> { + onSelect: () => void; +} + +export function AutomaticLocationRow( + props: SpecialLocationRowInnerProps, +) { + const icon = props.source.selected ? 'icon-tick' : 'icon-nearest'; const selectedRef = props.source.selected ? props.selectedElementRef : undefined; const background = getButtonColor(props.source.selected, 0, props.source.disabled); return ( - - {icon && ( - + + + {props.source.label} + + + + ); +} + +export function CustomExitLocationRow(props: SpecialLocationRowInnerProps) { + const selectedRef = props.source.selected ? props.selectedElementRef : undefined; + const background = getButtonColor(props.source.selected, 0, props.source.disabled); + return ( + + {props.source.label} - {props.source.info && ( - + ); +} + +export function CustomBridgeLocationRow( + props: SpecialLocationRowInnerProps, +) { + const history = useHistory(); + + const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); + const icon = bridgeSettings.custom !== undefined ? 'icon-edit' : 'icon-add'; + + const selectedRef = props.source.selected ? props.selectedElementRef : undefined; + const background = getButtonColor(props.source.selected, 0, props.source.disabled); + + const navigate = useCallback(() => history.push(RoutePath.editCustomBridge), [history.push]); + + return ( + + + + {props.source.label} + + + + - )} + ); } diff --git a/gui/src/renderer/components/select-location/select-location-hooks.ts b/gui/src/renderer/components/select-location/select-location-hooks.ts index b795f4a74f96..08ed2dccf3fb 100644 --- a/gui/src/renderer/components/select-location/select-location-hooks.ts +++ b/gui/src/renderer/components/select-location/select-location-hooks.ts @@ -1,6 +1,5 @@ import { useCallback } from 'react'; -import BridgeSettingsBuilder from '../../../shared/bridge-settings-builder'; import { BridgeSettings, RelayLocation, @@ -10,8 +9,8 @@ import { import log from '../../../shared/logging'; import { useAppContext } from '../../context'; import { useRelaySettingsModifier } from '../../lib/constraint-updater'; +import { useBridgeSettingsModifier } from '../../lib/constraint-updater'; import { useHistory } from '../../lib/history'; -import { useSelector } from '../../redux/store'; import { LocationType, SpecialBridgeLocationType } from './select-location-types'; import { useSelectLocationContext } from './SelectLocationContainer'; @@ -89,7 +88,7 @@ function useOnSelectLocation() { export function useOnSelectBridgeLocation() { const { updateBridgeSettings } = useAppContext(); const { setLocationType } = useSelectLocationContext(); - const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); + const bridgeSettingsModifier = useBridgeSettingsModifier(); const setLocation = useCallback(async (bridgeUpdate: BridgeSettings) => { if (bridgeUpdate) { @@ -105,21 +104,38 @@ export function useOnSelectBridgeLocation() { const onSelectRelay = useCallback( (location: RelayLocation) => { - const bridgeUpdate = new BridgeSettingsBuilder().location.fromRaw(location).build(); - bridgeUpdate.custom = bridgeSettings.custom; - return setLocation(bridgeUpdate); + return setLocation( + bridgeSettingsModifier((bridgeSettings) => { + bridgeSettings.type = 'normal'; + bridgeSettings.normal.location = wrapConstraint(location); + return bridgeSettings; + }), + ); }, - [bridgeSettings], + [bridgeSettingsModifier], ); - const onSelectSpecial = useCallback((location: SpecialBridgeLocationType) => { - switch (location) { - case SpecialBridgeLocationType.closestToExit: { - const bridgeUpdate = new BridgeSettingsBuilder().location.any().build(); - return setLocation(bridgeUpdate); + const onSelectSpecial = useCallback( + (location: SpecialBridgeLocationType) => { + switch (location) { + case SpecialBridgeLocationType.closestToExit: + return setLocation( + bridgeSettingsModifier((bridgeSettings) => { + bridgeSettings.normal.location = 'any'; + return bridgeSettings; + }), + ); + case SpecialBridgeLocationType.custom: + return setLocation( + bridgeSettingsModifier((bridgeSettings) => { + bridgeSettings.type = 'custom'; + return bridgeSettings; + }), + ); } - } - }, []); + }, + [bridgeSettingsModifier], + ); return [onSelectRelay, onSelectSpecial] as const; } diff --git a/gui/src/renderer/components/select-location/select-location-types.ts b/gui/src/renderer/components/select-location/select-location-types.ts index c42cc45f6b19..ca6afecf7495 100644 --- a/gui/src/renderer/components/select-location/select-location-types.ts +++ b/gui/src/renderer/components/select-location/select-location-types.ts @@ -11,6 +11,7 @@ import { IRelayLocationCountryRedux, IRelayLocationRelayRedux, } from '../../redux/settings/reducers'; +import { SpecialLocationRowInnerProps } from './SpecialLocationList'; export enum LocationType { entry = 0, @@ -21,11 +22,8 @@ export type RelayList = GeographicalRelayList | Array; export type GeographicalRelayList = Array; export enum SpecialBridgeLocationType { - closestToExit = 0, -} - -export enum SpecialLocationIcon { - geoLocation = 'icon-nearest', + closestToExit, + custom, } export interface LocationVisibility { @@ -40,9 +38,8 @@ interface CommonLocationSpecification { } export interface SpecialLocation extends CommonLocationSpecification { - icon?: SpecialLocationIcon; - info?: string; value: T; + component: React.ComponentType>; } type GeographicalLocationSpecification = From 41a01db5963e0f499c0dc23419af8ed6bd8bf772 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 8 Apr 2024 07:57:05 +0200 Subject: [PATCH 028/214] Refactor custom proxy form out of Api access method component --- .../components/EditApiAccessMethod.tsx | 453 +-------------- gui/src/renderer/components/ProxyForm.tsx | 532 ++++++++++++++++++ 2 files changed, 555 insertions(+), 430 deletions(-) create mode 100644 gui/src/renderer/components/ProxyForm.tsx diff --git a/gui/src/renderer/components/EditApiAccessMethod.tsx b/gui/src/renderer/components/EditApiAccessMethod.tsx index b5b3bd6d4831..59a5f2ff3727 100644 --- a/gui/src/renderer/components/EditApiAccessMethod.tsx +++ b/gui/src/renderer/components/EditApiAccessMethod.tsx @@ -1,38 +1,27 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; import { useParams } from 'react-router'; import { sprintf } from 'sprintf-js'; import { - AccessMethod, - AccessMethodSetting, CustomProxy, + NamedCustomProxy, NewAccessMethodSetting, - RelayProtocol, - ShadowsocksAccessMethod, - Socks5LocalAccessMethod, - Socks5RemoteAccessMethod, } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; import { useScheduler } from '../../shared/scheduler'; import { useAppContext } from '../context'; import { useApiAccessMethodTest } from '../lib/api-access-methods'; import { useHistory } from '../lib/history'; -import { IpAddress } from '../lib/ip'; import { useSelector } from '../redux/store'; -import * as Cell from './cell'; -import { SettingsForm, useSettingsFormSubmittable } from './cell/SettingsForm'; -import { SettingsGroup } from './cell/SettingsGroup'; -import { SettingsRadioGroup } from './cell/SettingsRadioGroup'; -import { SettingsRow } from './cell/SettingsRow'; -import { SettingsSelect, SettingsSelectItem } from './cell/SettingsSelect'; -import { SettingsNumberInput, SettingsTextInput } from './cell/SettingsTextInput'; +import { SettingsForm } from './cell/SettingsForm'; import { BackAction } from './KeyboardNavigation'; import { Layout, SettingsContainer } from './Layout'; import { ModalAlert, ModalAlertType } from './Modal'; import { NavigationBar, NavigationContainer, NavigationItems, TitleBarItem } from './NavigationBar'; +import { NamedProxyForm } from './ProxyForm'; import SettingsHeader, { HeaderSubTitle, HeaderTitle } from './SettingsHeader'; import { StyledContent, StyledNavigationScrollbars, StyledSettingsContent } from './SettingsStyles'; -import { SmallButton, SmallButtonGroup } from './SmallButton'; +import { SmallButton } from './SmallButton'; export function EditApiAccessMethod() { return ( @@ -45,7 +34,7 @@ export function EditApiAccessMethod() { function AccessMethodForm() { const history = useHistory(); const { addApiAccessMethod, updateApiAccessMethod } = useAppContext(); - const methods = useSelector((state) => state.settings.apiAccessMethods); + const methods = useSelector((state) => state.settings.apiAccessMethods.custom); const [testing, testResult, testApiAccessMethod, resetTestResult] = useApiAccessMethodTest( false, @@ -55,19 +44,9 @@ function AccessMethodForm() { // Use id in url to figure out which method is to be edited. undefined means this is a new method. const { id } = useParams<{ id: string | undefined }>(); - // Ugly way of iterating over all access methods, but it works. - const method = [methods.direct, methods.mullvadBridges, ...methods.custom].find( - (method) => method.id === id, - ); - - const updatedMethod = useRef(method); - const updateMethod = useCallback( - (method: NewAccessMethodSetting) => (updatedMethod.current = method), - [], - ); + const method = methods.find((method) => method.id === id); - // Contains form submittability to know whether or not to enable the Add/Save button. - const formSubmittable = useSettingsFormSubmittable(); + const updatedMethod = useRef | undefined>(method); const save = useCallback(() => { if (updatedMethod.current !== undefined) { @@ -81,15 +60,20 @@ function AccessMethodForm() { } }, [updatedMethod.current, id]); - const onSave = useCallback(async () => { - if ( - updatedMethod.current !== undefined && - (await testApiAccessMethod(updatedMethod.current as CustomProxy)) - ) { - // Hide the save dialog after 1.5 seconds. - saveScheduler.schedule(save, 1500); - } - }, [updatedMethod, save, history.pop]); + const onSave = useCallback( + async (newMethod: NamedCustomProxy) => { + const enabled = id === undefined ? true : method?.enabled ?? true; + updatedMethod.current = { ...newMethod, enabled }; + if ( + updatedMethod.current !== undefined && + (await testApiAccessMethod(updatedMethod.current as CustomProxy)) + ) { + // Hide the save dialog after 1.5 seconds. + saveScheduler.schedule(save, 1500); + } + }, + [updatedMethod, save, history.pop], + ); const title = getTitle(id === undefined); const subtitle = getSubtitle(id === undefined); @@ -116,15 +100,8 @@ function AccessMethodForm() { {id !== undefined && method === undefined ? ( Failed to open method ) : ( - + )} - - - {messages.gettext('Cancel')} - - {id === undefined ? messages.gettext('Add') : messages.gettext('Save')} - - void, cancel: return [cancelButton]; } } - -interface EditApiAccessMethodImplProps { - method?: AccessMethodSetting; - updateMethod: (method: NewAccessMethodSetting) => void; -} - -function AccessMethodFormImpl(props: EditApiAccessMethodImplProps) { - // Available method types. - const types = useMemo>>( - () => [ - { value: 'shadowsocks', label: 'Shadowsocks' }, - { - value: 'socks5-remote', - label: messages.pgettext('api-access-methods-view', 'SOCKS5 remote'), - }, - { - value: 'socks5-local', - label: messages.pgettext('api-access-methods-view', 'SOCKS5 local'), - }, - ], - [], - ); - const [type, setType] = useState(props.method?.type ?? 'shadowsocks'); - - // State for the name input. - const name = useRef(props.method?.name ?? ''); - const method = useRef(props.method); - - // When the form makes up a valid method the parent is updated. - const onUpdate = useCallback(() => { - if (method.current !== undefined && name.current !== '') { - props.updateMethod({ ...method.current, name: name.current, enabled: true }); - } - }, []); - - const updateName = useCallback( - (value: string) => { - name.current = value; - onUpdate(); - }, - [onUpdate], - ); - - const updateMethod = useCallback( - (value: AccessMethod) => { - method.current = value; - onUpdate(); - }, - [onUpdate], - ); - - return ( - <> - - - - - - - - - {type === 'shadowsocks' && ( - - )} - {type === 'socks5-remote' && ( - - )} - {type === 'socks5-local' && ( - - )} - - ); -} - -interface EditMethodProps { - method?: T; - onUpdate: (method: AccessMethod) => void; -} - -function EditShadowsocks(props: EditMethodProps) { - const [ip, setIp] = useState(props.method?.ip ?? ''); - const [port, setPort] = useState(props.method?.port); - const [password, setPassword] = useState(props.method?.password ?? ''); - const [cipher, setCipher] = useState(props.method?.cipher); - - const ciphers = useMemo( - () => - [ - { value: 'aes-128-cfb', label: 'aes-128-cfb' }, - { value: 'aes-128-cfb1', label: 'aes-128-cfb1' }, - { value: 'aes-128-cfb8', label: 'aes-128-cfb8' }, - { value: 'aes-128-cfb128', label: 'aes-128-cfb128' }, - { value: 'aes-256-cfb', label: 'aes-256-cfb' }, - { value: 'aes-256-cfb1', label: 'aes-256-cfb1' }, - { value: 'aes-256-cfb8', label: 'aes-256-cfb8' }, - { value: 'aes-256-cfb128', label: 'aes-256-cfb128' }, - { value: 'rc4', label: 'rc4' }, - { value: 'rc4-md5', label: 'rc4-md5' }, - { value: 'chacha20', label: 'chacha20' }, - { value: 'salsa20', label: 'salsa20' }, - { value: 'chacha20-ietf', label: 'chacha20-ietf' }, - { value: 'aes-128-gcm', label: 'aes-128-gcm' }, - { value: 'aes-256-gcm', label: 'aes-256-gcm' }, - { value: 'chacha20-ietf-poly1305', label: 'chacha20-ietf-poly1305' }, - { value: 'xchacha20-ietf-poly1305', label: 'xchacha20-ietf-poly1305' }, - { value: 'aes-128-pmac-siv', label: 'aes-128-pmac-siv' }, - { value: 'aes-256-pmac-siv', label: 'aes-256-pmac-siv' }, - ].sort((a, b) => a.label.localeCompare(b.label)), - [], - ); - - // Report back to form component with the method values when all required values are set. - useEffect(() => { - if (ip !== '' && port !== undefined && cipher !== undefined) { - props.onUpdate({ - type: 'shadowsocks', - ip, - port, - password, - cipher, - }); - } - }, [ip, port, password, cipher]); - - return ( - - - - - - - - - - - - - - - - - - ); -} - -function EditSocks5Remote(props: EditMethodProps) { - const [ip, setIp] = useState(props.method?.ip ?? ''); - const [port, setPort] = useState(props.method?.port); - const [authentication, setAuthentication] = useState(props.method?.authentication !== undefined); - const [username, setUsername] = useState(props.method?.authentication?.username ?? ''); - const [password, setPassword] = useState(props.method?.authentication?.password ?? ''); - - // Report back to form component with the method values when all required values are set. - useEffect(() => { - if ( - ip !== '' && - port !== undefined && - (!authentication || (username !== '' && password !== '')) - ) { - props.onUpdate({ - type: 'socks5-remote', - ip, - port, - authentication: authentication ? { username, password } : undefined, - }); - } - }, [ip, port, username, password]); - - return ( - - - - - - - - - - - - - - {authentication && ( - <> - - - - - - - - - )} - - ); -} - -function EditSocks5Local(props: EditMethodProps) { - const [remoteIp, setRemoteIp] = useState(props.method?.remoteIp ?? ''); - const [remotePort, setRemotePort] = useState(props.method?.remotePort); - const [remoteTransportProtocol, setRemoteTransportProtocol] = useState( - props.method?.remoteTransportProtocol ?? 'tcp', - ); - const [localPort, setLocalPort] = useState(props.method?.localPort); - - const remoteTransportProtocols = useMemo>>( - () => [ - { value: 'tcp', label: 'TCP' }, - { value: 'udp', label: 'UDP' }, - ], - [], - ); - - useEffect(() => { - if (remoteIp !== '' && remotePort !== undefined && localPort !== undefined) { - props.onUpdate({ - type: 'socks5-local', - remoteIp, - remotePort, - remoteTransportProtocol, - localPort, - }); - } - }, [remoteIp, remotePort, localPort, remoteTransportProtocol]); - - return ( - <> - - - - - - - - - - - - - - - - - - defaultValue={remoteTransportProtocol} - onUpdate={setRemoteTransportProtocol} - items={remoteTransportProtocols} - /> - - - - ); -} - -function validateIp(ip: string): boolean { - try { - void IpAddress.fromString(ip); - return true; - } catch { - return false; - } -} - -function validatePort(port: number): boolean { - return port > 0 && port <= 65535; -} diff --git a/gui/src/renderer/components/ProxyForm.tsx b/gui/src/renderer/components/ProxyForm.tsx new file mode 100644 index 000000000000..5fbcf34d0f4e --- /dev/null +++ b/gui/src/renderer/components/ProxyForm.tsx @@ -0,0 +1,532 @@ +import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import React from 'react'; + +import { + CustomProxy, + NamedCustomProxy, + RelayProtocol, + ShadowsocksCustomProxy, + Socks5LocalCustomProxy, + Socks5RemoteCustomProxy, +} from '../../shared/daemon-rpc-types'; +import { messages } from '../../shared/gettext'; +import { IpAddress } from '../lib/ip'; +import * as Cell from './cell'; +import { SettingsForm, useSettingsFormSubmittable } from './cell/SettingsForm'; +import { SettingsGroup } from './cell/SettingsGroup'; +import { SettingsRadioGroup } from './cell/SettingsRadioGroup'; +import { SettingsRow } from './cell/SettingsRow'; +import { SettingsSelect, SettingsSelectItem } from './cell/SettingsSelect'; +import { SettingsNumberInput, SettingsTextInput } from './cell/SettingsTextInput'; +import { + SmallButton, + SmallButtonColor, + SmallButtonGroup, + SmallButtonGroupStart, +} from './SmallButton'; + +interface ProxyFormContext { + proxy?: CustomProxy; + setProxy: (proxy: CustomProxy) => void; + onSave: () => void; + onCancel: () => void; + onDelete?: () => void; +} + +const proxyFormContext = React.createContext({ + get proxy(): CustomProxy { + throw new Error('Missing ProxyFromContext provider'); + }, + setProxy(): void { + throw new Error('Missing ProxyFromContext provider'); + }, + onSave(): void { + throw new Error('Missing ProxyFromContext provider'); + }, + onCancel(): void { + throw new Error('Missing ProxyFromContext provider'); + }, + onDelete(): void { + throw new Error('Missing ProxyFromContext provider'); + }, +}); + +interface ProxyFormContextProviderProps { + proxy?: CustomProxy; + onSave: (proxy: CustomProxy) => void; + onCancel: () => void; + onDelete?: () => void; +} + +function ProxyFormContextProvider(props: React.PropsWithChildren) { + const [proxy, setProxy] = useState(props.proxy); + + const onSave = useCallback(() => { + if (proxy !== undefined) { + props.onSave(proxy); + } + }, [proxy, props.onSave]); + + const value = useMemo( + () => ({ proxy, setProxy, onSave, onCancel: props.onCancel, onDelete: props.onDelete }), + [proxy, onSave, props.onCancel, props.onDelete], + ); + + return {props.children}; +} + +export function ProxyForm(props: ProxyFormContextProviderProps) { + return ( + + + + + + + ); +} + +interface NamedProxyFormContext { + name?: string; + setName: (name: string) => void; +} + +const namedProxyFormContext = React.createContext({ + get name(): string { + throw new Error('Missing NamedProxyFromContext provider'); + }, + setName(): void { + throw new Error('Missing NamedProxyFromContext provider'); + }, +}); + +interface NamedProxyFormContainerProps + extends Omit { + proxy?: NamedCustomProxy; + onSave: (proxy: NamedCustomProxy) => void; +} + +export function NamedProxyForm(props: NamedProxyFormContainerProps) { + const { onSave, ...otherProps } = props; + + const [name, setName] = useState(props.proxy?.name ?? ''); + + const save = useCallback( + (proxy: CustomProxy) => { + if (name !== '') { + onSave({ ...proxy, name }); + } + }, + [name, onSave], + ); + + const nameContextValue = useMemo(() => ({ name, setName }), [name]); + + return ( + + + + + + + + + + ); +} + +function ProxyFormNameField() { + const { name, setName } = useContext(namedProxyFormContext); + + return ( + + + + ); +} + +export function ProxyFormButtons() { + const { proxy, onSave, onCancel, onDelete } = useContext(proxyFormContext); + + // Contains form submittability to know whether or not to enable the Add/Save button. + const formSubmittable = useSettingsFormSubmittable(); + + return ( + + {onDelete !== undefined && ( + + + {messages.gettext('Delete')} + + + )} + {messages.gettext('Cancel')} + + {proxy === undefined ? messages.gettext('Add') : messages.gettext('Save')} + + + ); +} + +function ProxyFormInner() { + const { proxy, setProxy } = useContext(proxyFormContext); + + // Available custom proxies + const types = useMemo>>( + () => [ + { value: 'shadowsocks', label: 'Shadowsocks' }, + { + value: 'socks5-remote', + label: messages.pgettext('api-access-methods-view', 'SOCKS5 remote'), + }, + { + value: 'socks5-local', + label: messages.pgettext('api-access-methods-view', 'SOCKS5 local'), + }, + ], + [], + ); + const [type, setType] = useState(proxy?.type ?? 'shadowsocks'); + const proxyRef = useRef(proxy); + + const updateProxy = useCallback( + (value: CustomProxy) => { + proxyRef.current = value; + + // When the form makes up a valid proxy the parent is updated. + if (proxyRef.current !== undefined) { + setProxy(proxyRef.current); + } + }, + [setProxy], + ); + + return ( + <> + + + + + {type === 'shadowsocks' && ( + + )} + {type === 'socks5-remote' && ( + + )} + {type === 'socks5-local' && ( + + )} + + ); +} + +interface EditProxyProps { + proxy?: T; + onUpdate: (proxy: CustomProxy) => void; +} + +function EditShadowsocks(props: EditProxyProps) { + const [ip, setIp] = useState(props.proxy?.ip ?? ''); + const [port, setPort] = useState(props.proxy?.port); + const [password, setPassword] = useState(props.proxy?.password ?? ''); + const [cipher, setCipher] = useState(props.proxy?.cipher); + + const ciphers = useMemo( + () => + [ + { value: 'aes-128-cfb', label: 'aes-128-cfb' }, + { value: 'aes-128-cfb1', label: 'aes-128-cfb1' }, + { value: 'aes-128-cfb8', label: 'aes-128-cfb8' }, + { value: 'aes-128-cfb128', label: 'aes-128-cfb128' }, + { value: 'aes-256-cfb', label: 'aes-256-cfb' }, + { value: 'aes-256-cfb1', label: 'aes-256-cfb1' }, + { value: 'aes-256-cfb8', label: 'aes-256-cfb8' }, + { value: 'aes-256-cfb128', label: 'aes-256-cfb128' }, + { value: 'rc4', label: 'rc4' }, + { value: 'rc4-md5', label: 'rc4-md5' }, + { value: 'chacha20', label: 'chacha20' }, + { value: 'salsa20', label: 'salsa20' }, + { value: 'chacha20-ietf', label: 'chacha20-ietf' }, + { value: 'aes-128-gcm', label: 'aes-128-gcm' }, + { value: 'aes-256-gcm', label: 'aes-256-gcm' }, + { value: 'chacha20-ietf-poly1305', label: 'chacha20-ietf-poly1305' }, + { value: 'xchacha20-ietf-poly1305', label: 'xchacha20-ietf-poly1305' }, + { value: 'aes-128-pmac-siv', label: 'aes-128-pmac-siv' }, + { value: 'aes-256-pmac-siv', label: 'aes-256-pmac-siv' }, + ].sort((a, b) => a.label.localeCompare(b.label)), + [], + ); + + // Report back to form component with the proxy values when all required values are set. + useEffect(() => { + if (ip !== '' && port !== undefined && cipher !== undefined) { + props.onUpdate({ + type: 'shadowsocks', + ip, + port, + password, + cipher, + }); + } + }, [ip, port, password, cipher]); + + return ( + + + + + + + + + + + + + + + + + + ); +} + +function EditSocks5Remote(props: EditProxyProps) { + const [ip, setIp] = useState(props.proxy?.ip ?? ''); + const [port, setPort] = useState(props.proxy?.port); + const [authentication, setAuthentication] = useState(props.proxy?.authentication !== undefined); + const [username, setUsername] = useState(props.proxy?.authentication?.username ?? ''); + const [password, setPassword] = useState(props.proxy?.authentication?.password ?? ''); + + // Report back to form component with the proxy values when all required values are set. + useEffect(() => { + if ( + ip !== '' && + port !== undefined && + (!authentication || (username !== '' && password !== '')) + ) { + props.onUpdate({ + type: 'socks5-remote', + ip, + port, + authentication: authentication ? { username, password } : undefined, + }); + } + }, [ip, port, username, password]); + + return ( + + + + + + + + + + + + + + {authentication && ( + <> + + + + + + + + + )} + + ); +} + +function EditSocks5Local(props: EditProxyProps) { + const [remoteIp, setRemoteIp] = useState(props.proxy?.remoteIp ?? ''); + const [remotePort, setRemotePort] = useState(props.proxy?.remotePort); + const [remoteTransportProtocol, setRemoteTransportProtocol] = useState( + props.proxy?.remoteTransportProtocol ?? 'tcp', + ); + const [localPort, setLocalPort] = useState(props.proxy?.localPort); + + const remoteTransportProtocols = useMemo>>( + () => [ + { value: 'tcp', label: 'TCP' }, + { value: 'udp', label: 'UDP' }, + ], + [], + ); + + useEffect(() => { + if (remoteIp !== '' && remotePort !== undefined && localPort !== undefined) { + props.onUpdate({ + type: 'socks5-local', + remoteIp, + remotePort, + remoteTransportProtocol, + localPort, + }); + } + }, [remoteIp, remotePort, localPort, remoteTransportProtocol]); + + return ( + <> + + + + + + + + + + + + + + + + + + defaultValue={remoteTransportProtocol} + onUpdate={setRemoteTransportProtocol} + items={remoteTransportProtocols} + /> + + + + ); +} + +function validateIp(ip: string): boolean { + try { + void IpAddress.fromString(ip); + return true; + } catch { + return false; + } +} + +function validatePort(port: number): boolean { + return port > 0 && port <= 65535; +} From 0470304a2b365c5062b26f9d817e9f9a0c23dad4 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 9 Apr 2024 08:09:38 +0200 Subject: [PATCH 029/214] Add view for editing custom bridge --- gui/src/renderer/components/AppRouter.tsx | 2 + .../renderer/components/EditCustomBridge.tsx | 116 ++++++++++++++++++ gui/src/renderer/lib/routes.ts | 1 + gui/src/shared/localization-contexts.ts | 1 + 4 files changed, 120 insertions(+) create mode 100644 gui/src/renderer/components/EditCustomBridge.tsx diff --git a/gui/src/renderer/components/AppRouter.tsx b/gui/src/renderer/components/AppRouter.tsx index 9e4a13adc8fb..0f85601a92d7 100644 --- a/gui/src/renderer/components/AppRouter.tsx +++ b/gui/src/renderer/components/AppRouter.tsx @@ -12,6 +12,7 @@ import Connect from './Connect'; import Debug from './Debug'; import { DeviceRevokedView } from './DeviceRevokedView'; import { EditApiAccessMethod } from './EditApiAccessMethod'; +import { EditCustomBridge } from './EditCustomBridge'; import { SetupFinished, TimeAdded, @@ -92,6 +93,7 @@ export default function AppRouter() { + diff --git a/gui/src/renderer/components/EditCustomBridge.tsx b/gui/src/renderer/components/EditCustomBridge.tsx new file mode 100644 index 000000000000..2692217ea0a7 --- /dev/null +++ b/gui/src/renderer/components/EditCustomBridge.tsx @@ -0,0 +1,116 @@ +import { useCallback } from 'react'; + +import { CustomProxy } from '../../shared/daemon-rpc-types'; +import { messages } from '../../shared/gettext'; +import { useBridgeSettingsUpdater } from '../lib/constraint-updater'; +import { useHistory } from '../lib/history'; +import { useBoolean } from '../lib/utilityHooks'; +import { useSelector } from '../redux/store'; +import { SettingsForm } from './cell/SettingsForm'; +import { BackAction } from './KeyboardNavigation'; +import { Layout, SettingsContainer } from './Layout'; +import { ModalAlert, ModalAlertType } from './Modal'; +import { NavigationBar, NavigationContainer, NavigationItems, TitleBarItem } from './NavigationBar'; +import { ProxyForm } from './ProxyForm'; +import SettingsHeader, { HeaderTitle } from './SettingsHeader'; +import { StyledContent, StyledNavigationScrollbars, StyledSettingsContent } from './SettingsStyles'; +import { SmallButton, SmallButtonColor } from './SmallButton'; + +export function EditCustomBridge() { + return ( + + + + ); +} + +function CustomBridgeForm() { + const history = useHistory(); + const bridgeSettingsUpdater = useBridgeSettingsUpdater(); + const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); + + const [deleteDialogVisible, showDeleteDialog, hideDeleteDialog] = useBoolean(); + + // If there are no custom bridge settings, we should prompt the user to add a custom bridge. + // Otherwise, we should prompt them to edit the existing custom bridge settings. + const title = + bridgeSettings.custom === undefined + ? messages.pgettext('custom-bridge', 'Add custom bridge') + : messages.pgettext('custom-bridge', 'Edit custom bridge'); + + const onSave = useCallback( + (newBridge: CustomProxy) => { + void bridgeSettingsUpdater((bridgeSettings) => { + bridgeSettings.type = 'custom'; + bridgeSettings.custom = newBridge; + return bridgeSettings; + }); + history.pop(); + }, + [bridgeSettingsUpdater, history.pop], + ); + + const onDelete = useCallback(() => { + if (bridgeSettings.custom !== undefined) { + hideDeleteDialog(); + void bridgeSettingsUpdater((bridgeSettings) => { + bridgeSettings.type = 'normal'; + delete bridgeSettings.custom; + return bridgeSettings; + }); + history.pop(); + } + }, [bridgeSettingsUpdater, history.pop]); + + return ( + + + + + + + {title} + + + + + + + {title} + + + + + + + + {messages.gettext('Cancel')} + , + + {messages.gettext('Delete')} + , + ]} + close={hideDeleteDialog} + title={messages.pgettext('custom-bridge', 'Delete custom bridge?')} + message={messages.pgettext( + 'custom-bridge', + 'Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead.', + )} + /> + + + + + + + ); +} diff --git a/gui/src/renderer/lib/routes.ts b/gui/src/renderer/lib/routes.ts index 499157976ac7..1f2a3c28a74e 100644 --- a/gui/src/renderer/lib/routes.ts +++ b/gui/src/renderer/lib/routes.ts @@ -25,5 +25,6 @@ export enum RoutePath { problemReport = '/settings/support/problem-report', debug = '/settings/debug', selectLocation = '/select-location', + editCustomBridge = '/select-location/edit-custom-bridge', filter = '/select-location/filter', } diff --git a/gui/src/shared/localization-contexts.ts b/gui/src/shared/localization-contexts.ts index 71a663def05d..f30212025cea 100644 --- a/gui/src/shared/localization-contexts.ts +++ b/gui/src/shared/localization-contexts.ts @@ -15,6 +15,7 @@ export type LocalizationContexts = | 'account-expiry' | 'select-location-view' | 'select-location-nav' + | 'custom-bridge' | 'filter-view' | 'filter-nav' | 'settings-view' From 70cc5dd60d9e84432c24adcf10ea28530f809816 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 9 Apr 2024 15:52:26 +0200 Subject: [PATCH 030/214] Change special location item icon --- gui/assets/images/icon-nearest.svg | 6 ------ gui/src/config.json | 1 + .../renderer/components/RelayStatusIndicator.tsx | 12 +++++++++++- .../select-location/SpecialLocationList.tsx | 14 +++----------- 4 files changed, 15 insertions(+), 18 deletions(-) delete mode 100644 gui/assets/images/icon-nearest.svg diff --git a/gui/assets/images/icon-nearest.svg b/gui/assets/images/icon-nearest.svg deleted file mode 100644 index badbf3674d30..000000000000 --- a/gui/assets/images/icon-nearest.svg +++ /dev/null @@ -1,6 +0,0 @@ - - icon-nearest - Mullvad VPN app - - - diff --git a/gui/src/config.json b/gui/src/config.json index 8db99ed2fb8f..c1ddcd6cac17 100644 --- a/gui/src/config.json +++ b/gui/src/config.json @@ -17,6 +17,7 @@ "yellow": "rgb(255, 213, 36)", "black": "rgb(0, 0, 0)", "white": "rgb(255, 255, 255)", + "white90": "rgba(255, 255, 255, 0.9)", "white80": "rgba(255, 255, 255, 0.8)", "white60": "rgba(255, 255, 255, 0.6)", "white50": "rgba(255, 255, 255, 0.5)", diff --git a/gui/src/renderer/components/RelayStatusIndicator.tsx b/gui/src/renderer/components/RelayStatusIndicator.tsx index 549fec4e7037..1060121b9e09 100644 --- a/gui/src/renderer/components/RelayStatusIndicator.tsx +++ b/gui/src/renderer/components/RelayStatusIndicator.tsx @@ -1,13 +1,19 @@ import styled from 'styled-components'; +import { Styles } from 'styled-components/dist/types'; import { colors } from '../../config.json'; import * as Cell from './cell'; -const StyledRelayStatus = styled.div<{ $active: boolean }>((props) => ({ +const indicatorStyles: Styles< + React.DetailedHTMLProps, HTMLDivElement> +> = { width: '16px', height: '16px', borderRadius: '8px', margin: '0 12px 0 4px', +}; + +const StyledRelayStatus = styled.div<{ $active: boolean }>(indicatorStyles, (props) => ({ backgroundColor: props.$active ? colors.green90 : colors.red95, })); @@ -28,3 +34,7 @@ export default function RelayStatusIndicator(props: IProps) { ); } + +export const SpecialLocationIndicator = styled.div(indicatorStyles, { + backgroundColor: colors.white90, +}); diff --git a/gui/src/renderer/components/select-location/SpecialLocationList.tsx b/gui/src/renderer/components/select-location/SpecialLocationList.tsx index 2e658c60463d..60b1e0c6664b 100644 --- a/gui/src/renderer/components/select-location/SpecialLocationList.tsx +++ b/gui/src/renderer/components/select-location/SpecialLocationList.tsx @@ -6,10 +6,9 @@ import { messages } from '../../../shared/gettext'; import { useHistory } from '../../lib/history'; import { RoutePath } from '../../lib/routes'; import { useSelector } from '../../redux/store'; -import * as Cell from '../cell'; import ImageView from '../ImageView'; import InfoButton from '../InfoButton'; -import RelayStatusIndicator from '../RelayStatusIndicator'; +import { SpecialLocationIndicator } from '../RelayStatusIndicator'; import { getButtonColor, StyledHoverInfoButton, @@ -36,12 +35,6 @@ export default function SpecialLocationList({ source, ...props }: SpecialLoca ); } -const StyledSpecialLocationIcon = styled(Cell.Icon)({ - flex: 0, - marginLeft: '2px', - marginRight: '8px', -}); - const StyledSpecialLocationInfoButton = styled(InfoButton)({ padding: '0 25px', margin: 0 }); const StyledSpecialLocationSideButton = styled(ImageView)({ padding: '0 3px' }); @@ -73,13 +66,12 @@ export interface SpecialLocationRowInnerProps export function AutomaticLocationRow( props: SpecialLocationRowInnerProps, ) { - const icon = props.source.selected ? 'icon-tick' : 'icon-nearest'; const selectedRef = props.source.selected ? props.selectedElementRef : undefined; const background = getButtonColor(props.source.selected, 0, props.source.disabled); return ( - + {props.source.label} - + {props.source.label} Date: Tue, 9 Apr 2024 17:14:55 +0200 Subject: [PATCH 031/214] Update translations --- gui/locales/messages.pot | 46 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index 35df069d1846..da18e9b0210a 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -129,6 +129,9 @@ msgstr "" msgid "Dismiss" msgstr "" +msgid "Enable" +msgstr "" + msgid "Enable anyway" msgstr "" @@ -221,6 +224,8 @@ msgstr "" msgid "TCP" msgstr "" +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "" @@ -623,6 +628,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -639,6 +648,22 @@ msgctxt "connection-info" msgid "Out" msgstr "" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "" + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1147,6 +1172,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1193,6 +1222,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "" +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "" + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "" @@ -1256,6 +1291,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "" + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1275,6 +1314,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "" + msgctxt "select-location-view" msgid "Custom lists" msgstr "" @@ -2145,9 +2188,6 @@ msgstr "" msgid "Edit name" msgstr "" -msgid "Enable" -msgstr "" - msgid "Enter MTU" msgstr "" From 8f5bd86e79e5541b9ec521d18fed6235ad369994 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 9 Apr 2024 17:18:06 +0200 Subject: [PATCH 032/214] Add changelog item about custom bridge --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4217f6079f52..41c1337065cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Line wrap the file at 100 chars. Th - Add automatic MTU detection for desktop platforms. This currently only uses information about dropped packets and does not take fragmentation into account. - Add ability to import server IP overrides in GUI. +- Add custom bridge settings in GUI. ### Changed - Change default obfuscation setting to `auto`. From 0f124473959628b95adb3d65ee167ef187ddcf6a Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 11 Apr 2024 17:19:59 +0200 Subject: [PATCH 033/214] Make custom bridge hover button persistent --- .../components/select-location/SpecialLocationList.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gui/src/renderer/components/select-location/SpecialLocationList.tsx b/gui/src/renderer/components/select-location/SpecialLocationList.tsx index 60b1e0c6664b..9579a6b4b511 100644 --- a/gui/src/renderer/components/select-location/SpecialLocationList.tsx +++ b/gui/src/renderer/components/select-location/SpecialLocationList.tsx @@ -100,6 +100,8 @@ export function CustomExitLocationRow(props: SpecialLocationRowInnerProps, ) { @@ -124,7 +126,7 @@ export function CustomBridgeLocationRow( {props.source.label} - Date: Thu, 11 Apr 2024 18:00:12 +0200 Subject: [PATCH 034/214] Convert comment to JSDoc comments --- gui/src/renderer/redux/settings/reducers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts index e64b0d63237f..3c502ec103cb 100644 --- a/gui/src/renderer/redux/settings/reducers.ts +++ b/gui/src/renderer/redux/settings/reducers.ts @@ -42,8 +42,9 @@ export type NormalRelaySettingsRedux = { export type NormalBridgeSettingsRedux = { location: LiftedConstraint; - // Providers and ownership are used to filter bridges and as bridge constraints for the daemon. + /** Providers are used to filter bridges and as bridge constraints for the daemon. */ providers: string[]; + /** Ownership is used to filter bridges and as bridge constraints for the daemon. */ ownership: Ownership; }; From 59b15ef4e02c05c2fef0a1843872b022832e415d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Thu, 11 Apr 2024 17:18:35 +0200 Subject: [PATCH 035/214] Change WireGuard key rotation interval to 30 days for desktop + Android --- mullvad-types/src/wireguard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs index 3bfe7f79f2dd..c85860a776aa 100644 --- a/mullvad-types/src/wireguard.rs +++ b/mullvad-types/src/wireguard.rs @@ -7,7 +7,7 @@ use std::{fmt, str::FromStr, time::Duration}; use talpid_types::net::wireguard; pub const MIN_ROTATION_INTERVAL: Duration = Duration::from_secs(1 * 24 * 60 * 60); -pub const MAX_ROTATION_INTERVAL: Duration = Duration::from_secs(14 * 24 * 60 * 60); +pub const MAX_ROTATION_INTERVAL: Duration = Duration::from_secs(30 * 24 * 60 * 60); pub const DEFAULT_ROTATION_INTERVAL: Duration = MAX_ROTATION_INTERVAL; /// Whether to enable or disable quantum resistant tunnels when the setting is set to From 4a89ddad7dc974bc11e9af91e15dedcfc3850120 Mon Sep 17 00:00:00 2001 From: Niklas Berglund Date: Tue, 2 Apr 2024 11:07:18 +0200 Subject: [PATCH 036/214] Add time left properly displayed test --- .../Classes/AccessbilityIdentifier.swift | 1 + .../Account/AccountExpiryRow.swift | 1 + ios/MullvadVPNUITests/AccountTests.swift | 12 +++++++++ .../Networking/MullvadAPIWrapper.swift | 6 +++-- ios/MullvadVPNUITests/Pages/AccountPage.swift | 25 +++++++++++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index 371412c97581..cda791d04258 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -57,6 +57,7 @@ public enum AccessibilityIdentifier: String { case quantumResistantTunnelCell // Labels + case accountPagePaidUntilLabel case headerDeviceNameLabel case connectionStatusConnectedLabel case connectionStatusNotConnectedLabel diff --git a/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift b/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift index b87e7c33cf2a..00f0ebbedf77 100644 --- a/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift +++ b/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift @@ -62,6 +62,7 @@ class AccountExpiryRow: UIView { valueLabel.translatesAutoresizingMaskIntoConstraints = false valueLabel.font = UIFont.systemFont(ofSize: 17) valueLabel.textColor = .white + valueLabel.accessibilityIdentifier = .accountPagePaidUntilLabel return valueLabel }() diff --git a/ios/MullvadVPNUITests/AccountTests.swift b/ios/MullvadVPNUITests/AccountTests.swift index b3b47dc858ff..f78235360154 100644 --- a/ios/MullvadVPNUITests/AccountTests.swift +++ b/ios/MullvadVPNUITests/AccountTests.swift @@ -89,4 +89,16 @@ class AccountTests: LoggedOutUITestCase { XCTAssertEqual(try MullvadAPIWrapper().getDevices(newAccountNumber).count, 0) try MullvadAPIWrapper().deleteAccount(newAccountNumber) } + + func testTimeLeft() throws { + login(accountNumber: hasTimeAccountNumber) + + let accountExpiry = try MullvadAPIWrapper().getAccountExpiry(hasTimeAccountNumber) + + HeaderBar(app) + .tapAccountButton() + + AccountPage(app) + .verifyPaidUntil(accountExpiry) + } } diff --git a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift index b3d7332cedfa..25439c987a70 100644 --- a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift +++ b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift @@ -88,9 +88,11 @@ class MullvadAPIWrapper { } } - func getAccountExpiry(_ account: String) throws -> UInt64 { + func getAccountExpiry(_ account: String) throws -> Date { do { - return try mullvadAPI.getExpiry(forAccount: account) + let accountExpiryTimestamp = Double(try mullvadAPI.getExpiry(forAccount: account)) + let accountExpiryDate = Date(timeIntervalSince1970: accountExpiryTimestamp) + return accountExpiryDate } catch { throw MullvadAPIError.requestError } diff --git a/ios/MullvadVPNUITests/Pages/AccountPage.swift b/ios/MullvadVPNUITests/Pages/AccountPage.swift index 8ff5e935af7b..b076c09b99d8 100644 --- a/ios/MullvadVPNUITests/Pages/AccountPage.swift +++ b/ios/MullvadVPNUITests/Pages/AccountPage.swift @@ -41,4 +41,29 @@ class AccountPage: Page { app.buttons[AccessibilityIdentifier.deleteButton.rawValue].tap() return self } + + @discardableResult func verifyPaidUntil(_ date: Date) -> Self { + // Strip seconds from date, since the app don't display seconds + let calendar = Calendar.current + var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date) + components.second = 0 + guard let strippedDate = calendar.date(from: components) else { + XCTFail("Failed to remove seconds from date") + return self + } + + let paidUntilLabelText = app.staticTexts[AccessibilityIdentifier.accountPagePaidUntilLabel].label + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + dateFormatter.timeStyle = .short + + guard let paidUntilLabelDate = dateFormatter.date(from: paidUntilLabelText) else { + XCTFail("Failed to convert presented date to Date object") + return self + } + + XCTAssertEqual(strippedDate, paidUntilLabelDate) + + return self + } } From 14c8897713c8ed2c3ebb336f4e9a490266099dad Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 12 Apr 2024 11:54:10 +0200 Subject: [PATCH 037/214] Revert "Use faster GH runners for slow jobs" This reverts commit cae065cd6dfb3ee98682114c9f7ccb709e9f9772. Reverted due to unreasonable cost. --- .github/workflows/android-app.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/android-app.yml b/.github/workflows/android-app.yml index 54c00cbc59c6..eee7ebfa5e51 100644 --- a/.github/workflows/android-app.yml +++ b/.github/workflows/android-app.yml @@ -138,7 +138,7 @@ jobs: build-native: name: Build native needs: prepare - runs-on: ubuntu-latest-large + runs-on: ubuntu-latest container: image: "${{ needs.prepare.outputs.container_image }}" strategy: @@ -249,7 +249,7 @@ jobs: build-app: name: Build app needs: [prepare, generate-debug-keystore] - runs-on: ubuntu-latest-large + runs-on: ubuntu-latest container: image: ${{ needs.prepare.outputs.container_image }} steps: @@ -341,7 +341,7 @@ jobs: build-instrumented-tests: name: Build instrumented test packages needs: [prepare, generate-debug-keystore] - runs-on: ubuntu-latest-large + runs-on: ubuntu-latest container: image: ${{ needs.prepare.outputs.container_image }} strategy: From e8407a85589b5cb7794a441a30b87e5eb9ba3b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls?= Date: Thu, 11 Apr 2024 18:03:53 +0200 Subject: [PATCH 038/214] Bump key rotation interval to 30 days on iOS --- ios/MullvadVPN/TunnelManager/WgKeyRotation.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/MullvadVPN/TunnelManager/WgKeyRotation.swift b/ios/MullvadVPN/TunnelManager/WgKeyRotation.swift index bca8bb0b33a5..89474e5afd76 100644 --- a/ios/MullvadVPN/TunnelManager/WgKeyRotation.swift +++ b/ios/MullvadVPN/TunnelManager/WgKeyRotation.swift @@ -18,7 +18,7 @@ import WireGuardKitTypes struct WgKeyRotation { /// Private key rotation interval counted from the time when the key was successfully pushed /// to the backend. - public static let rotationInterval: Duration = .days(14) + public static let rotationInterval: Duration = .days(30) /// Private key rotation retry interval counted from the time when the last rotation /// attempt took place. From fe15f605ef4d05a6c655209f0a10196141ce6021 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Fri, 12 Apr 2024 12:01:09 +0200 Subject: [PATCH 039/214] Add missing translations for some strings --- gui/locales/messages.pot | 12 +++++++++++- gui/src/renderer/components/ApiAccessMethods.tsx | 12 ++++++++---- gui/src/renderer/components/EditApiAccessMethod.tsx | 10 +++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index da18e9b0210a..1873be203877 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -129,6 +129,9 @@ msgstr "" msgid "Dismiss" msgstr "" +msgid "Edit" +msgstr "" + msgid "Enable" msgstr "" @@ -224,6 +227,9 @@ msgstr "" msgid "TCP" msgstr "" +msgid "Test" +msgstr "" + #. Warning shown in dialog to users when they enable setting that increases #. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. @@ -248,6 +254,9 @@ msgstr "" msgid "UNSECURED CONNECTION" msgstr "" +msgid "Use" +msgstr "" + msgid "Username" msgstr "" @@ -383,8 +392,9 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." +msgid "Clicking “%(save)s” changes the in use method." msgstr "" msgctxt "api-access-methods-view" diff --git a/gui/src/renderer/components/ApiAccessMethods.tsx b/gui/src/renderer/components/ApiAccessMethods.tsx index ccec1ce823fd..78f0a11e7711 100644 --- a/gui/src/renderer/components/ApiAccessMethods.tsx +++ b/gui/src/renderer/components/ApiAccessMethods.tsx @@ -200,11 +200,15 @@ function ApiAccessMethod(props: ApiAccessMethodProps) { const items: Array = [ { type: 'item' as const, - label: 'Use', + label: messages.gettext('Use'), disabled: props.inUse, onClick: setApiAccessMethod, }, - { type: 'item' as const, label: 'Test', onClick: () => testApiAccessMethod(props.method.id) }, + { + type: 'item' as const, + label: messages.gettext('Test'), + onClick: () => testApiAccessMethod(props.method.id), + }, ]; // Edit and Delete shouldn't be available for direct and bridges. @@ -213,7 +217,7 @@ function ApiAccessMethod(props: ApiAccessMethodProps) { { type: 'separator' as const }, { type: 'item' as const, - label: 'Edit', + label: messages.gettext('Edit'), onClick: () => history.push( generateRoutePath(RoutePath.editApiAccessMethods, { id: props.method.id }), @@ -221,7 +225,7 @@ function ApiAccessMethod(props: ApiAccessMethodProps) { }, { type: 'item' as const, - label: 'Delete', + label: messages.gettext('Delete'), onClick: showRemoveConfirmation, }, ); diff --git a/gui/src/renderer/components/EditApiAccessMethod.tsx b/gui/src/renderer/components/EditApiAccessMethod.tsx index 59a5f2ff3727..2c5384254325 100644 --- a/gui/src/renderer/components/EditApiAccessMethod.tsx +++ b/gui/src/renderer/components/EditApiAccessMethod.tsx @@ -198,9 +198,13 @@ function getTestingDialogSubTitle(type: ModalAlertType, newMethod: boolean, name ), { name }, ) - : messages.pgettext( - 'api-access-methods-view', - 'Clicking “Save” changes the in use method.', + : sprintf( + // TRANSLATORS: %(save)s - Will be replaced with the translation for the word "Save". + messages.pgettext( + 'api-access-methods-view', + 'Clicking “%(save)s” changes the in use method.', + ), + { save: messages.gettext('Save') }, ); default: return undefined; From 32ad869dd3074a3ef6f4f1427600ec0821aaaad6 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 9 Apr 2024 16:30:30 +0200 Subject: [PATCH 040/214] Make sure we clear all settings between e2e tests --- test/test-manager/src/tests/mod.rs | 63 ++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/test/test-manager/src/tests/mod.rs b/test/test-manager/src/tests/mod.rs index 32a32cb44a09..0ccc900c5fe7 100644 --- a/test/test-manager/src/tests/mod.rs +++ b/test/test-manager/src/tests/mod.rs @@ -77,58 +77,95 @@ pub async fn cleanup_after_test(mullvad_client: &mut MullvadProxyClient) -> anyh helpers::disconnect_and_wait(mullvad_client).await?; - let default_settings = mullvad_types::settings::Settings::default(); + // Bring all the settings into scope so we remember to reset them. + let mullvad_types::settings::Settings { + relay_settings, + bridge_settings, + obfuscation_settings, + bridge_state, + custom_lists, + api_access_methods, + allow_lan, + block_when_disconnected, + auto_connect, + tunnel_options, + relay_overrides, + show_beta_releases, + settings_version: _, // N/A + } = Default::default(); mullvad_client - .set_relay_settings(default_settings.relay_settings) + .set_relay_settings(relay_settings) .await .context("Could not set relay settings")?; + + let _ = relay_overrides; + mullvad_client + .clear_all_relay_overrides() + .await + .context("Could not set relay overrides")?; + mullvad_client - .set_auto_connect(default_settings.auto_connect) + .set_auto_connect(auto_connect) .await .context("Could not set auto connect in cleanup")?; + mullvad_client - .set_allow_lan(default_settings.allow_lan) + .set_allow_lan(allow_lan) .await .context("Could not set allow lan in cleanup")?; + mullvad_client - .set_show_beta_releases(default_settings.show_beta_releases) + .set_show_beta_releases(show_beta_releases) .await .context("Could not set show beta releases in cleanup")?; + mullvad_client - .set_bridge_state(default_settings.bridge_state) + .set_bridge_state(bridge_state) .await .context("Could not set bridge state in cleanup")?; + mullvad_client - .set_bridge_settings(default_settings.bridge_settings.clone()) + .set_bridge_settings(bridge_settings.clone()) .await .context("Could not set bridge settings in cleanup")?; + mullvad_client - .set_obfuscation_settings(default_settings.obfuscation_settings.clone()) + .set_obfuscation_settings(obfuscation_settings.clone()) .await .context("Could set obfuscation settings in cleanup")?; + mullvad_client - .set_block_when_disconnected(default_settings.block_when_disconnected) + .set_block_when_disconnected(block_when_disconnected) .await .context("Could not set block when disconnected setting in cleanup")?; - #[cfg(target_os = "windows")] + mullvad_client .clear_split_tunnel_apps() .await .context("Could not clear split tunnel apps in cleanup")?; - #[cfg(target_os = "linux")] + mullvad_client .clear_split_tunnel_processes() .await .context("Could not clear split tunnel processes in cleanup")?; + mullvad_client - .set_dns_options(default_settings.tunnel_options.dns_options.clone()) + .set_dns_options(tunnel_options.dns_options.clone()) .await .context("Could not clear dns options in cleanup")?; + mullvad_client - .set_quantum_resistant_tunnel(default_settings.tunnel_options.wireguard.quantum_resistant) + .set_quantum_resistant_tunnel(tunnel_options.wireguard.quantum_resistant) .await .context("Could not clear PQ options in cleanup")?; + for custom_list in custom_lists { + mullvad_client + .delete_custom_list(custom_list.name) + .await + .context("Could not remove custom list")?; + } + Ok(()) } From 9be8fb440ebefad790e8a09bd0ce1252575852be Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 9 Apr 2024 16:30:55 +0200 Subject: [PATCH 041/214] Add ClearCustomApiAccessMethods rpc call --- mullvad-daemon/src/access_method.rs | 11 +++++++++++ mullvad-daemon/src/lib.rs | 11 +++++++++++ mullvad-daemon/src/management_interface.rs | 10 ++++++++++ .../proto/management_interface.proto | 1 + mullvad-management-interface/src/client.rs | 9 +++++++++ mullvad-types/src/access_method.rs | 6 ++++++ test/test-manager/src/tests/mod.rs | 11 +++++++++++ 7 files changed, 59 insertions(+) diff --git a/mullvad-daemon/src/access_method.rs b/mullvad-daemon/src/access_method.rs index bc843f529e2b..edae229ae00f 100644 --- a/mullvad-daemon/src/access_method.rs +++ b/mullvad-daemon/src/access_method.rs @@ -128,6 +128,17 @@ where Ok(()) } + /// Remove all custom [`AccessMethodSetting`]. + pub async fn clear_custom_api_access_methods(&mut self) -> Result<(), Error> { + self.settings + .update(|settings: &mut Settings| { + settings.api_access_methods.clear_custom(); + }) + .await?; + + Ok(()) + } + /// Return the [`AccessMethodSetting`] which is currently used to access the /// Mullvad API. pub async fn get_current_access_method(&self) -> Result { diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index c3c64ebacfa6..da02cb6ffc95 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -292,6 +292,8 @@ pub enum DaemonCommand { SetApiAccessMethod(ResponseTx<(), Error>, mullvad_types::access_method::Id), /// Edit an API access method UpdateApiAccessMethod(ResponseTx<(), Error>, AccessMethodSetting), + /// Remove all custom API access methods + ClearCustomApiAccessMethods(ResponseTx<(), Error>), /// Get the currently used API access method GetCurrentAccessMethod(ResponseTx), /// Test an API access method @@ -1260,6 +1262,7 @@ where } RemoveApiAccessMethod(tx, method) => self.on_remove_api_access_method(tx, method).await, UpdateApiAccessMethod(tx, method) => self.on_update_api_access_method(tx, method).await, + ClearCustomApiAccessMethods(tx) => self.on_clear_custom_api_access_methods(tx).await, GetCurrentAccessMethod(tx) => self.on_get_current_api_access_method(tx), SetApiAccessMethod(tx, method) => self.on_set_api_access_method(tx, method).await, TestApiAccessMethodById(tx, method) => self.on_test_api_access_method(tx, method).await, @@ -2482,6 +2485,14 @@ where Self::oneshot_send(tx, result, "update_api_access_method response"); } + async fn on_clear_custom_api_access_methods(&mut self, tx: ResponseTx<(), Error>) { + let result = self + .clear_custom_api_access_methods() + .await + .map_err(Error::AccessMethodError); + Self::oneshot_send(tx, result, "clear_custom_api_access_methods response"); + } + fn on_get_current_api_access_method(&mut self, tx: ResponseTx) { let handle = self.access_mode_handler.clone(); tokio::spawn(async move { diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index ee0fadaa0eb4..52c2c50d4cc2 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -663,6 +663,16 @@ impl ManagementService for ManagementServiceImpl { .map_err(map_daemon_error) } + async fn clear_custom_api_access_methods(&self, _: Request<()>) -> ServiceResult<()> { + log::debug!("clear_custom_api_access_methods"); + let (tx, rx) = oneshot::channel(); + self.send_command_to_daemon(DaemonCommand::ClearCustomApiAccessMethods(tx))?; + self.wait_for_result(rx) + .await? + .map(Response::new) + .map_err(map_daemon_error) + } + /// Return the [`types::AccessMethodSetting`] which the daemon is using to /// connect to the Mullvad API. async fn get_current_api_access_method( diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 7fbdb6eba672..cd3e6afaac0c 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -79,6 +79,7 @@ service ManagementService { rpc RemoveApiAccessMethod(UUID) returns (google.protobuf.Empty) {} rpc SetApiAccessMethod(UUID) returns (google.protobuf.Empty) {} rpc UpdateApiAccessMethod(AccessMethodSetting) returns (google.protobuf.Empty) {} + rpc ClearCustomApiAccessMethods(google.protobuf.Empty) returns (google.protobuf.Empty) {} rpc GetCurrentApiAccessMethod(google.protobuf.Empty) returns (AccessMethodSetting) {} rpc TestCustomApiAccessMethod(CustomProxy) returns (google.protobuf.BoolValue) {} rpc TestApiAccessMethodById(UUID) returns (google.protobuf.BoolValue) {} diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index fd6c56e19fb2..93a446cbc9e6 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -575,6 +575,15 @@ impl MullvadProxyClient { .map(drop) } + /// Remove all custom API access methods. + pub async fn clear_custom_access_methods(&mut self) -> Result<()> { + self.0 + .clear_custom_api_access_methods(()) + .await + .map_err(Error::Rpc) + .map(drop) + } + /// Set the [`AccessMethod`] which [`ApiConnectionModeProvider`] should /// pick. /// diff --git a/mullvad-types/src/access_method.rs b/mullvad-types/src/access_method.rs index c5f0b4185971..c42b099baeb5 100644 --- a/mullvad-types/src/access_method.rs +++ b/mullvad-types/src/access_method.rs @@ -74,6 +74,12 @@ impl Settings { updated } + /// Remove all custom access methods. + pub fn clear_custom(&mut self) { + self.custom.clear(); + self.ensure_consistent_state(); + } + /// Check that `self` contains atleast one enabled access methods. If not, /// the `Direct` access method is re-enabled. fn ensure_consistent_state(&mut self) { diff --git a/test/test-manager/src/tests/mod.rs b/test/test-manager/src/tests/mod.rs index 0ccc900c5fe7..e99300232b9f 100644 --- a/test/test-manager/src/tests/mod.rs +++ b/test/test-manager/src/tests/mod.rs @@ -94,6 +94,17 @@ pub async fn cleanup_after_test(mullvad_client: &mut MullvadProxyClient) -> anyh settings_version: _, // N/A } = Default::default(); + mullvad_client + .clear_custom_access_methods() + .await + .context("Could not clear custom api access methods")?; + for access_method in api_access_methods.iter() { + mullvad_client + .update_access_method(access_method.clone()) + .await + .context("Could not reset default access method")?; + } + mullvad_client .set_relay_settings(relay_settings) .await From eb3c0562b17f3e4928a98c13ac29b3a582a6977c Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 10 Apr 2024 16:30:38 +0200 Subject: [PATCH 042/214] Add ClearCustomLists RPC call --- mullvad-daemon/src/custom_list.rs | 113 ++++++++++++------ mullvad-daemon/src/lib.rs | 8 ++ mullvad-daemon/src/management_interface.rs | 10 ++ .../proto/management_interface.proto | 1 + mullvad-management-interface/src/client.rs | 9 ++ mullvad-types/src/custom_list.rs | 5 + test/test-manager/src/tests/mod.rs | 11 +- 7 files changed, 115 insertions(+), 42 deletions(-) diff --git a/mullvad-daemon/src/custom_list.rs b/mullvad-daemon/src/custom_list.rs index c15fd14a701d..df9f30a51921 100644 --- a/mullvad-daemon/src/custom_list.rs +++ b/mullvad-daemon/src/custom_list.rs @@ -10,6 +10,9 @@ impl Daemon where L: EventListener + Clone + Send + 'static, { + /// Create a new custom list. + /// + /// Returns an error if the name is not unique. pub async fn create_custom_list(&mut self, name: String) -> Result { if self .settings @@ -33,6 +36,9 @@ where Ok(id) } + /// Update a custom list. + /// + /// Returns an error if the list doesn't exist. pub async fn delete_custom_list(&mut self, id: Id) -> Result<(), Error> { let Some(list_index) = self .settings @@ -56,7 +62,7 @@ where self.relay_selector .set_config(new_selector_config(&self.settings)); - if self.change_should_cause_reconnect(id) { + if self.change_should_cause_reconnect(Some(id)) { log::info!("Initiating tunnel restart because a selected custom list was deleted"); self.reconnect_tunnel(); } @@ -66,6 +72,11 @@ where Ok(()) } + /// Update a custom list. + /// + /// Returns an error if... + /// - there is no existing list with the same ID, + /// - or the existing list has a different name. pub async fn update_custom_list(&mut self, new_list: CustomList) -> Result<(), Error> { let Some((list_index, old_list)) = self .settings @@ -100,7 +111,7 @@ where self.relay_selector .set_config(new_selector_config(&self.settings)); - if self.change_should_cause_reconnect(id) { + if self.change_should_cause_reconnect(Some(id)) { log::info!("Initiating tunnel restart because a selected custom list changed"); self.reconnect_tunnel(); } @@ -110,48 +121,78 @@ where Ok(()) } - fn change_should_cause_reconnect(&self, custom_list_id: Id) -> bool { + /// Remove all custom lists. + pub async fn clear_custom_lists(&mut self) -> Result<(), Error> { + let settings_changed = self + .settings + .update(|settings| { + settings.custom_lists.clear(); + }) + .await + .map_err(Error::SettingsError); + + if let Ok(true) = settings_changed { + self.relay_selector + .set_config(new_selector_config(&self.settings)); + + if self.change_should_cause_reconnect(None) { + log::info!("Initiating tunnel restart because a selected custom list was deleted"); + self.reconnect_tunnel(); + } + } + + settings_changed?; + Ok(()) + } + + /// Check whether we need to reconnect after changing custom lists. + /// + /// If `custom_list_id` is `Some`, only changes to that custom list will trigger a reconnect. + fn change_should_cause_reconnect(&self, custom_list_id: Option) -> bool { use mullvad_types::states::TunnelState; let mut need_to_reconnect = false; - if let RelaySettings::Normal(relay_settings) = &self.settings.relay_settings { - if let Constraint::Only(LocationConstraint::CustomList { list_id }) = - &relay_settings.location - { - need_to_reconnect |= list_id == &custom_list_id; - } + let RelaySettings::Normal(relay_settings) = &self.settings.relay_settings else { + return false; + }; - if let TunnelState::Connecting { - endpoint, - location: _, - } - | TunnelState::Connected { - endpoint, - location: _, - } = &self.tunnel_state - { - match endpoint.tunnel_type { - TunnelType::Wireguard => { - if relay_settings.wireguard_constraints.multihop() { - if let Constraint::Only(LocationConstraint::CustomList { list_id }) = - &relay_settings.wireguard_constraints.entry_location - { - need_to_reconnect |= list_id == &custom_list_id; - } + if let Constraint::Only(LocationConstraint::CustomList { list_id }) = + &relay_settings.location + { + need_to_reconnect |= custom_list_id.map(|id| &id == list_id).unwrap_or(true); + } + + if let TunnelState::Connecting { + endpoint, + location: _, + } + | TunnelState::Connected { + endpoint, + location: _, + } = &self.tunnel_state + { + match endpoint.tunnel_type { + TunnelType::Wireguard => { + if relay_settings.wireguard_constraints.multihop() { + if let Constraint::Only(LocationConstraint::CustomList { list_id }) = + &relay_settings.wireguard_constraints.entry_location + { + need_to_reconnect |= + custom_list_id.map(|id| &id == list_id).unwrap_or(true); } } + } - TunnelType::OpenVpn => { - if !matches!(self.settings.bridge_state, BridgeState::Off) { - if let Ok(ResolvedBridgeSettings::Normal(bridge_settings)) = - self.settings.bridge_settings.resolve() + TunnelType::OpenVpn => { + if !matches!(self.settings.bridge_state, BridgeState::Off) { + if let Ok(ResolvedBridgeSettings::Normal(bridge_settings)) = + self.settings.bridge_settings.resolve() + { + if let Constraint::Only(LocationConstraint::CustomList { list_id }) = + &bridge_settings.location { - if let Constraint::Only(LocationConstraint::CustomList { - list_id, - }) = &bridge_settings.location - { - need_to_reconnect |= list_id == &custom_list_id; - } + need_to_reconnect |= + custom_list_id.map(|id| &id == list_id).unwrap_or(true); } } } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index da02cb6ffc95..365e82efeba1 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -279,6 +279,8 @@ pub enum DaemonCommand { DeleteCustomList(ResponseTx<(), Error>, mullvad_types::custom_list::Id), /// Update a custom list with a given id UpdateCustomList(ResponseTx<(), Error>, CustomList), + /// Remove all custom lists + ClearCustomLists(ResponseTx<(), Error>), /// Add API access methods AddApiAccessMethod( ResponseTx, @@ -1255,6 +1257,7 @@ where CreateCustomList(tx, name) => self.on_create_custom_list(tx, name).await, DeleteCustomList(tx, id) => self.on_delete_custom_list(tx, id).await, UpdateCustomList(tx, update) => self.on_update_custom_list(tx, update).await, + ClearCustomLists(tx) => self.on_clear_custom_lists(tx).await, GetVersionInfo(tx) => self.on_get_version_info(tx), AddApiAccessMethod(tx, name, enabled, access_method) => { self.on_add_access_method(tx, name, enabled, access_method) @@ -2435,6 +2438,11 @@ where Self::oneshot_send(tx, result, "update_custom_list response"); } + async fn on_clear_custom_lists(&mut self, tx: ResponseTx<(), Error>) { + let result = self.clear_custom_lists().await; + Self::oneshot_send(tx, result, "clear_custom_lists response"); + } + async fn on_add_access_method( &mut self, tx: ResponseTx, diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 52c2c50d4cc2..55573f87a7d1 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -598,6 +598,16 @@ impl ManagementService for ManagementServiceImpl { .map_err(map_daemon_error) } + async fn clear_custom_lists(&self, _: Request<()>) -> ServiceResult<()> { + log::debug!("clear_custom_lists"); + let (tx, rx) = oneshot::channel(); + self.send_command_to_daemon(DaemonCommand::ClearCustomLists(tx))?; + self.wait_for_result(rx) + .await? + .map(Response::new) + .map_err(map_daemon_error) + } + // Access Methods async fn add_api_access_method( diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index cd3e6afaac0c..8af34aa8afe3 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -73,6 +73,7 @@ service ManagementService { rpc CreateCustomList(google.protobuf.StringValue) returns (google.protobuf.StringValue) {} rpc DeleteCustomList(google.protobuf.StringValue) returns (google.protobuf.Empty) {} rpc UpdateCustomList(CustomList) returns (google.protobuf.Empty) {} + rpc ClearCustomLists(google.protobuf.Empty) returns (google.protobuf.Empty) {} // Access methods rpc AddApiAccessMethod(NewAccessMethodSetting) returns (UUID) {} diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 93a446cbc9e6..8cf3ac495d2e 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -535,6 +535,15 @@ impl MullvadProxyClient { Ok(()) } + /// Remove all custom lists. + pub async fn clear_custom_lists(&mut self) -> Result<()> { + self.0 + .clear_custom_lists(()) + .await + .map_err(map_custom_list_error)?; + Ok(()) + } + pub async fn add_access_method( &mut self, name: String, diff --git a/mullvad-types/src/custom_list.rs b/mullvad-types/src/custom_list.rs index b92f128a3bd3..89febe81abad 100644 --- a/mullvad-types/src/custom_list.rs +++ b/mullvad-types/src/custom_list.rs @@ -98,6 +98,11 @@ impl CustomListsSettings { pub fn remove(&mut self, index: usize) { self.custom_lists.remove(index); } + + /// Remove all custom lists + pub fn clear(&mut self) { + self.custom_lists.clear(); + } } impl IntoIterator for CustomListsSettings { diff --git a/test/test-manager/src/tests/mod.rs b/test/test-manager/src/tests/mod.rs index e99300232b9f..f4c4563bde08 100644 --- a/test/test-manager/src/tests/mod.rs +++ b/test/test-manager/src/tests/mod.rs @@ -171,12 +171,11 @@ pub async fn cleanup_after_test(mullvad_client: &mut MullvadProxyClient) -> anyh .await .context("Could not clear PQ options in cleanup")?; - for custom_list in custom_lists { - mullvad_client - .delete_custom_list(custom_list.name) - .await - .context("Could not remove custom list")?; - } + let _ = custom_lists; + mullvad_client + .clear_custom_lists() + .await + .context("Could not remove custom list")?; Ok(()) } From 08f1883f88f71d1959762452ceb2e07ead7bce4a Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Tue, 9 Apr 2024 10:08:45 +0200 Subject: [PATCH 043/214] Refactor custom access method integration tests Break out the Shadowsocks and SOCKS5 custom access method tests into seperate tests. The intent is to increase logging granularity. --- test/test-manager/src/tests/access_methods.rs | 94 ++++++++++--------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/test/test-manager/src/tests/access_methods.rs b/test/test-manager/src/tests/access_methods.rs index 1d5660f4b01e..747ebd23e590 100644 --- a/test/test-manager/src/tests/access_methods.rs +++ b/test/test-manager/src/tests/access_methods.rs @@ -1,50 +1,30 @@ //! Integration tests for API access methods. -use super::{Error, TestContext}; +//! +//! The tested access methods are: +//! * Shadowsocks +//! * SOCKS5 in remote mode +//! +//! These tests rely on working proxies to exist *somewhere* for all tested protocols. +//! If the proxies themselves are bad/not running, this test will fail due to issues +//! that are out of the test manager's control. +use anyhow::{anyhow, ensure, Context}; + use mullvad_management_interface::MullvadProxyClient; +use talpid_types::net::proxy::CustomProxy; use test_macro::test_function; use test_rpc::ServiceClient; -/// Assert that custom access methods may be used to access the Mullvad API. -/// -/// The tested access methods are: -/// * Shadowsocks -/// * Socks5 in remote mode -/// -/// # Note -/// -/// This tests assume that there exists working proxies *somewhere* for all -/// tested protocols. If the proxies themselves are bad/not running, this test -/// will fail due to issues that are out of the test manager's control. +use super::TestContext; + +/// Assert that API traffic can be proxied via a custom Shadowsocks proxy. #[test_function] -pub async fn test_custom_access_methods( +async fn test_access_method_shadowsocks( _: TestContext, _rpc: ServiceClient, - mullvad_client: MullvadProxyClient, -) -> Result<(), Error> { - log::info!("Testing Shadowsocks access method"); - test_shadowsocks(mullvad_client.clone()).await?; - log::info!("Testing SOCKS5 (Remote) access method"); - test_socks_remote(mullvad_client.clone()).await?; - Ok(()) -} - -macro_rules! assert_access_method_works { - ($mullvad_client:expr, $access_method:expr) => { - let successful = $mullvad_client - .test_custom_api_access_method($access_method.clone().into()) - .await - .expect("Failed to test custom API access method"); - - assert!( - successful, - "Failed while testing access method - {:?}", - $access_method - ); - }; -} - -async fn test_shadowsocks(mut mullvad_client: MullvadProxyClient) -> Result<(), Error> { + mut mullvad_client: MullvadProxyClient, +) -> anyhow::Result<()> { use mullvad_relay_selector::{RelaySelector, SelectorConfig}; + log::info!("Testing Shadowsocks access method"); // Set up all the parameters needed to create a custom Shadowsocks access method. // // Since Mullvad's bridge servers host Shadowsocks relays, we can simply @@ -53,22 +33,48 @@ async fn test_shadowsocks(mut mullvad_client: MullvadProxyClient) -> Result<(), let relay_selector = RelaySelector::from_list(SelectorConfig::default(), relay_list); let access_method = relay_selector .get_bridge_forced() - .expect("`test_shadowsocks` needs at least one shadowsocks relay to execute. Found none in relay list."); - assert_access_method_works!(mullvad_client, access_method); + .context("`test_shadowsocks` needs at least one shadowsocks relay to execute. Found none in relay list.")?; + log::info!("Selected shadowsocks bridge: {access_method:?}"); + assert_access_method_works(mullvad_client, access_method.clone()) + .await + .with_context(|| anyhow!("Access method {access_method:?} did not work!"))?; Ok(()) } -async fn test_socks_remote(mut mullvad_client: MullvadProxyClient) -> Result<(), Error> { +/// Assert that API traffic can be proxied via a custom SOCKS5 proxy. +#[test_function] +async fn test_access_method_socks5_remote( + _: TestContext, + _rpc: ServiceClient, + mullvad_client: MullvadProxyClient, +) -> anyhow::Result<()> { use crate::vm::network::{NON_TUN_GATEWAY, SOCKS5_PORT}; use std::net::SocketAddr; - use talpid_types::net::proxy::{CustomProxy, Socks5Remote}; + use talpid_types::net::proxy::Socks5Remote; + log::info!("Testing SOCKS5 (Remote) access method"); // Set up all the parameters needed to create a custom SOCKS5 access method. // // The remote SOCKS5 proxy is assumed to be running on the test manager. On // which port it listens to is defined as a constant in the `test-manager` // crate. let endpoint = SocketAddr::from((NON_TUN_GATEWAY, SOCKS5_PORT)); - let access_method = CustomProxy::from(Socks5Remote::new(endpoint)); - assert_access_method_works!(mullvad_client, access_method); + let access_method = Socks5Remote::new(endpoint); + log::info!("Testing SOCKS5-proxy: {access_method:?}"); + assert_access_method_works(mullvad_client, access_method.clone()) + .await + .with_context(|| anyhow!("Access method {access_method:?} did not work!"))?; + Ok(()) +} + +async fn assert_access_method_works( + mut mullvad_client: MullvadProxyClient, + access_method: impl Into + std::fmt::Debug, +) -> anyhow::Result<()> { + let successful = mullvad_client + .test_custom_api_access_method(access_method.into()) + .await + .context("Failed to test custom API access method")?; + + ensure!(successful, "Failed while testing access method"); Ok(()) } From 94c3ce99aebd53f8c740fe093d03f06f483ea692 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Tue, 9 Apr 2024 11:42:55 +0200 Subject: [PATCH 044/214] Fix SOCKS5 server should accept all traffic Specify that the `Authentication` should be 'Accept all traffic' by default for the SOCKS5-server we spawn on the test manager. Otherwise, the default is to 'Deny all traffic' by default. --- test/socks-server/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/socks-server/src/lib.rs b/test/socks-server/src/lib.rs index 1b3e712643ef..cc8847db1bc5 100644 --- a/test/socks-server/src/lib.rs +++ b/test/socks-server/src/lib.rs @@ -1,3 +1,4 @@ +use fast_socks5::server::{AcceptAuthentication, Socks5Server}; use futures::StreamExt; use std::{io, net::SocketAddr}; @@ -13,10 +14,9 @@ pub struct Handle { /// Spawn a SOCKS server bound to `bind_addr` pub async fn spawn(bind_addr: SocketAddr) -> Result { - let socks_server: fast_socks5::server::Socks5Server = - fast_socks5::server::Socks5Server::bind(bind_addr) - .await - .map_err(Error::StartSocksServer)?; + let socks_server: Socks5Server = Socks5Server::bind(bind_addr) + .await + .map_err(Error::StartSocksServer)?; let handle = tokio::spawn(async move { let mut incoming = socks_server.incoming(); From 39cac2951a0a63814541cade4f0f80b33bbbd18d Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Tue, 9 Apr 2024 13:14:49 +0200 Subject: [PATCH 045/214] Get rid of type casting for test function argument Replace the `Box` type for the third test function argument 'mullvad client' - replace it with a dedicated enum type `MullvadClientArgument`. This change got rid of the type casting from `Box` to `MullvadProxyClient` done in the `test_function` macro. --- test/test-manager/src/mullvad_daemon.rs | 15 +++++++++------ test/test-manager/src/run_tests.rs | 14 +++++++------- test/test-manager/src/tests/mod.rs | 9 +++------ test/test-manager/test_macro/src/lib.rs | 20 ++++++++------------ 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/test/test-manager/src/mullvad_daemon.rs b/test/test-manager/src/mullvad_daemon.rs index e1a7680cb458..115a09e049cc 100644 --- a/test/test-manager/src/mullvad_daemon.rs +++ b/test/test-manager/src/mullvad_daemon.rs @@ -51,14 +51,17 @@ pub struct RpcClientProvider { service: DummyService, } +pub enum MullvadClientArgument { + WithClient(MullvadProxyClient), + None, +} + impl RpcClientProvider { - pub async fn as_type( - &self, - client_type: MullvadClientVersion, - ) -> Box { + /// Whether a [test case](test_macro::test_function) needs a [`MullvadProxyClient`]. + pub async fn mullvad_client(&self, client_type: MullvadClientVersion) -> MullvadClientArgument { match client_type { - MullvadClientVersion::New => Box::new(self.new_client().await), - MullvadClientVersion::None => Box::new(()), + MullvadClientVersion::New => MullvadClientArgument::WithClient(self.new_client().await), + MullvadClientVersion::None => MullvadClientArgument::None, } } diff --git a/test/test-manager/src/run_tests.rs b/test/test-manager/src/run_tests.rs index 0c7868e52914..005d0c89d4a6 100644 --- a/test/test-manager/src/run_tests.rs +++ b/test/test-manager/src/run_tests.rs @@ -1,6 +1,6 @@ use crate::{ logging::{panic_as_string, TestOutput}, - mullvad_daemon, + mullvad_daemon::{self, MullvadClientArgument}, summary::{self, maybe_log_test_result}, tests::{self, config::TEST_CONFIG, get_tests, TestContext}, vm, @@ -80,9 +80,9 @@ pub async fn run( let logger = super::logging::Logger::get_or_init(); for test in tests { - let mclient = test_context + let mullvad_client = test_context .rpc_provider - .as_type(test.mullvad_client_version) + .mullvad_client(test.mullvad_client_version) .await; log::info!("Running {}", test.name); @@ -94,7 +94,7 @@ pub async fn run( let test_result = run_test( client.clone(), - mclient, + mullvad_client, &test.func, test.name, test_context.clone(), @@ -175,15 +175,15 @@ pub async fn run( final_result } -pub async fn run_test( +pub async fn run_test( runner_rpc: ServiceClient, - mullvad_rpc: MullvadClient, + mullvad_rpc: MullvadClientArgument, test: &F, test_name: &'static str, test_context: super::tests::TestContext, ) -> TestOutput where - F: Fn(super::tests::TestContext, ServiceClient, MullvadClient) -> R, + F: Fn(super::tests::TestContext, ServiceClient, MullvadClientArgument) -> R, R: Future>, { let _flushed = runner_rpc.try_poll_output().await; diff --git a/test/test-manager/src/tests/mod.rs b/test/test-manager/src/tests/mod.rs index f4c4563bde08..b89b4f0fd086 100644 --- a/test/test-manager/src/tests/mod.rs +++ b/test/test-manager/src/tests/mod.rs @@ -13,7 +13,7 @@ mod tunnel; mod tunnel_state; mod ui; -use crate::mullvad_daemon::RpcClientProvider; +use crate::mullvad_daemon::{MullvadClientArgument, RpcClientProvider}; use anyhow::Context; pub use test_metadata::TestMetadata; use test_rpc::ServiceClient; @@ -30,11 +30,8 @@ pub struct TestContext { pub rpc_provider: RpcClientProvider, } -pub type TestWrapperFunction = fn( - TestContext, - ServiceClient, - Box, -) -> BoxFuture<'static, anyhow::Result<()>>; +pub type TestWrapperFunction = + fn(TestContext, ServiceClient, MullvadClientArgument) -> BoxFuture<'static, anyhow::Result<()>>; #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/test/test-manager/test_macro/src/lib.rs b/test/test-manager/test_macro/src/lib.rs index 161605e8ca01..7e19990b97a1 100644 --- a/test/test-manager/test_macro/src/lib.rs +++ b/test/test-manager/test_macro/src/lib.rs @@ -199,20 +199,18 @@ fn create_test(test_function: TestFunction) -> proc_macro2::TokenStream { let func_name = test_function.name; let function_mullvad_version = test_function.function_parameters.mullvad_client.version(); let wrapper_closure = match test_function.function_parameters.mullvad_client { - MullvadClient::New { - mullvad_client_type, - .. - } => { - let mullvad_client_type = *mullvad_client_type; + MullvadClient::New { .. } => { quote! { |test_context: crate::tests::TestContext, rpc: test_rpc::ServiceClient, - mullvad_client: Box,| + mullvad_client: crate::mullvad_daemon::MullvadClientArgument| { - use std::any::Any; - let mullvad_client = mullvad_client.downcast::<#mullvad_client_type>().expect("invalid mullvad client"); + let mullvad_client = match mullvad_client { + crate::mullvad_daemon::MullvadClientArgument::WithClient(client) => client, + crate::mullvad_daemon::MullvadClientArgument::None => unreachable!("invalid mullvad client") + }; Box::pin(async move { - #func_name(test_context, rpc, *mullvad_client).await.map_err(Into::into) + #func_name(test_context, rpc, mullvad_client).await.map_err(Into::into) }) } } @@ -221,7 +219,7 @@ fn create_test(test_function: TestFunction) -> proc_macro2::TokenStream { quote! { |test_context: crate::tests::TestContext, rpc: test_rpc::ServiceClient, - _mullvad_client: Box| { + _mullvad_client: crate::mullvad_daemon::MullvadClientArgument| { Box::pin(async move { #func_name(test_context, rpc).await.map_err(Into::into) }) @@ -264,7 +262,6 @@ enum MullvadClient { mullvad_client_version: proc_macro2::TokenStream, }, New { - mullvad_client_type: Box, mullvad_client_version: proc_macro2::TokenStream, }, } @@ -314,7 +311,6 @@ fn get_test_function_parameters( let mullvad_client_version = quote! { test_rpc::mullvad_daemon::MullvadClientVersion::New }; MullvadClient::New { - mullvad_client_type: pat_type.ty, mullvad_client_version, } } From 43663fe14b25e390fea2cad36148c5609434aea8 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 10 Apr 2024 14:11:28 +0200 Subject: [PATCH 046/214] Do not panic in DNS tests Avoid panicking in more tests - prefer to propagate an error value instead. --- test/test-manager/src/tests/dns.rs | 39 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/test/test-manager/src/tests/dns.rs b/test/test-manager/src/tests/dns.rs index bff7059aeb94..a6f564139f95 100644 --- a/test/test-manager/src/tests/dns.rs +++ b/test/test-manager/src/tests/dns.rs @@ -1,3 +1,4 @@ +use anyhow::{anyhow, Context}; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, sync::atomic::{AtomicUsize, Ordering}, @@ -334,7 +335,7 @@ pub async fn test_dns_config_default( _: TestContext, rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, -) -> Result<(), Error> { +) -> anyhow::Result<()> { run_dns_config_tunnel_test( &rpc, &mut mullvad_client, @@ -353,7 +354,7 @@ pub async fn test_dns_config_custom_private( _: TestContext, rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, -) -> Result<(), Error> { +) -> anyhow::Result<()> { log::debug!("Setting custom DNS resolver to {NON_TUN_GATEWAY}"); mullvad_client @@ -365,7 +366,7 @@ pub async fn test_dns_config_custom_private( state: settings::DnsState::Custom, }) .await - .expect("failed to configure DNS server"); + .context("failed to configure DNS server")?; run_dns_config_non_tunnel_test(&rpc, &mut mullvad_client, IpAddr::V4(NON_TUN_GATEWAY)).await } @@ -380,7 +381,7 @@ pub async fn test_dns_config_custom_public( _: TestContext, rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, -) -> Result<(), Error> { +) -> anyhow::Result<()> { let custom_ip = IpAddr::V4(Ipv4Addr::new(1, 3, 3, 7)); log::debug!("Setting custom DNS resolver to {custom_ip}"); @@ -394,7 +395,7 @@ pub async fn test_dns_config_custom_public( state: settings::DnsState::Custom, }) .await - .expect("failed to configure DNS server"); + .context("failed to configure DNS server")?; run_dns_config_tunnel_test(&rpc, &mut mullvad_client, custom_ip).await } @@ -406,7 +407,7 @@ pub async fn test_content_blockers( _: TestContext, rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, -) -> Result<(), Error> { +) -> anyhow::Result<()> { const DNS_BLOCKING_IP_BASE: Ipv4Addr = Ipv4Addr::new(100, 64, 0, 0); let content_blockers = [ ( @@ -498,7 +499,7 @@ pub async fn test_content_blockers( state: settings::DnsState::Default, }) .await - .expect("failed to configure DNS server"); + .context("failed to configure DNS server")?; run_dns_config_tunnel_test(&rpc, &mut mullvad_client, test_ip).await?; } @@ -510,7 +511,7 @@ async fn run_dns_config_tunnel_test( rpc: &ServiceClient, mullvad_client: &mut MullvadProxyClient, expected_dns_resolver: IpAddr, -) -> Result<(), Error> { +) -> anyhow::Result<()> { run_dns_config_test( rpc, || { @@ -534,7 +535,7 @@ async fn run_dns_config_non_tunnel_test( rpc: &ServiceClient, mullvad_client: &mut MullvadProxyClient, expected_dns_resolver: IpAddr, -) -> Result<(), Error> { +) -> anyhow::Result<()> { run_dns_config_test( rpc, || { @@ -561,33 +562,33 @@ async fn run_dns_config_test< create_monitor: impl FnOnce() -> F, mullvad_client: &mut MullvadProxyClient, expected_dns_resolver: IpAddr, -) -> Result<(), Error> { +) -> anyhow::Result<()> { match mullvad_client.get_tunnel_state().await { // prevent reconnect Ok(mullvad_types::states::TunnelState::Connected { .. }) => (), _ => { connect_local_wg_relay(mullvad_client) .await - .expect("failed to connect to custom wg relay"); + .context("failed to connect to custom wg relay")?; } } let nontun_iface = rpc .get_default_interface() .await - .expect("failed to find non-tun interface"); + .context("failed to find non-tun interface")?; let tunnel_iface = helpers::get_tunnel_interface(mullvad_client) .await - .expect("failed to find tunnel interface"); + .context("failed to find tunnel interface")?; let nontun_ip = rpc .get_interface_ip(nontun_iface) .await - .expect("failed to obtain guest IP"); + .context("failed to obtain guest IP")?; let tunnel_ip = rpc .get_interface_ip(tunnel_iface) .await - .expect("failed to obtain tunnel IP"); + .context("failed to obtain tunnel IP")?; log::debug!("Tunnel (guest) IP: {tunnel_ip}"); log::debug!("Non-tunnel (guest) IP: {nontun_ip}"); @@ -612,7 +613,13 @@ async fn run_dns_config_test< }); assert_eq!( - monitor.wait().await.unwrap().packets[0].destination, + monitor + .wait() + .await? + .packets + .first() + .ok_or(anyhow!("Expected at least one packet, saw none"))? + .destination, SocketAddr::new(expected_dns_resolver, 53), "expected tunnel packet to expected destination {expected_dns_resolver}" ); From 60562b51bfaae339230b9a6bc552f8c7a65a99d7 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 12 Apr 2024 09:41:03 +0200 Subject: [PATCH 047/214] Upgrade `pcap` to `1.3` This fixes type checking of `pcap` functions on Windows. Without it, type checking of the ``test-runner` crate from a Linux host to a Windows target would not work. --- test/Cargo.lock | 5 +++-- test/test-manager/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/Cargo.lock b/test/Cargo.lock index eb38b5f37ae6..6d559010905a 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -2123,15 +2123,16 @@ dependencies = [ [[package]] name = "pcap" -version = "0.10.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da544c8115cc65b474554569c7654fc94a4f6a167f79192536e148fd654e17a" +checksum = "99e935fc73d54a89fff576526c2ccd42bbf8247aae05b358693475b14fd4ff79" dependencies = [ "bitflags 1.3.2", "errno 0.2.8", "futures", "libc", "libloading", + "pkg-config", "regex", "tokio", "windows-sys 0.36.1", diff --git a/test/test-manager/Cargo.toml b/test/test-manager/Cargo.toml index 37cfbc5a8599..fa86db329e2b 100644 --- a/test/test-manager/Cargo.toml +++ b/test/test-manager/Cargo.toml @@ -39,7 +39,7 @@ serde_json = { workspace = true } tokio-serde = { workspace = true } log = { workspace = true } -pcap = { version = "0.10.1", features = ["capture-stream"] } +pcap = { version = "1.3", features = ["capture-stream"] } pnet_packet = "0.31.0" test-rpc = { path = "../test-rpc" } From 9531b3085b3831108fee3e4d7a8563ac79dc4225 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Tue, 9 Apr 2024 13:48:07 +0200 Subject: [PATCH 048/214] Reset daemon environment when needed --- test/test-manager/src/run_tests.rs | 4 +- test/test-manager/src/tests/mod.rs | 31 +++- test/test-rpc/src/client.rs | 14 ++ test/test-rpc/src/lib.rs | 12 ++ test/test-runner/src/main.rs | 7 + test/test-runner/src/sys.rs | 223 +++++++++++++++++++++++++---- 6 files changed, 256 insertions(+), 35 deletions(-) diff --git a/test/test-manager/src/run_tests.rs b/test/test-manager/src/run_tests.rs index 005d0c89d4a6..73c72ebc2ef5 100644 --- a/test/test-manager/src/run_tests.rs +++ b/test/test-manager/src/run_tests.rs @@ -105,8 +105,8 @@ pub async fn run( // Try to reset the daemon state if the test failed OR if the test doesn't explicitly // disabled cleanup. if test.cleanup || matches!(test_result.result, Err(_) | Ok(Err(_))) { - let mut client = test_context.rpc_provider.new_client().await; - crate::tests::cleanup_after_test(&mut client).await?; + crate::tests::cleanup_after_test(client.clone(), &test_context.rpc_provider) + .await?; } } diff --git a/test/test-manager/src/tests/mod.rs b/test/test-manager/src/tests/mod.rs index b89b4f0fd086..ce38308b0d7a 100644 --- a/test/test-manager/src/tests/mod.rs +++ b/test/test-manager/src/tests/mod.rs @@ -13,14 +13,16 @@ mod tunnel; mod tunnel_state; mod ui; -use crate::mullvad_daemon::{MullvadClientArgument, RpcClientProvider}; +use crate::{ + mullvad_daemon::{MullvadClientArgument, RpcClientProvider}, + tests::helpers::get_app_env, +}; use anyhow::Context; pub use test_metadata::TestMetadata; use test_rpc::ServiceClient; use futures::future::BoxFuture; -use mullvad_management_interface::MullvadProxyClient; use std::time::Duration; const WAIT_FOR_TUNNEL_STATE_TIMEOUT: Duration = Duration::from_secs(40); @@ -69,10 +71,16 @@ pub fn get_tests() -> Vec<&'static TestMetadata> { } /// Restore settings to the defaults. -pub async fn cleanup_after_test(mullvad_client: &mut MullvadProxyClient) -> anyhow::Result<()> { +pub async fn cleanup_after_test( + rpc: ServiceClient, + rpc_provider: &RpcClientProvider, +) -> anyhow::Result<()> { log::debug!("Cleaning up daemon in test cleanup"); + // Check if daemon should be restarted + restart_daemon(rpc).await?; + let mut mullvad_client = rpc_provider.new_client().await; - helpers::disconnect_and_wait(mullvad_client).await?; + helpers::disconnect_and_wait(&mut mullvad_client).await?; // Bring all the settings into scope so we remember to reset them. let mullvad_types::settings::Settings { @@ -176,3 +184,18 @@ pub async fn cleanup_after_test(mullvad_client: &mut MullvadProxyClient) -> anyh Ok(()) } + +/// Conditionally restart the running daemon +/// +/// If the daemon was started with non-standard environment variables, subsequent tests may break +/// due to assuming a default configuration. In that case, reset the environment variables and +/// restart. +async fn restart_daemon(rpc: ServiceClient) -> anyhow::Result<()> { + let current_env = rpc.get_daemon_environment().await?; + let default_env = get_app_env(); + if current_env != default_env { + log::debug!("Restarting daemon due changed environment variables. Values since last test {current_env:?}"); + rpc.set_daemon_environment(default_env).await?; + } + Ok(()) +} diff --git a/test/test-rpc/src/client.rs b/test/test-rpc/src/client.rs index 79c6a038ca96..84d923a826bc 100644 --- a/test/test-rpc/src/client.rs +++ b/test/test-rpc/src/client.rs @@ -314,6 +314,20 @@ impl ServiceClient { Ok(()) } + /// Get the current daemon's environment variables. + /// + /// # Returns + /// - `Result::Ok(env)` if the current environment variables could be read. + /// - `Result::Err(Error)` if communication with the daemon failed or the environment values + /// could not be parsed. + pub async fn get_daemon_environment(&self) -> Result, Error> { + let env = self + .client + .get_daemon_environment(tarpc::context::current()) + .await??; + Ok(env) + } + pub async fn copy_file(&self, src: String, dest: String) -> Result<(), Error> { log::debug!("Copying \"{src}\" to \"{dest}\""); self.client diff --git a/test/test-rpc/src/lib.rs b/test/test-rpc/src/lib.rs index fc9ea67c8449..82fed91541a9 100644 --- a/test/test-rpc/src/lib.rs +++ b/test/test-rpc/src/lib.rs @@ -59,10 +59,19 @@ pub enum Error { TcpForward, #[error("Unknown process ID: {0}")] UnknownPid(u32), + #[error("Failed to join tokio task: {0}")] + TokioJoinError(String), #[error("{0}")] Other(String), } +impl Error { + /// Convenient mapping from a Tokio error to the test_rpc Error type. + pub fn from_tokio_join_error(error: tokio::task::JoinError) -> Error { + Error::TokioJoinError(error.to_string()) + } +} + /// Response from am.i.mullvad.net #[derive(Debug, Serialize, Deserialize)] pub struct AmIMullvad { @@ -213,6 +222,9 @@ mod service { /// service. async fn set_daemon_environment(env: HashMap) -> Result<(), Error>; + /// Get the environment variables for the running daemon service. + async fn get_daemon_environment() -> Result, Error>; + /// Copy a file from `src` to `dest` on the test runner. async fn copy_file(src: String, dest: String) -> Result<(), Error>; diff --git a/test/test-runner/src/main.rs b/test/test-runner/src/main.rs index 29f0f936d363..79e11e0506f5 100644 --- a/test/test-runner/src/main.rs +++ b/test/test-runner/src/main.rs @@ -309,6 +309,13 @@ impl Service for TestServer { sys::set_daemon_environment(env).await } + async fn get_daemon_environment( + self, + _: context::Context, + ) -> Result, test_rpc::Error> { + sys::get_daemon_environment().await + } + async fn copy_file( self, _: context::Context, diff --git a/test/test-runner/src/sys.rs b/test/test-runner/src/sys.rs index 5015f26ca126..0a176b1c3d5e 100644 --- a/test/test-runner/src/sys.rs +++ b/test/test-runner/src/sys.rs @@ -4,13 +4,22 @@ use std::io; use test_rpc::{meta::OsVersion, mullvad_daemon::Verbosity}; #[cfg(target_os = "windows")] -use std::ffi::OsString; +use std::{ffi::OsString, path::Path}; #[cfg(target_os = "windows")] use windows_service::{ service::{ServiceAccess, ServiceInfo}, service_manager::{ServiceManager, ServiceManagerAccess}, }; +#[cfg(target_os = "linux")] +const SYSTEMD_OVERRIDE_FILE: &str = "/etc/systemd/system/mullvad-daemon.service.d/override.conf"; + +#[cfg(target_os = "macos")] +const PLIST_OVERRIDE_FILE: &str = "/Library/LaunchDaemons/net.mullvad.daemon.plist"; + +#[cfg(target_os = "windows")] +const MULLVAD_WIN_REGISTRY: &str = r"SYSTEM\CurrentControlSet\Services\Mullvad VPN"; + #[cfg(target_os = "windows")] pub fn reboot() -> Result<(), test_rpc::Error> { use windows_sys::Win32::{ @@ -154,9 +163,6 @@ pub fn reboot() -> Result<(), test_rpc::Error> { #[cfg(target_os = "linux")] pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test_rpc::Error> { use tokio::io::AsyncWriteExt; - const SYSTEMD_OVERRIDE_FILE: &str = - "/etc/systemd/system/mullvad-daemon.service.d/override.conf"; - log::debug!("Setting log level"); let verbosity = match verbosity_level { @@ -388,18 +394,58 @@ pub async fn set_daemon_log_level(_verbosity_level: Verbosity) -> Result<(), tes Ok(()) } +#[cfg(target_os = "linux")] +#[derive(Debug)] +struct EnvVar { + var: String, + value: String, +} + +#[cfg(target_os = "linux")] +impl EnvVar { + fn from_systemd_string(s: &str) -> Result { + // Here, we are only concerned with parsing a line that starts with "Environment". + let error = "Failed to parse systemd env-config"; + let mut input = s.trim().split('='); + let pre = input.next().ok_or(error)?; + match pre { + "Environment" => { + // Proccess the input just a bit more - remove the leading and trailing quote ("). + let var = input + .next() + .ok_or(error)? + .trim_start_matches('"') + .to_string(); + let value = input.next().ok_or(error)?.trim_end_matches('"').to_string(); + Ok(EnvVar { var, value }) + } + _ => Err(error), + } + } + + fn to_systemd_string(&self) -> String { + format!( + "Environment=\"{key}={value}\"", + key = self.var, + value = self.value + ) + } +} + #[cfg(target_os = "linux")] pub async fn set_daemon_environment(env: HashMap) -> Result<(), test_rpc::Error> { use std::fmt::Write; - use tokio::io::AsyncWriteExt; - - const SYSTEMD_OVERRIDE_FILE: &str = "/etc/systemd/system/mullvad-daemon.service.d/env.conf"; let mut override_content = String::new(); override_content.push_str("[Service]\n"); - for (k, v) in env { - writeln!(&mut override_content, "Environment=\"{k}={v}\"").unwrap(); + for env_var in env + .into_iter() + .map(|(var, value)| EnvVar { var, value }) + .map(|env_var| env_var.to_systemd_string()) + { + writeln!(&mut override_content, "{env_var}") + .map_err(|err| test_rpc::Error::Service(err.to_string()))?; } let override_path = std::path::Path::new(SYSTEMD_OVERRIDE_FILE); @@ -409,15 +455,7 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), .map_err(|e| test_rpc::Error::Service(e.to_string()))?; } - let mut file = tokio::fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(override_path) - .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; - - file.write_all(override_content.as_bytes()) + tokio::fs::write(override_path, override_content) .await .map_err(|e| test_rpc::Error::Service(e.to_string()))?; @@ -440,7 +478,7 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), #[cfg(target_os = "windows")] pub async fn set_daemon_environment(env: HashMap) -> Result<(), test_rpc::Error> { // Set environment globally (not for service) to prevent it from being lost on upgrade - for (k, v) in env { + for (k, v) in env.clone() { tokio::process::Command::new("setx") .arg("/m") .args([k, v]) @@ -448,6 +486,18 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), .await .map_err(|e| test_rpc::Error::Registry(e.to_string()))?; } + // Persist the changed environment variables, such that we can retrieve them at will. + use winreg::{enums::*, RegKey}; + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + let path = Path::new(MULLVAD_WIN_REGISTRY).join("Environment"); + let (registry, _) = hklm.create_subkey(&path).map_err(|error| { + test_rpc::Error::Registry(format!("Failed to open Mullvad VPN subkey: {}", error)) + })?; + for (k, v) in env { + registry.set_value(k, &v).map_err(|error| { + test_rpc::Error::Registry(format!("Failed to set Environment var: {}", error)) + })?; + } // Restart service stop_app().await?; @@ -464,7 +514,7 @@ pub fn get_system_path_var() -> Result { let key = hklm .open_subkey("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment") .map_err(|error| { - test_rpc::Error::Registry(format!("Failed to open environment subkey: {}", error)) + test_rpc::Error::Registry(format!("Failed to open Environment subkey: {}", error)) })?; let path: String = key @@ -476,14 +526,11 @@ pub fn get_system_path_var() -> Result { #[cfg(target_os = "macos")] pub async fn set_daemon_environment(env: HashMap) -> Result<(), test_rpc::Error> { - const PLIST_PATH: &str = "/Library/LaunchDaemons/net.mullvad.daemon.plist"; - tokio::task::spawn_blocking(|| { - let mut parsed_plist: plist::Value = plist::from_file(PLIST_PATH) + let mut parsed_plist = plist::Value::from_file(PLIST_OVERRIDE_FILE) .map_err(|error| test_rpc::Error::Service(format!("failed to parse plist: {error}")))?; let mut vars = plist::Dictionary::new(); - for (k, v) in env { // Set environment globally (not for service) to prevent it from being lost on upgrade std::process::Command::new("launchctl") @@ -503,10 +550,7 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), plist::Value::Dictionary(vars), ); - let daemon_plist = std::fs::OpenOptions::new() - .write(true) - .truncate(true) - .open(PLIST_PATH) + let daemon_plist = std::fs::File::create(PLIST_OVERRIDE_FILE) .map_err(|e| test_rpc::Error::Service(format!("failed to open plist: {e}")))?; parsed_plist @@ -532,7 +576,7 @@ async fn set_launch_daemon_state(on: bool) -> Result<(), test_rpc::Error> { .args([ if on { "load" } else { "unload" }, "-w", - "/Library/LaunchDaemons/net.mullvad.daemon.plist", + PLIST_OVERRIDE_FILE, ]) .status() .await @@ -540,6 +584,99 @@ async fn set_launch_daemon_state(on: bool) -> Result<(), test_rpc::Error> { Ok(()) } +#[cfg(target_os = "linux")] +pub async fn get_daemon_environment() -> Result, test_rpc::Error> { + let text = tokio::fs::read_to_string(SYSTEMD_OVERRIDE_FILE) + .await + .map_err(|err| test_rpc::Error::FileSystem(err.to_string()))?; + + let env: HashMap = parse_systemd_env_file(&text) + .map(|EnvVar { var, value }| (var, value)) + .collect(); + Ok(env) +} + +/// Parse a systemd env-file. `input` is assumed to be the entire text content of a systemd-env file. +/// +/// Example systemd-env file: +/// ``` +/// [Service] +/// Environment="VAR1=pGNqduRFkB4K9C2vijOmUDa2kPtUhArN" +/// Environment="VAR2=JP8YLOc2bsNlrGuD6LVTq7L36obpjzxd" +/// ``` +#[cfg(target_os = "linux")] +fn parse_systemd_env_file(input: &str) -> impl Iterator + '_ { + input + .lines() + .map(EnvVar::from_systemd_string) + .filter_map(|env_var| env_var.ok()) + .inspect(|env_var| log::trace!("Parsed {env_var:?}")) +} + +#[cfg(target_os = "windows")] +pub async fn get_daemon_environment() -> Result, test_rpc::Error> { + use winreg::{enums::*, RegKey}; + + let env = + tokio::task::spawn_blocking(|| -> Result, test_rpc::Error> { + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + let key = hklm.open_subkey(MULLVAD_WIN_REGISTRY).map_err(|error| { + test_rpc::Error::Registry(format!("Failed to open Mullvad VPN subkey: {}", error)) + })?; + + // The Strings will be quoted (surrounded by ") when read from the registry - we should trim that! + let trim = |string: String| string.trim_matches('"').to_owned(); + let env = key + .open_subkey("Environment") + .map_err(|error| { + test_rpc::Error::Registry(format!( + "Failed to open Environment subkey: {}", + error + )) + })? + .enum_values() + .filter_map(|x| x.inspect_err(|err| log::trace!("{err}")).ok()) + .map(|(k, v)| (trim(k), trim(v.to_string()))) + .collect(); + Ok(env) + }) + .await + .map_err(test_rpc::Error::from_tokio_join_error)??; + + Ok(env) +} + +#[cfg(target_os = "macos")] +pub async fn get_daemon_environment() -> Result, test_rpc::Error> { + let plist = tokio::task::spawn_blocking(|| { + let parsed_plist = plist::Value::from_file(PLIST_OVERRIDE_FILE) + .map_err(|error| test_rpc::Error::Service(format!("failed to parse plist: {error}")))?; + + Ok::(parsed_plist) + }) + .await + .map_err(test_rpc::Error::from_tokio_join_error)??; + + let plist_tree = plist + .as_dictionary() + .ok_or_else(|| test_rpc::Error::Service("plist missing dict".to_owned()))?; + let Some(env_vars) = plist_tree.get("EnvironmentVariables") else { + // `EnvironmentVariables` does not exist in plist file, so there are no env variables to parse. + return Ok(HashMap::new()); + }; + let env_vars = env_vars.as_dictionary().ok_or_else(|| { + test_rpc::Error::Service("`EnvironmentVariables` is not a dict".to_owned()) + })?; + + let env = env_vars + .clone() + .into_iter() + .filter_map(|(key, value)| Some((key, value.into_string()?))) + .collect(); + + Ok(env) +} + #[cfg(target_os = "linux")] enum ServiceState { Running, @@ -618,3 +755,31 @@ pub fn get_os_version() -> Result { pub fn get_os_version() -> Result { Ok(OsVersion::Linux) } + +#[cfg(test)] +mod test { + + #[cfg(target_os = "linux")] + #[test] + fn parse_systemd_environment_variables() { + use super::parse_systemd_env_file; + // Define an example systemd environment file + let systemd_file = " + [Service] + Environment=\"var1=value1\" + Environment=\"var2=value2\" + "; + + // Parse the "file" + let env_vars: Vec<_> = parse_systemd_env_file(systemd_file).collect(); + + // Assert that the environment variables it defines are parsed as expected. + assert_eq!(env_vars.len(), 2); + let first = env_vars.first().unwrap(); + assert_eq!(first.var, "var1"); + assert_eq!(first.value, "value1"); + let second = env_vars.get(1).unwrap(); + assert_eq!(second.var, "var2"); + assert_eq!(second.value, "value2"); + } +} From 7cfc90251636fb66661ca85a79463029209141fc Mon Sep 17 00:00:00 2001 From: Niklas Berglund Date: Tue, 5 Mar 2024 17:20:35 +0100 Subject: [PATCH 049/214] Add WireGuard tests for iOS app --- .../xcshareddata/swiftpm/Package.resolved | 22 ------------------- ios/MullvadVPNUITests/RelayTests.swift | 7 +++++- .../Test base classes/BaseUITestCase.swift | 1 + 3 files changed, 7 insertions(+), 23 deletions(-) delete mode 100644 ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 02691892fed1..000000000000 --- a/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,22 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-log", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log.git", - "state" : { - "revision" : "173f567a2dfec11d74588eea82cecea555bdc0bc", - "version" : "1.4.0" - } - }, - { - "identity" : "wireguard-apple", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mullvad/wireguard-apple.git", - "state" : { - "revision" : "11a00c20dc03f2751db47e94f585c0778c7bde82" - } - } - ], - "version" : 2 -} diff --git a/ios/MullvadVPNUITests/RelayTests.swift b/ios/MullvadVPNUITests/RelayTests.swift index d0f8ea20375a..99b36175ee94 100644 --- a/ios/MullvadVPNUITests/RelayTests.swift +++ b/ios/MullvadVPNUITests/RelayTests.swift @@ -205,7 +205,12 @@ class RelayTests: LoggedInWithTimeUITestCase { TunnelControlPage(app) .tapSecureConnectionButton() - allowAddVPNConfigurations() + allowAddVPNConfigurationsIfAsked() + + TunnelControlPage(app) + .waitForSecureConnectionLabel() + + try Networking.verifyCanAccessInternet() HeaderBar(app) .tapSettingsButton() diff --git a/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift index 11b0cf6ee5f0..ae4011cfc7ef 100644 --- a/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift +++ b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift @@ -129,6 +129,7 @@ class BaseUITestCase: XCTestCase { func logoutIfLoggedIn() { if isLoggedIn() { + // First dismiss settings modal if presented if isPresentingSettings() { SettingsPage(app) .swipeDownToDismissModal() From 3a4acfb16cf86c5b0a9ccfcae855a3df22a4342f Mon Sep 17 00:00:00 2001 From: Niklas Berglund Date: Thu, 14 Mar 2024 10:23:48 +0100 Subject: [PATCH 050/214] Add iOS app connection test --- .../xcshareddata/swiftpm/Package.resolved | 22 ++++++ .../Networking/Networking.swift | 67 +++++++++++++++---- ios/MullvadVPNUITests/RelayTests.swift | 13 ++++ 3 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000000..02691892fed1 --- /dev/null +++ b/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,22 @@ +{ + "pins" : [ + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "173f567a2dfec11d74588eea82cecea555bdc0bc", + "version" : "1.4.0" + } + }, + { + "identity" : "wireguard-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mullvad/wireguard-apple.git", + "state" : { + "revision" : "11a00c20dc03f2751db47e94f585c0778c7bde82" + } + } + ], + "version" : 2 +} diff --git a/ios/MullvadVPNUITests/Networking/Networking.swift b/ios/MullvadVPNUITests/Networking/Networking.swift index 4af87b5523a9..003cc4386c3d 100644 --- a/ios/MullvadVPNUITests/Networking/Networking.swift +++ b/ios/MullvadVPNUITests/Networking/Networking.swift @@ -68,18 +68,6 @@ class Networking { throw NetworkingError.internalError(reason: "Failed to determine device's IP address") } - /// Get configured ad serving domain as URL object - private static func getAdServingDomainURL() -> URL? { - guard let adServingDomain = Bundle(for: BaseUITestCase.self) - .infoDictionary?["AdServingDomain"] as? String, - let adServingDomainURL = URL(string: adServingDomain) else { - XCTFail("Ad serving domain not configured") - return nil - } - - return adServingDomainURL - } - /// Get configured ad serving domain private static func getAdServingDomain() throws -> String { guard let adServingDomain = Bundle(for: Networking.self) @@ -226,4 +214,59 @@ class Networking { XCTFail("Failed to verify DNS server provider - couldn't serialize JSON") } } + + public static func verifyConnectedThroughMullvad() { + let mullvadConnectionJsonEndpoint = "https://am.i.mullvad.net/json" + guard let url = URL(string: mullvadConnectionJsonEndpoint) else { + XCTFail("Failed to unwrap URL") + return + } + + let request = URLRequest(url: url) + let completionHandlerInvokedExpectation = XCTestExpectation( + description: "Completion handler for the request is invoked" + ) + + let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in + if let response = response as? HTTPURLResponse { + if response.statusCode != 200 { + XCTFail("Request to connection check API failed - unexpected server response") + } + } + + if let error = error { + XCTFail("Request to connection check API failed - encountered error \(error.localizedDescription)") + } + + guard let data = data else { + XCTFail("Didn't receive any data") + return + } + + do { + let jsonObject = try JSONSerialization.jsonObject(with: data) + + if let dictionary = jsonObject as? [String: Any] { + guard let isConnectedThroughMullvad = dictionary["mullvad_exit_ip"] as? Bool else { + XCTFail("Unexpected JSON format") + return + } + + XCTAssertTrue(isConnectedThroughMullvad) + } + } catch { + XCTFail("Failed to verify whether connected through Mullvad or not") + } + + completionHandlerInvokedExpectation.fulfill() + } + + dataTask.resume() + + let waitResult = XCTWaiter.wait(for: [completionHandlerInvokedExpectation], timeout: 30) + + if waitResult != .completed { + XCTFail("Request to connection check API failed - timeout") + } + } } diff --git a/ios/MullvadVPNUITests/RelayTests.swift b/ios/MullvadVPNUITests/RelayTests.swift index 99b36175ee94..6db6dd5f6126 100644 --- a/ios/MullvadVPNUITests/RelayTests.swift +++ b/ios/MullvadVPNUITests/RelayTests.swift @@ -26,6 +26,19 @@ class RelayTests: LoggedInWithTimeUITestCase { } } + func testAppConnection() throws { + TunnelControlPage(app) + .tapSecureConnectionButton() + + allowAddVPNConfigurationsIfAsked() + + TunnelControlPage(app) + .waitForSecureConnectionLabel() + + try Networking.verifyCanAccessInternet() + Networking.verifyConnectedThroughMullvad() + } + func testAdBlockingViaDNS() throws { HeaderBar(app) .tapSettingsButton() From b1722ac1b2eea19d6cb3073c0662b20960858aab Mon Sep 17 00:00:00 2001 From: mojganii Date: Thu, 11 Apr 2024 11:04:51 +0200 Subject: [PATCH 051/214] Add alphabetical sorting for custom list locations --- .../CustomListLocationNodeBuilder.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ios/MullvadVPN/View controllers/SelectLocation/CustomListLocationNodeBuilder.swift b/ios/MullvadVPN/View controllers/SelectLocation/CustomListLocationNodeBuilder.swift index 66e4ddbb5a6c..e92a8bda8de7 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/CustomListLocationNodeBuilder.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/CustomListLocationNodeBuilder.swift @@ -8,6 +8,7 @@ import Foundation import MullvadSettings +import MullvadTypes struct CustomListLocationNodeBuilder { let customList: CustomList @@ -18,6 +19,7 @@ struct CustomListLocationNodeBuilder { name: customList.name, code: customList.name.lowercased(), locations: customList.locations, + isActive: !customList.locations.isEmpty, customList: customList ) @@ -44,6 +46,33 @@ struct CustomListLocationNodeBuilder { .copy(withParent: listNode) } } + + listNode.sort() return listNode } } + +private extension CustomListLocationNode { + func sort() { + let sortedChildren = Dictionary(grouping: children, by: { + return switch RelayLocation(dashSeparatedString: $0.code)! { + case .country: + LocationGroup.country + case .city: + LocationGroup.city + case .hostname: + LocationGroup.host + } + }) + .sorted(by: { $0.key < $1.key }) + .reduce([]) { + return $0 + $1.value.sorted(by: { $0.name < $1.name }) + } + + children = sortedChildren + } +} + +private enum LocationGroup: CaseIterable, Comparable { + case country, city, host +} From f26f31509afca5ad984c3c945e34b948462917bf Mon Sep 17 00:00:00 2001 From: mojganii Date: Fri, 12 Apr 2024 15:57:53 +0200 Subject: [PATCH 052/214] Update privacy manifest --- .../Supporting Files/PrivacyInfo.xcprivacy | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/ios/MullvadVPN/Supporting Files/PrivacyInfo.xcprivacy b/ios/MullvadVPN/Supporting Files/PrivacyInfo.xcprivacy index a38a73421e6d..d506621876bc 100644 --- a/ios/MullvadVPN/Supporting Files/PrivacyInfo.xcprivacy +++ b/ios/MullvadVPN/Supporting Files/PrivacyInfo.xcprivacy @@ -1,21 +1,30 @@ - - NSPrivacyTrackingDomains - - NSPrivacyTracking - - NSPrivacyAccessedAPITypes - - - NSPrivacyAccessedAPITypeReasons - - CA92.1 - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults - - - + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + 3B52.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + From ef57b79fad0a8c512b22a4ea729f5513b574a986 Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 12 Apr 2024 16:26:54 +0200 Subject: [PATCH 053/214] Suppress Joda-Time CVE-2024-23080 --- android/config/dependency-check-suppression.xml | 9 +++++++++ android/test/test-suppression.xml | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/android/config/dependency-check-suppression.xml b/android/config/dependency-check-suppression.xml index b8712349edc7..6a9729d70515 100644 --- a/android/config/dependency-check-suppression.xml +++ b/android/config/dependency-check-suppression.xml @@ -68,4 +68,13 @@ ^pkg:maven/androidx\.test\.services/storage@.*$ CVE-2014-9152 + + + ^pkg:maven/joda-time/joda-time@.*$ + CVE-2024-23080 + diff --git a/android/test/test-suppression.xml b/android/test/test-suppression.xml index 5932bda92e90..adebd4c116dc 100644 --- a/android/test/test-suppression.xml +++ b/android/test/test-suppression.xml @@ -118,4 +118,13 @@ ^pkg:maven/androidx\.test\.services/storage@.*$ CVE-2014-9152 + + + ^pkg:maven/joda-time/joda-time@.*$ + CVE-2024-23080 + From 80ccb2b656c5e5701ef3f8310c7098ce5f1ab5bd Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 12 Apr 2024 16:07:20 +0200 Subject: [PATCH 054/214] Update F-Droid description This commit makes sure the F-Droid description is aligned with the one used in the Play store. The only difference is that we here refer to our website rather than in-app purchases. --- .../play/listings/en-US/full-description.txt | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/android/src/main/play/listings/en-US/full-description.txt b/android/src/main/play/listings/en-US/full-description.txt index 63b51cf557c1..f6a26fb0eb7f 100644 --- a/android/src/main/play/listings/en-US/full-description.txt +++ b/android/src/main/play/listings/en-US/full-description.txt @@ -1,35 +1,31 @@ +Free the internet from data collection with Mullvad VPN – a service that helps keep your online activity, identity, and location private. Only €5/month. + Get started 1. Install the app. -2. Click “Create account” to generate an account number. -3. Add time to your account on our website. Just €5/month. - -Why use Mullvad VPN? +2. Create an account. +3. Add time to your account via our website or vouchers. -Maintain your anonymity: +To make sure to block third-party cookies and other tracking technologies – use Mullvad VPN together with Mullvad Browser (free of charge). -• Creating an account requires no personal info — not even an email address. +ANONYMOUS ACCOUNTS – NO ACTIVITY LOGS +• Creating an account requires no personal info - not even an email address. • We keep no activity logs. -• Pay anonymously with cash or cryptocurrency. - -Mitigate ISP blocking and throttling. - -Bypass geographical restrictions with our global network of VPN servers. - -Our Android app uses WireGuard, a superior VPN protocol that connects fast and doesn’t drain your battery. - -How does Mullvad VPN work? - -Mullvad VPN allows you to browse the web securely and privately. - -With Mullvad, your traffic travels through an encrypted tunnel to one of our VPN servers and then onward to the website you are visiting. In this way, websites will only see our server’s identity instead of yours. In addition, any information that your internet service provider (ISP) saves cannot be tied specifically to you. +• We offer the possibility to pay anonymously with cash or cryptocurrency. +• Bypass geographical restrictions with our global network of VPN servers. +• Our app uses WireGuard, a superior VPN protocol that connects fast and doesn’t drain your battery. -Using a VPN is a great first step toward protecting your privacy. We believe that privacy is a universal right. +HOW DOES MULLVAD VPN WORK? +With Mullvad VPN, your traffic travels through an encrypted tunnel to one of our VPN servers and then onward to the website you are visiting. In this way, websites will only see our server’s identity instead of yours. Same goes for your ISP (internet service provider); they’ll see that you’re connected to Mullvad, but not your activity. +It also means that all the third-party actors with technology integrated into the various websites you visit can’t sniff out your IP address and use it to track you from one site to another. -For your right to privacy +Using a trustworthy VPN is a great first step to reclaim your privacy online. In combination with Mullvad Browser you make sure to block third-party cookies and other tracking technologies. -Mullvad was founded in 2009 purely with the ambition of upholding the universal right to privacy — for you, for us, for everyone. And not only that, we want to make Internet censorship and mass surveillance ineffective. +FREE THE INTERNET FROM MASS SURVEILLANCE AND DATA COLLECTION +A free and open society is a society where people have the right to privacy. That’s why we fight for a free internet. +Free from mass surveillance and censorship. Free from big data markets where your personal information is up for sale. Free from authorities mass monitoring every click you make. Free from an infrastructure mapping your whole life. Mullvad VPN and Mullvad Browser is our contribution to the fight. -That's a tall order, but if you want to make a change, you've gotta start somewhere. +TELEMETRY AND CRASH REPORTS +The app collects a very minimal amount of telemetry, and it does not in any way tie it to an account number, IP or other identifiable information. Account numbers are used for authentication. App logs are never sent automatically but are rather explicitly sent by the user. App version checks are performed every 24 hours to tell the app if there are any upgrades available and if the currently running version is still supported. -Since our humble beginning, our VPN service has helped to keep users' online activity, identity, and location private. Over the years, we've been blazing a trail forward to provide the most secure and anonymous VPN out there for everyone, for us, for you. +If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device. From c3d575e11f42e13553a34c13da6c9e1f6c24289f Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Wed, 10 Apr 2024 14:56:20 +0200 Subject: [PATCH 055/214] Prevent duplicate list names in custom lists --- ios/MullvadSettings/CustomListRepository.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ios/MullvadSettings/CustomListRepository.swift b/ios/MullvadSettings/CustomListRepository.swift index 4466b2f9aa7d..c6709782e2b2 100644 --- a/ios/MullvadSettings/CustomListRepository.swift +++ b/ios/MullvadSettings/CustomListRepository.swift @@ -39,11 +39,12 @@ public struct CustomListRepository: CustomListRepositoryProtocol { public func save(list: CustomList) throws { var lists = fetchAll() - if let index = lists.firstIndex(where: { $0.id == list.id }) { + if let listWithSameName = lists.first(where: { $0.name.caseInsensitiveCompare(list.name) == .orderedSame }), + listWithSameName.id != list.id { + throw CustomRelayListError.duplicateName + } else if let index = lists.firstIndex(where: { $0.id == list.id }) { lists[index] = list try write(lists) - } else if lists.contains(where: { $0.name == list.name }) { - throw CustomRelayListError.duplicateName } else { lists.append(list) try write(lists) From fb551fdf45ca1680d5e01f1100795f731609155b Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 15 Apr 2024 10:33:00 +0200 Subject: [PATCH 056/214] Add Zlib to allowed licences --- deny.toml | 3 ++- test/deny.toml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deny.toml b/deny.toml index ef0da92bcc56..0066de94827c 100644 --- a/deny.toml +++ b/deny.toml @@ -50,7 +50,8 @@ allow = [ "CC0-1.0", # https://github.com/briansmith/ring/issues/902 "LicenseRef-ring", - "Unicode-DFS-2016" + "Unicode-DFS-2016", + "Zlib" ] [[licenses.clarify]] diff --git a/test/deny.toml b/test/deny.toml index e792575b7710..3ab381a987f3 100644 --- a/test/deny.toml +++ b/test/deny.toml @@ -40,7 +40,8 @@ allow = [ "CC0-1.0", # https://github.com/briansmith/ring/issues/902 "LicenseRef-ring", - "Unicode-DFS-2016" + "Unicode-DFS-2016", + "Zlib" ] [[licenses.clarify]] From a4cbf312b6a09b3cb0d00333a0735dd64ccccb37 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 15 Apr 2024 08:40:55 +0200 Subject: [PATCH 057/214] Update changelog with 2024.2-beta1 section --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c1337065cf..af15f7ddf161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,10 +23,14 @@ Line wrap the file at 100 chars. Th ## [Unreleased] ### Added +- Add custom bridge settings in GUI. + + +## [2024.2-beta1] - 2024-04-15 +### Added - Add automatic MTU detection for desktop platforms. This currently only uses information about dropped packets and does not take fragmentation into account. - Add ability to import server IP overrides in GUI. -- Add custom bridge settings in GUI. ### Changed - Change default obfuscation setting to `auto`. From 71b9e7fbe4a00a37975b35d6d646cec125a9884b Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 15 Apr 2024 08:42:03 +0200 Subject: [PATCH 058/214] Update desktop app version to 2024.2-beta1 --- dist-assets/desktop-product-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist-assets/desktop-product-version.txt b/dist-assets/desktop-product-version.txt index 702b928acc8b..9fb1ab01cafd 100644 --- a/dist-assets/desktop-product-version.txt +++ b/dist-assets/desktop-product-version.txt @@ -1 +1 @@ -2024.1 +2024.2-beta1 From c396d7099944ae2e4f096c9fdedbe64e6242e75e Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Mon, 15 Apr 2024 16:03:42 +0200 Subject: [PATCH 059/214] Fix Add/Save button in ProxyForm --- gui/src/renderer/components/ProxyForm.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/gui/src/renderer/components/ProxyForm.tsx b/gui/src/renderer/components/ProxyForm.tsx index 5fbcf34d0f4e..7e006f65c014 100644 --- a/gui/src/renderer/components/ProxyForm.tsx +++ b/gui/src/renderer/components/ProxyForm.tsx @@ -80,7 +80,7 @@ export function ProxyForm(props: ProxyFormContextProviderProps) { - + ); @@ -128,7 +128,7 @@ export function NamedProxyForm(props: NamedProxyFormContainerProps) { - + @@ -149,8 +149,12 @@ function ProxyFormNameField() { ); } -export function ProxyFormButtons() { - const { proxy, onSave, onCancel, onDelete } = useContext(proxyFormContext); +interface ProxyFormButtonsProps { + new: boolean; +} + +export function ProxyFormButtons(props: ProxyFormButtonsProps) { + const { onSave, onCancel, onDelete } = useContext(proxyFormContext); // Contains form submittability to know whether or not to enable the Add/Save button. const formSubmittable = useSettingsFormSubmittable(); @@ -166,7 +170,7 @@ export function ProxyFormButtons() { )} {messages.gettext('Cancel')} - {proxy === undefined ? messages.gettext('Add') : messages.gettext('Save')} + {props.new ? messages.gettext('Add') : messages.gettext('Save')} ); From 8cbff64336be8d837109e9b4a177eba63f8d7952 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 12 Apr 2024 17:42:04 +0200 Subject: [PATCH 060/214] Add missing `windows_sys` feature Fix `talpid-openvpn` refusing to compile for Windows due to a missing feature: `Win32_System_Com`. Fix `talpid-windows` refusing to compile for Windows due to a missing feature: `Win32_Security`. --- talpid-openvpn/Cargo.toml | 1 + talpid-windows/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/talpid-openvpn/Cargo.toml b/talpid-openvpn/Cargo.toml index c9341add9bd2..dcdbfafdac4d 100644 --- a/talpid-openvpn/Cargo.toml +++ b/talpid-openvpn/Cargo.toml @@ -39,6 +39,7 @@ talpid-windows = { path = "../talpid-windows" } workspace = true features = [ "Win32_Foundation", + "Win32_System_Com", "Win32_System_LibraryLoader", "Win32_System_Registry", "Win32_NetworkManagement_Ndis", diff --git a/talpid-windows/Cargo.toml b/talpid-windows/Cargo.toml index c968397465e3..c786ac7a6673 100644 --- a/talpid-windows/Cargo.toml +++ b/talpid-windows/Cargo.toml @@ -22,6 +22,7 @@ workspace = true features = [ "Win32_Foundation", "Win32_Globalization", + "Win32_Security", "Win32_System_Diagnostics_ToolHelp", "Win32_System_IO", "Win32_Networking_WinSock", From 6b29d785ce142fede1c378631ea258d508010aa3 Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Mon, 15 Apr 2024 10:12:16 +0200 Subject: [PATCH 061/214] Add default settings when app is uninstalled --- ios/MullvadVPN/AppDelegate.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index a2239e3e7cb8..aef14c594af2 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -502,7 +502,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } SettingsManager.resetStore(completely: true) + try? SettingsManager.writeSettings(LatestTunnelSettings()) + // Default access methods need to be repopulated again after settings wipe. self.accessMethodRepository.reloadWithDefaultsAfterDataRemoval() // At app startup, the relay cache tracker will get populated with a list of overriden IPs. // The overriden IPs will get wiped, therefore, the cache needs to be pruned as well. From b111e72a1d3cfc0351b3da6011f47b48c20637be Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Sun, 7 Apr 2024 22:33:45 +0200 Subject: [PATCH 062/214] Intercept back button when leaving an unsaved custom list --- ios/MullvadVPN.xcodeproj/project.pbxproj | 4 + .../InterceptibleNavigationController.swift | 36 +++++++ .../AddCustomListCoordinator.swift | 11 +- .../CustomLists/AddLocationsCoordinator.swift | 17 ++- .../CustomLists/AddLocationsDataSource.swift | 15 ++- .../AddLocationsViewController.swift | 19 +--- .../CustomListViewController.swift | 101 ++++++++++++++++++ .../CustomLists/CustomListViewModel.swift | 5 + .../EditCustomListCoordinator.swift | 11 +- .../EditLocationsCoordinator.swift | 17 ++- .../Coordinators/LocationCoordinator.swift | 2 +- .../LocationCellViewModel.swift | 4 +- .../SelectLocation/LocationDataSource.swift | 66 +++++++++--- .../SelectLocation/LocationNode.swift | 6 ++ .../LocationSectionHeaderView.swift | 7 +- 15 files changed, 243 insertions(+), 78 deletions(-) create mode 100644 ios/MullvadVPN/Classes/InterceptibleNavigationController.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 7879654647b1..15d379e53fb4 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -540,6 +540,7 @@ 7A6F2FAB2AFD3097006D0856 /* CustomDNSCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FAA2AFD3097006D0856 /* CustomDNSCellFactory.swift */; }; 7A6F2FAD2AFD3DA7006D0856 /* CustomDNSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FAC2AFD3DA7006D0856 /* CustomDNSViewController.swift */; }; 7A6F2FAF2AFE36E7006D0856 /* VPNSettingsInfoButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FAE2AFE36E7006D0856 /* VPNSettingsInfoButtonItem.swift */; }; + 7A7907332BC0280A00B61F81 /* InterceptibleNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7907322BC0280A00B61F81 /* InterceptibleNavigationController.swift */; }; 7A7AD28D29DC677800480EF1 /* FirstTimeLaunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */; }; 7A818F1F29F0305800C7F0F4 /* RootConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */; }; 7A83A0C62B29A750008B5CE7 /* APIAccessMethodsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A83A0C52B29A750008B5CE7 /* APIAccessMethodsTests.swift */; }; @@ -1805,6 +1806,7 @@ 7A6F2FAA2AFD3097006D0856 /* CustomDNSCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDNSCellFactory.swift; sourceTree = ""; }; 7A6F2FAC2AFD3DA7006D0856 /* CustomDNSViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDNSViewController.swift; sourceTree = ""; }; 7A6F2FAE2AFE36E7006D0856 /* VPNSettingsInfoButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNSettingsInfoButtonItem.swift; sourceTree = ""; }; + 7A7907322BC0280A00B61F81 /* InterceptibleNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptibleNavigationController.swift; sourceTree = ""; }; 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTimeLaunch.swift; sourceTree = ""; }; 7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootConfiguration.swift; sourceTree = ""; }; 7A83A0C52B29A750008B5CE7 /* APIAccessMethodsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIAccessMethodsTests.swift; sourceTree = ""; }; @@ -2694,6 +2696,7 @@ 58138E60294871C600684F0C /* DeviceDataThrottling.swift */, 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */, 582AE30F2440A6CA00E6733A /* InputTextFormatter.swift */, + 7A7907322BC0280A00B61F81 /* InterceptibleNavigationController.swift */, 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */, 58CC40EE24A601900019D96E /* ObserverList.swift */, ); @@ -5463,6 +5466,7 @@ 7A42DEC92A05164100B209BE /* SettingsInputCell.swift in Sources */, 5803B4B22940A48700C23744 /* TunnelStore.swift in Sources */, 586A950F29012BEE007BAF2B /* AddressCacheTracker.swift in Sources */, + 7A7907332BC0280A00B61F81 /* InterceptibleNavigationController.swift in Sources */, F02F41A02B9723AF00625A4F /* AddLocationsViewController.swift in Sources */, 587B753D2666468F00DEF7E9 /* NotificationController.swift in Sources */, ); diff --git a/ios/MullvadVPN/Classes/InterceptibleNavigationController.swift b/ios/MullvadVPN/Classes/InterceptibleNavigationController.swift new file mode 100644 index 000000000000..69b0c9f428cb --- /dev/null +++ b/ios/MullvadVPN/Classes/InterceptibleNavigationController.swift @@ -0,0 +1,36 @@ +// +// InterceptibleNavigationController.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-04-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +class InterceptibleNavigationController: CustomNavigationController { + var shouldPopViewController: ((UIViewController) -> Bool)? + var shouldPopToViewController: ((UIViewController) -> Bool)? + + // Called when popping the topmost view controller in the stack, eg. by pressing a navigation + // bar back button. + override func popViewController(animated: Bool) -> UIViewController? { + guard let viewController = viewControllers.last else { return nil } + + if shouldPopViewController?(viewController) ?? true { + return super.popViewController(animated: animated) + } else { + return nil + } + } + + // Called when popping to a specific view controller, eg. by long pressing a navigation bar + // back button (revealing a navigation menu) and selecting a destination view controller. + override func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? { + if shouldPopToViewController?(viewController) ?? true { + return super.popToViewController(viewController, animated: animated) + } else { + return nil + } + } +} diff --git a/ios/MullvadVPN/Coordinators/CustomLists/AddCustomListCoordinator.swift b/ios/MullvadVPN/Coordinators/CustomLists/AddCustomListCoordinator.swift index bbbf45ad54bc..6ef8d044fd6b 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/AddCustomListCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/AddCustomListCoordinator.swift @@ -84,17 +84,10 @@ extension AddCustomListCoordinator: CustomListViewControllerDelegate { let coordinator = AddLocationsCoordinator( navigationController: navigationController, nodes: nodes, - customList: list + subject: subject ) - coordinator.didFinish = { [weak self] locationsCoordinator, customList in - guard let self else { return } - subject.send(CustomListViewModel( - id: customList.id, - name: customList.name, - locations: customList.locations, - tableSections: subject.value.tableSections - )) + coordinator.didFinish = { locationsCoordinator in locationsCoordinator.removeFromParent() } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsCoordinator.swift b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsCoordinator.swift index feb5bd415e5b..1634a24e8075 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsCoordinator.swift @@ -6,6 +6,7 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import Combine import MullvadSettings import MullvadTypes import Routing @@ -14,9 +15,9 @@ import UIKit class AddLocationsCoordinator: Coordinator, Presentable, Presenting { private let navigationController: UINavigationController private let nodes: [LocationNode] - private var customList: CustomList + private var subject: CurrentValueSubject - var didFinish: ((AddLocationsCoordinator, CustomList) -> Void)? + var didFinish: ((AddLocationsCoordinator) -> Void)? var presentedViewController: UIViewController { navigationController @@ -25,17 +26,17 @@ class AddLocationsCoordinator: Coordinator, Presentable, Presenting { init( navigationController: UINavigationController, nodes: [LocationNode], - customList: CustomList + subject: CurrentValueSubject ) { self.navigationController = navigationController self.nodes = nodes - self.customList = customList + self.subject = subject } func start() { let controller = AddLocationsViewController( allLocationsNodes: nodes, - customList: customList + subject: subject ) controller.delegate = self @@ -51,11 +52,7 @@ class AddLocationsCoordinator: Coordinator, Presentable, Presenting { } extension AddLocationsCoordinator: AddLocationsViewControllerDelegate { - func didUpdateSelectedLocations(locations: [RelayLocation]) { - customList.locations = locations - } - func didBack() { - didFinish?(self, customList) + didFinish?(self) } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsDataSource.swift b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsDataSource.swift index 048d3e51fa1a..0af9f14d4818 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsDataSource.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsDataSource.swift @@ -6,6 +6,7 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import Combine import MullvadSettings import MullvadTypes import UIKit @@ -15,20 +16,21 @@ class AddLocationsDataSource: LocationDiffableDataSourceProtocol { private var customListLocationNode: CustomListLocationNode private let nodes: [LocationNode] - var didUpdateCustomList: ((CustomListLocationNode) -> Void)? + private let subject: CurrentValueSubject let tableView: UITableView let sections: [LocationSection] init( tableView: UITableView, allLocationNodes: [LocationNode], - customList: CustomList + subject: CurrentValueSubject ) { self.tableView = tableView self.nodes = allLocationNodes + self.subject = subject self.customListLocationNode = CustomListLocationNodeBuilder( - customList: customList, + customList: subject.value.customList, allLocations: self.nodes ).customListLocationNode @@ -51,10 +53,12 @@ class AddLocationsDataSource: reloadWithSelectedLocations() } + // Called from `LocationDiffableDataSourceProtocol`. func nodeShowsChildren(_ node: LocationNode) -> Bool { isLocationInCustomList(node: node) } + // Called from `LocationDiffableDataSourceProtocol`. func nodeShouldBeSelected(_ node: LocationNode) -> Bool { customListLocationNode.children.contains(node) } @@ -149,7 +153,10 @@ extension AddLocationsDataSource: LocationCellDelegate { customListLocationNode.remove(selectedLocation: item.node, with: locationList) } updateDataSnapshot(with: [locationList], completion: { - self.didUpdateCustomList?(self.customListLocationNode) + let locations = self.customListLocationNode.children.reduce([]) { partialResult, locationNode in + partialResult + locationNode.locations + } + self.subject.value.locations = locations }) } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift index c728982fdbbc..1dbd7ac7ae8d 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift @@ -6,19 +6,19 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import Combine import MullvadSettings import MullvadTypes import UIKit protocol AddLocationsViewControllerDelegate: AnyObject { - func didUpdateSelectedLocations(locations: [RelayLocation]) func didBack() } class AddLocationsViewController: UIViewController { private var dataSource: AddLocationsDataSource? private let nodes: [LocationNode] - private let customList: CustomList + private let subject: CurrentValueSubject weak var delegate: AddLocationsViewControllerDelegate? private let tableView: UITableView = { @@ -33,10 +33,10 @@ class AddLocationsViewController: UIViewController { init( allLocationsNodes: [LocationNode], - customList: CustomList + subject: CurrentValueSubject ) { self.nodes = allLocationsNodes - self.customList = customList + self.subject = subject super.init(nibName: nil, bundle: nil) } @@ -70,17 +70,8 @@ class AddLocationsViewController: UIViewController { dataSource = AddLocationsDataSource( tableView: tableView, allLocationNodes: nodes.copy(), - customList: customList + subject: subject ) - - dataSource?.didUpdateCustomList = { [weak self] customListLocationNode in - guard let self else { return } - delegate?.didUpdateSelectedLocations( - locations: customListLocationNode.children.reduce([]) { partialResult, locationNode in - partialResult + locationNode.locations - } - ) - } } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift index 4e5891658dcd..be6f39089f0e 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift @@ -27,6 +27,14 @@ class CustomListViewController: UIViewController { private let alertPresenter: AlertPresenter private var validationErrors: Set = [] + private var persistedCustomList: CustomList? { + return interactor.fetchAll().first(where: { $0.id == subject.value.id }) + } + + private var hasUnsavedChanges: Bool { + persistedCustomList != subject.value.customList + } + private lazy var cellConfiguration: CustomListCellConfiguration = { CustomListCellConfiguration(tableView: tableView, subject: subject) }() @@ -91,9 +99,38 @@ class CustomListViewController: UIViewController { } private func configureNavigationItem() { + if let navigationController = navigationController as? InterceptibleNavigationController { + interceptNavigation(navigationController) + } + + navigationController?.interactivePopGestureRecognizer?.delegate = self navigationItem.rightBarButtonItem = saveBarButton } + private func interceptNavigation(_ navigationController: InterceptibleNavigationController) { + navigationController.shouldPopViewController = { [weak self] viewController in + guard + let self, + viewController is Self, + hasUnsavedChanges + else { return true } + + self.onUnsavedChanges() + return false + } + + navigationController.shouldPopToViewController = { [weak self] viewController in + guard + let self, + viewController is ListCustomListViewController, + hasUnsavedChanges + else { return true } + + self.onUnsavedChanges() + return false + } + } + private func configureTableView() { tableView.delegate = dataSourceConfiguration tableView.backgroundColor = .secondaryColor @@ -195,4 +232,68 @@ class CustomListViewController: UIViewController { alertPresenter.showAlert(presentation: presentation, animated: true) } + + @objc private func onUnsavedChanges() { + let message = NSMutableAttributedString( + markdownString: NSLocalizedString( + "CUSTOM_LISTS_UNSAVED_CHANGES_PROMPT", + tableName: "CustomLists", + value: "You have unsaved changes.", + comment: "" + ), + options: MarkdownStylingOptions(font: .preferredFont(forTextStyle: .body)) + ) + + let presentation = AlertPresentation( + id: "api-custom-lists-unsaved-changes-alert", + icon: .alert, + attributedMessage: message, + buttons: [ + AlertAction( + title: NSLocalizedString( + "CUSTOM_LISTS_DISCARD_CHANGES_BUTTON", + tableName: "CustomLists", + value: "Discard changes", + comment: "" + ), + style: .destructive, + handler: { + // Reset subject/view model to no longer having unsaved changes. + if let persistedCustomList = self.persistedCustomList { + self.subject.value.update(with: persistedCustomList) + } + self.delegate?.customListDidSave(self.subject.value.customList) + } + ), + AlertAction( + title: NSLocalizedString( + "CUSTOM_LISTS_BACK_TO_EDITING_BUTTON", + tableName: "CustomLists", + value: "Back to editing", + comment: "" + ), + style: .default + ), + ] + ) + + alertPresenter.showAlert(presentation: presentation, animated: true) + } +} + +extension CustomListViewController: UIGestureRecognizerDelegate { + // For some reason, intercepting `popViewController(animated: Bool)` in `InterceptibleNavigationController` + // by SWIPING back leads to weird behaviour where subsequent navigation seem to happen systemwise but not + // UI-wise. This leads to the UI freezing up, and the only remedy is to restart the app. + // + // To get around this issue we can intercept the back swipe gesture and manually perform the transition + // instead, thereby bypassing the inner mechanisms that seem to go out of sync. + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + guard gestureRecognizer == navigationController?.interactivePopGestureRecognizer else { + return true + } + + navigationController?.popViewController(animated: true) + return false + } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewModel.swift b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewModel.swift index b41d52d2f572..10b9e1359242 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewModel.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewModel.swift @@ -18,4 +18,9 @@ struct CustomListViewModel { var customList: CustomList { CustomList(id: id, name: name, locations: locations) } + + mutating func update(with list: CustomList) { + name = list.name + locations = list.locations + } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift b/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift index 5545f1bc951b..8cc441738ae9 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift @@ -78,17 +78,10 @@ extension EditCustomListCoordinator: CustomListViewControllerDelegate { let coordinator = EditLocationsCoordinator( navigationController: navigationController, nodes: nodes, - customList: list + subject: subject ) - coordinator.didFinish = { [weak self] locationsCoordinator, customList in - guard let self else { return } - subject.send(CustomListViewModel( - id: customList.id, - name: customList.name, - locations: customList.locations, - tableSections: subject.value.tableSections - )) + coordinator.didFinish = { locationsCoordinator in locationsCoordinator.removeFromParent() } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/EditLocationsCoordinator.swift b/ios/MullvadVPN/Coordinators/CustomLists/EditLocationsCoordinator.swift index 9255a2bc29db..9ca615ea693b 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/EditLocationsCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/EditLocationsCoordinator.swift @@ -6,6 +6,7 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import Combine import MullvadSettings import MullvadTypes import Routing @@ -14,9 +15,9 @@ import UIKit class EditLocationsCoordinator: Coordinator, Presentable, Presenting { private let navigationController: UINavigationController private let nodes: [LocationNode] - private var customList: CustomList + private var subject: CurrentValueSubject - var didFinish: ((EditLocationsCoordinator, CustomList) -> Void)? + var didFinish: ((EditLocationsCoordinator) -> Void)? var presentedViewController: UIViewController { navigationController @@ -25,17 +26,17 @@ class EditLocationsCoordinator: Coordinator, Presentable, Presenting { init( navigationController: UINavigationController, nodes: [LocationNode], - customList: CustomList + subject: CurrentValueSubject ) { self.navigationController = navigationController self.nodes = nodes - self.customList = customList + self.subject = subject } func start() { let controller = AddLocationsViewController( allLocationsNodes: nodes, - customList: customList + subject: subject ) controller.delegate = self @@ -50,11 +51,7 @@ class EditLocationsCoordinator: Coordinator, Presentable, Presenting { } extension EditLocationsCoordinator: AddLocationsViewControllerDelegate { - func didUpdateSelectedLocations(locations: [RelayLocation]) { - customList.locations = locations - } - func didBack() { - didFinish?(self, customList) + didFinish?(self) } } diff --git a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift index e146a7cd06a3..30fe23d8e3fa 100644 --- a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift @@ -147,7 +147,7 @@ class LocationCoordinator: Coordinator, Presentable, Presenting { private func showEditCustomLists(nodes: [LocationNode]) { let coordinator = ListCustomListCoordinator( - navigationController: CustomNavigationController(), + navigationController: InterceptibleNavigationController(), interactor: CustomListInteractor(repository: customListRepository), tunnelManager: tunnelManager, nodes: nodes diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift index df0a3ba62c15..f5f3054fdc6b 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift @@ -17,12 +17,14 @@ struct LocationCellViewModel: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(section) hasher.combine(node) + hasher.combine(indentationLevel) } static func == (lhs: Self, rhs: Self) -> Bool { lhs.node == rhs.node && lhs.section == rhs.section && - lhs.isSelected == rhs.isSelected + lhs.isSelected == rhs.isSelected && + lhs.indentationLevel == rhs.indentationLevel } } diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift index 42957d24d671..b62c13f1ee33 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift @@ -71,7 +71,7 @@ final class LocationDataSource: filterRelays(by: currentSearchString) } - func filterRelays(by searchString: String, scrollToSelected: Bool = true) { + func filterRelays(by searchString: String) { currentSearchString = searchString let list = sections.enumerated().map { index, section in @@ -84,11 +84,6 @@ final class LocationDataSource: } updateDataSnapshot(with: list, reloadExisting: !searchString.isEmpty) { - guard scrollToSelected else { - self.setSelectedItem(self.selectedItem, animated: false) - return - } - if searchString.isEmpty { self.setSelectedItem(self.selectedItem, animated: false, completion: { self.scrollToSelectedRelay() @@ -99,23 +94,54 @@ final class LocationDataSource: } } + /// Refreshes the custom list section and keeps all modifications intact (selection and expanded states). func refreshCustomLists(selectedRelays: UserSelectedRelays?) { - let allLocationsDataSource = - dataSources.first(where: { $0 is AllLocationDataSource }) as? AllLocationDataSource - - let customListsDataSource = + guard let allLocationsDataSource = + dataSources.first(where: { $0 is AllLocationDataSource }) as? AllLocationDataSource, + let customListsDataSource = dataSources.first(where: { $0 is CustomListsDataSource }) as? CustomListsDataSource + else { + return + } - customListsDataSource?.reload(allLocationNodes: allLocationsDataSource?.nodes ?? [], isFiltered: hasFilter) + // Take a "snapshot" of the currently expanded nodes. + let expandedNodes = customListsDataSource.nodes + .flatMap { [$0] + $0.flattened } + .filter { $0.showsChildren } + + // Reload data source with (possibly) updated custom lists. + customListsDataSource.reload(allLocationNodes: allLocationsDataSource.nodes, isFiltered: hasFilter) + // Reapply current selection. mapSelectedItem(from: selectedRelays) - filterRelays(by: currentSearchString, scrollToSelected: false) + + // Reapply current search filter. + let searchResultNodes = dataSources[0].search(by: currentSearchString) + + // Reapply expanded status and override nodes being hidden by search filter. + RootLocationNode(children: searchResultNodes).forEachDescendant { node in + node.showsChildren = expandedNodes.contains(node) + node.isHiddenFromSearch = false + } + + // Construct node tree. + let list = searchResultNodes.flatMap { node in + let rootNode = RootLocationNode(children: [node]) + return recursivelyCreateCellViewModelTree(for: rootNode, in: .customLists, indentationLevel: 0) + } + + updateDataSnapshot(with: [ + list, + snapshot().itemIdentifiers(inSection: .allLocations), + ], reloadExisting: true) } + // Called from `LocationDiffableDataSourceProtocol`. func nodeShowsChildren(_ node: LocationNode) -> Bool { node.showsChildren } + // Called from `LocationDiffableDataSourceProtocol`. func nodeShouldBeSelected(_ node: LocationNode) -> Bool { false } @@ -136,11 +162,19 @@ final class LocationDataSource: if let customListSelection = selectedRelays.customListSelection, let customList = customListsDataSource?.customList(by: customListSelection.listId), let selectedNode = customListsDataSource?.node(by: selectedRelays, for: customList) { - selectedItem = LocationCellViewModel(section: .customLists, node: selectedNode) + selectedItem = LocationCellViewModel( + section: .customLists, + node: selectedNode, + indentationLevel: selectedNode.hierarchyLevel + ) // Look for a matching all locations node. } else if let location = selectedRelays.locations.first, let selectedNode = allLocationsDataSource?.node(by: location) { - selectedItem = LocationCellViewModel(section: .allLocations, node: selectedNode) + selectedItem = LocationCellViewModel( + section: .allLocations, + node: selectedNode, + indentationLevel: selectedNode.hierarchyLevel + ) } } } @@ -175,7 +209,7 @@ final class LocationDataSource: indentationLevel: 1 ) - // Insert the new node tree below the select item. + // Insert the new node tree below the selected item. var snapshotItems = snapshot().itemIdentifiers(inSection: selectedItem.section) snapshotItems.insert(contentsOf: nodesToAdd, at: indexPath.row + 1) @@ -243,7 +277,7 @@ extension LocationDataSource: UITableViewDelegate { func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if let item = itemIdentifier(for: indexPath), item == selectedItem { - tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) + cell.setSelected(true, animated: false) } } diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationNode.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationNode.swift index fbf2fbf8fbf5..4534ac4d8e01 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationNode.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationNode.swift @@ -45,6 +45,12 @@ extension LocationNode { parent?.root ?? self } + var hierarchyLevel: Int { + var level = 0 + forEachAncestor { _ in level += 1 } + return level + } + func countryFor(code: String) -> LocationNode? { self.code == code ? self : children.first(where: { $0.code == code }) } diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift index 4a137d9cc1bb..220df6323df1 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift @@ -54,10 +54,9 @@ class LocationSectionHeaderView: UIView, UIContentView { addConstrainedSubviews([nameLabel, actionButton]) { nameLabel.pinEdgesToSuperviewMargins(.all().excluding(.trailing)) - actionButton.pinEdgesToSuperviewMargins(PinnableEdges([.trailing(.zero)])) - actionButton.widthAnchor.constraint(equalToConstant: 24) - actionButton.heightAnchor.constraint(equalTo: actionButton.widthAnchor, multiplier: 1) - actionButton.centerYAnchor.constraint(equalTo: self.centerYAnchor) + actionButton.pinEdgesToSuperview(PinnableEdges([.trailing(8)])) + actionButton.heightAnchor.constraint(equalTo: heightAnchor) + actionButton.widthAnchor.constraint(equalTo: actionButton.heightAnchor) actionButton.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 16) } From 0149f0084c691d9e7c7f9efad1e8e3a3d907c5dd Mon Sep 17 00:00:00 2001 From: mojganii Date: Fri, 12 Apr 2024 11:51:09 +0200 Subject: [PATCH 063/214] Move tracking unsaved changes into coordinator --- .../InterceptibleNavigationController.swift | 2 + .../CustomListViewController.swift | 101 +----------------- .../EditCustomListCoordinator.swift | 92 +++++++++++++++- .../ListCustomListCoordinator.swift | 14 +++ .../LocationCellViewModel.swift | 5 +- .../SelectLocation/LocationDataSource.swift | 2 +- 6 files changed, 114 insertions(+), 102 deletions(-) diff --git a/ios/MullvadVPN/Classes/InterceptibleNavigationController.swift b/ios/MullvadVPN/Classes/InterceptibleNavigationController.swift index 69b0c9f428cb..f93795c5913f 100644 --- a/ios/MullvadVPN/Classes/InterceptibleNavigationController.swift +++ b/ios/MullvadVPN/Classes/InterceptibleNavigationController.swift @@ -14,6 +14,7 @@ class InterceptibleNavigationController: CustomNavigationController { // Called when popping the topmost view controller in the stack, eg. by pressing a navigation // bar back button. + @discardableResult override func popViewController(animated: Bool) -> UIViewController? { guard let viewController = viewControllers.last else { return nil } @@ -26,6 +27,7 @@ class InterceptibleNavigationController: CustomNavigationController { // Called when popping to a specific view controller, eg. by long pressing a navigation bar // back button (revealing a navigation menu) and selecting a destination view controller. + @discardableResult override func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? { if shouldPopToViewController?(viewController) ?? true { return super.popToViewController(viewController, animated: animated) diff --git a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift index be6f39089f0e..a3a7518af8dc 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift @@ -31,7 +31,7 @@ class CustomListViewController: UIViewController { return interactor.fetchAll().first(where: { $0.id == subject.value.id }) } - private var hasUnsavedChanges: Bool { + var hasUnsavedChanges: Bool { persistedCustomList != subject.value.customList } @@ -83,12 +83,12 @@ class CustomListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + navigationItem.rightBarButtonItem = saveBarButton view.directionalLayoutMargins = UIMetrics.contentLayoutMargins view.backgroundColor = .secondaryColor isModalInPresentation = true addSubviews() - configureNavigationItem() configureDataSource() configureTableView() @@ -98,39 +98,6 @@ class CustomListViewController: UIViewController { }.store(in: &cancellables) } - private func configureNavigationItem() { - if let navigationController = navigationController as? InterceptibleNavigationController { - interceptNavigation(navigationController) - } - - navigationController?.interactivePopGestureRecognizer?.delegate = self - navigationItem.rightBarButtonItem = saveBarButton - } - - private func interceptNavigation(_ navigationController: InterceptibleNavigationController) { - navigationController.shouldPopViewController = { [weak self] viewController in - guard - let self, - viewController is Self, - hasUnsavedChanges - else { return true } - - self.onUnsavedChanges() - return false - } - - navigationController.shouldPopToViewController = { [weak self] viewController in - guard - let self, - viewController is ListCustomListViewController, - hasUnsavedChanges - else { return true } - - self.onUnsavedChanges() - return false - } - } - private func configureTableView() { tableView.delegate = dataSourceConfiguration tableView.backgroundColor = .secondaryColor @@ -232,68 +199,4 @@ class CustomListViewController: UIViewController { alertPresenter.showAlert(presentation: presentation, animated: true) } - - @objc private func onUnsavedChanges() { - let message = NSMutableAttributedString( - markdownString: NSLocalizedString( - "CUSTOM_LISTS_UNSAVED_CHANGES_PROMPT", - tableName: "CustomLists", - value: "You have unsaved changes.", - comment: "" - ), - options: MarkdownStylingOptions(font: .preferredFont(forTextStyle: .body)) - ) - - let presentation = AlertPresentation( - id: "api-custom-lists-unsaved-changes-alert", - icon: .alert, - attributedMessage: message, - buttons: [ - AlertAction( - title: NSLocalizedString( - "CUSTOM_LISTS_DISCARD_CHANGES_BUTTON", - tableName: "CustomLists", - value: "Discard changes", - comment: "" - ), - style: .destructive, - handler: { - // Reset subject/view model to no longer having unsaved changes. - if let persistedCustomList = self.persistedCustomList { - self.subject.value.update(with: persistedCustomList) - } - self.delegate?.customListDidSave(self.subject.value.customList) - } - ), - AlertAction( - title: NSLocalizedString( - "CUSTOM_LISTS_BACK_TO_EDITING_BUTTON", - tableName: "CustomLists", - value: "Back to editing", - comment: "" - ), - style: .default - ), - ] - ) - - alertPresenter.showAlert(presentation: presentation, animated: true) - } -} - -extension CustomListViewController: UIGestureRecognizerDelegate { - // For some reason, intercepting `popViewController(animated: Bool)` in `InterceptibleNavigationController` - // by SWIPING back leads to weird behaviour where subsequent navigation seem to happen systemwise but not - // UI-wise. This leads to the UI freezing up, and the only remedy is to restart the app. - // - // To get around this issue we can intercept the back swipe gesture and manually perform the transition - // instead, thereby bypassing the inner mechanisms that seem to go out of sync. - func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - guard gestureRecognizer == navigationController?.interactivePopGestureRecognizer else { - return true - } - - navigationController?.popViewController(animated: true) - return false - } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift b/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift index 8cc441738ae9..2e3c8c9a0c65 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift @@ -21,12 +21,16 @@ class EditCustomListCoordinator: Coordinator, Presentable, Presenting { let customList: CustomList let nodes: [LocationNode] let subject: CurrentValueSubject + private lazy var alertPresenter: AlertPresenter = { + AlertPresenter(context: self) + }() var presentedViewController: UIViewController { navigationController } var didFinish: ((EditCustomListCoordinator, FinishAction, CustomList) -> Void)? + var didCancel: ((EditCustomListCoordinator) -> Void)? init( navigationController: UINavigationController, @@ -50,7 +54,7 @@ class EditCustomListCoordinator: Coordinator, Presentable, Presenting { let controller = CustomListViewController( interactor: customListInteractor, subject: subject, - alertPresenter: AlertPresenter(context: self) + alertPresenter: alertPresenter ) controller.delegate = self @@ -61,7 +65,77 @@ class EditCustomListCoordinator: Coordinator, Presentable, Presenting { comment: "" ) + navigationController.interactivePopGestureRecognizer?.delegate = self navigationController.pushViewController(controller, animated: true) + + guard let interceptibleNavigationController = navigationController as? InterceptibleNavigationController else { + return + } + + interceptibleNavigationController.shouldPopViewController = { [weak self] viewController in + guard + let self, + let customListViewController = viewController as? CustomListViewController, + customListViewController.hasUnsavedChanges + else { return true } + + presentUnsavedChangesDialog() + return false + } + + interceptibleNavigationController.shouldPopToViewController = { [weak self] viewController in + guard + let self, + let customListViewController = viewController as? CustomListViewController, + customListViewController.hasUnsavedChanges + else { return true } + + presentUnsavedChangesDialog() + return false + } + } + + private func presentUnsavedChangesDialog() { + let message = NSMutableAttributedString( + markdownString: NSLocalizedString( + "CUSTOM_LISTS_UNSAVED_CHANGES_PROMPT", + tableName: "CustomLists", + value: "You have unsaved changes.", + comment: "" + ), + options: MarkdownStylingOptions(font: .preferredFont(forTextStyle: .body)) + ) + + let presentation = AlertPresentation( + id: "api-custom-lists-unsaved-changes-alert", + icon: .alert, + attributedMessage: message, + buttons: [ + AlertAction( + title: NSLocalizedString( + "CUSTOM_LISTS_DISCARD_CHANGES_BUTTON", + tableName: "CustomLists", + value: "Discard changes", + comment: "" + ), + style: .destructive, + handler: { + self.didCancel?(self) + } + ), + AlertAction( + title: NSLocalizedString( + "CUSTOM_LISTS_BACK_TO_EDITING_BUTTON", + tableName: "CustomLists", + value: "Back to editing", + comment: "" + ), + style: .default + ), + ] + ) + + alertPresenter.showAlert(presentation: presentation, animated: true) } } @@ -90,3 +164,19 @@ extension EditCustomListCoordinator: CustomListViewControllerDelegate { addChild(coordinator) } } + +extension EditCustomListCoordinator: UIGestureRecognizerDelegate { + // For some reason, intercepting `popViewController(animated: Bool)` in `InterceptibleNavigationController` + // by SWIPING back leads to weird behaviour where subsequent navigation seem to happen systemwise but not + // UI-wise. This leads to the UI freezing up, and the only remedy is to restart the app. + // + // To get around this issue we can intercept the back swipe gesture and manually perform the transition + // instead, thereby bypassing the inner mechanisms that seem to go out of sync. + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + guard gestureRecognizer == navigationController.interactivePopGestureRecognizer else { + return true + } + navigationController.popViewController(animated: true) + return false + } +} diff --git a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift index 2b238dd1e5de..713458e5b540 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift @@ -65,6 +65,12 @@ class ListCustomListCoordinator: Coordinator, Presentable, Presenting { self.updateRelayConstraints(for: action, in: list) } + coordinator.didCancel = { [weak self] editCustomListCoordinator in + guard let self else { return } + popToList() + editCustomListCoordinator.removeFromParent() + } + coordinator.start() addChild(coordinator) } @@ -84,6 +90,14 @@ class ListCustomListCoordinator: Coordinator, Presentable, Presenting { customListSelection: UserSelectedRelays.CustomListSelection(listId: list.id, isList: true) ) relayConstraints.locations = .only(selectedRelays) + } else { + let selectedConstraintIsRemovedFromList = list.locations.filter { + relayConstraints.locations.value?.locations.contains($0) ?? false + }.isEmpty + + if selectedConstraintIsRemovedFromList { + relayConstraints.locations = .only(UserSelectedRelays(locations: [])) + } } case .delete: relayConstraints.locations = .only(UserSelectedRelays(locations: [])) diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift index f5f3054fdc6b..14b7745efd27 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationCellViewModel.swift @@ -15,13 +15,16 @@ struct LocationCellViewModel: Hashable { var isSelected = false func hash(into hasher: inout Hasher) { - hasher.combine(section) hasher.combine(node) + hasher.combine(node.children.count) + hasher.combine(section) + hasher.combine(isSelected) hasher.combine(indentationLevel) } static func == (lhs: Self, rhs: Self) -> Bool { lhs.node == rhs.node && + lhs.node.children.count == rhs.node.children.count && lhs.section == rhs.section && lhs.isSelected == rhs.isSelected && lhs.indentationLevel == rhs.indentationLevel diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift index b62c13f1ee33..3272f0e65b4e 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift @@ -185,7 +185,7 @@ final class LocationDataSource: let rootNode = selectedItem.node.root - // Exit early if no changes to the node tree are necessary. + // Exit early if no changes to the node tree should be made. guard selectedItem.node != rootNode else { completion?() return From 25ea585ba1b1b06830ae1dd30e04e34e21b3b246 Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Tue, 16 Apr 2024 11:18:35 +0200 Subject: [PATCH 064/214] Limit the name of a custom list to 30 characters --- .../CustomLists/CustomListCellConfiguration.swift | 1 + .../View controllers/SelectLocation/LocationCell.swift | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift b/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift index b1db9122eb6c..4731a74cc890 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift @@ -75,6 +75,7 @@ struct CustomListCellConfiguration { contentConfiguration.setPlaceholder(type: .required) contentConfiguration.textFieldProperties = .withSmartFeaturesDisabled() contentConfiguration.inputText = subject.value.name + contentConfiguration.maxLength = 30 contentConfiguration.editingEvents.onChange = subject.bindTextAction(to: \.name) cell.contentConfiguration = contentConfiguration diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift index d68417f4c30d..63ff5bcaeede 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift @@ -20,9 +20,8 @@ class LocationCell: UITableViewCell { let label = UILabel() label.font = UIFont.systemFont(ofSize: 16) label.textColor = .white - label.lineBreakMode = .byWordWrapping - label.numberOfLines = 0 - label.lineBreakStrategy = [] + label.lineBreakMode = .byTruncatingTail + label.numberOfLines = 1 return label }() From 77d21ad97bedef15a0350e4cc5ee492b35ce332c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Mon, 15 Apr 2024 23:32:02 +0200 Subject: [PATCH 065/214] Fix custom DNS address being restored incorrectly --- talpid-core/src/dns/macos.rs | 114 ++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 35 deletions(-) diff --git a/talpid-core/src/dns/macos.rs b/talpid-core/src/dns/macos.rs index b125bbaf29c9..ea70951b336f 100644 --- a/talpid-core/src/dns/macos.rs +++ b/talpid-core/src/dns/macos.rs @@ -84,7 +84,7 @@ impl State { match &self.dns_settings { None => { self.dns_settings = Some(new_settings); - self.update_known_state(store); + self.update_and_apply_state(store); } Some(old_settings) => { if new_settings.address_set() != old_settings.address_set() { @@ -99,54 +99,98 @@ impl State { Ok(()) } - /// Apply the desired DNS settings to all interfaces, and save the original state. The operation - /// is idempotent. - fn update_known_state(&mut self, store: &SCDynamicStore) { - let Some(expected_settings) = &self.dns_settings else { + /// Store changes to the DNS config, ignoring any changes that we have applied. Then apply our + /// desired state to any services to which it has not already been applied. + fn update_and_apply_state(&mut self, store: &SCDynamicStore) { + let actual_state = read_all_dns(store); + self.update_backup_state(&actual_state); + self.apply_desired_state(store, &actual_state); + } + + /// Store changes to the DNS config, ignoring any changes that we have applied. The operation is + /// idempotent. + fn update_backup_state(&mut self, actual_state: &HashMap>) { + let Some(ref desired_settings) = self.dns_settings else { return; }; - let new_settings = read_all_dns(store); - let mut prev_settings = mem::take(&mut self.backup); - - for (path, settings) in new_settings { - let old_entry = prev_settings.remove(&path).flatten(); - - let should_set_dns = match settings { - Some(settings) => { - if settings.address_set() != expected_settings.address_set() { - let servers = settings.server_addresses().join(","); - log::debug!("Saving DNS settings [{}] for {}", servers, path); - self.backup.insert(path.to_owned(), Some(settings)); - true - } else { - self.backup.insert(path.to_owned(), old_entry); - false - } - } - None => { - self.backup.insert(path.to_owned(), None); - true - } - }; + let prev_state = mem::take(&mut self.backup); + let desired_set = desired_settings.address_set(); - if should_set_dns { - let path_cf = CFString::new(&path); - if let Err(e) = expected_settings.save(store, path_cf) { - log::error!("Failed changing DNS for {}: {}", path, e); + self.backup = Self::merge_states(actual_state, prev_state, desired_set); + } + + /// Merge `new_state` set by the OS with a previous `prev_state`, but ignore any service whose + /// addresses are `ignore_addresses`. + fn merge_states( + new_state: &HashMap>, + mut prev_state: HashMap>, + ignore_addresses: BTreeSet, + ) -> HashMap> { + let mut modified_state = HashMap::new(); + + for (path, settings) in new_state { + let old_entry = prev_state.remove(path); + match settings { + // If the service is using the desired addresses, don't save changes + Some(settings) if settings.address_set() == ignore_addresses => { + let settings = old_entry.unwrap_or_else(|| Some(settings.to_owned())); + modified_state.insert(path.to_owned(), settings); + } + // Otherwise, save the new settings + settings => { + let servers = settings + .as_ref() + .map(|settings| settings.server_addresses().join(",")) + .unwrap_or_default(); + log::debug!("Saving DNS settings [{}] for {}", servers, path); + modified_state.insert(path.to_owned(), settings.to_owned()); } } } - for path in prev_settings.keys() { + for path in prev_state.keys() { log::debug!("DNS removed for {path}"); } + + modified_state + } + + /// Apply the desired addresses to all network services. The operation is idempotent. + fn apply_desired_state( + &mut self, + store: &SCDynamicStore, + actual_state: &HashMap>, + ) { + let Some(ref desired_settings) = self.dns_settings else { + return; + }; + let desired_set = desired_settings.address_set(); + + for (path, settings) in actual_state { + match settings { + // Do nothing if the state is already what we want + Some(settings) if settings.address_set() == desired_set => (), + // Apply desired state to service + _ => { + let path_cf = CFString::new(path); + if let Err(e) = desired_settings.save(store, path_cf) { + log::error!("Failed changing DNS for {}: {}", path, e); + } + } + } + } } fn reset(&mut self, store: &SCDynamicStore) -> Result<()> { log::trace!("Restoring DNS settings to: {:#?}", self.backup); - let old_backup = std::mem::take(&mut self.backup); + + let actual_state = read_all_dns(store); + self.update_backup_state(&actual_state); self.dns_settings.take(); + + let old_backup = std::mem::take(&mut self.backup); + for (service_path, settings) in old_backup { if let Some(settings) = settings { settings.save(store, service_path.as_str())?; @@ -359,7 +403,7 @@ fn create_dynamic_store(state: Arc>) -> Result { BURST_LONGEST_BUFFER_PERIOD, move || { if let Some(store) = &*store_container.read().unwrap() { - state.lock().update_known_state(&store.store); + state.lock().update_and_apply_state(&store.store); } }, ); From 3efb7a7bdaf0c4e3c59d62735eb1d314bf202b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Tue, 16 Apr 2024 01:28:40 +0200 Subject: [PATCH 066/214] Add unit tests for macOS DNS monitor --- talpid-core/src/dns/macos.rs | 196 +++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/talpid-core/src/dns/macos.rs b/talpid-core/src/dns/macos.rs index ea70951b336f..e0535fedf7a0 100644 --- a/talpid-core/src/dns/macos.rs +++ b/talpid-core/src/dns/macos.rs @@ -491,3 +491,199 @@ fn state_to_setup_path(state_path: &str) -> Option { None } } + +#[cfg(test)] +mod test { + use super::{DnsSettings, State}; + use std::collections::{BTreeSet, HashMap}; + + /// The initial backup should equal whatever the first provided state is. + #[test] + fn test_backup_new_dns_config() { + let prev_state = HashMap::new(); + + let new_state = HashMap::from([ + ("a".to_owned(), None), + ( + "b".to_owned(), + Some(DnsSettings::from_server_addresses( + &["1.2.3.4".to_owned()], + "iface_b".to_owned(), + )), + ), + // One of our states already equals the desired state. It should be stored regardless. + ( + "c".to_owned(), + Some(DnsSettings::from_server_addresses( + &["10.64.0.1".to_owned()], + "iface_c".to_owned(), + )), + ), + ]); + + let desired_addresses: BTreeSet = ["10.64.0.1".to_owned()].into(); + + let merged_state = State::merge_states(&new_state, prev_state, desired_addresses); + + assert_eq!(merged_state, new_state); + } + + /// Any changes equal to the desired state should be ignored. Other changes should be recorded. + #[test] + fn test_backup_ignore_desired_state() { + let prev_state = HashMap::from([ + ("a".to_owned(), None), + ( + "b".to_owned(), + Some(DnsSettings::from_server_addresses( + &["1.2.3.4".to_owned()], + "iface_b".to_owned(), + )), + ), + ( + "c".to_owned(), + Some(DnsSettings::from_server_addresses( + &["10.64.0.1".to_owned()], + "iface_c".to_owned(), + )), + ), + ( + "d".to_owned(), + Some(DnsSettings::from_server_addresses( + &["1.3.3.7".to_owned()], + "iface_d".to_owned(), + )), + ), + ]); + let new_state = HashMap::from([ + // This change should be ignored + ( + "a".to_owned(), + Some(DnsSettings::from_server_addresses( + &["10.64.0.1".to_owned()], + "iface_a".to_owned(), + )), + ), + // This change should be ignored + ( + "b".to_owned(), + Some(DnsSettings::from_server_addresses( + &["10.64.0.1".to_owned()], + "iface_b".to_owned(), + )), + ), + // This change should be ignored + ( + "c".to_owned(), + Some(DnsSettings::from_server_addresses( + &["4.3.2.1".to_owned()], + "iface_c".to_owned(), + )), + ), + // This change should NOT be ignored + ( + "d".to_owned(), + Some(DnsSettings::from_server_addresses( + &["4.3.2.1".to_owned()], + "iface_d".to_owned(), + )), + ), + ]); + let expect_state = HashMap::from([ + ("a".to_owned(), None), + ( + "b".to_owned(), + Some(DnsSettings::from_server_addresses( + &["1.2.3.4".to_owned()], + "iface_b".to_owned(), + )), + ), + ( + "c".to_owned(), + Some(DnsSettings::from_server_addresses( + &["4.3.2.1".to_owned()], + "iface_c".to_owned(), + )), + ), + ( + "d".to_owned(), + Some(DnsSettings::from_server_addresses( + &["4.3.2.1".to_owned()], + "iface_d".to_owned(), + )), + ), + ]); + + let desired_addresses: BTreeSet = ["10.64.0.1".to_owned()].into(); + + let merged_state = State::merge_states(&new_state, prev_state, desired_addresses); + + assert_eq!(merged_state, expect_state); + } + + /// Services not specified in the new state should be removed from the backed up state + #[test] + fn test_backup_remove_dns_config() { + let prev_state = HashMap::from([ + ( + "a".to_owned(), + Some(DnsSettings::from_server_addresses( + &["10.64.0.1".to_owned()], + "iface_a".to_owned(), + )), + ), + ( + "b".to_owned(), + Some(DnsSettings::from_server_addresses( + &["1.2.3.4".to_owned()], + "iface_b".to_owned(), + )), + ), + ("c".to_owned(), None), + ]); + let new_state = HashMap::from([("c".to_owned(), None)]); + let expected_state = new_state.clone(); + + let desired_addresses: BTreeSet = ["10.64.0.1".to_owned()].into(); + + let merged_state = State::merge_states(&new_state, prev_state, desired_addresses); + + assert_eq!(merged_state, expected_state); + } + + /// If DHCP provides an IP identical to our desired state, the tracked state will not reflect + /// this. This is a known limitation. + // TODO: This should actually succeed. If we happen to switch to a network whose IP equals + // the "desired IP", we should still back up the result. + #[test] + #[should_panic] + fn test_backup_change_equals_desired_state() { + let prev_state = HashMap::from([( + "a".to_owned(), + Some(DnsSettings::from_server_addresses( + &["192.168.100.1".to_owned()], + "iface_a".to_owned(), + )), + )]); + let new_state = HashMap::from([( + "a".to_owned(), + Some(DnsSettings::from_server_addresses( + &["192.168.1.1".to_owned()], + "iface_a".to_owned(), + )), + )]); + let expect_state = HashMap::from([( + "a".to_owned(), + Some(DnsSettings::from_server_addresses( + &["192.168.1.1".to_owned()], + "iface_a".to_owned(), + )), + )]); + + let desired_addresses: BTreeSet = ["192.168.1.1".to_owned()].into(); + + let merged_state = State::merge_states(&new_state, prev_state, desired_addresses); + + assert_eq!(merged_state, expect_state); + } +} From a7d589f10b9f81f2edf041979dd26669e0fe9fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Tue, 16 Apr 2024 09:21:35 +0200 Subject: [PATCH 067/214] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af15f7ddf161..d37ca3025acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,10 @@ Line wrap the file at 100 chars. Th ### Added - Add custom bridge settings in GUI. +### Fixed +#### macOS +- DNS was not properly restored in some cases when using custom DNS. + ## [2024.2-beta1] - 2024-04-15 ### Added From 9b9a6a16cd382516b9af84f3626ad968d014baf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Tue, 16 Apr 2024 11:29:17 +0200 Subject: [PATCH 068/214] Fix overflow & max lines for custom list name --- .../mullvadvpn/compose/cell/NavigationComposeCell.kt | 5 ++++- .../mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt | 5 ++++- .../net/mullvad/mullvadvpn/compose/cell/TwoRowCell.kt | 9 +++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt index 27b74227cae7..7fbc4bdda380 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.lib.theme.AppTheme @@ -99,7 +100,9 @@ internal fun NavigationTitleView( text = title, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onPrimary, - modifier = modifier + modifier = modifier, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt index 032695be8895..0342a0f5e7af 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import net.mullvad.mullvadvpn.R @@ -296,7 +297,9 @@ private fun Name(modifier: Modifier = Modifier, relay: RelayItem) { AlphaInactive } ) - .padding(horizontal = Dimens.smallPadding, vertical = Dimens.mediumPadding) + .padding(horizontal = Dimens.smallPadding, vertical = Dimens.mediumPadding), + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/TwoRowCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/TwoRowCell.kt index 0b1f36d21d95..17eb5d315a34 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/TwoRowCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/TwoRowCell.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.Dimens @@ -33,13 +34,17 @@ fun TwoRowCell( modifier = Modifier.fillMaxWidth(), text = titleText, style = MaterialTheme.typography.labelLarge, - color = titleColor + color = titleColor, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) Text( modifier = Modifier.fillMaxWidth(), text = subtitleText, style = MaterialTheme.typography.labelLarge, - color = subtitleColor + color = subtitleColor, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } }, From a8f5a9097d20448774828cd3349f5bbc228843f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Tue, 16 Apr 2024 14:03:51 +0200 Subject: [PATCH 069/214] Trim custom list name and limit to 30 characters --- .../compose/data/DummyRelayItems.kt | 15 +++++++++++-- .../compose/communication/CustomListAction.kt | 12 +++++++---- .../compose/communication/CustomListResult.kt | 9 ++++---- .../component/CustomListNameTextField.kt | 2 ++ .../relaylist/CustomListExtensions.kt | 3 ++- .../mullvad/mullvadvpn/relaylist/RelayItem.kt | 4 +++- .../repository/CustomListsRepository.kt | 9 ++++---- .../customlists/CustomListActionUseCase.kt | 9 ++++---- .../CreateCustomListDialogViewModel.kt | 3 ++- .../viewmodel/CustomListLocationsViewModel.kt | 10 +++------ .../EditCustomListNameDialogViewModel.kt | 5 +++-- .../repository/CustomListsRepositoryTest.kt | 19 +++++++++++++---- .../usecase/CustomListActionUseCaseTest.kt | 21 ++++++++++--------- .../viewmodel/EditCustomListViewModelTest.kt | 10 +++++++-- .../mullvadvpn/model/CustomListName.kt | 20 ++++++++++++++++++ 15 files changed, 105 insertions(+), 46 deletions(-) create mode 100644 android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomListName.kt diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt index 5a20438c2359..fd4a97a1d251 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt @@ -1,6 +1,7 @@ package net.mullvad.mullvadvpn.compose.data import net.mullvad.mullvadvpn.model.Constraint +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.model.RelayEndpointData import net.mullvad.mullvadvpn.model.RelayList @@ -46,6 +47,16 @@ val DUMMY_RELAY_COUNTRIES = val DUMMY_CUSTOM_LISTS = listOf( - RelayItem.CustomList("First list", false, "1", locations = DUMMY_RELAY_COUNTRIES), - RelayItem.CustomList("Empty list", expanded = false, "2", locations = emptyList()) + RelayItem.CustomList( + CustomListName.fromString("First list"), + false, + "1", + locations = DUMMY_RELAY_COUNTRIES + ), + RelayItem.CustomList( + CustomListName.fromString("Empty list"), + expanded = false, + "2", + locations = emptyList() + ) ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListAction.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListAction.kt index 0b478f5272f4..9ddee73e22d0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListAction.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListAction.kt @@ -2,22 +2,26 @@ package net.mullvad.mullvadvpn.compose.communication import android.os.Parcelable import kotlinx.parcelize.Parcelize +import net.mullvad.mullvadvpn.model.CustomListName sealed interface CustomListAction : Parcelable { @Parcelize - data class Rename(val customListId: String, val name: String, val newName: String) : - CustomListAction { + data class Rename( + val customListId: String, + val name: CustomListName, + val newName: CustomListName + ) : CustomListAction { fun not() = this.copy(name = newName, newName = name) } @Parcelize data class Delete(val customListId: String) : CustomListAction { - fun not(name: String, locations: List) = Create(name, locations) + fun not(name: CustomListName, locations: List) = Create(name, locations) } @Parcelize - data class Create(val name: String = "", val locations: List = emptyList()) : + data class Create(val name: CustomListName, val locations: List = emptyList()) : CustomListAction, Parcelable { fun not(customListId: String) = Delete(customListId) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListResult.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListResult.kt index 32fa077a7f18..14cba09b44d0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListResult.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListResult.kt @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.compose.communication import android.os.Parcelable import kotlinx.parcelize.Parcelize +import net.mullvad.mullvadvpn.model.CustomListName sealed interface CustomListResult : Parcelable { val undo: CustomListAction @@ -9,26 +10,26 @@ sealed interface CustomListResult : Parcelable { @Parcelize data class Created( val id: String, - val name: String, + val name: CustomListName, val locationName: String?, override val undo: CustomListAction.Delete ) : CustomListResult @Parcelize data class Deleted(override val undo: CustomListAction.Create) : CustomListResult { - val name + val name: CustomListName get() = undo.name } @Parcelize data class Renamed(override val undo: CustomListAction.Rename) : CustomListResult { - val name: String + val name: CustomListName get() = undo.name } @Parcelize data class LocationsChanged( - val name: String, + val name: CustomListName, override val undo: CustomListAction.UpdateLocations ) : CustomListResult } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CustomListNameTextField.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CustomListNameTextField.kt index 675f6f8f14dc..b3a0ece5773e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CustomListNameTextField.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CustomListNameTextField.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.textfield.CustomTextField +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.CustomListsError @Composable @@ -41,6 +42,7 @@ fun CustomListNameTextField( placeholderText = null, isValidValue = error == null, isDigitsOnlyAllowed = false, + maxCharLength = CustomListName.MAX_LENGTH, supportingText = error?.let { { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/CustomListExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/CustomListExtensions.kt index 6fb87a6af523..ad668ed9e88c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/CustomListExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/CustomListExtensions.kt @@ -1,13 +1,14 @@ package net.mullvad.mullvadvpn.relaylist import net.mullvad.mullvadvpn.model.CustomList +import net.mullvad.mullvadvpn.model.CustomListName private fun CustomList.toRelayItemCustomList( relayCountries: List ): RelayItem.CustomList = RelayItem.CustomList( id = this.id, - name = this.name, + customListName = CustomListName.fromString(name), expanded = false, locations = this.locations.mapNotNull { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt index 54c4a9bef4e5..ce4be395b651 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt @@ -1,5 +1,6 @@ package net.mullvad.mullvadvpn.relaylist +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.GeoIpLocation import net.mullvad.mullvadvpn.model.GeographicLocationConstraint @@ -15,11 +16,12 @@ sealed interface RelayItem { val expanded: Boolean data class CustomList( - override val name: String, + val customListName: CustomListName, override val expanded: Boolean, val id: String, val locations: List, ) : RelayItem { + override val name: String = customListName.value override val active get() = locations.any { location -> location.active } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepository.kt index f1a38871bd9f..0832f434a56a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepository.kt @@ -8,6 +8,7 @@ import net.mullvad.mullvadvpn.lib.ipc.Request import net.mullvad.mullvadvpn.lib.ipc.events import net.mullvad.mullvadvpn.model.CreateCustomListResult import net.mullvad.mullvadvpn.model.CustomList +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.CustomListsError import net.mullvad.mullvadvpn.model.GeographicLocationConstraint import net.mullvad.mullvadvpn.model.UpdateCustomListResult @@ -20,8 +21,8 @@ class CustomListsRepository( private val settingsRepository: SettingsRepository, private val relayListListener: RelayListListener ) { - suspend fun createCustomList(name: String): CreateCustomListResult { - val result = messageHandler.trySendRequest(Request.CreateCustomList(name)) + suspend fun createCustomList(name: CustomListName): CreateCustomListResult { + val result = messageHandler.trySendRequest(Request.CreateCustomList(name.value)) return if (result) { messageHandler.events().first().result @@ -52,8 +53,8 @@ class CustomListsRepository( ArrayList(locationCodes.mapNotNull { getGeographicLocationConstraintByCode(it) }) ) - suspend fun updateCustomListName(id: String, name: String): UpdateCustomListResult = - getCustomListById(id)?.let { updateCustomList(it.copy(name = name)) } + suspend fun updateCustomListName(id: String, name: CustomListName): UpdateCustomListResult = + getCustomListById(id)?.let { updateCustomList(it.copy(name = name.value)) } ?: UpdateCustomListResult.Error(CustomListsError.OtherError) private suspend fun updateCustomListLocations( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt index 7b2e5a43aa00..8d722325d650 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt @@ -5,6 +5,7 @@ import net.mullvad.mullvadvpn.compose.communication.CustomListAction import net.mullvad.mullvadvpn.compose.communication.CustomListResult import net.mullvad.mullvadvpn.model.CreateCustomListResult import net.mullvad.mullvadvpn.model.CustomList +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.GeographicLocationConstraint import net.mullvad.mullvadvpn.model.UpdateCustomListResult import net.mullvad.mullvadvpn.relaylist.getRelayItemsByCodes @@ -79,9 +80,9 @@ class CustomListActionUseCase( } fun performAction(action: CustomListAction.Delete): Result { - val customList: CustomList? = customListsRepository.getCustomListById(action.customListId) + val customList: CustomList = customListsRepository.getCustomListById(action.customListId)!! val oldLocations = customList.locations() - val name = customList?.name ?: "" + val name = CustomListName.fromString(customList.name) customListsRepository.deleteCustomList(action.customListId) return Result.success( CustomListResult.Deleted(undo = action.not(locations = oldLocations, name = name)) @@ -91,9 +92,9 @@ class CustomListActionUseCase( suspend fun performAction( action: CustomListAction.UpdateLocations ): Result { - val customList: CustomList? = customListsRepository.getCustomListById(action.customListId) + val customList = customListsRepository.getCustomListById(action.customListId)!! val oldLocations = customList.locations() - val name = customList?.name ?: "" + val name = CustomListName.fromString(customList.name) customListsRepository.updateCustomListLocationsFromCodes( action.customListId, action.locations diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt index 9ae5bb7a648b..f58916cd66c6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.communication.CustomListAction import net.mullvad.mullvadvpn.compose.communication.CustomListResult import net.mullvad.mullvadvpn.compose.state.CreateCustomListUiState +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.CustomListsError import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase import net.mullvad.mullvadvpn.usecase.customlists.CustomListsException @@ -38,7 +39,7 @@ class CreateCustomListDialogViewModel( customListActionUseCase .performAction( CustomListAction.Create( - name, + CustomListName.fromString(name), if (locationCode.isNotEmpty()) { listOf(locationCode) } else { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt index 5fa99306a35f..5efba5321ee3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt @@ -28,8 +28,6 @@ class CustomListLocationsViewModel( private val relayListUseCase: RelayListUseCase, private val customListActionUseCase: CustomListActionUseCase ) : ViewModel() { - private var customListName: String = "" - private val _uiSideEffect = MutableSharedFlow(replay = 1, extraBufferCapacity = 1) val uiSideEffect: SharedFlow = _uiSideEffect @@ -195,11 +193,9 @@ class CustomListLocationsViewModel( private suspend fun fetchInitialSelectedLocations() { _selectedLocations.value = - awaitCustomListById(customListId) - ?.apply { customListName = name } - ?.locations - ?.selectChildren() - .apply { _initialLocations.value = this ?: emptySet() } + awaitCustomListById(customListId)?.locations?.selectChildren().apply { + _initialLocations.value = this ?: emptySet() + } } companion object { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt index c2625e6d567d..9a8d3d2f6236 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.communication.CustomListAction import net.mullvad.mullvadvpn.compose.communication.CustomListResult import net.mullvad.mullvadvpn.compose.state.UpdateCustomListUiState +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.CustomListsError import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase import net.mullvad.mullvadvpn.usecase.customlists.CustomListsException @@ -44,8 +45,8 @@ class EditCustomListNameDialogViewModel( .performAction( CustomListAction.Rename( customListId = customListId, - name = initialName, - newName = name + name = CustomListName.fromString(initialName), + newName = CustomListName.fromString(name) ) ) .fold( diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt index 129d921c36ea..9c2ac615c31e 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt @@ -13,6 +13,7 @@ import net.mullvad.mullvadvpn.lib.ipc.Request import net.mullvad.mullvadvpn.lib.ipc.events import net.mullvad.mullvadvpn.model.CreateCustomListResult import net.mullvad.mullvadvpn.model.CustomList +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.CustomListsError import net.mullvad.mullvadvpn.model.GeographicLocationConstraint import net.mullvad.mullvadvpn.model.RelayList @@ -94,7 +95,8 @@ class CustomListsRepositoryTest { flowOf(Event.CreateCustomListResultEvent(expectedResult)) // Act - val result = customListsRepository.createCustomList(customListName) + val result = + customListsRepository.createCustomList(CustomListName.fromString(customListName)) // Assert assertEquals(expectedResult, result) @@ -113,7 +115,8 @@ class CustomListsRepositoryTest { flowOf(Event.CreateCustomListResultEvent(expectedResult)) // Act - val result = customListsRepository.createCustomList(customListName) + val result = + customListsRepository.createCustomList(CustomListName.fromString(customListName)) // Assert assertEquals(expectedResult, result) @@ -139,7 +142,11 @@ class CustomListsRepositoryTest { every { mockSettings.customLists.customLists } returns arrayListOf(mockCustomList) // Act - val result = customListsRepository.updateCustomListName(customListId, customListName) + val result = + customListsRepository.updateCustomListName( + customListId, + CustomListName.fromString(customListName) + ) // Assert assertEquals(expectedResult, result) @@ -167,7 +174,11 @@ class CustomListsRepositoryTest { every { mockSettings.customLists.customLists } returns arrayListOf(mockCustomList) // Act - val result = customListsRepository.updateCustomListName(customListId, customListName) + val result = + customListsRepository.updateCustomListName( + customListId, + CustomListName.fromString(customListName) + ) // Assert assertEquals(expectedResult, result) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt index 0370f23ffb41..fdcb4170f00f 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt @@ -11,6 +11,7 @@ import net.mullvad.mullvadvpn.compose.communication.CustomListAction import net.mullvad.mullvadvpn.compose.communication.CustomListResult import net.mullvad.mullvadvpn.model.CreateCustomListResult import net.mullvad.mullvadvpn.model.CustomList +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.CustomListsError import net.mullvad.mullvadvpn.model.GeographicLocationConstraint import net.mullvad.mullvadvpn.model.UpdateCustomListResult @@ -40,7 +41,7 @@ class CustomListActionUseCaseTest { @Test fun `create action should return success when ok`() = runTest { // Arrange - val name = "test" + val name = CustomListName.fromString("test") val locationCode = "AB" val locationName = "Acklaba" val createdId = "1" @@ -83,7 +84,7 @@ class CustomListActionUseCaseTest { @Test fun `create action should return error when name already exists`() = runTest { // Arrange - val name = "test" + val name = CustomListName.fromString("test") val locationCode = "AB" val action = CustomListAction.Create(name = name, locations = listOf(locationCode)) val expectedError = CustomListsError.CustomListExists @@ -103,8 +104,8 @@ class CustomListActionUseCaseTest { @Test fun `rename action should return success when ok`() = runTest { // Arrange - val name = "test" - val newName = "test2" + val name = CustomListName.fromString("test") + val newName = CustomListName.fromString("test2") val customListId = "1" val action = CustomListAction.Rename(customListId = customListId, name = name, newName = newName) @@ -123,8 +124,8 @@ class CustomListActionUseCaseTest { @Test fun `rename action should return error when name already exists`() = runTest { // Arrange - val name = "test" - val newName = "test2" + val name = CustomListName.fromString("test") + val newName = CustomListName.fromString("test2") val customListId = "1" val action = CustomListAction.Rename(customListId = customListId, name = name, newName = newName) @@ -149,7 +150,7 @@ class CustomListActionUseCaseTest { val mockCustomList: CustomList = mockk() val mockLocation: GeographicLocationConstraint.Country = mockk() val mockLocations: ArrayList = arrayListOf(mockLocation) - val name = "test" + val name = CustomListName.fromString("test") val customListId = "1" val locationCode = "AB" val action = CustomListAction.Delete(customListId = customListId) @@ -160,7 +161,7 @@ class CustomListActionUseCaseTest { ) ) every { mockCustomList.locations } returns mockLocations - every { mockCustomList.name } returns name + every { mockCustomList.name } returns name.value every { mockLocation.countryCode } returns locationCode coEvery { mockCustomListsRepository.deleteCustomList(id = customListId) } returns true every { mockCustomListsRepository.getCustomListById(customListId) } returns mockCustomList @@ -175,7 +176,7 @@ class CustomListActionUseCaseTest { @Test fun `update locations action should return success with changed locations`() = runTest { // Arrange - val name = "test" + val name = CustomListName.fromString("test") val oldLocationCodes = listOf("AB", "CD") val newLocationCodes = listOf("EF", "GH") val oldLocations: ArrayList = @@ -184,7 +185,7 @@ class CustomListActionUseCaseTest { GeographicLocationConstraint.Country("CD") ) val customListId = "1" - val customList = CustomList(id = customListId, name = name, locations = oldLocations) + val customList = CustomList(id = customListId, name = name.value, locations = oldLocations) val action = CustomListAction.UpdateLocations( customListId = customListId, diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt index 33986961b3a1..cbc5ff1c50dc 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import net.mullvad.mullvadvpn.compose.state.EditCustomListState import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule +import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.usecase.RelayListUseCase import org.junit.jupiter.api.Assertions.assertEquals @@ -23,7 +24,12 @@ class EditCustomListViewModelTest { // Arrange val customListId = "2" val customList = - RelayItem.CustomList(id = "1", name = "test", expanded = false, locations = emptyList()) + RelayItem.CustomList( + id = "1", + customListName = CustomListName.fromString("test"), + expanded = false, + locations = emptyList() + ) every { mockRelayListUseCase.customLists() } returns flowOf(listOf(customList)) val viewModel = createViewModel(customListId) @@ -41,7 +47,7 @@ class EditCustomListViewModelTest { val customList = RelayItem.CustomList( id = customListId, - name = "test", + customListName = CustomListName.fromString("test"), expanded = false, locations = emptyList() ) diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomListName.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomListName.kt new file mode 100644 index 000000000000..5822eec2b3a1 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomListName.kt @@ -0,0 +1,20 @@ +package net.mullvad.mullvadvpn.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +@JvmInline +value class CustomListName private constructor(val value: String) : Parcelable { + + override fun toString() = value + + companion object { + const val MAX_LENGTH = 30 + + fun fromString(name: String): CustomListName { + val trimmedName = name.trim().take(MAX_LENGTH) + return CustomListName(trimmedName) + } + } +} From 99ae0b436f173b576343111cac38d6bec4ce2487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Mon, 20 Nov 2023 19:35:52 +0100 Subject: [PATCH 070/214] Add Event::duplicate --- talpid-windows/src/sync.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/talpid-windows/src/sync.rs b/talpid-windows/src/sync.rs index 202c96524d28..7b4ed59be3d6 100644 --- a/talpid-windows/src/sync.rs +++ b/talpid-windows/src/sync.rs @@ -1,7 +1,7 @@ use std::{io, ptr}; use windows_sys::Win32::{ - Foundation::{CloseHandle, BOOL, HANDLE}, - System::Threading::{CreateEventW, SetEvent}, + Foundation::{CloseHandle, DuplicateHandle, BOOL, DUPLICATE_SAME_ACCESS, HANDLE}, + System::Threading::{CreateEventW, GetCurrentProcess, SetEvent}, }; /// Windows event object @@ -39,6 +39,26 @@ impl Event { pub fn as_raw(&self) -> HANDLE { self.0 } + + /// Duplicate the event object with `DuplicateHandle()` + pub fn duplicate(&self) -> io::Result { + let mut new_event = 0; + let status = unsafe { + DuplicateHandle( + GetCurrentProcess(), + self.0, + GetCurrentProcess(), + &mut new_event, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ) + }; + if status == 0 { + return Err(io::Error::last_os_error()); + } + Ok(Event(new_event)) + } } impl Drop for Event { From af96a710398870587df9e07ee6f5afd16b8d9888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Tue, 5 Sep 2023 10:17:09 +0200 Subject: [PATCH 071/214] Add DAITA Windows client and updated tuncfg --- Cargo.lock | 135 ++++++ Cargo.toml | 1 + dist-assets/maybenot_machines | 4 + docs/architecture.md | 2 +- gui/tasks/distribution.js | 1 + mullvad-api/src/relay_list.rs | 3 + mullvad-cli/src/cmds/relay.rs | 2 + mullvad-cli/src/cmds/tunnel.rs | 29 +- mullvad-cli/src/format.rs | 13 +- mullvad-daemon/src/lib.rs | 55 ++- mullvad-daemon/src/management_interface.rs | 19 + .../proto/management_interface.proto | 10 +- mullvad-management-interface/src/client.rs | 12 + .../src/types/conversions/custom_tunnel.rs | 2 + .../src/types/conversions/net.rs | 6 + .../src/types/conversions/relay_list.rs | 2 + .../src/types/conversions/settings.rs | 11 + .../src/types/conversions/wireguard.rs | 18 + .../src/relay_selector/detailer.rs | 9 + .../src/relay_selector/mod.rs | 16 + .../tests/relay_selector.rs | 6 + mullvad-types/src/relay_list.rs | 4 + mullvad-types/src/wireguard.rs | 13 + talpid-core/src/tunnel/mod.rs | 1 - talpid-tunnel-config-client/build.rs | 2 +- .../examples/psk-exchange.rs | 6 +- .../examples/tuncfg-server.rs | 105 ++-- .../proto/ephemeralpeer.proto | 85 ++++ .../proto/tunnel_config.proto | 83 ---- .../src/classic_mceliece.rs | 5 +- talpid-tunnel-config-client/src/kyber.rs | 5 +- talpid-tunnel-config-client/src/lib.rs | 164 ++++--- talpid-types/src/net/mod.rs | 6 + talpid-types/src/net/wireguard.rs | 7 + talpid-wireguard/Cargo.toml | 2 + talpid-wireguard/src/config.rs | 9 + talpid-wireguard/src/connectivity_check.rs | 5 + talpid-wireguard/src/lib.rs | 103 ++-- talpid-wireguard/src/wireguard_nt/daita.rs | 450 ++++++++++++++++++ .../{wireguard_nt.rs => wireguard_nt/mod.rs} | 172 ++++++- 40 files changed, 1335 insertions(+), 248 deletions(-) create mode 100644 dist-assets/maybenot_machines create mode 100644 talpid-tunnel-config-client/proto/ephemeralpeer.proto delete mode 100644 talpid-tunnel-config-client/proto/tunnel_config.proto create mode 100644 talpid-wireguard/src/wireguard_nt/daita.rs rename talpid-wireguard/src/{wireguard_nt.rs => wireguard_nt/mod.rs} (86%) diff --git a/Cargo.lock b/Cargo.lock index cc54dcb168c7..eedb5dd48029 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aead" version = "0.5.2" @@ -52,6 +58,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.0.5" @@ -582,6 +600,15 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.9" @@ -591,6 +618,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -714,6 +750,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + [[package]] name = "dashmap" version = "5.5.3" @@ -1217,6 +1259,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.0" @@ -1756,6 +1807,30 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libflate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" +dependencies = [ + "core2", + "hashbrown 0.13.2", + "rle-decode-fast", +] + [[package]] name = "libm" version = "0.2.8" @@ -1835,6 +1910,22 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" +[[package]] +name = "maybenot" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc2e64fe3f5fb1e247110a9a408449eff2259cc272cf57bad6f161e801ac962" +dependencies = [ + "byteorder", + "hex", + "libflate", + "rand 0.8.5", + "rand_distr", + "ring", + "serde", + "simple-error", +] + [[package]] name = "md-5" version = "0.10.5" @@ -2973,6 +3064,16 @@ dependencies = [ "getrandom 0.2.10", ] +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -3099,6 +3200,12 @@ dependencies = [ "signature", ] +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + [[package]] name = "rs-release" version = "0.1.9" @@ -3467,6 +3574,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simple-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8542b68b8800c3cda649d2c72d688b6907b30f1580043135d61669d4aad1c175" + [[package]] name = "simple-signal" version = "1.1.1" @@ -3870,6 +3983,7 @@ dependencies = [ name = "talpid-wireguard" version = "0.0.0" dependencies = [ + "base64 0.13.1", "bitflags 1.3.2", "byteorder", "chrono", @@ -3880,6 +3994,7 @@ dependencies = [ "ipnetwork", "libc", "log", + "maybenot", "netlink-packet-core", "netlink-packet-route", "netlink-packet-utils", @@ -4821,6 +4936,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index b0c28f016aa8..65391bcf3755 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "ios/MullvadREST/Transport/Shadowsocks/shadowsocks-proxy", "ios/TunnelObfuscation/tunnel-obfuscator-proxy", "mullvad-api", + "mullvad-daemon", "mullvad-cli", "mullvad-daemon", "mullvad-exclude", diff --git a/dist-assets/maybenot_machines b/dist-assets/maybenot_machines new file mode 100644 index 000000000000..e7bde4f764c1 --- /dev/null +++ b/dist-assets/maybenot_machines @@ -0,0 +1,4 @@ +789cd5cfbb0900200c04d08b833886adb889389f5bb9801be811acb58ae2837ce02010c158b070555c9538b6377a64dbb0ceff242c20b79038507dd169fbede9f629bf6f021efa1b66 +789ccd934b48024118c777357ae9250aa14b8542085d2a0f264233160975a80c440f5b04455deaa050143d0e81d8835ea708f6d2a9a2430511d2a136118bcaa854422a93c44d93a243121844b3204b8b87acadf0c7f0c137f3cdccf7e08f635cc200190eb81099af106235eda133a95a4f4df5dcbfad2eb640b4c921135330e42ba1585066951456b311b3f6d8ca4254ae415f212f356efba0928e44d9642df27769b79162fd9ff1ccf3fef7b9b19982cee963405699ee1c136e107bf0ae19d6fdff9ec76770ac8bc1a9a34847c986d9a8858391d28af2a20b76622824f55125186dedd813d9c2207bd22c382f76f1aed070ad7a896bbdc09317b48c1121deefa51f599855e23b7d1519a1eb044134414662573e1d546d1df6efd6ce401cdb3f5aaadb56e8a9b69d7ab140d60c0924a601b7086929019a54ef66e5b02cd7c376a86199d63cc57fbb63c9daf18f2b9805e6d5f49c9a0e249dff158df6919c0232003a87321e2f0ff86be903a6c37ad5 +789ced956d28435118c7cfdde63544f9424292489222493a77214962de92248992bc535e4221ad2549920f5e22e5ada525496b1faee52d9fc6e4255bb3469b96661f282379ee1d574bb7257c50fb7d38dd73efd3e9dcf33fe77708e4880943e3002180c6197c74b6d09ce6dd24a2a003a4530dc6e715ab4d44dea8222e9f8c6292878ee0a1afb598ca18ae876f0212ca1c2110b19f932c1156eee2e99d70596bed360e1294f8c5adeb71aa2e6f64b251f965721f840d99455ba15adc92e60d13d173d6b9f87bf8e87e690f7224a8aedba804cfc842366979b60f4f23731342d228a67c54f910ad6093a2739eb1e8f05c70bc36d2fd827dcf857bff44fa70bd19c356b21ee772ef8cef220d29ab4ed519d8f196e95f119eb37d179f78a0177af1ab4c78d602918b0ba8d7934e3a73d28717eb2119881542114be05a96be3bf0143f46d3d15f73ae682f4eaa90676bb0adaf4ed13eaec2200490889ab3dec54fe123469fbe45e4e2142dde3092c951524a32a734b103ec5de55f9ab261ca27db6ac7ee7ac4f9ef9e47088ced55531300679a2056e9cde07f8507a9c3f9cc4d3566464938f8f5e418af4835d87e9370dbde9339cc066cf79173a7fc77de00293aaf16 +789ce5914b4802511486ef75c8405a84b4aa8810029b8236b9a8b03b5af6801e24512d24225ab409a2550521152e24c455b831706a216ea4402c5a0d1404465022980d990d5941140c328bc81eb7910686bb89dc047e1c0edc737e2e87ff8740cd23c24d05a4702b1e0a685737bb36fc36a6ef76b92adc6be5a2b9f7d1abee6a0b5e1680009a75c99d79730cf5e065623185fcc7f5bb0bb329e2a81f663a6e863dbe341a9aae9c688b6408ddd6a51bd719a2e880860e0868dc29e13a2174a503050a9ed6304792d1e1ddb63378a8420bd6b9186bfbb033721834a9f84e0af75fe191d8bdced6b8e2787b594b3093bd57de7fe5b3e2d9600df3c8b9e47e491b85a2fffb7f68c1296b8be6aec798b987b790a86f22722807f990181f140618d7d3c8419dcbc4e1a14ca3c3ab5933d31600a19294087d0dfae63b549bbf88f0c124e1d854f6dcc0bf0a484e5db78f569069f2b03f41e84a8d2f5e5a7583 diff --git a/docs/architecture.md b/docs/architecture.md index 1deba30706d1..78305a8c6a99 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -200,7 +200,7 @@ WireGuard tunnel to the relay and deriving the PSK within the tunnel. The PSK is stored in memory on the relay and the client, along with a new client generated ephemeral WireGuard key. Subsequently, a new tunnel is created using the new WireGuard key and the PSK, ensuring that the tunnel is quantum-resistant. -See the [protocol definition file](../talpid-tunnel-config-client/proto/tunnel_config.proto) for +See the [protocol definition file](../talpid-tunnel-config-client/proto/ephemeralpeer.proto) for more details on the protocol. #### Quantum-resistant tunnels & Multihop diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index 99c8fb87efb1..7087d71301ed 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -152,6 +152,7 @@ const config = { from: distAssets('binaries/x86_64-pc-windows-msvc/wireguard-nt/mullvad-wireguard.dll'), to: '.', }, + { from: distAssets('maybenot_machines'), to: '.' }, ], }, diff --git a/mullvad-api/src/relay_list.rs b/mullvad-api/src/relay_list.rs index deaf29ef10d6..73b387a8d8c0 100644 --- a/mullvad-api/src/relay_list.rs +++ b/mullvad-api/src/relay_list.rs @@ -303,6 +303,8 @@ struct WireGuardRelay { #[serde(flatten)] relay: Relay, public_key: wireguard::PublicKey, + #[serde(default)] + daita: bool, } impl WireGuardRelay { @@ -312,6 +314,7 @@ impl WireGuardRelay { location, relay_list::RelayEndpointData::Wireguard(relay_list::WireguardRelayEndpointData { public_key: self.public_key, + daita: self.daita, }), ) } diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index 7ef60d758cc9..f022402a8316 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -542,6 +542,8 @@ impl Relay { allowed_ips: all_of_the_internet(), endpoint: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port), psk: None, + #[cfg(target_os = "windows")] + constant_packet_size: false, }, exit_peer: None, ipv4_gateway, diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index 19d5c1a3c956..77338ee336b8 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -1,6 +1,8 @@ use anyhow::Result; use clap::Subcommand; use mullvad_management_interface::MullvadProxyClient; +#[cfg(target_os = "windows")] +use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ constraints::Constraint, wireguard::{QuantumResistantState, RotationInterval, DEFAULT_ROTATION_INTERVAL}, @@ -38,6 +40,10 @@ pub enum TunnelOptions { /// Configure quantum-resistant key exchange #[arg(long)] quantum_resistant: Option, + /// Configure whether to enable DAITA + #[cfg(target_os = "windows")] + #[arg(long)] + daita: Option, /// The key rotation interval. Number of hours, or 'any' #[arg(long)] rotation_interval: Option>, @@ -95,6 +101,9 @@ impl Tunnel { tunnel_options.wireguard.quantum_resistant, ); + #[cfg(target_os = "windows")] + print_option!("DAITA", tunnel_options.wireguard.daita.enabled); + let key = rpc.get_wireguard_key().await?; print_option!("Public key", key.key,); print_option!(format_args!( @@ -129,10 +138,20 @@ impl Tunnel { TunnelOptions::Wireguard { mtu, quantum_resistant, + #[cfg(target_os = "windows")] + daita, rotation_interval, rotate_key, } => { - Self::handle_wireguard(mtu, quantum_resistant, rotation_interval, rotate_key).await + Self::handle_wireguard( + mtu, + quantum_resistant, + #[cfg(target_os = "windows")] + daita, + rotation_interval, + rotate_key, + ) + .await } TunnelOptions::Ipv6 { state } => Self::handle_ipv6(state).await, } @@ -159,6 +178,7 @@ impl Tunnel { async fn handle_wireguard( mtu: Option>, quantum_resistant: Option, + #[cfg(target_os = "windows")] daita: Option, rotation_interval: Option>, rotate_key: Option, ) -> Result<()> { @@ -174,6 +194,13 @@ impl Tunnel { println!("Quantum resistant setting has been updated"); } + #[cfg(target_os = "windows")] + if let Some(daita) = daita { + rpc.set_daita_settings(DaitaSettings { enabled: *daita }) + .await?; + println!("DAITA setting has been updated"); + } + if let Some(interval) = rotation_interval { match interval { Constraint::Only(interval) => { diff --git a/mullvad-cli/src/format.rs b/mullvad-cli/src/format.rs index e605efbe3bc5..0d6ea0b0c034 100644 --- a/mullvad-cli/src/format.rs +++ b/mullvad-cli/src/format.rs @@ -174,6 +174,17 @@ fn format_relay_connection( "\nQuantum resistant tunnel: no" }; + #[cfg(target_os = "windows")] + let daita = if !verbose { + "" + } else if endpoint.daita { + "\nDAITA: yes" + } else { + "\nDAITA: no" + }; + #[cfg(not(target_os = "windows"))] + let daita = ""; + let mut bridge_type = String::new(); let mut obfuscator_type = String::new(); if verbose { @@ -186,7 +197,7 @@ fn format_relay_connection( } format!( - "{exit_endpoint}{first_hop}{bridge}{obfuscator}{tunnel_type}{quantum_resistant}{bridge_type}{obfuscator_type}", + "{exit_endpoint}{first_hop}{bridge}{obfuscator}{tunnel_type}{quantum_resistant}{daita}{bridge_type}{obfuscator_type}", first_hop = first_hop.unwrap_or_default(), bridge = bridge.unwrap_or_default(), obfuscator = obfuscator.unwrap_or_default(), diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 365e82efeba1..2733482a4c36 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -37,9 +37,11 @@ use futures::{ StreamExt, }; use geoip::GeoIpHandler; -use mullvad_relay_selector::{RelaySelector, SelectorConfig}; +use mullvad_relay_selector::{AdditionalRelayConstraints, AdditionalWireguardConstraints, RelaySelector, SelectorConfig}; #[cfg(target_os = "android")] use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; +#[cfg(target_os = "windows")] +use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ access_method::{AccessMethod, AccessMethodSetting}, account::{AccountData, AccountToken, VoucherSubmission}, @@ -256,6 +258,9 @@ pub enum DaemonCommand { SetEnableIpv6(ResponseTx<(), settings::Error>, bool), /// Set whether to enable PQ PSK exchange in the tunnel SetQuantumResistantTunnel(ResponseTx<(), settings::Error>, QuantumResistantState), + /// Set DAITA settings for the tunnel + #[cfg(target_os = "windows")] + SetDaitaSettings(ResponseTx<(), settings::Error>, DaitaSettings), /// Set DNS options or servers to use SetDnsOptions(ResponseTx<(), settings::Error>, DnsOptions), /// Set override options to use for a given relay @@ -1242,6 +1247,10 @@ where self.on_set_quantum_resistant_tunnel(tx, quantum_resistant_state) .await } + #[cfg(target_os = "windows")] + SetDaitaSettings(tx, daita_settings) => { + self.on_set_daita_settings(tx, daita_settings).await + } SetDnsOptions(tx, dns_servers) => self.on_set_dns_options(tx, dns_servers).await, SetRelayOverride(tx, relay_override) => { self.on_set_relay_override(tx, relay_override).await @@ -2259,6 +2268,40 @@ where } } + #[cfg(target_os = "windows")] + async fn on_set_daita_settings( + &mut self, + tx: ResponseTx<(), settings::Error>, + daita_settings: DaitaSettings, + ) { + match self + .settings + .update(|settings| settings.tunnel_options.wireguard.daita = daita_settings) + .await + { + Ok(settings_changed) => { + Self::oneshot_send(tx, Ok(()), "set_daita_settings response"); + if settings_changed { + self.parameters_generator + .set_tunnel_options(&self.settings.tunnel_options) + .await; + self.event_listener + .notify_settings(self.settings.to_settings()); + self.relay_selector + .set_config(new_selector_config(&self.settings)); + if self.get_target_tunnel_type() == Some(TunnelType::Wireguard) { + log::info!("Reconnecting because DAITA settings changed"); + self.reconnect_tunnel(); + } + } + } + Err(e) => { + log::error!("{}", e.display_chain_with_msg("Unable to save settings")); + Self::oneshot_send(tx, Err(e), "set_daita_settings response"); + } + } + } + async fn on_set_dns_options( &mut self, tx: ResponseTx<(), settings::Error>, @@ -2780,8 +2823,18 @@ impl DaemonShutdownHandle { } fn new_selector_config(settings: &Settings) -> SelectorConfig { + let additional_constraints = AdditionalRelayConstraints { + wireguard: AdditionalWireguardConstraints { + #[cfg(target_os = "windows")] + daita: settings.tunnel_options.wireguard.daita.enabled, + #[cfg(not(target_os = "windows"))] + daita: false, + }, + }; + SelectorConfig { relay_settings: settings.relay_settings.clone(), + additional_constraints, bridge_state: settings.bridge_state, bridge_settings: settings.bridge_settings.clone(), obfuscation_settings: settings.obfuscation_settings.clone(), diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 55573f87a7d1..ce203e7ba8d6 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -325,6 +325,25 @@ impl ManagementService for ManagementServiceImpl { Ok(Response::new(())) } + #[cfg(target_os = "windows")] + async fn set_daita_settings( + &self, + request: Request, + ) -> ServiceResult<()> { + let state = mullvad_types::wireguard::DaitaSettings::from(request.into_inner()); + + log::debug!("set_daita_settings({state:?})"); + let (tx, rx) = oneshot::channel(); + self.send_command_to_daemon(DaemonCommand::SetDaitaSettings(tx, state))?; + self.wait_for_result(rx).await?.map(Response::new)?; + Ok(Response::new(())) + } + + #[cfg(not(target_os = "windows"))] + async fn set_daita_settings(&self, _: Request) -> ServiceResult<()> { + Ok(Response::new(())) + } + #[cfg(not(target_os = "android"))] async fn set_dns_options(&self, request: Request) -> ServiceResult<()> { let options = DnsOptions::try_from(request.into_inner()).map_err(map_protobuf_type_err)?; diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 8af34aa8afe3..31d39db3069b 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -43,6 +43,7 @@ service ManagementService { rpc SetWireguardMtu(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {} rpc SetEnableIpv6(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} rpc SetQuantumResistantTunnel(QuantumResistantState) returns (google.protobuf.Empty) {} + rpc SetDaitaSettings(DaitaSettings) returns (google.protobuf.Empty) {} rpc SetDnsOptions(DnsOptions) returns (google.protobuf.Empty) {} rpc SetRelayOverride(RelayOverride) returns (google.protobuf.Empty) {} rpc ClearAllRelayOverrides(google.protobuf.Empty) returns (google.protobuf.Empty) {} @@ -220,6 +221,7 @@ message TunnelEndpoint { ObfuscationEndpoint obfuscation = 6; Endpoint entry_endpoint = 7; TunnelMetadata tunnel_metadata = 8; + bool daita = 9; } enum ObfuscationType { @@ -494,12 +496,15 @@ message QuantumResistantState { State state = 1; } +message DaitaSettings { bool enabled = 1; } + message TunnelOptions { message OpenvpnOptions { optional uint32 mssfix = 1; } message WireguardOptions { optional uint32 mtu = 1; google.protobuf.Duration rotation_interval = 2; QuantumResistantState quantum_resistant = 4; + DaitaSettings daita = 5; } message GenericOptions { bool enable_ipv6 = 1; } @@ -584,7 +589,10 @@ message Relay { Location location = 11; } -message WireguardRelayEndpointData { bytes public_key = 1; } +message WireguardRelayEndpointData { + bytes public_key = 1; + bool daita = 2; +} message Location { string country = 1; diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 8cf3ac495d2e..04304a19ec4d 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -2,6 +2,8 @@ use crate::types; use futures::{Stream, StreamExt}; +#[cfg(target_os = "windows")] +use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ access_method::{self, AccessMethod, AccessMethodSetting}, account::{AccountData, AccountToken, VoucherSubmission}, @@ -344,6 +346,16 @@ impl MullvadProxyClient { Ok(()) } + #[cfg(target_os = "windows")] + pub async fn set_daita_settings(&mut self, settings: DaitaSettings) -> Result<()> { + let settings = types::DaitaSettings::from(settings); + self.0 + .set_daita_settings(settings) + .await + .map_err(Error::Rpc)?; + Ok(()) + } + pub async fn set_dns_options(&mut self, options: DnsOptions) -> Result<()> { let options = types::DnsOptions::from(&options); self.0.set_dns_options(options).await.map_err(Error::Rpc)?; diff --git a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs index 8a4408ec83d3..2445ec3292d1 100644 --- a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs +++ b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs @@ -91,6 +91,8 @@ impl TryFrom for mullvad_types::ConnectionConfig { allowed_ips, endpoint, psk: None, + #[cfg(target_os = "windows")] + constant_packet_size: false, }, exit_peer: None, ipv4_gateway, diff --git a/mullvad-management-interface/src/types/conversions/net.rs b/mullvad-management-interface/src/types/conversions/net.rs index 80648cbf8dcb..3557a6a636f9 100644 --- a/mullvad-management-interface/src/types/conversions/net.rs +++ b/mullvad-management-interface/src/types/conversions/net.rs @@ -40,6 +40,10 @@ impl From for proto::TunnelEndpoint { tunnel_metadata: endpoint .tunnel_interface .map(|tunnel_interface| proto::TunnelMetadata { tunnel_interface }), + #[cfg(target_os = "windows")] + daita: endpoint.daita, + #[cfg(not(target_os = "windows"))] + daita: false, } } } @@ -123,6 +127,8 @@ impl TryFrom for talpid_types::net::TunnelEndpoint { tunnel_interface: endpoint .tunnel_metadata .map(|tunnel_metadata| tunnel_metadata.tunnel_interface), + #[cfg(target_os = "windows")] + daita: endpoint.daita, }) } } diff --git a/mullvad-management-interface/src/types/conversions/relay_list.rs b/mullvad-management-interface/src/types/conversions/relay_list.rs index 32aee834afe1..4e0a3637022a 100644 --- a/mullvad-management-interface/src/types/conversions/relay_list.rs +++ b/mullvad-management-interface/src/types/conversions/relay_list.rs @@ -122,6 +122,7 @@ impl From for proto::Relay { "mullvad_daemon.management_interface/WireguardRelayEndpointData", proto::WireguardRelayEndpointData { public_key: data.public_key.as_bytes().to_vec(), + daita: data.daita, }, )), _ => None, @@ -236,6 +237,7 @@ impl TryFrom for mullvad_types::relay_list::Relay { MullvadEndpointData::Wireguard( mullvad_types::relay_list::WireguardRelayEndpointData { public_key: bytes_to_pubkey(&data.public_key)?, + daita: data.daita, }, ) } diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs index a4d63131584c..857f32d99102 100644 --- a/mullvad-management-interface/src/types/conversions/settings.rs +++ b/mullvad-management-interface/src/types/conversions/settings.rs @@ -97,6 +97,10 @@ impl From<&mullvad_types::settings::TunnelOptions> for proto::TunnelOptions { .expect("Failed to convert std::time::Duration to prost_types::Duration for tunnel_options.wireguard.rotation_interval") }), quantum_resistant: Some(proto::QuantumResistantState::from(options.wireguard.quantum_resistant)), + #[cfg(target_os = "windows")] + daita: Some(proto::DaitaSettings::from(options.wireguard.daita.clone())), + #[cfg(not(target_os = "windows"))] + daita: None, }), generic: Some(proto::tunnel_options::GenericOptions { enable_ipv6: options.generic.enable_ipv6, @@ -282,6 +286,13 @@ impl TryFrom for mullvad_types::settings::TunnelOptions { .ok_or(FromProtobufTypeError::InvalidArgument( "missing quantum resistant state", ))??, + #[cfg(target_os = "windows")] + daita: wireguard_options + .daita + .map(mullvad_types::wireguard::DaitaSettings::from) + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing daita settings", + ))?, }, generic: net::GenericTunnelOptions { enable_ipv6: generic_options.enable_ipv6, diff --git a/mullvad-management-interface/src/types/conversions/wireguard.rs b/mullvad-management-interface/src/types/conversions/wireguard.rs index f35a8c72160e..4a4341339cfc 100644 --- a/mullvad-management-interface/src/types/conversions/wireguard.rs +++ b/mullvad-management-interface/src/types/conversions/wireguard.rs @@ -71,3 +71,21 @@ impl TryFrom for mullvad_types::wireguard::Quantum } } } + +#[cfg(target_os = "windows")] +impl From for proto::DaitaSettings { + fn from(settings: mullvad_types::wireguard::DaitaSettings) -> Self { + proto::DaitaSettings { + enabled: settings.enabled, + } + } +} + +#[cfg(target_os = "windows")] +impl From for mullvad_types::wireguard::DaitaSettings { + fn from(settings: proto::DaitaSettings) -> Self { + mullvad_types::wireguard::DaitaSettings { + enabled: settings.enabled, + } + } +} diff --git a/mullvad-relay-selector/src/relay_selector/detailer.rs b/mullvad-relay-selector/src/relay_selector/detailer.rs index 807903eb39ba..3dc1cc903e7d 100644 --- a/mullvad-relay-selector/src/relay_selector/detailer.rs +++ b/mullvad-relay-selector/src/relay_selector/detailer.rs @@ -84,6 +84,9 @@ fn wireguard_singlehop_endpoint( allowed_ips: all_of_the_internet(), // This will be filled in later, not the relay selector's problem psk: None, + // This will be filled in later + #[cfg(target_os = "windows")] + constant_packet_size: false, }; Ok(MullvadWireguardEndpoint { peer: peer_config, @@ -122,6 +125,9 @@ fn wireguard_multihop_endpoint( allowed_ips: all_of_the_internet(), // This will be filled in later, not the relay selector's problem psk: None, + // This will be filled in later + #[cfg(target_os = "windows")] + constant_packet_size: false, }; let entry_endpoint = { @@ -137,6 +143,9 @@ fn wireguard_multihop_endpoint( allowed_ips: vec![IpNetwork::from(exit.endpoint.ip())], // This will be filled in later psk: None, + // This will be filled in later + #[cfg(target_os = "windows")] + constant_packet_size: false, }; Ok(MullvadWireguardEndpoint { diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index fbf3ac1c4bde..7ec2fc02bbe7 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -99,6 +99,7 @@ pub struct RelaySelector { pub struct SelectorConfig { // Normal relay settings pub relay_settings: RelaySettings, + pub additional_constraints: AdditionalRelayConstraints, pub custom_lists: CustomListsSettings, pub relay_overrides: Vec, // Wireguard specific data @@ -108,6 +109,20 @@ pub struct SelectorConfig { pub bridge_settings: BridgeSettings, } +/// Extra relay constraints not specified in `relay_settings`. +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct AdditionalRelayConstraints { + pub wireguard: AdditionalWireguardConstraints, +} + +/// Constraints to use when selecting WireGuard servers +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct AdditionalWireguardConstraints { + /// If true, select WireGuard relays that support DAITA. If false, select any + /// server. + pub daita: bool, +} + /// Values which affect the choice of relay but are only known at runtime. #[derive(Clone, Debug)] pub struct RuntimeParameters { @@ -273,6 +288,7 @@ impl Default for SelectorConfig { let default_settings = Settings::default(); SelectorConfig { relay_settings: default_settings.relay_settings, + additional_constraints: default_settings.additional_constraints, bridge_settings: default_settings.bridge_settings, obfuscation_settings: default_settings.obfuscation_settings, bridge_state: default_settings.bridge_state, diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs index ed3546c62ecf..40efae24e2a6 100644 --- a/mullvad-relay-selector/tests/relay_selector.rs +++ b/mullvad-relay-selector/tests/relay_selector.rs @@ -54,6 +54,7 @@ static RELAYS: Lazy = Lazy::new(|| RelayList { "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", ) .unwrap(), + daita: false, }), location: None, }, @@ -71,6 +72,7 @@ static RELAYS: Lazy = Lazy::new(|| RelayList { "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", ) .unwrap(), + daita: false, }), location: None, }, @@ -414,6 +416,7 @@ fn test_wireguard_entry() { "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", ) .unwrap(), + daita: false, }), location: None, }, @@ -431,6 +434,7 @@ fn test_wireguard_entry() { "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", ) .unwrap(), + daita: false, }), location: None, }, @@ -932,6 +936,7 @@ fn test_include_in_country() { "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", ) .unwrap(), + daita: false, }), location: None, }, @@ -949,6 +954,7 @@ fn test_include_in_country() { "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", ) .unwrap(), + daita: false, }), location: None, }, diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs index 77cc621728bb..09ab312a5f64 100644 --- a/mullvad-types/src/relay_list.rs +++ b/mullvad-types/src/relay_list.rs @@ -129,6 +129,7 @@ impl PartialEq for Relay { /// # "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", /// # ) /// # .unwrap(), + /// # daita: false, /// # }), /// # location: None, /// }; @@ -232,6 +233,9 @@ struct PortRange { pub struct WireguardRelayEndpointData { /// Public key used by the relay peer pub public_key: wireguard::PublicKey, + /// Whether the server supports DAITA + #[serde(default)] + pub daita: bool, } #[derive(Debug, Default, Clone, Deserialize, Serialize)] diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs index c85860a776aa..d8e9bd403b71 100644 --- a/mullvad-types/src/wireguard.rs +++ b/mullvad-types/src/wireguard.rs @@ -55,6 +55,12 @@ impl FromStr for QuantumResistantState { #[error("Not a valid state")] pub struct QuantumResistantStateParseError; +#[cfg(target_os = "windows")] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)] +pub struct DaitaSettings { + pub enabled: bool, +} + /// Contains account specific wireguard data #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct WireguardData { @@ -201,6 +207,9 @@ pub struct TunnelOptions { pub mtu: Option, /// Obtain a PSK using the relay config client. pub quantum_resistant: QuantumResistantState, + /// Configure DAITA + #[cfg(target_os = "windows")] + pub daita: DaitaSettings, /// Interval used for automatic key rotation #[cfg_attr(target_os = "android", jnix(skip))] pub rotation_interval: Option, @@ -212,6 +221,8 @@ impl Default for TunnelOptions { TunnelOptions { mtu: None, quantum_resistant: QuantumResistantState::Auto, + #[cfg(target_os = "windows")] + daita: DaitaSettings::default(), rotation_interval: None, } } @@ -226,6 +237,8 @@ impl TunnelOptions { QuantumResistantState::On => true, QuantumResistantState::Off => false, }, + #[cfg(target_os = "windows")] + daita: self.daita.enabled, } } } diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 0daa8b996c73..ce860d0ed635 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -177,7 +177,6 @@ impl TunnelMonitor { let config = talpid_wireguard::config::Config::from_parameters(params, default_mtu)?; let monitor = talpid_wireguard::WireguardMonitor::start( config, - params.options.quantum_resistant, #[cfg(not(target_os = "android"))] detect_mtu, log.as_deref(), diff --git a/talpid-tunnel-config-client/build.rs b/talpid-tunnel-config-client/build.rs index 129cfd71546b..aeb21fe009f2 100644 --- a/talpid-tunnel-config-client/build.rs +++ b/talpid-tunnel-config-client/build.rs @@ -1,3 +1,3 @@ fn main() { - tonic_build::compile_protos("proto/tunnel_config.proto").unwrap(); + tonic_build::compile_protos("proto/ephemeralpeer.proto").unwrap(); } diff --git a/talpid-tunnel-config-client/examples/psk-exchange.rs b/talpid-tunnel-config-client/examples/psk-exchange.rs index 87200d033660..e7b34ab851a5 100644 --- a/talpid-tunnel-config-client/examples/psk-exchange.rs +++ b/talpid-tunnel-config-client/examples/psk-exchange.rs @@ -18,14 +18,16 @@ async fn main() { let pubkey = PublicKey::from_base64(pubkey_string.trim()).expect("Invalid public key"); let private_key = PrivateKey::new_from_random(); - let psk = talpid_tunnel_config_client::push_pq_key( + let ephemeral_peer = talpid_tunnel_config_client::request_ephemeral_peer( tuncfg_server_ip, pubkey, private_key.public_key(), + true, + false, ) .await .unwrap(); println!("private key: {private_key:?}"); - println!("psk: {psk:?}"); + println!("psk: {:?}", ephemeral_peer.psk); } diff --git a/talpid-tunnel-config-client/examples/tuncfg-server.rs b/talpid-tunnel-config-client/examples/tuncfg-server.rs index 4d29d764a268..928587e96813 100644 --- a/talpid-tunnel-config-client/examples/tuncfg-server.rs +++ b/talpid-tunnel-config-client/examples/tuncfg-server.rs @@ -1,80 +1,93 @@ -//! A server implementation of the tuncfg PskExchangeV1 RPC to test -//! the client side implementation. +//! A server implementation of the tuncfg RegisterPeerV1 RPC to test +//! the client side implementation of PQ. #[allow(clippy::derive_partial_eq_without_eq)] mod proto { - tonic::include_proto!("tunnel_config"); + tonic::include_proto!("ephemeralpeer"); } use classic_mceliece_rust::{PublicKey, CRYPTO_PUBLICKEYBYTES}; use proto::{ - post_quantum_secure_server::{PostQuantumSecure, PostQuantumSecureServer}, - PskRequestV1, PskResponseV1, + ephemeral_peer_server::{EphemeralPeer, EphemeralPeerServer}, + EphemeralPeerRequestV1, EphemeralPeerResponseV1, PostQuantumResponseV1, }; use talpid_types::net::wireguard::PresharedKey; use tonic::{transport::Server, Request, Response, Status}; #[derive(Debug, Default)] -pub struct PostQuantumSecureImpl {} +pub struct EphemeralPeerImpl {} #[tonic::async_trait] -impl PostQuantumSecure for PostQuantumSecureImpl { - async fn psk_exchange_v1( +impl EphemeralPeer for EphemeralPeerImpl { + async fn register_peer_v1( &self, - request: Request, - ) -> Result, Status> { + request: Request, + ) -> Result, Status> { let mut rng = rand::thread_rng(); let request = request.into_inner(); - println!("wg_pubkey: {:?}", request.wg_pubkey); - println!("wg_psk_pubkey: {:?}", request.wg_psk_pubkey); + println!("wg_parent_pubkey: {:?}", request.wg_parent_pubkey); + println!( + "wg_ephemeral_peer_pubkey: {:?}", + request.wg_ephemeral_peer_pubkey + ); + println!("daita (no-op): {:?}", request.daita); - // The ciphertexts that will be returned to the client - let mut ciphertexts = Vec::new(); - // The final PSK that is computed by XORing together all the KEM outputs. - let mut psk_data = Box::new([0u8; 32]); + let post_quantum = if let Some(post_quantum) = request.post_quantum { + // The ciphertexts that will be returned to the client + let mut ciphertexts = Vec::new(); - for kem_pubkey in request.kem_pubkeys { - println!("\tKEM algorithm: {}", kem_pubkey.algorithm_name); - let (ciphertext, shared_secret) = match kem_pubkey.algorithm_name.as_str() { - "Classic-McEliece-460896f-round3" => { - let key_data: [u8; CRYPTO_PUBLICKEYBYTES] = - kem_pubkey.key_data.as_slice().try_into().unwrap(); - let public_key = PublicKey::from(&key_data); - let (ciphertext, shared_secret) = - classic_mceliece_rust::encapsulate_boxed(&public_key, &mut rng); - (ciphertext.as_array().to_vec(), *shared_secret.as_array()) - } - "Kyber1024" => { - let public_key = kem_pubkey.key_data.as_slice(); - let (ciphertext, shared_secret) = - pqc_kyber::encapsulate(public_key, &mut rng).unwrap(); - (ciphertext.to_vec(), shared_secret) - } - name => panic!("Unsupported KEM algorithm: {name}"), - }; + // The final PSK that is computed by XORing together all the KEM outputs. + let mut psk_data = Box::new([0u8; 32]); + + for kem_pubkey in post_quantum.kem_pubkeys { + println!("\tKEM algorithm: {}", kem_pubkey.algorithm_name); + let (ciphertext, shared_secret) = match kem_pubkey.algorithm_name.as_str() { + "Classic-McEliece-460896f-round3" => { + let key_data: [u8; CRYPTO_PUBLICKEYBYTES] = + kem_pubkey.key_data.as_slice().try_into().unwrap(); + let public_key = PublicKey::from(&key_data); + let (ciphertext, shared_secret) = + classic_mceliece_rust::encapsulate_boxed(&public_key, &mut rng); + (ciphertext.as_array().to_vec(), *shared_secret.as_array()) + } + "Kyber1024" => { + let public_key = kem_pubkey.key_data.as_slice(); + let (ciphertext, shared_secret) = + pqc_kyber::encapsulate(public_key, &mut rng).unwrap(); + (ciphertext.to_vec(), shared_secret) + } + name => panic!("Unsupported KEM algorithm: {name}"), + }; - ciphertexts.push(ciphertext); - println!("\tshared secret: {shared_secret:?}"); - for (psk_byte, shared_secret_byte) in psk_data.iter_mut().zip(shared_secret.iter()) { - *psk_byte ^= shared_secret_byte; + ciphertexts.push(ciphertext); + println!("\tshared secret: {shared_secret:?}"); + for (psk_byte, shared_secret_byte) in psk_data.iter_mut().zip(shared_secret.iter()) + { + *psk_byte ^= shared_secret_byte; + } } - } - let psk = PresharedKey::from(psk_data); - println!("psk: {psk:?}"); - println!("=============================================="); - Ok(Response::new(PskResponseV1 { ciphertexts })) + let psk = PresharedKey::from(psk_data); + println!("psk: {psk:?}"); + println!("=============================================="); + + Some(PostQuantumResponseV1 { ciphertexts }) + } else { + None + }; + + Ok(Response::new(EphemeralPeerResponseV1 { post_quantum })) } } #[tokio::main] async fn main() -> Result<(), Box> { let addr = "127.0.0.1:1337".parse()?; - let server = PostQuantumSecureImpl::default(); + let server = EphemeralPeerImpl::default(); Server::builder() - .add_service(PostQuantumSecureServer::new(server)) + .add_service(EphemeralPeerServer::new(server)) .serve(addr) .await?; diff --git a/talpid-tunnel-config-client/proto/ephemeralpeer.proto b/talpid-tunnel-config-client/proto/ephemeralpeer.proto new file mode 100644 index 000000000000..bb49eb5598b1 --- /dev/null +++ b/talpid-tunnel-config-client/proto/ephemeralpeer.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; + +option go_package = "github.com/mullvad/wg-manager/tuncfg/api/ephemeralpeer"; + +package ephemeralpeer; + +service EphemeralPeer { + // Derive an ephemeral peer with one or several options enabled, such as PQ or DAITA. + // + // The VPN server associates the ephemeral peer with the peer who performed the exchange. Any + // already existing ephemeral peer for the normal peer is replaced. Each normal peer can have + // at most one ephemeral peer. + // + // The ephemeral peer is mutually exclusive to the normal peer. The server keeps both peers in + // memory, but only one of them is loaded into WireGuard at any point in time. A handshake from + // the normal peer unloads the corresponding ephemeral peer from WireGuard and vice versa. + // + // A new peer is negotiated to avoid a premature break of the tunnel used for negotiation. + // A tunnel would break prematurely if configuration such as preshared key were applied before the + // normal peer received the server's response. This cannot occur now because the client decides + // when to switch to the ephemeral tunnel. This design also allows the client to switch back to + // using a non-ephemeral tunnel at any point. + // + // The server gives no guarantees how long the ephemeral peer will be valid and working when it's + // no longer in use. The client should negotiate a new ephemeral peer every time it establishes a + // new tunnel to the server. + // + // The request from the VPN client should contain: + // * `wg_parent_pubkey` - The public key used by the current tunnel (that the request travels + // inside). + // * `wg_ephemeral_peer_pubkey` - A newly generated ephemeral WireGuard public key for the + // ephemeral peer. The server will associate the new configuration with this key. + // * One or more requests for different types of options. See the individual messages for more + // information. If a request is provided, a corresponding response may be returned in the + // server's response. + rpc RegisterPeerV1(EphemeralPeerRequestV1) returns (EphemeralPeerResponseV1) {} +} + +message EphemeralPeerRequestV1 { + bytes wg_parent_pubkey = 1; + bytes wg_ephemeral_peer_pubkey = 2; + PostQuantumRequestV1 post_quantum = 3; + DaitaRequestV1 daita = 4; +} + +// The v1 request supports exactly two algorithms. +// The algorithms can appear soletary or in mixed order: +// - "Classic-McEliece-460896f", but explicitly identified as "Classic-McEliece-460896f-round3" +// - "Kyber1024" +message PostQuantumRequestV1 { repeated KemPubkeyV1 kem_pubkeys = 1; } + +message KemPubkeyV1 { + string algorithm_name = 1; + bytes key_data = 2; +} + +message DaitaRequestV1 { bool activate_daita = 1; } + +message EphemeralPeerResponseV1 { + // The response from the VPN server contains: + // * `ciphertexts` - A list of the ciphertexts (the encapsulated shared secrets) for all + // public keys in `kem_pubkeys` in the request, in the same order as in the request. + // + // # Deriving the WireGuard PSK + // + // The PSK to be used in WireGuard's preshared-key field is computed by XORing the resulting + // shared secrets of all the KEM algorithms. All currently supported and planned to be + // supported algorithms output 32 bytes, so this is trivial. + // + // Since the PSK provided to WireGuard is directly fed into a HKDF, it is not important that + // the entropy in the PSK is uniformly distributed. The actual keys used for encrypting the + // data channel will have uniformly distributed entropy anyway, thanks to the HKDF. + // But even if that was not true, since both CME and Kyber run SHAKE256 as the last step + // of their internal key derivation, the output they produce are uniformly distributed. + // + // If we later want to support another type of KEM that produce longer or shorter output, + // we can hash that secret into a 32 byte hash before proceeding to the XOR step. + // + // Mixing with XOR (A = B ^ C) is fine since nothing about A is revealed even if one of B or C + // is known. Both B *and* C must be known to compute any bit in A. This means all involved + // KEM algorithms must be broken before the PSK can be computed by an attacker. + PostQuantumResponseV1 post_quantum = 1; +} + +message PostQuantumResponseV1 { repeated bytes ciphertexts = 1; } diff --git a/talpid-tunnel-config-client/proto/tunnel_config.proto b/talpid-tunnel-config-client/proto/tunnel_config.proto deleted file mode 100644 index e6e4b73d97ba..000000000000 --- a/talpid-tunnel-config-client/proto/tunnel_config.proto +++ /dev/null @@ -1,83 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/mullvad/wg-manager/server/tuncfg"; - -package tunnel_config; - -service PostQuantumSecure { - // Allows deriving a preshared key (PSK) using one or multiple PQ-secure key-encapsulation - // mechanisms (KEM). The preshared key is added to WireGuard's preshared-key field in a new - // ephemeral peer (PQ-peer). This makes the tunnel resistant towards attacks using - // quantum computers. - // - // The VPN server associates the PQ-peer with the peer who performed the exchange. Any - // already existing PQ-peer for the normal peer is replaced. Each normal peer can have - // at most one PQ-peer. - // - // The PQ-peer is mutually exclusive to the normal peer. The server keeps both peers in memory, - // but only one of them is loaded into WireGuard at any point in time. A handshake from the - // normal peer unloads the corresponding PQ-peer from WireGuard and vice versa. - // - // A new peer is negotiated for PQ to avoid a premature break of the tunnel used for negotiation. - // A tunnel would break prematurely if the preshared key is applied before the normal peer - // received the server's contribution to the KEM exchange. This cannot occur now because - // the client decides when to switch to the PQ-secure tunnel. This design also allows - // the client to switch back to using a non-PQ-secure tunnel at any point. - // - // The negotiated PQ-peer is ephemeral. The server gives no guarantees how long it will be - // valid and working. The client should negotiate a new PQ-peer every time it establishes a new - // tunnel to the server. - // - // The full exchange requires just a single request-response round trip between the VPN client - // and the VPN server. - // - // # Request-response format - // - // The request from the VPN client contains: - // * `wg_pubkey` - The public key used by the current tunnel (that the request travels inside). - // * `wg_psk_pubkey` - A newly generated ephemeral WireGuard public key for the PQ-peer. - // The server will associate the derived PSK with this public key. - // * `kem_pubkeys` - A list describing the KEM algorithms. Must have at least one entry. - // The same KEM must not be listed more than once. Each list item contains: - // * `algorithm_name` - The name of the KEM, including which variant. Should be the same - // name/format that `liboqs` uses. - // * `key_data` - The client's public key for this KEM. Will be used by the server to - // encapsulate the shared secret for this KEM. - // - // The response from the VPN server contains: - // * `ciphertexts` - A list of the ciphertexts (the encapsulated shared secrets) for all - // public keys in `kem_pubkeys` in the request, in the same order as in the request. - // - // # Deriving the WireGuard PSK - // - // The PSK to be used in WireGuard's preshared-key field is computed by XORing the resulting - // shared secrets of all the KEM algorithms. All currently supported and planned to be - // supported algorithms output 32 bytes, so this is trivial. - // - // Since the PSK provided to WireGuard is directly fed into a HKDF, it is not important that - // the entropy in the PSK is uniformly distributed. The actual keys used for encrypting the - // data channel will have uniformly distributed entropy anyway, thanks to the HKDF. - // But even if that was not true, since both CME and Kyber run SHAKE256 as the last step - // of their internal key derivation, the output they produce are uniformly distributed. - // - // If we later want to support another type of KEM that produce longer or shorter output, - // we can hash that secret into a 32 byte hash before proceeding to the XOR step. - // - // Mixing with XOR (A = B ^ C) is fine since nothing about A is revealed even if one of B or C - // is known. Both B *and* C must be known to compute any bit in A. This means all involved - // KEM algorithms must be broken before the PSK can be computed by an attacker. - rpc PskExchangeV1(PskRequestV1) returns (PskResponseV1) {} -} - -message PskRequestV1 { - bytes wg_pubkey = 1; - bytes wg_psk_pubkey = 2; - repeated KemPubkeyV1 kem_pubkeys = 3; -} - -message KemPubkeyV1 { - string algorithm_name = 1; - bytes key_data = 2; -} - -message PskResponseV1 { repeated bytes ciphertexts = 1; } diff --git a/talpid-tunnel-config-client/src/classic_mceliece.rs b/talpid-tunnel-config-client/src/classic_mceliece.rs index a2d5dc0be20d..2036bc3fc7df 100644 --- a/talpid-tunnel-config-client/src/classic_mceliece.rs +++ b/talpid-tunnel-config-client/src/classic_mceliece.rs @@ -1,6 +1,5 @@ -use classic_mceliece_rust::{ - keypair_boxed, Ciphertext, PublicKey, SecretKey, SharedSecret, CRYPTO_CIPHERTEXTBYTES, -}; +use classic_mceliece_rust::{keypair_boxed, Ciphertext, CRYPTO_CIPHERTEXTBYTES}; +pub use classic_mceliece_rust::{PublicKey, SecretKey, SharedSecret}; /// The `keypair_boxed` function needs just under 1 MiB of stack in debug /// builds. Even though it probably works to run it directly on the main diff --git a/talpid-tunnel-config-client/src/kyber.rs b/talpid-tunnel-config-client/src/kyber.rs index 5654b2e10b71..003c88dc484d 100644 --- a/talpid-tunnel-config-client/src/kyber.rs +++ b/talpid-tunnel-config-client/src/kyber.rs @@ -1,6 +1,5 @@ -use pqc_kyber::{SecretKey, KYBER_CIPHERTEXTBYTES}; - -pub use pqc_kyber::{keypair, KyberError}; +use pqc_kyber::KYBER_CIPHERTEXTBYTES; +pub use pqc_kyber::{keypair, KyberError, SecretKey}; /// Use the strongest variant of Kyber. It is fast and the keys are small, so there is no practical /// benefit of going with anything lower. diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs index 2c7b5e58f38c..e89b1be42d38 100644 --- a/talpid-tunnel-config-client/src/lib.rs +++ b/talpid-tunnel-config-client/src/lib.rs @@ -13,7 +13,7 @@ mod kyber; #[allow(clippy::derive_partial_eq_without_eq)] mod proto { - tonic::include_proto!("tunnel_config"); + tonic::include_proto!("ephemeralpeer"); } use libc::setsockopt; @@ -34,6 +34,7 @@ use sys::*; pub enum Error { GrpcConnectError(tonic::transport::Error), GrpcError(tonic::Status), + MissingCiphertexts, InvalidCiphertextLength { algorithm: &'static str, actual: usize, @@ -51,6 +52,7 @@ impl std::fmt::Display for Error { match self { GrpcConnectError(_) => "Failed to connect to config service".fmt(f), GrpcError(status) => write!(f, "RPC failed: {status}"), + MissingCiphertexts => write!(f, "Found no ciphertexts in response"), InvalidCiphertextLength { algorithm, actual, @@ -77,7 +79,7 @@ impl std::error::Error for Error { } } -type RelayConfigService = proto::post_quantum_secure_client::PostQuantumSecureClient; +type RelayConfigService = proto::ephemeral_peer_client::EphemeralPeerClient; /// Port used by the tunnel config service. pub const CONFIG_SERVICE_PORT: u16 = 1337; @@ -93,72 +95,118 @@ pub const CONFIG_SERVICE_PORT: u16 = 1337; /// handshake to work even if there is fragmentation. const CONFIG_CLIENT_MTU: u16 = 576; -/// Generates a new WireGuard key pair and negotiates a PSK with the relay in a PQ-safe -/// manner. This creates a peer on the relay with the new WireGuard pubkey and PSK, -/// which can then be used to establish a PQ-safe tunnel to the relay. -// TODO: consider binding to the tunnel interface here, on non-windows platforms -pub async fn push_pq_key( +pub struct EphemeralPeer { + pub psk: Option, +} + +/// Negotiate a short-lived peer with a PQ-safe PSK or with DAITA enabled. +pub async fn request_ephemeral_peer( + service_address: IpAddr, + parent_pubkey: PublicKey, + ephemeral_pubkey: PublicKey, + enable_post_quantum: bool, + enable_daita: bool, +) -> Result { + request_ephemeral_peer_with_opts( + service_address, + parent_pubkey, + ephemeral_pubkey, + enable_post_quantum, + enable_daita, + ) + .await +} + +pub async fn request_ephemeral_peer_with_opts( service_address: IpAddr, - wg_pubkey: PublicKey, - wg_psk_pubkey: PublicKey, -) -> Result { - let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await; - let kyber_keypair = kyber::keypair(&mut rand::thread_rng()); + parent_pubkey: PublicKey, + ephemeral_pubkey: PublicKey, + enable_post_quantum: bool, + enable_daita: bool, +) -> Result { + let (pq_request, kem_secrets) = if enable_post_quantum { + let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await; + let kyber_keypair = kyber::keypair(&mut rand::thread_rng()); + + ( + Some(proto::PostQuantumRequestV1 { + kem_pubkeys: vec![ + proto::KemPubkeyV1 { + algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(), + key_data: cme_kem_pubkey.as_array().to_vec(), + }, + proto::KemPubkeyV1 { + algorithm_name: kyber::ALGORITHM_NAME.to_owned(), + key_data: kyber_keypair.public.to_vec(), + }, + ], + }), + Some((cme_kem_secret, kyber_keypair.secret)), + ) + } else { + (None, None) + }; + + let daita = Some(proto::DaitaRequestV1 { + activate_daita: enable_daita, + }); let mut client = new_client(service_address).await?; let response = client - .psk_exchange_v1(proto::PskRequestV1 { - wg_pubkey: wg_pubkey.as_bytes().to_vec(), - wg_psk_pubkey: wg_psk_pubkey.as_bytes().to_vec(), - kem_pubkeys: vec![ - proto::KemPubkeyV1 { - algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(), - key_data: cme_kem_pubkey.as_array().to_vec(), - }, - proto::KemPubkeyV1 { - algorithm_name: kyber::ALGORITHM_NAME.to_owned(), - key_data: kyber_keypair.public.to_vec(), - }, - ], + .register_peer_v1(proto::EphemeralPeerRequestV1 { + wg_parent_pubkey: parent_pubkey.as_bytes().to_vec(), + wg_ephemeral_peer_pubkey: ephemeral_pubkey.as_bytes().to_vec(), + post_quantum: pq_request, + daita, }) .await .map_err(Error::GrpcError)?; - let ciphertexts = response.into_inner().ciphertexts; - - // Unpack the ciphertexts into one per KEM without needing to access them by index. - let [cme_ciphertext, kyber_ciphertext] = <&[Vec; 2]>::try_from(ciphertexts.as_slice()) - .map_err(|_| Error::InvalidCiphertextCount { - actual: ciphertexts.len(), - })?; - - // Store the PSK data on the heap. So it can be passed around and then zeroized on drop without - // being stored in a bunch of places on the stack. - let mut psk_data = Box::new([0u8; 32]); - - // Decapsulate Classic McEliece and mix into PSK - { - let mut shared_secret = classic_mceliece::decapsulate(&cme_kem_secret, cme_ciphertext)?; - xor_assign(&mut psk_data, shared_secret.as_array()); + let psk = if let Some((cme_kem_secret, kyber_secret)) = kem_secrets { + let ciphertexts = response + .into_inner() + .post_quantum + .ok_or(Error::MissingCiphertexts)? + .ciphertexts; + + // Unpack the ciphertexts into one per KEM without needing to access them by index. + let [cme_ciphertext, kyber_ciphertext] = <&[Vec; 2]>::try_from(ciphertexts.as_slice()) + .map_err(|_| Error::InvalidCiphertextCount { + actual: ciphertexts.len(), + })?; + + // Store the PSK data on the heap. So it can be passed around and then zeroized on drop without + // being stored in a bunch of places on the stack. + let mut psk_data = Box::new([0u8; 32]); + + // Decapsulate Classic McEliece and mix into PSK + { + let mut shared_secret = classic_mceliece::decapsulate(&cme_kem_secret, cme_ciphertext)?; + xor_assign(&mut psk_data, shared_secret.as_array()); + + // This should happen automatically due to `SharedSecret` implementing ZeroizeOnDrop. But + // doing it explicitly provides a stronger guarantee that it's not accidentally + // removed. + shared_secret.zeroize(); + } + // Decapsulate Kyber and mix into PSK + { + let mut shared_secret = kyber::decapsulate(kyber_secret, kyber_ciphertext)?; + xor_assign(&mut psk_data, &shared_secret); + + // The shared secret is sadly stored in an array on the stack. So we can't get any + // guarantees that it's not copied around on the stack. The best we can do here + // is to zero out the version we have and hope the compiler optimizes out copies. + // https://github.com/Argyle-Software/kyber/issues/59 + shared_secret.zeroize(); + } - // This should happen automatically due to `SharedSecret` implementing ZeroizeOnDrop. But - // doing it explicitly provides a stronger guarantee that it's not accidentally - // removed. - shared_secret.zeroize(); - } - // Decapsulate Kyber and mix into PSK - { - let mut shared_secret = kyber::decapsulate(kyber_keypair.secret, kyber_ciphertext)?; - xor_assign(&mut psk_data, &shared_secret); - - // The shared secret is sadly stored in an array on the stack. So we can't get any - // guarantees that it's not copied around on the stack. The best we can do here - // is to zero out the version we have and hope the compiler optimizes out copies. - // https://github.com/Argyle-Software/kyber/issues/59 - shared_secret.zeroize(); - } + Some(PresharedKey::from(psk_data)) + } else { + None + }; - Ok(PresharedKey::from(psk_data)) + Ok(EphemeralPeer { psk }) } /// Performs `dst = dst ^ src`. diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs index 2b9f0a73664f..a17d8ceb5f25 100644 --- a/talpid-types/src/net/mod.rs +++ b/talpid-types/src/net/mod.rs @@ -40,6 +40,8 @@ impl TunnelParameters { obfuscation: None, entry_endpoint: None, tunnel_interface: None, + #[cfg(target_os = "windows")] + daita: false, }, TunnelParameters::Wireguard(params) => TunnelEndpoint { tunnel_type: TunnelType::Wireguard, @@ -55,6 +57,8 @@ impl TunnelParameters { .get_exit_endpoint() .map(|_| params.connection.get_endpoint()), tunnel_interface: None, + #[cfg(target_os = "windows")] + daita: params.options.daita, }, } } @@ -183,6 +187,8 @@ pub struct TunnelEndpoint { pub entry_endpoint: Option, #[cfg_attr(target_os = "android", jnix(skip))] pub tunnel_interface: Option, + #[cfg(target_os = "windows")] + pub daita: bool, } impl fmt::Display for TunnelEndpoint { diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs index db7b2da3a919..f7212236e2f9 100644 --- a/talpid-types/src/net/wireguard.rs +++ b/talpid-types/src/net/wireguard.rs @@ -60,6 +60,10 @@ pub struct PeerConfig { /// ephemeral and living in memory only. #[serde(skip)] pub psk: Option, + /// Enable constant packet sizes for `entry_peer`` + #[cfg(target_os = "windows")] + #[serde(skip)] + pub constant_packet_size: bool, } #[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)] @@ -76,6 +80,9 @@ pub struct TunnelOptions { pub mtu: Option, /// Perform PQ-safe PSK exchange when connecting pub quantum_resistant: bool, + /// Enable DAITA during tunnel config + #[cfg(target_os = "windows")] + pub daita: bool, } /// Wireguard x25519 private key diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml index 1fc6e13b3a40..c2562d212c30 100644 --- a/talpid-wireguard/Cargo.toml +++ b/talpid-wireguard/Cargo.toml @@ -12,6 +12,7 @@ workspace = true [dependencies] thiserror = { workspace = true } +base64 = "0.13" futures = "0.3.15" hex = "0.4" ipnetwork = "0.16" @@ -54,6 +55,7 @@ talpid-dbus = { path = "../talpid-dbus" } bitflags = "1.2" talpid-windows = { path = "../talpid-windows" } widestring = "1.0" +maybenot = "1.0" # TODO: Figure out which features are needed and which are not [target.'cfg(windows)'.dependencies.windows-sys] diff --git a/talpid-wireguard/src/config.rs b/talpid-wireguard/src/config.rs index 29328eb68152..f10a0e485991 100644 --- a/talpid-wireguard/src/config.rs +++ b/talpid-wireguard/src/config.rs @@ -28,6 +28,10 @@ pub struct Config { pub enable_ipv6: bool, /// Obfuscator config to be used for reaching the relay. pub obfuscator_config: Option, + /// Enable quantum-resistant PSK exchange + pub quantum_resistant: bool, + /// Enable DAITA + pub daita: bool, } /// Configuration errors @@ -92,6 +96,11 @@ impl Config { #[cfg(target_os = "linux")] enable_ipv6: generic_options.enable_ipv6, obfuscator_config: obfuscator_config.to_owned(), + quantum_resistant: wg_options.quantum_resistant, + #[cfg(target_os = "windows")] + daita: wg_options.daita, + #[cfg(not(target_os = "windows"))] + daita: false, }; for peer in config.peers_mut() { diff --git a/talpid-wireguard/src/connectivity_check.rs b/talpid-wireguard/src/connectivity_check.rs index e820a3eb73a1..70f88e687215 100644 --- a/talpid-wireguard/src/connectivity_check.rs +++ b/talpid-wireguard/src/connectivity_check.rs @@ -612,6 +612,11 @@ mod test { ) -> Pin> + Send>> { Box::pin(async { Ok(()) }) } + + #[cfg(target_os = "windows")] + fn start_daita(&mut self) -> std::result::Result<(), TunnelError> { + Ok(()) + } } fn mock_monitor( diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs index 72d6a31566df..bdef52343e16 100644 --- a/talpid-wireguard/src/lib.rs +++ b/talpid-wireguard/src/lib.rs @@ -260,7 +260,6 @@ impl WireguardMonitor { + 'static, >( mut config: Config, - psk_negotiation: bool, #[cfg(not(target_os = "android"))] detect_mtu: bool, log_path: Option<&Path>, args: TunnelArgs<'_, F>, @@ -283,12 +282,12 @@ impl WireguardMonitor { log_path, args.resource_dir, args.tun_provider.clone(), + #[cfg(target_os = "android")] + config.quantum_resistant, #[cfg(target_os = "windows")] args.route_manager.clone(), #[cfg(target_os = "windows")] setup_done_tx, - #[cfg(target_os = "android")] - psk_negotiation, )?; let iface_name = tunnel.get_interface_name(); @@ -336,7 +335,7 @@ impl WireguardMonitor { .await?; let metadata = Self::tunnel_metadata(&iface_name, &config); - let allowed_traffic = if psk_negotiation { + let allowed_traffic = if config.quantum_resistant || config.daita { AllowedTunnelTraffic::One(Endpoint::new( config.ipv4_gateway, talpid_tunnel_config_client::CONFIG_SERVICE_PORT, @@ -365,16 +364,16 @@ impl WireguardMonitor { .map_err(Error::SetupRoutingError) .map_err(CloseMsg::SetupError)?; - let psk_obfs_sender = close_obfs_sender.clone(); - if psk_negotiation { - Self::psk_negotiation( + let ephemeral_obfs_sender = close_obfs_sender.clone(); + if config.quantum_resistant || config.daita { + Self::config_ephemeral_peers( &tunnel, &mut config, args.retry_attempt, args.on_event.clone(), &iface_name, obfuscator.clone(), - psk_obfs_sender, + ephemeral_obfs_sender, #[cfg(target_os = "android")] args.tun_provider, ) @@ -386,6 +385,14 @@ impl WireguardMonitor { let config = config.clone(); let iface_name = iface_name.clone(); tokio::task::spawn(async move { + #[cfg(target_os = "windows")] + if config.daita { + // TODO: For now, we assume the MTU during the tunnel lifetime. + // We could instead poke maybenot whenever we detect changes to it. + log::warn!("MTU detection is not supported with DAITA. Skipping"); + return; + } + if let Err(e) = mtu_detection::automatic_mtu_correction( gateway, iface_name, @@ -465,7 +472,7 @@ impl WireguardMonitor { } #[allow(clippy::too_many_arguments)] - async fn psk_negotiation( + async fn config_ephemeral_peers( tunnel: &Arc>>>, config: &mut Config, retry_attempt: u32, @@ -482,7 +489,7 @@ impl WireguardMonitor { + Clone + 'static, { - let wg_psk_privkey = PrivateKey::new_from_random(); + let ephemeral_private_key = PrivateKey::new_from_random(); let close_obfs_sender = close_obfs_sender.clone(); let allowed_traffic = Endpoint::new( @@ -507,11 +514,17 @@ impl WireguardMonitor { let metadata = Self::tunnel_metadata(iface_name, config); (on_event)(TunnelEvent::InterfaceUp(metadata, allowed_traffic.clone())).await; - let exit_psk = - Self::perform_psk_negotiation(retry_attempt, config, wg_psk_privkey.public_key()) - .await?; + let exit_should_have_daita = config.daita && !config.is_multihop(); + let exit_psk = Self::request_ephemeral_peer( + retry_attempt, + config, + ephemeral_private_key.public_key(), + config.quantum_resistant, + exit_should_have_daita, + ) + .await?; - log::debug!("Successfully exchanged PSK with exit peer"); + log::debug!("Retrieved ephemeral peer"); if config.is_multihop() { // Set up tunnel to lead to entry @@ -531,22 +544,27 @@ impl WireguardMonitor { &tun_provider, ) .await?; - let entry_psk = Some( - Self::perform_psk_negotiation( - retry_attempt, - &entry_config, - wg_psk_privkey.public_key(), - ) - .await?, - ); + let entry_psk = Self::request_ephemeral_peer( + retry_attempt, + &entry_config, + ephemeral_private_key.public_key(), + config.quantum_resistant, + config.daita, + ) + .await?; log::debug!("Successfully exchanged PSK with entry peer"); config.entry_peer.psk = entry_psk; } - config.exit_peer_mut().psk = Some(exit_psk); + config.exit_peer_mut().psk = exit_psk; + #[cfg(target_os = "windows")] + if config.daita { + log::trace!("Enabling constant packet size for entry peer"); + config.entry_peer.constant_packet_size = true; + } - config.tunnel.private_key = wg_psk_privkey; + config.tunnel.private_key = ephemeral_private_key; *config = Self::reconfigure_tunnel( tunnel, @@ -557,6 +575,19 @@ impl WireguardMonitor { &tun_provider, ) .await?; + + #[cfg(target_os = "windows")] + if config.daita { + // Start local DAITA machines + let mut tunnel = tunnel.lock().unwrap(); + if let Some(tunnel) = tunnel.as_mut() { + tunnel + .start_daita() + .map_err(Error::TunnelError) + .map_err(CloseMsg::SetupError)?; + } + } + let metadata = Self::tunnel_metadata(iface_name, config); (on_event)(TunnelEvent::InterfaceUp( metadata, @@ -678,12 +709,14 @@ impl WireguardMonitor { Ok(()) } - async fn perform_psk_negotiation( + async fn request_ephemeral_peer( retry_attempt: u32, config: &Config, wg_psk_pubkey: PublicKey, - ) -> std::result::Result { - log::debug!("Performing PQ-safe PSK exchange"); + enable_pq: bool, + enable_daita: bool, + ) -> std::result::Result, CloseMsg> { + log::debug!("Requesting ephemeral peer"); let timeout = std::cmp::min( MAX_PSK_EXCHANGE_TIMEOUT, @@ -691,23 +724,25 @@ impl WireguardMonitor { .saturating_mul(PSK_EXCHANGE_TIMEOUT_MULTIPLIER.saturating_pow(retry_attempt)), ); - let psk = tokio::time::timeout( + let ephemeral = tokio::time::timeout( timeout, - talpid_tunnel_config_client::push_pq_key( + talpid_tunnel_config_client::request_ephemeral_peer_with_opts( IpAddr::from(config.ipv4_gateway), config.tunnel.private_key.public_key(), wg_psk_pubkey, + enable_pq, + enable_daita, ), ) .await .map_err(|_timeout_err| { - log::warn!("Timeout while negotiating PSK"); + log::warn!("Timeout while negotiating ephemeral peer"); CloseMsg::PskNegotiationTimeout })? .map_err(Error::PskNegotiationError) .map_err(CloseMsg::SetupError)?; - Ok(psk) + Ok(ephemeral.psk) } #[allow(unused_variables)] @@ -717,7 +752,7 @@ impl WireguardMonitor { log_path: Option<&Path>, resource_dir: &Path, tun_provider: Arc>, - #[cfg(target_os = "android")] psk_negotiation: bool, + #[cfg(target_os = "android")] gateway_only: bool, #[cfg(windows)] route_manager: crate::routing::RouteManagerHandle, #[cfg(windows)] setup_done_tx: mpsc::Sender>, ) -> Result> { @@ -771,7 +806,7 @@ impl WireguardMonitor { Self::get_tunnel_destinations(config).flat_map(Self::replace_default_prefixes); #[cfg(target_os = "android")] - let config = Self::patch_allowed_ips(config, psk_negotiation); + let config = Self::patch_allowed_ips(config, gateway_only); #[cfg(target_os = "linux")] log::debug!("Using userspace WireGuard implementation"); @@ -994,6 +1029,8 @@ pub(crate) trait Tunnel: Send { &self, _config: Config, ) -> Pin> + Send>>; + #[cfg(target_os = "windows")] + fn start_daita(&mut self) -> std::result::Result<(), TunnelError>; } /// Errors to be returned from WireGuard implementations, namely implementers of the Tunnel trait diff --git a/talpid-wireguard/src/wireguard_nt/daita.rs b/talpid-wireguard/src/wireguard_nt/daita.rs new file mode 100644 index 000000000000..75d6ebaa4d58 --- /dev/null +++ b/talpid-wireguard/src/wireguard_nt/daita.rs @@ -0,0 +1,450 @@ +use super::WIREGUARD_KEY_LENGTH; +use maybenot::framework::MachineId; +use once_cell::sync::OnceCell; +use std::{collections::HashMap, fs, io, path::Path, time::Duration}; +use std::{os::windows::prelude::RawHandle, sync::Arc}; +use talpid_types::net::wireguard::PublicKey; +use tokio::task::JoinHandle; +use windows_sys::Win32::Foundation::BOOLEAN; +use windows_sys::Win32::{ + Foundation::ERROR_NO_MORE_ITEMS, + System::Threading::{WaitForMultipleObjects, WaitForSingleObject, INFINITE}, +}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Failed to find maybenot machines + #[error("Failed to enumerate maybenot machines")] + EnumerateMachines(#[source] io::Error), + /// Failed to parse maybenot machine + #[error("Failed to parse maybenot machine \"{0}\"")] + InvalidMachine(String), + /// Failed to initialize quit event + #[error("Failed to initialize quit event")] + InitializeQuitEvent(#[source] io::Error), + /// Failed to initialize machinist handle + #[error("Failed to initialize machinist handle")] + InitializeHandle(#[source] io::Error), + /// Failed to initialize maybenot framework + #[error("Failed to initialize maybenot framework: {0}")] + InitializeMaybenot(String), +} + +// See DAITA_EVENT_TYPE: +// https://github.com/mullvad/wireguard-nt-priv/blob/mullvad-patches/driver/daita.h +#[repr(C)] +#[derive(Debug)] +#[allow(dead_code)] +pub enum EventType { + NonpaddingSent, + NonpaddingReceived, + PaddingSent, + PaddingReceived, +} + +// See DAITA_EVENT: +// https://github.com/mullvad/wireguard-nt-priv/blob/mullvad-patches/driver/daita.h +#[repr(C)] +#[derive(Debug)] +pub struct Event { + pub peer: [u8; WIREGUARD_KEY_LENGTH], + pub event_type: EventType, + pub xmit_bytes: u16, + pub user_context: usize, +} + +// See DAITA_ACTION_TYPE: +// https://github.com/mullvad/wireguard-nt-priv/blob/mullvad-patches/driver/daita.h +#[repr(C)] +pub enum ActionType { + InjectPadding, +} + +// See DAITA_PADDING_ACTION: +// https://github.com/mullvad/wireguard-nt-priv/blob/mullvad-patches/driver/daita.h +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct PaddingAction { + pub byte_count: u16, + pub replace: BOOLEAN, +} + +// See DAITA_ACTION: +// https://github.com/mullvad/wireguard-nt-priv/blob/mullvad-patches/driver/daita.h +#[repr(C)] +pub struct Action { + pub peer: [u8; WIREGUARD_KEY_LENGTH], + pub action_type: ActionType, + pub payload: ActionPayload, + pub user_context: usize, +} + +#[repr(C)] +pub union ActionPayload { + pub padding: PaddingAction, +} + +/// Maximum number of events that can be stored in the underlying buffer +const EVENTS_CAPACITY: usize = 1000; +/// Maximum number of actions that can be stored in the underlying buffer +const ACTIONS_CAPACITY: usize = 1000; + +pub mod bindings { + use super::*; + use windows_sys::Win32::Foundation::BOOL; + + pub type WireGuardDaitaActivateFn = unsafe extern "stdcall" fn( + adapter: RawHandle, + events_capacity: usize, + actions_capacity: usize, + ) -> BOOL; + pub type WireGuardDaitaEventDataAvailableEventFn = + unsafe extern "stdcall" fn(adapter: RawHandle) -> RawHandle; + pub type WireGuardDaitaReceiveEventsFn = + unsafe extern "stdcall" fn(adapter: RawHandle, events: *mut Event) -> usize; + pub type WireGuardDaitaSendActionFn = + unsafe extern "stdcall" fn(adapter: RawHandle, action: *const Action) -> BOOL; +} + +#[derive(Debug)] +pub struct Session { + adapter: Arc, +} + +impl Session { + /// Call `WireGuardDaitaActivate` for an existing WireGuard interface + pub(super) fn from_adapter(adapter: Arc) -> io::Result { + // SAFETY: `WgNtAdapter` has a valid adapter handle + unsafe { + adapter + .dll_handle + .daita_activate(adapter.handle, EVENTS_CAPACITY, ACTIONS_CAPACITY) + }?; + Ok(Self { adapter }) + } + + pub fn receive_events<'a>( + &self, + buffer: &'a mut [Event; EVENTS_CAPACITY], + ) -> io::Result<&'a [Event]> { + let num_events = unsafe { + // SAFETY: The adapter is valid, and the buffer is large enough to accommodate all + // events. + self.adapter + .dll_handle + .daita_receive_events(self.adapter.handle, buffer.as_mut_ptr())? + }; + Ok(unsafe { std::slice::from_raw_parts(buffer.as_ptr(), num_events) }) + } + + pub fn send_action(&self, action: &Action) -> io::Result<()> { + // SAFETY: The adapter is valid + unsafe { + self.adapter + .dll_handle + .daita_send_action(self.adapter.handle, action) + } + } + + pub fn event_data_available_event(&self) -> RawHandle { + // SAFETY: The adapter is valid + // This never fails when there's a DAITA session + unsafe { + self.adapter + .dll_handle + .daita_event_data_available_event(self.adapter.handle) + .unwrap() + } + } +} + +fn maybenot_event_from_event( + event: &Event, + machine_ids: &MachineMap, + override_size: Option, +) -> Option { + let xmit_bytes = override_size.unwrap_or(event.xmit_bytes); + match event.event_type { + EventType::PaddingReceived => Some(maybenot::framework::TriggerEvent::PaddingRecv { + bytes_recv: xmit_bytes, + }), + EventType::NonpaddingSent => Some(maybenot::framework::TriggerEvent::NonPaddingSent { + bytes_sent: xmit_bytes, + }), + EventType::NonpaddingReceived => Some(maybenot::framework::TriggerEvent::NonPaddingRecv { + bytes_recv: xmit_bytes, + }), + EventType::PaddingSent => Some(maybenot::framework::TriggerEvent::PaddingSent { + bytes_sent: xmit_bytes, + machine: machine_ids.get_machine_id(event.user_context)?.to_owned(), + }), + } +} + +/// Handle for a set of DAITA machines. +/// Note: `close` is NOT called implicitly when this is dropped. +pub struct MachinistHandle { + quit_event: talpid_windows::sync::Event, +} + +impl MachinistHandle { + fn new(quit_event: &talpid_windows::sync::Event) -> io::Result { + Ok(MachinistHandle { + quit_event: quit_event.duplicate()?, + }) + } + + /// Signal quit event + pub fn close(&self) -> io::Result<()> { + self.quit_event.set() + } +} + +pub struct Machinist { + daita: Arc, + machine_ids: MachineMap, + machine_tasks: HashMap>, + tokio_handle: tokio::runtime::Handle, + quit_event: talpid_windows::sync::Event, + peer: PublicKey, + override_size: Option, +} + +// TODO: This is silly. Let me use the raw ID of MachineId, please. +struct MachineMap { + id_to_num: HashMap, + num_to_id: HashMap, +} + +impl MachineMap { + fn new() -> Self { + Self { + id_to_num: HashMap::new(), + num_to_id: HashMap::new(), + } + } + + fn get_or_create_raw_id(&mut self, machine_id: MachineId) -> usize { + *self.id_to_num.entry(machine_id).or_insert_with(|| { + let raw_id = self.num_to_id.len(); + self.num_to_id.insert(raw_id, machine_id); + raw_id + }) + } + + fn get_machine_id(&self, raw_id: usize) -> Option<&MachineId> { + self.num_to_id.get(&raw_id) + } +} + +impl Machinist { + /// Spawn an actor that handles scheduling of Maybenot actions and forwards DAITA events to the framework. + pub fn spawn( + resource_dir: &Path, + daita: Session, + peer: PublicKey, + mtu: u16, + ) -> std::result::Result { + const MAX_PADDING_BYTES: f64 = 0.0; + const MAX_BLOCKING_BYTES: f64 = 0.0; + + static MAYBENOT_MACHINES: OnceCell> = OnceCell::new(); + + let machines = MAYBENOT_MACHINES.get_or_try_init(|| { + let path = resource_dir.join("maybenot_machines"); + log::debug!("Reading maybenot machines from {}", path.display()); + + let mut machines = vec![]; + let machines_str = fs::read_to_string(path).map_err(Error::EnumerateMachines)?; + for machine_str in machines_str.lines() { + let machine_str = machine_str.trim(); + if matches!(machine_str.chars().next(), None | Some('#')) { + continue; + } + log::debug!("Adding maybenot machine: {machine_str}"); + machines.push( + machine_str + .parse::() + .map_err(|_error| Error::InvalidMachine(machine_str.to_owned()))?, + ); + } + Ok(machines) + })?; + + let quit_event = + talpid_windows::sync::Event::new(true, false).map_err(Error::InitializeQuitEvent)?; + let handle = MachinistHandle::new(&quit_event).map_err(Error::InitializeHandle)?; + + let framework = maybenot::framework::Framework::new( + machines.clone(), + MAX_PADDING_BYTES, + MAX_BLOCKING_BYTES, + mtu, + std::time::Instant::now(), + ) + .map_err(|error| Error::InitializeMaybenot(error.to_string()))?; + + let daita = Arc::new(daita); + let tokio_handle = tokio::runtime::Handle::current(); + + std::thread::spawn(move || { + Self { + daita, + machine_ids: MachineMap::new(), + machine_tasks: HashMap::new(), + tokio_handle, + quit_event, + peer, + // TODO: We're assuming that constant packet size is always enabled here + override_size: Some(mtu), + } + .event_loop(framework); + }); + + Ok(handle) + } + + fn event_loop( + mut self, + mut framework: maybenot::framework::Framework>, + ) { + use windows_sys::Win32::Foundation::WAIT_OBJECT_0; + + loop { + if unsafe { WaitForSingleObject(self.quit_event.as_raw(), 0) } == WAIT_OBJECT_0 { + break; + } + + let events = match self.wait_for_events() { + Ok(events) => { + if events.is_empty() { + break; + } + events + } + Err(error) => { + log::error!("Error while waiting for DAITA events: {error}"); + break; + } + }; + + for action in framework.trigger_events(&events, std::time::Instant::now()) { + self.handle_action(action); + } + } + + log::debug!("Stopped DAITA event loop"); + } + + fn handle_action(&mut self, action: &maybenot::framework::Action) { + match *action { + maybenot::framework::Action::Cancel { machine } => { + let raw_id = self.machine_ids.get_or_create_raw_id(machine); + + // Drop all scheduled actions for a given machine + if let Some(task) = self.machine_tasks.get_mut(&raw_id) { + task.abort(); + } + } + maybenot::framework::Action::InjectPadding { + timeout, + size, + machine, + replace, + .. + } => { + let peer = self.peer.clone(); + + let raw_id = self.machine_ids.get_or_create_raw_id(machine); + self.machine_tasks.entry(raw_id).and_modify(|f| f.abort()); + + let action = Action { + peer: *peer.as_bytes(), + action_type: ActionType::InjectPadding, + user_context: raw_id, + payload: ActionPayload { + padding: PaddingAction { + byte_count: size, + replace: if replace { 1 } else { 0 }, + }, + }, + }; + + if timeout == Duration::ZERO { + if let Err(error) = self.daita.send_action(&action) { + log::error!("Failed to send DAITA action: {error}"); + } + } else { + // Schedule action on the tokio runtime + let daita = Arc::downgrade(&self.daita); + let task = self.tokio_handle.spawn(async move { + tokio::time::sleep(timeout).await; + + let Some(daita) = daita.upgrade() else { return }; + + if let Err(error) = daita.send_action(&action) { + log::error!("Failed to send DAITA action: {error}"); + } + }); + self.machine_tasks.insert(raw_id, task); + } + } + maybenot::framework::Action::BlockOutgoing { .. } => {} + } + } + + /// Take all events from the ring buffer while there are any left. + /// If there are no events available, wait for events to arrive. + /// Otherwise, break and return a non-zero number of events to be processed. + /// If the quit event was signaled, this returns an empty vector. + fn wait_for_events(&mut self) -> io::Result> { + use windows_sys::Win32::Foundation::WAIT_OBJECT_0; + + let wait_events = [ + self.quit_event.as_raw(), + self.daita.event_data_available_event() as isize, + ]; + + let mut event_buffer: [Event; EVENTS_CAPACITY] = unsafe { std::mem::zeroed() }; + + loop { + match self.daita.receive_events(&mut event_buffer) { + Ok(events) => { + let converted_events: Vec<_> = events + .iter() + .filter(|event| &event.peer == self.peer.as_bytes()) + .filter_map(|event| { + maybenot_event_from_event(event, &self.machine_ids, self.override_size) + }) + .collect(); + if !converted_events.is_empty() { + return Ok(converted_events); + } + // Try again if we only received events for irrelevant peers + } + Err(error) => { + if error.raw_os_error() == Some(ERROR_NO_MORE_ITEMS as i32) { + let wait_result = unsafe { + WaitForMultipleObjects( + u32::try_from(wait_events.len()).unwrap(), + wait_events.as_ptr(), + 0, + INFINITE, + ) + }; + + if wait_result == WAIT_OBJECT_0 { + // Quit event signaled + break Ok(vec![]); + } + if wait_result == WAIT_OBJECT_0 + 1 { + // Event object signaled -- try to receive more events + continue; + } + } + break Err(std::io::Error::last_os_error()); + } + } + } + } +} diff --git a/talpid-wireguard/src/wireguard_nt.rs b/talpid-wireguard/src/wireguard_nt/mod.rs similarity index 86% rename from talpid-wireguard/src/wireguard_nt.rs rename to talpid-wireguard/src/wireguard_nt/mod.rs index 10cc45b38418..6acd6dc71070 100644 --- a/talpid-wireguard/src/wireguard_nt.rs +++ b/talpid-wireguard/src/wireguard_nt/mod.rs @@ -9,14 +9,14 @@ use futures::SinkExt; use ipnetwork::IpNetwork; use once_cell::sync::{Lazy, OnceCell}; use std::{ - ffi::CStr, + ffi::{c_uchar, CStr}, fmt, future::Future, - io, mem, - mem::MaybeUninit, + io, + mem::{self, MaybeUninit}, net::{IpAddr, Ipv4Addr, Ipv6Addr}, os::windows::io::RawHandle, - path::Path, + path::{Path, PathBuf}, pin::Pin, ptr, sync::{Arc, Mutex}, @@ -38,6 +38,8 @@ use windows_sys::{ }, }; +mod daita; + static WG_NT_DLL: OnceCell = OnceCell::new(); static ADAPTER_TYPE: Lazy = Lazy::new(|| U16CString::from_str("Mullvad").unwrap()); static ADAPTER_ALIAS: Lazy = Lazy::new(|| U16CString::from_str("Mullvad").unwrap()); @@ -159,12 +161,23 @@ pub enum Error { /// Failed to parse data returned by the driver #[error("Failed to parse data returned by wireguard-nt")] InvalidConfigData, + + /// DAITA machinist failed + #[error("Failed to enable DAITA on tunnel device")] + EnableTunnelDaita(#[source] io::Error), + + /// DAITA machinist failed + #[error("Failed to initialize DAITA machinist")] + InitializeMachinist(#[source] daita::Error), } pub struct WgNtTunnel { + resource_dir: PathBuf, + config: Arc>, device: Option>, interface_name: String, setup_handle: tokio::task::JoinHandle<()>, + daita_handle: Option, _logger_handle: LoggerHandle, } @@ -305,6 +318,7 @@ bitflags! { const REPLACE_ALLOWED_IPS = 0b00100000; const REMOVE = 0b01000000; const UPDATE = 0b10000000; + const HAS_CONSTANT_PACKET_SIZE = 0b100000000; } } @@ -322,6 +336,7 @@ struct WgPeer { rx_bytes: u64, last_handshake: u64, allowed_ips_count: u32, + constant_packet_size: c_uchar, } #[derive(Clone, Copy)] @@ -446,7 +461,7 @@ impl WgNtTunnel { let device = Some(device2.clone()); let setup_future = setup_ip_listener( - device2, + device2.clone(), u32::from(config.mtu), config.tunnel.addresses.iter().any(|addr| addr.is_ipv6()), ); @@ -457,17 +472,50 @@ impl WgNtTunnel { }); Ok(WgNtTunnel { + resource_dir: resource_dir.to_owned(), + config: Arc::new(Mutex::new(config.clone())), device, interface_name, setup_handle, + daita_handle: None, _logger_handle: logger_handle, }) } fn stop_tunnel(&mut self) { self.setup_handle.abort(); + if let Some(daita_handle) = self.daita_handle.take() { + let _ = daita_handle.close(); + } let _ = self.device.take(); } + + fn spawn_machinist(&mut self) -> Result<()> { + if let Some(handle) = self.daita_handle.take() { + log::info!("Stopping previous DAITA machines"); + let _ = handle.close(); + } + + let Some(device) = self.device.clone() else { + log::debug!("Tunnel is stopped; not starting machines"); + return Ok(()); + }; + + let config = self.config.lock().unwrap(); + + log::info!("Initializing DAITA for wireguard device"); + let session = daita::Session::from_adapter(device).map_err(Error::EnableTunnelDaita)?; + self.daita_handle = Some( + daita::Machinist::spawn( + &self.resource_dir, + session, + config.entry_peer.public_key.clone(), + config.mtu, + ) + .map_err(Error::InitializeMachinist)?, + ); + Ok(()) + } } async fn setup_ip_listener(device: Arc, mtu: u32, has_ipv6: bool) -> Result<()> { @@ -622,6 +670,11 @@ struct WgNtDll { func_set_adapter_state: WireGuardSetStateFn, func_set_logger: WireGuardSetLoggerFn, func_set_adapter_logging: WireGuardSetAdapterLoggingFn, + + func_daita_activate: daita::bindings::WireGuardDaitaActivateFn, + func_daita_event_data_available_event: daita::bindings::WireGuardDaitaEventDataAvailableEventFn, + func_daita_receive_events: daita::bindings::WireGuardDaitaReceiveEventsFn, + func_daita_send_action: daita::bindings::WireGuardDaitaSendActionFn, } unsafe impl Send for WgNtDll {} @@ -694,6 +747,30 @@ impl WgNtDll { CStr::from_bytes_with_nul(b"WireGuardSetAdapterLogging\0").unwrap(), )?) as *const _ as *const _) }, + func_daita_activate: unsafe { + *((&get_proc_fn( + handle, + CStr::from_bytes_with_nul(b"WireGuardDaitaActivate\0").unwrap(), + )?) as *const _ as *const _) + }, + func_daita_event_data_available_event: unsafe { + *((&get_proc_fn( + handle, + CStr::from_bytes_with_nul(b"WireGuardDaitaEventDataAvailableEvent\0").unwrap(), + )?) as *const _ as *const _) + }, + func_daita_receive_events: unsafe { + *((&get_proc_fn( + handle, + CStr::from_bytes_with_nul(b"WireGuardDaitaReceiveEvents\0").unwrap(), + )?) as *const _ as *const _) + }, + func_daita_send_action: unsafe { + *((&get_proc_fn( + handle, + CStr::from_bytes_with_nul(b"WireGuardDaitaSendAction\0").unwrap(), + )?) as *const _ as *const _) + }, }) } @@ -790,6 +867,52 @@ impl WgNtDll { } Ok(()) } + + pub unsafe fn daita_activate( + &self, + adapter: RawHandle, + events_capacity: usize, + actions_capacity: usize, + ) -> io::Result<()> { + if (self.func_daita_activate)(adapter, events_capacity, actions_capacity) == 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + pub unsafe fn daita_event_data_available_event( + &self, + adapter: RawHandle, + ) -> io::Result { + let ready_event = (self.func_daita_event_data_available_event)(adapter); + if ready_event.is_null() { + return Err(io::Error::last_os_error()); + } + Ok(ready_event) + } + + pub unsafe fn daita_receive_events( + &self, + adapter: RawHandle, + events: *mut daita::Event, + ) -> io::Result { + let num_events = (self.func_daita_receive_events)(adapter, events); + if num_events == 0 { + return Err(io::Error::last_os_error()); + } + Ok(num_events) + } + + pub unsafe fn daita_send_action( + &self, + adapter: RawHandle, + action: *const daita::Action, + ) -> io::Result<()> { + if (self.func_daita_send_action)(adapter, action) == 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } } impl Drop for WgNtDll { @@ -816,11 +939,13 @@ fn serialize_config(config: &Config) -> Result>> { buffer.extend(as_uninit_byte_slice(&header)); for peer in config.peers() { - let flags = if peer.psk.is_some() { - WgPeerFlag::HAS_PRESHARED_KEY | WgPeerFlag::HAS_PUBLIC_KEY | WgPeerFlag::HAS_ENDPOINT - } else { - WgPeerFlag::HAS_PUBLIC_KEY | WgPeerFlag::HAS_ENDPOINT - }; + let mut flags = WgPeerFlag::HAS_PUBLIC_KEY + | WgPeerFlag::HAS_ENDPOINT + | WgPeerFlag::HAS_CONSTANT_PACKET_SIZE; + if peer.psk.is_some() { + flags |= WgPeerFlag::HAS_PRESHARED_KEY; + } + let constant_packet_size = if peer.constant_packet_size { 1 } else { 0 }; let wg_peer = WgPeer { flags, reserved: 0, @@ -836,6 +961,7 @@ fn serialize_config(config: &Config) -> Result>> { rx_bytes: 0, last_handshake: 0, allowed_ips_count: u32::try_from(peer.allowed_ips.len()).unwrap(), + constant_packet_size, }; buffer.extend(as_uninit_byte_slice(&wg_peer)); @@ -960,13 +1086,16 @@ impl Tunnel for WgNtTunnel { config: Config, ) -> Pin> + Send>> { let device = self.device.clone(); + let current_config = self.config.clone(); Box::pin(async move { let Some(device) = device else { log::error!("Failed to set config: No tunnel device"); return Err(super::TunnelError::SetConfigError); }; - device.set_config(&config).map_err(|error| { + let mut current_config = current_config.lock().unwrap(); + *current_config = config; + device.set_config(¤t_config).map_err(|error| { log::error!( "{}", error.display_chain_with_msg("Failed to set wg-nt tunnel config") @@ -975,6 +1104,16 @@ impl Tunnel for WgNtTunnel { }) }) } + + fn start_daita(&mut self) -> std::result::Result<(), crate::TunnelError> { + self.spawn_machinist().map_err(|error| { + log::error!( + "{}", + error.display_chain_with_msg("Failed to start DAITA for wg-nt tunnel") + ); + super::TunnelError::SetConfigError + }) + } } pub fn as_uninit_byte_slice(value: &T) -> &[mem::MaybeUninit] { @@ -984,7 +1123,6 @@ pub fn as_uninit_byte_slice(value: &T) -> &[mem::MaybeUninit = Lazy::new(|| Interface { @@ -1026,7 +1168,9 @@ mod tests { peers_count: 1, }, p0: WgPeer { - flags: WgPeerFlag::HAS_PUBLIC_KEY | WgPeerFlag::HAS_ENDPOINT, + flags: WgPeerFlag::HAS_PUBLIC_KEY + | WgPeerFlag::HAS_ENDPOINT + | WgPeerFlag::HAS_CONSTANT_PACKET_SIZE, reserved: 0, public_key: *WG_PUBLIC_KEY.as_bytes(), preshared_key: [0; WIREGUARD_KEY_LENGTH], @@ -1039,6 +1183,8 @@ mod tests { rx_bytes: 0, last_handshake: 0, allowed_ips_count: 1, + #[cfg(target_os = "windows")] + constant_packet_size: 0, }, p0_allowed_ip_0: WgAllowedIp { address: WgIpAddr::from("1.3.3.0".parse::().unwrap()), From f099a103565f04c18222a8d86025db4e4b2d777d Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 5 Dec 2023 19:01:26 +0100 Subject: [PATCH 072/214] Add daita toggle --- gui/src/main/daemon-rpc.ts | 11 +++ gui/src/main/settings.ts | 3 + gui/src/renderer/app.tsx | 4 + gui/src/renderer/components/BetaLabel.tsx | 26 ------ .../renderer/components/WireguardSettings.tsx | 82 ++++++++++++++++--- gui/src/renderer/components/YellowLabel.tsx | 18 ++++ gui/src/renderer/components/cell/Label.tsx | 2 + gui/src/renderer/redux/settings/actions.ts | 15 ++++ gui/src/renderer/redux/settings/reducers.ts | 10 +++ gui/src/shared/daemon-rpc-types.ts | 6 ++ gui/src/shared/ipc-schema.ts | 2 + gui/test/e2e/mocked/tunnel-state.spec.ts | 1 + 12 files changed, 143 insertions(+), 37 deletions(-) delete mode 100644 gui/src/renderer/components/BetaLabel.tsx create mode 100644 gui/src/renderer/components/YellowLabel.tsx diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 531ec80ced55..91c63d268c2e 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -37,6 +37,7 @@ import { IAppVersionInfo, IBridgeConstraints, ICustomList, + IDaitaSettings, IDevice, IDeviceRemoval, IDnsOptions, @@ -561,6 +562,15 @@ export class DaemonRpc { await this.callEmpty(this.client.updateDevice); } + public async setDaitaSettings(daitaSettings: IDaitaSettings): Promise { + const grpcDaitaSettings = new grpcTypes.DaitaSettings(); + grpcDaitaSettings.setEnabled(daitaSettings.enabled); + await this.call( + this.client.setDaitaSettings, + grpcDaitaSettings, + ); + } + public async listDevices(accountToken: AccountToken): Promise> { try { const response = await this.callString( @@ -1333,6 +1343,7 @@ function convertFromTunnelOptions(tunnelOptions: grpcTypes.TunnelOptions.AsObjec quantumResistant: convertFromQuantumResistantState( tunnelOptions.wireguard?.quantumResistant?.state, ), + daita: tunnelOptions.wireguard!.daita, }, generic: { enableIpv6: tunnelOptions.generic!.enableIpv6, diff --git a/gui/src/main/settings.ts b/gui/src/main/settings.ts index 8bf60d9108e2..22238c72c459 100644 --- a/gui/src/main/settings.ts +++ b/gui/src/main/settings.ts @@ -107,6 +107,9 @@ export default class Settings implements Readonly { const settings = await fs.readFile(path); return this.daemonRpc.applyJsonSettings(settings.toString()); }); + IpcMainEventChannel.settings.handleSetDaitaSettings((daitaSettings) => { + return this.daemonRpc.setDaitaSettings(daitaSettings); + }); IpcMainEventChannel.guiSettings.handleSetEnableSystemNotifications((flag: boolean) => { this.guiSettings.enableSystemNotifications = flag; diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index a8c1901ae85a..92c4cb56989f 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -16,6 +16,7 @@ import { IAccountData, IAppVersionInfo, ICustomList, + IDaitaSettings, IDevice, IDeviceRemoval, IDnsOptions, @@ -340,6 +341,8 @@ export default class AppRenderer { IpcRendererEventChannel.windowsSplitTunneling.forgetManuallyAddedApplication(application); public setObfuscationSettings = (obfuscationSettings: ObfuscationSettings) => IpcRendererEventChannel.settings.setObfuscationSettings(obfuscationSettings); + public setDaitaSettings = (daitaSettings: IDaitaSettings) => + IpcRendererEventChannel.settings.setDaitaSettings(daitaSettings); public collectProblemReport = (toRedact: string | undefined) => IpcRendererEventChannel.problemReport.collectLogs(toRedact); public viewLog = (path: string) => IpcRendererEventChannel.problemReport.viewLog(path); @@ -812,6 +815,7 @@ export default class AppRenderer { reduxSettings.updateWireguardQuantumResistant( newSettings.tunnelOptions.wireguard.quantumResistant, ); + reduxSettings.updateWireguardDaita(newSettings.tunnelOptions.wireguard.daita); reduxSettings.updateBridgeState(newSettings.bridgeState); reduxSettings.updateDnsOptions(newSettings.tunnelOptions.dns); reduxSettings.updateSplitTunnelingState(newSettings.splitTunnel.enableExclusions); diff --git a/gui/src/renderer/components/BetaLabel.tsx b/gui/src/renderer/components/BetaLabel.tsx deleted file mode 100644 index ca19a9ef4b7c..000000000000 --- a/gui/src/renderer/components/BetaLabel.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import styled from 'styled-components'; - -import { colors } from '../../config.json'; -import { messages } from '../../shared/gettext'; - -const StyledBetaLabel = styled.span({ - display: 'inline-block', - fontFamily: 'Open Sans', - color: colors.blue, - fontSize: '13px', - fontWeight: 800, - lineHeight: '20px', - padding: '2px 0', - background: colors.yellow, - borderRadius: '5px', - width: '50px', - textAlign: 'center', -}); - -interface IBetaLabelProps { - className?: string; -} - -export default function BetaLabel(props: IBetaLabelProps) { - return {messages.gettext('BETA')}; -} diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index 7cd93b98bd2e..892df0afbc3a 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -22,7 +22,7 @@ import * as AppButton from './AppButton'; import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup'; import * as Cell from './cell'; import Selector, { SelectorItem, SelectorWithCustomItem } from './cell/Selector'; -import { InfoIcon } from './InfoButton'; +import InfoButton from './InfoButton'; import { BackAction } from './KeyboardNavigation'; import { Layout, SettingsContainer } from './Layout'; import { ModalAlert, ModalAlertType, ModalMessage } from './Modal'; @@ -34,6 +34,7 @@ import { TitleBarItem, } from './NavigationBar'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; +import YellowLabel from './YellowLabel'; const MIN_WIREGUARD_MTU_VALUE = 1280; const MAX_WIREGUARD_MTU_VALUE = 1420; @@ -44,22 +45,14 @@ function mapPortToSelectorItem(value: number): SelectorItem { return { label: value.toString(), value }; } -export const StyledContent = styled.div({ +const StyledContent = styled.div({ display: 'flex', flexDirection: 'column', flex: 1, marginBottom: '2px', }); -export const StyledCellIcon = styled(Cell.UntintedIcon)({ - marginRight: '8px', -}); - -export const StyledInfoIcon = styled(InfoIcon)({ - marginRight: '16px', -}); - -export const StyledSelectorContainer = styled.div({ +const StyledSelectorContainer = styled.div({ flex: 0, }); @@ -107,6 +100,10 @@ export default function WireguardSettings() { + + + + @@ -558,6 +555,69 @@ function MtuSetting() { ); } +function DaitaSettings() { + const { setDaitaSettings } = useAppContext(); + const daita = useSelector((state) => state.settings.wireguard.daita); + + const [confirmationDialogVisible, showConfirmationDialog, hideConfirmationDialog] = useBoolean(); + + const setDaita = useCallback((value: boolean) => { + if (value) { + showConfirmationDialog(); + } else { + void setDaitaSettings({ enabled: value }); + } + }, []); + + const confirmDaita = useCallback(() => { + void setDaitaSettings({ enabled: true }); + hideConfirmationDialog(); + }, []); + + return ( + <> + + + + + DAITA + {messages.gettext('BETA')} + + + + + + + + + + {messages.gettext('Enable anyway')} + , + + {messages.gettext('Back')} + , + ]} + close={hideConfirmationDialog} + /> + + ); +} + function QuantumResistantSetting() { const { setWireguardQuantumResistant } = useAppContext(); const quantumResistant = useSelector((state) => state.settings.wireguard.quantumResistant); diff --git a/gui/src/renderer/components/YellowLabel.tsx b/gui/src/renderer/components/YellowLabel.tsx new file mode 100644 index 000000000000..55059e223f31 --- /dev/null +++ b/gui/src/renderer/components/YellowLabel.tsx @@ -0,0 +1,18 @@ +import styled from 'styled-components'; + +import { colors } from '../../config.json'; + +export default styled.span({ + display: 'inline-block', + fontFamily: 'Open Sans', + color: colors.blue, + fontSize: '12px', + fontWeight: 800, + lineHeight: '20px', + padding: '1px 8px', + marginLeft: '8px', + background: colors.yellow, + borderRadius: '5px', + textAlign: 'center', + verticalAlign: 'middle', +}); diff --git a/gui/src/renderer/components/cell/Label.tsx b/gui/src/renderer/components/cell/Label.tsx index 729b7dc31d74..f74e57b39c19 100644 --- a/gui/src/renderer/components/cell/Label.tsx +++ b/gui/src/renderer/components/cell/Label.tsx @@ -8,6 +8,8 @@ import { CellButton } from './CellButton'; import { CellDisabledContext } from './Container'; const StyledLabel = styled.div<{ disabled: boolean }>(buttonText, (props) => ({ + display: 'flex', + alignItems: 'center', margin: '10px 0', flex: 1, color: props.disabled ? colors.white40 : colors.white, diff --git a/gui/src/renderer/redux/settings/actions.ts b/gui/src/renderer/redux/settings/actions.ts index aa4e460e6fa0..ba6e4ce5a8e0 100644 --- a/gui/src/renderer/redux/settings/actions.ts +++ b/gui/src/renderer/redux/settings/actions.ts @@ -4,6 +4,7 @@ import { ApiAccessMethodSettings, BridgeState, CustomLists, + IDaitaSettings, IDnsOptions, IWireguardEndpointData, ObfuscationSettings, @@ -77,6 +78,11 @@ export interface IUpdateWireguardQuantumResistantAction { quantumResistant?: boolean; } +export interface IUpdateWireguardDaitaAction { + type: 'UPDATE_WIREGUARD_DAITA'; + daita?: IDaitaSettings; +} + export interface IUpdateAutoStartAction { type: 'UPDATE_AUTO_START'; autoStart: boolean; @@ -136,6 +142,7 @@ export type SettingsAction = | IUpdateOpenVpnMssfixAction | IUpdateWireguardMtuAction | IUpdateWireguardQuantumResistantAction + | IUpdateWireguardDaitaAction | IUpdateAutoStartAction | IUpdateDnsOptionsAction | IUpdateSplitTunnelingStateAction @@ -245,6 +252,13 @@ function updateWireguardQuantumResistant( }; } +function updateWireguardDaita(daita?: IDaitaSettings): IUpdateWireguardDaitaAction { + return { + type: 'UPDATE_WIREGUARD_DAITA', + daita, + }; +} + function updateAutoStart(autoStart: boolean): IUpdateAutoStartAction { return { type: 'UPDATE_AUTO_START', @@ -326,6 +340,7 @@ export default { updateOpenVpnMssfix, updateWireguardMtu, updateWireguardQuantumResistant, + updateWireguardDaita, updateAutoStart, updateDnsOptions, updateSplitTunnelingState, diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts index 3c502ec103cb..2f8020ebf7d5 100644 --- a/gui/src/renderer/redux/settings/reducers.ts +++ b/gui/src/renderer/redux/settings/reducers.ts @@ -7,6 +7,7 @@ import { BridgeType, CustomLists, CustomProxy, + IDaitaSettings, IDnsOptions, IpVersion, IWireguardEndpointData, @@ -109,6 +110,7 @@ export interface ISettingsReduxState { wireguard: { mtu?: number; quantumResistant?: boolean; + daita?: IDaitaSettings; }; dns: IDnsOptions; splitTunneling: boolean; @@ -271,6 +273,14 @@ export default function ( quantumResistant: action.quantumResistant, }, }; + case 'UPDATE_WIREGUARD_DAITA': + return { + ...state, + wireguard: { + ...state.wireguard, + daita: action.daita, + }, + }; case 'UPDATE_AUTO_START': return { diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index 738eef5e9561..fa4f53090bad 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -143,6 +143,7 @@ export interface ITunnelEndpoint { proxy?: IProxyEndpoint; obfuscationEndpoint?: IObfuscationEndpoint; entryEndpoint?: IEndpoint; + daita: boolean; } export interface IEndpoint { @@ -320,6 +321,7 @@ export interface ITunnelOptions { wireguard: { mtu?: number; quantumResistant?: boolean; + daita?: IDaitaSettings; }; generic: { enableIpv6: boolean; @@ -503,6 +505,10 @@ export interface RelayOverride { ipv6AddrIn?: string; } +export interface IDaitaSettings { + enabled: boolean; +} + export function parseSocketAddress(socketAddrStr: string): ISocketAddress { const re = new RegExp(/(.+):(\d+)$/); const matches = socketAddrStr.match(re); diff --git a/gui/src/shared/ipc-schema.ts b/gui/src/shared/ipc-schema.ts index d90957764e00..561ec924a094 100644 --- a/gui/src/shared/ipc-schema.ts +++ b/gui/src/shared/ipc-schema.ts @@ -14,6 +14,7 @@ import { IAccountData, IAppVersionInfo, ICustomList, + IDaitaSettings, IDevice, IDeviceRemoval, IDnsOptions, @@ -192,6 +193,7 @@ export const ipcSchema = { testApiAccessMethodById: invoke(), testCustomApiAccessMethod: invoke(), clearAllRelayOverrides: invoke(), + setDaitaSettings: invoke(), }, guiSettings: { '': notifyRenderer(), diff --git a/gui/test/e2e/mocked/tunnel-state.spec.ts b/gui/test/e2e/mocked/tunnel-state.spec.ts index db919e09d31a..b4de4058410b 100644 --- a/gui/test/e2e/mocked/tunnel-state.spec.ts +++ b/gui/test/e2e/mocked/tunnel-state.spec.ts @@ -69,6 +69,7 @@ test('App should show connected tunnel state', async () => { protocol: 'tcp', quantumResistant: false, tunnelType: 'wireguard', + daita: false, }; await util.sendMockIpcResponse({ channel: 'tunnel-', From ee841c08bc6fa76e6147296ac43266aff963f5b8 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 5 Dec 2023 19:10:21 +0100 Subject: [PATCH 073/214] Add daita filtering of relay list --- .../select-location/RelayListContext.tsx | 14 ++++++++------ gui/src/renderer/lib/filter-locations.ts | 9 +++++++++ gui/src/renderer/redux/settings/reducers.ts | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/gui/src/renderer/components/select-location/RelayListContext.tsx b/gui/src/renderer/components/select-location/RelayListContext.tsx index 86dd7e54676a..2341932f2df3 100644 --- a/gui/src/renderer/components/select-location/RelayListContext.tsx +++ b/gui/src/renderer/components/select-location/RelayListContext.tsx @@ -4,6 +4,7 @@ import { compareRelayLocation, RelayLocation } from '../../../shared/daemon-rpc- import { EndpointType, filterLocations, + filterLocationsByDaita, filterLocationsByEndPointType, getLocationsExpandedBySearch, searchForLocations, @@ -60,6 +61,7 @@ interface RelayListContextProviderProps { export function RelayListContextProvider(props: RelayListContextProviderProps) { const { locationType, searchTerm } = useSelectLocationContext(); + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); const fullRelayList = useSelector((state) => state.settings.relayLocations); const relaySettings = useNormalRelaySettings(); @@ -71,15 +73,15 @@ export function RelayListContextProvider(props: RelayListContextProviderProps) { return filterLocationsByEndPointType(fullRelayList, endpointType, relaySettings); }, [fullRelayList, locationType, relaySettings?.tunnelProtocol]); + const relayListForDaita = useMemo(() => { + return filterLocationsByDaita(relayListForEndpointType, daita); + }, [daita, relayListForEndpointType]); + // Filters the relays to only keep the relays matching the currently selected filters, e.g. // ownership and providers const relayListForFilters = useMemo(() => { - return filterLocations( - relayListForEndpointType, - relaySettings?.ownership, - relaySettings?.providers, - ); - }, [relaySettings?.ownership, relaySettings?.providers, relayListForEndpointType]); + return filterLocations(relayListForDaita, relaySettings?.ownership, relaySettings?.providers); + }, [relaySettings?.ownership, relaySettings?.providers, relayListForDaita]); // Filters the relays based on the provided search term const relayListForSearch = useMemo(() => { diff --git a/gui/src/renderer/lib/filter-locations.ts b/gui/src/renderer/lib/filter-locations.ts index 63ca56cbf253..802748680a2f 100644 --- a/gui/src/renderer/lib/filter-locations.ts +++ b/gui/src/renderer/lib/filter-locations.ts @@ -27,6 +27,15 @@ export function filterLocationsByEndPointType( return filterLocationsImpl(locations, getTunnelProtocolFilter(endpointType, relaySettings)); } +export function filterLocationsByDaita( + locations: IRelayLocationCountryRedux[], + daita: boolean, +): IRelayLocationCountryRedux[] { + return daita + ? filterLocationsImpl(locations, (relay: IRelayLocationRelayRedux) => relay.daita) + : locations; +} + export function filterLocations( locations: IRelayLocationCountryRedux[], ownership?: Ownership, diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts index 2f8020ebf7d5..18873eea2002 100644 --- a/gui/src/renderer/redux/settings/reducers.ts +++ b/gui/src/renderer/redux/settings/reducers.ts @@ -76,6 +76,7 @@ export interface IRelayLocationRelayRedux { owned: boolean; weight: number; endpointType: RelayEndpointType; + daita: boolean; } export interface IRelayLocationCityRedux { From 52a2bed28a4360d9c0f98fed2898e713dc85371c Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 5 Dec 2023 19:17:11 +0100 Subject: [PATCH 074/214] Add daita label to filters in location selector --- gui/src/main/daemon-rpc.ts | 37 ++++++++++++------- .../renderer/components/WireguardSettings.tsx | 26 +++++++++---- .../select-location/SelectLocation.tsx | 12 +++++- gui/src/shared/daemon-rpc-types.ts | 1 + gui/test/e2e/setup/main.ts | 1 + 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 91c63d268c2e..ba913fea8762 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -857,9 +857,7 @@ function convertFromRelayList(relayList: grpcTypes.RelayList): IRelayListWithEnd relayList: { countries: relayList .getCountriesList() - .map((country: grpcTypes.RelayListCountry) => - convertFromRelayListCountry(country.toObject()), - ), + .map((country: grpcTypes.RelayListCountry) => convertFromRelayListCountry(country)), }, wireguardEndpointData: convertWireguardEndpointData(relayList.getWireguard()!), }; @@ -874,26 +872,37 @@ function convertWireguardEndpointData( }; } -function convertFromRelayListCountry( - country: grpcTypes.RelayListCountry.AsObject, -): IRelayListCountry { +function convertFromRelayListCountry(country: grpcTypes.RelayListCountry): IRelayListCountry { + const countryObject = country.toObject(); return { - ...country, - cities: country.citiesList.map(convertFromRelayListCity), + ...countryObject, + cities: country.getCitiesList().map(convertFromRelayListCity), }; } -function convertFromRelayListCity(city: grpcTypes.RelayListCity.AsObject): IRelayListCity { +function convertFromRelayListCity(city: grpcTypes.RelayListCity): IRelayListCity { + const cityObject = city.toObject(); return { - ...city, - relays: city.relaysList.map(convertFromRelayListRelay), + ...cityObject, + relays: city.getRelaysList().map(convertFromRelayListRelay), }; } -function convertFromRelayListRelay(relay: grpcTypes.Relay.AsObject): IRelayListHostname { +function convertFromRelayListRelay(relay: grpcTypes.Relay): IRelayListHostname { + const relayObject = relay.toObject(); + + let daita = false; + if (relayObject.endpointType === grpcTypes.Relay.RelayType.WIREGUARD) { + const endpointDataU8 = relay.getEndpointData()?.getValue_asU8(); + if (endpointDataU8) { + daita = grpcTypes.WireguardRelayEndpointData.deserializeBinary(endpointDataU8).getDaita(); + } + } + return { - ...relay, - endpointType: convertFromRelayType(relay.endpointType), + ...relayObject, + endpointType: convertFromRelayType(relayObject.endpointType), + daita, }; } diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index 892df0afbc3a..8e59e2c30c8f 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -557,7 +557,7 @@ function MtuSetting() { function DaitaSettings() { const { setDaitaSettings } = useAppContext(); - const daita = useSelector((state) => state.settings.wireguard.daita); + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); const [confirmationDialogVisible, showConfirmationDialog, hideConfirmationDialog] = useBoolean(); @@ -584,13 +584,25 @@ function DaitaSettings() { {messages.gettext('BETA')} - + + + { + // TODO: These texts need to be polished + messages.pgettext( + 'wireguard-settings-view', + 'Enabling DAITA (Defence against AI Traffic Analysis) hides patterns in the VPN tunnel by generating dummy traffic and using a fixed packet size.', + ) + } + + + {messages.pgettext( + 'wireguard-settings-view', + 'DAITA may cause significant overhead. We do not recommend enabling DAITA on a metered internet connection.', + )} + + - + diff --git a/gui/src/renderer/components/select-location/SelectLocation.tsx b/gui/src/renderer/components/select-location/SelectLocation.tsx index 5ba02039395f..e7bd4865cdee 100644 --- a/gui/src/renderer/components/select-location/SelectLocation.tsx +++ b/gui/src/renderer/components/select-location/SelectLocation.tsx @@ -71,6 +71,7 @@ export default function SelectLocation() { const ownership = relaySettings?.ownership ?? Ownership.any; const providers = relaySettings?.providers ?? []; const filteredProviders = useFilteredProviders(providers, ownership); + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); const [searchValue, setSearchValue] = useState(''); @@ -122,7 +123,7 @@ export default function SelectLocation() { const showOwnershipFilter = ownership !== Ownership.any; const showProvidersFilter = providers.length > 0; - const showFilters = showOwnershipFilter || showProvidersFilter; + const showFilters = showOwnershipFilter || showProvidersFilter || daita; return ( @@ -220,6 +221,15 @@ export default function SelectLocation() { )} + + {daita && ( + + {sprintf( + messages.pgettext('select-location-view', 'Setting: %(settingName)s'), + { settingName: 'DAITA' }, + )} + + )} )} diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index fa4f53090bad..761dd01a4cac 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -310,6 +310,7 @@ export interface IRelayListHostname { weight: number; owned: boolean; endpointType: RelayEndpointType; + daita: boolean; } export type RelayEndpointType = 'wireguard' | 'openvpn' | 'bridge'; diff --git a/gui/test/e2e/setup/main.ts b/gui/test/e2e/setup/main.ts index c0114494d9be..78da7f59b5a7 100644 --- a/gui/test/e2e/setup/main.ts +++ b/gui/test/e2e/setup/main.ts @@ -94,6 +94,7 @@ class ApplicationMain { weight: 0, owned: true, endpointType: 'wireguard', + daita: false, }, ], }, From 424a519d6eb10be91971054733c8738715eb0906 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 14 Dec 2023 08:42:23 +0100 Subject: [PATCH 075/214] Only filter locations for first wg hop --- .../select-location/RelayListContext.tsx | 8 +++++- .../select-location/SelectLocation.tsx | 12 ++++++--- gui/src/renderer/lib/filter-locations.ts | 26 +++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/gui/src/renderer/components/select-location/RelayListContext.tsx b/gui/src/renderer/components/select-location/RelayListContext.tsx index 2341932f2df3..13e2dc28e8d1 100644 --- a/gui/src/renderer/components/select-location/RelayListContext.tsx +++ b/gui/src/renderer/components/select-location/RelayListContext.tsx @@ -74,7 +74,13 @@ export function RelayListContextProvider(props: RelayListContextProviderProps) { }, [fullRelayList, locationType, relaySettings?.tunnelProtocol]); const relayListForDaita = useMemo(() => { - return filterLocationsByDaita(relayListForEndpointType, daita); + return filterLocationsByDaita( + relayListForEndpointType, + daita, + locationType, + relaySettings?.tunnelProtocol ?? 'any', + relaySettings?.wireguard.useMultihop ?? false, + ); }, [daita, relayListForEndpointType]); // Filters the relays to only keep the relays matching the currently selected filters, e.g. diff --git a/gui/src/renderer/components/select-location/SelectLocation.tsx b/gui/src/renderer/components/select-location/SelectLocation.tsx index e7bd4865cdee..3f3535225ed2 100644 --- a/gui/src/renderer/components/select-location/SelectLocation.tsx +++ b/gui/src/renderer/components/select-location/SelectLocation.tsx @@ -5,7 +5,7 @@ import { colors } from '../../../config.json'; import { Ownership } from '../../../shared/daemon-rpc-types'; import { messages } from '../../../shared/gettext'; import { useRelaySettingsUpdater } from '../../lib/constraint-updater'; -import { filterSpecialLocations } from '../../lib/filter-locations'; +import { daitaFilterActive, filterSpecialLocations } from '../../lib/filter-locations'; import { useHistory } from '../../lib/history'; import { formatHtml } from '../../lib/html-formatter'; import { RoutePath } from '../../lib/routes'; @@ -72,6 +72,12 @@ export default function SelectLocation() { const providers = relaySettings?.providers ?? []; const filteredProviders = useFilteredProviders(providers, ownership); const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); + const showDaitaFilter = daitaFilterActive( + daita, + locationType, + relaySettings?.tunnelProtocol ?? 'any', + relaySettings?.wireguard.useMultihop ?? false, + ); const [searchValue, setSearchValue] = useState(''); @@ -123,7 +129,7 @@ export default function SelectLocation() { const showOwnershipFilter = ownership !== Ownership.any; const showProvidersFilter = providers.length > 0; - const showFilters = showOwnershipFilter || showProvidersFilter || daita; + const showFilters = showOwnershipFilter || showProvidersFilter || showDaitaFilter; return ( @@ -222,7 +228,7 @@ export default function SelectLocation() { )} - {daita && ( + {showDaitaFilter && ( {sprintf( messages.pgettext('select-location-view', 'Setting: %(settingName)s'), diff --git a/gui/src/renderer/lib/filter-locations.ts b/gui/src/renderer/lib/filter-locations.ts index 802748680a2f..78126f1fb417 100644 --- a/gui/src/renderer/lib/filter-locations.ts +++ b/gui/src/renderer/lib/filter-locations.ts @@ -1,6 +1,13 @@ -import { Ownership, RelayEndpointType, RelayLocation } from '../../shared/daemon-rpc-types'; +import { + LiftedConstraint, + Ownership, + RelayEndpointType, + RelayLocation, + TunnelProtocol, +} from '../../shared/daemon-rpc-types'; import { relayLocations } from '../../shared/gettext'; import { + LocationType, RelayLocationCityWithVisibility, RelayLocationCountryWithVisibility, RelayLocationRelayWithVisibility, @@ -30,12 +37,27 @@ export function filterLocationsByEndPointType( export function filterLocationsByDaita( locations: IRelayLocationCountryRedux[], daita: boolean, + locationType: LocationType, + tunnelProtocol: LiftedConstraint, + multihop: boolean, ): IRelayLocationCountryRedux[] { - return daita + return daitaFilterActive(daita, locationType, tunnelProtocol, multihop) ? filterLocationsImpl(locations, (relay: IRelayLocationRelayRedux) => relay.daita) : locations; } +export function daitaFilterActive( + daita: boolean, + locationType: LocationType, + tunnelProtocol: LiftedConstraint, + multihop: boolean, +) { + const isEntry = multihop + ? locationType === LocationType.entry + : locationType === LocationType.exit; + return daita && isEntry && tunnelProtocol !== 'openvpn'; +} + export function filterLocations( locations: IRelayLocationCountryRedux[], ownership?: Ownership, From 5cfc8e5da288091a1219bfae0e54029af58e3def Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 4 Jan 2024 19:16:24 +0100 Subject: [PATCH 076/214] Update translations --- gui/locales/messages.pot | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index 1873be203877..ac6d1886006a 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -1368,6 +1368,10 @@ msgctxt "select-location-view" msgid "Relay" msgstr "" +msgctxt "select-location-view" +msgid "Setting: %(settingName)s" +msgstr "" + msgctxt "select-location-view" msgid "The app selects a random bridge server, but servers have a higher probability the closer they are to you." msgstr "" @@ -1986,6 +1990,14 @@ msgctxt "wireguard-settings-view" msgid "%(wireguard)s settings" msgstr "" +msgctxt "wireguard-settings-view" +msgid "DAITA may cause significant overhead. We do not recommend enabling DAITA on a metered internet connection." +msgstr "" + +msgctxt "wireguard-settings-view" +msgid "Enabling DAITA (Defence against AI Traffic Analysis) hides patterns in the VPN tunnel by generating dummy traffic and using a fixed packet size." +msgstr "" + msgctxt "wireguard-settings-view" msgid "IP version" msgstr "" @@ -2045,6 +2057,11 @@ msgctxt "wireguard-settings-view" msgid "This allows access to %(wireguard)s for devices that only support IPv6." msgstr "" +#. Warning text in a dialog that is displayed after a setting is toggled. +msgctxt "wireguard-settings-view" +msgid "This feature isn't available on all servers. You might need to change location after enabling." +msgstr "" + msgctxt "wireguard-settings-view" msgid "This feature makes the WireGuard tunnel resistant to potential attacks from quantum computers." msgstr "" From d1cdd30982f7164928e8ba90b621b7b8273b0893 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 4 Jan 2024 19:16:24 +0100 Subject: [PATCH 077/214] Update info dialog text --- gui/locales/messages.pot | 13 ++++++++++++ gui/src/config.json | 4 +++- .../renderer/components/WireguardSettings.tsx | 21 +++++++++++-------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index ac6d1886006a..8a962b39f238 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -1984,6 +1984,10 @@ msgctxt "wireguard-settings-nav" msgid "%(wireguard)s settings" msgstr "" +msgctxt "wireguard-settings-view" +msgid "%(daita)s (%(daitaFull)s) hides patterns in the encrypted VPN traffic. It does this by padding all packets to the same size, and by inserting extra noise traffic into the stream. This makes it significantly harder for an observer on the network to identify what services you are talking to." +msgstr "" + #. Available placeholders: #. %(wireguard)s - Will be replaced with the string "WireGuard" msgctxt "wireguard-settings-view" @@ -2062,6 +2066,15 @@ msgctxt "wireguard-settings-view" msgid "This feature isn't available on all servers. You might need to change location after enabling." msgstr "" +msgctxt "wireguard-settings-view" +msgid "This feature cause significant performance and network overhead. We do not recommend enabling it unless you know you need it. We also do not recommend enabling %(daita)s on a metered internet connection." +msgstr "" + +#. Warning text in a dialog that is displayed after a setting is toggled. +msgctxt "wireguard-settings-view" +msgid "This feature isn't available on all servers. You might need to change location after enabling." +msgstr "" + msgctxt "wireguard-settings-view" msgid "This feature makes the WireGuard tunnel resistant to potential attacks from quantum computers." msgstr "" diff --git a/gui/src/config.json b/gui/src/config.json index c1ddcd6cac17..172370aba4f1 100644 --- a/gui/src/config.json +++ b/gui/src/config.json @@ -41,6 +41,8 @@ "strings": { "wireguard": "WireGuard", "openvpn": "OpenVPN", - "splitTunneling": "Split tunneling" + "splitTunneling": "Split tunneling", + "daita": "DAITA", + "daitaFull": "Defence Against AI-guided Traffic Analysis" } } diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index 8e59e2c30c8f..7ad202bf40c8 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -580,24 +580,27 @@ function DaitaSettings() { - DAITA + {strings.daita} {messages.gettext('BETA')} - { - // TODO: These texts need to be polished + {sprintf( messages.pgettext( 'wireguard-settings-view', - 'Enabling DAITA (Defence against AI Traffic Analysis) hides patterns in the VPN tunnel by generating dummy traffic and using a fixed packet size.', - ) - } + '%(daita)s (%(daitaFull)s) hides patterns in the encrypted VPN traffic. It does this by padding all packets to the same size, and by inserting extra noise traffic into the stream. This makes it significantly harder for an observer on the network to identify what services you are talking to.', + ), + { daita: strings.daita, daitaFull: strings.daitaFull }, + )} - {messages.pgettext( - 'wireguard-settings-view', - 'DAITA may cause significant overhead. We do not recommend enabling DAITA on a metered internet connection.', + {sprintf( + messages.pgettext( + 'wireguard-settings-view', + 'This feature cause significant performance and network overhead. We do not recommend enabling it unless you know you need it. We also do not recommend enabling %(daita)s on a metered internet connection.', + ), + { daita: strings.daita }, )} From b3d948bb2e5851ca0b181b1e2249506b3150e1a7 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Wed, 17 Jan 2024 11:41:38 +0100 Subject: [PATCH 078/214] Hide the DAITA setting on non-windows --- gui/src/renderer/components/WireguardSettings.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index 7ad202bf40c8..345231040e4d 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -100,9 +100,11 @@ export default function WireguardSettings() { - - - + {window.env.platform === 'win32' && ( + + + + )} From bc9bf8d7b2a544c4f9ee325799427bdbf609ae54 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 11 Apr 2024 18:51:21 +0200 Subject: [PATCH 079/214] Fix DAITA help texts --- .../renderer/components/WireguardSettings.tsx | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index 345231040e4d..d315485659ad 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -591,7 +591,7 @@ function DaitaSettings() { {sprintf( messages.pgettext( 'wireguard-settings-view', - '%(daita)s (%(daitaFull)s) hides patterns in the encrypted VPN traffic. It does this by padding all packets to the same size, and by inserting extra noise traffic into the stream. This makes it significantly harder for an observer on the network to identify what services you are talking to.', + '%(daita)s (%(daitaFull)s) hides patterns in your encrypted VPN traffic. If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting. It does this by carefully adding network noise and making all network packets the same size.', ), { daita: strings.daita, daitaFull: strings.daitaFull }, )} @@ -600,7 +600,7 @@ function DaitaSettings() { {sprintf( messages.pgettext( 'wireguard-settings-view', - 'This feature cause significant performance and network overhead. We do not recommend enabling it unless you know you need it. We also do not recommend enabling %(daita)s on a metered internet connection.', + 'Attention: Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed. Please consider this if you want to enable %(daita)s.', ), { daita: strings.daita }, )} @@ -614,13 +614,6 @@ function DaitaSettings() { {messages.gettext('Enable anyway')} @@ -629,8 +622,26 @@ function DaitaSettings() { {messages.gettext('Back')} , ]} - close={hideConfirmationDialog} - /> + close={hideConfirmationDialog}> + + { + // TRANSLATORS: Warning text in a dialog that is displayed after a setting is toggled. + messages.pgettext( + 'wireguard-settings-view', + "This feature isn't available on all servers. You might need to change location after enabling.", + ) + } + + + {sprintf( + messages.pgettext( + 'wireguard-settings-view', + 'Attention: Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed. Please consider this if you want to enable %(daita)s.', + ), + { daita: strings.daita }, + )} + + ); } From 65a00acb877e639d18380069b8def86107fd73b9 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 11 Apr 2024 19:05:01 +0200 Subject: [PATCH 080/214] Add DAITA display to main view --- .../renderer/components/ConnectionPanel.tsx | 33 +++++++++++++++---- .../containers/ConnectionPanelContainer.tsx | 6 ++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/gui/src/renderer/components/ConnectionPanel.tsx b/gui/src/renderer/components/ConnectionPanel.tsx index a99140bb431d..4c709b7204ed 100644 --- a/gui/src/renderer/components/ConnectionPanel.tsx +++ b/gui/src/renderer/components/ConnectionPanel.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { sprintf } from 'sprintf-js'; import styled from 'styled-components'; -import { colors } from '../../config.json'; +import { colors, strings } from '../../config.json'; import { EndpointObfuscationType, ProxyType, @@ -48,6 +48,7 @@ interface IProps { bridgeInfo?: IBridgeData; outAddress?: IOutAddress; obfuscationEndpoint?: IObfuscationData; + daita: boolean; onToggle: () => void; className?: string; } @@ -146,13 +147,15 @@ export default class ConnectionPanel extends React.Component { } private hostnameLine() { + let hostname = ''; + if (this.props.hostname && this.props.bridgeHostname) { - return sprintf(messages.pgettext('connection-info', '%(relay)s via %(entry)s'), { + hostname = sprintf(messages.pgettext('connection-info', '%(relay)s via %(entry)s'), { relay: this.props.hostname, entry: this.props.bridgeHostname, }); } else if (this.props.hostname && this.props.entryHostname) { - return sprintf( + hostname = sprintf( // TRANSLATORS: The hostname line displayed below the country on the main screen // TRANSLATORS: Available placeholders: // TRANSLATORS: %(relay)s - the relay hostname @@ -163,13 +166,31 @@ export default class ConnectionPanel extends React.Component { entry: this.props.entryHostname, }, ); + } else if (this.props.bridgeInfo?.ip) { + hostname = sprintf(messages.pgettext('connection-info', '%(relay)s via %(entry)s'), { + relay: this.props.hostname, + }); } else if (this.props.bridgeInfo !== undefined) { - return sprintf(messages.pgettext('connection-info', '%(relay)s via Custom bridge'), { + hostname = sprintf(messages.pgettext('connection-info', '%(relay)s via Custom bridge'), { relay: this.props.hostname, }); - } else { - return this.props.hostname || ''; + } else if (this.props.hostname) { + hostname = this.props.hostname; + } + + if (hostname !== '' && this.props.daita) { + hostname = sprintf( + // TRANSLATORS: %(hostname)s - The current server the app is connected to, e.g. "se-got-wg-001 using DAITA" + // TRANSLATORS: %(daita)s - Will be replaced with "DAITA" + messages.pgettext('connection-info', '%(hostname)s using %(daita)s'), + { + hostname, + daita: strings.daita, + }, + ); } + + return hostname; } private transportLine() { diff --git a/gui/src/renderer/containers/ConnectionPanelContainer.tsx b/gui/src/renderer/containers/ConnectionPanelContainer.tsx index 5311f1a8e424..b12902bc5d80 100644 --- a/gui/src/renderer/containers/ConnectionPanelContainer.tsx +++ b/gui/src/renderer/containers/ConnectionPanelContainer.tsx @@ -94,6 +94,11 @@ const mapStateToProps = (state: IReduxState) => { ? tunnelEndpointToObfuscationEndpoint(status.details.endpoint) : undefined; + const daita = + ((status.state === 'connected' || status.state === 'connecting') && + status.details?.endpoint.daita) ?? + false; + return { isOpen: state.userInterface.connectionPanelVisible, hostname: state.connection.hostname, @@ -104,6 +109,7 @@ const mapStateToProps = (state: IReduxState) => { bridgeInfo, outAddress, obfuscationEndpoint, + daita, }; }; From cdb220b9ed68ab7ec31b47c24db223345139fedd Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 11 Apr 2024 19:11:41 +0200 Subject: [PATCH 081/214] Switch to lower case a in against --- gui/src/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/config.json b/gui/src/config.json index 172370aba4f1..a77c47de4501 100644 --- a/gui/src/config.json +++ b/gui/src/config.json @@ -43,6 +43,6 @@ "openvpn": "OpenVPN", "splitTunneling": "Split tunneling", "daita": "DAITA", - "daitaFull": "Defence Against AI-guided Traffic Analysis" + "daitaFull": "Defence against AI-guided Traffic Analysis" } } From bab89328c3ee70b31af034f5809c1667edd6610a Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 11 Apr 2024 19:51:30 +0200 Subject: [PATCH 082/214] Update translations --- gui/locales/messages.pot | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index 8a962b39f238..5eb26192b29a 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -630,6 +630,12 @@ msgctxt "connect-view" msgid "You’re all set!" msgstr "" +#. %(hostname)s - The current server the app is connected to, e.g. "se-got-wg-001 using DAITA" +#. %(daita)s - Will be replaced with "DAITA" +msgctxt "connection-info" +msgid "%(hostname)s using %(daita)s" +msgstr "" + #. The hostname line displayed below the country on the main screen #. Available placeholders: #. %(relay)s - the relay hostname @@ -1985,7 +1991,7 @@ msgid "%(wireguard)s settings" msgstr "" msgctxt "wireguard-settings-view" -msgid "%(daita)s (%(daitaFull)s) hides patterns in the encrypted VPN traffic. It does this by padding all packets to the same size, and by inserting extra noise traffic into the stream. This makes it significantly harder for an observer on the network to identify what services you are talking to." +msgid "%(daita)s (%(daitaFull)s) hides patterns in your encrypted VPN traffic. If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting. It does this by carefully adding network noise and making all network packets the same size." msgstr "" #. Available placeholders: @@ -1995,11 +2001,7 @@ msgid "%(wireguard)s settings" msgstr "" msgctxt "wireguard-settings-view" -msgid "DAITA may cause significant overhead. We do not recommend enabling DAITA on a metered internet connection." -msgstr "" - -msgctxt "wireguard-settings-view" -msgid "Enabling DAITA (Defence against AI Traffic Analysis) hides patterns in the VPN tunnel by generating dummy traffic and using a fixed packet size." +msgid "Attention: Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed. Please consider this if you want to enable %(daita)s." msgstr "" msgctxt "wireguard-settings-view" @@ -2066,15 +2068,6 @@ msgctxt "wireguard-settings-view" msgid "This feature isn't available on all servers. You might need to change location after enabling." msgstr "" -msgctxt "wireguard-settings-view" -msgid "This feature cause significant performance and network overhead. We do not recommend enabling it unless you know you need it. We also do not recommend enabling %(daita)s on a metered internet connection." -msgstr "" - -#. Warning text in a dialog that is displayed after a setting is toggled. -msgctxt "wireguard-settings-view" -msgid "This feature isn't available on all servers. You might need to change location after enabling." -msgstr "" - msgctxt "wireguard-settings-view" msgid "This feature makes the WireGuard tunnel resistant to potential attacks from quantum computers." msgstr "" From 3f2a9b376ce9a18c1e2d030afbab81223f5bc261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Fri, 12 Apr 2024 16:04:39 +0200 Subject: [PATCH 083/214] Add DAITA to relay selection --- mullvad-daemon/src/lib.rs | 4 +- mullvad-relay-selector/src/lib.rs | 5 +- .../src/relay_selector/matcher.rs | 21 ++++++-- .../src/relay_selector/mod.rs | 18 +++++-- .../src/relay_selector/query.rs | 52 +++++++++++++++---- .../tests/relay_selector.rs | 8 +-- talpid-tunnel-config-client/src/lib.rs | 17 ------ talpid-wireguard/src/lib.rs | 2 +- 8 files changed, 87 insertions(+), 40 deletions(-) diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 2733482a4c36..d0fad0493d76 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -37,7 +37,9 @@ use futures::{ StreamExt, }; use geoip::GeoIpHandler; -use mullvad_relay_selector::{AdditionalRelayConstraints, AdditionalWireguardConstraints, RelaySelector, SelectorConfig}; +use mullvad_relay_selector::{ + AdditionalRelayConstraints, AdditionalWireguardConstraints, RelaySelector, SelectorConfig, +}; #[cfg(target_os = "android")] use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; #[cfg(target_os = "windows")] diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs index 9ac49d0b1f63..73c709a844a7 100644 --- a/mullvad-relay-selector/src/lib.rs +++ b/mullvad-relay-selector/src/lib.rs @@ -10,6 +10,7 @@ mod relay_selector; pub use error::Error; pub use relay_selector::detailer; pub use relay_selector::{ - query, GetRelay, RelaySelector, RuntimeParameters, SelectedBridge, SelectedObfuscator, - SelectorConfig, WireguardConfig, RETRY_ORDER, + query, AdditionalRelayConstraints, AdditionalWireguardConstraints, GetRelay, RelaySelector, + RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, WireguardConfig, + RETRY_ORDER, }; diff --git a/mullvad-relay-selector/src/relay_selector/matcher.rs b/mullvad-relay-selector/src/relay_selector/matcher.rs index 2987d8965abf..f6c244d3f255 100644 --- a/mullvad-relay-selector/src/relay_selector/matcher.rs +++ b/mullvad-relay-selector/src/relay_selector/matcher.rs @@ -8,7 +8,7 @@ use mullvad_types::{ GeographicLocationConstraint, InternalBridgeConstraints, LocationConstraint, Ownership, Providers, }, - relay_list::{Relay, RelayEndpointData}, + relay_list::{Relay, RelayEndpointData, WireguardRelayEndpointData}, }; use talpid_types::net::TunnelType; @@ -32,7 +32,9 @@ pub fn filter_matching_relay_list<'a, R: Iterator + Clone>( // Filter by ownership .filter(|relay| filter_on_ownership(&query.ownership, relay)) // Filter by providers - .filter(|relay| filter_on_providers(&query.providers, relay)); + .filter(|relay| filter_on_providers(&query.providers, relay)) + // Filter by DAITA support + .filter(|relay| filter_on_daita(&query.wireguard_constraints.daita, relay)); // The last filtering to be done is on the `include_in_country` attribute found on each // relay. When the location constraint is based on country, a relay which has @@ -76,7 +78,7 @@ pub fn filter_matching_bridges<'a, R: Iterator + Clone>( .filter(|relay| filter_on_location(&locations, relay)) // Filter by ownership .filter(|relay| filter_on_ownership(&constraints.ownership, relay)) - // Filter by constraints + // Filter by providers .filter(|relay| filter_on_providers(&constraints.providers, relay)) .cloned() .collect() @@ -108,6 +110,19 @@ pub fn filter_on_providers(filter: &Constraint, relay: &Relay) -> boo filter.matches(relay) } +/// Returns whether `relay` satisfy the daita constraint posed by `filter`. +pub fn filter_on_daita(filter: &Constraint, relay: &Relay) -> bool { + match (filter, &relay.endpoint_data) { + // Only a subset of relays support DAITA, so filter out ones that don't. + ( + Constraint::Only(true), + RelayEndpointData::Wireguard(WireguardRelayEndpointData { daita, .. }), + ) => *daita, + // If we don't require DAITA, any relay works. + _ => true, + } +} + /// Returns whether the relay is an OpenVPN relay. pub const fn filter_openvpn(relay: &Relay) -> bool { matches!(relay.endpoint_data, RelayEndpointData::Openvpn) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 7ec2fc02bbe7..3f92bd344bf8 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -184,6 +184,7 @@ enum SpecializedSelectorConfig<'a> { #[derive(Debug, Clone)] struct NormalSelectorConfig<'a> { user_preferences: &'a RelayConstraints, + additional_preferences: &'a AdditionalRelayConstraints, custom_lists: &'a CustomListsSettings, // Wireguard specific data obfuscation_settings: &'a ObfuscationSettings, @@ -288,7 +289,7 @@ impl Default for SelectorConfig { let default_settings = Settings::default(); SelectorConfig { relay_settings: default_settings.relay_settings, - additional_constraints: default_settings.additional_constraints, + additional_constraints: AdditionalRelayConstraints::default(), bridge_settings: default_settings.bridge_settings, obfuscation_settings: default_settings.obfuscation_settings, bridge_state: default_settings.bridge_state, @@ -307,6 +308,7 @@ impl<'a> From<&'a SelectorConfig> for SpecializedSelectorConfig<'a> { RelaySettings::Normal(user_preferences) => { SpecializedSelectorConfig::Normal(NormalSelectorConfig { user_preferences, + additional_preferences: &value.additional_constraints, obfuscation_settings: &value.obfuscation_settings, bridge_state: &value.bridge_state, bridge_settings: &value.bridge_settings, @@ -323,6 +325,7 @@ impl<'a> From> for RelayQuery { /// Map the Wireguard-specific bits of `value` to [`WireguradRelayQuery`] fn wireguard_constraints( wireguard_constraints: WireguardConstraints, + additional_constraints: AdditionalWireguardConstraints, obfuscation_settings: ObfuscationSettings, ) -> WireguardRelayQuery { let WireguardConstraints { @@ -331,6 +334,7 @@ impl<'a> From> for RelayQuery { use_multihop, entry_location, } = wireguard_constraints; + let AdditionalWireguardConstraints { daita } = additional_constraints; WireguardRelayQuery { port, ip_version, @@ -338,6 +342,7 @@ impl<'a> From> for RelayQuery { entry_location, obfuscation: obfuscation_settings.selected_obfuscation, udp2tcp_port: Constraint::Only(obfuscation_settings.udp2tcp.clone()), + daita: Constraint::Only(daita), } } @@ -366,6 +371,7 @@ impl<'a> From> for RelayQuery { let wireguard_constraints = wireguard_constraints( value.user_preferences.wireguard_constraints.clone(), + value.additional_preferences.wireguard.clone(), value.obfuscation_settings.clone(), ); let openvpn_constraints = openvpn_constraints( @@ -740,8 +746,14 @@ impl RelaySelector { // After we have our two queries (one for the exit relay & one for the entry relay), // we can query for all exit & entry candidates! All candidates are needed for the next // step. - let exit_candidates = - filter_matching_relay_list(query, parsed_relays.relays(), config.custom_lists); + let mut exit_relay_query = query.clone(); + // DAITA should only be enabled for the entry relay + exit_relay_query.wireguard_constraints.daita = Constraint::Only(false); + let exit_candidates = filter_matching_relay_list( + &exit_relay_query, + parsed_relays.relays(), + config.custom_lists, + ); let entry_candidates = filter_matching_relay_list( &entry_relay_query, parsed_relays.relays(), diff --git a/mullvad-relay-selector/src/relay_selector/query.rs b/mullvad-relay-selector/src/relay_selector/query.rs index f112e83968a4..ff6840705121 100644 --- a/mullvad-relay-selector/src/relay_selector/query.rs +++ b/mullvad-relay-selector/src/relay_selector/query.rs @@ -28,6 +28,7 @@ //! queries and ensure that queries are built in a type-safe manner, reducing the risk //! of runtime errors and improving code readability. +use crate::AdditionalWireguardConstraints; use mullvad_types::{ constraints::Constraint, relay_constraints::{ @@ -139,6 +140,7 @@ pub struct WireguardRelayQuery { pub entry_location: Constraint, pub obfuscation: SelectedObfuscation, pub udp2tcp_port: Constraint, + pub daita: Constraint, } impl WireguardRelayQuery { @@ -156,6 +158,7 @@ impl WireguardRelayQuery { entry_location: Constraint::Any, obfuscation: SelectedObfuscation::Auto, udp2tcp_port: Constraint::Any, + daita: Constraint::Any, } } } @@ -172,6 +175,17 @@ impl From for WireguardConstraints { } } +impl From for AdditionalWireguardConstraints { + /// The mapping from [`WireguardRelayQuery`] to [`AdditionalWireguardConstraints`]. + fn from(value: WireguardRelayQuery) -> Self { + AdditionalWireguardConstraints { + daita: value + .daita + .unwrap_or(AdditionalWireguardConstraints::default().daita), + } + } +} + /// A query for a relay with OpenVPN-specific properties, such as `bridge_settings`. /// /// This struct may look a lot like [`OpenVpnConstraints`], and that is the point! @@ -344,10 +358,11 @@ pub mod builder { } } /// Set the VPN protocol for this [`RelayQueryBuilder`] to Wireguard. - pub fn wireguard(mut self) -> RelayQueryBuilder> { + pub fn wireguard(mut self) -> RelayQueryBuilder> { let protocol = Wireguard { multihop: Any, obfuscation: Any, + daita: Any, }; self.query.tunnel_protocol = Constraint::Only(TunnelType::Wireguard); // Update the type state @@ -381,13 +396,14 @@ pub mod builder { /// select entry point. /// /// [`WireguardRelayQuery`]: super::WireguardRelayQuery - pub struct Wireguard { + pub struct Wireguard { multihop: Multihop, obfuscation: Obfuscation, + daita: Daita, } // This impl-block is quantified over all configurations - impl RelayQueryBuilder> { + impl RelayQueryBuilder> { /// Specify the port to ues when connecting to the selected /// Wireguard relay. pub const fn port(mut self, port: u16) -> Self { @@ -403,11 +419,27 @@ pub mod builder { } } - impl RelayQueryBuilder> { + impl RelayQueryBuilder> { + /// Enable DAITA support. + pub fn daita(mut self) -> RelayQueryBuilder> { + self.query.wireguard_constraints.daita = Constraint::Only(true); + // Update the type state + RelayQueryBuilder { + query: self.query, + protocol: Wireguard { + multihop: self.protocol.multihop, + obfuscation: self.protocol.obfuscation, + daita: true, + }, + } + } + } + + impl RelayQueryBuilder> { /// Enable multihop. /// /// To configure the entry relay, see [`RelayQueryBuilder::entry`]. - pub fn multihop(mut self) -> RelayQueryBuilder> { + pub fn multihop(mut self) -> RelayQueryBuilder> { self.query.wireguard_constraints.use_multihop = Constraint::Only(true); // Update the type state RelayQueryBuilder { @@ -415,12 +447,13 @@ pub mod builder { protocol: Wireguard { multihop: true, obfuscation: self.protocol.obfuscation, + daita: self.protocol.daita, }, } } } - impl RelayQueryBuilder> { + impl RelayQueryBuilder> { /// Set the entry location in a multihop configuration. This requires /// multihop to be enabled. pub fn entry(mut self, location: GeographicLocationConstraint) -> Self { @@ -430,18 +463,19 @@ pub mod builder { } } - impl RelayQueryBuilder> { + impl RelayQueryBuilder> { /// Enable `UDP2TCP` obufscation. This will in turn enable the option to configure the /// `UDP2TCP` port. pub fn udp2tcp( mut self, - ) -> RelayQueryBuilder> { + ) -> RelayQueryBuilder> { let obfuscation = Udp2TcpObfuscationSettings { port: Constraint::Any, }; let protocol = Wireguard { multihop: self.protocol.multihop, obfuscation: obfuscation.clone(), + daita: self.protocol.daita, }; self.query.wireguard_constraints.udp2tcp_port = Constraint::Only(obfuscation); self.query.wireguard_constraints.obfuscation = SelectedObfuscation::Udp2Tcp; @@ -452,7 +486,7 @@ pub mod builder { } } - impl RelayQueryBuilder> { + impl RelayQueryBuilder> { /// Set the `UDP2TCP` port. This is the TCP port which the `UDP2TCP` obfuscation /// protocol should use to connect to a relay. pub fn udp2tcp_port(mut self, port: u16) -> Self { diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs index 40efae24e2a6..2a53afe11acc 100644 --- a/mullvad-relay-selector/tests/relay_selector.rs +++ b/mullvad-relay-selector/tests/relay_selector.rs @@ -12,15 +12,15 @@ use talpid_types::net::{ use mullvad_relay_selector::{ query::{builder::RelayQueryBuilder, BridgeQuery, OpenVpnRelayQuery}, - Error, GetRelay, RelaySelector, RuntimeParameters, SelectorConfig, WireguardConfig, - RETRY_ORDER, + AdditionalRelayConstraints, AdditionalWireguardConstraints, Error, GetRelay, RelaySelector, + RuntimeParameters, SelectorConfig, WireguardConfig, RETRY_ORDER, }; use mullvad_types::{ constraints::Constraint, endpoint::MullvadEndpoint, relay_constraints::{ - BridgeConstraints, BridgeState, GeographicLocationConstraint, Ownership, Providers, - SelectedObfuscation, TransportPort, + BridgeConstraints, BridgeState, GeographicLocationConstraint, LocationConstraint, + Ownership, Providers, SelectedObfuscation, TransportPort, }, relay_list::{ BridgeEndpointData, OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayEndpointData, diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs index e89b1be42d38..e272e794c776 100644 --- a/talpid-tunnel-config-client/src/lib.rs +++ b/talpid-tunnel-config-client/src/lib.rs @@ -106,23 +106,6 @@ pub async fn request_ephemeral_peer( ephemeral_pubkey: PublicKey, enable_post_quantum: bool, enable_daita: bool, -) -> Result { - request_ephemeral_peer_with_opts( - service_address, - parent_pubkey, - ephemeral_pubkey, - enable_post_quantum, - enable_daita, - ) - .await -} - -pub async fn request_ephemeral_peer_with_opts( - service_address: IpAddr, - parent_pubkey: PublicKey, - ephemeral_pubkey: PublicKey, - enable_post_quantum: bool, - enable_daita: bool, ) -> Result { let (pq_request, kem_secrets) = if enable_post_quantum { let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await; diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs index bdef52343e16..7c01538b60ac 100644 --- a/talpid-wireguard/src/lib.rs +++ b/talpid-wireguard/src/lib.rs @@ -726,7 +726,7 @@ impl WireguardMonitor { let ephemeral = tokio::time::timeout( timeout, - talpid_tunnel_config_client::request_ephemeral_peer_with_opts( + talpid_tunnel_config_client::request_ephemeral_peer( IpAddr::from(config.ipv4_gateway), config.tunnel.private_key.public_key(), wg_psk_pubkey, From 87d5f411533c6d0863a4588f4190984f4d04e5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Fri, 12 Apr 2024 19:57:18 +0200 Subject: [PATCH 084/214] Add DAITA relay selection tests --- .../tests/relay_selector.rs | 181 +++++++++++++++++- 1 file changed, 177 insertions(+), 4 deletions(-) diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs index 2a53afe11acc..d92e7a72a9cb 100644 --- a/mullvad-relay-selector/tests/relay_selector.rs +++ b/mullvad-relay-selector/tests/relay_selector.rs @@ -12,15 +12,15 @@ use talpid_types::net::{ use mullvad_relay_selector::{ query::{builder::RelayQueryBuilder, BridgeQuery, OpenVpnRelayQuery}, - AdditionalRelayConstraints, AdditionalWireguardConstraints, Error, GetRelay, RelaySelector, - RuntimeParameters, SelectorConfig, WireguardConfig, RETRY_ORDER, + Error, GetRelay, RelaySelector, RuntimeParameters, SelectorConfig, WireguardConfig, + RETRY_ORDER, }; use mullvad_types::{ constraints::Constraint, endpoint::MullvadEndpoint, relay_constraints::{ - BridgeConstraints, BridgeState, GeographicLocationConstraint, LocationConstraint, - Ownership, Providers, SelectedObfuscation, TransportPort, + BridgeConstraints, BridgeState, GeographicLocationConstraint, Ownership, Providers, + SelectedObfuscation, TransportPort, }, relay_list::{ BridgeEndpointData, OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayEndpointData, @@ -181,6 +181,19 @@ fn unwrap_relay(get_result: GetRelay) -> Relay { } } +fn unwrap_entry_relay(get_result: GetRelay) -> Relay { + match get_result { + GetRelay::Wireguard { inner, .. } => match inner { + crate::WireguardConfig::Singlehop { exit } => exit, + crate::WireguardConfig::Multihop { entry, .. } => entry, + }, + GetRelay::OpenVpn { exit, .. } => exit, + GetRelay::Custom(custom) => { + panic!("Can not extract regular relay from custom relay: {custom}") + } + } +} + fn unwrap_endpoint(get_result: GetRelay) -> MullvadEndpoint { match get_result { GetRelay::Wireguard { endpoint, .. } => MullvadEndpoint::Wireguard(endpoint), @@ -202,6 +215,13 @@ fn default_relay_selector() -> RelaySelector { RelaySelector::from_list(SelectorConfig::default(), RELAYS.clone()) } +fn supports_daita(relay: &Relay) -> bool { + match relay.endpoint_data { + RelayEndpointData::Wireguard(WireguardRelayEndpointData { daita, .. }) => daita, + _ => false, + } +} + /// This is not an actual test. Rather, it serves as a reminder that if [`RETRY_ORDER`] is modified, /// the programmer should be made aware to update all external documents which rely on the retry /// order to be correct. @@ -1123,3 +1143,156 @@ fn openvpn_bridge_with_automatic_transport_protocol() { assert!(relay.is_err()) } } + +/// Return only entry relays that support DAITA when DAITA filtering is enabled. All relays that +/// support DAITA also support NOT DAITA. Thus, disabling it should not cause any WireGuard relays +/// to be filtered out. +#[test] +fn test_daita() { + let relay_list = RelayList { + etag: None, + countries: vec![RelayListCountry { + name: "Sweden".to_string(), + code: "se".to_string(), + cities: vec![RelayListCity { + name: "Gothenburg".to_string(), + code: "got".to_string(), + latitude: 57.70887, + longitude: 11.97456, + relays: vec![ + Relay { + hostname: "se9-wireguard".to_string(), + ipv4_addr_in: "185.213.154.68".parse().unwrap(), + ipv6_addr_in: Some("2a03:1b20:5:f011::a09f".parse().unwrap()), + include_in_country: true, + active: true, + owned: true, + provider: "31173".to_string(), + weight: 1, + endpoint_data: RelayEndpointData::Wireguard(WireguardRelayEndpointData { + public_key: PublicKey::from_base64( + "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", + ) + .unwrap(), + daita: false, + }), + location: None, + }, + Relay { + hostname: "se10-wireguard".to_string(), + ipv4_addr_in: "185.213.154.69".parse().unwrap(), + ipv6_addr_in: Some("2a03:1b20:5:f011::a10f".parse().unwrap()), + include_in_country: true, + active: true, + owned: false, + provider: "31173".to_string(), + weight: 1, + endpoint_data: RelayEndpointData::Wireguard(WireguardRelayEndpointData { + public_key: PublicKey::from_base64( + "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", + ) + .unwrap(), + daita: true, + }), + location: None, + }, + ], + }], + }], + openvpn: OpenVpnEndpointData { ports: vec![] }, + bridge: BridgeEndpointData { + shadowsocks: vec![], + }, + wireguard: WireguardEndpointData { + port_ranges: vec![(53, 53), (4000, 33433), (33565, 51820), (52000, 60000)], + ipv4_gateway: "10.64.0.1".parse().unwrap(), + ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(), + udp2tcp_ports: vec![], + }, + }; + + let daita_supporting_relay = + GeographicLocationConstraint::hostname("se", "got", "se10-wireguard"); + let nondaita_supporting_relay = + GeographicLocationConstraint::hostname("se", "got", "se9-wireguard"); + + let relay_selector = RelaySelector::from_list(SelectorConfig::default(), relay_list); + + // Only pick relays that support DAITA + let query = RelayQueryBuilder::new().wireguard().daita().build(); + let relay = unwrap_entry_relay(relay_selector.get_relay_by_query(query).unwrap()); + assert!( + supports_daita(&relay), + "Selector supported relay that does not support DAITA: {relay:?}" + ); + + // Fail when only non-DAITA relays match constraints + let query = RelayQueryBuilder::new() + .wireguard() + .daita() + .location(nondaita_supporting_relay.clone()) + .build(); + relay_selector + .get_relay_by_query(query) + .expect_err("Expected to find no matching relay"); + + // DAITA-supporting relays can be picked even when it is disabled + let query = RelayQueryBuilder::new() + .wireguard() + .location(daita_supporting_relay.clone()) + .build(); + relay_selector + .get_relay_by_query(query) + .expect("Expected DAITA-supporting relay to work without DAITA"); + + // Non DAITA-supporting relays can be picked when it is disabled + let query = RelayQueryBuilder::new() + .wireguard() + .location(nondaita_supporting_relay.clone()) + .build(); + relay_selector + .get_relay_by_query(query) + .expect("Expected DAITA-supporting relay to work without DAITA"); + + // Entry relay must support daita + let query = RelayQueryBuilder::new() + .wireguard() + .daita() + .multihop() + .build(); + let relay = relay_selector.get_relay_by_query(query).unwrap(); + match relay { + GetRelay::Wireguard { + inner: WireguardConfig::Multihop { exit: _, entry }, + .. + } => { + assert!(supports_daita(&entry), "entry relay must support DAITA"); + } + wrong_relay => panic!( + "Relay selector should have picked a Wireguard relay, instead chose {wrong_relay:?}" + ), + } + + // Exit relay does not have to support daita + let query = RelayQueryBuilder::new() + .wireguard() + .daita() + .multihop() + .location(nondaita_supporting_relay) + .build(); + let relay = relay_selector.get_relay_by_query(query).unwrap(); + match relay { + GetRelay::Wireguard { + inner: WireguardConfig::Multihop { exit, entry: _ }, + .. + } => { + assert!( + !supports_daita(&exit), + "expected non DAITA-supporting exit relay, got {exit:?}" + ); + } + wrong_relay => panic!( + "Relay selector should have picked a Wireguard relay, instead chose {wrong_relay:?}" + ), + } +} From 7fa8ba2623dfc66ed8c23127afbea23bb4eabcd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Fri, 12 Apr 2024 20:55:18 +0200 Subject: [PATCH 085/214] Bump binaries submodule --- dist-assets/binaries | 2 +- windows/driverlogic/driverlogic.vcxproj | 6 +++--- windows/driverlogic/driverlogic.vcxproj.filters | 2 +- windows/driverlogic/src/driverlogic.cpp | 2 +- windows/driverlogic/src/{wireguard.h => wireguard_dll.h} | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename windows/driverlogic/src/{wireguard.h => wireguard_dll.h} (96%) diff --git a/dist-assets/binaries b/dist-assets/binaries index d5772339cee9..d9edc48af33d 160000 --- a/dist-assets/binaries +++ b/dist-assets/binaries @@ -1 +1 @@ -Subproject commit d5772339cee9c1a0d7671968746f02499b78e245 +Subproject commit d9edc48af33db4afbcb8d6c7754e1dfe963d5172 diff --git a/windows/driverlogic/driverlogic.vcxproj b/windows/driverlogic/driverlogic.vcxproj index c68d0568ab82..ab9e6c19b5f5 100644 --- a/windows/driverlogic/driverlogic.vcxproj +++ b/windows/driverlogic/driverlogic.vcxproj @@ -61,7 +61,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ + $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../../dist-assets/binaries/wireguard-nt/api/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ stdcpp20 MultiThreadedDebug @@ -83,7 +83,7 @@ NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 - $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ + $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../../dist-assets/binaries/wireguard-nt/api/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ MultiThreaded @@ -115,7 +115,7 @@ - + diff --git a/windows/driverlogic/driverlogic.vcxproj.filters b/windows/driverlogic/driverlogic.vcxproj.filters index bd73d300299b..fae74cfdee3e 100644 --- a/windows/driverlogic/driverlogic.vcxproj.filters +++ b/windows/driverlogic/driverlogic.vcxproj.filters @@ -26,6 +26,6 @@ - + \ No newline at end of file diff --git a/windows/driverlogic/src/driverlogic.cpp b/windows/driverlogic/src/driverlogic.cpp index 7cd8cf5f51fc..795960b6d2f9 100644 --- a/windows/driverlogic/src/driverlogic.cpp +++ b/windows/driverlogic/src/driverlogic.cpp @@ -4,7 +4,7 @@ #include "service.h" #include "log.h" #include "wintun.h" -#include "wireguard.h" +#include "wireguard_dll.h" #include "devenum.h" #include #include diff --git a/windows/driverlogic/src/wireguard.h b/windows/driverlogic/src/wireguard_dll.h similarity index 96% rename from windows/driverlogic/src/wireguard.h rename to windows/driverlogic/src/wireguard_dll.h index e99da56c05c5..7260ce5b4dfa 100644 --- a/windows/driverlogic/src/wireguard.h +++ b/windows/driverlogic/src/wireguard_dll.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include "util.h" From 1929cdae674fbe6524c759bfd3e4a612314305e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Mon, 15 Apr 2024 09:45:33 +0200 Subject: [PATCH 086/214] Update maybenot machines --- dist-assets/maybenot_machines | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist-assets/maybenot_machines b/dist-assets/maybenot_machines index e7bde4f764c1..87e72c4562a2 100644 --- a/dist-assets/maybenot_machines +++ b/dist-assets/maybenot_machines @@ -1,4 +1,4 @@ 789cd5cfbb0900200c04d08b833886adb889389f5bb9801be811acb58ae2837ce02010c158b070555c9538b6377a64dbb0ceff242c20b79038507dd169fbede9f629bf6f021efa1b66 -789ccd934b48024118c777357ae9250aa14b8542085d2a0f264233160975a80c440f5b04455deaa050143d0e81d8835ea708f6d2a9a2430511d2a136118bcaa854422a93c44d93a243121844b3204b8b87acadf0c7f0c137f3cdccf7e08f635cc200190eb81099af106235eda133a95a4f4df5dcbfad2eb640b4c921135330e42ba1585066951456b311b3f6d8ca4254ae415f212f356efba0928e44d9642df27769b79162fd9ff1ccf3fef7b9b19982cee963405699ee1c136e107bf0ae19d6fdff9ec76770ac8bc1a9a34847c986d9a8858391d28af2a20b76622824f55125186dedd813d9c2207bd22c382f76f1aed070ad7a896bbdc09317b48c1121deefa51f599855e23b7d1519a1eb044134414662573e1d546d1df6efd6ce401cdb3f5aaadb56e8a9b69d7ab140d60c0924a601b7086929019a54ef66e5b02cd7c376a86199d63cc57fbb63c9daf18f2b9805e6d5f49c9a0e249dff158df6919c0232003a87321e2f0ff86be903a6c37ad5 -789ced956d28435118c7cfdde63544f9424292489222493a77214962de92248992bc535e4221ad2549920f5e22e5ada525496b1faee52d9fc6e4255bb3469b96661f282379ee1d574bb7257c50fb7d38dd73efd3e9dcf33fe77708e4880943e3002180c6197c74b6d09ce6dd24a2a003a4530dc6e715ab4d44dea8222e9f8c6292878ee0a1afb598ca18ae876f0212ca1c2110b19f932c1156eee2e99d70596bed360e1294f8c5adeb71aa2e6f64b251f965721f840d99455ba15adc92e60d13d173d6b9f87bf8e87e690f7224a8aedba804cfc842366979b60f4f23731342d228a67c54f910ad6093a2739eb1e8f05c70bc36d2fd827dcf857bff44fa70bd19c356b21ee772ef8cef220d29ab4ed519d8f196e95f119eb37d179f78a0177af1ab4c78d602918b0ba8d7934e3a73d28717eb2119881542114be05a96be3bf0143f46d3d15f73ae682f4eaa90676bb0adaf4ed13eaec2200490889ab3dec54fe123469fbe45e4e2142dde3092c951524a32a734b103ec5de55f9ab261ca27db6ac7ee7ac4f9ef9e47088ced55531300679a2056e9cde07f8507a9c3f9cc4d3566464938f8f5e418af4835d87e9370dbde9339cc066cf79173a7fc77de00293aaf16 -789ce5914b4802511486ef75c8405a84b4aa8810029b8236b9a8b03b5af6801e24512d24225ab409a2550521152e24c455b831706a216ea4402c5a0d1404465022980d990d5941140c328bc81eb7910686bb89dc047e1c0edc737e2e87ff8740cd23c24d05a4702b1e0a685737bb36fc36a6ef76b92adc6be5a2b9f7d1abee6a0b5e1680009a75c99d79730cf5e065623185fcc7f5bb0bb329e2a81f663a6e863dbe341a9aae9c688b6408ddd6a51bd719a2e880860e0868dc29e13a2174a503050a9ed6304792d1e1ddb63378a8420bd6b9186bfbb033721834a9f84e0af75fe191d8bdced6b8e2787b594b3093bd57de7fe5b3e2d9600df3c8b9e47e491b85a2fffb7f68c1296b8be6aec798b987b790a86f22722807f990181f140618d7d3c8419dcbc4e1a14ca3c3ab5933d31600a19294087d0dfae63b549bbf88f0c124e1d854f6dcc0bf0a484e5db78f569069f2b03f41e84a8d2f5e5a7583 +789cedd0510a401110856153f665ffbbb1030e991735717890f8eabae22f35e252e31a6c2b6c2bf158ce88010b61b69fed14db7fabd8498ffad17d8fedbf5decc4adde3ab7b0fddb045fa153d3ff09ecdb6c7faf0cd04322e8 +789cedd1510a80200c80617de95addff36dda096e84b209bfe0f1bb40f32297f1056cbdd954eb68d6c9b7ac8e2e33a650176fbdd6ea07df24227b7daaf9effa27df2462768edade766689fa2a093d47aedbf86f6291a3ad1593ffb6e45fb4454795e630ae31d11bd1bedffeb0151b22c62 +789cedd2bd4b82411c07f07b2a48846a089a326889861a9c2452ee12221e0ca2a5201ca5a122fc0bda042544370771f3159c9c0405e51914e45c5414df7d441075f00515c4c553f0117906075f40f0331c77c7ddef8efb1e05a6de8e6f3d96661392ee1c6a9f349b72e6316a6e542c4c980b5dbb24cfbbcccefa1d00d57df145ff44079506a677adacc29623a4780f30288a312e99440f809a7d9b457c4711a1b8cf7249ea2adf75cb698e1b4fb50f19dc4d67e0c95d12d354186ab3b1e74f5b9ab7aecc4e70f332e918bfdef6db03ce9042a2bba491e88324629022ef85a6f1aa95a3ece0efcb5f5223012039997e3b726455cb84a9f82322db96f6ff531dbada6558bb120bdce79915bf6c6bc5f51659febc1141265ee0 From 1832ccc05c4f1898d36b2647e254229c821a4674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Mon, 15 Apr 2024 11:46:48 +0200 Subject: [PATCH 087/214] Check out wg-nt in CI --- .github/workflows/daemon.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/daemon.yml b/.github/workflows/daemon.yml index 3293e028791e..a5107fbcf8c1 100644 --- a/.github/workflows/daemon.yml +++ b/.github/workflows/daemon.yml @@ -114,6 +114,10 @@ jobs: - name: Checkout submodules run: git submodule update --init --depth=1 + - name: Checkout wireguard-nt + working-directory: dist-assets/binaries + run: git submodule update --init -- wireguard-nt + - name: Install Protoc uses: arduino/setup-protoc@v1 with: From bea01509a1b7221d3af6324b4bd9fc7243d90d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Tue, 16 Apr 2024 16:28:24 +0200 Subject: [PATCH 088/214] Remove duplicate workspace member --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 65391bcf3755..b0c28f016aa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ "ios/MullvadREST/Transport/Shadowsocks/shadowsocks-proxy", "ios/TunnelObfuscation/tunnel-obfuscator-proxy", "mullvad-api", - "mullvad-daemon", "mullvad-cli", "mullvad-daemon", "mullvad-exclude", From 85ba2643fc3350570fe19e5d3d1180b3301105ef Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Mon, 8 Apr 2024 11:29:37 +0200 Subject: [PATCH 089/214] Show loading states in purchasing flow an extra amount of time --- .../mullvadvpn/usecase/PaymentUseCase.kt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt index 5a50e8040151..82efd5d72206 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt @@ -1,9 +1,11 @@ package net.mullvad.mullvadvpn.usecase import android.app.Activity +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.transform import net.mullvad.mullvadvpn.constant.VERIFICATION_BACK_OFF_FACTOR import net.mullvad.mullvadvpn.constant.VERIFICATION_INITIAL_BACK_OFF_MILLISECONDS import net.mullvad.mullvadvpn.constant.VERIFICATION_MAX_ATTEMPTS @@ -35,7 +37,15 @@ class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : Pay override val purchaseResult = _purchaseResult.asStateFlow() override suspend fun purchaseProduct(productId: ProductId, activityProvider: () -> Activity) { - paymentRepository.purchaseProduct(productId, activityProvider).collect(_purchaseResult) + paymentRepository + .purchaseProduct(productId, activityProvider) + .transform { + emit(it) + if (it.shouldDelayLoading()) { + delay(EXTRA_LOADING_DELAY_MS) + } + } + .collect(_purchaseResult) } override suspend fun queryPaymentAvailability() { @@ -64,6 +74,13 @@ class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : Pay } } } + + private fun PurchaseResult?.shouldDelayLoading() = + this is PurchaseResult.FetchingProducts || this is PurchaseResult.VerificationStarted + + companion object { + const val EXTRA_LOADING_DELAY_MS = 300L + } } class EmptyPaymentUseCase : PaymentUseCase { From 293be10e3404ae8b858c85b3bb8ca30b56b9e469 Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Mon, 8 Apr 2024 13:11:54 +0200 Subject: [PATCH 090/214] Change log rotation to a quota based system --- ios/MullvadLogging/Date+LogFormat.swift | 7 ++ ios/MullvadLogging/LogRotation.swift | 94 +++++++++++++----- ios/MullvadLogging/Logging.swift | 7 +- ios/MullvadVPN.xcodeproj/project.pbxproj | 4 + ios/MullvadVPN/AppDelegate.swift | 2 +- .../Classes/ConsolidatedApplicationLog.swift | 12 +-- .../ProblemReportInteractor.swift | 5 +- ios/MullvadVPNTests/LogRotationTests.swift | 98 +++++++++++++++++++ .../PacketTunnelProvider.swift | 2 +- ios/Shared/ApplicationConfiguration.swift | 22 ++++- 10 files changed, 210 insertions(+), 43 deletions(-) create mode 100644 ios/MullvadVPNTests/LogRotationTests.swift diff --git a/ios/MullvadLogging/Date+LogFormat.swift b/ios/MullvadLogging/Date+LogFormat.swift index dca177599f0b..463d340b5992 100644 --- a/ios/MullvadLogging/Date+LogFormat.swift +++ b/ios/MullvadLogging/Date+LogFormat.swift @@ -15,4 +15,11 @@ extension Date { return formatter.string(from: self) } + + public func logFormatFilename() -> String { + let formatter = DateFormatter() + formatter.dateFormat = "dd-MM-yyyy'T'HH:mm:ss" + + return formatter.string(from: self) + } } diff --git a/ios/MullvadLogging/LogRotation.swift b/ios/MullvadLogging/LogRotation.swift index 57c5597327e6..23d1e5f17815 100644 --- a/ios/MullvadLogging/LogRotation.swift +++ b/ios/MullvadLogging/LogRotation.swift @@ -10,48 +10,96 @@ import Foundation import MullvadTypes public enum LogRotation { + private struct LogData { + var path: URL + var size: UInt64 + var creationDate: Date + } + + public struct Options { + let storageSizeLimit: Int + let oldestAllowedDate: Date + + /// Options for log rotation, defining how logs should be retained. + /// + /// - Parameter storageSizeLimit: Storage size limit in bytes. + /// - Parameter oldestAllowedDate: Oldest allowed date. + public init(storageSizeLimit: Int, oldestAllowedDate: Date) { + self.storageSizeLimit = storageSizeLimit + self.oldestAllowedDate = oldestAllowedDate + } + } + public enum Error: LocalizedError, WrappingError { - case noSourceLogFile - case moveSourceLogFile(Swift.Error) + case rotateLogFiles(Swift.Error) public var errorDescription: String? { switch self { - case .noSourceLogFile: - return "Source log file does not exist." - case .moveSourceLogFile: - return "Failure to move the source log file to backup." + case .rotateLogFiles: + return "Failure to rotate the source log file to backup." } } public var underlyingError: Swift.Error? { switch self { - case .noSourceLogFile: - return nil - case let .moveSourceLogFile(error): + case let .rotateLogFiles(error): return error } } } - public static func rotateLog(logsDirectory: URL, logFileName: String) throws { - let source = logsDirectory.appendingPathComponent(logFileName) - let backup = source.deletingPathExtension().appendingPathExtension("old.log") + public static func rotateLogs(logDirectory: URL, options: Options) throws { + let fileManager = FileManager.default do { - _ = try FileManager.default.replaceItemAt(backup, withItemAt: source) - } catch { - // FileManager returns a very obscure error chain so we need to traverse it to find - // the root cause of the error. - for case let fileError as CocoaError in error.underlyingErrorChain { - // .fileNoSuchFile is returned when both backup and source log files do not exist - // .fileReadNoSuchFile is returned when backup exists but source log file does not - if fileError.code == .fileNoSuchFile || fileError.code == .fileReadNoSuchFile, - fileError.url == source { - throw Error.noSourceLogFile + // Filter out all log files in directory. + let logPaths: [URL] = (try fileManager.contentsOfDirectory( + atPath: logDirectory.relativePath + )).compactMap { file in + if file.split(separator: ".").last == "log" { + logDirectory.appendingPathComponent(file) + } else { + nil } } - throw Error.moveSourceLogFile(error) + // Convert logs into objects with necessary meta data. + let logs = try logPaths.map { logPath in + let attributes = try fileManager.attributesOfItem(atPath: logPath.relativePath) + let size = (attributes[.size] as? UInt64) ?? 0 + let creationDate = (attributes[.creationDate] as? Date) ?? Date.distantPast + + return LogData(path: logPath, size: size, creationDate: creationDate) + }.sorted { log1, log2 in + log1.creationDate > log2.creationDate + } + + try deleteLogsOlderThan(options.oldestAllowedDate, in: logs) + try deleteLogsWithCombinedSizeLargerThan(options.storageSizeLimit, in: logs) + } catch { + throw Error.rotateLogFiles(error) + } + } + + private static func deleteLogsOlderThan(_ dateThreshold: Date, in logs: [LogData]) throws { + let fileManager = FileManager.default + + for log in logs where log.creationDate < dateThreshold { + try fileManager.removeItem(at: log.path) + } + } + + private static func deleteLogsWithCombinedSizeLargerThan(_ sizeThreshold: Int, in logs: [LogData]) throws { + let fileManager = FileManager.default + + // Delete all logs outside maximum capacity (ordered newest to oldest). + var fileSizes = UInt64.zero + for log in logs { + fileSizes += log.size + + if fileSizes > sizeThreshold { + try fileManager.removeItem(at: log.path) + } } } } diff --git a/ios/MullvadLogging/Logging.swift b/ios/MullvadLogging/Logging.swift index a7a19ce7e18d..76c3c57f8c8a 100644 --- a/ios/MullvadLogging/Logging.swift +++ b/ios/MullvadLogging/Logging.swift @@ -8,6 +8,7 @@ import Foundation @_exported import Logging +import MullvadTypes private enum LoggerOutput { case fileOutput(_ fileOutput: LogFileOutputStream) @@ -24,7 +25,6 @@ public struct LoggerBuilder { public init() {} public mutating func addFileOutput(fileURL: URL) { - let logFileName = fileURL.lastPathComponent let logsDirectoryURL = fileURL.deletingLastPathComponent() try? FileManager.default.createDirectory( @@ -34,7 +34,10 @@ public struct LoggerBuilder { ) do { - try LogRotation.rotateLog(logsDirectory: logsDirectoryURL, logFileName: logFileName) + try LogRotation.rotateLogs(logDirectory: logsDirectoryURL, options: LogRotation.Options( + storageSizeLimit: 5_242_880, // 5 MB + oldestAllowedDate: Date(timeIntervalSinceNow: Duration.days(7).timeInterval) + )) } catch { logRotationErrors.append(error) } diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 15d379e53fb4..fca1fe112cd7 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -582,6 +582,7 @@ 7A9CCCC42A96302800DD6A34 /* TunnelCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */; }; 7A9FA1422A2E3306000B728D /* CheckboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1412A2E3306000B728D /* CheckboxView.swift */; }; 7A9FA1442A2E3FE5000B728D /* CheckableSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */; }; + 7AA513862BC91C6B00D081A4 /* LogRotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */; }; 7AB2B6702BA1EB8C00B03E3B /* ListCustomListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB2B66E2BA1EB8C00B03E3B /* ListCustomListViewController.swift */; }; 7AB2B6712BA1EB8C00B03E3B /* ListCustomListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB2B66F2BA1EB8C00B03E3B /* ListCustomListCoordinator.swift */; }; 7AB4CCB92B69097E006037F5 /* IPOverrideTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */; }; @@ -1841,6 +1842,7 @@ 7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelCoordinator.swift; sourceTree = ""; }; 7A9FA1412A2E3306000B728D /* CheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxView.swift; sourceTree = ""; }; 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckableSettingsCell.swift; sourceTree = ""; }; + 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogRotationTests.swift; sourceTree = ""; }; 7AB2B66E2BA1EB8C00B03E3B /* ListCustomListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCustomListViewController.swift; sourceTree = ""; }; 7AB2B66F2BA1EB8C00B03E3B /* ListCustomListCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCustomListCoordinator.swift; sourceTree = ""; }; 7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideTests.swift; sourceTree = ""; }; @@ -2959,6 +2961,7 @@ 7A5869C22B5820CE00640D27 /* IPOverrideRepositoryTests.swift */, 7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */, 7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */, + 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */, A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */, 58C3FA652A38549D006A450A /* MockFileCache.swift */, F09D04B42AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift */, @@ -4973,6 +4976,7 @@ A9A5FA2D2ACB05160083449F /* DurationTests.swift in Sources */, A9A5FA2E2ACB05160083449F /* FileCacheTests.swift in Sources */, A9A5FA2F2ACB05160083449F /* FixedWidthIntegerArithmeticsTests.swift in Sources */, + 7AA513862BC91C6B00D081A4 /* LogRotationTests.swift in Sources */, F04413622BA45CE30018A6EE /* CustomListLocationNodeBuilder.swift in Sources */, A9A5FA302ACB05160083449F /* InputTextFormatterTests.swift in Sources */, F0B0E6972AFE6E7E001DC66B /* XCTest+Async.swift in Sources */, diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index aef14c594af2..be750d852483 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -353,7 +353,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD private func configureLogging() { var loggerBuilder = LoggerBuilder() - loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.logFileURL(for: .mainApp)) + loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.newLogFileURL(for: .mainApp)) #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.mainApp.bundleIdentifier) #endif diff --git a/ios/MullvadVPN/Classes/ConsolidatedApplicationLog.swift b/ios/MullvadVPN/Classes/ConsolidatedApplicationLog.swift index f6f5f0956fcb..0876e86deada 100644 --- a/ios/MullvadVPN/Classes/ConsolidatedApplicationLog.swift +++ b/ios/MullvadVPN/Classes/ConsolidatedApplicationLog.swift @@ -47,17 +47,9 @@ class ConsolidatedApplicationLog: TextOutputStreamable { } } - func addLogFile(fileURL: URL, includeLogBackup: Bool) { - addSingleLogFile(fileURL) - if includeLogBackup { - let oldLogFileURL = fileURL.deletingPathExtension().appendingPathExtension("old.log") - addSingleLogFile(oldLogFileURL) - } - } - - func addLogFiles(fileURLs: [URL], includeLogBackups: Bool) { + func addLogFiles(fileURLs: [URL]) { for fileURL in fileURLs { - addLogFile(fileURL: fileURL, includeLogBackup: includeLogBackups) + addSingleLogFile(fileURL) } } diff --git a/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift b/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift index 9dbf3a5b6b3f..09fa2dfd0a96 100644 --- a/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift +++ b/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift @@ -24,9 +24,8 @@ final class ProblemReportInteractor { redactContainerPathsForSecurityGroupIdentifiers: [securityGroupIdentifier] ) - let logFileURLs = ApplicationTarget.allCases.map { ApplicationConfiguration.logFileURL(for: $0) } - - report.addLogFiles(fileURLs: logFileURLs, includeLogBackups: true) + let logFileURLs = ApplicationTarget.allCases.flatMap { ApplicationConfiguration.logFileURLs(for: $0) } + report.addLogFiles(fileURLs: logFileURLs) return report }() diff --git a/ios/MullvadVPNTests/LogRotationTests.swift b/ios/MullvadVPNTests/LogRotationTests.swift new file mode 100644 index 000000000000..e67687c3a2d1 --- /dev/null +++ b/ios/MullvadVPNTests/LogRotationTests.swift @@ -0,0 +1,98 @@ +// +// LogRotationTests.swift +// MullvadVPNTests +// +// Created by Jon Petersson on 2024-04-12. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import MullvadLogging +import XCTest + +final class LogRotationTests: XCTestCase { + let fileManager = FileManager.default + let directoryPath = FileManager.default.temporaryDirectory.appendingPathComponent("LogRotationTests") + + override func setUpWithError() throws { + try? fileManager.createDirectory( + at: directoryPath, + withIntermediateDirectories: false + ) + } + + override func tearDownWithError() throws { + try fileManager.removeItem(atPath: directoryPath.relativePath) + } + + func testRotateLogsByStorageSizeLimit() throws { + let logPaths = [ + directoryPath.appendingPathComponent("test1.log"), + directoryPath.appendingPathComponent("test2.log"), + directoryPath.appendingPathComponent("test3.log"), + directoryPath.appendingPathComponent("test4.log"), + directoryPath.appendingPathComponent("test5.log"), + ] + + try logPaths.forEach { logPath in + try writeDataToDisk(path: logPath, fileSize: 1000) + } + + try LogRotation.rotateLogs(logDirectory: directoryPath, options: LogRotation.Options( + storageSizeLimit: 5000, + oldestAllowedDate: .distantPast) + ) + var logFileCount = try fileManager.contentsOfDirectory(atPath: directoryPath.relativePath).count + XCTAssertEqual(logFileCount, 5) + + try LogRotation.rotateLogs(logDirectory: directoryPath, options: LogRotation.Options( + storageSizeLimit: 3999, + oldestAllowedDate: .distantPast) + ) + logFileCount = try fileManager.contentsOfDirectory(atPath: directoryPath.relativePath).count + XCTAssertEqual(logFileCount, 3) + } + + func testRotateLogsByOldestAllowedDate() throws { + let firstBatchOflogPaths = [ + directoryPath.appendingPathComponent("test1.log"), + directoryPath.appendingPathComponent("test2.log"), + directoryPath.appendingPathComponent("test3.log"), + ] + + let secondBatchOflogPaths = [ + directoryPath.appendingPathComponent("test4.log"), + directoryPath.appendingPathComponent("test5.log"), + ] + + let oldestDateAllowedForFirstBatch = Date() + try firstBatchOflogPaths.forEach { logPath in + try writeDataToDisk(path: logPath, fileSize: 1000) + } + + let oldestDateAllowedForSecondBatch = Date() + try secondBatchOflogPaths.forEach { logPath in + try writeDataToDisk(path: logPath, fileSize: 1000) + } + + try LogRotation.rotateLogs( + logDirectory: directoryPath, + options: LogRotation.Options(storageSizeLimit: .max, oldestAllowedDate: oldestDateAllowedForFirstBatch) + ) + var logFileCount = try fileManager.contentsOfDirectory(atPath: directoryPath.relativePath).count + XCTAssertEqual(logFileCount, 5) + + try LogRotation.rotateLogs( + logDirectory: directoryPath, + options: LogRotation.Options(storageSizeLimit: .max, oldestAllowedDate: oldestDateAllowedForSecondBatch) + ) + logFileCount = try fileManager.contentsOfDirectory(atPath: directoryPath.relativePath).count + XCTAssertEqual(logFileCount, 2) + } +} + +extension LogRotationTests { + private func writeDataToDisk(path: URL, fileSize: Int) throws { + let data = Data((0 ..< fileSize).map { UInt8($0 & 0xff) }) + try data.write(to: path) + } +} diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 5b56b1675a45..d4745737309f 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -163,7 +163,7 @@ extension PacketTunnelProvider { var loggerBuilder = LoggerBuilder() let pid = ProcessInfo.processInfo.processIdentifier loggerBuilder.metadata["pid"] = .string("\(pid)") - loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.logFileURL(for: .packetTunnel)) + loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.newLogFileURL(for: .packetTunnel)) #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.packetTunnel.bundleIdentifier) #endif diff --git a/ios/Shared/ApplicationConfiguration.swift b/ios/Shared/ApplicationConfiguration.swift index 426067e1dd63..9acea1b97135 100644 --- a/ios/Shared/ApplicationConfiguration.swift +++ b/ios/Shared/ApplicationConfiguration.swift @@ -21,9 +21,25 @@ enum ApplicationConfiguration { FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: securityGroupIdentifier)! } - /// Returns URL for log file associated with application target and located within shared container. - static func logFileURL(for target: ApplicationTarget) -> URL { - containerURL.appendingPathComponent("\(target.bundleIdentifier).log", isDirectory: false) + /// Returns URL for new log file associated with application target and located within shared container. + static func newLogFileURL(for target: ApplicationTarget) -> URL { + containerURL.appendingPathComponent( + "\(target.bundleIdentifier)_\(Date().logFormatFilename()).log", + isDirectory: false + ) + } + + /// Returns URLs for log files associated with application target and located within shared container. + static func logFileURLs(for target: ApplicationTarget) -> [URL] { + let containerUrl = containerURL + + return (try? FileManager.default.contentsOfDirectory(atPath: containerURL.relativePath))?.compactMap { file in + if file.split(separator: ".").last == "log" { + containerUrl.appendingPathComponent(file) + } else { + nil + } + }.sorted { $0.relativePath > $1.relativePath } ?? [] } /// Privacy policy URL. From 720b66ce4c6b8e17e5e556da75f99519a9ada26a Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Wed, 17 Apr 2024 10:02:06 +0200 Subject: [PATCH 091/214] Remove DAITA beta label --- gui/locales/messages.pot | 3 --- gui/src/renderer/components/WireguardSettings.tsx | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index 5eb26192b29a..7a0c2fc456f0 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -56,9 +56,6 @@ msgstr "" msgid "Back" msgstr "" -msgid "BETA" -msgstr "" - msgid "BLOCKED CONNECTION" msgstr "" diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index d315485659ad..f926fe9f4c92 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -34,7 +34,6 @@ import { TitleBarItem, } from './NavigationBar'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; -import YellowLabel from './YellowLabel'; const MIN_WIREGUARD_MTU_VALUE = 1280; const MAX_WIREGUARD_MTU_VALUE = 1420; @@ -581,10 +580,7 @@ function DaitaSettings() { - - {strings.daita} - {messages.gettext('BETA')} - + {strings.daita} From d4270807cd360ab7b659628782255811ee0af579 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 09:11:20 +0200 Subject: [PATCH 092/214] Prevent select location scroll position reset on pop --- .../select-location/ScrollPositionContext.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gui/src/renderer/components/select-location/ScrollPositionContext.tsx b/gui/src/renderer/components/select-location/ScrollPositionContext.tsx index 04fcd0bccc81..d27e252427dd 100644 --- a/gui/src/renderer/components/select-location/ScrollPositionContext.tsx +++ b/gui/src/renderer/components/select-location/ScrollPositionContext.tsx @@ -1,5 +1,7 @@ +import { Action } from 'history'; import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; +import { useHistory } from '../../lib/history'; import { useNormalRelaySettings, useStyledRef } from '../../lib/utilityHooks'; import { CustomScrollbarsRef } from '../CustomScrollbars'; import { LocationType } from './select-location-types'; @@ -37,6 +39,9 @@ export function ScrollPositionContextProvider(props: ScrollPositionContextProps) const { locationType, searchTerm } = useSelectLocationContext(); const relaySettings = useNormalRelaySettings(); + const { action } = useHistory(); + const recentNavigationAction = useRef(action); + const scrollPositions = useRef>>({}); const scrollViewRef = useRef(null); const spacePreAllocationViewRef = useStyledRef(); @@ -82,6 +87,11 @@ export function ScrollPositionContextProvider(props: ScrollPositionContextProps) // Restore the scroll position when parameters change useEffect(() => { + if (recentNavigationAction.current === 'POP') { + recentNavigationAction.current = null; + return; + } + const scrollPosition = scrollPositions.current?.[locationType]; if (scrollPosition) { scrollViewRef.current?.scrollTo(...scrollPosition); From 5ea76a5321649fa1b330e92de5451d9af845343a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 10 Apr 2024 10:22:00 +0200 Subject: [PATCH 093/214] Run nightly formatter for tests --- test/test-manager/src/tests/helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index 86cbecb50cee..dd0efdc182a5 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -509,8 +509,8 @@ pub fn get_app_env() -> HashMap { /// /// # Note /// This function does not handle bridges and multihop configurations (currently). There is no -/// particular reason for this other than it not being needed at the time, so feel free to extend this -/// function :). +/// particular reason for this other than it not being needed at the time, so feel free to extend +/// this function :). pub async fn constrain_to_relay( mullvad_client: &mut MullvadProxyClient, query: RelayQuery, From d1e928106c2aacb848d16092b72fcf475c89083a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Mon, 8 Apr 2024 19:42:55 +0200 Subject: [PATCH 094/214] Prevent ARP lookups during LAN tests --- test/test-manager/src/tests/helpers.rs | 4 +- test/test-manager/src/tests/settings.rs | 62 +++++++++------------ test/test-manager/src/tests/tunnel_state.rs | 42 +++++++------- 3 files changed, 48 insertions(+), 60 deletions(-) diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index dd0efdc182a5..da50679a26b4 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -121,7 +121,7 @@ pub async fn send_guest_probes( rpc: ServiceClient, interface: String, destination: SocketAddr, -) -> Result { +) -> ProbeResult { const MONITOR_DURATION: Duration = Duration::from_secs(8); let pktmon = start_packet_monitor( @@ -162,7 +162,7 @@ pub async fn send_guest_probes( } } - Ok(result) + result } /// Send one probe per transport protocol to `destination` without running a packet monitor diff --git a/test/test-manager/src/tests/settings.rs b/test/test-manager/src/tests/settings.rs index 6d1ade18c720..bd528004992b 100644 --- a/test/test-manager/src/tests/settings.rs +++ b/test/test-manager/src/tests/settings.rs @@ -3,11 +3,9 @@ use super::{ helpers::{connect_and_wait, send_guest_probes}, Error, TestContext, }; -use crate::{assert_tunnel_state, vm::network::DUMMY_LAN_INTERFACE_IP}; use mullvad_management_interface::MullvadProxyClient; -use mullvad_types::states::TunnelState; -use std::net::{IpAddr, SocketAddr}; +use std::net::SocketAddr; use test_macro::test_function; use test_rpc::ServiceClient; @@ -22,12 +20,9 @@ pub async fn test_lan( rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, ) -> Result<(), Error> { - let lan_destination = SocketAddr::new(IpAddr::V4(DUMMY_LAN_INTERFACE_IP), 1234); - - // Connect - // - - connect_and_wait(&mut mullvad_client).await?; + // Take care not to use some bogus IP in the guest's subnet, lest we just send ARP requests + // These will fail if there's no actual host present + let lan_destination = "10.1.2.3:1234".parse().unwrap(); // Disable LAN sharing // @@ -39,6 +34,11 @@ pub async fn test_lan( .await .expect("failed to disable LAN sharing"); + // Connect + // + + connect_and_wait(&mut mullvad_client).await?; + // Ensure LAN is not reachable // @@ -46,7 +46,7 @@ pub async fn test_lan( let default_interface = rpc.get_default_interface().await?; let detected_probes = - send_guest_probes(rpc.clone(), default_interface.clone(), lan_destination).await?; + send_guest_probes(rpc.clone(), default_interface.clone(), lan_destination).await; assert!( detected_probes.none(), "observed unexpected outgoing LAN packets: {detected_probes:?}" @@ -67,8 +67,7 @@ pub async fn test_lan( log::info!("Test whether outgoing LAN traffic is blocked"); - let detected_probes = - send_guest_probes(rpc.clone(), default_interface, lan_destination).await?; + let detected_probes = send_guest_probes(rpc.clone(), default_interface, lan_destination).await; assert!( detected_probes.all(), "did not observe all outgoing LAN packets: {detected_probes:?}" @@ -96,19 +95,11 @@ pub async fn test_lockdown( rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, ) -> Result<(), Error> { - let lan_destination: SocketAddr = SocketAddr::new(IpAddr::V4(DUMMY_LAN_INTERFACE_IP), 1337); + // Take care not to use some bogus IP in the guest's subnet, lest we just send ARP requests + // These will fail if there's no actual host present + let lan_destination = "10.1.2.3:1234".parse().unwrap(); let inet_destination: SocketAddr = "1.1.1.1:1337".parse().unwrap(); - log::info!("Verify tunnel state: disconnected"); - assert_tunnel_state!(&mut mullvad_client, TunnelState::Disconnected { .. }); - - // Enable lockdown mode - // - mullvad_client - .set_block_when_disconnected(true) - .await - .expect("failed to enable lockdown mode"); - // Disable LAN sharing // @@ -119,20 +110,27 @@ pub async fn test_lockdown( .await .expect("failed to disable LAN sharing"); + // Enable lockdown mode + // + mullvad_client + .set_block_when_disconnected(true) + .await + .expect("failed to enable lockdown mode"); + // Ensure all destinations are unreachable // let default_interface = rpc.get_default_interface().await?; let detected_probes = - send_guest_probes(rpc.clone(), default_interface.clone(), lan_destination).await?; + send_guest_probes(rpc.clone(), default_interface.clone(), lan_destination).await; assert!( detected_probes.none(), "observed outgoing packets to LAN: {detected_probes:?}" ); let detected_probes = - send_guest_probes(rpc.clone(), default_interface.clone(), inet_destination).await?; + send_guest_probes(rpc.clone(), default_interface.clone(), inet_destination).await; assert!( detected_probes.none(), "observed outgoing packets to internet: {detected_probes:?}" @@ -152,14 +150,14 @@ pub async fn test_lockdown( // let detected_probes = - send_guest_probes(rpc.clone(), default_interface.clone(), lan_destination).await?; + send_guest_probes(rpc.clone(), default_interface.clone(), lan_destination).await; assert!( detected_probes.all(), "did not observe some outgoing packets: {detected_probes:?}" ); let detected_probes = - send_guest_probes(rpc.clone(), default_interface.clone(), inet_destination).await?; + send_guest_probes(rpc.clone(), default_interface.clone(), inet_destination).await; assert!( detected_probes.none(), "observed outgoing packets to internet: {detected_probes:?}" @@ -180,19 +178,11 @@ pub async fn test_lockdown( // Send traffic outside the tunnel to sanity check that the internet is *not* reachable via non- // tunnel interfaces. - let detected_probes = - send_guest_probes(rpc.clone(), default_interface, inet_destination).await?; + let detected_probes = send_guest_probes(rpc.clone(), default_interface, inet_destination).await; assert!( detected_probes.none(), "observed outgoing packets to internet: {detected_probes:?}" ); - // Disable lockdown mode - // - mullvad_client - .set_block_when_disconnected(false) - .await - .expect("failed to disable lockdown mode"); - Ok(()) } diff --git a/test/test-manager/src/tests/tunnel_state.rs b/test/test-manager/src/tests/tunnel_state.rs index edbe59509262..f75bc79498d3 100644 --- a/test/test-manager/src/tests/tunnel_state.rs +++ b/test/test-manager/src/tests/tunnel_state.rs @@ -5,10 +5,7 @@ use super::{ }, ui, Error, TestContext, }; -use crate::{ - assert_tunnel_state, tests::helpers::ping_sized_with_timeout, - vm::network::DUMMY_LAN_INTERFACE_IP, -}; +use crate::{assert_tunnel_state, tests::helpers::ping_sized_with_timeout}; use mullvad_management_interface::MullvadProxyClient; use mullvad_relay_selector::query::builder::RelayQueryBuilder; @@ -20,10 +17,7 @@ use mullvad_types::{ states::TunnelState, CustomTunnelEndpoint, }; -use std::{ - net::{IpAddr, SocketAddr}, - time::Duration, -}; +use std::{net::SocketAddr, time::Duration}; use talpid_types::net::{Endpoint, TransportProtocol, TunnelEndpoint, TunnelType}; use test_macro::test_function; use test_rpc::ServiceClient; @@ -143,7 +137,7 @@ pub async fn test_disconnected_state( .expect("failed to obtain non-tun interface"); let detected_probes = - send_guest_probes(rpc.clone(), non_tunnel_interface, inet_destination).await?; + send_guest_probes(rpc.clone(), non_tunnel_interface, inet_destination).await; assert!( detected_probes.all(), "did not see (all) outgoing packets to destination: {detected_probes:?}", @@ -181,9 +175,11 @@ pub async fn test_connecting_state( mut mullvad_client: MullvadProxyClient, ) -> Result<(), Error> { let inet_destination = "1.1.1.1:1337".parse().unwrap(); - let lan_destination: SocketAddr = SocketAddr::new(IpAddr::V4(DUMMY_LAN_INTERFACE_IP), 1337); + // Take care not to use some bogus IP in the guest's subnet, lest we just send ARP requests + // These will fail if there's no actual host present + let lan_destination = "10.1.2.3:1234".parse().unwrap(); let inet_dns = "1.1.1.1:53".parse().unwrap(); - let lan_dns: SocketAddr = SocketAddr::new(IpAddr::V4(DUMMY_LAN_INTERFACE_IP), 53); + let lan_dns = "10.1.2.3:53".parse().unwrap(); log::info!("Verify tunnel state: disconnected"); assert_tunnel_state!(&mut mullvad_client, TunnelState::Disconnected { .. }); @@ -225,25 +221,25 @@ pub async fn test_connecting_state( assert!( send_guest_probes(rpc.clone(), non_tunnel_interface.clone(), inet_destination) - .await? + .await .none(), "observed unexpected outgoing packets (inet)" ); assert!( send_guest_probes(rpc.clone(), non_tunnel_interface.clone(), lan_destination) - .await? + .await .none(), "observed unexpected outgoing packets (lan)" ); assert!( send_guest_probes(rpc.clone(), non_tunnel_interface.clone(), inet_dns) - .await? + .await .none(), "observed unexpected outgoing packets (DNS, inet)" ); assert!( send_guest_probes(rpc.clone(), non_tunnel_interface, lan_dns) - .await? + .await .none(), "observed unexpected outgoing packets (DNS, lan)" ); @@ -262,9 +258,11 @@ pub async fn test_error_state( mut mullvad_client: MullvadProxyClient, ) -> Result<(), Error> { let inet_destination = "1.1.1.1:1337".parse().unwrap(); - let lan_destination: SocketAddr = SocketAddr::new(IpAddr::V4(DUMMY_LAN_INTERFACE_IP), 1337); + // Take care not to use some bogus IP in the guest's subnet, lest we just send ARP requests + // These will fail if there's no actual host present + let lan_destination = "10.1.2.3:1234".parse().unwrap(); let inet_dns = "1.1.1.1:53".parse().unwrap(); - let lan_dns: SocketAddr = SocketAddr::new(IpAddr::V4(DUMMY_LAN_INTERFACE_IP), 53); + let lan_dns = "10.1.2.3:53".parse().unwrap(); log::info!("Verify tunnel state: disconnected"); assert_tunnel_state!(&mut mullvad_client, TunnelState::Disconnected { .. }); @@ -303,25 +301,25 @@ pub async fn test_error_state( assert!( send_guest_probes(rpc.clone(), default_interface.clone(), inet_destination) - .await? + .await .none(), "observed unexpected outgoing packets (inet)" ); assert!( send_guest_probes(rpc.clone(), default_interface.clone(), lan_destination) - .await? + .await .none(), "observed unexpected outgoing packets (lan)" ); assert!( send_guest_probes(rpc.clone(), default_interface.clone(), inet_dns) - .await? + .await .none(), "observed unexpected outgoing packets (DNS, inet)" ); assert!( send_guest_probes(rpc.clone(), default_interface, lan_dns) - .await? + .await .none(), "observed unexpected outgoing packets (DNS, lan)" ); @@ -389,7 +387,7 @@ pub async fn test_connected_state( .await .expect("failed to find non-tun interface"); - let detected_probes = send_guest_probes(rpc.clone(), nontun_iface, inet_destination).await?; + let detected_probes = send_guest_probes(rpc.clone(), nontun_iface, inet_destination).await; assert!( detected_probes.none(), "observed unexpected outgoing packets: {detected_probes:?}" From 84d15c421fc7efe55da0be45e09fc8d12fc2eddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 10 Apr 2024 00:24:27 +0200 Subject: [PATCH 095/214] Remove dummy interfaces --- test/test-manager/src/vm/network/linux.rs | 35 ++--------------------- test/test-manager/src/vm/network/macos.rs | 35 ----------------------- test/test-manager/src/vm/network/mod.rs | 3 +- 3 files changed, 3 insertions(+), 70 deletions(-) diff --git a/test/test-manager/src/vm/network/linux.rs b/test/test-manager/src/vm/network/linux.rs index f54d218b2f55..c767a35072c9 100644 --- a/test/test-manager/src/vm/network/linux.rs +++ b/test/test-manager/src/vm/network/linux.rs @@ -25,15 +25,6 @@ pub const BRIDGE_NAME: &str = "br-mullvadtest"; /// TAP interface used by the guest pub const TAP_NAME: &str = "tap-mullvadtest"; -/// Pingable dummy LAN interface (name) -pub const DUMMY_LAN_INTERFACE_NAME: &str = "lan-mullvadtest"; -/// Pingable dummy LAN interface (IP) -pub const DUMMY_LAN_INTERFACE_IP: Ipv4Addr = Ipv4Addr::new(172, 29, 1, 200); -/// Pingable dummy interface with public IP (name) -pub const DUMMY_INET_INTERFACE_NAME: &str = "net-mullvadtest"; -/// Pingable dummy interface with public IP (IP) -pub const DUMMY_INET_INTERFACE_IP: Ipv4Addr = Ipv4Addr::new(1, 3, 3, 7); - // Private key of the wireguard remote peer on host. const CUSTOM_TUN_REMOTE_PRIVKEY: &str = "gLvQuyqazziyf+pUCAFUgTnWIwn6fPE5MOReOqPEGHU="; // Public key of the wireguard remote peer on host. @@ -47,9 +38,9 @@ data_encoding_macro::base64_array!( "pub const CUSTOM_TUN_LOCAL_PRIVKEY" = "mPue6Xt0pdz4NRAhfQSp/SLKo7kV7DW+2zvBq0N9iUI=" ); -/// "Real" (non-tunnel) IP of the wireguard remote peer as defined in `setup-network.sh`. +/// "Real" (non-tunnel) IP of the wireguard remote peer on the host #[allow(dead_code)] -pub const CUSTOM_TUN_REMOTE_REAL_ADDR: Ipv4Addr = Ipv4Addr::new(172, 29, 1, 200); +pub const CUSTOM_TUN_REMOTE_REAL_ADDR: Ipv4Addr = Ipv4Addr::new(172, 29, 1, 1); /// Port of the wireguard remote peer as defined in `setup-network.sh`. #[allow(dead_code)] pub const CUSTOM_TUN_REMOTE_REAL_PORT: u16 = 51820; @@ -134,28 +125,6 @@ table ip mullvad_test_nat {{ )) .await?; - log::debug!("Set up pingable hosts"); - - run_ip_cmd(["link", "add", DUMMY_LAN_INTERFACE_NAME, "type", "dummy"]).await?; - run_ip_cmd([ - "addr", - "add", - "dev", - DUMMY_LAN_INTERFACE_NAME, - &DUMMY_LAN_INTERFACE_IP.to_string(), - ]) - .await?; - - run_ip_cmd(["link", "add", DUMMY_INET_INTERFACE_NAME, "type", "dummy"]).await?; - run_ip_cmd([ - "addr", - "add", - "dev", - DUMMY_INET_INTERFACE_NAME, - &DUMMY_INET_INTERFACE_IP.to_string(), - ]) - .await?; - log::debug!("Create WireGuard peer"); create_local_wireguard_peer().await?; diff --git a/test/test-manager/src/vm/network/macos.rs b/test/test-manager/src/vm/network/macos.rs index ef815f203a1e..78653df41c67 100644 --- a/test/test-manager/src/vm/network/macos.rs +++ b/test/test-manager/src/vm/network/macos.rs @@ -4,9 +4,6 @@ use anyhow::{anyhow, Context, Result}; use futures::future::{self, Either}; use tokio::{io::AsyncWriteExt, process::Command}; -/// Pingable dummy LAN interface (IP) -pub const DUMMY_LAN_INTERFACE_IP: Ipv4Addr = Ipv4Addr::new(192, 168, 64, 254); - // Private key of the wireguard remote peer on host. const CUSTOM_TUN_REMOTE_PRIVKEY: &str = "gLvQuyqazziyf+pUCAFUgTnWIwn6fPE5MOReOqPEGHU="; // Public key of the wireguard remote peer on host. @@ -50,41 +47,9 @@ pub async fn setup_test_network() -> Result<()> { .await .context("Failed to create WireGuard interface")?; - // A bit of trickery to detect when the bridge is available. - tokio::spawn(async move { - for _ in 0..30 { - let Ok(interface) = find_vm_bridge() else { - tokio::time::sleep(Duration::from_secs(1)).await; - continue; - }; - match create_dummy_interface(interface).await { - Ok(_) => log::debug!("Created dummy interface"), - Err(error) => log::error!("Failed to create dummy interface: {error}"), - } - return; - } - log::error!("Failed to create dummy interface: timed out"); - }); - Ok(()) } -async fn create_dummy_interface(interface: String) -> Result<()> { - let mut cmd = Command::new("/usr/bin/sudo"); - cmd.args([ - "/sbin/ifconfig", - &interface, - "alias", - &DUMMY_LAN_INTERFACE_IP.to_string(), - ]); - let output = cmd.output().await.context("Create dummy interface")?; - if output.status.success() { - Ok(()) - } else { - Err(anyhow!("ifconfig failed: {:?}", output.status.code())) - } -} - /// A hack to find the Tart bridge interface using `NON_TUN_GATEWAY`. /// It should be possible to retrieve this using the virtualization framework instead, /// but that requires an entitlement. diff --git a/test/test-manager/src/vm/network/mod.rs b/test/test-manager/src/vm/network/mod.rs index 944e24101302..de055376b09c 100644 --- a/test/test-manager/src/vm/network/mod.rs +++ b/test/test-manager/src/vm/network/mod.rs @@ -12,8 +12,7 @@ pub use macos as platform; pub use platform::{ CUSTOM_TUN_GATEWAY, CUSTOM_TUN_INTERFACE_NAME, CUSTOM_TUN_LOCAL_PRIVKEY, CUSTOM_TUN_LOCAL_TUN_ADDR, CUSTOM_TUN_REMOTE_PUBKEY, CUSTOM_TUN_REMOTE_REAL_ADDR, - CUSTOM_TUN_REMOTE_REAL_PORT, CUSTOM_TUN_REMOTE_TUN_ADDR, DUMMY_LAN_INTERFACE_IP, - NON_TUN_GATEWAY, + CUSTOM_TUN_REMOTE_REAL_PORT, CUSTOM_TUN_REMOTE_TUN_ADDR, NON_TUN_GATEWAY, }; /// Port on NON_TUN_GATEWAY that hosts a SOCKS5 server From 7f1efa0fe1677d622cee54bbd725b36454175605 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 17 Apr 2024 10:01:40 +0200 Subject: [PATCH 096/214] Fix formatting issues with swiftformat --- ios/MullvadVPNTests/LogRotationTests.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ios/MullvadVPNTests/LogRotationTests.swift b/ios/MullvadVPNTests/LogRotationTests.swift index e67687c3a2d1..4d7baf621232 100644 --- a/ios/MullvadVPNTests/LogRotationTests.swift +++ b/ios/MullvadVPNTests/LogRotationTests.swift @@ -37,16 +37,22 @@ final class LogRotationTests: XCTestCase { try writeDataToDisk(path: logPath, fileSize: 1000) } - try LogRotation.rotateLogs(logDirectory: directoryPath, options: LogRotation.Options( - storageSizeLimit: 5000, - oldestAllowedDate: .distantPast) + try LogRotation.rotateLogs( + logDirectory: directoryPath, + options: LogRotation.Options( + storageSizeLimit: 5000, + oldestAllowedDate: .distantPast + ) ) var logFileCount = try fileManager.contentsOfDirectory(atPath: directoryPath.relativePath).count XCTAssertEqual(logFileCount, 5) - try LogRotation.rotateLogs(logDirectory: directoryPath, options: LogRotation.Options( - storageSizeLimit: 3999, - oldestAllowedDate: .distantPast) + try LogRotation.rotateLogs( + logDirectory: directoryPath, + options: LogRotation.Options( + storageSizeLimit: 3999, + oldestAllowedDate: .distantPast + ) ) logFileCount = try fileManager.contentsOfDirectory(atPath: directoryPath.relativePath).count XCTAssertEqual(logFileCount, 3) From 7a366aed3d553e09bad8af6ab16c27734c9f6365 Mon Sep 17 00:00:00 2001 From: Albin Date: Wed, 17 Apr 2024 10:41:30 +0200 Subject: [PATCH 097/214] Align server IP override title with desktop app --- android/lib/resource/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml index 0956a97d0a79..a8e9978f71fe 100644 --- a/android/lib/resource/src/main/res/values/strings.xml +++ b/android/lib/resource/src/main/res/values/strings.xml @@ -318,7 +318,7 @@ Locations were changed for \"%s\" Not found Import - Server Ip overrides + Server IP override Overrides active Overrides inactive On some networks, where various types of censorship are being used, our server IP addresses are sometimes blocked. From 97e6d266ae660fb16688a290a4e0af0e29506813 Mon Sep 17 00:00:00 2001 From: Albin Date: Wed, 17 Apr 2024 10:45:59 +0200 Subject: [PATCH 098/214] Update translations --- android/lib/resource/src/main/res/values-da/strings.xml | 1 + android/lib/resource/src/main/res/values-de/strings.xml | 1 + android/lib/resource/src/main/res/values-es/strings.xml | 1 + android/lib/resource/src/main/res/values-fi/strings.xml | 1 + android/lib/resource/src/main/res/values-fr/strings.xml | 1 + android/lib/resource/src/main/res/values-it/strings.xml | 1 + android/lib/resource/src/main/res/values-ja/strings.xml | 1 + android/lib/resource/src/main/res/values-ko/strings.xml | 1 + android/lib/resource/src/main/res/values-my/strings.xml | 1 + android/lib/resource/src/main/res/values-nb/strings.xml | 1 + android/lib/resource/src/main/res/values-nl/strings.xml | 1 + android/lib/resource/src/main/res/values-pl/strings.xml | 1 + android/lib/resource/src/main/res/values-pt/strings.xml | 1 + android/lib/resource/src/main/res/values-ru/strings.xml | 1 + android/lib/resource/src/main/res/values-sv/strings.xml | 1 + android/lib/resource/src/main/res/values-th/strings.xml | 1 + android/lib/resource/src/main/res/values-tr/strings.xml | 1 + android/lib/resource/src/main/res/values-zh-rCN/strings.xml | 1 + android/lib/resource/src/main/res/values-zh-rTW/strings.xml | 1 + gui/locales/messages.pot | 3 --- 20 files changed, 19 insertions(+), 3 deletions(-) diff --git a/android/lib/resource/src/main/res/values-da/strings.xml b/android/lib/resource/src/main/res/values-da/strings.xml index b154eb73f36d..0afd16e0561b 100644 --- a/android/lib/resource/src/main/res/values-da/strings.xml +++ b/android/lib/resource/src/main/res/values-da/strings.xml @@ -215,6 +215,7 @@ Sendt Hvis det er nødvendigt, kontakter vi dig på %1$s Tak! + Server IP tilsidesættelse På nogle netværk, hvor der bruges forskellige typer censur, er vores server IP-adresser nogle gange blokeret. For at omgå dette kan du importere en fil eller en tekst, leveret af vores supportteam, med nye IP-adresser, der tilsidesætter standardadresserne på serverne i visningen Vælg placering. Hvis du har problemer med at oprette forbindelse til VPN-servere, bedes du kontakte support. diff --git a/android/lib/resource/src/main/res/values-de/strings.xml b/android/lib/resource/src/main/res/values-de/strings.xml index a25057c2e093..6109cb4d464d 100644 --- a/android/lib/resource/src/main/res/values-de/strings.xml +++ b/android/lib/resource/src/main/res/values-de/strings.xml @@ -215,6 +215,7 @@ Gesendet Bei Bedarf werden wir Sie über %1$s kontaktieren Danke! + Server-IP überschreiben In einigen Netzwerken, in denen verschiedene Arten der Zensur eingesetzt werden, werden die IP-Adressen unserer Server manchmal blockiert. Um dies zu umgehen, können Sie eine Datei oder einen von unserem Support-Team bereitgestellten Text mit neuen IP-Adressen importieren, die die Standardadressen der Server in der Ortsauswahl außer Kraft setzen. Wenn Sie Probleme mit der Verbindung zu VPN-Servern haben, wenden Sie sich bitte an den Support. diff --git a/android/lib/resource/src/main/res/values-es/strings.xml b/android/lib/resource/src/main/res/values-es/strings.xml index 76b00aa4f6bd..ed211d74a546 100644 --- a/android/lib/resource/src/main/res/values-es/strings.xml +++ b/android/lib/resource/src/main/res/values-es/strings.xml @@ -215,6 +215,7 @@ Enviado Si es necesario, le enviaremos un correo electrónico a %1$s ¡Gracias! + Anulación de IP de servidor En algunas redes, donde se aplican diversos tipos de censura, a veces se bloquean las direcciones IP de nuestro servidor. Para eludir esto, puede importar un archivo o texto, suministrado por nuestro equipo de asistencia, con nuevas direcciones IP que anulan las direcciones predeterminadas de los servidores en la vista Seleccionar ubicación. Si tiene problemas para conectarse a los servidores VPN, póngase en contacto con el servicio de asistencia. diff --git a/android/lib/resource/src/main/res/values-fi/strings.xml b/android/lib/resource/src/main/res/values-fi/strings.xml index 469e37c2ac79..52df0e8d1635 100644 --- a/android/lib/resource/src/main/res/values-fi/strings.xml +++ b/android/lib/resource/src/main/res/values-fi/strings.xml @@ -215,6 +215,7 @@ Lähetetty Tarvittaessa otamme sinuun yhteyttä osoitteeseen %1$s Kiitos! + Palvelimen IP-osoitteen ohitus Palvelimiemme IP-osoitteet estetään toisinaan joissakin useita erityyppistä sensurointimenetelmiä käyttävissä verkoissa. Voit kiertää estot tuomalla tukitiimimme toimittaman tiedoston tai tekstin, josta löytyy uusia, palvelimien oletusosoitteet sijainnin valintanäkymässä ohittavia IP-osoitteita. Jos sinulla on ongelmia yhteyden muodostamisessa VPN-palvelimiin, ota yhteyttä tukeen. diff --git a/android/lib/resource/src/main/res/values-fr/strings.xml b/android/lib/resource/src/main/res/values-fr/strings.xml index c3a015dadfe0..584c700bf311 100644 --- a/android/lib/resource/src/main/res/values-fr/strings.xml +++ b/android/lib/resource/src/main/res/values-fr/strings.xml @@ -215,6 +215,7 @@ Envoyé Si nécessaire, nous vous contacterons à l\'adresse %1$s Merci ! + Substitution d\'IP de serveur Sur certains réseaux, où divers types de censure sont utilisés, les adresses IP de notre serveur sont parfois bloquées. Pour contourner ce problème, vous pouvez importer un fichier ou du texte fourni par notre équipe d\'assistance, avec de nouvelles adresses IP qui remplacent les adresses par défaut des serveurs dans la vue Sélectionner un emplacement. Si vous rencontrez des problèmes de connexion aux serveurs VPN, veuillez contacter l\'assistance. diff --git a/android/lib/resource/src/main/res/values-it/strings.xml b/android/lib/resource/src/main/res/values-it/strings.xml index 5cfe7da92ee6..5ec9034143cc 100644 --- a/android/lib/resource/src/main/res/values-it/strings.xml +++ b/android/lib/resource/src/main/res/values-it/strings.xml @@ -215,6 +215,7 @@ Inviato Se necessario, ti contatteremo all\'indirizzo %1$s Grazie! + Sovrascritture IP server Su alcune reti, dove vengono utilizzati vari tipi di censura, gli indirizzi IP dei nostri server vengono talvolta bloccati. Per aggirare questo problema, puoi importare un file o un testo, fornito dal nostro team di supporto, con nuovi indirizzi IP che sovrascrivono gli indirizzi predefiniti dei server nella vista Seleziona posizione. Se riscontri problemi di connessione ai server VPN, contatta l\'assistenza. diff --git a/android/lib/resource/src/main/res/values-ja/strings.xml b/android/lib/resource/src/main/res/values-ja/strings.xml index 773a2de378d7..67ecf7f2a907 100644 --- a/android/lib/resource/src/main/res/values-ja/strings.xml +++ b/android/lib/resource/src/main/res/values-ja/strings.xml @@ -215,6 +215,7 @@ 送信済み 必要に応じて %1$s 宛にご連絡します  ありがとうございます! + サーバーIPのオーバーライド 各種の検閲が使用されている一部のネットワークでは、サーバーIPアドレスがブロックされる場合があります。 これを回避するには、「場所を選択」ビューでサポートチームが提供したサーバーのデフォルトアドレスをオーバーライドする新しいIPアドレスを含むファイルまたはテキストをインポートできます。 VPNサーバーへの接続に問題が生じている場合は、サポートにお問い合わせください。 diff --git a/android/lib/resource/src/main/res/values-ko/strings.xml b/android/lib/resource/src/main/res/values-ko/strings.xml index b4ea3edaa6c0..25f7213cb6f1 100644 --- a/android/lib/resource/src/main/res/values-ko/strings.xml +++ b/android/lib/resource/src/main/res/values-ko/strings.xml @@ -215,6 +215,7 @@ 전송 완료 필요한 경우 %1$s(으)로 연락드리겠습니다. 감사합니다! + 서버 IP 재정의 다양한 유형의 검열이 사용되고 있는 일부 네트워크에서는 때때로 당사 서버 IP 주소가 차단됩니다. 이를 우회하려면 \'위치 선택\' 보기에서 서버의 기본 주소를 재정의하는 새 IP 주소를 사용하여 지원 팀에서 제공한 파일이나 텍스트를 가져올 수 있습니다. VPN 서버 연결에 문제가 있는 경우 지원 팀에 문의하세요. diff --git a/android/lib/resource/src/main/res/values-my/strings.xml b/android/lib/resource/src/main/res/values-my/strings.xml index f5397f987746..1ec1d2c14315 100644 --- a/android/lib/resource/src/main/res/values-my/strings.xml +++ b/android/lib/resource/src/main/res/values-my/strings.xml @@ -215,6 +215,7 @@ ပို့ပြီး လိုအပ်ပါက %1$s မှတစ်ဆင့် ကျွန်ုပ်တို့ထံ ဆက်သွယ်ပါ ကျေးဇူးတင်ပါသည်။ + ဆာဗာ IP ကျော်လွန် ပယ်ဖျက်မှု အမျိုးအမျိုးသော စိစစ်ဖြတ်တောက်မှု အမျိုးအစားများ အသုံးပြုသည့် ကွန်ရက်အချို့တွင် ကျွန်ုပ်တို့၏ ဆာဗာ IP လိပ်စာများကို တစ်ခါတစ်ရံ ပိတ်ဆို့ထားပါသည်။ ဤသည်ကို ရှောင်လွှဲရန် ကျွန်ုပ်တို့ အကူအညီပေးရေးအဖွဲ့မှ ပေးထားသော တည်နေရာ ရွေးရန် ပြသမှုအတွင်းရှိ ဆာဗာများ၏ ပုံသေ လိပ်စာများကို ကျော်လွန် ပယ်ဖျက်သည့် IP လိပ်စာအသစ်များဖြင့် ဖိုင် သို့မဟုတ် စာသားကို သင် ထည့်သွင်းနိုင်ပါသည်။ VPN ဆာဗာများကို ချိတ်ဆက်ရာတွင် ပြဿနာများရှိနေပါက အကူအညီပေးရေးအဖွဲ့ကို ဆက်သွယ်ပါ။ diff --git a/android/lib/resource/src/main/res/values-nb/strings.xml b/android/lib/resource/src/main/res/values-nb/strings.xml index 334d6fd025e0..ebc5ef76215f 100644 --- a/android/lib/resource/src/main/res/values-nb/strings.xml +++ b/android/lib/resource/src/main/res/values-nb/strings.xml @@ -215,6 +215,7 @@ Sendt Vi vil kontakte deg på %1$s ved behov Takk! + Overstyring av server-IP På enkelte nettverk der det brukes ulike typer sensur, kan server-IP-adressene av og til være blokkerte. For å omgå dette kan du importere en fil eller tekst, som du har fått fra kundestøtteteamet, med nye IP-adresser som overstyrer standardadressene til serverne i «Velg plassering». Hvis du har mistet tilkoblingen til VPN-serverne, kan du ta kontak med kundestøtten. diff --git a/android/lib/resource/src/main/res/values-nl/strings.xml b/android/lib/resource/src/main/res/values-nl/strings.xml index 5c7beb43393f..9402bf2a4cb2 100644 --- a/android/lib/resource/src/main/res/values-nl/strings.xml +++ b/android/lib/resource/src/main/res/values-nl/strings.xml @@ -215,6 +215,7 @@ Verzonden Indien nodig nemen we u contact op via %1$s Bedankt! + Overschrijving van server-IP-adressen Op sommige netwerken, waar verschillende soorten censuur worden gebruikt, worden onze server-IP-adressen soms geblokkeerd. Om dit te omzeilen, kunt u een door ons ondersteuningsteam verstrekt bestand of tekst importeren, met nieuwe IP-adressen die de standaardadressen van de servers in de weergave Locatie selecteren overschrijven. Als u problemen hebt met het verbinden met VPN-servers, neem dan contact op met de ondersteuning. diff --git a/android/lib/resource/src/main/res/values-pl/strings.xml b/android/lib/resource/src/main/res/values-pl/strings.xml index 1bce8833fc4b..1ab45e2d9cbd 100644 --- a/android/lib/resource/src/main/res/values-pl/strings.xml +++ b/android/lib/resource/src/main/res/values-pl/strings.xml @@ -215,6 +215,7 @@ Wysłano W razie potrzeby skontaktujemy się z Tobą pod adresem %1$s Dziękujemy! + Zastąpienie adresu IP serwera W niektórych sieciach, w których stosowane są różnego rodzaju cenzury, adresy IP naszych serwerów są czasami blokowane. Aby obejść ten problem, można zaimportować plik lub tekst dostarczony przez nasz zespół pomocy technicznej, zawierający nowe adresy IP, które zastępują domyślne adresy serwerów w widoku Wybierz lokalizację. Jeśli masz problemy z łączeniem się z serwerami VPN, skontaktuj się z pomocą techniczną. diff --git a/android/lib/resource/src/main/res/values-pt/strings.xml b/android/lib/resource/src/main/res/values-pt/strings.xml index 03c8aad24b28..fde7b4e0b19b 100644 --- a/android/lib/resource/src/main/res/values-pt/strings.xml +++ b/android/lib/resource/src/main/res/values-pt/strings.xml @@ -215,6 +215,7 @@ Enviado Se necessário, iremos contactá-lo através de %1$s Obrigado! + Substituição de IP de servidor Em algumas redes, onde são utilizados vários tipos de censura, os endereços IP dos nossos servidores são por vezes bloqueados. Para contornar esta situação, pode importar um ficheiro ou texto, fornecido pela nossa equipa de apoio, com novos endereços IP que substituem os endereços padrão dos servidores na vista Selecionar local. Se tiver problemas em ligar-se aos servidores VPN, contacte o apoio. diff --git a/android/lib/resource/src/main/res/values-ru/strings.xml b/android/lib/resource/src/main/res/values-ru/strings.xml index c96888d668a6..03bd3a85af7e 100644 --- a/android/lib/resource/src/main/res/values-ru/strings.xml +++ b/android/lib/resource/src/main/res/values-ru/strings.xml @@ -215,6 +215,7 @@ Отправлено При необходимости мы свяжемся с вами по адресу %1$s Спасибо! + Переопределение IP-адреса сервера В некоторых сетях, где используются различные виды цензуры, IP-адреса наших серверов иногда блокируются. Чтобы обойти эту проблему, можно импортировать файл или текст, предоставленный нашей службой поддержки, с новыми IP-адресами, которые заменяют адреса серверов по умолчанию в представлении «Выбор местоположения». Если у вас возникли проблемы с подключением к VPN-серверам, обратитесь в службу поддержки. diff --git a/android/lib/resource/src/main/res/values-sv/strings.xml b/android/lib/resource/src/main/res/values-sv/strings.xml index 402b7663b9d5..fbe8433920ec 100644 --- a/android/lib/resource/src/main/res/values-sv/strings.xml +++ b/android/lib/resource/src/main/res/values-sv/strings.xml @@ -215,6 +215,7 @@ Skickat Om det behövs kontaktar vi dig på %1$s Tack! + Åsidosättning av server-IP På vissa nätverk där olika typer av censureringar används blockeras blir ibland vår servers IP-adresser blockerade. För att kringgå detta kan du importera en fil eller text, som tillhandahålls av vårt supportteam, med nya IP-adresser som åsidosätter servrarnas standardadresser i Välj platsvy. Kontakta supporten om du har problem med att ansluta till VPN-servrar. diff --git a/android/lib/resource/src/main/res/values-th/strings.xml b/android/lib/resource/src/main/res/values-th/strings.xml index e9c733986242..0ad6b06a53ae 100644 --- a/android/lib/resource/src/main/res/values-th/strings.xml +++ b/android/lib/resource/src/main/res/values-th/strings.xml @@ -215,6 +215,7 @@ ส่ง เราจะติดต่อคุณไปทาง %1$s ในกรณีจำเป็น ขอบคุณ! + โอเวอร์ไรด์เซิร์ฟเวอร์ IP บางครั้งที่อยู่ IP เซิร์ฟเวอร์ของเราอาจถูกบล็อก ในบางเครือข่ายที่มีการใช้งานเซ็นเซอร์หลายประเภท ในการหลีกเลี่ยงปัญหานี้ คุณสามารถนำเข้าไฟล์หรือข้อความที่ได้รับจากทีมสนับสนุนของเรา พร้อมด้วยที่อยู่ IP ใหม่ที่โอเวอร์ไรด์ที่อยู่เริ่มต้นของเซิร์ฟเวอร์ในมุมมองเลือกตำแหน่งที่ตั้ง หากคุณประสบปัญหาในการเชื่อมต่อกับเซิร์ฟเวอร์ VPN โปรดติดต่อฝ่ายสนับสนุน diff --git a/android/lib/resource/src/main/res/values-tr/strings.xml b/android/lib/resource/src/main/res/values-tr/strings.xml index 0f9aaf615194..d7d534209d8e 100644 --- a/android/lib/resource/src/main/res/values-tr/strings.xml +++ b/android/lib/resource/src/main/res/values-tr/strings.xml @@ -215,6 +215,7 @@ Gönderildi Gerektiğinde sizinle %1$s adresinden iletişime geçeceğiz Teşekkürler! + Sunucu IP\'sini geçersiz kılma Farklı sansür türlerinin kullanıldığı bazı ağlarda sunucu IP adreslerimiz zaman zaman engellenir. Bu sınırlamadan kaçınmak için Konum Seç görünümündeki varsayılan sunucu adreslerini geçersiz kılan yeni IP adreslerine sahip bir dosyayı veya metni (destek ekibimiz tarafından sağlanır) içe aktarabilirsiniz. VPN sunucularına bağlanırken sorun yaşıyorsanız lütfen destek ekibiyle iletişime geçin. diff --git a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml index bef4e2ce873b..0b14a5ba0ec6 100644 --- a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml @@ -215,6 +215,7 @@ 已发送 如果需要,我们将通过 %1$s 与您联系 谢谢! + 服务器 IP 覆盖 在某些使用各类审查的网络上,我们的服务器 IP 地址有时会被阻止。 为了避免这种情况,您可以导入由我们的支持团队提供的文件或文本,其中的新 IP 地址会覆盖“选择位置”视图中服务器的默认地址。 如果您在连接到 VPN 服务器时遇到问题,请联系支持团队。 diff --git a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml index 799a0c1c41b8..2b27c6493058 100644 --- a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml @@ -215,6 +215,7 @@ 已傳送 如有需要,我們將透過 %1$s 與您聯絡 謝謝! + 伺服器 IP 覆寫 在某些採用了各類審查功能的網路上,我們的伺服器 IP 位址有時會遭到封鎖。 為了避免這種情況,您可以匯入由我們支援團隊所提供的檔案或文字,其中的新 IP 位址會覆蓋「選取位置」視圖中伺服器的預設位址。 如果您在連線至 VPN 伺服器時遇到問題,請聯絡支援人員。 diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index 7a0c2fc456f0..b1c39c2d2d99 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -2353,9 +2353,6 @@ msgstr "" msgid "Secured" msgstr "" -msgid "Server Ip overrides" -msgstr "" - msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "" From 4838c055d1280d6950d968ef37562649defc6304 Mon Sep 17 00:00:00 2001 From: Emils Date: Fri, 22 Sep 2023 14:28:07 +0200 Subject: [PATCH 099/214] Add a header to log files --- ios/MullvadLogging/Logging.swift | 3 ++- ios/MullvadVPN.xcodeproj/project.pbxproj | 2 ++ ios/MullvadVPN/AppDelegate.swift | 4 ++- ios/MullvadVPNTests/LoggingTests.swift | 31 ++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 ios/MullvadVPNTests/LoggingTests.swift diff --git a/ios/MullvadLogging/Logging.swift b/ios/MullvadLogging/Logging.swift index 76c3c57f8c8a..c6d86a549baa 100644 --- a/ios/MullvadLogging/Logging.swift +++ b/ios/MullvadLogging/Logging.swift @@ -49,11 +49,12 @@ public struct LoggerBuilder { outputs.append(.osLogOutput(subsystem)) } - public func install() { + public func install(header: String) { LoggingSystem.bootstrap { label -> LogHandler in let logHandlers: [LogHandler] = outputs.map { output in switch output { case let .fileOutput(stream): + stream.write("\(header)\n") return CustomFormatLogHandler(label: label, streams: [stream]) case let .osLogOutput(subsystem): diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index fca1fe112cd7..1312c0c182be 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -2948,6 +2948,7 @@ 7A83A0C52B29A750008B5CE7 /* APIAccessMethodsTests.swift */, A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */, A9EC20E72A5D3A8C0040D56E /* CoordinatesTests.swift */, + 01168B192BBEE7F800D5F382 /* LoggingTests.swift */, 5896AE85246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift */, 58915D622A25F8400066445B /* DeviceCheckOperationTests.swift */, A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */, @@ -5112,6 +5113,7 @@ 7A3FD1B52AD4465A0042BEA6 /* AppMessageHandlerTests.swift in Sources */, 58C7A4702A8649ED0060C66F /* PingerTests.swift in Sources */, A97D25B22B0CB02D00946B2D /* ProtocolObfuscatorTests.swift in Sources */, + 01168B1A2BBEE7F800D5F382 /* LoggingTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index be750d852483..dda8fdc5e49b 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -357,9 +357,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.mainApp.bundleIdentifier) #endif - loggerBuilder.install() + loggerBuilder.install(header: "TODO: Add version info here") logger = Logger(label: "AppDelegate") + + loggerBuilder.logLevel = .debug } private func addApplicationNotifications(application: UIApplication) { diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift new file mode 100644 index 000000000000..3c1abefbc108 --- /dev/null +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -0,0 +1,31 @@ +// +// LoggingTests.swift +// MullvadVPNTests +// +// Created by Emils on 04/04/2024. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import XCTest +@testable import MullvadLogging + +class MullvadLoggingTests: XCTestCase { + func testLogHeader() { + let dummySig = "test-sgi"; + let testFileName = "test" + let expectedHeader = "Header of a log file" + + var builder = LoggerBuilder() + try! builder.addFileOutput(securityGroupIdentifier: dummySig, basename: testFileName) + + builder.install(header: expectedHeader) + + let logFileUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: dummySig)!.appendingPathComponent("Logs", isDirectory: true).appendingPathComponent("\(testFileName).log", isDirectory: false) + + let contents = String(decoding: try! Data(contentsOf: logFileUrl), as: UTF8.self) + + XCTAssert(contents.hasPrefix(expectedHeader)) + XCTAssertEqual("\(expectedHeader)\n", contents) + } +} From 5b1ad96a88863d570191caf33990e00dd67dbe2b Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Tue, 9 Apr 2024 16:15:22 +0200 Subject: [PATCH 100/214] Fix tests for log header --- ios/MullvadVPN.xcodeproj/project.pbxproj | 6 ++-- ios/MullvadVPNTests/LoggingTests.swift | 39 +++++++++++++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 1312c0c182be..4a4ba51e8e2f 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 449EB9FD2B95F8AD00DFA4EB /* DeviceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EB9FC2B95F8AD00DFA4EB /* DeviceMock.swift */; }; 449EB9FF2B95FF2500DFA4EB /* AccountMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */; }; 449EBA262B975B9700DFA4EB /* PostQuantumKeyReceiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EBA252B975B9700DFA4EB /* PostQuantumKeyReceiving.swift */; }; + 44B02E3B2BC5732D008EDF34 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44B02E3A2BC5732D008EDF34 /* LoggingTests.swift */; }; 44DD7D242B6CFFD70005F67F /* StartTunnelOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */; }; 44DD7D272B6D18FB0005F67F /* MockTunnelInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */; }; 44DD7D292B7113CA0005F67F /* MockTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D282B7113CA0005F67F /* MockTunnel.swift */; }; @@ -1328,6 +1329,7 @@ 449EB9FC2B95F8AD00DFA4EB /* DeviceMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceMock.swift; sourceTree = ""; }; 449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountMock.swift; sourceTree = ""; }; 449EBA252B975B9700DFA4EB /* PostQuantumKeyReceiving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyReceiving.swift; sourceTree = ""; }; + 44B02E3A2BC5732D008EDF34 /* LoggingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = ""; }; 44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartTunnelOperationTests.swift; sourceTree = ""; }; 44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnelInteractor.swift; sourceTree = ""; }; 44DD7D282B7113CA0005F67F /* MockTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnel.swift; sourceTree = ""; }; @@ -2948,7 +2950,6 @@ 7A83A0C52B29A750008B5CE7 /* APIAccessMethodsTests.swift */, A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */, A9EC20E72A5D3A8C0040D56E /* CoordinatesTests.swift */, - 01168B192BBEE7F800D5F382 /* LoggingTests.swift */, 5896AE85246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift */, 58915D622A25F8400066445B /* DeviceCheckOperationTests.swift */, A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */, @@ -2963,6 +2964,7 @@ 7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */, 7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */, 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */, + 44B02E3A2BC5732D008EDF34 /* LoggingTests.swift */, A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */, 58C3FA652A38549D006A450A /* MockFileCache.swift */, F09D04B42AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift */, @@ -4980,6 +4982,7 @@ 7AA513862BC91C6B00D081A4 /* LogRotationTests.swift in Sources */, F04413622BA45CE30018A6EE /* CustomListLocationNodeBuilder.swift in Sources */, A9A5FA302ACB05160083449F /* InputTextFormatterTests.swift in Sources */, + 44B02E3B2BC5732D008EDF34 /* LoggingTests.swift in Sources */, F0B0E6972AFE6E7E001DC66B /* XCTest+Async.swift in Sources */, 449EB9FF2B95FF2500DFA4EB /* AccountMock.swift in Sources */, 7ADCB2DA2B6A730400C88F89 /* IPOverrideRepositoryStub.swift in Sources */, @@ -5113,7 +5116,6 @@ 7A3FD1B52AD4465A0042BEA6 /* AppMessageHandlerTests.swift in Sources */, 58C7A4702A8649ED0060C66F /* PingerTests.swift in Sources */, A97D25B22B0CB02D00946B2D /* ProtocolObfuscatorTests.swift in Sources */, - 01168B1A2BBEE7F800D5F382 /* LoggingTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift index 3c1abefbc108..be21d14b7c7d 100644 --- a/ios/MullvadVPNTests/LoggingTests.swift +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -11,21 +11,44 @@ import XCTest @testable import MullvadLogging class MullvadLoggingTests: XCTestCase { + + func temporaryFileURL() -> URL { + + // Create a URL for an unique file in the system's temporary directory. + let directory = NSTemporaryDirectory() + let filename = UUID().uuidString + let fileURL = URL(fileURLWithPath: directory).appendingPathComponent(filename) + + // Add a teardown block to delete any file at `fileURL`. + addTeardownBlock { + do { + let fileManager = FileManager.default + // Check that the file exists before trying to delete it. + if fileManager.fileExists(atPath: fileURL.path) { + // Perform the deletion. + try fileManager.removeItem(at: fileURL) + } + } catch { + // Treat any errors during file deletion as a test failure. + XCTFail("Error while deleting temporary file: \(error)") + } + } + + // Return the temporary file URL for use in a test method. + return fileURL + } + func testLogHeader() { - let dummySig = "test-sgi"; - let testFileName = "test" let expectedHeader = "Header of a log file" var builder = LoggerBuilder() - try! builder.addFileOutput(securityGroupIdentifier: dummySig, basename: testFileName) + let fileURL = temporaryFileURL() + builder.addFileOutput(fileURL: fileURL) builder.install(header: expectedHeader) - - let logFileUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: dummySig)!.appendingPathComponent("Logs", isDirectory: true).appendingPathComponent("\(testFileName).log", isDirectory: false) - - let contents = String(decoding: try! Data(contentsOf: logFileUrl), as: UTF8.self) + + let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) XCTAssert(contents.hasPrefix(expectedHeader)) - XCTAssertEqual("\(expectedHeader)\n", contents) } } From 4654acb952e4b4024329f99bb0b4f780154bdd12 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Tue, 9 Apr 2024 19:59:40 +0200 Subject: [PATCH 101/214] Add bundle version to log header --- ios/MullvadVPN.xcodeproj/project.pbxproj | 2 ++ ios/MullvadVPN/AppDelegate.swift | 2 +- .../PacketTunnelProvider/PacketTunnelProvider.swift | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 4a4ba51e8e2f..aa8c56501933 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 449EB9FF2B95FF2500DFA4EB /* AccountMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */; }; 449EBA262B975B9700DFA4EB /* PostQuantumKeyReceiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EBA252B975B9700DFA4EB /* PostQuantumKeyReceiving.swift */; }; 44B02E3B2BC5732D008EDF34 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44B02E3A2BC5732D008EDF34 /* LoggingTests.swift */; }; + 44B02E3C2BC5B8A5008EDF34 /* Bundle+ProductVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */; }; 44DD7D242B6CFFD70005F67F /* StartTunnelOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */; }; 44DD7D272B6D18FB0005F67F /* MockTunnelInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */; }; 44DD7D292B7113CA0005F67F /* MockTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D282B7113CA0005F67F /* MockTunnel.swift */; }; @@ -5486,6 +5487,7 @@ files = ( 581DA2762A1E2FD10046ED47 /* WgKeyRotation.swift in Sources */, 580810E52A30E13A00B74552 /* DeviceStateAccessorProtocol.swift in Sources */, + 44B02E3C2BC5B8A5008EDF34 /* Bundle+ProductVersion.swift in Sources */, 580810E82A30E15500B74552 /* DeviceCheckRemoteServiceProtocol.swift in Sources */, 58C9B8CE2ABB252E00040B46 /* DeviceCheck.swift in Sources */, 58915D682A25FA080066445B /* DeviceCheckRemoteService.swift in Sources */, diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index dda8fdc5e49b..e79a1226a898 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -357,7 +357,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.mainApp.bundleIdentifier) #endif - loggerBuilder.install(header: "TODO: Add version info here") + loggerBuilder.install(header: "MullvadVPN version \(Bundle.main.productVersion)") logger = Logger(label: "AppDelegate") diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index d4745737309f..7cdc35715095 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -167,7 +167,7 @@ extension PacketTunnelProvider { #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.packetTunnel.bundleIdentifier) #endif - loggerBuilder.install() + loggerBuilder.install(header: "PacketTunnel version \(Bundle.main.productVersion)") } private func parseStartOptions(_ options: [String: NSObject]) -> StartOptions { From 6cd934154cda7e4e33b6a62dbdec982a96a219c8 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Thu, 11 Apr 2024 12:55:52 +0200 Subject: [PATCH 102/214] Move log file header writing to LogFileOutputStream --- ios/MullvadLogging/LogFileOutputStream.swift | 25 +++++++++++++------ ios/MullvadLogging/Logging.swift | 10 +++++--- ios/MullvadVPN/AppDelegate.swift | 6 ++--- ios/MullvadVPNTests/LoggingTests.swift | 22 ++++++++-------- .../PacketTunnelProvider.swift | 4 +-- 5 files changed, 39 insertions(+), 28 deletions(-) diff --git a/ios/MullvadLogging/LogFileOutputStream.swift b/ios/MullvadLogging/LogFileOutputStream.swift index a3b7ca4caa6a..26b059638e81 100644 --- a/ios/MullvadLogging/LogFileOutputStream.swift +++ b/ios/MullvadLogging/LogFileOutputStream.swift @@ -19,6 +19,7 @@ class LogFileOutputStream: TextOutputStream { private let fileURL: URL private let encoding: String.Encoding private let maxBufferCapacity: Int + private let fileHeader: String private var state: State = .closed { didSet { @@ -44,10 +45,11 @@ class LogFileOutputStream: TextOutputStream { case waitingToReopen } - init(fileURL: URL, encoding: String.Encoding = .utf8, maxBufferCapacity: Int = 16 * 1024) { + init(fileURL: URL, header: String, encoding: String.Encoding = .utf8, maxBufferCapacity: Int = 16 * 1024) { self.fileURL = fileURL self.encoding = encoding self.maxBufferCapacity = maxBufferCapacity + self.fileHeader = header } deinit { @@ -66,7 +68,7 @@ class LogFileOutputStream: TextOutputStream { switch state { case .closed: do { - let fileHandle = try openFile() + let fileHandle = try openFileWithHeader(fileHeader) state = .opened(fileHandle) try write(fileHandle: fileHandle, data: data) } catch { @@ -137,15 +139,22 @@ class LogFileOutputStream: TextOutputStream { timer = nil } + private func openFileWithHeader(_ header: String) throws -> FileHandle { + let fileHandle = try openFile() + + let messageData = + "\(header)\n" + .data(using: encoding, allowLossyConversion: true)! + try write(fileHandle: fileHandle, data: messageData) + + return fileHandle + } + private func reopenFile() { do { - let fileHandle = try openFile() - // Write a message indicating that the file was reopened. - let messageData = - "\n" - .data(using: encoding, allowLossyConversion: true)! - try write(fileHandle: fileHandle, data: messageData) + let fileHandle = + try openFileWithHeader("") // Write all buffered messages. if !buffer.isEmpty { diff --git a/ios/MullvadLogging/Logging.swift b/ios/MullvadLogging/Logging.swift index c6d86a549baa..b8ad66c1916a 100644 --- a/ios/MullvadLogging/Logging.swift +++ b/ios/MullvadLogging/Logging.swift @@ -21,8 +21,11 @@ public struct LoggerBuilder { public var metadata: Logger.Metadata = [:] public var logLevel: Logger.Level = .debug + public var header: String - public init() {} + public init(header: String) { + self.header = header + } public mutating func addFileOutput(fileURL: URL) { let logsDirectoryURL = fileURL.deletingLastPathComponent() @@ -42,19 +45,18 @@ public struct LoggerBuilder { logRotationErrors.append(error) } - outputs.append(.fileOutput(LogFileOutputStream(fileURL: fileURL))) + outputs.append(.fileOutput(LogFileOutputStream(fileURL: fileURL, header: header))) } public mutating func addOSLogOutput(subsystem: String) { outputs.append(.osLogOutput(subsystem)) } - public func install(header: String) { + public func install() { LoggingSystem.bootstrap { label -> LogHandler in let logHandlers: [LogHandler] = outputs.map { output in switch output { case let .fileOutput(stream): - stream.write("\(header)\n") return CustomFormatLogHandler(label: label, streams: [stream]) case let .osLogOutput(subsystem): diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index e79a1226a898..88c9b0e4e62e 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -352,12 +352,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: - Private private func configureLogging() { - var loggerBuilder = LoggerBuilder() - loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.newLogFileURL(for: .mainApp)) + var loggerBuilder = LoggerBuilder(header: "MullvadVPN version \(Bundle.main.productVersion)") + loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.logFileURL(for: .mainApp)) #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.mainApp.bundleIdentifier) #endif - loggerBuilder.install(header: "MullvadVPN version \(Bundle.main.productVersion)") + loggerBuilder.install() logger = Logger(label: "AppDelegate") diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift index be21d14b7c7d..e422f3d6058e 100644 --- a/ios/MullvadVPNTests/LoggingTests.swift +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -7,18 +7,16 @@ // import Foundation -import XCTest @testable import MullvadLogging +import XCTest class MullvadLoggingTests: XCTestCase { - func temporaryFileURL() -> URL { - // Create a URL for an unique file in the system's temporary directory. let directory = NSTemporaryDirectory() let filename = UUID().uuidString let fileURL = URL(fileURLWithPath: directory).appendingPathComponent(filename) - + // Add a teardown block to delete any file at `fileURL`. addTeardownBlock { do { @@ -33,22 +31,24 @@ class MullvadLoggingTests: XCTestCase { XCTFail("Error while deleting temporary file: \(error)") } } - + // Return the temporary file URL for use in a test method. return fileURL } func testLogHeader() { let expectedHeader = "Header of a log file" - - var builder = LoggerBuilder() + + var builder = LoggerBuilder(header: expectedHeader) let fileURL = temporaryFileURL() builder.addFileOutput(fileURL: fileURL) - - builder.install(header: expectedHeader) - + + builder.install() + + Logger(label: "test").info(":-P") + let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) - + XCTAssert(contents.hasPrefix(expectedHeader)) } } diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 7cdc35715095..628828f5282c 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -160,14 +160,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider { extension PacketTunnelProvider { private static func configureLogging() { - var loggerBuilder = LoggerBuilder() + var loggerBuilder = LoggerBuilder(header: "PacketTunnel version \(Bundle.main.productVersion)") let pid = ProcessInfo.processInfo.processIdentifier loggerBuilder.metadata["pid"] = .string("\(pid)") loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.newLogFileURL(for: .packetTunnel)) #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.packetTunnel.bundleIdentifier) #endif - loggerBuilder.install(header: "PacketTunnel version \(Bundle.main.productVersion)") + loggerBuilder.install() } private func parseStartOptions(_ options: [String: NSObject]) -> StartOptions { From 983f48f04e8b73f8222a8405237dcc3454e4e6ba Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Thu, 11 Apr 2024 12:58:37 +0200 Subject: [PATCH 103/214] Disable failing test --- ios/MullvadVPNTests/LoggingTests.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift index e422f3d6058e..b4f18c751322 100644 --- a/ios/MullvadVPNTests/LoggingTests.swift +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -47,8 +47,9 @@ class MullvadLoggingTests: XCTestCase { Logger(label: "test").info(":-P") - let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) - - XCTAssert(contents.hasPrefix(expectedHeader)) +// For some reason, reading the file fails here, despite the file existing. Manual inspection reveals the file to have the correct header. ¯\_(ツ)_/¯ +// let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) +// +// XCTAssert(contents.hasPrefix(expectedHeader)) } } From bafb84a0bddf56962e0940e13a39cce131a49f83 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Thu, 11 Apr 2024 14:04:39 +0200 Subject: [PATCH 104/214] Adjust indentation --- ios/MullvadVPNTests/LoggingTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift index b4f18c751322..337f850560e7 100644 --- a/ios/MullvadVPNTests/LoggingTests.swift +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -47,7 +47,7 @@ class MullvadLoggingTests: XCTestCase { Logger(label: "test").info(":-P") -// For some reason, reading the file fails here, despite the file existing. Manual inspection reveals the file to have the correct header. ¯\_(ツ)_/¯ + // For some reason, reading the file fails here, despite the file existing. Manual inspection reveals the file to have the correct header. ¯\_(ツ)_/¯ // let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) // // XCTAssert(contents.hasPrefix(expectedHeader)) From 9af50acb7e20c7d6f50b7faf40969bb63d7e4ccf Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Thu, 11 Apr 2024 18:41:46 +0200 Subject: [PATCH 105/214] Make changes suggested in PR --- ios/MullvadVPN/AppDelegate.swift | 2 -- ios/MullvadVPNTests/LoggingTests.swift | 12 +----------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index 88c9b0e4e62e..291ed2b46bfe 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -360,8 +360,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD loggerBuilder.install() logger = Logger(label: "AppDelegate") - - loggerBuilder.logLevel = .debug } private func addApplicationNotifications(application: UIApplication) { diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift index 337f850560e7..af0a9e857aff 100644 --- a/ios/MullvadVPNTests/LoggingTests.swift +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -19,17 +19,7 @@ class MullvadLoggingTests: XCTestCase { // Add a teardown block to delete any file at `fileURL`. addTeardownBlock { - do { - let fileManager = FileManager.default - // Check that the file exists before trying to delete it. - if fileManager.fileExists(atPath: fileURL.path) { - // Perform the deletion. - try fileManager.removeItem(at: fileURL) - } - } catch { - // Treat any errors during file deletion as a test failure. - XCTFail("Error while deleting temporary file: \(error)") - } + try? FileManager.default.removeItem(at: fileURL) } // Return the temporary file URL for use in a test method. From 0cab19c236d34a4c0dbdb7eae4a2872dc93e7c42 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Mon, 15 Apr 2024 10:38:46 +0200 Subject: [PATCH 106/214] Wait for the logfile to settle before attempting to read it in tests --- ios/MullvadVPNTests/LoggingTests.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift index af0a9e857aff..ecb782571ac7 100644 --- a/ios/MullvadVPNTests/LoggingTests.swift +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -37,9 +37,11 @@ class MullvadLoggingTests: XCTestCase { Logger(label: "test").info(":-P") - // For some reason, reading the file fails here, despite the file existing. Manual inspection reveals the file to have the correct header. ¯\_(ツ)_/¯ -// let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) -// -// XCTAssert(contents.hasPrefix(expectedHeader)) + // Wait for the log file to settle + usleep(100000) + + let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) + + XCTAssert(contents.hasPrefix(expectedHeader)) } } From b8e3bfe33156fe5f09365dabd244fd14b262f490 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Mon, 15 Apr 2024 15:29:06 +0200 Subject: [PATCH 107/214] Make logging tests synchronous on file writes / add test --- ios/MullvadVPNTests/LoggingTests.swift | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ios/MullvadVPNTests/LoggingTests.swift b/ios/MullvadVPNTests/LoggingTests.swift index ecb782571ac7..bdc08e959c22 100644 --- a/ios/MullvadVPNTests/LoggingTests.swift +++ b/ios/MullvadVPNTests/LoggingTests.swift @@ -26,6 +26,18 @@ class MullvadLoggingTests: XCTestCase { return fileURL } + func testLogFileOutputStreamWritesHeader() { + let headerText = "This is a header" + let logMessage = "And this is a log message\n" + let fileURL = temporaryFileURL() + let stream = LogFileOutputStream(fileURL: fileURL, header: headerText) + stream.write(logMessage) + sync() + + let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) + XCTAssertEqual(contents, "\(headerText)\n\(logMessage)") + } + func testLogHeader() { let expectedHeader = "Header of a log file" @@ -37,8 +49,7 @@ class MullvadLoggingTests: XCTestCase { Logger(label: "test").info(":-P") - // Wait for the log file to settle - usleep(100000) + sync() let contents = String(decoding: try! Data(contentsOf: fileURL), as: UTF8.self) From 49d950f33d494b3b8fb39044d436ee7d624946d1 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 17 Apr 2024 11:50:31 +0200 Subject: [PATCH 108/214] Fix rebase from main --- ios/MullvadVPN/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index 291ed2b46bfe..a1087d70a95c 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -353,7 +353,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD private func configureLogging() { var loggerBuilder = LoggerBuilder(header: "MullvadVPN version \(Bundle.main.productVersion)") - loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.logFileURL(for: .mainApp)) + loggerBuilder.addFileOutput(fileURL: ApplicationConfiguration.newLogFileURL(for: .mainApp)) #if DEBUG loggerBuilder.addOSLogOutput(subsystem: ApplicationTarget.mainApp.bundleIdentifier) #endif From 323e10171bcf3299a0161013bda0dba45c0878b2 Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Tue, 16 Apr 2024 14:12:46 +0200 Subject: [PATCH 109/214] Do not filter locations for custom lists With the exception of the select location screen list --- .../compose/screen/EditCustomListScreen.kt | 5 +- .../mullvadvpn/compose/util/PreviewData.kt | 5 +- .../mullvad/mullvadvpn/relaylist/RelayItem.kt | 3 ++ .../mullvad/mullvadvpn/relaylist/RelayList.kt | 3 +- .../relaylist/RelayListExtensions.kt | 48 ++++++++++++------- .../mullvadvpn/usecase/RelayListUseCase.kt | 18 +++++-- .../customlists/CustomListActionUseCase.kt | 4 +- .../viewmodel/CustomListLocationsViewModel.kt | 2 +- .../viewmodel/SelectLocationViewModel.kt | 13 ++++- 9 files changed, 71 insertions(+), 30 deletions(-) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt index 87ed88d2639b..9186e639c56e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt @@ -45,6 +45,7 @@ import net.mullvad.mullvadvpn.compose.transitions.SlideInFromRightTransition import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.Dimens import net.mullvad.mullvadvpn.model.GeographicLocationConstraint +import net.mullvad.mullvadvpn.model.Ownership import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.viewmodel.EditCustomListViewModel import org.koin.androidx.compose.koinViewModel @@ -69,7 +70,9 @@ private fun PreviewEditCustomListScreen() { "hostname", "hostname", "hostname" - ) + ), + "Provider", + Ownership.MullvadOwned ) ) ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/PreviewData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/PreviewData.kt index 61b563564ce4..3581d1d0b41d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/PreviewData.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/PreviewData.kt @@ -1,6 +1,7 @@ package net.mullvad.mullvadvpn.compose.util import net.mullvad.mullvadvpn.model.GeographicLocationConstraint +import net.mullvad.mullvadvpn.model.Ownership import net.mullvad.mullvadvpn.relaylist.RelayItem fun generateRelayItemCountry( @@ -65,7 +66,9 @@ fun generateRelayItemRelay( hostname = hostName, ), locationName = "$cityCode $hostName", - active = active + active = active, + providerName = "Provider", + ownership = Ownership.MullvadOwned, ) private fun String.generateCountryCode() = (take(1) + takeLast(1)).lowercase() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt index ce4be395b651..af4a0084d293 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt @@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.relaylist import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.GeoIpLocation import net.mullvad.mullvadvpn.model.GeographicLocationConstraint +import net.mullvad.mullvadvpn.model.Ownership sealed interface RelayItem { val name: String @@ -66,6 +67,8 @@ sealed interface RelayItem { override val locationName: String, override val active: Boolean, val location: GeographicLocationConstraint.Hostname, + val providerName: String, + val ownership: Ownership, ) : RelayItem { override val code = name override val hasChildren = false diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt index 30e4146245ac..e469aec11816 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.relaylist data class RelayList( val customLists: List, - val country: List, + val allCountries: List, + val filteredCountries: List, val selectedItem: RelayItem?, ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt index 882a3e42a4de..8d2ea5f34850 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt @@ -12,10 +12,7 @@ import net.mullvad.mullvadvpn.model.RelayList * filtered out and also relays that do not fit the ownership and provider list So are also cities * that only contains non-wireguard relays Countries, cities and relays are ordered by name */ -fun RelayList.toRelayCountries( - ownership: Constraint, - providers: Constraint -): List { +fun RelayList.toRelayCountries(): List { val relayCountries = this.countries .map { country -> @@ -33,8 +30,7 @@ fun RelayList.toRelayCountries( relays = relays ) - val validCityRelays = - city.relays.filterValidRelays(ownership = ownership, providers = providers) + val validCityRelays = city.relays.filterValidRelays() for (relay in validCityRelays) { relays.add( @@ -47,7 +43,10 @@ fun RelayList.toRelayCountries( relay.hostname ), locationName = "${city.name} (${relay.hostname})", - active = relay.active + active = relay.active, + providerName = relay.provider, + ownership = + if (relay.owned) Ownership.MullvadOwned else Ownership.Rented ) ) } @@ -172,25 +171,40 @@ fun List.filterOnSearchTerm( } } -private fun List.filterValidRelays( +private fun List.filterValidRelays(): List = filter { + it.isWireguardRelay +} + +fun List.filterOnOwnershipAndProviders( ownership: Constraint, providers: Constraint -): List = - filter { it.isWireguardRelay } - .filter { +): List { + return map { country -> + val cities = + country.cities.map { city -> + val relays = + city.relays.filterRelayByOwnershipAndProviders(ownership, providers) + city.copy(relays = relays) + } + country.copy(cities = cities.filter { it.relays.isNotEmpty() }) + } + .filter { it.cities.isNotEmpty() } +} + +private fun List.filterRelayByOwnershipAndProviders( + ownership: Constraint, + providers: Constraint +): List = + filter { when (ownership) { is Constraint.Any -> true - is Constraint.Only -> - when (ownership.value) { - Ownership.MullvadOwned -> it.owned - Ownership.Rented -> !it.owned - } + is Constraint.Only -> it.ownership == ownership.value } } .filter { relay -> when (providers) { is Constraint.Any -> true - is Constraint.Only -> providers.value.providers.contains(relay.provider) + is Constraint.Only -> providers.value.providers.contains(relay.providerName) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt index 4957818283f7..bdeaac0d0c3f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt @@ -9,6 +9,7 @@ import net.mullvad.mullvadvpn.model.RelaySettings import net.mullvad.mullvadvpn.model.WireguardConstraints import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.RelayList +import net.mullvad.mullvadvpn.relaylist.filterOnOwnershipAndProviders import net.mullvad.mullvadvpn.relaylist.findItemForGeographicLocationConstraint import net.mullvad.mullvadvpn.relaylist.toRelayCountries import net.mullvad.mullvadvpn.relaylist.toRelayItemLists @@ -36,22 +37,29 @@ class RelayListUseCase( settings?.relaySettings?.relayConstraints()?.ownership ?: Constraint.Any() val providers = settings?.relaySettings?.relayConstraints()?.providers ?: Constraint.Any() - val relayCountries = - relayList.toRelayCountries(ownership = ownership, providers = providers) + val relayCountries = relayList.toRelayCountries() val customLists = settings?.customLists?.customLists?.toRelayItemLists(relayCountries) ?: emptyList() + val relayCountriesFiltered = + relayCountries.filterOnOwnershipAndProviders(ownership, providers) val selectedItem = findSelectedRelayItem( relaySettings = settings?.relaySettings, - relayCountries = relayCountries, + relayCountries = relayCountriesFiltered, customLists = customLists, ) - RelayList(customLists, relayCountries, selectedItem) + RelayList( + customLists = customLists, + allCountries = relayCountries, + filteredCountries = relayCountriesFiltered, + selectedItem = selectedItem + ) } fun selectedRelayItem(): Flow = relayListWithSelection().map { it.selectedItem } - fun relayList(): Flow> = relayListWithSelection().map { it.country } + fun fullRelayList(): Flow> = + relayListWithSelection().map { it.allCountries } fun customLists(): Flow> = relayListWithSelection().map { it.customLists } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt index 8d722325d650..16c86c0d59e3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt @@ -53,7 +53,7 @@ class CustomListActionUseCase( ) val locationNames = relayListUseCase - .relayList() + .fullRelayList() .firstOrNull() ?.getRelayItemsByCodes(action.locations) ?.map { it.name } @@ -61,7 +61,7 @@ class CustomListActionUseCase( CustomListResult.Created( id = result.id, name = action.name, - locationName = locationNames?.first(), + locationName = locationNames?.firstOrNull(), undo = action.not(result.id) ) ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt index 5efba5321ee3..cdbcebbb8369 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt @@ -37,7 +37,7 @@ class CustomListLocationsViewModel( private val _searchTerm = MutableStateFlow(EMPTY_SEARCH_TERM) val uiState = - combine(relayListUseCase.relayList(), _searchTerm, _selectedLocations) { + combine(relayListUseCase.fullRelayList(), _searchTerm, _selectedLocations) { relayCountries, searchTerm, selectedLocations -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt index 15df90be9e4c..586bbf8bc172 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt @@ -35,6 +35,7 @@ class SelectLocationViewModel( ) : ViewModel() { private val _searchTerm = MutableStateFlow(EMPTY_SEARCH_TERM) + @Suppress("DestructuringDeclarationWithTooManyEntries") val uiState = combine( relayListUseCase.relayListWithSelection(), @@ -43,7 +44,7 @@ class SelectLocationViewModel( relayListFilterUseCase.availableProviders(), relayListFilterUseCase.selectedProviders(), ) { - (customLists, relayCountries, selectedItem), + (customLists, _, relayCountries, selectedItem), searchTerm, selectedOwnership, allProviders, @@ -63,7 +64,15 @@ class SelectLocationViewModel( val filteredRelayCountries = relayCountries.filterOnSearchTerm(searchTerm, selectedItem) - val filteredCustomLists = customLists.filterOnSearchTerm(searchTerm) + val filteredCustomLists = + customLists.filterOnSearchTerm(searchTerm).map { customList -> + customList.copy( + locations = + customList.locations.filter { location -> + filteredRelayCountries.any { it.code == location.code } + } + ) + } SelectLocationUiState.Content( searchTerm = searchTerm, From 2e4bae97bc8a94b2c82710e703629d7dd3683715 Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Tue, 16 Apr 2024 14:13:46 +0200 Subject: [PATCH 110/214] Fix ui tests --- .../net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt index fd4a97a1d251..2adfa22220bd 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt @@ -1,6 +1,5 @@ package net.mullvad.mullvadvpn.compose.data -import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.CustomListName import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.model.RelayEndpointData @@ -43,7 +42,7 @@ val DUMMY_RELAY_COUNTRIES = arrayListOf(DUMMY_RELAY_COUNTRY_1, DUMMY_RELAY_COUNTRY_2), DUMMY_WIREGUARD_ENDPOINT_DATA, ) - .toRelayCountries(ownership = Constraint.Any(), providers = Constraint.Any()) + .toRelayCountries() val DUMMY_CUSTOM_LISTS = listOf( From 9a335754d64f1e819bd4dac4753d8a91962ca2cd Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Tue, 16 Apr 2024 14:14:00 +0200 Subject: [PATCH 111/214] Fix unit tests --- .../relaylist/RelayNameComparatorTest.kt | 73 +++++++++++++++++-- .../usecase/CustomListActionUseCaseTest.kt | 2 +- .../CustomListLocationsViewModelTest.kt | 7 +- .../viewmodel/SelectLocationViewModelTest.kt | 21 +++--- 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt index 1532328729ce..eb66c2d4f907 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.relaylist import io.mockk.mockk import io.mockk.unmockkAll +import net.mullvad.mullvadvpn.model.Ownership import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -21,6 +22,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relay10 = RelayItem.Relay( @@ -28,6 +31,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) relay9 assertOrderBothDirection relay10 @@ -41,6 +46,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relay9b = RelayItem.Relay( @@ -48,6 +55,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) assertTrue(RelayNameComparator.compare(relay9a, relay9b) == 0) @@ -57,13 +66,41 @@ class RelayNameComparatorTest { @Test fun `comparator should be able to handle name of only numbers`() { val relay001 = - RelayItem.Relay(name = "001", location = mockk(), locationName = "mock", active = false) + RelayItem.Relay( + name = "001", + location = mockk(), + locationName = "mock", + active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned + ) val relay1 = - RelayItem.Relay(name = "1", location = mockk(), locationName = "mock", active = false) + RelayItem.Relay( + name = "1", + location = mockk(), + locationName = "mock", + active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned + ) val relay3 = - RelayItem.Relay(name = "3", location = mockk(), locationName = "mock", active = false) + RelayItem.Relay( + name = "3", + location = mockk(), + locationName = "mock", + active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned + ) val relay100 = - RelayItem.Relay(name = "100", location = mockk(), locationName = "mock", active = false) + RelayItem.Relay( + name = "100", + location = mockk(), + locationName = "mock", + active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned + ) relay001 assertOrderBothDirection relay1 relay001 assertOrderBothDirection relay3 @@ -79,6 +116,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relay9b = RelayItem.Relay( @@ -86,6 +125,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) assertTrue(RelayNameComparator.compare(relay9a, relay9b) == 0) @@ -100,6 +141,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relay005 = RelayItem.Relay( @@ -107,6 +150,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) relay001 assertOrderBothDirection relay005 @@ -120,6 +165,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relayAr8 = RelayItem.Relay( @@ -127,6 +174,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relaySe5 = RelayItem.Relay( @@ -134,6 +183,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relaySe10 = RelayItem.Relay( @@ -141,6 +192,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) relayAr2 assertOrderBothDirection relayAr8 @@ -155,7 +208,9 @@ class RelayNameComparatorTest { name = "se2-cloud", location = mockk(), locationName = "mock", - active = false + active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relay2w = RelayItem.Relay( @@ -163,6 +218,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) relay2c assertOrderBothDirection relay2w @@ -175,7 +232,9 @@ class RelayNameComparatorTest { name = "se22", location = mockk(), locationName = "mock", - active = false + active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) val relay22b = RelayItem.Relay( @@ -183,6 +242,8 @@ class RelayNameComparatorTest { location = mockk(), locationName = "mock", active = false, + providerName = "Provider", + ownership = Ownership.MullvadOwned ) relay22a assertOrderBothDirection relay22b diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt index fdcb4170f00f..4dfb95768bd2 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/CustomListActionUseCaseTest.kt @@ -71,7 +71,7 @@ class CustomListActionUseCaseTest { listOf(locationCode) ) } returns UpdateCustomListResult.Ok - coEvery { mockRelayListUseCase.relayList() } returns flowOf(mockLocations) + coEvery { mockRelayListUseCase.fullRelayList() } returns flowOf(mockLocations) every { mockLocations.getRelayItemsByCodes(listOf(locationCode)) } returns mockLocations // Act diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt index df10ba96c4e6..d21789d36f35 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt @@ -12,6 +12,7 @@ import net.mullvad.mullvadvpn.compose.communication.CustomListResult import net.mullvad.mullvadvpn.compose.state.CustomListLocationsUiState import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.model.GeographicLocationConstraint +import net.mullvad.mullvadvpn.model.Ownership import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.descendants import net.mullvad.mullvadvpn.usecase.RelayListUseCase @@ -31,7 +32,7 @@ class CustomListLocationsViewModelTest { @BeforeEach fun setup() { - every { mockRelayListUseCase.relayList() } returns relayListFlow + every { mockRelayListUseCase.fullRelayList() } returns relayListFlow every { mockRelayListUseCase.customLists() } returns customListFlow } @@ -283,7 +284,9 @@ class CustomListLocationsViewModelTest { "SE", "GBG", "gbg-1" - ) + ), + providerName = "Provider", + ownership = Ownership.MullvadOwned ) ) ) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt index 41bff94ccd12..6522892ec2f6 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt @@ -49,7 +49,7 @@ class SelectLocationViewModelTest { private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private lateinit var viewModel: SelectLocationViewModel private val relayListWithSelectionFlow = - MutableStateFlow(RelayList(emptyList(), emptyList(), null)) + MutableStateFlow(RelayList(emptyList(), emptyList(), emptyList(), null)) private val mockRelayListUseCase: RelayListUseCase = mockk() private val mockCustomListActionUseCase: CustomListActionUseCase = mockk(relaxed = true) private val selectedOwnership = MutableStateFlow>(Constraint.Any()) @@ -93,10 +93,11 @@ class SelectLocationViewModelTest { fun `given relayListWithSelection emits update uiState should contain new update`() = runTest { // Arrange val mockCountries = listOf(mockk(), mockk()) - val mockCustomList = listOf(mockk()) + val mockCustomList = listOf(mockk(relaxed = true)) val selectedItem: RelayItem = mockk() every { mockCountries.filterOnSearchTerm(any(), selectedItem) } returns mockCountries - relayListWithSelectionFlow.value = RelayList(mockCustomList, mockCountries, selectedItem) + relayListWithSelectionFlow.value = + RelayList(mockCustomList, mockCountries, mockCountries, selectedItem) // Act, Assert viewModel.uiState.test { @@ -111,12 +112,12 @@ class SelectLocationViewModelTest { fun `given relayListWithSelection emits update with no selections selectedItem should be null`() = runTest { // Arrange - val mockCustomList = listOf(mockk()) + val mockCustomList = listOf(mockk(relaxed = true)) val mockCountries = listOf(mockk(), mockk()) val selectedItem: RelayItem? = null every { mockCountries.filterOnSearchTerm(any(), selectedItem) } returns mockCountries relayListWithSelectionFlow.value = - RelayList(mockCustomList, mockCountries, selectedItem) + RelayList(mockCustomList, mockCountries, mockCountries, selectedItem) // Act, Assert viewModel.uiState.test { @@ -155,7 +156,7 @@ class SelectLocationViewModelTest { @Test fun `on onSearchTermInput call uiState should emit with filtered countries`() = runTest { // Arrange - val mockCustomList = listOf(mockk()) + val mockCustomList = listOf(mockk(relaxed = true)) val mockCountries = listOf(mockk(), mockk()) val selectedItem: RelayItem? = null val mockRelayList: List = mockk(relaxed = true) @@ -163,7 +164,8 @@ class SelectLocationViewModelTest { every { mockRelayList.filterOnSearchTerm(mockSearchString, selectedItem) } returns mockCountries every { mockCustomList.filterOnSearchTerm(mockSearchString) } returns mockCustomList - relayListWithSelectionFlow.value = RelayList(mockCustomList, mockRelayList, selectedItem) + relayListWithSelectionFlow.value = + RelayList(mockCustomList, mockRelayList, mockRelayList, selectedItem) // Act, Assert viewModel.uiState.test { @@ -184,7 +186,7 @@ class SelectLocationViewModelTest { @Test fun `when onSearchTermInput returns empty result uiState should return empty list`() = runTest { // Arrange - val mockCustomList = listOf(mockk()) + val mockCustomList = listOf(mockk(relaxed = true)) val mockCountries = emptyList() val selectedItem: RelayItem? = null val mockRelayList: List = mockk(relaxed = true) @@ -192,7 +194,8 @@ class SelectLocationViewModelTest { every { mockRelayList.filterOnSearchTerm(mockSearchString, selectedItem) } returns mockCountries every { mockCustomList.filterOnSearchTerm(mockSearchString) } returns mockCustomList - relayListWithSelectionFlow.value = RelayList(mockCustomList, mockRelayList, selectedItem) + relayListWithSelectionFlow.value = + RelayList(mockCustomList, mockRelayList, mockRelayList, selectedItem) // Act, Assert viewModel.uiState.test { From 559d15a8058eedd745f9f7fd83d8931597fae3f9 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 12 Apr 2024 17:09:59 +0200 Subject: [PATCH 112/214] Make sure to query app verion every 24 hours even if the daemon was restarted. --- mullvad-daemon/src/lib.rs | 7 +- mullvad-daemon/src/version_check.rs | 175 +++++++++++++++++++--------- 2 files changed, 123 insertions(+), 59 deletions(-) diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index d0fad0493d76..b8629ee32dfc 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -704,8 +704,6 @@ where settings_event_listener.notify_settings(settings.to_owned()); }); - let app_version_info = version_check::load_cache(&cache_dir).await; - let initial_selector_config = new_selector_config(&settings); let relay_selector = RelaySelector::new( initial_selector_config, @@ -868,9 +866,10 @@ where api_availability.clone(), cache_dir.clone(), internal_event_tx.to_specialized_sender(), - app_version_info.clone(), settings.show_beta_releases, - ); + ) + .await; + let app_version_info = version_updater.last_app_version_info().cloned(); tokio::spawn(version_updater.run()); // Attempt to download a fresh relay list diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index afb1d7bee853..b87da5db5c10 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -1,6 +1,7 @@ use crate::{version::is_beta_version, DaemonEventSender}; use futures::{ channel::{mpsc, oneshot}, + future::FusedFuture, stream::FusedStream, FutureExt, SinkExt, StreamExt, TryFutureExt, }; @@ -9,16 +10,18 @@ use mullvad_types::version::{AppVersionInfo, ParsedAppVersion}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::{ + cmp::max, future::Future, io, path::{Path, PathBuf}, + pin::Pin, str::FromStr, - time::Duration, + time::{Duration, SystemTime}, }; use talpid_core::mpsc::Sender; use talpid_future::retry::{retry_future, ConstantInterval}; use talpid_types::ErrorExt; -use tokio::fs::{self, File}; +use tokio::{fs::File, io::AsyncReadExt}; const VERSION_INFO_FILENAME: &str = "version-info.json"; @@ -94,12 +97,15 @@ pub(crate) struct VersionUpdater { version_proxy: AppVersionProxy, cache_path: PathBuf, update_sender: DaemonEventSender, - last_app_version_info: Option, + /// The last known [AppVersionInfo], along with the time it was determined. + last_app_version_info: Option<(AppVersionInfo, SystemTime)>, platform_version: String, show_beta_releases: bool, rx: Option>, availability_handle: ApiAvailabilityHandle, - internal_done_tx: Option>, + + /// Oneshot channels for responding to [VersionUpdaterCommand::RunVersionCheck]. + run_version_check_responders: Vec>, } #[derive(Clone)] @@ -142,14 +148,16 @@ impl VersionUpdaterHandle { } impl VersionUpdater { - pub fn new( + pub async fn new( mut api_handle: MullvadRestHandle, availability_handle: ApiAvailabilityHandle, cache_dir: PathBuf, update_sender: DaemonEventSender, - last_app_version_info: Option, show_beta_releases: bool, ) -> (Self, VersionUpdaterHandle) { + // load the last known AppVersionInfo from cache + let last_app_version_info = load_cache(&cache_dir).await; + api_handle.factory = api_handle.factory.default_timeout(DOWNLOAD_TIMEOUT); let version_proxy = AppVersionProxy::new(api_handle); let cache_path = cache_dir.join(VERSION_INFO_FILENAME); @@ -166,20 +174,23 @@ impl VersionUpdater { show_beta_releases, rx: Some(rx), availability_handle, - internal_done_tx: None, + run_version_check_responders: vec![], }, VersionUpdaterHandle { tx }, ) } - fn create_update_future( + /// Get the last known [AppVersionInfo]. May be stale. + pub fn last_app_version_info(&self) -> Option<&AppVersionInfo> { + self.last_app_version_info.as_ref().map(|(info, _)| info) + } + + /// Immediately query the API for the latest [AppVersionInfo]. + fn query_app_version( &mut self, - done_tx: oneshot::Sender, - ) -> std::pin::Pin< + ) -> Pin< Box> + Send + 'static>, > { - self.internal_done_tx = Some(done_tx); - let api_handle = self.availability_handle.clone(); let version_proxy = self.version_proxy.clone(); let platform_version = self.platform_version.clone(); @@ -193,28 +204,31 @@ impl VersionUpdater { .map_err(Error::Download) }; + // retry immediately on network errors (unless we're offline) + let should_retry_immediate = move |result: &Result<_, Error>| { + if let Err(Error::Download(error)) = result { + error.is_network_error() && !api_handle.get_state().is_offline() + } else { + false + } + }; + Box::pin(retry_future( download_future_factory, - move |result| Self::should_retry_immediate(result, &api_handle), + should_retry_immediate, IMMEDIATE_RETRY_STRATEGY, )) } - fn should_retry_immediate( - result: &Result, - api_handle: &ApiAvailabilityHandle, - ) -> bool { - match result { - Err(Error::Download(error)) if error.is_network_error() => { - !api_handle.get_state().is_offline() - } - _ => false, - } - } - - fn create_update_background_future( + /// Query the API for the latest [AppVersionInfo]. + /// + /// This function waits until background calls are enabled in + /// [ApiAvailability](mullvad_api::availability::ApiAvailability). + /// + /// On any error, this function retries repeatedly every [UPDATE_INTERVAL_ERROR] until success. + fn query_app_version_in_background( &self, - ) -> std::pin::Pin< + ) -> Pin< Box> + Send + 'static>, > { let api_handle = self.availability_handle.clone(); @@ -240,8 +254,9 @@ impl VersionUpdater { )) } + /// Write [Self::last_app_version_info], if any, to the cache file ([VERSION_INFO_FILENAME]). async fn write_cache(&self) -> Result<(), Error> { - let last_app_version_info = match self.last_app_version_info.as_ref() { + let last_app_version_info = match self.last_app_version_info() { Some(version_info) => version_info, None => { log::debug!("The version cache is empty -- not writing"); @@ -265,6 +280,7 @@ impl VersionUpdater { Ok(()) } + /// Convert a [mullvad_api::AppVersionResponse] to an [AppVersionInfo]. fn response_to_version_info( &mut self, response: mullvad_api::AppVersionResponse, @@ -284,6 +300,7 @@ impl VersionUpdater { } } + /// If current_version is not the latest, return a string containing the latest version. fn suggested_upgrade( current_version: &ParsedAppVersion, latest_stable: &Option, @@ -300,35 +317,60 @@ impl VersionUpdater { None }; - let latest_version = stable_version.iter().chain(beta_version.iter()).max()?; + let latest_version = max(stable_version, beta_version)?; - if current_version < latest_version { + if current_version < &latest_version { Some(latest_version.to_string()) } else { None } } + /// Update [Self::last_app_version_info] and write it to disk cache. + /// + /// Also, if we are currently have a pending [RunVersionCheck][rvc] command, respond to it. + /// + /// [rvc]: VersionUpdaterCommand::RunVersionCheck async fn update_version_info(&mut self, new_version_info: AppVersionInfo) { - if let Some(done_tx) = self.internal_done_tx.take() { - let _ = done_tx.send(new_version_info.clone()); - } - // if daemon can't be reached, return immediately if self.update_sender.send(new_version_info.clone()).is_err() { return; } - self.last_app_version_info = Some(new_version_info); + self.last_app_version_info = Some((new_version_info, SystemTime::now())); if let Err(err) = self.write_cache().await { log::error!("Failed to save version cache to disk: {}", err); } } + /// Wait until [VersionUpdater::last_app_version_info] becomes stale and needs to be refreshed. + /// + /// This happens [UPDATE_INTERVAL] after the last version query. + fn wait_until_version_is_stale(&self) -> Pin>> { + let now = SystemTime::now(); + let time_until_stale = self + .last_app_version_info + .as_ref() + .map(|(_, last_update_time)| last_update_time) + .and_then(|&last_update_time| now.duration_since(last_update_time).ok()) + .map(|time_since_last_update| UPDATE_INTERVAL.saturating_sub(time_since_last_update)) + // if there is no last_app_version_info, or if clocks are being weird, + // assume that the version is stale + .unwrap_or(Duration::ZERO); + + // Boxed, pinned, and fused. + // Alternate title: "We don't want to deal with the borrow checker." + Box::pin(talpid_time::sleep(time_until_stale).fuse()) + } + + /// Returns true if we are currently handling one or more `RunVersionCheck` commands. + fn is_running_version_check(&self) -> bool { + !self.run_version_check_responders.is_empty() + } + pub async fn run(mut self) { let mut rx = self.rx.take().unwrap().fuse(); - let next_delay = || Box::pin(talpid_time::sleep(UPDATE_INTERVAL)).fuse(); - let mut check_delay = next_delay(); + let mut version_is_stale = self.wait_until_version_is_stale(); let mut version_check = futures::future::Fuse::terminated(); // If this is a dev build, there's no need to pester the API for version checks. @@ -351,8 +393,8 @@ impl VersionUpdater { self.show_beta_releases = show_beta_releases; if let Some(last_app_version_info) = self - .last_app_version_info - .clone() + .last_app_version_info() + .cloned() { let suggested_upgrade = Self::suggested_upgrade( &APP_VERSION, @@ -373,8 +415,12 @@ impl VersionUpdater { if self.update_sender.is_closed() { return; } - let download_future = self.create_update_future(done_tx).fuse(); - version_check = download_future; + + if !self.is_running_version_check() { + version_check = self.query_app_version().fuse(); + } + + self.run_version_check_responders.push(done_tx); } // time to shut down None => { @@ -383,15 +429,14 @@ impl VersionUpdater { } }, - _sleep = check_delay => { + _ = version_is_stale => { if rx.is_terminated() || self.update_sender.is_closed() { return; } - if self.internal_done_tx.is_some() { - // Sync check in progress + if self.is_running_version_check() { continue; } - version_check = self.create_update_background_future().fuse(); + version_check = self.query_app_version_in_background().fuse(); }, response = version_check => { @@ -403,42 +448,62 @@ impl VersionUpdater { Ok(version_info_response) => { let new_version_info = self.response_to_version_info(version_info_response); + + // Respond to all pending RunVersionCheck commands + for done_tx in self.run_version_check_responders.drain(..) { + let _ = done_tx.send(new_version_info.clone()); + } + self.update_version_info(new_version_info).await; - }, + + } Err(err) => { - log::error!("Failed to fetch version info: {}", err); - self.internal_done_tx = None; - }, + log::error!("Failed to fetch version info: {err:#}"); + self.run_version_check_responders.clear(); + } } - check_delay = next_delay(); + version_is_stale = self.wait_until_version_is_stale(); }, } } } } -async fn try_load_cache(cache_dir: &Path) -> Result { +async fn try_load_cache(cache_dir: &Path) -> Result<(AppVersionInfo, SystemTime), Error> { if *IS_DEV_BUILD { - return Ok(dev_version_cache()); + return Ok((dev_version_cache(), SystemTime::now())); } let path = cache_dir.join(VERSION_INFO_FILENAME); log::debug!("Loading version check cache from {}", path.display()); - let content = fs::read_to_string(&path) + + let mut file = File::open(&path).map_err(Error::ReadVersionCache).await?; + let meta = file.metadata().map_err(Error::ReadVersionCache).await?; + let mtime = meta + .modified() + .expect("Platforms without file modification times aren't supported"); + + let mut content = String::new(); + file.read_to_string(&mut content) .map_err(Error::ReadVersionCache) .await?; + let version_info: CachedAppVersionInfo = serde_json::from_str(&content).map_err(Error::Deserialize)?; if version_info.cached_from_version == mullvad_version::VERSION { - Ok(version_info.version_info) + Ok((version_info.version_info, mtime)) } else { Err(Error::CacheVersionMismatch) } } -pub async fn load_cache(cache_dir: &Path) -> Option { +/// Read the app version cache from the provided directory. +/// +/// Returns the [AppVersionInfo] along with the modification time of the cache file, +/// or `None` on any error. +async fn load_cache(cache_dir: &Path) -> Option<(AppVersionInfo, SystemTime)> { match try_load_cache(cache_dir).await { Ok(app_version_info) => Some(app_version_info), Err(error) => { From f5326b74ddc6eecdc35660ffe08531342ffdcead Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Mon, 15 Apr 2024 14:44:14 +0200 Subject: [PATCH 113/214] Do not query latest version unless stale --- mullvad-daemon/src/version_check.rs | 97 +++++++++++++++++------------ 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index b87da5db5c10..c36c693816ae 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -343,12 +343,13 @@ impl VersionUpdater { } } - /// Wait until [VersionUpdater::last_app_version_info] becomes stale and needs to be refreshed. + /// Get the time left until [Self::last_app_version_info] becomes stale, and should be + /// refreshed, or [Duration::ZERO] if it already is stale. /// /// This happens [UPDATE_INTERVAL] after the last version query. - fn wait_until_version_is_stale(&self) -> Pin>> { + fn time_until_version_is_stale(&self) -> Duration { let now = SystemTime::now(); - let time_until_stale = self + self .last_app_version_info .as_ref() .map(|(_, last_update_time)| last_update_time) @@ -356,7 +357,18 @@ impl VersionUpdater { .map(|time_since_last_update| UPDATE_INTERVAL.saturating_sub(time_since_last_update)) // if there is no last_app_version_info, or if clocks are being weird, // assume that the version is stale - .unwrap_or(Duration::ZERO); + .unwrap_or(Duration::ZERO) + } + + fn version_is_stale(&self) -> bool { + self.time_until_version_is_stale().is_zero() + } + + /// Wait until [Self::last_app_version_info] becomes stale and needs to be refreshed. + /// + /// This happens [UPDATE_INTERVAL] after the last version query. + fn wait_until_version_is_stale(&self) -> Pin>> { + let time_until_stale = self.time_until_version_is_stale(); // Boxed, pinned, and fused. // Alternate title: "We don't want to deal with the borrow checker." @@ -387,45 +399,52 @@ impl VersionUpdater { loop { futures::select! { - command = rx.next() => { - match command { - Some(VersionUpdaterCommand::SetShowBetaReleases(show_beta_releases)) => { - self.show_beta_releases = show_beta_releases; - - if let Some(last_app_version_info) = self - .last_app_version_info() - .cloned() - { - let suggested_upgrade = Self::suggested_upgrade( - &APP_VERSION, - &Some(last_app_version_info.latest_stable.clone()), - &last_app_version_info.latest_beta, - self.show_beta_releases || is_beta_version(), - ); - - self.update_version_info(AppVersionInfo { - supported: last_app_version_info.supported, - latest_stable: last_app_version_info.latest_stable, - latest_beta: last_app_version_info.latest_beta, - suggested_upgrade, - }).await; - } + command = rx.next() => match command { + Some(VersionUpdaterCommand::SetShowBetaReleases(show_beta_releases)) => { + self.show_beta_releases = show_beta_releases; + + if let Some(last_app_version_info) = self + .last_app_version_info() + .cloned() + { + let suggested_upgrade = Self::suggested_upgrade( + &APP_VERSION, + &Some(last_app_version_info.latest_stable.clone()), + &last_app_version_info.latest_beta, + self.show_beta_releases || is_beta_version(), + ); + + self.update_version_info(AppVersionInfo { + supported: last_app_version_info.supported, + latest_stable: last_app_version_info.latest_stable, + latest_beta: last_app_version_info.latest_beta, + suggested_upgrade, + }).await; } - Some(VersionUpdaterCommand::RunVersionCheck(done_tx)) => { - if self.update_sender.is_closed() { - return; - } - - if !self.is_running_version_check() { - version_check = self.query_app_version().fuse(); - } + } - self.run_version_check_responders.push(done_tx); - } - // time to shut down - None => { + Some(VersionUpdaterCommand::RunVersionCheck(done_tx)) => { + if self.update_sender.is_closed() { return; } + match (self.version_is_stale(), self.last_app_version_info()) { + (false, Some(version_info)) => { + // if the version_info isn't stale, return it immediately. + let _ = done_tx.send(version_info.clone()); + } + _ => { + // otherwise, start a foreground query to get the latest version_info. + if !self.is_running_version_check() { + version_check = self.query_app_version().fuse(); + } + self.run_version_check_responders.push(done_tx); + } + } + } + + // time to shut down + None => { + return; } }, From d3fb9392e486540c843b477a86e705b8de43fac1 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Mon, 15 Apr 2024 17:48:44 +0200 Subject: [PATCH 114/214] Refactor away Daemon::app_version_info --- mullvad-daemon/src/lib.rs | 39 +++++++-------------- mullvad-daemon/src/version_check.rs | 54 ++++++++++++++++------------- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index b8629ee32dfc..a6b6794d33d0 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -647,7 +647,6 @@ pub struct Daemon { relay_selector: RelaySelector, relay_list_updater: RelayListUpdaterHandle, parameters_generator: tunnel::ParametersGenerator, - app_version_info: Option, shutdown_tasks: Vec>>>, tunnel_state_machine_handle: TunnelStateMachineHandle, #[cfg(target_os = "windows")] @@ -869,7 +868,6 @@ where settings.show_beta_releases, ) .await; - let app_version_info = version_updater.last_app_version_info().cloned(); tokio::spawn(version_updater.run()); // Attempt to download a fresh relay list @@ -905,7 +903,6 @@ where relay_selector, relay_list_updater, parameters_generator, - app_version_info, shutdown_tasks: vec![], tunnel_state_machine_handle, #[cfg(target_os = "windows")] @@ -1322,7 +1319,6 @@ where } fn handle_new_app_version_info(&mut self, app_version_info: AppVersionInfo) { - self.app_version_info = Some(app_version_info.clone()); self.event_listener.notify_app_version(app_version_info); } @@ -1719,32 +1715,23 @@ where } fn on_get_version_info(&mut self, tx: oneshot::Sender>) { - if self.app_version_info.is_none() { - log::debug!("No version cache found. Fetching new info"); - let mut handle = self.version_updater_handle.clone(); - tokio::spawn(async move { - Self::oneshot_send( - tx, - handle - .run_version_check() - .await - .map_err(|error| { - log::error!( - "{}", - error.display_chain_with_msg("Error running version check") - ) - }) - .ok(), - "get_version_info response", - ); - }); - } else { + let mut handle = self.version_updater_handle.clone(); + tokio::spawn(async move { Self::oneshot_send( tx, - self.app_version_info.clone(), + handle + .get_version_info() + .await + .map_err(|error| { + log::error!( + "{}", + error.display_chain_with_msg("Error running version check") + ) + }) + .ok(), "get_version_info response", ); - } + }); } fn on_get_current_version(&mut self, tx: oneshot::Sender) { diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index c36c693816ae..a53424324d1d 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -35,7 +35,7 @@ const DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(15); const UPDATE_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24); /// Wait this long until next try if an update failed const UPDATE_INTERVAL_ERROR: Duration = Duration::from_secs(60 * 60 * 6); -/// Retry strategy for `RunVersionCheck`. +/// Retry strategy for `GetVersionInfo`. const IMMEDIATE_RETRY_STRATEGY: ConstantInterval = ConstantInterval::new(Duration::ZERO, Some(3)); #[cfg(target_os = "linux")] @@ -104,8 +104,8 @@ pub(crate) struct VersionUpdater { rx: Option>, availability_handle: ApiAvailabilityHandle, - /// Oneshot channels for responding to [VersionUpdaterCommand::RunVersionCheck]. - run_version_check_responders: Vec>, + /// Oneshot channels for responding to [VersionUpdaterCommand::GetVersionInfo]. + get_version_info_responders: Vec>, } #[derive(Clone)] @@ -115,7 +115,7 @@ pub(crate) struct VersionUpdaterHandle { enum VersionUpdaterCommand { SetShowBetaReleases(bool), - RunVersionCheck(oneshot::Sender), + GetVersionInfo(oneshot::Sender), } impl VersionUpdaterHandle { @@ -132,11 +132,15 @@ impl VersionUpdaterHandle { } } - pub async fn run_version_check(&mut self) -> Result { + /// Get the latest cached [AppVersionInfo]. + /// + /// If the cache is stale or missing, this will immediately query the API for the latest + /// version. This may take a few seconds. + pub async fn get_version_info(&mut self) -> Result { let (done_tx, done_rx) = oneshot::channel(); if self .tx - .send(VersionUpdaterCommand::RunVersionCheck(done_tx)) + .send(VersionUpdaterCommand::GetVersionInfo(done_tx)) .await .is_err() { @@ -174,7 +178,7 @@ impl VersionUpdater { show_beta_releases, rx: Some(rx), availability_handle, - run_version_check_responders: vec![], + get_version_info_responders: vec![], }, VersionUpdaterHandle { tx }, ) @@ -186,7 +190,7 @@ impl VersionUpdater { } /// Immediately query the API for the latest [AppVersionInfo]. - fn query_app_version( + fn do_version_check( &mut self, ) -> Pin< Box> + Send + 'static>, @@ -226,7 +230,7 @@ impl VersionUpdater { /// [ApiAvailability](mullvad_api::availability::ApiAvailability). /// /// On any error, this function retries repeatedly every [UPDATE_INTERVAL_ERROR] until success. - fn query_app_version_in_background( + fn do_version_check_in_background( &self, ) -> Pin< Box> + Send + 'static>, @@ -328,9 +332,9 @@ impl VersionUpdater { /// Update [Self::last_app_version_info] and write it to disk cache. /// - /// Also, if we are currently have a pending [RunVersionCheck][rvc] command, respond to it. + /// Also, if we are currently have a pending [GetVersionInfo][rvc] command, respond to it. /// - /// [rvc]: VersionUpdaterCommand::RunVersionCheck + /// [rvc]: VersionUpdaterCommand::GetVersionInfo async fn update_version_info(&mut self, new_version_info: AppVersionInfo) { // if daemon can't be reached, return immediately if self.update_sender.send(new_version_info.clone()).is_err() { @@ -346,7 +350,7 @@ impl VersionUpdater { /// Get the time left until [Self::last_app_version_info] becomes stale, and should be /// refreshed, or [Duration::ZERO] if it already is stale. /// - /// This happens [UPDATE_INTERVAL] after the last version query. + /// This happens [UPDATE_INTERVAL] after the last version check. fn time_until_version_is_stale(&self) -> Duration { let now = SystemTime::now(); self @@ -360,13 +364,14 @@ impl VersionUpdater { .unwrap_or(Duration::ZERO) } + /// Is [Self::last_app_version_info] stale? fn version_is_stale(&self) -> bool { self.time_until_version_is_stale().is_zero() } /// Wait until [Self::last_app_version_info] becomes stale and needs to be refreshed. /// - /// This happens [UPDATE_INTERVAL] after the last version query. + /// This happens [UPDATE_INTERVAL] after the last version check. fn wait_until_version_is_stale(&self) -> Pin>> { let time_until_stale = self.time_until_version_is_stale(); @@ -375,13 +380,13 @@ impl VersionUpdater { Box::pin(talpid_time::sleep(time_until_stale).fuse()) } - /// Returns true if we are currently handling one or more `RunVersionCheck` commands. + /// Returns true if we are currently handling one or more `GetVersionInfo` commands. fn is_running_version_check(&self) -> bool { - !self.run_version_check_responders.is_empty() + !self.get_version_info_responders.is_empty() } pub async fn run(mut self) { - let mut rx = self.rx.take().unwrap().fuse(); + let mut rx = self.rx.take().unwrap(); let mut version_is_stale = self.wait_until_version_is_stale(); let mut version_check = futures::future::Fuse::terminated(); @@ -389,7 +394,7 @@ impl VersionUpdater { if *IS_DEV_BUILD { log::warn!("Not checking for updates because this is a development build"); while let Some(cmd) = rx.next().await { - if let VersionUpdaterCommand::RunVersionCheck(done_tx) = cmd { + if let VersionUpdaterCommand::GetVersionInfo(done_tx) = cmd { log::info!("Version check is disabled in dev builds"); let _ = done_tx.send(dev_version_cache()); } @@ -423,7 +428,7 @@ impl VersionUpdater { } } - Some(VersionUpdaterCommand::RunVersionCheck(done_tx)) => { + Some(VersionUpdaterCommand::GetVersionInfo(done_tx)) => { if self.update_sender.is_closed() { return; } @@ -435,9 +440,10 @@ impl VersionUpdater { _ => { // otherwise, start a foreground query to get the latest version_info. if !self.is_running_version_check() { - version_check = self.query_app_version().fuse(); + version_check = self.do_version_check().fuse(); } - self.run_version_check_responders.push(done_tx); + self.get_version_info_responders.retain(|r| !r.is_canceled()); + self.get_version_info_responders.push(done_tx); } } } @@ -455,7 +461,7 @@ impl VersionUpdater { if self.is_running_version_check() { continue; } - version_check = self.query_app_version_in_background().fuse(); + version_check = self.do_version_check_in_background().fuse(); }, response = version_check => { @@ -468,8 +474,8 @@ impl VersionUpdater { let new_version_info = self.response_to_version_info(version_info_response); - // Respond to all pending RunVersionCheck commands - for done_tx in self.run_version_check_responders.drain(..) { + // Respond to all pending GetVersionInfo commands + for done_tx in self.get_version_info_responders.drain(..) { let _ = done_tx.send(new_version_info.clone()); } @@ -478,7 +484,7 @@ impl VersionUpdater { } Err(err) => { log::error!("Failed to fetch version info: {err:#}"); - self.run_version_check_responders.clear(); + self.get_version_info_responders.clear(); } } From ec585b78b0155b79ac9153cd44c726c15b93f76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls?= Date: Wed, 17 Apr 2024 15:37:41 +0200 Subject: [PATCH 115/214] Remove logs older than 7 days, instead all of them --- ios/MullvadLogging/Logging.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/MullvadLogging/Logging.swift b/ios/MullvadLogging/Logging.swift index b8ad66c1916a..60ad30de6fe1 100644 --- a/ios/MullvadLogging/Logging.swift +++ b/ios/MullvadLogging/Logging.swift @@ -39,7 +39,7 @@ public struct LoggerBuilder { do { try LogRotation.rotateLogs(logDirectory: logsDirectoryURL, options: LogRotation.Options( storageSizeLimit: 5_242_880, // 5 MB - oldestAllowedDate: Date(timeIntervalSinceNow: Duration.days(7).timeInterval) + oldestAllowedDate: Date(timeIntervalSinceNow: -Duration.days(7).timeInterval) )) } catch { logRotationErrors.append(error) From 3f22b8dde675d91f807d68738231878969856e7f Mon Sep 17 00:00:00 2001 From: Niklas Berglund Date: Fri, 12 Apr 2024 16:50:16 +0200 Subject: [PATCH 116/214] Fix for failing nightly iOS end to end tests --- ios/Configurations/UITests.xcconfig.template | 2 -- ios/MullvadVPN.xcodeproj/project.pbxproj | 4 --- ios/MullvadVPNUITests/AccountTests.swift | 2 +- .../Pages/DNSSettingsPage.swift | 10 ++++++ ios/MullvadVPNUITests/RelayTests.swift | 31 ++++++++++++++++++- .../Test base classes/BaseUITestCase.swift | 2 -- .../LoggedInWithoutTimeUITestCase.swift | 26 ---------------- 7 files changed, 41 insertions(+), 36 deletions(-) delete mode 100644 ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift diff --git a/ios/Configurations/UITests.xcconfig.template b/ios/Configurations/UITests.xcconfig.template index c2a0fe821b9e..e39224eedfe0 100644 --- a/ios/Configurations/UITests.xcconfig.template +++ b/ios/Configurations/UITests.xcconfig.template @@ -7,8 +7,6 @@ IOS_DEVICE_PIN_CODE = TEST_DEVICE_IDENTIFIER_UUID = // Mullvad accounts used by UI tests -NO_TIME_ACCOUNT_NUMBER[config=Debug] = -NO_TIME_ACCOUNT_NUMBER[config=Staging] = HAS_TIME_ACCOUNT_NUMBER[config=Debug] = HAS_TIME_ACCOUNT_NUMBER[config=Staging] = FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER = diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index aa8c56501933..ea4432d9ef62 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -641,7 +641,6 @@ 8556EB562B9B0AC500D26DD4 /* RevokedDevicePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */; }; 855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */; }; 8587A05D2B84D43100152938 /* ChangeLogAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */; }; - 8590896C2B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089682B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift */; }; 8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */; }; 85A42B862BB1D627007BABF7 /* XCUIElement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A42B852BB1D627007BABF7 /* XCUIElement+Extensions.swift */; }; 85B267612B849ADB0098E3CD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 85B267602B849ADB0098E3CD /* mullvad-api.h */; }; @@ -1903,7 +1902,6 @@ 8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevokedDevicePage.swift; sourceTree = ""; }; 855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportPage.swift; sourceTree = ""; }; 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogAlert.swift; sourceTree = ""; }; - 859089682B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInWithoutTimeUITestCase.swift; sourceTree = ""; }; 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInWithTimeUITestCase.swift; sourceTree = ""; }; 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUITestCase.swift; sourceTree = ""; }; 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutUITestCase.swift; sourceTree = ""; }; @@ -3613,7 +3611,6 @@ isa = PBXGroup; children = ( 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */, - 859089682B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift */, 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */, 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */, 8518F6372B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift */, @@ -5652,7 +5649,6 @@ 852969362B4E9724007EAD4C /* AccessbilityIdentifier.swift in Sources */, 85E3BDE52B70E18C00FA71FD /* Networking.swift in Sources */, 85C7A2E92B89024B00035D5A /* SettingsTests.swift in Sources */, - 8590896C2B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift in Sources */, 8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */, 85557B202B5FBBD700795FE1 /* AccountPage.swift in Sources */, 852969352B4E9270007EAD4C /* LoginPage.swift in Sources */, diff --git a/ios/MullvadVPNUITests/AccountTests.swift b/ios/MullvadVPNUITests/AccountTests.swift index f78235360154..6c8d9094b9d5 100644 --- a/ios/MullvadVPNUITests/AccountTests.swift +++ b/ios/MullvadVPNUITests/AccountTests.swift @@ -58,7 +58,7 @@ class AccountTests: LoggedOutUITestCase { func testLogin() throws { LoginPage(app) .tapAccountNumberTextField() - .enterText(self.noTimeAccountNumber) + .enterText(hasTimeAccountNumber) .tapAccountNumberSubmitButton() .verifySuccessIconShown() .verifyDeviceLabelShown() diff --git a/ios/MullvadVPNUITests/Pages/DNSSettingsPage.swift b/ios/MullvadVPNUITests/Pages/DNSSettingsPage.swift index 9c994921fb84..e592862cbf33 100644 --- a/ios/MullvadVPNUITests/Pages/DNSSettingsPage.swift +++ b/ios/MullvadVPNUITests/Pages/DNSSettingsPage.swift @@ -60,6 +60,16 @@ class DNSSettingsPage: Page { return self } + @discardableResult func tapBlockAdsSwitchIfOn() -> Self { + let blockAdsSwitch = app.cells[AccessibilityIdentifier.blockAdvertising] + .switches[AccessibilityIdentifier.customSwitch] + + if blockAdsSwitch.value as? String == "1" { + tapBlockAdsSwitch() + } + return self + } + @discardableResult func tapBlockTrackerSwitch() -> Self { app.cells[AccessibilityIdentifier.blockTracking] .switches[AccessibilityIdentifier.customSwitch] diff --git a/ios/MullvadVPNUITests/RelayTests.swift b/ios/MullvadVPNUITests/RelayTests.swift index 6db6dd5f6126..b3ebedda0953 100644 --- a/ios/MullvadVPNUITests/RelayTests.swift +++ b/ios/MullvadVPNUITests/RelayTests.swift @@ -40,6 +40,22 @@ class RelayTests: LoggedInWithTimeUITestCase { } func testAdBlockingViaDNS() throws { + // Undo enabling block ads in teardown + addTeardownBlock { + HeaderBar(self.app) + .tapSettingsButton() + + SettingsPage(self.app) + .tapVPNSettingsCell() + + VPNSettingsPage(self.app) + .tapDNSSettingsCell() + + DNSSettingsPage(self.app) + .tapDNSContentBlockersHeaderExpandButton() + .tapBlockAdsSwitchIfOn() + } + HeaderBar(app) .tapSettingsButton() @@ -57,7 +73,7 @@ class RelayTests: LoggedInWithTimeUITestCase { TunnelControlPage(app) .tapSecureConnectionButton() - allowAddVPNConfigurations() // Allow adding VPN configurations iOS permission + allowAddVPNConfigurationsIfAsked() // Allow adding VPN configurations iOS permission TunnelControlPage(app) .waitForSecureConnectionLabel() @@ -90,6 +106,18 @@ class RelayTests: LoggedInWithTimeUITestCase { } func testWireGuardOverTCPManually() throws { + addTeardownBlock { + HeaderBar(self.app) + .tapSettingsButton() + + SettingsPage(self.app) + .tapVPNSettingsCell() + + VPNSettingsPage(self.app) + .tapWireGuardObfuscationExpandButton() + .tapWireGuardObfuscationOffCell() + } + HeaderBar(app) .tapSettingsButton() @@ -168,6 +196,7 @@ class RelayTests: LoggedInWithTimeUITestCase { .enterText("4001") .dismissKeyboard() .swipeDownToDismissModal() + .swipeDownToDismissModal() // After editing text field the table is first responder for the first swipe so we need to swipe twice to swipe the modal TunnelControlPage(app) .tapSecureConnectionButton() diff --git a/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift index ae4011cfc7ef..82feccc89ef5 100644 --- a/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift +++ b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift @@ -17,8 +17,6 @@ class BaseUITestCase: XCTestCase { // swiftlint:disable force_cast let displayName = Bundle(for: BaseUITestCase.self) .infoDictionary?["DisplayName"] as! String - let noTimeAccountNumber = Bundle(for: BaseUITestCase.self) - .infoDictionary?["NoTimeAccountNumber"] as! String let hasTimeAccountNumber = Bundle(for: BaseUITestCase.self) .infoDictionary?["HasTimeAccountNumber"] as! String let fiveWireGuardKeysAccountNumber = Bundle(for: BaseUITestCase.self) diff --git a/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift deleted file mode 100644 index 8c40c4e76abe..000000000000 --- a/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// LoggedInWithoutTimeUITestCase.swift -// MullvadVPNUITests -// -// Created by Niklas Berglund on 2024-01-23. -// Copyright © 2024 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -/// Base class for tests that should start from a state of being logged on to an account without time left -class LoggedInWithoutTimeUITestCase: BaseUITestCase { - override func setUp() { - super.setUp() - - agreeToTermsOfServiceIfShown() - dismissChangeLogIfShown() - logoutIfLoggedIn() - - login(accountNumber: noTimeAccountNumber) - - // Relaunch app so that tests start from a deterministic state - app.terminate() - app.launch() - } -} From 247b25ea3268ebce72209b4fdecab391fe91b4d1 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Wed, 17 Apr 2024 14:21:16 +0200 Subject: [PATCH 117/214] Remove unused fn `would_return` --- .../src/relay_selector/mod.rs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 3f92bd344bf8..758e580b0e76 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -527,30 +527,6 @@ impl RelaySelector { self.get_relay_with_custom_params(retry_attempt, &RETRY_ORDER, runtime_params) } - /// Peek at which [`TunnelType`] that would be returned for a certain connection attempt for a - /// given [`SelectorConfig`]. Returns [`Option::None`] if the given config would return a - /// custom tunnel endpoint. - /// - /// # Note - /// This function is only really useful for testing-purposes. It is exposed to ease testing of - /// other mullvad crates which depend on the retry behaviour of [`RelaySelector`]. - pub fn would_return(connection_attempt: usize, config: &SelectorConfig) -> Option { - match SpecializedSelectorConfig::from(config) { - // This case is not really interesting - SpecializedSelectorConfig::Custom(_) => None, - SpecializedSelectorConfig::Normal(config) => Some( - Self::pick_and_merge_query( - connection_attempt, - &RETRY_ORDER, - RuntimeParameters::default(), - RelayQuery::from(config), - ) - .tunnel_protocol - .unwrap_or(TunnelType::Wireguard), - ), - } - } - /// Returns a random relay and relay endpoint matching the current constraints defined by /// `retry_order` corresponding to `retry_attempt`. pub fn get_relay_with_custom_params( From c531b5708d4de9af9416e9f5fd2f0367f185e278 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Wed, 17 Apr 2024 14:22:14 +0200 Subject: [PATCH 118/214] Filter queries with no matching relays --- .../src/relay_selector/mod.rs | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 758e580b0e76..35c0e8a3e740 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -510,7 +510,7 @@ impl RelaySelector { } SpecializedSelectorConfig::Normal(pure_config) => { let parsed_relays = &self.parsed_relays.lock().unwrap(); - Self::get_relay_inner(query, parsed_relays, &pure_config) + Self::get_relay_inner(&query, parsed_relays, &pure_config) } } } @@ -547,14 +547,14 @@ impl RelaySelector { SpecializedSelectorConfig::Normal(normal_config) => { let parsed_relays = &self.parsed_relays.lock().unwrap(); // Merge user preferences with the relay selector's default preferences. - let user_preferences = RelayQuery::from(normal_config.clone()); let query = Self::pick_and_merge_query( retry_attempt, retry_order, runtime_params, - user_preferences, - ); - Self::get_relay_inner(query, parsed_relays, &normal_config) + &normal_config, + parsed_relays, + )?; + Self::get_relay_inner(&query, parsed_relays, &normal_config) } } } @@ -569,22 +569,29 @@ impl RelaySelector { /// Runtime parameters may affect which of the default queries that are considered. For example, /// queries which rely on IPv6 will not be considered if working IPv6 is not available at /// runtime. + /// + /// Returns an error iff the intersection between the user's preferences and every default retry + /// attempt-query yields queries with no matching relays. I.e., no retry attempt could ever + /// resolve to a relay. fn pick_and_merge_query( retry_attempt: usize, retry_order: &[RelayQuery], runtime_params: RuntimeParameters, - user_preferences: RelayQuery, - ) -> RelayQuery { - log::trace!("Merging user preferences {user_preferences:?} with default retry strategy"); + user_config: &NormalSelectorConfig<'_>, + parsed_relays: &ParsedRelays, + ) -> Result { + let user_query = RelayQuery::from(user_config.clone()); + log::trace!("Merging user preferences {user_query:?} with default retry strategy"); retry_order .iter() // Remove candidate queries based on runtime parameters before trying to merge user // settings .filter(|query| runtime_params.compatible(query)) - .cycle() - .filter_map(|query| query.clone().intersection(user_preferences.clone())) + .filter_map(|query| query.clone().intersection(user_query.clone())) + .filter(|query| Self::get_relay_inner(query, parsed_relays, user_config).is_ok()) + .cycle() // If the above filters remove all relays, cycle will also return an empty iterator .nth(retry_attempt) - .unwrap_or(user_preferences) + .ok_or(Error::NoRelay) } /// "Execute" the given query, yielding a final set of relays and/or bridges which the VPN @@ -605,7 +612,7 @@ impl RelaySelector { /// * An `Err` if no suitable bridge is found #[cfg(not(target_os = "android"))] fn get_relay_inner( - query: RelayQuery, + query: &RelayQuery, parsed_relays: &ParsedRelays, config: &NormalSelectorConfig<'_>, ) -> Result { @@ -622,7 +629,7 @@ impl RelaySelector { let mut new_query = query.clone(); new_query.tunnel_protocol = Constraint::Only(tunnel_type); // If a suitable relay is found, short-circuit and return it - if let Ok(relay) = Self::get_relay_inner(new_query, parsed_relays, config) { + if let Ok(relay) = Self::get_relay_inner(&new_query, parsed_relays, config) { return Ok(relay); } } @@ -659,7 +666,7 @@ impl RelaySelector { /// /// [`MullvadEndpoint`]: mullvad_types::endpoint::MullvadEndpoint fn get_wireguard_relay( - query: RelayQuery, + query: &RelayQuery, config: &NormalSelectorConfig<'_>, parsed_relays: &ParsedRelays, ) -> Result { @@ -668,13 +675,13 @@ impl RelaySelector { Constraint::Only(TunnelType::Wireguard) ); let inner = if !query.wireguard_constraints.multihop() { - Self::get_wireguard_singlehop_config(&query, config, parsed_relays)? + Self::get_wireguard_singlehop_config(query, config, parsed_relays)? } else { - Self::get_wireguard_multihop_config(&query, config, parsed_relays)? + Self::get_wireguard_multihop_config(query, config, parsed_relays)? }; - let endpoint = Self::get_wireguard_endpoint(&query, parsed_relays, &inner)?; + let endpoint = Self::get_wireguard_endpoint(query, parsed_relays, &inner)?; let obfuscator = - Self::get_wireguard_obfuscator(&query, inner.clone(), &endpoint, parsed_relays)?; + Self::get_wireguard_obfuscator(query, inner.clone(), &endpoint, parsed_relays)?; Ok(GetRelay::Wireguard { endpoint, @@ -822,16 +829,16 @@ impl RelaySelector { /// [`MullvadEndpoint`]: mullvad_types::endpoint::MullvadEndpoint #[cfg(not(target_os = "android"))] fn get_openvpn_relay( - query: RelayQuery, + query: &RelayQuery, config: &NormalSelectorConfig<'_>, parsed_relays: &ParsedRelays, ) -> Result { assert_eq!(query.tunnel_protocol, Constraint::Only(TunnelType::OpenVpn)); let exit = - Self::choose_openvpn_relay(&query, config, parsed_relays).ok_or(Error::NoRelay)?; - let endpoint = Self::get_openvpn_endpoint(&query, &exit, parsed_relays)?; + Self::choose_openvpn_relay(query, config, parsed_relays).ok_or(Error::NoRelay)?; + let endpoint = Self::get_openvpn_endpoint(query, &exit, parsed_relays)?; let bridge = - Self::get_openvpn_bridge(&query, &exit, &endpoint.protocol, parsed_relays, config)?; + Self::get_openvpn_bridge(query, &exit, &endpoint.protocol, parsed_relays, config)?; // FIXME: This assert would be better to encode at the type level. assert!(matches!(exit.endpoint_data, RelayEndpointData::Openvpn)); From 1e0a5831e7d206e4c4b05cf7a0c2d1c69757563b Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Wed, 17 Apr 2024 14:33:34 +0200 Subject: [PATCH 119/214] Replace refs to config with just custom list The entire config was passed around just for the custom lists. --- .../src/relay_selector/mod.rs | 79 ++++++++++--------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 35c0e8a3e740..843eea9a204a 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -472,7 +472,7 @@ impl RelaySelector { let near_location = match specialized_config { SpecializedSelectorConfig::Normal(config) => { let user_preferences = RelayQuery::from(config.clone()); - Self::get_relay_midpoint(&user_preferences, parsed_relays, &config) + Self::get_relay_midpoint(&user_preferences, parsed_relays, config.custom_lists) } SpecializedSelectorConfig::Custom(_) => None, }; @@ -508,9 +508,9 @@ impl RelaySelector { SpecializedSelectorConfig::Custom(custom_config) => { Ok(GetRelay::Custom(custom_config.clone())) } - SpecializedSelectorConfig::Normal(pure_config) => { + SpecializedSelectorConfig::Normal(normal_config) => { let parsed_relays = &self.parsed_relays.lock().unwrap(); - Self::get_relay_inner(&query, parsed_relays, &pure_config) + Self::get_relay_inner(&query, parsed_relays, normal_config.custom_lists) } } } @@ -554,7 +554,7 @@ impl RelaySelector { &normal_config, parsed_relays, )?; - Self::get_relay_inner(&query, parsed_relays, &normal_config) + Self::get_relay_inner(&query, parsed_relays, normal_config.custom_lists) } } } @@ -588,7 +588,7 @@ impl RelaySelector { // settings .filter(|query| runtime_params.compatible(query)) .filter_map(|query| query.clone().intersection(user_query.clone())) - .filter(|query| Self::get_relay_inner(query, parsed_relays, user_config).is_ok()) + .filter(|query| Self::get_relay_inner(query, parsed_relays, user_config.custom_lists).is_ok()) .cycle() // If the above filters remove all relays, cycle will also return an empty iterator .nth(retry_attempt) .ok_or(Error::NoRelay) @@ -614,14 +614,14 @@ impl RelaySelector { fn get_relay_inner( query: &RelayQuery, parsed_relays: &ParsedRelays, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, ) -> Result { match query.tunnel_protocol { Constraint::Only(TunnelType::Wireguard) => { - Self::get_wireguard_relay(query, config, parsed_relays) + Self::get_wireguard_relay(query, custom_lists, parsed_relays) } Constraint::Only(TunnelType::OpenVpn) => { - Self::get_openvpn_relay(query, config, parsed_relays) + Self::get_openvpn_relay(query, custom_lists, parsed_relays) } Constraint::Any => { // Try Wireguard, then OpenVPN, then fail @@ -629,7 +629,9 @@ impl RelaySelector { let mut new_query = query.clone(); new_query.tunnel_protocol = Constraint::Only(tunnel_type); // If a suitable relay is found, short-circuit and return it - if let Ok(relay) = Self::get_relay_inner(&new_query, parsed_relays, config) { + if let Ok(relay) = + Self::get_relay_inner(&new_query, parsed_relays, custom_lists) + { return Ok(relay); } } @@ -640,16 +642,17 @@ impl RelaySelector { #[cfg(target_os = "android")] fn get_relay_inner( - mut query: RelayQuery, + query: &RelayQuery, parsed_relays: &ParsedRelays, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, ) -> Result { // FIXME: A bit of defensive programming - calling `get_wiregurad_relay` with a query that // doesn't specify Wireguard as the desired tunnel type is not valid and will lead // to unwanted behavior. This should be seen as a workaround, and it would be nicer // to lift this invariant to be checked by the type system instead. + let mut query = query.clone(); query.tunnel_protocol = Constraint::Only(TunnelType::Wireguard); - Self::get_wireguard_relay(query, config, parsed_relays) + Self::get_wireguard_relay(&query, custom_lists, parsed_relays) } /// Derive a valid Wireguard relay configuration from `query`. @@ -667,7 +670,7 @@ impl RelaySelector { /// [`MullvadEndpoint`]: mullvad_types::endpoint::MullvadEndpoint fn get_wireguard_relay( query: &RelayQuery, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { assert_eq!( @@ -675,9 +678,9 @@ impl RelaySelector { Constraint::Only(TunnelType::Wireguard) ); let inner = if !query.wireguard_constraints.multihop() { - Self::get_wireguard_singlehop_config(query, config, parsed_relays)? + Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays)? } else { - Self::get_wireguard_multihop_config(query, config, parsed_relays)? + Self::get_wireguard_multihop_config(query, custom_lists, parsed_relays)? }; let endpoint = Self::get_wireguard_endpoint(query, parsed_relays, &inner)?; let obfuscator = @@ -697,11 +700,10 @@ impl RelaySelector { /// * `Ok(WireguardInner::Singlehop)` otherwise fn get_wireguard_singlehop_config( query: &RelayQuery, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { - let candidates = - filter_matching_relay_list(query, parsed_relays.relays(), config.custom_lists); + let candidates = filter_matching_relay_list(query, parsed_relays.relays(), custom_lists); helpers::pick_random_relay(&candidates) .cloned() .map(WireguardConfig::singlehop) @@ -717,7 +719,7 @@ impl RelaySelector { /// * `Ok(WireguardInner::Multihop)` otherwise fn get_wireguard_multihop_config( query: &RelayQuery, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { // Here, we modify the original query just a bit. @@ -732,16 +734,10 @@ impl RelaySelector { let mut exit_relay_query = query.clone(); // DAITA should only be enabled for the entry relay exit_relay_query.wireguard_constraints.daita = Constraint::Only(false); - let exit_candidates = filter_matching_relay_list( - &exit_relay_query, - parsed_relays.relays(), - config.custom_lists, - ); - let entry_candidates = filter_matching_relay_list( - &entry_relay_query, - parsed_relays.relays(), - config.custom_lists, - ); + let exit_candidates = + filter_matching_relay_list(&exit_relay_query, parsed_relays.relays(), custom_lists); + let entry_candidates = + filter_matching_relay_list(&entry_relay_query, parsed_relays.relays(), custom_lists); fn pick_random_excluding<'a>(list: &'a [Relay], exclude: &'a Relay) -> Option<&'a Relay> { list.iter() @@ -830,15 +826,20 @@ impl RelaySelector { #[cfg(not(target_os = "android"))] fn get_openvpn_relay( query: &RelayQuery, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { assert_eq!(query.tunnel_protocol, Constraint::Only(TunnelType::OpenVpn)); let exit = - Self::choose_openvpn_relay(query, config, parsed_relays).ok_or(Error::NoRelay)?; + Self::choose_openvpn_relay(query, custom_lists, parsed_relays).ok_or(Error::NoRelay)?; let endpoint = Self::get_openvpn_endpoint(query, &exit, parsed_relays)?; - let bridge = - Self::get_openvpn_bridge(query, &exit, &endpoint.protocol, parsed_relays, config)?; + let bridge = Self::get_openvpn_bridge( + query, + &exit, + &endpoint.protocol, + parsed_relays, + custom_lists, + )?; // FIXME: This assert would be better to encode at the type level. assert!(matches!(exit.endpoint_data, RelayEndpointData::Openvpn)); @@ -891,13 +892,13 @@ impl RelaySelector { relay: &Relay, protocol: &TransportProtocol, parsed_relays: &ParsedRelays, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, ) -> Result, Error> { if !BridgeQuery::should_use_bridge(&query.openvpn_constraints.bridge_settings) { Ok(None) } else { let bridge_query = &query.openvpn_constraints.bridge_settings.clone().unwrap(); - let custom_lists = &config.custom_lists; + let custom_lists = &custom_lists; match protocol { TransportProtocol::Udp => { log::error!("Can not use OpenVPN bridges over UDP"); @@ -1015,7 +1016,7 @@ impl RelaySelector { fn get_relay_midpoint( query: &RelayQuery, parsed_relays: &ParsedRelays, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, ) -> Option { use std::ops::Not; if query.location.is_any() { @@ -1023,7 +1024,7 @@ impl RelaySelector { } let matching_locations: Vec = - filter_matching_relay_list(query, parsed_relays.relays(), config.custom_lists) + filter_matching_relay_list(query, parsed_relays.relays(), custom_lists) .into_iter() .filter_map(|relay| relay.location) .unique_by(|location| location.city.clone()) @@ -1041,12 +1042,12 @@ impl RelaySelector { #[cfg(not(target_os = "android"))] fn choose_openvpn_relay( query: &RelayQuery, - config: &NormalSelectorConfig<'_>, + custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Option { // Filter among all valid relays let relays = parsed_relays.relays(); - let candidates = filter_matching_relay_list(query, relays, config.custom_lists); + let candidates = filter_matching_relay_list(query, relays, custom_lists); // Pick one of the valid relays. helpers::pick_random_relay(&candidates).cloned() } From ce59eb20d2cde53a829f891e5e0aca0938e654a8 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Wed, 17 Apr 2024 16:01:26 +0200 Subject: [PATCH 120/214] Add test --- .../tests/relay_selector.rs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs index d92e7a72a9cb..7cf2329e9ae8 100644 --- a/mullvad-relay-selector/tests/relay_selector.rs +++ b/mullvad-relay-selector/tests/relay_selector.rs @@ -1296,3 +1296,30 @@ fn test_daita() { ), } } + +/// Check that if the original user query would yield a relay, the result of running the query +/// which is the intersection between the user query and any of the default queries shall never +/// fail. +#[test] +fn valid_user_setting_should_yield_relay() { + // Make a valid user relay constraint + let location = GeographicLocationConstraint::hostname("se", "got", "se9-wireguard"); + let user_query = RelayQueryBuilder::new().location(location.clone()).build(); + let user_constraints = RelayQueryBuilder::new() + .location(location.clone()) + .into_constraint(); + + let config = SelectorConfig { + relay_settings: user_constraints.into(), + ..SelectorConfig::default() + }; + let relay_selector = RelaySelector::from_list(config, RELAYS.clone()); + let user_result = relay_selector.get_relay_by_query(user_query.clone()); + for retry_attempt in 0..RETRY_ORDER.len() { + let post_unification_result = + relay_selector.get_relay(retry_attempt, RuntimeParameters::default()); + if user_result.is_ok() { + assert!(post_unification_result.is_ok(), "Expected Post-unification query to be valid because original query {:#?} yielded a connection configuration", user_query) + } + } +} From d7da373cc614430b359ad3d93af3d20eef606d07 Mon Sep 17 00:00:00 2001 From: Niklas Berglund Date: Wed, 27 Mar 2024 15:55:18 +0100 Subject: [PATCH 121/214] Add test for login to account with too many devices --- ios/MullvadVPN.xcodeproj/project.pbxproj | 4 ++ .../Classes/AccessbilityIdentifier.swift | 6 +++ .../DeviceManagementContentView.swift | 3 ++ .../DeviceManagementViewController.swift | 2 + .../DeviceList/DeviceRowView.swift | 2 + ios/MullvadVPNUITests/AccountTests.swift | 39 ++++++++++++++- .../Networking/MullvadAPIWrapper.swift | 20 +++++--- .../Pages/DeviceManagementPage.swift | 49 +++++++++++++++++++ 8 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 ios/MullvadVPNUITests/Pages/DeviceManagementPage.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index ea4432d9ef62..f178e1cdcd7f 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -643,6 +643,7 @@ 8587A05D2B84D43100152938 /* ChangeLogAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */; }; 8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */; }; 85A42B862BB1D627007BABF7 /* XCUIElement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A42B852BB1D627007BABF7 /* XCUIElement+Extensions.swift */; }; + 85A42B882BB44D31007BABF7 /* DeviceManagementPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */; }; 85B267612B849ADB0098E3CD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 85B267602B849ADB0098E3CD /* mullvad-api.h */; }; 85C7A2E92B89024B00035D5A /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85C7A2E82B89024B00035D5A /* SettingsTests.swift */; }; 85D039982BA4711800940E7F /* SettingsMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D039972BA4711800940E7F /* SettingsMigrationTests.swift */; }; @@ -1906,6 +1907,7 @@ 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUITestCase.swift; sourceTree = ""; }; 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutUITestCase.swift; sourceTree = ""; }; 85A42B852BB1D627007BABF7 /* XCUIElement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+Extensions.swift"; sourceTree = ""; }; + 85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceManagementPage.swift; sourceTree = ""; }; 85B267602B849ADB0098E3CD /* mullvad-api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mullvad-api.h"; path = "../../mullvad-api/include/mullvad-api.h"; sourceTree = ""; }; 85C7A2E82B89024B00035D5A /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = ""; }; 85D039972BA4711800940E7F /* SettingsMigrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMigrationTests.swift; sourceTree = ""; }; @@ -3646,6 +3648,7 @@ 8529693B2B4F0257007EAD4C /* Alert.swift */, 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */, 852A26452BA9C9CB006EB9C8 /* DNSSettingsPage.swift */, + 85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */, 85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */, 852969342B4E9270007EAD4C /* LoginPage.swift */, 85139B2C2B84B4A700734217 /* OutOfTimePage.swift */, @@ -5661,6 +5664,7 @@ 85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */, 855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */, 8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */, + 85A42B882BB44D31007BABF7 /* DeviceManagementPage.swift in Sources */, 8532E6872B8CCED600ACECD1 /* ProblemReportSubmittedPage.swift in Sources */, 85FB5A0C2B6903990015DCED /* WelcomePage.swift in Sources */, 852A26462BA9C9CB006EB9C8 /* DNSSettingsPage.swift in Sources */, diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index cda791d04258..174408f0afd8 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -16,15 +16,19 @@ public enum AccessibilityIdentifier: String { case applyButton case cancelButton case connectionPanelButton + case continueWithLoginButton case collapseButton case expandButton case createAccountButton case deleteButton + case deviceCellRemoveButton case disconnectButton case revokedDeviceLoginButton case dnsSettingsEditButton case infoButton case learnAboutPrivacyButton + case logOutDeviceConfirmButton + case logOutDeviceCancelButton case loginBarButton case loginTextFieldButton case logoutButton @@ -41,6 +45,7 @@ public enum AccessibilityIdentifier: String { case settingsDoneButton // Cells + case deviceCell case vpnSettingsCell case dnsSettingsAddServerCell case dnsSettingsUseCustomDNSCell @@ -70,6 +75,7 @@ public enum AccessibilityIdentifier: String { case alertContainerView case alertTitle case changeLogAlert + case deviceManagementView case headerBarView case loginView case outOfTimeView diff --git a/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementContentView.swift b/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementContentView.swift index 9aab160be520..5effb05beb3a 100644 --- a/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementContentView.swift +++ b/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementContentView.swift @@ -69,6 +69,7 @@ class DeviceManagementContentView: UIView { for: .normal ) button.isEnabled = false + button.accessibilityIdentifier = .continueWithLoginButton return button }() @@ -112,6 +113,8 @@ class DeviceManagementContentView: UIView { addViews() constraintViews() updateView() + + accessibilityIdentifier = .deviceManagementView } private func addViews() { diff --git a/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementViewController.swift b/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementViewController.swift index c54c55836bcc..b36c48b2e74e 100644 --- a/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementViewController.swift +++ b/ios/MullvadVPN/View controllers/DeviceList/DeviceManagementViewController.swift @@ -212,6 +212,7 @@ class DeviceManagementViewController: UIViewController, RootContainment { comment: "" ), style: .destructive, + accessibilityId: .logOutDeviceConfirmButton, handler: { completion(true) } @@ -224,6 +225,7 @@ class DeviceManagementViewController: UIViewController, RootContainment { comment: "" ), style: .default, + accessibilityId: .logOutDeviceCancelButton, handler: { completion(false) } diff --git a/ios/MullvadVPN/View controllers/DeviceList/DeviceRowView.swift b/ios/MullvadVPN/View controllers/DeviceList/DeviceRowView.swift index 2cb3dcb731d8..1fec0aeffe03 100644 --- a/ios/MullvadVPN/View controllers/DeviceList/DeviceRowView.swift +++ b/ios/MullvadVPN/View controllers/DeviceList/DeviceRowView.swift @@ -70,6 +70,7 @@ class DeviceRowView: UIView { super.init(frame: .zero) + accessibilityIdentifier = .deviceCell backgroundColor = .primaryColor directionalLayoutMargins = UIMetrics.TableView.rowViewLayoutMargins @@ -90,6 +91,7 @@ class DeviceRowView: UIView { ) removeButton.addTarget(self, action: #selector(handleButtonTap(_:)), for: .touchUpInside) + removeButton.accessibilityIdentifier = .deviceCellRemoveButton NSLayoutConstraint.activate([ textLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor), diff --git a/ios/MullvadVPNUITests/AccountTests.swift b/ios/MullvadVPNUITests/AccountTests.swift index 6c8d9094b9d5..6ca45169f3bf 100644 --- a/ios/MullvadVPNUITests/AccountTests.swift +++ b/ios/MullvadVPNUITests/AccountTests.swift @@ -15,8 +15,6 @@ class AccountTests: LoggedOutUITestCase { try super.setUpWithError() } - override func tearDownWithError() throws {} - func testCreateAccount() throws { LoginPage(app) .tapCreateAccountButton() @@ -73,6 +71,43 @@ class AccountTests: LoggedOutUITestCase { .waitForPageToBeShown() // Verify still on login page } + func testLoginToAccountWithTooManyDevices() throws { + // Setup + let temporaryAccountNumber = try MullvadAPIWrapper().createAccount() + try MullvadAPIWrapper().addDevices(5, account: temporaryAccountNumber) + + // Teardown + addTeardownBlock { + do { + try MullvadAPIWrapper().deleteAccount(temporaryAccountNumber) + } catch { + XCTFail("Failed to delete account using app API") + } + } + + LoginPage(app) + .tapAccountNumberTextField() + .enterText(temporaryAccountNumber) + .tapAccountNumberSubmitButton() + + DeviceManagementPage(app) + .tapRemoveDeviceButton(cellIndex: 0) + + DeviceManagementLogOutDeviceConfirmationAlert(app) + .tapYesLogOutDeviceButton() + + DeviceManagementPage(app) + .tapContinueWithLoginButton() + + // First taken back to login page and automatically being logged in + LoginPage(app) + .verifySuccessIconShown() + .verifyDeviceLabelShown() + + // And then taken to out of time page because this account don't have any time added to it + OutOfTimePage(app) + } + func testLogOut() throws { let newAccountNumber = try MullvadAPIWrapper().createAccount() login(accountNumber: newAccountNumber) diff --git a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift index 25439c987a70..588b692a7a3b 100644 --- a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift +++ b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift @@ -6,6 +6,7 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import CryptoKit import Foundation import XCTest @@ -48,15 +49,13 @@ class MullvadAPIWrapper { return port } - /// Generate a mock WireGuard key + /// Generate a mock public WireGuard key private func generateMockWireGuardKey() -> Data { - var bytes = [UInt8]() + let privateKey = Curve25519.KeyAgreement.PrivateKey() + let publicKey = privateKey.publicKey + let publicKeyData = publicKey.rawRepresentation - for _ in 0 ..< 44 { - bytes.append(UInt8.random(in: 0 ..< 255)) - } - - return Data(bytes) + return publicKeyData } func createAccount() -> String { @@ -88,6 +87,13 @@ class MullvadAPIWrapper { } } + /// Add multiple devices to specified account. Dummy WireGuard keys will be generated. + func addDevices(_ numberOfDevices: Int, account: String) throws { + for _ in 0 ..< numberOfDevices { + try self.addDevice(account) + } + } + func getAccountExpiry(_ account: String) throws -> Date { do { let accountExpiryTimestamp = Double(try mullvadAPI.getExpiry(forAccount: account)) diff --git a/ios/MullvadVPNUITests/Pages/DeviceManagementPage.swift b/ios/MullvadVPNUITests/Pages/DeviceManagementPage.swift new file mode 100644 index 000000000000..571fb1cfd6ec --- /dev/null +++ b/ios/MullvadVPNUITests/Pages/DeviceManagementPage.swift @@ -0,0 +1,49 @@ +// +// DeviceManagementPage.swift +// MullvadVPNUITests +// +// Created by Niklas Berglund on 2024-03-27. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import XCTest + +/// Page class for the "too many devices" page shown when logging on to an account with too many devices +class DeviceManagementPage: Page { + override init(_ app: XCUIApplication) { + super.init(app) + + self.pageAccessibilityIdentifier = .deviceManagementView + waitForPageToBeShown() + } + + @discardableResult func tapRemoveDeviceButton(cellIndex: Int) -> Self { + app + .otherElements.matching(identifier: AccessibilityIdentifier.deviceCell.rawValue).element(boundBy: cellIndex) + .buttons[AccessibilityIdentifier.deviceCellRemoveButton] + .tap() + + return self + } + + @discardableResult func tapContinueWithLoginButton() -> Self { + app.buttons[AccessibilityIdentifier.continueWithLoginButton].tap() + return self + } +} + +/// Confirmation alert displayed when removing a device +class DeviceManagementLogOutDeviceConfirmationAlert: Page { + override init(_ app: XCUIApplication) { + super.init(app) + + self.pageAccessibilityIdentifier = .alertContainerView + waitForPageToBeShown() + } + + @discardableResult func tapYesLogOutDeviceButton() -> Self { + app.buttons[AccessibilityIdentifier.logOutDeviceConfirmButton] + .tap() + return self + } +} From 6719c48ffaf894dc88678dbb209f11bca9dffd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls?= Date: Tue, 16 Apr 2024 12:42:24 +0200 Subject: [PATCH 122/214] Limit the size of custom list names to 30 graphemes --- mullvad-daemon/src/custom_list.rs | 55 ++-------------- mullvad-daemon/src/lib.rs | 8 +-- mullvad-daemon/src/management_interface.rs | 10 --- mullvad-daemon/src/settings/mod.rs | 37 ++++++++++- mullvad-management-interface/src/lib.rs | 1 + mullvad-types/src/custom_list.rs | 74 ++++++++++++++++++++-- 6 files changed, 113 insertions(+), 72 deletions(-) diff --git a/mullvad-daemon/src/custom_list.rs b/mullvad-daemon/src/custom_list.rs index df9f30a51921..71c5fc77e9c4 100644 --- a/mullvad-daemon/src/custom_list.rs +++ b/mullvad-daemon/src/custom_list.rs @@ -14,22 +14,11 @@ where /// /// Returns an error if the name is not unique. pub async fn create_custom_list(&mut self, name: String) -> Result { - if self - .settings - .custom_lists - .iter() - .any(|list| list.name == name) - { - return Err(Error::CustomListExists); - } - - let new_list = CustomList::new(name); + let new_list = CustomList::new(name).map_err(crate::Error::CustomListError)?; let id = new_list.id; self.settings - .update(|settings| { - settings.custom_lists.add(new_list); - }) + .try_update(|settings| settings.custom_lists.add(new_list)) .await .map_err(Error::SettingsError)?; @@ -40,20 +29,12 @@ where /// /// Returns an error if the list doesn't exist. pub async fn delete_custom_list(&mut self, id: Id) -> Result<(), Error> { - let Some(list_index) = self - .settings - .custom_lists - .iter() - .position(|elem| elem.id == id) - else { - return Err(Error::CustomListNotFound); - }; let settings_changed = self .settings - .update(|settings| { + .try_update(|settings| { // NOTE: Not using swap remove because it would make user output slightly // more confusing and the cost is so small. - settings.custom_lists.remove(list_index); + settings.custom_lists.remove(&id) }) .await .map_err(Error::SettingsError); @@ -78,32 +59,10 @@ where /// - there is no existing list with the same ID, /// - or the existing list has a different name. pub async fn update_custom_list(&mut self, new_list: CustomList) -> Result<(), Error> { - let Some((list_index, old_list)) = self - .settings - .custom_lists - .iter() - .enumerate() - .find(|elem| elem.1.id == new_list.id) - else { - return Err(Error::CustomListNotFound); - }; - let id = old_list.id; - - if old_list.name != new_list.name - && self - .settings - .custom_lists - .iter() - .any(|list| list.name == new_list.name) - { - return Err(Error::CustomListExists); - } - + let list_id = new_list.id; let settings_changed = self .settings - .update(|settings| { - settings.custom_lists[list_index] = new_list; - }) + .try_update(|settings| settings.custom_lists.update(new_list)) .await .map_err(Error::SettingsError); @@ -111,7 +70,7 @@ where self.relay_selector .set_config(new_selector_config(&self.settings)); - if self.change_should_cause_reconnect(Some(id)) { + if self.change_should_cause_reconnect(Some(list_id)) { log::info!("Initiating tunnel restart because a selected custom list changed"); self.reconnect_tunnel(); } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index a6b6794d33d0..875f797ec3d9 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -173,12 +173,8 @@ pub enum Error { TunnelError(#[source] tunnel_state_machine::Error), /// Custom list already exists - #[error("A list with that name already exists")] - CustomListExists, - - /// Custom list does not exist - #[error("A list with that name does not exist")] - CustomListNotFound, + #[error("Custom list error: {0}")] + CustomListError(#[source] mullvad_types::custom_list::Error), #[error("Access method error")] AccessMethodError(#[source] access_method::Error), diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index ce203e7ba8d6..0699ab697eb3 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -1115,16 +1115,6 @@ fn map_daemon_error(error: crate::Error) -> Status { DaemonError::NoAccountToken | DaemonError::NoAccountTokenHistory => { Status::unauthenticated(error.to_string()) } - DaemonError::CustomListExists => Status::with_details( - Code::AlreadyExists, - error.to_string(), - mullvad_management_interface::CUSTOM_LIST_LIST_EXISTS_DETAILS.into(), - ), - DaemonError::CustomListNotFound => Status::with_details( - Code::NotFound, - error.to_string(), - mullvad_management_interface::CUSTOM_LIST_LIST_NOT_FOUND_DETAILS.into(), - ), error => Status::unknown(error.to_string()), } } diff --git a/mullvad-daemon/src/settings/mod.rs b/mullvad-daemon/src/settings/mod.rs index 243764ec901a..1e710eebba38 100644 --- a/mullvad-daemon/src/settings/mod.rs +++ b/mullvad-daemon/src/settings/mod.rs @@ -1,5 +1,7 @@ #[cfg(not(target_os = "android"))] use futures::TryFutureExt; +#[cfg(not(target_os = "android"))] +use mullvad_types::custom_list::Error as CustomListError; use mullvad_types::{ relay_constraints::{RelayConstraints, RelaySettings, WireguardConstraints}, settings::{DnsState, Settings}, @@ -47,11 +49,18 @@ pub enum Error { impl From for mullvad_management_interface::Status { fn from(error: Error) -> mullvad_management_interface::Status { use mullvad_management_interface::{Code, Status}; - match error { Error::DeleteError(..) | Error::WriteError(..) | Error::ReadError(..) => { Status::new(Code::FailedPrecondition, error.to_string()) } + Error::UpdateFailed(err) + if err + .downcast_ref::() + .is_some() => + { + let custom_list_err = *err.downcast::().unwrap(); + handle_custom_list_error(custom_list_err) + } Error::SerializeError(..) | Error::ParseError(..) | Error::UpdateFailed(..) => { Status::new(Code::Internal, error.to_string()) } @@ -59,6 +68,32 @@ impl From for mullvad_management_interface::Status { } } +#[cfg(not(target_os = "android"))] +fn handle_custom_list_error( + custom_list_err: CustomListError, +) -> mullvad_management_interface::Status { + use mullvad_management_interface::{Code, Status}; + match custom_list_err { + error @ CustomListError::ListExists | error @ CustomListError::DuplicateName => { + Status::with_details( + Code::AlreadyExists, + error.to_string(), + mullvad_management_interface::CUSTOM_LIST_LIST_EXISTS_DETAILS.into(), + ) + } + error @ CustomListError::NameTooLong => Status::with_details( + Code::InvalidArgument, + error.to_string(), + mullvad_management_interface::CUSTOM_LIST_LIST_NAME_TOO_LONG_DETAILS.into(), + ), + error @ CustomListError::ListNotFound => Status::with_details( + Code::NotFound, + error.to_string(), + mullvad_management_interface::CUSTOM_LIST_LIST_NOT_FOUND_DETAILS.into(), + ), + } +} + pub struct SettingsPersister { settings: Settings, path: PathBuf, diff --git a/mullvad-management-interface/src/lib.rs b/mullvad-management-interface/src/lib.rs index 882db86ae1f3..763d2474ecc7 100644 --- a/mullvad-management-interface/src/lib.rs +++ b/mullvad-management-interface/src/lib.rs @@ -28,6 +28,7 @@ static MULLVAD_MANAGEMENT_SOCKET_GROUP: Lazy> = pub const CUSTOM_LIST_LIST_NOT_FOUND_DETAILS: &[u8] = b"custom_list_list_not_found"; pub const CUSTOM_LIST_LIST_EXISTS_DETAILS: &[u8] = b"custom_list_list_exists"; +pub const CUSTOM_LIST_LIST_NAME_TOO_LONG_DETAILS: &[u8] = b"custom_list_list_name_too_long"; #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/mullvad-types/src/custom_list.rs b/mullvad-types/src/custom_list.rs index 89febe81abad..0f6ca7d46170 100644 --- a/mullvad-types/src/custom_list.rs +++ b/mullvad-types/src/custom_list.rs @@ -11,6 +11,20 @@ use std::{ str::FromStr, }; +const CUSTOM_LIST_NAME_MAX_SIZE: usize = 30; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Custom list name too long")] + NameTooLong, + #[error("Custom list with name already exists")] + DuplicateName, + #[error("Custom list not found")] + ListNotFound, + #[error("List with given ID already exists")] + ListExists, +} + #[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct Id(uuid::Uuid); @@ -91,18 +105,60 @@ impl From> for CustomListsSettings { } impl CustomListsSettings { - pub fn add(&mut self, list: CustomList) { - self.custom_lists.push(list); + pub fn add(&mut self, new_list: CustomList) -> Result<(), Error> { + self.check_if_id_is_unique(&new_list)?; + self.check_list_name_is_unique(&new_list)?; + self.custom_lists.push(new_list); + Ok(()) } - pub fn remove(&mut self, index: usize) { - self.custom_lists.remove(index); + pub fn remove(&mut self, list_id: &Id) -> Result<(), Error> { + let Some(list_index) = self.find_list_index(list_id) else { + return Err(Error::ListNotFound); + }; + self.custom_lists.remove(list_index); + Ok(()) } /// Remove all custom lists pub fn clear(&mut self) { self.custom_lists.clear(); } + + pub fn update(&mut self, new_list: CustomList) -> Result<(), Error> { + let list_index = self + .find_list_index(&new_list.id) + .ok_or(Error::ListNotFound)?; + self.check_list_name_is_unique(&new_list)?; + self.custom_lists[list_index] = new_list; + Ok(()) + } + + fn check_list_name_is_unique(&self, new_list: &CustomList) -> Result<(), Error> { + if self + .custom_lists + .iter() + .any(|list| list.name == new_list.name && list.id != new_list.id) + { + return Err(Error::DuplicateName); + } + Ok(()) + } + + fn check_if_id_is_unique(&self, new_list: &CustomList) -> Result<(), Error> { + if self.custom_lists.iter().any(|list| list.id == new_list.id) { + return Err(Error::ListExists); + } + Ok(()) + } + + fn find_list_index(&self, list_id: &Id) -> Option { + self.custom_lists + .iter() + .enumerate() + .find(|(_idx, list)| list.id == *list_id) + .map(|(idx, _list)| idx) + } } impl IntoIterator for CustomListsSettings { @@ -144,12 +200,16 @@ pub struct CustomList { } impl CustomList { - pub fn new(name: String) -> Self { - CustomList { + pub fn new(name: String) -> Result { + if name.chars().count() > CUSTOM_LIST_NAME_MAX_SIZE { + return Err(Error::NameTooLong); + } + + Ok(CustomList { id: Id(uuid::Uuid::new_v4()), name, locations: BTreeSet::new(), - } + }) } } From 48226eeb5596df68f43fe7a3104a1f2ce3892ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Thu, 18 Apr 2024 10:07:06 +0200 Subject: [PATCH 123/214] Bump binaries submodule Also update driverlogic to point to the new wireguard header --- dist-assets/binaries | 2 +- windows/driverlogic/driverlogic.vcxproj | 6 +++--- windows/driverlogic/driverlogic.vcxproj.filters | 2 +- windows/driverlogic/src/driverlogic.cpp | 2 +- windows/driverlogic/src/{wireguard_dll.h => wireguard.h} | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename windows/driverlogic/src/{wireguard_dll.h => wireguard.h} (96%) diff --git a/dist-assets/binaries b/dist-assets/binaries index d9edc48af33d..7db2c76522e2 160000 --- a/dist-assets/binaries +++ b/dist-assets/binaries @@ -1 +1 @@ -Subproject commit d9edc48af33db4afbcb8d6c7754e1dfe963d5172 +Subproject commit 7db2c76522e29b4acd8f461fc87f794954c6df95 diff --git a/windows/driverlogic/driverlogic.vcxproj b/windows/driverlogic/driverlogic.vcxproj index ab9e6c19b5f5..c68d0568ab82 100644 --- a/windows/driverlogic/driverlogic.vcxproj +++ b/windows/driverlogic/driverlogic.vcxproj @@ -61,7 +61,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../../dist-assets/binaries/wireguard-nt/api/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ + $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ stdcpp20 MultiThreadedDebug @@ -83,7 +83,7 @@ NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 - $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../../dist-assets/binaries/wireguard-nt/api/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ + $(ProjectDir)../../dist-assets/binaries/x86_64-pc-windows-msvc/;$(ProjectDir)../windows-libraries/src/;$(ProjectDir)../ MultiThreaded @@ -115,7 +115,7 @@ - + diff --git a/windows/driverlogic/driverlogic.vcxproj.filters b/windows/driverlogic/driverlogic.vcxproj.filters index fae74cfdee3e..bd73d300299b 100644 --- a/windows/driverlogic/driverlogic.vcxproj.filters +++ b/windows/driverlogic/driverlogic.vcxproj.filters @@ -26,6 +26,6 @@ - + \ No newline at end of file diff --git a/windows/driverlogic/src/driverlogic.cpp b/windows/driverlogic/src/driverlogic.cpp index 795960b6d2f9..7cd8cf5f51fc 100644 --- a/windows/driverlogic/src/driverlogic.cpp +++ b/windows/driverlogic/src/driverlogic.cpp @@ -4,7 +4,7 @@ #include "service.h" #include "log.h" #include "wintun.h" -#include "wireguard_dll.h" +#include "wireguard.h" #include "devenum.h" #include #include diff --git a/windows/driverlogic/src/wireguard_dll.h b/windows/driverlogic/src/wireguard.h similarity index 96% rename from windows/driverlogic/src/wireguard_dll.h rename to windows/driverlogic/src/wireguard.h index 7260ce5b4dfa..e99da56c05c5 100644 --- a/windows/driverlogic/src/wireguard_dll.h +++ b/windows/driverlogic/src/wireguard.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include "util.h" From c80d2d35b33f367bf4aec014c3751663b616fb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Thu, 18 Apr 2024 10:21:22 +0200 Subject: [PATCH 124/214] Remove pointless submodule update --- .github/workflows/daemon.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/daemon.yml b/.github/workflows/daemon.yml index a5107fbcf8c1..3293e028791e 100644 --- a/.github/workflows/daemon.yml +++ b/.github/workflows/daemon.yml @@ -114,10 +114,6 @@ jobs: - name: Checkout submodules run: git submodule update --init --depth=1 - - name: Checkout wireguard-nt - working-directory: dist-assets/binaries - run: git submodule update --init -- wireguard-nt - - name: Install Protoc uses: arduino/setup-protoc@v1 with: From c0b5ad710dcf8474f91540c0182fe3d3e1e91c36 Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Thu, 18 Apr 2024 10:45:36 +0200 Subject: [PATCH 125/214] Fix missing cities and relays in custom list --- .../relaylist/RelayItemExtensions.kt | 91 +++++++++++++++++++ .../relaylist/RelayListExtensions.kt | 35 ------- .../mullvadvpn/usecase/RelayListUseCase.kt | 6 +- .../viewmodel/SelectLocationViewModel.kt | 9 +- 4 files changed, 98 insertions(+), 43 deletions(-) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt index a71005a78a57..3f138dee29dc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt @@ -1,6 +1,10 @@ package net.mullvad.mullvadvpn.relaylist +import java.lang.IllegalArgumentException +import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.LocationConstraint +import net.mullvad.mullvadvpn.model.Ownership +import net.mullvad.mullvadvpn.model.Providers fun RelayItem.toLocationConstraint(): LocationConstraint { return when (this) { @@ -24,3 +28,90 @@ fun RelayItem.descendants(): List { val children = children() return children + children.flatMap { it.descendants() } } + +private fun RelayItem.hasOwnership(ownershipConstraint: Constraint): Boolean = + if (ownershipConstraint is Constraint.Only) { + when (this) { + is RelayItem.Country -> cities.any { it.hasOwnership(ownershipConstraint) } + is RelayItem.City -> relays.any { it.hasOwnership(ownershipConstraint) } + is RelayItem.Relay -> this.ownership == ownershipConstraint.value + is RelayItem.CustomList -> locations.any { it.hasOwnership(ownershipConstraint) } + } + } else { + true + } + +private fun RelayItem.hasProvider(providersConstraint: Constraint): Boolean = + if (providersConstraint is Constraint.Only) { + when (this) { + is RelayItem.Country -> cities.any { it.hasProvider(providersConstraint) } + is RelayItem.City -> relays.any { it.hasProvider(providersConstraint) } + is RelayItem.Relay -> providersConstraint.value.providers.contains(providerName) + is RelayItem.CustomList -> locations.any { it.hasProvider(providersConstraint) } + } + } else { + true + } + +fun RelayItem.filterOnOwnershipAndProvider( + ownership: Constraint, + providers: Constraint +): RelayItem? = + when (this) { + is RelayItem.City -> filterOnOwnershipAndProvider(ownership, providers) + is RelayItem.Country -> filterOnOwnershipAndProvider(ownership, providers) + is RelayItem.CustomList -> filterOnOwnershipAndProvider(ownership, providers) + is RelayItem.Relay -> filterOnOwnershipAndProvider(ownership, providers) + } + +fun RelayItem.CustomList.filterOnOwnershipAndProvider( + ownership: Constraint, + providers: Constraint +): RelayItem.CustomList { + val newLocations = + locations.mapNotNull { + when (it) { + is RelayItem.City -> it.filterOnOwnershipAndProvider(ownership, providers) + is RelayItem.Country -> it.filterOnOwnershipAndProvider(ownership, providers) + is RelayItem.CustomList -> + throw IllegalArgumentException("CustomList can't contain CustomList") + is RelayItem.Relay -> it.filterOnOwnershipAndProvider(ownership, providers) + } + } + return copy(locations = newLocations) +} + +fun RelayItem.Country.filterOnOwnershipAndProvider( + ownership: Constraint, + providers: Constraint +): RelayItem.Country? { + val cities = cities.mapNotNull { it.filterOnOwnershipAndProvider(ownership, providers) } + return if (cities.isNotEmpty()) { + this.copy(cities = cities) + } else { + null + } +} + +private fun RelayItem.City.filterOnOwnershipAndProvider( + ownership: Constraint, + providers: Constraint +): RelayItem.City? { + val relays = relays.mapNotNull { it.filterOnOwnershipAndProvider(ownership, providers) } + return if (relays.isNotEmpty()) { + this.copy(relays = relays) + } else { + null + } +} + +private fun RelayItem.Relay.filterOnOwnershipAndProvider( + ownership: Constraint, + providers: Constraint +): RelayItem.Relay? { + return if (hasOwnership(ownership) && hasProvider(providers)) { + this + } else { + null + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt index 8d2ea5f34850..78b3732734ad 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt @@ -1,9 +1,7 @@ package net.mullvad.mullvadvpn.relaylist -import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.GeographicLocationConstraint import net.mullvad.mullvadvpn.model.Ownership -import net.mullvad.mullvadvpn.model.Providers import net.mullvad.mullvadvpn.model.Relay as DaemonRelay import net.mullvad.mullvadvpn.model.RelayList @@ -175,39 +173,6 @@ private fun List.filterValidRelays(): List = filter { it.isWireguardRelay } -fun List.filterOnOwnershipAndProviders( - ownership: Constraint, - providers: Constraint -): List { - return map { country -> - val cities = - country.cities.map { city -> - val relays = - city.relays.filterRelayByOwnershipAndProviders(ownership, providers) - city.copy(relays = relays) - } - country.copy(cities = cities.filter { it.relays.isNotEmpty() }) - } - .filter { it.cities.isNotEmpty() } -} - -private fun List.filterRelayByOwnershipAndProviders( - ownership: Constraint, - providers: Constraint -): List = - filter { - when (ownership) { - is Constraint.Any -> true - is Constraint.Only -> it.ownership == ownership.value - } - } - .filter { relay -> - when (providers) { - is Constraint.Any -> true - is Constraint.Only -> providers.value.providers.contains(relay.providerName) - } - } - /** Expand the parent(s), if any, for the current selected item */ private fun List.expandItemForSelection( selectedItem: RelayItem? diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt index bdeaac0d0c3f..b4197fe7b7a5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt @@ -9,7 +9,7 @@ import net.mullvad.mullvadvpn.model.RelaySettings import net.mullvad.mullvadvpn.model.WireguardConstraints import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.RelayList -import net.mullvad.mullvadvpn.relaylist.filterOnOwnershipAndProviders +import net.mullvad.mullvadvpn.relaylist.filterOnOwnershipAndProvider import net.mullvad.mullvadvpn.relaylist.findItemForGeographicLocationConstraint import net.mullvad.mullvadvpn.relaylist.toRelayCountries import net.mullvad.mullvadvpn.relaylist.toRelayItemLists @@ -41,11 +41,11 @@ class RelayListUseCase( val customLists = settings?.customLists?.customLists?.toRelayItemLists(relayCountries) ?: emptyList() val relayCountriesFiltered = - relayCountries.filterOnOwnershipAndProviders(ownership, providers) + relayCountries.mapNotNull { it.filterOnOwnershipAndProvider(ownership, providers) } val selectedItem = findSelectedRelayItem( relaySettings = settings?.relaySettings, - relayCountries = relayCountriesFiltered, + relayCountries = relayCountries, customLists = customLists, ) RelayList( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt index 586bbf8bc172..1759055bda9e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt @@ -19,6 +19,7 @@ import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.Ownership import net.mullvad.mullvadvpn.relaylist.Provider import net.mullvad.mullvadvpn.relaylist.RelayItem +import net.mullvad.mullvadvpn.relaylist.filterOnOwnershipAndProvider import net.mullvad.mullvadvpn.relaylist.filterOnSearchTerm import net.mullvad.mullvadvpn.relaylist.toLocationConstraint import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager @@ -66,11 +67,9 @@ class SelectLocationViewModel( val filteredCustomLists = customLists.filterOnSearchTerm(searchTerm).map { customList -> - customList.copy( - locations = - customList.locations.filter { location -> - filteredRelayCountries.any { it.code == location.code } - } + customList.filterOnOwnershipAndProvider( + selectedOwnership, + selectedConstraintProviders ) } From 25fb6959b91fce23d1eba877fa1763553046f745 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Thu, 18 Apr 2024 13:47:23 +0200 Subject: [PATCH 126/214] Remove support for iPad layout --- ios/MullvadVPN.xcodeproj/project.pbxproj | 190 +++++++++++++++------ ios/MullvadVPN/Supporting Files/Info.plist | 7 - 2 files changed, 140 insertions(+), 57 deletions(-) diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index f178e1cdcd7f..80f5499f8b54 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -5921,11 +5921,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -5961,11 +5962,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6007,7 +6009,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6047,7 +6049,7 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6068,7 +6070,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6089,7 +6091,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6105,9 +6107,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6125,9 +6129,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6144,7 +6150,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6161,7 +6170,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6197,7 +6209,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6235,7 +6247,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6274,7 +6286,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6313,7 +6325,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6336,7 +6348,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6357,7 +6369,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6496,8 +6508,11 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6517,7 +6532,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6536,8 +6554,11 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6556,7 +6577,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6573,7 +6597,10 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Debug; @@ -6591,7 +6618,10 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Release; @@ -6622,11 +6652,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -6657,11 +6688,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6692,11 +6724,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -6727,11 +6760,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6767,7 +6801,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -6803,7 +6837,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6849,9 +6883,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6867,9 +6903,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6912,6 +6950,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6953,6 +6992,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6980,7 +7020,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -7006,7 +7046,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -7044,11 +7084,13 @@ PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "MullvadVPN app integration tests"; SECURITY_GROUP_IDENTIFIER = group.net.mullvad.MullvadVPN; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG MULLVAD_ENVIRONMENT_PRODUCTION $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Debug; @@ -7076,10 +7118,12 @@ PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "MullvadVPN app integration tests"; SECURITY_GROUP_IDENTIFIER = group.net.mullvad.MullvadVPN; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Release; @@ -7165,8 +7209,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7203,8 +7250,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7221,7 +7271,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7238,7 +7291,10 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Staging; @@ -7269,11 +7325,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7290,9 +7347,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7327,11 +7386,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7350,9 +7410,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7382,11 +7444,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7422,7 +7485,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7464,7 +7527,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7485,7 +7548,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7522,7 +7585,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -7545,7 +7608,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7581,7 +7644,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -7626,6 +7689,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -7653,7 +7717,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7675,9 +7739,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; PRODUCT_NAME = MullvadVPNUITests; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "MullvadVPN app integration tests"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG MULLVAD_ENVIRONMENT_STAGING"; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Staging; @@ -7696,9 +7763,12 @@ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release"; PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; PRODUCT_NAME = MullvadVPNUITests; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = MULLVAD_ENVIRONMENT_PRODUCTION; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = MockRelease; @@ -7778,7 +7848,10 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7812,7 +7885,10 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7829,7 +7905,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7846,7 +7925,10 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = MockRelease; @@ -7877,11 +7959,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -7898,9 +7981,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7935,11 +8020,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -7958,9 +8044,11 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7990,11 +8078,12 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -8030,7 +8119,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -8071,7 +8160,7 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -8092,7 +8181,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -8129,7 +8218,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -8152,7 +8241,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -8188,7 +8277,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -8230,6 +8319,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -8257,7 +8347,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; diff --git a/ios/MullvadVPN/Supporting Files/Info.plist b/ios/MullvadVPN/Supporting Files/Info.plist index b6b1ffa0031f..8ea1a5074af3 100644 --- a/ios/MullvadVPN/Supporting Files/Info.plist +++ b/ios/MullvadVPN/Supporting Files/Info.plist @@ -111,12 +111,5 @@ UIInterfaceOrientationPortrait - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - From f7050a927b5062ce24a4071a78c894963f1408fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 17 Apr 2024 17:19:11 +0200 Subject: [PATCH 127/214] Bump pfctl to 0.4.6 This includes a function to clear connection states --- Cargo.lock | 4 ++-- talpid-core/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eedb5dd48029..fb0d7780c63f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2665,9 +2665,9 @@ dependencies = [ [[package]] name = "pfctl" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e0c1e1bc65fb241166b7ec8278d89cc2432d41adcbe57ffe1095c81e1d7b44" +checksum = "27590368dee28aa01e3024b639818a6bf0ad31635d9eca000aad63021a59284d" dependencies = [ "derive_builder", "errno 0.2.8", diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index da10c6851382..923ce42a1156 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -48,7 +48,7 @@ duct = "0.13" [target.'cfg(target_os = "macos")'.dependencies] async-trait = "0.1" duct = "0.13" -pfctl = "0.4.4" +pfctl = "0.4.6" subslice = "0.2" system-configuration = "0.5.1" hickory-proto = { git = "https://github.com/mullvad/hickory-dns", rev = "9e8f8c67fbcb6d2985503027362a3fb022529802" } From a38d103f5e901ce1da2fde4278c2cad03cd92194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 17 Apr 2024 11:04:04 +0200 Subject: [PATCH 128/214] Flush PF states using pfctl-rs This fixes already-existing connections leaking when entering secured states, e.g. when internet sharing was enabled --- talpid-core/src/firewall/macos.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs index 07e74f45ffe4..b3502a8441d0 100644 --- a/talpid-core/src/firewall/macos.rs +++ b/talpid-core/src/firewall/macos.rs @@ -49,7 +49,15 @@ impl Firewall { pub fn apply_policy(&mut self, policy: FirewallPolicy) -> Result<()> { self.enable()?; self.add_anchor()?; - self.set_rules(policy) + self.set_rules(policy)?; + + // When entering a secured state, clear connection states + // Otherwise, an existing connection may be approved by some other anchor, and leak + if let Err(error) = self.pf.clear_interface_states(pfctl::Interface::Any) { + log::error!("Failed to clear source state tracking nodes: {error}"); + } + + Ok(()) } pub fn reset_policy(&mut self) -> Result<()> { From 511a6e534e4f9b080ecf717c9a5fb9331d3b8e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 17 Apr 2024 15:25:36 +0200 Subject: [PATCH 129/214] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d37ca3025acc..76dd17e16c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,11 @@ Line wrap the file at 100 chars. Th #### macOS - DNS was not properly restored in some cases when using custom DNS. +### Security +#### macOS +- Flush states on tunnel state changes. Previously, pre-existing connections could leak when + internet sharing was enabled on a device. + ## [2024.2-beta1] - 2024-04-15 ### Added From 5815716fcf1262699866c251cf0d3b9b7e7f3af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Thu, 18 Apr 2024 14:34:37 +0200 Subject: [PATCH 130/214] Bump hickory-dns version to 0.24.1 The patch for handling shut down sockets is included --- Cargo.lock | 60 +++++++++--------------------------------- talpid-core/Cargo.toml | 4 +-- 2 files changed, 15 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb0d7780c63f..8b16c1825021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,17 +184,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "async-recursion" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.51", -] - [[package]] name = "async-stream" version = "0.3.5" @@ -1294,10 +1283,10 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hickory-proto" -version = "0.24.0" -source = "git+https://github.com/mullvad/hickory-dns?rev=9e8f8c67fbcb6d2985503027362a3fb022529802#9e8f8c67fbcb6d2985503027362a3fb022529802" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" dependencies = [ - "async-recursion", "async-trait", "cfg-if", "data-encoding", @@ -1305,7 +1294,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.5.0", + "idna", "ipnet", "once_cell", "rand 0.8.5", @@ -1319,8 +1308,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.0" -source = "git+https://github.com/mullvad/hickory-dns?rev=9e8f8c67fbcb6d2985503027362a3fb022529802#9e8f8c67fbcb6d2985503027362a3fb022529802" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" dependencies = [ "cfg-if", "futures-util", @@ -1340,8 +1330,9 @@ dependencies = [ [[package]] name = "hickory-server" -version = "0.24.0" -source = "git+https://github.com/mullvad/hickory-dns?rev=9e8f8c67fbcb6d2985503027362a3fb022529802#9e8f8c67fbcb6d2985503027362a3fb022529802" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be0e43c556b9b3fdb6c7c71a9a32153a2275d02419e3de809e520bfcfe40c37" dependencies = [ "async-trait", "bytes", @@ -1350,8 +1341,6 @@ dependencies = [ "futures-util", "hickory-proto", "hickory-resolver", - "ipnet", - "prefix-trie", "serde", "thiserror", "time", @@ -1526,16 +1515,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -1636,9 +1615,6 @@ name = "ipnet" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" -dependencies = [ - "serde", -] [[package]] name = "ipnetwork" @@ -2850,16 +2826,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "prefix-trie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fe48f29e6e6fcf123d0d03d63028dbe4c4a738023d35d525df4882f4929418" -dependencies = [ - "ipnet", - "num-traits", -] - [[package]] name = "prettyplease" version = "0.2.15" @@ -3518,7 +3484,7 @@ dependencies = [ "cfg-if", "futures", "hyper", - "idna 0.4.0", + "idna", "ipnet", "iprange", "json5", @@ -4345,7 +4311,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.4.0", + "idna", "ipnet", "once_cell", "rand 0.8.5", @@ -4497,7 +4463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna", "percent-encoding", "serde", ] diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index 923ce42a1156..b079257cb3b1 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -51,8 +51,8 @@ duct = "0.13" pfctl = "0.4.6" subslice = "0.2" system-configuration = "0.5.1" -hickory-proto = { git = "https://github.com/mullvad/hickory-dns", rev = "9e8f8c67fbcb6d2985503027362a3fb022529802" } -hickory-server = { git = "https://github.com/mullvad/hickory-dns", rev = "9e8f8c67fbcb6d2985503027362a3fb022529802", features = ["resolver"] } +hickory-proto = "0.24.1" +hickory-server = { version = "0.24.1", features = ["resolver"] } [target.'cfg(windows)'.dependencies] bitflags = "1.2" From 28563f3980e234775bb8574d979537e299305ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 19 Apr 2024 10:37:13 +0200 Subject: [PATCH 131/214] Fix duplicate entries when adding through bottom sheet --- .../mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt | 4 +++- .../mullvadvpn/viewmodel/SelectLocationViewModelTest.kt | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt index 1759055bda9e..9772fb636286 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt @@ -19,6 +19,7 @@ import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.Ownership import net.mullvad.mullvadvpn.relaylist.Provider import net.mullvad.mullvadvpn.relaylist.RelayItem +import net.mullvad.mullvadvpn.relaylist.descendants import net.mullvad.mullvadvpn.relaylist.filterOnOwnershipAndProvider import net.mullvad.mullvadvpn.relaylist.filterOnSearchTerm import net.mullvad.mullvadvpn.relaylist.toLocationConstraint @@ -137,7 +138,8 @@ class SelectLocationViewModel( fun addLocationToList(item: RelayItem, customList: RelayItem.CustomList) { viewModelScope.launch { - val newLocations = (customList.locations + item).map { it.code } + val newLocations = + (customList.locations + item).filter { it !in item.descendants() }.map { it.code } val result = customListActionUseCase.performAction( CustomListAction.UpdateLocations(customList.id, newLocations) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt index 6522892ec2f6..5d0ab5f604e5 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt @@ -29,6 +29,7 @@ import net.mullvad.mullvadvpn.model.Providers import net.mullvad.mullvadvpn.relaylist.Provider import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.RelayList +import net.mullvad.mullvadvpn.relaylist.descendants import net.mullvad.mullvadvpn.relaylist.filterOnSearchTerm import net.mullvad.mullvadvpn.relaylist.toLocationConstraint import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy @@ -264,7 +265,10 @@ class SelectLocationViewModelTest { fun `after adding a location to a list should emit location added side effect`() = runTest { // Arrange val expectedResult: CustomListResult.LocationsChanged = mockk() - val location: RelayItem = mockk { every { code } returns "code" } + val location: RelayItem = mockk { + every { code } returns "code" + every { descendants() } returns emptyList() + } val customList: RelayItem.CustomList = mockk { every { id } returns "1" every { locations } returns emptyList() From ca1d45e7244ce2e7c5d0810d430cf432ccba3381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 19 Apr 2024 11:09:41 +0200 Subject: [PATCH 132/214] Add delete custom list entry bottom sheet --- .../compose/cell/RelayLocationCell.kt | 2 +- .../compose/screen/SelectLocationScreen.kt | 80 ++++++++++++++++++- .../viewmodel/SelectLocationViewModel.kt | 16 ++++ .../src/main/res/drawable/ic_remove.xml | 9 +++ .../resource/src/main/res/values/strings.xml | 1 + gui/locales/messages.pot | 3 + 6 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 android/lib/resource/src/main/res/drawable/ic_remove.xml diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt index 0342a0f5e7af..a2d7cc74c140 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt @@ -136,7 +136,7 @@ fun StatusRelayLocationCell( disabledColor: Color = MaterialTheme.colorScheme.onSecondary, selectedItem: RelayItem? = null, onSelectRelay: (item: RelayItem) -> Unit = {}, - onLongClick: (item: RelayItem) -> Unit = {} + onLongClick: (item: RelayItem) -> Unit = {}, ) { RelayLocationCell( relay = relay, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt index 705877951b9d..4476b8064a14 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt @@ -136,7 +136,15 @@ fun SelectLocation( LaunchedEffectCollect(vm.uiSideEffect) { when (it) { SelectLocationSideEffect.CloseScreen -> navigator.navigateUp() - is SelectLocationSideEffect.LocationAddedToCustomList -> { + is SelectLocationSideEffect.LocationAddedToCustomList -> + launch { + snackbarHostState.showResultSnackbar( + context = context, + result = it.result, + onUndo = vm::performAction + ) + } + is SelectLocationSideEffect.LocationRemovedFromCustomList -> launch { snackbarHostState.showResultSnackbar( context = context, @@ -144,7 +152,6 @@ fun SelectLocation( onUndo = vm::performAction ) } - } } } @@ -181,6 +188,7 @@ fun SelectLocation( removeOwnershipFilter = vm::removeOwnerFilter, removeProviderFilter = vm::removeProviderFilter, onAddLocationToList = vm::addLocationToList, + onRemoveLocationFromList = vm::removeLocationFromList, onEditCustomListName = { navigator.navigate( EditCustomListNameDestination(customListId = it.id, initialName = it.name) @@ -213,6 +221,9 @@ fun SelectLocationScreen( removeProviderFilter: () -> Unit = {}, onAddLocationToList: (location: RelayItem, customList: RelayItem.CustomList) -> Unit = { _, _ -> }, + onRemoveLocationFromList: (location: RelayItem, customList: RelayItem.CustomList) -> Unit = + { _, _ -> + }, onEditCustomListName: (RelayItem.CustomList) -> Unit = {}, onEditLocationsCustomList: (RelayItem.CustomList) -> Unit = {}, onDeleteCustomList: (RelayItem.CustomList) -> Unit = {} @@ -233,6 +244,7 @@ fun SelectLocationScreen( onCreateCustomList = onCreateCustomList, onEditCustomLists = onEditCustomLists, onAddLocationToList = onAddLocationToList, + onRemoveLocationFromList = onRemoveLocationFromList, onEditCustomListName = onEditCustomListName, onEditLocationsCustomList = onEditLocationsCustomList, onDeleteCustomList = onDeleteCustomList, @@ -331,6 +343,15 @@ fun SelectLocationScreen( onShowEditBottomSheet = { customList -> bottomSheetState = BottomSheetState.ShowEditCustomListBottomSheet(customList) + }, + onShowEditCustomListEntryBottomSheet = { + item: RelayItem, + customList: RelayItem.CustomList -> + bottomSheetState = + BottomSheetState.ShowCustomListsEntryBottomSheet( + customList, + item, + ) } ) item { @@ -379,7 +400,8 @@ private fun LazyListScope.customLists( backgroundColor: Color, onSelectRelay: (item: RelayItem) -> Unit, onShowCustomListBottomSheet: () -> Unit, - onShowEditBottomSheet: (RelayItem.CustomList) -> Unit + onShowEditBottomSheet: (RelayItem.CustomList) -> Unit, + onShowEditCustomListEntryBottomSheet: (item: RelayItem, RelayItem.CustomList) -> Unit ) { item( contentType = { ContentType.HEADER }, @@ -407,6 +429,8 @@ private fun LazyListScope.customLists( onLongClick = { if (it is RelayItem.CustomList) { onShowEditBottomSheet(it) + } else if (it in customList.locations) { + onShowEditCustomListEntryBottomSheet(it, customList) } }, modifier = Modifier.animateContentSize().animateItemPlacement(), @@ -467,6 +491,7 @@ private fun BottomSheets( onCreateCustomList: (RelayItem?) -> Unit, onEditCustomLists: () -> Unit, onAddLocationToList: (RelayItem, RelayItem.CustomList) -> Unit, + onRemoveLocationFromList: (RelayItem, RelayItem.CustomList) -> Unit, onEditCustomListName: (RelayItem.CustomList) -> Unit, onEditLocationsCustomList: (RelayItem.CustomList) -> Unit, onDeleteCustomList: (RelayItem.CustomList) -> Unit, @@ -522,6 +547,16 @@ private fun BottomSheets( closeBottomSheet = onCloseBottomSheet ) } + is BottomSheetState.ShowCustomListsEntryBottomSheet -> { + CustomListEntryBottomSheet( + sheetState = sheetState, + onBackgroundColor = onBackgroundColor, + customList = bottomSheetState.customList, + item = bottomSheetState.item, + onRemoveLocationFromList = onRemoveLocationFromList, + closeBottomSheet = onCloseBottomSheet + ) + } null -> { /* Do nothing */ } @@ -715,6 +750,40 @@ private fun EditCustomListBottomSheet( } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun CustomListEntryBottomSheet( + onBackgroundColor: Color, + sheetState: SheetState, + customList: RelayItem.CustomList, + item: RelayItem, + onRemoveLocationFromList: (location: RelayItem, customList: RelayItem.CustomList) -> Unit, + closeBottomSheet: (animate: Boolean) -> Unit +) { + MullvadModalBottomSheet( + sheetState = sheetState, + onDismissRequest = { closeBottomSheet(false) }, + modifier = Modifier.testTag(SELECT_LOCATION_LOCATION_BOTTOM_SHEET_TEST_TAG) + ) { -> + HeaderCell( + text = stringResource(id = R.string.remove_location_from_list, item.name), + background = Color.Unspecified + ) + HorizontalDivider(color = onBackgroundColor) + + IconCell( + iconId = R.drawable.ic_remove, + title = stringResource(id = R.string.remove_button), + titleColor = onBackgroundColor, + onClick = { + onRemoveLocationFromList(item, customList) + closeBottomSheet(true) + }, + background = Color.Unspecified + ) + } +} + private suspend fun LazyListState.animateScrollAndCentralizeItem(index: Int) { val itemInfo = this.layoutInfo.visibleItemsInfo.firstOrNull { it.index == index } if (itemInfo != null) { @@ -785,6 +854,11 @@ sealed interface BottomSheetState { data class ShowCustomListsBottomSheet(val editListEnabled: Boolean) : BottomSheetState + data class ShowCustomListsEntryBottomSheet( + val customList: RelayItem.CustomList, + val item: RelayItem + ) : BottomSheetState + data class ShowLocationBottomSheet( val customLists: List, val item: RelayItem diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt index 9772fb636286..27edb954578b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt @@ -154,6 +154,19 @@ class SelectLocationViewModel( viewModelScope.launch { customListActionUseCase.performAction(action) } } + fun removeLocationFromList(item: RelayItem, customList: RelayItem.CustomList) { + viewModelScope.launch { + val newLocations = (customList.locations - item).map { it.code } + val result = + customListActionUseCase.performAction( + CustomListAction.UpdateLocations(customList.id, newLocations) + ) + _uiSideEffect.send( + SelectLocationSideEffect.LocationRemovedFromCustomList(result.getOrThrow()) + ) + } + } + companion object { private const val EMPTY_SEARCH_TERM = "" } @@ -164,4 +177,7 @@ sealed interface SelectLocationSideEffect { data class LocationAddedToCustomList(val result: CustomListResult.LocationsChanged) : SelectLocationSideEffect + + class LocationRemovedFromCustomList(val result: CustomListResult.LocationsChanged) : + SelectLocationSideEffect } diff --git a/android/lib/resource/src/main/res/drawable/ic_remove.xml b/android/lib/resource/src/main/res/drawable/ic_remove.xml new file mode 100644 index 000000000000..93b0990d6b6c --- /dev/null +++ b/android/lib/resource/src/main/res/drawable/ic_remove.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml index a8e9978f71fe..38a839bed3be 100644 --- a/android/lib/resource/src/main/res/values/strings.xml +++ b/android/lib/resource/src/main/res/values/strings.xml @@ -311,6 +311,7 @@ Discard changes? Discard Add %s to list + Remove %s from list %s was added to \"%s\" %s (added) Edit name diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index b1c39c2d2d99..1976d04278ce 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -2335,6 +2335,9 @@ msgstr "" msgid "Remove" msgstr "" +msgid "Remove %s from list" +msgstr "" + msgid "Remove custom port" msgstr "" From f9ad3d2fcf4dbf50e97fe60892af43643c285aa8 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 08:03:02 +0200 Subject: [PATCH 133/214] Add testid to custom bridge delete confirmation button --- gui/src/renderer/components/EditCustomBridge.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gui/src/renderer/components/EditCustomBridge.tsx b/gui/src/renderer/components/EditCustomBridge.tsx index 2692217ea0a7..7cc403aa6ed4 100644 --- a/gui/src/renderer/components/EditCustomBridge.tsx +++ b/gui/src/renderer/components/EditCustomBridge.tsx @@ -95,7 +95,11 @@ function CustomBridgeForm() { {messages.gettext('Cancel')} , - + {messages.gettext('Delete')} , ]} From bc7b8e9fc5f5776df1a2ec0520d1a26656e6c004 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 08:03:59 +0200 Subject: [PATCH 134/214] Add aria-label to edit custom bridge button --- .../select-location/SpecialLocationList.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/gui/src/renderer/components/select-location/SpecialLocationList.tsx b/gui/src/renderer/components/select-location/SpecialLocationList.tsx index 9579a6b4b511..a347638b0e28 100644 --- a/gui/src/renderer/components/select-location/SpecialLocationList.tsx +++ b/gui/src/renderer/components/select-location/SpecialLocationList.tsx @@ -108,7 +108,8 @@ export function CustomBridgeLocationRow( const history = useHistory(); const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); - const icon = bridgeSettings.custom !== undefined ? 'icon-edit' : 'icon-add'; + const bridgeConfigured = bridgeSettings.custom !== undefined; + const icon = bridgeConfigured ? 'icon-edit' : 'icon-add'; const selectedRef = props.source.selected ? props.selectedElementRef : undefined; const background = getButtonColor(props.source.selected, 0, props.source.disabled); @@ -135,7 +136,14 @@ export function CustomBridgeLocationRow( 'A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work.', )} /> - + Date: Tue, 16 Apr 2024 08:04:22 +0200 Subject: [PATCH 135/214] Fix not resetting to bridge type normal when selecting automatic --- .../renderer/components/select-location/select-location-hooks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/src/renderer/components/select-location/select-location-hooks.ts b/gui/src/renderer/components/select-location/select-location-hooks.ts index 08ed2dccf3fb..027185e416a6 100644 --- a/gui/src/renderer/components/select-location/select-location-hooks.ts +++ b/gui/src/renderer/components/select-location/select-location-hooks.ts @@ -121,6 +121,7 @@ export function useOnSelectBridgeLocation() { case SpecialBridgeLocationType.closestToExit: return setLocation( bridgeSettingsModifier((bridgeSettings) => { + bridgeSettings.type = 'normal'; bridgeSettings.normal.location = 'any'; return bridgeSettings; }), From a45790318396d5f8b73a55c82d512868968fbe4b Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 08:06:53 +0200 Subject: [PATCH 136/214] Add GUI test for custom bridge --- .../renderer/components/OpenVpnSettings.tsx | 3 +- gui/src/renderer/components/cell/Selector.tsx | 10 +- .../state-dependent/custom-bridge.spec.ts | 173 ++++++++++++++++++ 3 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts diff --git a/gui/src/renderer/components/OpenVpnSettings.tsx b/gui/src/renderer/components/OpenVpnSettings.tsx index dee1e6408968..5013b158f8eb 100644 --- a/gui/src/renderer/components/OpenVpnSettings.tsx +++ b/gui/src/renderer/components/OpenVpnSettings.tsx @@ -269,6 +269,7 @@ function BridgeModeSelector() { label: messages.gettext('On'), value: 'on', disabled: tunnelProtocol !== 'openvpn' || transportProtocol === 'udp', + 'data-testid': 'bridge-mode-on', }, { label: messages.gettext('Off'), @@ -367,7 +368,7 @@ function BridgeModeSelector() { {messages.gettext('Cancel')} , - + {messages.gettext('Enable')} , ]} diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx index e527017435d3..5e5d685a59f7 100644 --- a/gui/src/renderer/components/cell/Selector.tsx +++ b/gui/src/renderer/components/cell/Selector.tsx @@ -16,6 +16,8 @@ export interface SelectorItem { label: string; value: T; disabled?: boolean; + // eslint-disable-next-line @typescript-eslint/naming-convention + 'data-testid'?: string; } // T represents the available values and U represent the value of "Automatic"/"Any" if there is one. @@ -51,7 +53,8 @@ export default function Selector(props: SelectorProps) { isSelected={selected} disabled={props.disabled || item.disabled} forwardedRef={ref} - onSelect={props.onSelect}> + onSelect={props.onSelect} + data-testid={item['data-testid']}> {item.label} ); @@ -133,6 +136,8 @@ interface SelectorCellProps { onSelect: (value: T) => void; children: React.ReactNode | Array; forwardedRef?: React.Ref; + // eslint-disable-next-line @typescript-eslint/naming-convention + 'data-testid'?: string; } function SelectorCell(props: SelectorCellProps) { @@ -150,7 +155,8 @@ function SelectorCell(props: SelectorCellProps) { disabled={props.disabled} role="option" aria-selected={props.isSelected} - aria-disabled={props.disabled}> + aria-disabled={props.disabled} + data-testid={props['data-testid']}> { + ({ page, util } = await startInstalledApp()); +}); + +test.afterAll(async () => { + await page.close(); +}); + +test('App should enable bridge mode', async () => { + await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); + expect( + await util.waitForNavigation(async () => await page.getByText('VPN settings').click()), + ).toBe(RoutePath.vpnSettings); + + await page.getByRole('option', { name: 'OpenVPN' }).click(); + + expect( + await util.waitForNavigation(async () => await page.getByText('OpenVPN settings').click()), + ).toBe(RoutePath.openVpnSettings); + + await page.getByTestId('bridge-mode-on').click(); + await expect(page.getByText('Enable bridge mode?')).toBeVisible(); + + page.getByTestId('enable-confirm').click(); + + await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + expect( + await util.waitForNavigation(async () => await page.click('button[aria-label="Close"]')), + ).toBe(RoutePath.main); +}); + +test('App display disabled custom bridge', async () => { + expect( + await util.waitForNavigation( + async () => await page.click('button[aria-label^="Select location"]'), + ), + ).toBe(RoutePath.selectLocation); + + const title = page.locator('h1') + await expect(title).toHaveText('Select location'); + + await page.getByText(/^Entry$/).click(); + + const customBridgeButton = page.getByText('Custom bridge'); + await expect(customBridgeButton).toBeDisabled(); +}); + +test('App should add new custom bridge', async () => { + expect( + await util.waitForNavigation( + async () => await page.click('button[aria-label="Add new custom bridge"]'), + ), + ).toBe(RoutePath.editCustomBridge); + + const title = page.locator('h1') + await expect(title).toHaveText('Add custom bridge'); + + const inputs = page.locator('input'); + const addButton = page.locator('button:has-text("Add")'); + await expect(addButton).toBeVisible(); + await expect(addButton).toBeDisabled(); + + await inputs.first().fill(process.env.SHADOWSOCKS_SERVER_IP!); + await expect(addButton).toBeDisabled(); + + await inputs.nth(1).fill('443'); + await expect(addButton).toBeEnabled(); + + await inputs.nth(2).fill(process.env.SHADOWSOCKS_SERVER_PASSWORD!); + + await page.getByTestId('ciphers').click(); + await page.getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }).click(); + + expect( + await util.waitForNavigation(async () => await addButton.click()) + ).toEqual(RoutePath.selectLocation); + + const customBridgeButton = page.getByText('Custom bridge'); + await expect(customBridgeButton).toBeEnabled(); + + await expect(page.locator('button[aria-label="Edit custom bridge"]')).toBeVisible(); +}); + +test('App should select custom bridge', async () => { + const customBridgeButton = page.locator('button:has-text("Custom bridge")'); + await expect(customBridgeButton).toHaveCSS('background-color', colors.green); + + const automaticButton = page.getByText('Automatic'); + await automaticButton.click(); + await page.getByText(/^Entry$/).click(); + await expect(customBridgeButton).not.toHaveCSS('background-color', colors.green); + + + await customBridgeButton.click(); + await page.getByText(/^Entry$/).click(); + await expect(customBridgeButton).toHaveCSS('background-color', colors.green); + +}); + +test('App should edit custom bridge', async () => { + const automaticButton = page.getByText('Automatic'); + await automaticButton.click(); + await page.getByText(/^Entry$/).click(); + + expect( + await util.waitForNavigation( + async () => await page.click('button[aria-label="Edit custom bridge"]'), + ), + ).toBe(RoutePath.editCustomBridge); + + const title = page.locator('h1') + await expect(title).toHaveText('Edit custom bridge'); + + const inputs = page.locator('input'); + const saveButton = page.locator('button:has-text("Save")'); + await expect(saveButton).toBeVisible(); + await expect(saveButton).toBeEnabled(); + + await inputs.nth(1).fill(process.env.SHADOWSOCKS_SERVER_PORT!); + await expect(saveButton).toBeEnabled(); + + + expect( + await util.waitForNavigation(async () => await saveButton.click()) + ).toEqual(RoutePath.selectLocation); + + const customBridgeButton = page.locator('button:has-text("Custom bridge")'); + await expect(customBridgeButton).toBeEnabled(); + await expect(customBridgeButton).toHaveCSS('background-color', colors.green); +}); + +test('App should delete custom bridge', async () => { + expect( + await util.waitForNavigation( + async () => await page.click('button[aria-label="Edit custom bridge"]'), + ), + ).toBe(RoutePath.editCustomBridge); + + const deleteButton = page.locator('button:has-text("Delete")'); + await expect(deleteButton).toBeVisible(); + await expect(deleteButton).toBeEnabled(); + + await deleteButton.click(); + await expect(page.getByText('Delete custom bridge?')).toBeVisible(); + + const confirmButton = page.getByTestId('delete-confirm'); + expect( + await util.waitForNavigation(async () => await confirmButton.click()) + ).toEqual(RoutePath.selectLocation); + + const customBridgeButton = page.locator('button:has-text("Custom bridge")'); + await expect(customBridgeButton).toBeDisabled(); + await expect(customBridgeButton).not.toHaveCSS('background-color', colors.green); +}); From 67e772a453c4df480f03e82fb75e44ced89f7679 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 09:39:23 +0200 Subject: [PATCH 137/214] Add ensure_logged_in test helper function --- test/test-manager/src/tests/helpers.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index da50679a26b4..b733939da073 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -249,6 +249,21 @@ pub async fn login_with_retries( } } +/// Ensure that the test runner is logged in to an account. +/// +/// This will first check whether we are logged in. If not, it will also try to login +/// on your behalf. If this function returns without any errors, we are logged in to a valid +/// account. +pub async fn ensure_logged_in( + mullvad_client: &mut MullvadProxyClient, +) -> Result<(), mullvad_management_interface::Error> { + if mullvad_client.get_device().await?.is_logged_in() { + return Ok(()); + } + // We are apparently not logged in already.. Try to log in. + login_with_retries(mullvad_client).await +} + /// Try to connect to a Mullvad Tunnel. /// /// # Returns From 1637cdf7c3e1d3fdb087bb94e734bad2d3d8790f Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 08:57:01 +0200 Subject: [PATCH 138/214] Add gui test to test framework --- test/test-manager/src/tests/ui.rs | 74 ++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/test/test-manager/src/tests/ui.rs b/test/test-manager/src/tests/ui.rs index cce7cdd990c8..7adaf432afc6 100644 --- a/test/test-manager/src/tests/ui.rs +++ b/test/test-manager/src/tests/ui.rs @@ -1,4 +1,8 @@ -use super::{config::TEST_CONFIG, helpers, Error, TestContext}; +use super::{ + config::TEST_CONFIG, + helpers::{self, ensure_logged_in}, + Error, TestContext, +}; use mullvad_management_interface::MullvadProxyClient; use mullvad_relay_selector::query::builder::RelayQueryBuilder; use std::{ @@ -125,7 +129,7 @@ pub async fn test_ui_login(_: TestContext, rpc: ServiceClient) -> Result<(), Err Ok(()) } -#[test_function(priority = 1000, must_succeed = true)] +#[test_function(priority = 1000)] async fn test_custom_access_methods_gui( _: TestContext, rpc: ServiceClient, @@ -196,13 +200,69 @@ async fn test_custom_access_methods_gui( assert!(ui_result.success()); - // Reset the `api-override` feature. - tokio::time::timeout( - std::time::Duration::from_secs(60), - rpc.set_daemon_environment(helpers::get_app_env()), + Ok(()) +} + +#[test_function(priority = 1000)] +async fn test_custom_bridge_gui( + _: TestContext, + rpc: ServiceClient, + mut mullvad_client: MullvadProxyClient, +) -> Result<(), Error> { + use mullvad_relay_selector::{RelaySelector, SelectorConfig}; + use talpid_types::net::proxy::CustomProxy; + // For this test to work, we need to supply the following env-variables: + // + // * SHADOWSOCKS_SERVER_IP + // * SHADOWSOCKS_SERVER_PORT + // * SHADOWSOCKS_SERVER_CIPHER + // * SHADOWSOCKS_SERVER_PASSWORD + // + // See `gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts` + // for details. The setup should be the same as in + // `test_manager::tests::access_methods::test_shadowsocks`. + // + // # Note + // The test requires the app to already be logged in. + + ensure_logged_in(&mut mullvad_client) + .await + .expect("ensure_logged_in failed"); + + let gui_test = "custom-bridge.spec"; + let relay_list = mullvad_client.get_relay_locations().await.unwrap(); + let relay_selector = RelaySelector::from_list(SelectorConfig::default(), relay_list); + let custom_proxy = relay_selector + .get_bridge_forced() + .and_then(|proxy| match proxy { + CustomProxy::Shadowsocks(s) => Some(s), + _ => None + }) + .expect("`test_shadowsocks` needs at least one shadowsocks relay to execute. Found none in relay list."); + + let ui_result = run_test_env( + &rpc, + &[gui_test], + [ + ( + "SHADOWSOCKS_SERVER_IP", + custom_proxy.endpoint.ip().to_string().as_ref(), + ), + ( + "SHADOWSOCKS_SERVER_PORT", + custom_proxy.endpoint.port().to_string().as_ref(), + ), + ("SHADOWSOCKS_SERVER_CIPHER", custom_proxy.cipher.as_ref()), + ( + "SHADOWSOCKS_SERVER_PASSWORD", + custom_proxy.password.as_ref(), + ), + ], ) .await - .map_err(|_| Error::DaemonNotRunning)??; + .unwrap(); + + assert!(ui_result.success()); Ok(()) } From 9931f2c602ac9028bba7a05f52b86d707d77043f Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 16:07:20 +0200 Subject: [PATCH 139/214] Update translations --- gui/locales/messages.pot | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index 1976d04278ce..9126cb9c998b 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -261,6 +261,10 @@ msgctxt "accessibility" msgid "%(title)s, View loaded" msgstr "" +msgctxt "accessibility" +msgid "Add new custom bridge" +msgstr "" + msgctxt "accessibility" msgid "Close notification" msgstr "" @@ -275,6 +279,10 @@ msgctxt "accessibility" msgid "Copy account number" msgstr "" +msgctxt "accessibility" +msgid "Edit custom bridge" +msgstr "" + msgctxt "accessibility" msgid "Expand %(location)s" msgstr "" From a15d3738fb7e82f7466f560da9dc3265c8b91ac9 Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Mon, 22 Apr 2024 10:38:18 +0200 Subject: [PATCH 140/214] Use only blue as a top bar color in the welcome screen --- .../mullvadvpn/compose/screen/WelcomeScreen.kt | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt index aef6602ab66a..8dcbea035086 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt @@ -176,19 +176,8 @@ fun WelcomeScreen( val snackbarHostState = remember { SnackbarHostState() } ScaffoldWithTopBar( - topBarColor = - if (state.tunnelState.isSecured()) { - MaterialTheme.colorScheme.inversePrimary - } else { - MaterialTheme.colorScheme.error - }, - iconTintColor = - if (state.tunnelState.isSecured()) { - MaterialTheme.colorScheme.onPrimary - } else { - MaterialTheme.colorScheme.onError - } - .copy(alpha = AlphaTopBar), + topBarColor = MaterialTheme.colorScheme.primary, + iconTintColor = MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaTopBar), onSettingsClicked = onSettingsClick, onAccountClicked = onAccountClick, snackbarHostState = snackbarHostState, From a0f3abeaf168b563e9395138ba6c77f532e208e2 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Fri, 19 Apr 2024 16:25:41 +0200 Subject: [PATCH 141/214] Fix broken lints in rustdocs I ran `cargo doc` and fixed as many broken links as I could find. --- mullvad-api/src/lib.rs | 4 ++-- mullvad-api/src/relay_list.rs | 2 +- mullvad-cli/src/cmds/api_access.rs | 8 ++++---- mullvad-cli/src/cmds/proxies.rs | 14 +++++++------- mullvad-daemon/src/api.rs | 7 ++++--- mullvad-daemon/src/lib.rs | 1 + mullvad-daemon/src/management_interface.rs | 6 +++--- mullvad-daemon/src/migrations/v1.rs | 2 +- mullvad-management-interface/src/client.rs | 9 +-------- .../src/types/conversions/access_method.rs | 4 ++-- mullvad-relay-selector/src/lib.rs | 9 ++++----- .../src/relay_selector/helpers.rs | 2 +- .../src/relay_selector/mod.rs | 16 +++++++++------- mullvad-types/src/access_method.rs | 10 +++++----- mullvad-types/src/constraints/mod.rs | 18 ++++++++++-------- mullvad-types/src/location.rs | 4 ++-- talpid-routing/src/lib.rs | 2 +- .../src/windows/default_route_monitor.rs | 2 +- talpid-routing/src/windows/mod.rs | 2 +- talpid-types/src/net/proxy.rs | 5 +---- talpid-wireguard/src/wireguard_nt/mod.rs | 14 +++++++------- 21 files changed, 68 insertions(+), 73 deletions(-) diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs index c4d5665a0131..82cabddff0cc 100644 --- a/mullvad-api/src/lib.rs +++ b/mullvad-api/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(rustdoc::private_intra_doc_links)] #[cfg(target_os = "android")] use futures::channel::mpsc; use hyper::Method; @@ -279,8 +280,7 @@ impl ApiEndpoint { } /// Read the [`Self::address`] value, falling back to - /// [`Self::API_IP_DEFAULT`]:[`Self::API_PORT_DEFAULT`] as default if it - /// does not exist. + /// [`Self::API_IP_DEFAULT`] as default value if it does not exist. pub fn address(&self) -> SocketAddr { self.address.unwrap_or(SocketAddr::new( ApiEndpoint::API_IP_DEFAULT, diff --git a/mullvad-api/src/relay_list.rs b/mullvad-api/src/relay_list.rs index 73b387a8d8c0..6039abab6647 100644 --- a/mullvad-api/src/relay_list.rs +++ b/mullvad-api/src/relay_list.rs @@ -13,7 +13,7 @@ use std::{ time::Duration, }; -/// Fetches relay list from https://api.mullvad.net/app/v1/relays +/// Fetches relay list from #[derive(Clone)] pub struct RelayListProxy { handle: rest::MullvadRestHandle, diff --git a/mullvad-cli/src/cmds/api_access.rs b/mullvad-cli/src/cmds/api_access.rs index 6c4185078426..314ebf079571 100644 --- a/mullvad-cli/src/cmds/api_access.rs +++ b/mullvad-cli/src/cmds/api_access.rs @@ -340,7 +340,7 @@ pub struct EditCustomCommands { /// Which API access method to edit #[clap(flatten)] item: SelectItem, - /// Name of the API access method in the Mullvad client [All] + /// Name of the API access method in the Mullvad client \[All\] #[arg(long)] name: Option, /// Editing parameters @@ -350,7 +350,7 @@ pub struct EditCustomCommands { #[derive(Args, Debug, Clone)] pub struct EditParams { - /// Name of the API access method in the Mullvad client [All] + /// Name of the API access method in the Mullvad client \[All\] #[arg(long)] name: Option, #[clap(flatten)] @@ -406,7 +406,7 @@ mod conversions { } } -/// Pretty printing of [`ApiAccessMethod`]s +/// Pretty printing of [`AccessMethodSetting`]s mod pp { use crate::cmds::proxies::pp::CustomProxyFormatter; use mullvad_types::access_method::{AccessMethod, AccessMethodSetting}; @@ -418,7 +418,7 @@ mod pp { pub struct FormatterSettings { /// If the formatter should print the enabled status of an - /// [`AcessMethodSetting`] (*) next to its name. + /// [`AccessMethodSetting`] (*) next to its name. pub write_enabled: bool, } diff --git a/mullvad-cli/src/cmds/proxies.rs b/mullvad-cli/src/cmds/proxies.rs index b1fc2b6f3ae1..dbbb82afcc93 100644 --- a/mullvad-cli/src/cmds/proxies.rs +++ b/mullvad-cli/src/cmds/proxies.rs @@ -102,25 +102,25 @@ pub struct SocksAuthentication { #[derive(Args, Debug, Clone)] pub struct ProxyEditParams { - /// Username for authentication [Socks5 (Remote proxy)] + /// Username for authentication \[Socks5 (Remote proxy)\] #[arg(long)] pub username: Option, - /// Password for authentication [Socks5 (Remote proxy), Shadowsocks] + /// Password for authentication \[Socks5 (Remote proxy), Shadowsocks\] #[arg(long)] pub password: Option, - /// Cipher to use [Shadowsocks] + /// Cipher to use \[Shadowsocks\] #[arg(value_parser = SHADOWSOCKS_CIPHERS, long)] pub cipher: Option, - /// The IP of the remote proxy server [Socks5 (Local & Remote proxy), Shadowsocks] + /// The IP of the remote proxy server \[Socks5 (Local & Remote proxy), Shadowsocks\] #[arg(long)] pub ip: Option, - /// The port of the remote proxy server [Socks5 (Local & Remote proxy), Shadowsocks] + /// The port of the remote proxy server \[Socks5 (Local & Remote proxy), Shadowsocks\] #[arg(long)] pub port: Option, - /// The port that the server on localhost is listening on [Socks5 (Local proxy)] + /// The port that the server on localhost is listening on \[Socks5 (Local proxy)\] #[arg(long)] pub local_port: Option, - /// The transport protocol used by the remote proxy [Socks5 (Local proxy)] + /// The transport protocol used by the remote proxy \[Socks5 (Local proxy)\] #[arg(long)] pub transport_protocol: Option, } diff --git a/mullvad-daemon/src/api.rs b/mullvad-daemon/src/api.rs index f31b46d54855..1a427701e974 100644 --- a/mullvad-daemon/src/api.rs +++ b/mullvad-daemon/src/api.rs @@ -480,9 +480,10 @@ impl AccessModeSelector { } } None => { - // Current method was removed: A new access method will suddenly have the same index as the one - // we are removing, but we want it to still be a candidate. A minor - // hack to achieve this is to simply decrement the current index. + // Current method was removed: A new access method will suddenly have the same index + // as the one we are removing, but we want it to still be a + // candidate. A minor hack to achieve this is to simply decrement + // the current index. self.index = self.index.saturating_sub(1); self.next_connection_mode().await?; } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 875f797ec3d9..52df09e6d5b1 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -1,4 +1,5 @@ #![recursion_limit = "512"] +#![allow(rustdoc::private_intra_doc_links)] mod access_method; pub mod account_history; diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 0699ab697eb3..2e7f04b1d98d 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -1094,7 +1094,7 @@ impl ManagementInterfaceEventBroadcaster { } } -/// Converts [`mullvad_daemon::Error`] into a tonic status. +/// Converts [`crate::Error`] into a tonic status. fn map_daemon_error(error: crate::Error) -> Status { use crate::Error as DaemonError; @@ -1150,7 +1150,7 @@ fn map_rest_error(error: &RestError) -> Status { } } -/// Converts an instance of [`mullvad_daemon::device::Error`] into a tonic status. +/// Converts an instance of [`crate::device::Error`] into a tonic status. fn map_device_error(error: &device::Error) -> Status { match error { device::Error::MaxDevicesReached => Status::new(Code::ResourceExhausted, error.to_string()), @@ -1168,7 +1168,7 @@ fn map_device_error(error: &device::Error) -> Status { } } -/// Converts an instance of [`mullvad_daemon::account_history::Error`] into a tonic status. +/// Converts an instance of [`crate::account_history::Error`] into a tonic status. fn map_account_history_error(error: account_history::Error) -> Status { match error { account_history::Error::Read(..) | account_history::Error::Write(..) => { diff --git a/mullvad-daemon/src/migrations/v1.rs b/mullvad-daemon/src/migrations/v1.rs index 4205f23ebf7e..b20f96488b21 100644 --- a/mullvad-daemon/src/migrations/v1.rs +++ b/mullvad-daemon/src/migrations/v1.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; // Section for vendoring types and values that // this settings version depend on. See `mod.rs`. -/// The tunnel protocol used by a [`TunnelEndpoint`]. +/// The tunnel protocol used by a [`TunnelEndpoint`][talpid_types::net::TunnelEndpoint]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename = "tunnel_type")] pub enum TunnelType { diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 04304a19ec4d..35ffb57622a4 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -605,14 +605,7 @@ impl MullvadProxyClient { .map(drop) } - /// Set the [`AccessMethod`] which [`ApiConnectionModeProvider`] should - /// pick. - /// - /// - `access_method`: If `Some(access_method)`, [`ApiConnectionModeProvider`] will skip ahead - /// and return `access_method` when asked for a new access method. If `None`, - /// [`ApiConnectionModeProvider`] will pick the next access method "randomly" - /// - /// [`ApiConnectionModeProvider`]: mullvad_daemon::api::ApiConnectionModeProvider + /// Set the [`AccessMethod`] which `AccessModeSelector` should pick. pub async fn set_access_method(&mut self, api_access_method: access_method::Id) -> Result<()> { self.0 .set_api_access_method(types::Uuid::from(api_access_method)) diff --git a/mullvad-management-interface/src/types/conversions/access_method.rs b/mullvad-management-interface/src/types/conversions/access_method.rs index d9758a571c97..9f45957db6b8 100644 --- a/mullvad-management-interface/src/types/conversions/access_method.rs +++ b/mullvad-management-interface/src/types/conversions/access_method.rs @@ -52,8 +52,8 @@ mod settings { } } -/// Implements conversions for the auxilliary -/// [`crate::types::proto::ApiAccessMethod`] type to the internal +/// Implements conversions for the auxiliary +/// [`crate::types::proto::AccessMethodSetting`] type to the internal /// [`mullvad_types::access_method::AccessMethodSetting`] data type. mod data { use crate::types::{proto, FromProtobufTypeError}; diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs index 73c709a844a7..09708a059eeb 100644 --- a/mullvad-relay-selector/src/lib.rs +++ b/mullvad-relay-selector/src/lib.rs @@ -1,6 +1,6 @@ //! When changing relay selection, please verify if `docs/relay-selector.md` needs to be //! updated as well. - +#![allow(rustdoc::private_intra_doc_links)] mod constants; mod error; #[cfg_attr(target_os = "android", allow(unused))] @@ -8,9 +8,8 @@ mod relay_selector; // Re-exports pub use error::Error; -pub use relay_selector::detailer; pub use relay_selector::{ - query, AdditionalRelayConstraints, AdditionalWireguardConstraints, GetRelay, RelaySelector, - RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, WireguardConfig, - RETRY_ORDER, + detailer, query, AdditionalRelayConstraints, AdditionalWireguardConstraints, GetRelay, + RelaySelector, RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, + WireguardConfig, RETRY_ORDER, }; diff --git a/mullvad-relay-selector/src/relay_selector/helpers.rs b/mullvad-relay-selector/src/relay_selector/helpers.rs index e27a73684519..48c79f05d132 100644 --- a/mullvad-relay-selector/src/relay_selector/helpers.rs +++ b/mullvad-relay-selector/src/relay_selector/helpers.rs @@ -11,7 +11,7 @@ use talpid_types::net::obfuscation::ObfuscatorConfig; use crate::SelectedObfuscator; -/// Picks a relay using [pick_random_relay_fn], using the `weight` member of each relay +/// Picks a relay using [pick_random_relay_weighted], using the `weight` member of each relay /// as the weight function. pub fn pick_random_relay(relays: &[Relay]) -> Option<&Relay> { pick_random_relay_weighted(relays, |relay| relay.weight) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 843eea9a204a..7f38281fc525 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -161,15 +161,17 @@ impl Default for RuntimeParameters { /// This enum exists to separate the two types of [`SelectorConfig`] that exists. /// /// The first one is a "regular" config, where [`SelectorConfig::relay_settings`] is -/// [`RelaySettings::Normal`]. This is the most common variant, and there exists a mapping from this -/// variant to [`RelayQueryBuilder`]. Being able to implement `From for -/// RelayQueryBuilder` was the main motivator for introducing these seemingly useless derivates of -/// [`SelectorConfig`]. +/// [`RelaySettings::Normal`]. This is the most common variant, and there exists a +/// mapping from this variant to [`RelayQueryBuilder`]. Being able to implement +/// `From for RelayQueryBuilder` was the main motivator for introducing these +/// seemingly useless derivates of [`SelectorConfig`]. /// /// The second one is a custom config, where [`SelectorConfig::relay_settings`] is -/// [`RelaySettings::Custom`]. For this variant, the endpoint where the client should connect to is -/// already specified inside of the variant, so in practice the relay selector becomes superfluous. -/// Also, there exists no mapping to [`RelayQueryBuilder`]. +/// [`RelaySettings::CustomTunnelEndpoint`]. For this variant, the endpoint where the client should +/// connect to is already specified inside of the variant, so in practice the relay selector becomes +/// superfluous. Also, there exists no mapping to [`RelayQueryBuilder`]. +/// +/// [`RelayQueryBuilder`]: query::builder::RelayQueryBuilder #[derive(Debug, Clone)] enum SpecializedSelectorConfig<'a> { // This variant implements `From for RelayQuery` diff --git a/mullvad-types/src/access_method.rs b/mullvad-types/src/access_method.rs index c42b099baeb5..2b375efb920e 100644 --- a/mullvad-types/src/access_method.rs +++ b/mullvad-types/src/access_method.rs @@ -28,7 +28,7 @@ impl Settings { self.custom.push(api_access_method) } - /// Remove an [`ApiAccessMethod`] from `api_access_methods`. + /// Remove an [`AccessMethod`] from `api_access_methods`. /// /// This function will return an error if a built-in API access is about to /// be removed. @@ -205,15 +205,15 @@ impl AccessMethodSetting { } } - /// Just like [`new`], [`with_id`] will create a new [`ApiAccessMethod`]. + /// Just like [`new`], [`with_id`] will create a new [`AccessMethodSetting`]. /// But instead of automatically generating a new UUID, the id is instead /// passed as an argument. /// - /// This is useful when converting to [`ApiAccessMethod`] from other data + /// This is useful when converting to [`AccessMethodSetting`] from other data /// representations, such as protobuf. /// - /// [`new`]: ApiAccessMethod::new - /// [`with_id`]: ApiAccessMethod::with_id + /// [`new`]: AccessMethodSetting::new + /// [`with_id`]: AccessMethodSetting::with_id pub fn with_id(id: Id, name: String, enabled: bool, access_method: AccessMethod) -> Self { Self { id, diff --git a/mullvad-types/src/constraints/mod.rs b/mullvad-types/src/constraints/mod.rs index 756066c447fa..493c0ea4d548 100644 --- a/mullvad-types/src/constraints/mod.rs +++ b/mullvad-types/src/constraints/mod.rs @@ -19,6 +19,8 @@ impl, U> Match for Constraint { } } +// NOTE: This docstring cannot link to `mullvad_relay_selector::relay_selector::query` and +// `RETRY_ORDER`as `mullvad_relay_selector` is not a dependency of this crate /// The intersection of two sets of criteria on [`Relay`](crate::relay_list::Relay)s is another /// criteria which matches the given relay iff both of the original criteria matched. It is /// primarily used by the relay selector to check whether a given connection method is compatible @@ -26,9 +28,10 @@ impl, U> Match for Constraint { /// /// # Examples /// -/// The [`Intersection`] implementation of [`RelayQuery`] upholds the following properties: +/// The [`Intersection`] implementation of +/// `mullvad_relay_selector::relay_selector::query::RelayQuery` upholds the following properties: /// -/// * If two [`RelayQuery`]s differ such that no relay matches both, [`Option::None`] is returned: +/// * If two `RelayQuery`s differ such that no relay matches both, [`Option::None`] is returned: /// ```rust, ignore /// # use mullvad_relay_selector::query::builder::RelayQueryBuilder; /// let query_a = RelayQueryBuilder::new().wireguard().build(); @@ -36,7 +39,7 @@ impl, U> Match for Constraint { /// assert_eq!(query_a.intersection(query_b), None); /// ``` /// -/// * Otherwise, a new [`RelayQuery`] is returned where each constraint is +/// * Otherwise, a new `RelayQuery` is returned where each constraint is /// as specific as possible. See [`Constraint`] for further details. /// ```rust, ignore /// let query_a = RelayQueryBuilder::new().wireguard().build(); @@ -47,11 +50,10 @@ impl, U> Match for Constraint { /// ``` /// /// This way, if the mullvad app wants to check if the user's relay settings -/// are compatible with any other [`RelayQuery`], for examples those defined by -/// [`RETRY_ORDER`] , taking the intersection between them will never result in +/// are compatible with any other `RelayQuery`, for examples those defined by +/// `RETRY_ORDER` , taking the intersection between them will never result in /// a situation where the app can override the user's preferences. /// -/// [`RETRY_ORDER`]: crate::RETRY_ORDER /// /// The macro recursively applies the intersection on each field of the struct and returns the /// resulting type or `None` if any of the intersections failed to overlap. @@ -62,7 +64,7 @@ impl, U> Match for Constraint { /// # Implementing [`Intersection`] /// /// For structs where each field already implements `Intersection`, the easiest way to implement the -/// trait is using the derive macro. Using the derive macro on [`RelayQuery`] +/// trait is using the derive macro. Using the derive macro on `RelayQuery` /// ```rust, ignore /// #[derive(Intersection)] /// struct RelayQuery { @@ -106,7 +108,7 @@ impl, U> Match for Constraint { /// For less trivial cases, the trait needs to be implemented manually. When doing so, make sure /// that the following properties are upheld: /// -/// - idempotency (if there is an identity element) +/// - idempotence (if there is an identity element) /// - commutativity /// - associativity pub trait Intersection: Sized { diff --git a/mullvad-types/src/location.rs b/mullvad-types/src/location.rs index ca2626c7d2db..3f76c5f74a46 100644 --- a/mullvad-types/src/location.rs +++ b/mullvad-types/src/location.rs @@ -63,7 +63,7 @@ impl Coordinates { /// back to spherical coordinates. This is approximate, because the semi-minor (polar) /// axis is assumed to equal the semi-major (equatorial) axis. /// - /// https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates + /// pub fn midpoint(locations: &[Location]) -> Self { Self::midpoint_inner(locations.iter().map(Coordinates::from)) } @@ -110,7 +110,7 @@ fn haversine_dist_deg(lat: f64, lon: f64, other_lat: f64, other_lon: f64) -> f64 other_lon.to_radians(), ) } -/// Implemented as per https://en.wikipedia.org/wiki/Haversine_formula and https://rosettacode.org/wiki/Haversine_formula#Rust +/// Implemented as per and /// Takes input as radians, outputs kilometers. fn haversine_dist_rad(lat: f64, lon: f64, other_lat: f64, other_lon: f64) -> f64 { let d_lat = lat - other_lat; diff --git a/talpid-routing/src/lib.rs b/talpid-routing/src/lib.rs index 684f98a26212..0ba3eb1275ec 100644 --- a/talpid-routing/src/lib.rs +++ b/talpid-routing/src/lib.rs @@ -1,5 +1,5 @@ //! Manage routing tables on various platforms. - +#![allow(rustdoc::private_intra_doc_links)] #![deny(missing_docs)] use ipnetwork::IpNetwork; diff --git a/talpid-routing/src/windows/default_route_monitor.rs b/talpid-routing/src/windows/default_route_monitor.rs index 4e452ef31911..5f113beaf44c 100644 --- a/talpid-routing/src/windows/default_route_monitor.rs +++ b/talpid-routing/src/windows/default_route_monitor.rs @@ -112,7 +112,7 @@ pub struct DefaultRouteMonitor { } /// SAFETY: DefaultRouteMonitor is `Send` since `NotifyChangeHandle` is `Send` and -/// `ContextAndBurstGuard` is `Sync` as it holds Mutex and Arc> fields. +/// `ContextAndBurstGuard` is `Sync` as it holds `Mutex` and `Arc>` fields. unsafe impl Send for DefaultRouteMonitor {} impl Drop for DefaultRouteMonitor { diff --git a/talpid-routing/src/windows/mod.rs b/talpid-routing/src/windows/mod.rs index c158938ebdd2..2e8be7f7789c 100644 --- a/talpid-routing/src/windows/mod.rs +++ b/talpid-routing/src/windows/mod.rs @@ -165,7 +165,7 @@ impl RouteManagerHandle { _ = result_rx.await; } - /// Removes all routes previously applied in [`RouteManager::add_routes`]. + /// Removes all routes previously applied in [`RouteManagerInternal::add_routes`]. pub fn clear_routes(&self) -> Result<()> { self.tx .unbounded_send(RouteManagerCommand::ClearRoutes) diff --git a/talpid-types/src/net/proxy.rs b/talpid-types/src/net/proxy.rs index fc3333a69f8b..d60b2df436f6 100644 --- a/talpid-types/src/net/proxy.rs +++ b/talpid-types/src/net/proxy.rs @@ -87,10 +87,8 @@ impl From for CustomProxy { pub struct Shadowsocks { pub endpoint: SocketAddr, pub password: String, - /// One of [`shadowsocks_ciphers`]. + /// One of [`SHADOWSOCKS_CIPHERS`]. /// Gets validated at a later stage. Is assumed to be valid. - /// - /// shadowsocks_ciphers: talpid_types::net::openvpn::SHADOWSOCKS_CIPHERS pub cipher: String, } @@ -236,7 +234,6 @@ impl Socks5Remote { } /// List of ciphers usable by a Shadowsocks proxy. -/// Cf. [`ShadowsocksProxySettings::cipher`]. pub const SHADOWSOCKS_CIPHERS: [&str; 19] = [ // Stream ciphers. "aes-128-cfb", diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs index 6acd6dc71070..b914164b2f08 100644 --- a/talpid-wireguard/src/wireguard_nt/mod.rs +++ b/talpid-wireguard/src/wireguard_nt/mod.rs @@ -183,7 +183,7 @@ pub struct WgNtTunnel { const WIREGUARD_KEY_LENGTH: usize = 32; -/// See `WIREGUARD_ALLOWED_IP` at https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h. +/// See `WIREGUARD_ALLOWED_IP` at . #[derive(Clone, Copy)] #[repr(C, align(8))] union WgIpAddr { @@ -216,7 +216,7 @@ impl From for WgIpAddr { } } -/// See `WIREGUARD_ALLOWED_IP` at https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h. +/// See `WIREGUARD_ALLOWED_IP` at . #[derive(Clone, Copy)] #[repr(C, align(8))] struct WgAllowedIp { @@ -309,7 +309,7 @@ impl fmt::Debug for WgAllowedIp { } bitflags! { - /// See `WIREGUARD_PEER_FLAG` at https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h. + /// See `WIREGUARD_PEER_FLAG` at . struct WgPeerFlag: u32 { const HAS_PUBLIC_KEY = 0b00000001; const HAS_PRESHARED_KEY = 0b00000010; @@ -322,7 +322,7 @@ bitflags! { } } -/// See `WIREGUARD_PEER` at https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h. +/// See `WIREGUARD_PEER` at . #[derive(Debug, Eq, PartialEq, Clone, Copy)] #[repr(C, align(8))] struct WgPeer { @@ -388,7 +388,7 @@ impl fmt::Debug for SockAddrInet { } bitflags! { - /// See `WIREGUARD_INTERFACE_FLAG` at https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h. + /// See `WIREGUARD_INTERFACE_FLAG` at . struct WgInterfaceFlag: u32 { const HAS_PUBLIC_KEY = 0b00000001; const HAS_PRIVATE_KEY = 0b00000010; @@ -397,7 +397,7 @@ bitflags! { } } -/// See `WIREGUARD_INTERFACE` at https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h. +/// See `WIREGUARD_INTERFACE` at . #[derive(Debug, Eq, PartialEq, Clone, Copy)] #[repr(C, align(8))] struct WgInterface { @@ -408,7 +408,7 @@ struct WgInterface { peers_count: u32, } -/// See `WIREGUARD_ADAPTER_LOG_STATE` at https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h. +/// See `WIREGUARD_ADAPTER_LOG_STATE` at . #[derive(Debug, Eq, PartialEq, Clone, Copy)] #[repr(C)] #[allow(dead_code)] From 95f88f71a74ead6b0e9947cbc249903ccf848b8d Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Mon, 22 Apr 2024 10:09:09 +0200 Subject: [PATCH 142/214] Limit execution of `test_installation_idempotency` --- test/test-manager/src/tests/install.rs | 52 +++++++++++++++----------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/test/test-manager/src/tests/install.rs b/test/test-manager/src/tests/install.rs index f0e0942af7c3..f3541bcb5218 100644 --- a/test/test-manager/src/tests/install.rs +++ b/test/test-manager/src/tests/install.rs @@ -1,15 +1,16 @@ -use super::{ - config::TEST_CONFIG, - helpers::{connect_and_wait, get_app_env, get_package_desc, wait_for_tunnel_state, Pinger}, - Error, TestContext, -}; +use anyhow::Context; +use std::time::Duration; use mullvad_management_interface::MullvadProxyClient; use mullvad_types::{constraints::Constraint, relay_constraints}; use test_macro::test_function; use test_rpc::{meta::Os, mullvad_daemon::ServiceStatus, ServiceClient}; -use std::time::Duration; +use super::{ + config::TEST_CONFIG, + helpers::{connect_and_wait, get_app_env, get_package_desc, wait_for_tunnel_state, Pinger}, + Error, TestContext, +}; /// Install the last stable version of the app and verify that it is running. #[test_function(priority = -200)] @@ -277,7 +278,7 @@ pub async fn test_installation_idempotency( _: TestContext, rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, -) -> Result<(), Error> { +) -> Result<(), anyhow::Error> { // Connect to any relay. This forces the daemon to enter a secured target state connect_and_wait(&mut mullvad_client) .await @@ -290,27 +291,34 @@ pub async fn test_installation_idempotency( mullvad_client .set_auto_connect(false) .await - .expect("failed to enable auto-connect"); + .context("Failed to enable auto-connect")?; + // Check for traffic leaks during the installation processes. // // Start continously pinging while monitoring the network traffic. No // traffic should be observed going outside of the tunnel during either // installation process. let pinger = Pinger::start(&rpc).await; - for _ in 1..=2 { - // install package - log::debug!("Installing new app"); - rpc.install_app(get_package_desc(&TEST_CONFIG.current_app_filename)?) - .await?; - // verify that the daemon starts in a non-disconnected state - wait_for_tunnel_state(mullvad_client.clone(), |state| !state.is_disconnected()) - .await - .map_err(|err| { - log::error!( - "App did not start in the expected `Connected` state after the installation process." - ); - err - })?; + for _ in 0..2 { + // Install the app + log::info!("Installing new app"); + let app_package = get_package_desc(&TEST_CONFIG.current_app_filename)?; + rpc.install_app(app_package).await?; + log::info!("App was successfully installed!"); + + // Verify that the daemon starts in a blocking state. + // I.e., fail if the daemon starts in the disconnected state. + const STATE_TRANSITION_TIMEOUT: Duration = Duration::from_secs(60); + tokio::time::timeout( + STATE_TRANSITION_TIMEOUT, + wait_for_tunnel_state(mullvad_client.clone(), |state| !state.is_disconnected()), + ) + .await + .context("Timeout while waiting for tunnel state")? + .context( + "App did not start in the expected `Connected` state after the installation process.", + )?; + // Wait for an arbitrary amount of time. The point is that the pinger // should be able to ping while the newly installed app is running. if let Some(delay) = pinger.period().checked_mul(3) { From 0a194d97e66d25b44799835d4e9c472ed070255d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Fri, 19 Apr 2024 13:47:15 +0200 Subject: [PATCH 143/214] Rename .cargo/config to .cargo/config.toml This is the new default starting in Rust 1.78.0 --- .cargo/{config => config.toml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .cargo/{config => config.toml} (100%) diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml From 170458d27ad6c686bfa17f09339603ba45e7ee37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 23 Apr 2024 09:40:36 +0200 Subject: [PATCH 144/214] Upgrade rustls to avoid RUSTSEC-2024-0336 --- Cargo.lock | 45 +++++++++++++++++++++++++++++++++------------ test/Cargo.lock | 49 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b16c1825021..185af07aa73a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1897,7 +1897,7 @@ dependencies = [ "libflate", "rand 0.8.5", "rand_distr", - "ring", + "ring 0.16.20", "serde", "simple-error", ] @@ -3142,11 +3142,26 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.10", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "ring-compat" version = "0.5.1" @@ -3162,7 +3177,7 @@ dependencies = [ "p256", "p384", "pkcs8", - "ring", + "ring 0.16.20", "signature", ] @@ -3223,12 +3238,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-webpki", "sct", ] @@ -3244,12 +3259,12 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.4" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -3297,8 +3312,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -4456,6 +4471,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.4.1" diff --git a/test/Cargo.lock b/test/Cargo.lock index 6d559010905a..4060ab9977eb 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -2639,11 +2639,26 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.10", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "ring-compat" version = "0.5.1" @@ -2659,7 +2674,7 @@ dependencies = [ "p256", "p384", "pkcs8", - "ring", + "ring 0.16.20", "signature", ] @@ -2699,13 +2714,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", - "ring", - "rustls-webpki 0.101.6", + "ring 0.17.8", + "rustls-webpki 0.101.7", "sct", ] @@ -2745,18 +2760,18 @@ version = "0.100.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -2801,8 +2816,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -3867,6 +3882,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.4.1" From 7f0fd241d1b3817bc7378f0fcce49490756b8b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Tue, 23 Apr 2024 12:46:07 +0200 Subject: [PATCH 145/214] Bump setup-protoc action to v3 --- .github/workflows/clippy.yml | 2 +- .github/workflows/daemon.yml | 4 ++-- .github/workflows/desktop-e2e.yml | 4 ++-- .github/workflows/rust-unused-dependencies.yml | 2 +- .github/workflows/testframework-clippy.yml | 4 ++-- .github/workflows/testframework.yml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 2dfcb000c510..237ba8d953a3 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -39,7 +39,7 @@ jobs: uses: actions/checkout@v3 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/daemon.yml b/.github/workflows/daemon.yml index 3293e028791e..1d9c1d90a8e9 100644 --- a/.github/workflows/daemon.yml +++ b/.github/workflows/daemon.yml @@ -87,7 +87,7 @@ jobs: uses: actions/checkout@v2 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} @@ -115,7 +115,7 @@ jobs: run: git submodule update --init --depth=1 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/desktop-e2e.yml b/.github/workflows/desktop-e2e.yml index 3d88d8fc47b1..c1e4624895b7 100644 --- a/.github/workflows/desktop-e2e.yml +++ b/.github/workflows/desktop-e2e.yml @@ -163,7 +163,7 @@ jobs: - name: Checkout submodules run: git submodule update --init --depth=1 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-node@v3 @@ -239,7 +239,7 @@ jobs: with: go-version: 1.21.3 - name: Install Protoc - uses: arduino/setup-protoc@v2 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-node@v3 diff --git a/.github/workflows/rust-unused-dependencies.yml b/.github/workflows/rust-unused-dependencies.yml index ac07b079c96e..8e1a464e1226 100644 --- a/.github/workflows/rust-unused-dependencies.yml +++ b/.github/workflows/rust-unused-dependencies.yml @@ -95,7 +95,7 @@ jobs: uses: actions/checkout@v3 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/testframework-clippy.yml b/.github/workflows/testframework-clippy.yml index 11ee9af845b0..8f2b8f63f7ca 100644 --- a/.github/workflows/testframework-clippy.yml +++ b/.github/workflows/testframework-clippy.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v3 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@v3 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/testframework.yml b/.github/workflows/testframework.yml index f382567c52cd..469c61b48873 100644 --- a/.github/workflows/testframework.yml +++ b/.github/workflows/testframework.yml @@ -75,7 +75,7 @@ jobs: uses: actions/checkout@v2 - name: Install Protoc - uses: arduino/setup-protoc@v1 + uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} From d63e491cacd6836954dbc2ecb6497037ae4077d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Tue, 23 Apr 2024 13:30:52 +0200 Subject: [PATCH 146/214] Add TODO comment about removing symlink monitor --- .../src/split_tunnel/windows/path_monitor.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/talpid-core/src/split_tunnel/windows/path_monitor.rs b/talpid-core/src/split_tunnel/windows/path_monitor.rs index 8843909fd815..5dfff9dfb9cd 100644 --- a/talpid-core/src/split_tunnel/windows/path_monitor.rs +++ b/talpid-core/src/split_tunnel/windows/path_monitor.rs @@ -1,3 +1,20 @@ +//! This module detects changes to paths that are symlinks, resolves them and updates paths used by +//! the split tunnel driver accordingly. + +// TODO: Consider whether this code can be removed if paths are handled differently in the driver. +// The driver takes currently paths to the actual files on actual volumes (such as +// \Device\HarddiskVolume1\test.exe), not symlinks or DOS paths (such as C:\test.exe). If it +// instead accepted DOS paths and resolved them to NT/real paths when handling process +// arrivals/departures, then perhaps symlinks could be resolved when processes started, so any +// changes to symlinks would immediately be reflected (in new processes) without any config +// update. +// This would still have the limitation that changes to symlinks would not be detected without +// any monitoring. Assume that C:\a.exe is a symlink that points to +// \Device\HarddiskVolume1\test.exe. If C:\a.exe were deleted or replaced with a symlink pointing +// to \Device\HarddiskVolume2\lol.exe instead, then those old processes whose image is +// \Device\HarddiskVolume1\test.exe would still be excluded from the tunnel. This might be an +// acceptable limitation. + use std::{ collections::HashSet, ffi::{OsStr, OsString}, From 0329c9e1c813da258f09d435809f3c7ce0f6293f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 17 Apr 2024 22:12:38 +0200 Subject: [PATCH 147/214] Remove some pointless checks --- mullvad-daemon/src/version_check.rs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index a53424324d1d..91d34074d2c7 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -336,10 +336,7 @@ impl VersionUpdater { /// /// [rvc]: VersionUpdaterCommand::GetVersionInfo async fn update_version_info(&mut self, new_version_info: AppVersionInfo) { - // if daemon can't be reached, return immediately - if self.update_sender.send(new_version_info.clone()).is_err() { - return; - } + let _ = self.update_sender.send(new_version_info.clone()); self.last_app_version_info = Some((new_version_info, SystemTime::now())); if let Err(err) = self.write_cache().await { @@ -429,9 +426,6 @@ impl VersionUpdater { } Some(VersionUpdaterCommand::GetVersionInfo(done_tx)) => { - if self.update_sender.is_closed() { - return; - } match (self.version_is_stale(), self.last_app_version_info()) { (false, Some(version_info)) => { // if the version_info isn't stale, return it immediately. @@ -450,14 +444,11 @@ impl VersionUpdater { // time to shut down None => { - return; + break; } }, _ = version_is_stale => { - if rx.is_terminated() || self.update_sender.is_closed() { - return; - } if self.is_running_version_check() { continue; } @@ -465,10 +456,6 @@ impl VersionUpdater { }, response = version_check => { - if rx.is_terminated() || self.update_sender.is_closed() { - return; - } - match response { Ok(version_info_response) => { let new_version_info = From b0f5b60658fb723f224517b4232e3928fe7fe006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 17 Apr 2024 22:57:39 +0200 Subject: [PATCH 148/214] Remove pointless round trip for version check --- mullvad-daemon/src/lib.rs | 3 +- mullvad-daemon/src/version_check.rs | 59 ++++++++++++++--------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 52df09e6d5b1..20439368459c 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -857,7 +857,7 @@ where on_relay_list_update, ); - let (version_updater, version_updater_handle) = version_check::VersionUpdater::new( + let version_updater_handle = version_check::VersionUpdater::spawn( api_handle.clone(), api_availability.clone(), cache_dir.clone(), @@ -865,7 +865,6 @@ where settings.show_beta_releases, ) .await; - tokio::spawn(version_updater.run()); // Attempt to download a fresh relay list relay_list_updater.update().await; diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index 91d34074d2c7..97492b3734c7 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -2,7 +2,6 @@ use crate::{version::is_beta_version, DaemonEventSender}; use futures::{ channel::{mpsc, oneshot}, future::FusedFuture, - stream::FusedStream, FutureExt, SinkExt, StreamExt, TryFutureExt, }; use mullvad_api::{availability::ApiAvailabilityHandle, rest::MullvadRestHandle, AppVersionProxy}; @@ -101,7 +100,6 @@ pub(crate) struct VersionUpdater { last_app_version_info: Option<(AppVersionInfo, SystemTime)>, platform_version: String, show_beta_releases: bool, - rx: Option>, availability_handle: ApiAvailabilityHandle, /// Oneshot channels for responding to [VersionUpdaterCommand::GetVersionInfo]. @@ -152,13 +150,13 @@ impl VersionUpdaterHandle { } impl VersionUpdater { - pub async fn new( + pub async fn spawn( mut api_handle: MullvadRestHandle, availability_handle: ApiAvailabilityHandle, cache_dir: PathBuf, update_sender: DaemonEventSender, show_beta_releases: bool, - ) -> (Self, VersionUpdaterHandle) { + ) -> VersionUpdaterHandle { // load the last known AppVersionInfo from cache let last_app_version_info = load_cache(&cache_dir).await; @@ -168,7 +166,7 @@ impl VersionUpdater { let (tx, rx) = mpsc::channel(1); let platform_version = talpid_platform_metadata::short_version(); - ( + tokio::spawn( Self { version_proxy, cache_path, @@ -176,12 +174,13 @@ impl VersionUpdater { last_app_version_info, platform_version, show_beta_releases, - rx: Some(rx), availability_handle, get_version_info_responders: vec![], - }, - VersionUpdaterHandle { tx }, - ) + } + .run(rx), + ); + + VersionUpdaterHandle { tx } } /// Get the last known [AppVersionInfo]. May be stale. @@ -382,11 +381,7 @@ impl VersionUpdater { !self.get_version_info_responders.is_empty() } - pub async fn run(mut self) { - let mut rx = self.rx.take().unwrap(); - let mut version_is_stale = self.wait_until_version_is_stale(); - let mut version_check = futures::future::Fuse::terminated(); - + async fn run(mut self, mut rx: mpsc::Receiver) { // If this is a dev build, there's no need to pester the API for version checks. if *IS_DEV_BUILD { log::warn!("Not checking for updates because this is a development build"); @@ -399,6 +394,9 @@ impl VersionUpdater { return; } + let mut version_is_stale = self.wait_until_version_is_stale(); + let mut version_check = futures::future::Fuse::terminated(); + loop { futures::select! { command = rx.next() => match command { @@ -482,6 +480,22 @@ impl VersionUpdater { } } +/// Read the app version cache from the provided directory. +/// +/// Returns the [AppVersionInfo] along with the modification time of the cache file, +/// or `None` on any error. +async fn load_cache(cache_dir: &Path) -> Option<(AppVersionInfo, SystemTime)> { + try_load_cache(cache_dir) + .await + .inspect_err(|error| { + log::warn!( + "{}", + error.display_chain_with_msg("Unable to load cached version info") + ) + }) + .ok() +} + async fn try_load_cache(cache_dir: &Path) -> Result<(AppVersionInfo, SystemTime), Error> { if *IS_DEV_BUILD { return Ok((dev_version_cache(), SystemTime::now())); @@ -511,23 +525,6 @@ async fn try_load_cache(cache_dir: &Path) -> Result<(AppVersionInfo, SystemTime) } } -/// Read the app version cache from the provided directory. -/// -/// Returns the [AppVersionInfo] along with the modification time of the cache file, -/// or `None` on any error. -async fn load_cache(cache_dir: &Path) -> Option<(AppVersionInfo, SystemTime)> { - match try_load_cache(cache_dir).await { - Ok(app_version_info) => Some(app_version_info), - Err(error) => { - log::warn!( - "{}", - error.display_chain_with_msg("Unable to load cached version info") - ); - None - } - } -} - fn dev_version_cache() -> AppVersionInfo { assert!(*IS_DEV_BUILD); From ab6b6bb649aa654eb3e2238a8f9a27bf8298074d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 17 Apr 2024 22:58:18 +0200 Subject: [PATCH 149/214] Remove unused DaemonEventSender method --- mullvad-daemon/src/lib.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 20439368459c..69eebdbdd69a 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -570,18 +570,6 @@ impl DaemonEventSender { } } -impl DaemonEventSender -where - InternalDaemonEvent: From, -{ - pub fn is_closed(&self) -> bool { - self.sender - .upgrade() - .map(|sender| sender.is_closed()) - .unwrap_or(true) - } -} - impl Sender for DaemonEventSender where InternalDaemonEvent: From, From 0ba9f4c59118239b900cd52355e817bbe1f1eaed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Fri, 19 Apr 2024 09:53:54 +0200 Subject: [PATCH 150/214] Make talpid-time timers mockable in tests --- talpid-time/Cargo.toml | 3 +++ talpid-time/src/lib.rs | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/talpid-time/Cargo.toml b/talpid-time/Cargo.toml index 40fd2f5e74a5..4f6158ee532a 100644 --- a/talpid-time/Cargo.toml +++ b/talpid-time/Cargo.toml @@ -10,6 +10,9 @@ rust-version.workspace = true [lints] workspace = true +[features] +test = [] + [dependencies] tokio = { workspace = true, features = ["time"] } libc = "0.2" diff --git a/talpid-time/src/lib.rs b/talpid-time/src/lib.rs index 3b536eda54bb..4ca9c8bf5d8b 100644 --- a/talpid-time/src/lib.rs +++ b/talpid-time/src/lib.rs @@ -1,14 +1,20 @@ use std::time::Duration; -#[cfg(target_os = "windows")] +#[cfg(all(not(feature = "test"), target_os = "windows"))] mod inner { pub use std::time::Instant; } -#[cfg(unix)] +#[cfg(all(not(feature = "test"), unix))] #[path = "unix.rs"] mod inner; +#[cfg(feature = "test")] +mod inner { + /// Use mockable time for tests + pub use tokio::time::Instant; +} + const MAX_SLEEP_INTERVAL: Duration = Duration::from_secs(60); /// Represents a measurement of a monotonic clock. From 45cbe81ea3a5308b6915bff019d51f7c43317caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Wed, 17 Apr 2024 23:32:18 +0200 Subject: [PATCH 151/214] Add tests for version checker --- mullvad-daemon/Cargo.toml | 4 + mullvad-daemon/src/version_check.rs | 522 +++++++++++++++++++--------- 2 files changed, 359 insertions(+), 167 deletions(-) diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index c71a8ae07bba..4f7217056134 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -45,6 +45,10 @@ log-panics = "2.0.0" mullvad-management-interface = { path = "../mullvad-management-interface" } mullvad-paths = { path = "../mullvad-paths" } +[dev-dependencies] +talpid-time = { path = "../talpid-time", features = ["test"] } +tokio = { workspace = true, features = ["test-util"] } + [target.'cfg(target_os="android")'.dependencies] android_logger = "0.8" diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index 97492b3734c7..d4274ec10d32 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -1,7 +1,7 @@ use crate::{version::is_beta_version, DaemonEventSender}; use futures::{ channel::{mpsc, oneshot}, - future::FusedFuture, + future::{BoxFuture, FusedFuture}, FutureExt, SinkExt, StreamExt, TryFutureExt, }; use mullvad_api::{availability::ApiAvailabilityHandle, rest::MullvadRestHandle, AppVersionProxy}; @@ -92,16 +92,13 @@ pub enum Error { UpdateAborted, } -pub(crate) struct VersionUpdater { - version_proxy: AppVersionProxy, - cache_path: PathBuf, - update_sender: DaemonEventSender, +pub(crate) struct VersionUpdater; + +#[derive(Default)] +struct VersionUpdaterInner { /// The last known [AppVersionInfo], along with the time it was determined. last_app_version_info: Option<(AppVersionInfo, SystemTime)>, - platform_version: String, show_beta_releases: bool, - availability_handle: ApiAvailabilityHandle, - /// Oneshot channels for responding to [VersionUpdaterCommand::GetVersionInfo]. get_version_info_responders: Vec>, } @@ -160,135 +157,49 @@ impl VersionUpdater { // load the last known AppVersionInfo from cache let last_app_version_info = load_cache(&cache_dir).await; + let (tx, rx) = mpsc::channel(1); + api_handle.factory = api_handle.factory.default_timeout(DOWNLOAD_TIMEOUT); let version_proxy = AppVersionProxy::new(api_handle); let cache_path = cache_dir.join(VERSION_INFO_FILENAME); - let (tx, rx) = mpsc::channel(1); let platform_version = talpid_platform_metadata::short_version(); tokio::spawn( - Self { - version_proxy, - cache_path, - update_sender, + VersionUpdaterInner { last_app_version_info, - platform_version, show_beta_releases, - availability_handle, get_version_info_responders: vec![], } - .run(rx), + .run( + rx, + UpdateContext { + cache_path, + update_sender, + }, + ApiContext { + api_handle: availability_handle, + version_proxy, + platform_version, + }, + ), ); VersionUpdaterHandle { tx } } +} +impl VersionUpdaterInner { /// Get the last known [AppVersionInfo]. May be stale. pub fn last_app_version_info(&self) -> Option<&AppVersionInfo> { self.last_app_version_info.as_ref().map(|(info, _)| info) } - /// Immediately query the API for the latest [AppVersionInfo]. - fn do_version_check( - &mut self, - ) -> Pin< - Box> + Send + 'static>, - > { - let api_handle = self.availability_handle.clone(); - let version_proxy = self.version_proxy.clone(); - let platform_version = self.platform_version.clone(); - let download_future_factory = move || { - version_proxy - .version_check( - mullvad_version::VERSION.to_owned(), - PLATFORM, - platform_version.clone(), - ) - .map_err(Error::Download) - }; - - // retry immediately on network errors (unless we're offline) - let should_retry_immediate = move |result: &Result<_, Error>| { - if let Err(Error::Download(error)) = result { - error.is_network_error() && !api_handle.get_state().is_offline() - } else { - false - } - }; - - Box::pin(retry_future( - download_future_factory, - should_retry_immediate, - IMMEDIATE_RETRY_STRATEGY, - )) - } - - /// Query the API for the latest [AppVersionInfo]. - /// - /// This function waits until background calls are enabled in - /// [ApiAvailability](mullvad_api::availability::ApiAvailability). - /// - /// On any error, this function retries repeatedly every [UPDATE_INTERVAL_ERROR] until success. - fn do_version_check_in_background( - &self, - ) -> Pin< - Box> + Send + 'static>, - > { - let api_handle = self.availability_handle.clone(); - let version_proxy = self.version_proxy.clone(); - let platform_version = self.platform_version.clone(); - let download_future_factory = move || { - let when_available = api_handle.wait_background(); - let request = version_proxy.version_check( - mullvad_version::VERSION.to_owned(), - PLATFORM, - platform_version.clone(), - ); - async move { - when_available.await.map_err(Error::ApiCheck)?; - request.await.map_err(Error::Download) - } - }; - - Box::pin(retry_future( - download_future_factory, - |result| result.is_err(), - std::iter::repeat(UPDATE_INTERVAL_ERROR), - )) - } - - /// Write [Self::last_app_version_info], if any, to the cache file ([VERSION_INFO_FILENAME]). - async fn write_cache(&self) -> Result<(), Error> { - let last_app_version_info = match self.last_app_version_info() { - Some(version_info) => version_info, - None => { - log::debug!("The version cache is empty -- not writing"); - return Ok(()); - } - }; - log::debug!( - "Writing version check cache to {}", - self.cache_path.display() - ); - let mut file = File::create(&self.cache_path) - .await - .map_err(Error::WriteVersionCache)?; - let cached_app_version = CachedAppVersionInfo::from(last_app_version_info.clone()); - let mut buf = serde_json::to_vec_pretty(&cached_app_version).map_err(Error::Serialize)?; - let mut read_buf: &[u8] = buf.as_mut(); - - let _ = tokio::io::copy(&mut read_buf, &mut file) - .await - .map_err(Error::WriteVersionCache)?; - Ok(()) - } - /// Convert a [mullvad_api::AppVersionResponse] to an [AppVersionInfo]. fn response_to_version_info( - &mut self, + &self, response: mullvad_api::AppVersionResponse, ) -> AppVersionInfo { - let suggested_upgrade = Self::suggested_upgrade( + let suggested_upgrade = suggested_upgrade( &APP_VERSION, &response.latest_stable, &response.latest_beta, @@ -303,44 +214,17 @@ impl VersionUpdater { } } - /// If current_version is not the latest, return a string containing the latest version. - fn suggested_upgrade( - current_version: &ParsedAppVersion, - latest_stable: &Option, - latest_beta: &str, - show_beta: bool, - ) -> Option { - let stable_version = latest_stable - .as_ref() - .and_then(|stable| ParsedAppVersion::from_str(stable).ok()); - - let beta_version = if show_beta { - ParsedAppVersion::from_str(latest_beta).ok() - } else { - None - }; - - let latest_version = max(stable_version, beta_version)?; - - if current_version < &latest_version { - Some(latest_version.to_string()) - } else { - None - } - } - - /// Update [Self::last_app_version_info] and write it to disk cache. - /// - /// Also, if we are currently have a pending [GetVersionInfo][rvc] command, respond to it. - /// - /// [rvc]: VersionUpdaterCommand::GetVersionInfo - async fn update_version_info(&mut self, new_version_info: AppVersionInfo) { - let _ = self.update_sender.send(new_version_info.clone()); - - self.last_app_version_info = Some((new_version_info, SystemTime::now())); - if let Err(err) = self.write_cache().await { + /// Update [Self::last_app_version_info] and write it to disk cache, and notify the `update` + /// callback. + async fn update_version_info( + &mut self, + update: &impl Fn(AppVersionInfo) -> BoxFuture<'static, Result<(), Error>>, + new_version_info: AppVersionInfo, + ) { + if let Err(err) = update(new_version_info.clone()).await { log::error!("Failed to save version cache to disk: {}", err); } + self.last_app_version_info = Some((new_version_info, SystemTime::now())); } /// Get the time left until [Self::last_app_version_info] becomes stale, and should be @@ -381,7 +265,12 @@ impl VersionUpdater { !self.get_version_info_responders.is_empty() } - async fn run(mut self, mut rx: mpsc::Receiver) { + async fn run( + self, + mut rx: mpsc::Receiver, + update: UpdateContext, + api: ApiContext, + ) { // If this is a dev build, there's no need to pester the API for version checks. if *IS_DEV_BUILD { log::warn!("Not checking for updates because this is a development build"); @@ -394,6 +283,25 @@ impl VersionUpdater { return; } + let update = |info| Box::pin(update.update(info)) as BoxFuture<'static, _>; + let do_version_check = || do_version_check(api.clone()); + let do_version_check_in_background = || do_version_check_in_background(api.clone()); + + self.run_inner(rx, update, do_version_check, do_version_check_in_background) + .await + } + + async fn run_inner( + mut self, + mut rx: mpsc::Receiver, + update: impl Fn(AppVersionInfo) -> BoxFuture<'static, Result<(), Error>>, + do_version_check: impl Fn() + -> BoxFuture<'static, Result>, + do_version_check_in_background: impl Fn() -> BoxFuture< + 'static, + Result, + >, + ) { let mut version_is_stale = self.wait_until_version_is_stale(); let mut version_check = futures::future::Fuse::terminated(); @@ -407,14 +315,14 @@ impl VersionUpdater { .last_app_version_info() .cloned() { - let suggested_upgrade = Self::suggested_upgrade( + let suggested_upgrade = suggested_upgrade( &APP_VERSION, &Some(last_app_version_info.latest_stable.clone()), &last_app_version_info.latest_beta, self.show_beta_releases || is_beta_version(), ); - self.update_version_info(AppVersionInfo { + self.update_version_info(&update, AppVersionInfo { supported: last_app_version_info.supported, latest_stable: last_app_version_info.latest_stable, latest_beta: last_app_version_info.latest_beta, @@ -432,7 +340,7 @@ impl VersionUpdater { _ => { // otherwise, start a foreground query to get the latest version_info. if !self.is_running_version_check() { - version_check = self.do_version_check().fuse(); + version_check = do_version_check().fuse(); } self.get_version_info_responders.retain(|r| !r.is_canceled()); self.get_version_info_responders.push(done_tx); @@ -450,7 +358,7 @@ impl VersionUpdater { if self.is_running_version_check() { continue; } - version_check = self.do_version_check_in_background().fuse(); + version_check = do_version_check_in_background().fuse(); }, response = version_check => { @@ -464,7 +372,7 @@ impl VersionUpdater { let _ = done_tx.send(new_version_info.clone()); } - self.update_version_info(new_version_info).await; + self.update_version_info(&update, new_version_info).await; } Err(err) => { @@ -480,6 +388,95 @@ impl VersionUpdater { } } +struct UpdateContext { + cache_path: PathBuf, + update_sender: DaemonEventSender, +} + +impl UpdateContext { + /// Write [VersionUpdaterInner::last_app_version_info], if any, to the cache file + /// ([VERSION_INFO_FILENAME]). Also, notify `self.update_sender` + fn update(&self, last_app_version: AppVersionInfo) -> impl Future> { + let _ = self.update_sender.send(last_app_version.clone()); + let cache_path = self.cache_path.clone(); + + async move { + log::debug!("Writing version check cache to {}", cache_path.display()); + let cached_app_version = CachedAppVersionInfo::from(last_app_version.to_owned()); + let buf = serde_json::to_vec_pretty(&cached_app_version).map_err(Error::Serialize)?; + tokio::fs::write(cache_path, buf) + .await + .map_err(Error::WriteVersionCache) + } + } +} + +#[derive(Clone)] +struct ApiContext { + api_handle: ApiAvailabilityHandle, + version_proxy: AppVersionProxy, + platform_version: String, +} + +/// Immediately query the API for the latest [AppVersionInfo]. +fn do_version_check( + api: ApiContext, +) -> BoxFuture<'static, Result> { + let download_future_factory = move || { + api.version_proxy + .version_check( + mullvad_version::VERSION.to_owned(), + PLATFORM, + api.platform_version.clone(), + ) + .map_err(Error::Download) + }; + + // retry immediately on network errors (unless we're offline) + let should_retry_immediate = move |result: &Result<_, Error>| { + if let Err(Error::Download(error)) = result { + error.is_network_error() && !api.api_handle.get_state().is_offline() + } else { + false + } + }; + + Box::pin(retry_future( + download_future_factory, + should_retry_immediate, + IMMEDIATE_RETRY_STRATEGY, + )) +} + +/// Query the API for the latest [AppVersionInfo]. +/// +/// This function waits until background calls are enabled in +/// [ApiAvailability](mullvad_api::availability::ApiAvailability). +/// +/// On any error, this function retries repeatedly every [UPDATE_INTERVAL_ERROR] until success. +fn do_version_check_in_background( + api: ApiContext, +) -> BoxFuture<'static, Result> { + let download_future_factory = move || { + let when_available = api.api_handle.wait_background(); + let request = api.version_proxy.version_check( + mullvad_version::VERSION.to_owned(), + PLATFORM, + api.platform_version.clone(), + ); + async move { + when_available.await.map_err(Error::ApiCheck)?; + request.await.map_err(Error::Download) + } + }; + + Box::pin(retry_future( + download_future_factory, + |result| result.is_err(), + std::iter::repeat(UPDATE_INTERVAL_ERROR), + )) +} + /// Read the app version cache from the provided directory. /// /// Returns the [AppVersionInfo] along with the modification time of the cache file, @@ -535,11 +532,202 @@ fn dev_version_cache() -> AppVersionInfo { suggested_upgrade: None, } } +/// If current_version is not the latest, return a string containing the latest version. +fn suggested_upgrade( + current_version: &ParsedAppVersion, + latest_stable: &Option, + latest_beta: &str, + show_beta: bool, +) -> Option { + let stable_version = latest_stable + .as_ref() + .and_then(|stable| ParsedAppVersion::from_str(stable).ok()); + + let beta_version = if show_beta { + ParsedAppVersion::from_str(latest_beta).ok() + } else { + None + }; + + let latest_version = max(stable_version, beta_version)?; + + if current_version < &latest_version { + Some(latest_version.to_string()) + } else { + None + } +} #[cfg(test)] mod test { + use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }; + use super::*; + /// If there's no cached version, it should count as stale + #[test] + fn test_version_unknown_is_stale() { + let checker = VersionUpdaterInner::default(); + assert!(checker.last_app_version_info.is_none()); + assert!(checker.version_is_stale()); + } + + /// If the last checked time is in the future, the version is stale + #[test] + fn test_version_invalid_is_stale() { + let checker = VersionUpdaterInner { + last_app_version_info: Some(( + dev_version_cache(), + SystemTime::now() + Duration::from_secs(1), + )), + ..VersionUpdaterInner::default() + }; + assert!(checker.version_is_stale()); + } + + /// If we have a cached version that's less than `UPDATE_INTERVAL` old, it should not be stale + #[test] + fn test_version_actual_non_stale() { + let checker = VersionUpdaterInner { + last_app_version_info: Some(( + dev_version_cache(), + SystemTime::now() - UPDATE_INTERVAL + Duration::from_secs(1), + )), + ..VersionUpdaterInner::default() + }; + assert!(!checker.version_is_stale()); + } + + /// If `UPDATE_INTERVAL` has elapsed, the version should be stale + #[test] + fn test_version_actual_stale() { + let checker = VersionUpdaterInner { + last_app_version_info: Some((dev_version_cache(), SystemTime::now() - UPDATE_INTERVAL)), + ..VersionUpdaterInner::default() + }; + assert!(checker.version_is_stale()); + } + + /// Test whether check immediately fetches version info if it's non-existent + #[tokio::test(start_paused = true)] + async fn test_version_check_run_immediate() { + let checker = VersionUpdaterInner::default(); + + let updated = Arc::new(AtomicBool::new(false)); + let update = fake_updater(updated.clone()); + + let (_tx, rx) = mpsc::channel(1); + tokio::spawn(checker.run_inner(rx, update, fake_version_check, fake_version_check)); + + talpid_time::sleep(Duration::from_secs(10)).await; + assert!(updated.load(Ordering::SeqCst), "expected immediate update"); + } + + /// Test whether check actually runs after `UPDATE_INTERVAL` + #[tokio::test(start_paused = true)] + async fn test_version_check_run_when_stale() { + let checker = VersionUpdaterInner { + last_app_version_info: Some((dev_version_cache(), SystemTime::now())), + ..VersionUpdaterInner::default() + }; + + let updated = Arc::new(AtomicBool::new(false)); + let update = fake_updater(updated.clone()); + + let (_tx, rx) = mpsc::channel(1); + tokio::spawn(checker.run_inner(rx, update, fake_version_check, fake_version_check)); + + assert!(!updated.load(Ordering::SeqCst)); + + talpid_time::sleep(Duration::from_secs(10)).await; + assert!( + !updated.load(Ordering::SeqCst), + "short interval: no update should have occurred" + ); + + talpid_time::sleep(UPDATE_INTERVAL).await; + assert!( + updated.load(Ordering::SeqCst), + "check should have run after `UPDATE_INTERVAL`" + ); + } + + /// Test whether check runs immediately when requested, if stale + #[tokio::test(start_paused = true)] + async fn test_version_check_manual() { + let checker = VersionUpdaterInner { + last_app_version_info: Some((dev_version_cache(), SystemTime::now() - UPDATE_INTERVAL)), + ..VersionUpdaterInner::default() + }; + + let updated = Arc::new(AtomicBool::new(false)); + let update = fake_updater(updated.clone()); + + let (mut tx, rx) = mpsc::channel(1); + tokio::spawn(checker.run_inner(rx, update, fake_version_check, fake_version_check_err)); + + // Fail automatic update + talpid_time::sleep(Duration::from_secs(1)).await; + assert!(!updated.load(Ordering::SeqCst), "check should fail"); + + // Requesting version should trigger an immediate update + send_version_request(&mut tx).await.unwrap(); + talpid_time::sleep(Duration::from_secs(1)).await; + assert!( + updated.load(Ordering::SeqCst), + "expected immediate update from stale" + ); + + updated.store(false, Ordering::SeqCst); + + // The next request should do nothing + send_version_request(&mut tx).await.unwrap(); + talpid_time::sleep(Duration::from_secs(1)).await; + assert!(!updated.load(Ordering::SeqCst), "expected cached version"); + } + + async fn send_version_request( + tx: &mut mpsc::Sender, + ) -> Result<(), futures::channel::mpsc::SendError> { + let (done_tx, _done_rx) = oneshot::channel(); + tx.send(VersionUpdaterCommand::GetVersionInfo(done_tx)) + .await + } + + fn fake_updater( + updated: Arc, + ) -> impl Fn(AppVersionInfo) -> BoxFuture<'static, Result<(), Error>> { + move |_new_version| { + updated.store(true, Ordering::SeqCst); + Box::pin(async { Ok(()) }) + } + } + + fn fake_version_check() -> BoxFuture<'static, Result> { + Box::pin(async { Ok(fake_version_response()) }) + } + + fn fake_version_check_err() -> BoxFuture<'static, Result> + { + Box::pin(retry_future( + || async { Err(Error::Download(mullvad_api::rest::Error::TimeoutError)) }, + |_| true, + std::iter::repeat(UPDATE_INTERVAL_ERROR), + )) + } + + fn fake_version_response() -> mullvad_api::AppVersionResponse { + mullvad_api::AppVersionResponse { + supported: true, + latest: "2024.1".to_owned(), + latest_stable: None, + latest_beta: "2024.1-beta1".to_owned(), + } + } + #[test] fn test_version_upgrade_suggestions() { let latest_stable = Some("2020.4".to_string()); @@ -554,51 +742,51 @@ mod test { let newer_beta = ParsedAppVersion::from_str("2021.5-beta3").unwrap(); assert_eq!( - VersionUpdater::suggested_upgrade(&older_stable, &latest_stable, latest_beta, false), + suggested_upgrade(&older_stable, &latest_stable, latest_beta, false), Some("2020.4".to_owned()) ); assert_eq!( - VersionUpdater::suggested_upgrade(&older_stable, &latest_stable, latest_beta, true), + suggested_upgrade(&older_stable, &latest_stable, latest_beta, true), Some("2020.5-beta3".to_owned()) ); assert_eq!( - VersionUpdater::suggested_upgrade(¤t_stable, &latest_stable, latest_beta, false), + suggested_upgrade(¤t_stable, &latest_stable, latest_beta, false), None ); assert_eq!( - VersionUpdater::suggested_upgrade(¤t_stable, &latest_stable, latest_beta, true), + suggested_upgrade(¤t_stable, &latest_stable, latest_beta, true), Some("2020.5-beta3".to_owned()) ); assert_eq!( - VersionUpdater::suggested_upgrade(&newer_stable, &latest_stable, latest_beta, false), + suggested_upgrade(&newer_stable, &latest_stable, latest_beta, false), None ); assert_eq!( - VersionUpdater::suggested_upgrade(&newer_stable, &latest_stable, latest_beta, true), + suggested_upgrade(&newer_stable, &latest_stable, latest_beta, true), None ); assert_eq!( - VersionUpdater::suggested_upgrade(&older_beta, &latest_stable, latest_beta, false), + suggested_upgrade(&older_beta, &latest_stable, latest_beta, false), Some("2020.4".to_owned()) ); assert_eq!( - VersionUpdater::suggested_upgrade(&older_beta, &latest_stable, latest_beta, true), + suggested_upgrade(&older_beta, &latest_stable, latest_beta, true), Some("2020.5-beta3".to_owned()) ); assert_eq!( - VersionUpdater::suggested_upgrade(¤t_beta, &latest_stable, latest_beta, false), + suggested_upgrade(¤t_beta, &latest_stable, latest_beta, false), None ); assert_eq!( - VersionUpdater::suggested_upgrade(¤t_beta, &latest_stable, latest_beta, true), + suggested_upgrade(¤t_beta, &latest_stable, latest_beta, true), None ); assert_eq!( - VersionUpdater::suggested_upgrade(&newer_beta, &latest_stable, latest_beta, false), + suggested_upgrade(&newer_beta, &latest_stable, latest_beta, false), None ); assert_eq!( - VersionUpdater::suggested_upgrade(&newer_beta, &latest_stable, latest_beta, true), + suggested_upgrade(&newer_beta, &latest_stable, latest_beta, true), None ); } From 9e1f55d9b23eee7c91b47763a0735f397531b469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Fri, 19 Apr 2024 09:59:13 +0200 Subject: [PATCH 152/214] Document 'test' feature in talpid-time --- talpid-time/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/talpid-time/Cargo.toml b/talpid-time/Cargo.toml index 4f6158ee532a..accb46d65e0f 100644 --- a/talpid-time/Cargo.toml +++ b/talpid-time/Cargo.toml @@ -11,6 +11,8 @@ rust-version.workspace = true workspace = true [features] +# This feature should only be enabled for testing (in dev-dependencies). When enabled, timers will +# use the standard tokio `Instant`, making it possible to advance/pause timers as expected in tests. test = [] [dependencies] From f0cca903791ece22918b6f88e3d1638068f4766d Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Mon, 22 Apr 2024 09:57:35 +0200 Subject: [PATCH 153/214] Update android editorconfig to the latest from ktfmt repository --- android/.editorconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/.editorconfig b/android/.editorconfig index 1476e405bac3..200237231fb8 100644 --- a/android/.editorconfig +++ b/android/.editorconfig @@ -34,10 +34,11 @@ ij_kotlin_continuation_indent_in_supertype_lists = false ij_kotlin_else_on_new_line = false ij_kotlin_enum_constants_wrap = off ij_kotlin_extends_list_wrap = normal -ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_field_annotation_wrap = off ij_kotlin_finally_on_new_line = false ij_kotlin_if_rparen_on_new_line = false ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = * ij_kotlin_insert_whitespaces_in_simple_one_line_method = true ij_kotlin_keep_blank_lines_before_right_brace = 2 ij_kotlin_keep_blank_lines_in_code = 2 From 6dbfc10527270284615a1a893519e9da649f16bc Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Tue, 23 Apr 2024 16:26:45 +0200 Subject: [PATCH 154/214] Revert "Remove support for iPad layout" This reverts commit 25fb6959b91fce23d1eba877fa1763553046f745. --- ios/MullvadVPN.xcodeproj/project.pbxproj | 190 ++++++--------------- ios/MullvadVPN/Supporting Files/Info.plist | 7 + 2 files changed, 57 insertions(+), 140 deletions(-) diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 80f5499f8b54..f178e1cdcd7f 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -5921,12 +5921,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -5962,12 +5961,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6009,7 +6007,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -6049,7 +6047,7 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -6070,7 +6068,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -6091,7 +6089,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -6107,11 +6105,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -6129,11 +6125,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -6150,10 +6144,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6170,10 +6161,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6209,7 +6197,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6247,7 +6235,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6286,7 +6274,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6325,7 +6313,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6348,7 +6336,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -6369,7 +6357,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -6508,11 +6496,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6532,10 +6517,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6554,11 +6536,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -6577,10 +6556,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -6597,10 +6573,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Debug; @@ -6618,10 +6591,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Release; @@ -6652,12 +6622,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -6688,12 +6657,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6724,12 +6692,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -6760,12 +6727,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6801,7 +6767,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Debug; @@ -6837,7 +6803,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Release; @@ -6883,11 +6849,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -6903,11 +6867,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -6950,7 +6912,6 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -6992,7 +6953,6 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -7020,7 +6980,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -7046,7 +7006,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -7084,13 +7044,11 @@ PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "MullvadVPN app integration tests"; SECURITY_GROUP_IDENTIFIER = group.net.mullvad.MullvadVPN; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG MULLVAD_ENVIRONMENT_PRODUCTION $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = MullvadVPN; }; name = Debug; @@ -7118,12 +7076,10 @@ PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "MullvadVPN app integration tests"; SECURITY_GROUP_IDENTIFIER = group.net.mullvad.MullvadVPN; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = MullvadVPN; }; name = Release; @@ -7209,11 +7165,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7250,11 +7203,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7271,10 +7221,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = Staging; }; @@ -7291,10 +7238,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Staging; @@ -7325,12 +7269,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7347,11 +7290,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Staging; }; @@ -7386,12 +7327,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7410,11 +7350,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Staging; }; @@ -7444,12 +7382,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7485,7 +7422,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = Staging; @@ -7527,7 +7464,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Staging; }; @@ -7548,7 +7485,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Staging; }; @@ -7585,7 +7522,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -7608,7 +7545,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Staging; }; @@ -7644,7 +7581,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -7689,7 +7626,6 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -7717,7 +7653,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Staging; }; @@ -7739,12 +7675,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; PRODUCT_NAME = MullvadVPNUITests; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "MullvadVPN app integration tests"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG MULLVAD_ENVIRONMENT_STAGING"; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = Staging; @@ -7763,12 +7696,9 @@ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release"; PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; PRODUCT_NAME = MullvadVPNUITests; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = MULLVAD_ENVIRONMENT_PRODUCTION; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = MockRelease; @@ -7848,10 +7778,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7885,10 +7812,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7905,10 +7829,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; }; name = MockRelease; }; @@ -7925,10 +7846,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Screenshots"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; TEST_TARGET_NAME = MullvadVPN; }; name = MockRelease; @@ -7959,12 +7877,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).Operations"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -7981,11 +7898,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = MockRelease; }; @@ -8020,12 +7935,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadREST"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -8044,11 +7958,9 @@ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = MockRelease; }; @@ -8078,12 +7990,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadTypes"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -8119,7 +8030,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSION_INFO_PREFIX = ""; }; name = MockRelease; @@ -8160,7 +8071,7 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = MockRelease; }; @@ -8181,7 +8092,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = MockRelease; }; @@ -8218,7 +8129,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -8241,7 +8152,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = MockRelease; }; @@ -8277,7 +8188,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -8319,7 +8230,6 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -8347,7 +8257,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = MockRelease; }; diff --git a/ios/MullvadVPN/Supporting Files/Info.plist b/ios/MullvadVPN/Supporting Files/Info.plist index 8ea1a5074af3..b6b1ffa0031f 100644 --- a/ios/MullvadVPN/Supporting Files/Info.plist +++ b/ios/MullvadVPN/Supporting Files/Info.plist @@ -111,5 +111,12 @@ UIInterfaceOrientationPortrait + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + From d565a02e3d28bdfc0a277a17bcf97b026d1be9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Thu, 11 Apr 2024 16:33:14 +0200 Subject: [PATCH 155/214] Remove unused goruntime-boottime-over-monotonic.diff --- .../goruntime-boottime-over-monotonic.diff | 171 ------------------ 1 file changed, 171 deletions(-) delete mode 100644 wireguard/libwg/goruntime-boottime-over-monotonic.diff diff --git a/wireguard/libwg/goruntime-boottime-over-monotonic.diff b/wireguard/libwg/goruntime-boottime-over-monotonic.diff deleted file mode 100644 index 5d78242b139e..000000000000 --- a/wireguard/libwg/goruntime-boottime-over-monotonic.diff +++ /dev/null @@ -1,171 +0,0 @@ -From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001 -From: "Jason A. Donenfeld" -Date: Tue, 21 Mar 2023 15:33:56 +0100 -Subject: [PATCH] [release-branch.go1.20] runtime: use CLOCK_BOOTTIME in - nanotime on Linux - -This makes timers account for having expired while a computer was -asleep, which is quite common on mobile devices. Note that BOOTTIME is -identical to MONOTONIC, except that it takes into account time spent -in suspend. In Linux 4.17, the kernel will actually make MONOTONIC act -like BOOTTIME anyway, so this switch will additionally unify the -timer behavior across kernels. - -BOOTTIME was introduced into Linux 2.6.39-rc1 with 70a08cca1227d in -2011. - -Fixes #24595 - -Change-Id: I7b2a6ca0c5bc5fce57ec0eeafe7b68270b429321 ---- - src/runtime/sys_linux_386.s | 4 ++-- - src/runtime/sys_linux_amd64.s | 2 +- - src/runtime/sys_linux_arm.s | 4 ++-- - src/runtime/sys_linux_arm64.s | 4 ++-- - src/runtime/sys_linux_mips64x.s | 4 ++-- - src/runtime/sys_linux_mipsx.s | 2 +- - src/runtime/sys_linux_ppc64x.s | 2 +- - src/runtime/sys_linux_s390x.s | 2 +- - 8 files changed, 12 insertions(+), 12 deletions(-) - -diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s -index 12a294153d..17e3524b40 100644 ---- a/src/runtime/sys_linux_386.s -+++ b/src/runtime/sys_linux_386.s -@@ -352,13 +352,13 @@ noswitch: - - LEAL 8(SP), BX // &ts (struct timespec) - MOVL BX, 4(SP) -- MOVL $1, 0(SP) // CLOCK_MONOTONIC -+ MOVL $7, 0(SP) // CLOCK_BOOTTIME - CALL AX - JMP finish - - fallback: - MOVL $SYS_clock_gettime, AX -- MOVL $1, BX // CLOCK_MONOTONIC -+ MOVL $7, BX // CLOCK_BOOTTIME - LEAL 8(SP), CX - INVOKE_SYSCALL - -diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s -index c7a89ba536..01f0a6a26e 100644 ---- a/src/runtime/sys_linux_amd64.s -+++ b/src/runtime/sys_linux_amd64.s -@@ -255,7 +255,7 @@ noswitch: - SUBQ $16, SP // Space for results - ANDQ $~15, SP // Align for C code - -- MOVL $1, DI // CLOCK_MONOTONIC -+ MOVL $7, DI // CLOCK_BOOTTIME - LEAQ 0(SP), SI - MOVQ runtime·vdsoClockgettimeSym(SB), AX - CMPQ AX, $0 -diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s -index 7b8c4f0e04..9798a1334e 100644 ---- a/src/runtime/sys_linux_arm.s -+++ b/src/runtime/sys_linux_arm.s -@@ -11,7 +11,7 @@ - #include "textflag.h" - - #define CLOCK_REALTIME 0 --#define CLOCK_MONOTONIC 1 -+#define CLOCK_BOOTTIME 7 - - // for EABI, as we don't support OABI - #define SYS_BASE 0x0 -@@ -374,7 +374,7 @@ finish: - - // func nanotime1() int64 - TEXT runtime·nanotime1(SB),NOSPLIT,$12-8 -- MOVW $CLOCK_MONOTONIC, R0 -+ MOVW $CLOCK_BOOTTIME, R0 - MOVW $spec-12(SP), R1 // timespec - - MOVW runtime·vdsoClockgettimeSym(SB), R4 -diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s -index 38ff6ac330..6b819c5441 100644 ---- a/src/runtime/sys_linux_arm64.s -+++ b/src/runtime/sys_linux_arm64.s -@@ -14,7 +14,7 @@ - #define AT_FDCWD -100 - - #define CLOCK_REALTIME 0 --#define CLOCK_MONOTONIC 1 -+#define CLOCK_BOOTTIME 7 - - #define SYS_exit 93 - #define SYS_read 63 -@@ -338,7 +338,7 @@ noswitch: - BIC $15, R1 - MOVD R1, RSP - -- MOVW $CLOCK_MONOTONIC, R0 -+ MOVW $CLOCK_BOOTTIME, R0 - MOVD runtime·vdsoClockgettimeSym(SB), R2 - CBZ R2, fallback - -diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s -index 47f2da524d..a8b387f193 100644 ---- a/src/runtime/sys_linux_mips64x.s -+++ b/src/runtime/sys_linux_mips64x.s -@@ -326,7 +326,7 @@ noswitch: - AND $~15, R1 // Align for C code - MOVV R1, R29 - -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVV $0(R29), R5 - - MOVV runtime·vdsoClockgettimeSym(SB), R25 -@@ -336,7 +336,7 @@ noswitch: - // see walltime for detail - BEQ R2, R0, finish - MOVV R0, runtime·vdsoClockgettimeSym(SB) -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVV $0(R29), R5 - JMP fallback - -diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s -index 5e6b6c1504..7f5fd2a80e 100644 ---- a/src/runtime/sys_linux_mipsx.s -+++ b/src/runtime/sys_linux_mipsx.s -@@ -243,7 +243,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$8-12 - RET - - TEXT runtime·nanotime1(SB),NOSPLIT,$8-8 -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVW $4(R29), R5 - MOVW $SYS_clock_gettime, R2 - SYSCALL -diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s -index d0427a4807..05ee9fede9 100644 ---- a/src/runtime/sys_linux_ppc64x.s -+++ b/src/runtime/sys_linux_ppc64x.s -@@ -298,7 +298,7 @@ fallback: - JMP return - - TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 -- MOVD $1, R3 // CLOCK_MONOTONIC -+ MOVD $7, R3 // CLOCK_BOOTTIME - - MOVD R1, R15 // R15 is unchanged by C code - MOVD g_m(g), R21 // R21 = m -diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s -index 1448670b91..7d2ee3231c 100644 ---- a/src/runtime/sys_linux_s390x.s -+++ b/src/runtime/sys_linux_s390x.s -@@ -296,7 +296,7 @@ fallback: - RET - - TEXT runtime·nanotime1(SB),NOSPLIT,$32-8 -- MOVW $1, R2 // CLOCK_MONOTONIC -+ MOVW $7, R2 // CLOCK_BOOTTIME - - MOVD R15, R7 // Backup stack pointer - --- -2.17.1 - From 7b4c3153f282ef58b3cc6a998b41d3c90b2b2df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Wed, 24 Apr 2024 13:26:05 +0200 Subject: [PATCH 156/214] Upgrade golang.org/x/net, golang.org/x/crypto and golang.org/x/sys Fixes issues GO-2023-2402, GO-2022-0969, GO-2023-1495, GO-2023-1571, GO-2023-1988, GO-2023-2102 and GO-2024-2687. None of these issues affected our app (they were all about HTTP/2 or other parts of golang that wireguard-go don't use) --- wireguard/libwg/go.mod | 6 +++--- wireguard/libwg/go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/wireguard/libwg/go.mod b/wireguard/libwg/go.mod index 248eff4aa635..2f65c7bf7b3a 100644 --- a/wireguard/libwg/go.mod +++ b/wireguard/libwg/go.mod @@ -3,12 +3,12 @@ module github.com/mullvad/mullvadvpn-app/wireguard/libwg go 1.21 require ( - golang.org/x/sys v0.6.0 + golang.org/x/sys v0.19.0 golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 ) require ( - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect ) diff --git a/wireguard/libwg/go.sum b/wireguard/libwg/go.sum index d129c587a59a..c90f293b177f 100644 --- a/wireguard/libwg/go.sum +++ b/wireguard/libwg/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced h1:3dYNDff0VT5xj+mbj2XucFst9WKk6PdGOrb9n+SbIvw= -golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU= From 9d2a3170902fd2aa884c9b16d63dfa5654811640 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 23 Apr 2024 08:44:37 +0200 Subject: [PATCH 157/214] Update electron-builder --- CHANGELOG.md | 4 + gui/package-lock.json | 15531 +++++++++++++++++++--------------------- gui/package.json | 2 +- 3 files changed, 7369 insertions(+), 8168 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76dd17e16c30..eb13636d8260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,10 @@ Line wrap the file at 100 chars. Th - Flush states on tunnel state changes. Previously, pre-existing connections could leak when internet sharing was enabled on a device. +#### Windows +- Update electron-builder to 24.13.3 to fix CVE-2024-27303, which enabled privilege escelation when + running the installer. + ## [2024.2-beta1] - 2024-04-15 ### Added diff --git a/gui/package-lock.json b/gui/package-lock.json index f024a68be975..036ed8159fc5 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -50,9 +50,8 @@ "chai-spies": "^1.0.0", "cross-env": "^7.0.3", "electron": "^28.1.3", - "electron-builder": "^23.6.0", + "electron-builder": "^24.13.3", "electron-devtools-installer": "^3.2.0", - "electron-mocha": "^11.0.2", "eslint": "^8.36.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.32.2", @@ -172,6 +171,23 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/@electron/asar": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.9.tgz", + "integrity": "sha512-Vu2P3X2gcZ3MY9W7yH72X9+AMXwUQZEJBrsPIbX0JsdllLtoh62/Q8Wg370/DawIEVKOyfD6KtTLo645ezqxUA==", + "dev": true, + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/@electron/get": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", @@ -249,9 +265,9 @@ } }, "node_modules/@electron/notarize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.1.0.tgz", - "integrity": "sha512-Q02xem1D0sg4v437xHgmBLxI2iz/fc0D4K7fiVWHa/AnW8o7D751xyKNXgziA6HrTOme9ul1JfWN5ark8WH1xA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", "dev": true, "dependencies": { "debug": "^4.1.1", @@ -309,16 +325,72 @@ "node": ">= 10.0.0" } }, + "node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/@electron/universal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", - "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", "dev": true, "dependencies": { + "@electron/asar": "^3.2.1", "@malept/cross-spawn-promise": "^1.1.0", - "asar": "^3.1.0", "debug": "^4.3.1", - "dir-compare": "^2.4.0", + "dir-compare": "^3.0.0", "fs-extra": "^9.0.1", "minimatch": "^3.0.4", "plist": "^3.0.4" @@ -366,9 +438,9 @@ "dev": true }, "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -456,18 +528,6 @@ } } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/eslintrc/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -627,18 +687,6 @@ } } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/config-array/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -664,6 +712,102 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -788,9 +932,9 @@ "dev": true }, "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -969,6 +1113,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@playwright/test": { "version": "1.41.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.1.tgz", @@ -1167,9 +1321,9 @@ } }, "node_modules/@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "dependencies": { "@types/ms": "*" @@ -1271,9 +1425,9 @@ "dev": true }, "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", "dev": true }, "node_modules/@types/node": { @@ -1294,9 +1448,9 @@ "dev": true }, "node_modules/@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, "optional": true, "dependencies": { @@ -1405,27 +1559,12 @@ } }, "node_modules/@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", + "version": "1.10.10", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", + "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", "dev": true, "optional": true }, - "node_modules/@types/yargs": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", - "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, "node_modules/@types/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", @@ -1716,16 +1855,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } }, "node_modules/7zip-bin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", - "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true }, "node_modules/abbrev": { @@ -1875,6 +2017,20 @@ "node": ">=0.10.0" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", @@ -1913,40 +2069,54 @@ "dev": true }, "node_modules/app-builder-lib": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-23.6.0.tgz", - "integrity": "sha512-dQYDuqm/rmy8GSCE6Xl/3ShJg6Ab4bZJMT8KaTKGzT436gl1DN4REP3FCWfXoh75qGTJ+u+WsdnnpO9Jl8nyMA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", "dev": true, "dependencies": { "@develar/schema-utils": "~2.6.5", - "@electron/universal": "1.2.1", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", "@malept/flatpak-bundler": "^0.4.0", - "7zip-bin": "~5.1.1", + "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.4", - "ejs": "^3.1.7", - "electron-osx-sign": "^0.6.0", - "electron-publish": "23.6.0", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", - "isbinaryfile": "^4.0.10", + "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", - "minimatch": "^3.1.2", - "read-config-file": "6.2.0", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", "sanitize-filename": "^1.6.3", - "semver": "^7.3.7", - "tar": "^6.1.11", + "semver": "^7.3.8", + "tar": "^6.1.12", "temp-file": "^3.4.0" }, "engines": { "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "24.13.3", + "electron-builder-squirrel-windows": "24.13.3" + } + }, + "node_modules/app-builder-lib/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/app-builder-lib/node_modules/debug": { @@ -1966,20 +2136,6 @@ } } }, - "node_modules/app-builder-lib/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/app-builder-lib/node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -1993,15 +2149,15 @@ } }, "node_modules/app-builder-lib/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" } }, "node_modules/app-builder-lib/node_modules/ms": { @@ -2010,15 +2166,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/app-builder-lib/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/append-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", @@ -2037,24 +2184,80 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "devOptional": true }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } }, - "node_modules/are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "dev": true, + "peer": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/arg": { - "version": "4.1.3", + "node_modules/archiver/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/arg": { + "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true @@ -2289,37 +2492,6 @@ "get-intrinsic": "^1.1.3" } }, - "node_modules/asar": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", - "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==", - "deprecated": "Please use @electron/asar moving forward. There is no API change, just a package name change", - "dev": true, - "dependencies": { - "chromium-pickle-js": "^0.2.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" - }, - "engines": { - "node": ">=10.12.0" - }, - "optionalDependencies": { - "@types/glob": "^7.1.1" - } - }, - "node_modules/asar/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -2402,9 +2574,9 @@ } }, "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "node_modules/async-done": { @@ -2896,22 +3068,6 @@ "ieee754": "^1.1.4" } }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -2930,12 +3086,6 @@ "node": ">=0.4.0" } }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "dev": true - }, "node_modules/buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -2949,23 +3099,22 @@ "dev": true }, "node_modules/builder-util": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-23.6.0.tgz", - "integrity": "sha512-QiQHweYsh8o+U/KNCZFSvISRnvRctb8m/2rB2I1JdByzvNKxPeFLlHFRPQRXab6aYeXc18j9LpsDLJ3sGQmWTQ==", + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", "dev": true, "dependencies": { "@types/debug": "^4.1.6", - "@types/fs-extra": "^9.0.11", - "7zip-bin": "~5.1.1", + "7zip-bin": "~5.2.0", "app-builder-bin": "4.0.0", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", - "fs-extra": "^10.0.0", + "fs-extra": "^10.1.0", "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", "source-map-support": "^0.5.19", @@ -2974,9 +3123,9 @@ } }, "node_modules/builder-util-runtime": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.1.1.tgz", - "integrity": "sha512-azRhYLEoDvRDR8Dhis4JatELC/jUvYjm4cVSj7n9dauGTOM2eeNn9KS0z6YA6oDsjI1xphjNbY6PZZeHPzzqaw==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", "dev": true, "dependencies": { "debug": "^4.3.4", @@ -3009,55 +3158,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/builder-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/builder-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/builder-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/builder-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/builder-util/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3081,18 +3181,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/builder-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -3227,6 +3315,34 @@ "node": ">= 4.0.0" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -3274,9 +3390,9 @@ "dev": true }, "node_modules/ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -3474,6 +3590,22 @@ "node": ">=0.10.0" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -3483,15 +3615,6 @@ "color-support": "bin.js" } }, - "node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combine-source-map": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", @@ -3522,6 +3645,15 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -3537,6 +3669,37 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3558,6 +3721,71 @@ "typedarray": "^0.0.6" } }, + "node_modules/config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "dependencies": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + } + }, + "node_modules/config-file-ts/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", @@ -3637,18 +3865,60 @@ "buffer": "^5.1.0" } }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "dev": true, + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "peer": true, "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" } }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.11.9", + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true @@ -4174,30 +4444,13 @@ "dev": true }, "node_modules/dir-compare": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz", - "integrity": "sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==", - "dev": true, - "dependencies": { - "buffer-equal": "1.0.0", - "colors": "1.0.3", - "commander": "2.9.0", - "minimatch": "3.0.4" - }, - "bin": { - "dircompare": "src/cli/dircompare.js" - } - }, - "node_modules/dir-compare/node_modules/commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", "dev": true, "dependencies": { - "graceful-readlink": ">= 1.0.0" - }, - "engines": { - "node": ">= 0.6.x" + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" } }, "node_modules/dir-glob": { @@ -4222,15 +4475,15 @@ } }, "node_modules/dmg-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-23.6.0.tgz", - "integrity": "sha512-jFZvY1JohyHarIAlTbfQOk+HnceGjjAdFjVn3n8xlDWKsYNqbO4muca6qXEZTfGXeQMG7TYim6CeS5XKSfSsGA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", "dev": true, "dependencies": { - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "fs-extra": "^10.0.0", + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, @@ -4338,10 +4591,16 @@ "object.defaults": "^1.1.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "dependencies": { "jake": "^10.8.5" @@ -4372,23 +4631,22 @@ } }, "node_modules/electron-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-23.6.0.tgz", - "integrity": "sha512-y8D4zO+HXGCNxFBV/JlyhFnoQ0Y0K7/sFH+XwIbj47pqaW8S6PGYQbjoObolKBR1ddQFPt4rwp4CnwMJrW3HAw==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", "dev": true, "dependencies": { - "@types/yargs": "^17.0.1", - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "dmg-builder": "23.6.0", - "fs-extra": "^10.0.0", + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", - "read-config-file": "6.2.0", - "simple-update-notifier": "^1.0.7", - "yargs": "^17.5.1" + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", @@ -4398,65 +4656,17 @@ "node": ">=14.0.0" } }, - "node_modules/electron-builder/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/electron-builder/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/electron-builder/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/electron-builder/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/electron-builder/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", "dev": true, + "peer": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" } }, "node_modules/electron-devtools-installer": { @@ -4477,202 +4687,240 @@ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true }, - "node_modules/electron-mocha": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/electron-mocha/-/electron-mocha-11.0.2.tgz", - "integrity": "sha512-fOk+zUgSIsmL2cuIrd7IlK4eRhGVi1PYIB3QvqiBO+6f6AP8XLkYkT9eORlL2xwaS3yAAk02Y+4OTuhtqHPkEQ==", + "node_modules/electron-publish": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1", - "electron-window": "^0.8.0", - "fs-extra": "^10.0.0", - "mocha": "^9.1.1", - "which": "^2.0.2", - "yargs": "^16.2.0" - }, - "bin": { - "electron-mocha": "bin/electron-mocha" - }, - "engines": { - "node": ">= 7.0.0" + "@types/fs-extra": "^9.0.11", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" } }, - "node_modules/electron-mocha/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "engines": { "node": ">=6" } }, - "node_modules/electron-mocha/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "is-arrayish": "^0.2.1" } }, - "node_modules/electron-mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/electron-mocha/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/electron-mocha/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "has": "^1.0.3" } }, - "node_modules/electron-mocha/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/electron-mocha/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" } }, - "node_modules/electron-mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } + "optional": true }, - "node_modules/electron-mocha/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, - "node_modules/electron-mocha/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/electron-mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "d": "^1.0.1", + "ext": "^1.1.2" } }, - "node_modules/electron-mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/electron-mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, - "engines": { - "node": ">=0.3.1" + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" } }, - "node_modules/electron-mocha/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/electron-mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { "node": ">=10" }, @@ -4680,1921 +4928,1967 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/electron-mocha/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/electron-mocha/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/electron-mocha/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/eslint": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.1", + "@eslint/js": "8.36.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.5.0", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/electron-mocha/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/electron-mocha/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/electron-mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "node_modules/eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" }, "engines": { - "node": ">=10" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/electron-mocha/node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", "dev": true, "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 12.0.0" + "resolve": "bin/resolve" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/electron-mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/electron-mocha/node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "semver": "bin/semver.js" } }, - "node_modules/electron-mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/eslint-plugin-simple-import-sort": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz", + "integrity": "sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==", "dev": true, - "engines": { - "node": ">=8" + "peerDependencies": { + "eslint": ">=5.0.0" } }, - "node_modules/electron-mocha/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=8.10.0" + "node": ">=8.0.0" } }, - "node_modules/electron-mocha/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/electron-mocha/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/electron-mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/electron-mocha/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "ms": "2.1.2" }, "engines": { - "node": ">=8.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/electron-mocha/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/eslint/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "esutils": "^2.0.2" }, "engines": { - "node": ">= 8" + "node": ">=6.0.0" } }, - "node_modules/electron-mocha/node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true - }, - "node_modules/electron-mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/electron-mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/electron-mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=10" + "node": ">=10.13.0" } }, - "node_modules/electron-osx-sign": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz", - "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==", - "deprecated": "Please use @electron/osx-sign moving forward. Be aware the API is slightly different", + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/eslint/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "dependencies": { - "bluebird": "^3.5.0", - "compare-version": "^0.1.2", - "debug": "^2.6.8", - "isbinaryfile": "^3.0.2", - "minimist": "^1.2.0", - "plist": "^3.0.1" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/electron-osx-sign/node_modules/isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "buffer-alloc": "^1.2.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.6.0" + "node": ">=8" } }, - "node_modules/electron-publish": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-23.6.0.tgz", - "integrity": "sha512-jPj3y+eIZQJF/+t5SLvsI5eS4mazCbNYqatv5JihbqOstIM13k0d1Z3vAWntvtt13Itl61SO6seicWdioOU5dg==", + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "dependencies": { - "@types/fs-extra": "^9.0.11", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "fs-extra": "^10.0.0", - "lazy-val": "^1.0.5", - "mime": "^2.5.2" + "engines": { + "node": ">=8" } }, - "node_modules/electron-publish/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/espree": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/electron-publish/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/espree/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=0.4.0" } }, - "node_modules/electron-publish/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10" } }, - "node_modules/electron-publish/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/electron-publish/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/electron-window": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/electron-window/-/electron-window-0.8.1.tgz", - "integrity": "sha1-FsoYfrSHCwZ5J0/IKZxZYOarLF4=", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "dependencies": { - "is-electron-renderer": "^2.0.0" + "engines": { + "node": ">=4.0" } }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, "dependencies": { - "iconv-lite": "^0.6.2" + "d": "1", + "es5-ext": "~0.10.14" } }, - "node_modules/end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "dependencies": { - "once": "^1.4.0" + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "engines": { "node": ">=6" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "dependencies": { - "is-arrayish": "^0.2.1" + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" } }, - "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "is-descriptor": "^0.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "dependencies": { - "has": "^1.0.3" + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "optional": true - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "node_modules/ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", "dev": true, "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "type": "^2.0.0" } }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "node_modules/ext/node_modules/type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "is-plain-object": "^2.0.4" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "is-descriptor": "^1.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" + "kind-of": "^6.0.0" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "kind-of": "^6.0.0" }, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-simple-import-sort": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz", - "integrity": "sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==", - "dev": true, - "peerDependencies": { - "eslint": ">=5.0.0" + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/extract-zip/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "ms": "2.1.2" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } + "node_modules/extract-zip/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", "dev": true, - "engines": { - "node": ">=8" - } + "engines": [ + "node >=0.6.0" + ], + "optional": true }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=10" + "node": ">=8.6.0" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/fast-glob/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/fast-glob/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/eslint/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=6.0.0" + "node": ">= 6" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "node_modules/fast-glob/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=0.12.0" } }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/fast-glob/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.6" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/fast-glob/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "is-number": "^7.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=8.0" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "reusify": "^1.0.4" } }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/eslint/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "pend": "~1.2.0" } }, - "node_modules/eslint/node_modules/strip-ansi": { + "node_modules/file-entry-cache": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "flat-cache": "^3.0.4" }, "engines": { - "node": ">=8" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/eslint/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "dev": true, - "engines": { - "node": ">=8" - } + "optional": true }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "minimatch": "^5.0.1" } }, - "node_modules/espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "balanced-match": "^1.0.0" } }, - "node_modules/espree/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.4.0" + "node": ">=10" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "engines": { - "node": ">=0.10" + "node": ">=0.10.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "engines": { + "node": ">= 0.10" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, "engines": { - "node": ">=0.8.x" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" } }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, + "is-callable": "^1.1.3" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" + "for-in": "^1.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "dependencies": { - "homedir-polyfill": "^1.0.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "dependencies": { - "type": "^2.0.0" + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", "dev": true, "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" } }, - "node_modules/extend-shallow/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "dependencies": { - "is-plain-object": "^2.0.4" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "devOptional": true, "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "minipass": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/extglob/node_modules/define-property": { + "node_modules/fs-mkdirp-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", "dev": true, "dependencies": { - "is-descriptor": "^1.0.0" + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "devOptional": true + }, + "node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "is-extendable": "^0.1.0" + "bindings": "^1.5.0", + "nan": "^2.12.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 4.0" } }, - "node_modules/extglob/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", "dev": true, "dependencies": { - "kind-of": "^6.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extglob/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", "dev": true, "dependencies": { - "kind-of": "^6.0.0" - }, + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" + "pump": "^3.0.0" }, "engines": { - "node": ">= 10.17.0" + "node": ">=8" }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extract-zip/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "dependencies": { - "ms": "2.1.2" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, "engines": { - "node": ">=6.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extract-zip/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true, - "engines": [ - "node >=0.6.0" - ], - "optional": true + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "node_modules/gettext-extractor": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/gettext-extractor/-/gettext-extractor-3.5.4.tgz", + "integrity": "sha512-iK4tSnteSw+pFMts43OP8hUnsOklbkxz3ytWqru7dPf8Ec3uzTYv1aw70ojAvKItmofpj1ibfY7sZWsdSN6zIw==", "dev": true, "dependencies": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" + "@types/glob": "5 - 7", + "@types/parse5": "^5", + "css-selector-parser": "^1.3", + "glob": "5 - 7", + "parse5": "5 - 6", + "pofile": "1.0.x", + "typescript": "2 - 4" }, "engines": { - "node": ">= 0.10" + "node": ">=6" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "node_modules/gettext-extractor/node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "dev": true }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "node_modules/gettext-extractor/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=8.6.0" + "node": ">=4.2.0" } }, - "node_modules/fast-glob/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "node_modules/gettext-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-6.0.0.tgz", + "integrity": "sha512-eWFsR78gc/eKnzDgc919Us3cbxQbzxK1L8vAIZrKMQqOUgULyeqmczNlBjTlVTk2FaB7nV9C1oobd/PGBOqNmg==", "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" + "content-type": "^1.0.4", + "encoding": "^0.1.13", + "readable-stream": "^4.1.0", + "safe-buffer": "^5.2.1" } }, - "node_modules/fast-glob/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "node_modules/gettext-parser/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "to-regex-range": "^5.0.1" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/gettext-parser/node_modules/readable-stream": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz", + "integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "node_modules/gettext-parser/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "devOptional": true, "dependencies": { - "is-glob": "^4.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">= 6" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fast-glob/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, - "engines": { - "node": ">=0.12.0" + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" } }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "is-extglob": "^2.1.0" }, "engines": { - "node": ">=8.6" + "node": ">=0.10.0" } }, - "node_modules/fast-glob/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" }, "engines": { - "node": ">=8.0" + "node": ">= 0.10" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", "dev": true, "dependencies": { - "reusify": "^1.0.4" + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, + "optional": true, "dependencies": { - "pend": "~1.2.0" + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=0.10.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "define-properties": "^1.1.3" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "sparkles": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "node_modules/google-protobuf": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.0.tgz", + "integrity": "sha512-byR7MBTK4tZ5PZEb+u5ZTzpt4SfrTxv5682MjPlHN16XeqgZE2/8HOIWeiXe8JKnT9OVbtBGhbq8mtvkK8cd5g==" + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "get-intrinsic": "^1.1.3" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/grpc_tools_node_protoc_ts": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-5.3.2.tgz", + "integrity": "sha512-7xPSeu8bwjcird3i9R5+9O4BF2Lhv9fMBdeobfUc2Bys9tSVtm/VB3WjTpKV78WlLYJyD94+wL/8hJqaMZ53Hw==", "dev": true, "dependencies": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "google-protobuf": "3.15.8", + "handlebars": "4.7.7" }, - "engines": { - "node": ">= 0.10" + "bin": { + "protoc-gen-ts": "bin/protoc-gen-ts" } }, - "node_modules/flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true, - "engines": { - "node": ">= 0.10" - } + "node_modules/grpc_tools_node_protoc_ts/node_modules/google-protobuf": { + "version": "3.15.8", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.8.tgz", + "integrity": "sha512-2jtfdqTaSxk0cuBJBtTTWsot4WtR9RVr2rXg7x7OoqiuOKopPrwXpM1G4dXIkLcUNRh3RKzz76C8IOkksZSeOw==", + "dev": true }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, + "node_modules/grpc-tools": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.12.4.tgz", + "integrity": "sha512-5+mLAJJma3BjnW/KQp6JBjUMgvu7Mu3dBvBPd1dcbNIb+qiR0817zDpgPjS7gRb+l/8EVNIa3cB02xI9JLToKg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.5" + }, "bin": { - "flat": "cli.js" + "grpc_tools_node_protoc": "bin/protoc.js", + "grpc_tools_node_protoc_plugin": "bin/protoc_plugin.js" } }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "bin": { + "gulp": "bin/gulp.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.10" } }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "node_modules/gulp-inject-string": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/gulp-inject-string/-/gulp-inject-string-1.1.2.tgz", + "integrity": "sha512-+jhEyG+cEqvMdJgxD+7WkO/hDXz7AQl5aP9Rp+f23QaUDi5xme2YNvUjxCTlEySUapn27Pskcq9o8MsBBdvt4g==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "event-stream": "3.3.4", + "plugin-error": "^1.0.1" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/gulp-sourcemaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "node_modules/gulp-sourcemaps/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "node_modules/gulp-sourcemaps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "for-in": "^1.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "node_modules/gulp-typescript": { + "version": "6.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-6.0.0-alpha.1.tgz", + "integrity": "sha512-KoT0TTfjfT7w3JItHkgFH1T/zK4oXWC+a8xxKfniRfVcA0Fa1bKrIhztYelYmb+95RB80OLMBreknYkdwzdi2Q==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "ansi-colors": "^4.1.1", + "plugin-error": "^1.0.1", + "source-map": "^0.7.3", + "through2": "^3.0.1", + "vinyl": "^2.2.0", + "vinyl-fs": "^3.0.3" }, "engines": { - "node": ">= 6" + "node": ">= 8" + }, + "peerDependencies": { + "typescript": "~2.7.1 || >=2.8.0-dev || >=2.9.0-dev || ~3.0.0 || >=3.0.0-dev || >=3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev " } }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/gulp-typescript/node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true + "node_modules/gulp-typescript/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "node_modules/gulp-typescript/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "node_modules/gulp/node_modules/gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" }, "engines": { - "node": ">=12" + "node": ">= 0.10" } }, - "node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/gulp/node_modules/yargs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", + "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", "dev": true, - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "5.0.0-security.0" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "devOptional": true, + "node_modules/gulp/node_modules/yargs-parser": { + "version": "5.0.0-security.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", + "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" } }, - "node_modules/fs-mkdirp-stream": { + "node_modules/gulplog": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "dependencies": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" + "glogg": "^1.0.0" }, "engines": { "node": ">= 0.10" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "devOptional": true - }, - "node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">= 4.0" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "function-bind": "^1.1.1" }, "engines": { - "node": ">= 0.4" - }, + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-assigned-identifiers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", - "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", - "dev": true - }, - "node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-symbol-description": { + "node_modules/has-tostringtag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6603,693 +6897,540 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "devOptional": true + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gettext-extractor": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/gettext-extractor/-/gettext-extractor-3.5.4.tgz", - "integrity": "sha512-iK4tSnteSw+pFMts43OP8hUnsOklbkxz3ytWqru7dPf8Ec3uzTYv1aw70ojAvKItmofpj1ibfY7sZWsdSN6zIw==", + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "dependencies": { - "@types/glob": "5 - 7", - "@types/parse5": "^5", - "css-selector-parser": "^1.3", - "glob": "5 - 7", - "parse5": "5 - 6", - "pofile": "1.0.x", - "typescript": "2 - 4" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/gettext-extractor/node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true - }, - "node_modules/gettext-extractor/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "dependencies": { + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/gettext-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-6.0.0.tgz", - "integrity": "sha512-eWFsR78gc/eKnzDgc919Us3cbxQbzxK1L8vAIZrKMQqOUgULyeqmczNlBjTlVTk2FaB7nV9C1oobd/PGBOqNmg==", - "dependencies": { - "content-type": "^1.0.4", - "encoding": "^0.1.13", - "readable-stream": "^4.1.0", - "safe-buffer": "^5.2.1" + "node": ">=0.10.0" } }, - "node_modules/gettext-parser/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/gettext-parser/node_modules/readable-stream": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz", - "integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==", + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/gettext-parser/node_modules/safe-buffer": { + "node_modules/hash-base/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "dev": true }, - "node_modules/gl-matrix": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", - "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "devOptional": true, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } }, - "node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "bin": { + "he": "bin/he" } }, - "node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", - "dev": true, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dependencies": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" - }, - "engines": { - "node": ">= 0.10" + "react-is": "^16.7.0" } }, - "node_modules/glob-watcher": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", - "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "dependencies": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "normalize-path": "^3.0.0", - "object.defaults": "^1.1.0" + "parse-passwd": "^1.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", "dev": true, - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, "engines": { - "node": ">=10.0" + "node": ">=0.10" } }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "ms": "2.1.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.19.0" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "devOptional": true, "dependencies": { - "define-properties": "^1.1.3" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 6" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "devOptional": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "ms": "2.1.2" }, "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/glogg": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "devOptional": true + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", "dev": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "sparkles": "^1.0.0" + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" }, "engines": { - "node": ">= 0.10" + "node": "^8.11.2 || >=10" } }, - "node_modules/google-protobuf": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.0.tgz", - "integrity": "sha512-byR7MBTK4tZ5PZEb+u5ZTzpt4SfrTxv5682MjPlHN16XeqgZE2/8HOIWeiXe8JKnT9OVbtBGhbq8mtvkK8cd5g==" - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { - "get-intrinsic": "^1.1.3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=10.19.0" + "node": ">=6" }, "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "node_modules/graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true, "engines": { - "node": ">=4.x" + "node": ">=0.8.19" } }, - "node_modules/grpc_tools_node_protoc_ts": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-5.3.2.tgz", - "integrity": "sha512-7xPSeu8bwjcird3i9R5+9O4BF2Lhv9fMBdeobfUc2Bys9tSVtm/VB3WjTpKV78WlLYJyD94+wL/8hJqaMZ53Hw==", - "dev": true, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "devOptional": true, "dependencies": { - "google-protobuf": "3.15.8", - "handlebars": "4.7.7" - }, - "bin": { - "protoc-gen-ts": "bin/protoc-gen-ts" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/grpc_tools_node_protoc_ts/node_modules/google-protobuf": { - "version": "3.15.8", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.8.tgz", - "integrity": "sha512-2jtfdqTaSxk0cuBJBtTTWsot4WtR9RVr2rXg7x7OoqiuOKopPrwXpM1G4dXIkLcUNRh3RKzz76C8IOkksZSeOw==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "devOptional": true + }, + "node_modules/ini": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", "dev": true }, - "node_modules/grpc-tools": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.12.4.tgz", - "integrity": "sha512-5+mLAJJma3BjnW/KQp6JBjUMgvu7Mu3dBvBPd1dcbNIb+qiR0817zDpgPjS7gRb+l/8EVNIa3cB02xI9JLToKg==", - "hasInstallScript": true, - "optional": true, + "node_modules/inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.5" - }, - "bin": { - "grpc_tools_node_protoc": "bin/protoc.js", - "grpc_tools_node_protoc_plugin": "bin/protoc_plugin.js" + "source-map": "~0.5.3" } }, - "node_modules/gulp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "node_modules/insert-module-globals": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", "dev": true, "dependencies": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" }, "bin": { - "gulp": "bin/gulp.js" - }, - "engines": { - "node": ">= 0.10" + "insert-module-globals": "bin/cmd.js" } }, - "node_modules/gulp-inject-string": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/gulp-inject-string/-/gulp-inject-string-1.1.2.tgz", - "integrity": "sha512-+jhEyG+cEqvMdJgxD+7WkO/hDXz7AQl5aP9Rp+f23QaUDi5xme2YNvUjxCTlEySUapn27Pskcq9o8MsBBdvt4g==", + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "event-stream": "3.3.4", - "plugin-error": "^1.0.1" + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/gulp-sourcemaps": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", - "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true, - "dependencies": { - "@gulp-sourcemaps/identity-map": "^2.0.1", - "@gulp-sourcemaps/map-sources": "^1.0.0", - "acorn": "^6.4.1", - "convert-source-map": "^1.0.0", - "css": "^3.0.0", - "debug-fabulous": "^1.0.0", - "detect-newline": "^2.0.0", - "graceful-fs": "^4.0.0", - "source-map": "^0.6.0", - "strip-bom-string": "^1.0.0", - "through2": "^2.0.0" - }, "engines": { - "node": ">= 6" + "node": ">= 0.10" } }, - "node_modules/gulp-sourcemaps/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "node_modules/into-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gulp-sourcemaps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-typescript": { - "version": "6.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-6.0.0-alpha.1.tgz", - "integrity": "sha512-KoT0TTfjfT7w3JItHkgFH1T/zK4oXWC+a8xxKfniRfVcA0Fa1bKrIhztYelYmb+95RB80OLMBreknYkdwzdi2Q==", + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1", - "plugin-error": "^1.0.1", - "source-map": "^0.7.3", - "through2": "^3.0.1", - "vinyl": "^2.2.0", - "vinyl-fs": "^3.0.3" + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" }, "engines": { - "node": ">= 8" - }, - "peerDependencies": { - "typescript": "~2.7.1 || >=2.8.0-dev || >=2.9.0-dev || ~3.0.0 || >=3.0.0-dev || >=3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev " + "node": ">=0.10.0" } }, - "node_modules/gulp-typescript/node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/gulp-typescript/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/gulp-typescript/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "node_modules/is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", "dev": true, "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - }, - "node_modules/gulp/node_modules/gulp-cli": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", - "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", - "dev": true, - "dependencies": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.4.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.2.0", - "yargs": "^7.1.0" - }, - "bin": { - "gulp": "bin/gulp.js" + "call-bind": "^1.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.4" } }, - "node_modules/gulp/node_modules/yargs": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", - "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "5.0.0-security.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gulp/node_modules/yargs-parser": { - "version": "5.0.0-security.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", - "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", - "dev": true, - "dependencies": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" - } + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, - "node_modules/gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, "dependencies": { - "glogg": "^1.0.0" + "has-bigints": "^1.0.1" }, - "engines": { - "node": ">= 0.10" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" + "binary-extensions": "^1.0.0" }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -7298,432 +7439,335 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "ci-info": "^3.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "is-ci": "bin.js" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "has": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "devOptional": true - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "kind-of": "^3.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "node_modules/is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/hash-base/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/hash-base/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true, - "bin": { - "he": "bin/he" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" + "node_modules/is-generator-function": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", + "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==", + "dev": true, + "engines": { + "node": ">= 0.4" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { - "parse-passwd": "^1.0.0" + "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/htmlescape": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", "dev": true, "engines": { - "node": ">=0.10" + "node": ">=0.10.0" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "kind-of": "^3.0.2" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=6.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=10.19.0" + "node": ">=0.10.0" } }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "devOptional": true, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "isobject": "^3.0.1" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "devOptional": true, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=6.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true - }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], "dependencies": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" + "is-unc-path": "^1.0.0" }, "engines": { - "node": "^8.11.2 || >=10" + "node": ">=0.10.0" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "devOptional": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "devOptional": true - }, - "node_modules/ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "dev": true - }, - "node_modules/inline-source-map": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", - "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", - "dev": true, - "dependencies": { - "source-map": "~0.5.3" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/insert-module-globals": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", - "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { - "acorn-node": "^1.5.2", - "combine-source-map": "^0.8.0", - "concat-stream": "^1.6.1", - "is-buffer": "^1.1.0", - "JSONStream": "^1.0.3", - "path-is-absolute": "^1.0.1", - "process": "~0.11.0", - "through2": "^2.0.0", - "undeclared-identifiers": "^1.1.2", - "xtend": "^4.0.0" + "has-symbols": "^1.0.2" }, - "bin": { - "insert-module-globals": "bin/cmd.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, + "dependencies": { + "unc-path-regex": "^0.1.2" + }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, "engines": { "node": ">=10" }, @@ -7731,3448 +7775,3356 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/invert-kv": { + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/is-valid-glob": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/isbinaryfile": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", + "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" + "engines": { + "node": ">= 18.0.0" }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "node_modules/js-sdsl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", + "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", "dev": true }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "binary-extensions": "^1.0.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "optional": true }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, "bin": { - "is-ci": "bin.js" + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { - "has": "^1.0.3" + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.0.0" } }, - "node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } + "engines": [ + "node >= 0.2.0" + ] }, - "node_modules/is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, "engines": { - "node": ">= 0.4" + "node": "*" } }, - "node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/jsx-ast-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", + "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "array-includes": "^3.1.1", + "object.assign": "^4.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" } }, - "node_modules/is-electron-renderer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-electron-renderer/-/is-electron-renderer-2.0.1.tgz", - "integrity": "sha1-pGnQVvl1aXxYyYxgI+sKp5r4laI=", + "node_modules/just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", "dev": true }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "json-buffer": "3.0.1" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" + "node_modules/labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" } }, - "node_modules/is-generator-function": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", - "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==", + "node_modules/last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "dev": true, + "dependencies": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + }, "engines": { - "node": ">= 0.4" + "node": ">= 0.10" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true + }, + "node_modules/lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "readable-stream": "^2.0.5" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6.3" } }, - "node_modules/is-negated-glob": { + "node_modules/lcid": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "node_modules/lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "flush-write-stream": "^1.0.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.10" } }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "immediate": "~3.0.5" + } + }, + "node_modules/liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true, - "engines": { - "node": ">=8" - } + "peer": true }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } + "peer": true }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "peer": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", "dev": true }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { - "is-unc-path": "^1.0.0" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "get-func-name": "^2.0.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "devOptional": true, "dependencies": { - "has-symbols": "^1.0.2" + "yallist": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "es5-ext": "~0.10.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "optional": true, + "dependencies": { + "semver": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "dependencies": { - "unc-path-regex": "^0.1.2" + "kind-of": "^6.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", "dev": true }, - "node_modules/is-valid-glob": { + "node_modules/map-visit": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10.0" } }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "node_modules/matchdep/node_modules/findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "dev": true, - "engines": { - "node": ">= 8.0.0" + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" + "engines": { + "node": ">= 0.10" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "node_modules/matchdep/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, + "optional": true, "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" + "escape-string-regexp": "^4.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/memoizee/node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 8" } }, - "node_modules/jake/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/jake/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "bn.js": "^4.0.0", + "brorand": "^1.0.1" }, - "engines": { - "node": ">=8" + "bin": { + "miller-rabin": "bin/miller-rabin" } }, - "node_modules/js-sdsl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", - "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "optional": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, "bin": { - "json5": "lib/cli.js" + "mime": "cli.js" }, "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "node": ">=4.0.0" } }, - "node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "engines": { - "node": ">= 10.0.0" + "node": ">= 0.6" } }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true, - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" + "mime-db": "1.52.0" }, "engines": { - "node": "*" + "node": ">= 0.6" } }, - "node_modules/jsx-ast-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", - "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, - "dependencies": { - "array-includes": "^3.1.1", - "object.assign": "^4.1.1" - }, "engines": { - "node": ">=4.0" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dev": true, - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" + "node": ">=4" } }, - "node_modules/just-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", - "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", "dev": true }, - "node_modules/keyv": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", - "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", - "dev": true, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "devOptional": true, "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/labeled-stream-splicer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", - "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "stream-splicer": "^2.0.0" - } + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, - "node_modules/last-run": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", - "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", - "dev": true, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "devOptional": true, "dependencies": { - "default-resolution": "^2.0.0", - "es6-weak-map": "^2.0.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/lazy-val": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", - "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", - "dev": true - }, - "node_modules/lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "devOptional": true, "dependencies": { - "readable-stream": "^2.0.5" + "minipass": "^3.0.0", + "yallist": "^4.0.0" }, "engines": { - "node": ">= 0.6.3" + "node": ">= 8" } }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "dependencies": { - "invert-kv": "^1.0.0" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/lead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "dependencies": { - "flush-write-stream": "^1.0.2" + "is-plain-object": "^2.0.4" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "minimist": "^1.2.5" }, - "engines": { - "node": ">= 0.8.0" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "dependencies": { - "immediate": "~3.0.5" + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" } }, - "node_modules/liftoff": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", - "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "dependencies": { - "extend": "^3.0.0", - "findup-sync": "^3.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" - }, "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/mocha/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "node_modules/lodash.memoize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "node_modules/mocha/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "balanced-match": "^1.0.0" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/mocha/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "fill-range": "^7.0.1" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=10" + "node": ">= 8.10.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/mocha/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, - "bin": { - "loose-envify": "cli.js" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "node_modules/mocha/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.0" + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "node_modules/mocha/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { - "node": ">=8" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "devOptional": true, + "node_modules/mocha/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "node_modules/mocha/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "optional": true, - "dependencies": { - "semver": "^6.0.0" + "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "optional": true, - "bin": { - "semver": "bin/semver.js" + "node_modules/mocha/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "dependencies": { - "kind-of": "^6.0.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { - "object-visit": "^1.0.0" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.10.0" } }, - "node_modules/matchdep": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", - "stack-trace": "0.0.10" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.10.0" + "node": ">=8" } }, - "node_modules/matchdep/node_modules/findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/matchdep/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "is-extglob": "^2.1.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0" } }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "optional": true, "dependencies": { - "escape-string-regexp": "^4.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "engines": { + "node": ">=10" } }, - "node_modules/memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, - "node_modules/memoizee/node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", "dev": true, "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "module-deps": "bin/cmd.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/multistream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", + "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" + "once": "^1.4.0", + "readable-stream": "^3.6.0" } }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/multistream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, - "bin": { - "mime": "cli.js" + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=4.0.0" + "node": ">= 6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.10" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, - "dependencies": { - "mime-db": "1.52.0" + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">= 0.6" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "devOptional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "devOptional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "node_modules/next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "devOptional": true, + "node_modules/nise": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" } }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", "dev": true, "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "semver": "^5.4.1" } }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/node-abi/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" + "bin": { + "semver": "bin/semver" } }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } + "optional": true }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "node_modules/node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=", "dev": true }, - "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "devOptional": true, "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">= 14.0.0" + "node": "4.x || >=6.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/mocha/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/node-gettext": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", + "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", + "dependencies": { + "lodash.get": "^4.4.2" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/mocha/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "once": "^1.3.2" }, "engines": { - "node": ">= 8" + "node": ">= 0.10" } }, - "node_modules/mocha/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, + "node_modules/nseventmonitor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nseventmonitor/-/nseventmonitor-1.0.4.tgz", + "integrity": "sha512-z4dql4cB2GEzzWgBo9yM10NaHhEg5b0yeSd0niiH2Nde6TUbM6G7EZy5UeSjsaIkkL+LgLgiXXQrNB7ccZ8Wyw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "balanced-match": "^1.0.0" + "@mapbox/node-pre-gyp": "^1.0.11", + "nan": "^2.18.0" } }, - "node_modules/mocha/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "is-descriptor": "^0.1.0" }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">= 0.4" } }, - "node_modules/mocha/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "isobject": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 0.4" } }, - "node_modules/mocha/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, "engines": { - "node": ">=0.12.0" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "isobject": "^3.0.1" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { - "node": ">=8.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "devOptional": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "wrappy": "1" } }, - "node_modules/mocha/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.1" } }, - "node_modules/mocha/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "lcid": "^1.0.0" }, "engines": { - "node": ">=8.0" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "callsites": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/module-deps": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", - "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "node_modules/parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", "dev": true, "dependencies": { - "browser-resolve": "^2.0.0", - "cached-path-relative": "^1.0.2", - "concat-stream": "~1.6.0", - "defined": "^1.0.0", - "detective": "^5.2.0", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "JSONStream": "^1.0.3", - "parents": "^1.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.4.0", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" - }, - "bin": { - "module-deps": "bin/cmd.js" - }, - "engines": { - "node": ">= 0.8.0" + "path-platform": "~0.11.15" } }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } }, - "node_modules/multistream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", - "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "once": "^1.4.0", - "readable-stream": "^3.6.0" + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "node_modules/multistream/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "error-ex": "^1.2.0" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/mute-stdout": { + "node_modules/parse-node-version": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, "engines": { "node": ">= 0.10" } }, - "node_modules/nan": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=0.10.0" } }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "dev": true }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "node_modules/nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "dev": true, - "dependencies": { - "semver": "^5.4.1" + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "devOptional": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/node-abi/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">=8" } }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "dev": true, - "optional": true - }, - "node_modules/node-cleanup": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", - "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "devOptional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, + "node_modules/path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gettext": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", - "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", - "dependencies": { - "lodash.get": "^4.4.2" + "node": ">= 0.8.0" } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/now-and-later": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, - "dependencies": { - "once": "^1.3.2" - }, "engines": { - "node": ">= 0.10" + "node": "14 || >=16.14" } }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/nseventmonitor": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nseventmonitor/-/nseventmonitor-1.0.4.tgz", - "integrity": "sha512-z4dql4cB2GEzzWgBo9yM10NaHhEg5b0yeSd0niiH2Nde6TUbM6G7EZy5UeSjsaIkkL+LgLgiXXQrNB7ccZ8Wyw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "node_modules/path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "nan": "^2.18.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "devOptional": true, - "engines": { - "node": ">=0.10.0" + "isarray": "0.0.1" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "through": "~2.3" } }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.12" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "dependencies": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "pinkie": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "node_modules/pkg": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.0.tgz", + "integrity": "sha512-8h9PUDYFi+LOMLbIyGRdP21g08mAtHidSpofSrf8LWhxUWGHymaRzcopEGiynB5EhQmZUKM6PQ9kCImV2TpdjQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "@babel/generator": "7.18.2", + "@babel/parser": "7.18.4", + "@babel/types": "7.18.4", + "chalk": "^4.1.2", + "fs-extra": "^9.1.0", + "globby": "^11.1.0", + "into-stream": "^6.0.0", + "is-core-module": "2.9.0", + "minimist": "^1.2.6", + "multistream": "^4.1.0", + "pkg-fetch": "3.4.2", + "prebuild-install": "6.1.4", + "resolve": "^1.22.0", + "stream-meter": "^1.0.4" }, - "engines": { - "node": ">= 0.4" + "bin": { + "pkg": "lib-es5/bin.js" + }, + "peerDependencies": { + "node-notifier": ">=9.0.1" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "node_modules/pkg-fetch": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.4.2.tgz", + "integrity": "sha512-0+uijmzYcnhC0hStDjm/cl2VYdrmVVBpe7Q8k9YBojxmR5tG8mvR9/nooQq3QSXiQqORDVOTY3XqMEqJVIzkHA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "chalk": "^4.1.2", + "fs-extra": "^9.1.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.6", + "progress": "^2.0.3", + "semver": "^7.3.5", + "tar-fs": "^2.1.1", + "yargs": "^16.2.0" }, + "bin": { + "pkg-fetch": "lib-es5/bin.js" + } + }, + "node_modules/pkg-fetch/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "node_modules/pkg-fetch/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "node_modules/pkg-fetch/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "node_modules/pkg-fetch/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/object.reduce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", - "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "node_modules/pkg-fetch/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "node_modules/pkg-fetch/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "devOptional": true, - "dependencies": { - "wrappy": "1" + "node_modules/pkg-fetch/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/pkg-fetch/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/ordered-read-streams": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.1" + "node_modules/pkg-fetch/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" } }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "node_modules/os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "node_modules/pkg-fetch/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "lcid": "^1.0.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "node_modules/pkg/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "node_modules/pkg/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/playwright": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.1.tgz", + "integrity": "sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "playwright-core": "1.41.1" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=16" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/playwright-core": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.1.tgz", + "integrity": "sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg==", "dev": true, - "dependencies": { - "p-limit": "^3.0.2" + "bin": { + "playwright-core": "cli.js" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, "dependencies": { - "callsites": "^3.0.0" + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" }, "engines": { - "node": ">=6" + "node": ">=10.4.0" } }, - "node_modules/parents": { + "node_modules/plugin-error": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "dev": true, "dependencies": { - "path-platform": "~0.11.15" + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "node_modules/pofile": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.0.11.tgz", + "integrity": "sha512-Vy9eH1dRD9wHjYt/QqXcTz+RnX/zg53xK+KljFSX30PvdDMb2z+c6uDUeblUGqqJgz3QFsdlA0IJvHziPmWtQg==", + "dev": true + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true, - "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=0.8" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", "dev": true, "dependencies": { - "error-ex": "^1.2.0" + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">= 0.8.0" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "node_modules/path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true, - "dependencies": { - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "devOptional": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", "engines": { - "node": ">=8" + "node": ">= 0.6.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "node_modules/process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "node_modules/path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "engines": { - "node": ">= 0.8.0" + "node": ">=0.4.0" } }, - "node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, "dependencies": { - "path-root-regex": "^0.1.0" + "err-code": "^2.0.2", + "retry": "^0.12.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "hasInstallScript": true, "dependencies": { - "isarray": "0.0.1" + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" } }, - "node_modules/path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, - "engines": { - "node": "*" + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "dependencies": { - "through": "~2.3" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, - "node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.x" } }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.x" } }, - "node_modules/pkg": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.0.tgz", - "integrity": "sha512-8h9PUDYFi+LOMLbIyGRdP21g08mAtHidSpofSrf8LWhxUWGHymaRzcopEGiynB5EhQmZUKM6PQ9kCImV2TpdjQ==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "dependencies": { - "@babel/generator": "7.18.2", - "@babel/parser": "7.18.4", - "@babel/types": "7.18.4", - "chalk": "^4.1.2", - "fs-extra": "^9.1.0", - "globby": "^11.1.0", - "into-stream": "^6.0.0", - "is-core-module": "2.9.0", - "minimist": "^1.2.6", - "multistream": "^4.1.0", - "pkg-fetch": "3.4.2", - "prebuild-install": "6.1.4", - "resolve": "^1.22.0", - "stream-meter": "^1.0.4" - }, - "bin": { - "pkg": "lib-es5/bin.js" - }, - "peerDependencies": { - "node-notifier": ">=9.0.1" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - } + ] }, - "node_modules/pkg-fetch": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.4.2.tgz", - "integrity": "sha512-0+uijmzYcnhC0hStDjm/cl2VYdrmVVBpe7Q8k9YBojxmR5tG8mvR9/nooQq3QSXiQqORDVOTY3XqMEqJVIzkHA==", + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "fs-extra": "^9.1.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.6", - "progress": "^2.0.3", - "semver": "^7.3.5", - "tar-fs": "^2.1.1", - "yargs": "^16.2.0" + "engines": { + "node": ">=10" }, - "bin": { - "pkg-fetch": "lib-es5/bin.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-fetch/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "safe-buffer": "^5.1.0" } }, - "node_modules/pkg-fetch/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, - "node_modules/pkg-fetch/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "bin": { + "rc": "cli.js" } }, - "node_modules/pkg-fetch/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/pkg-fetch/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { - "color-name": "~1.1.4" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "react": "^18.2.0" } }, - "node_modules/pkg-fetch/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/pkg-fetch/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, + "node_modules/react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } } }, - "node_modules/pkg-fetch/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } + "node_modules/react-redux/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, - "node_modules/pkg-fetch/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": ">=15" } }, - "node_modules/pkg-fetch/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/react-router/node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" } }, - "node_modules/pkg-fetch/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/react-router/node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "node_modules/react-router/node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "node_modules/read-config-file": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "config-file-ts": "^0.2.4", + "dotenv": "^9.0.2", + "dotenv-expand": "^5.1.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.0", + "lazy-val": "^1.0.4" }, "engines": { - "node": ">=8" + "node": ">=12.0.0" } }, - "node_modules/pkg-fetch/node_modules/universalify": { + "node_modules/read-only-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", "dev": true, - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "readable-stream": "^2.0.2" } }, - "node_modules/pkg-fetch/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/pkg-fetch/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/pkg-fetch/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/pkg/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/pkg/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "dev": true, + "peer": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "minimatch": "^5.1.0" } }, - "node_modules/pkg/node_modules/color-convert": { + "node_modules/readdir-glob/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "peer": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/pkg/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/pkg/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "peer": true, "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, - "node_modules/pkg/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/pkg/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, "engines": { - "node": ">= 10.0.0" + "node": ">= 0.10" } }, - "node_modules/playwright": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.1.tgz", - "integrity": "sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ==", - "dev": true, + "node_modules/redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", "dependencies": { - "playwright-core": "1.41.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "fsevents": "2.3.2" + "@babel/runtime": "^7.9.2" } }, - "node_modules/playwright-core": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.1.tgz", - "integrity": "sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg==", + "node_modules/regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, - "bin": { - "playwright-core": "cli.js" + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" }, "engines": { - "node": ">=16" + "node": ">=0.10.0" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/plist": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.6.tgz", - "integrity": "sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA==", + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, "dependencies": { - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", "dev": true, "dependencies": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" }, "engines": { "node": ">= 0.10" } }, - "node_modules/pofile": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.0.11.tgz", - "integrity": "sha512-Vy9eH1dRD9wHjYt/QqXcTz+RnX/zg53xK+KljFSX30PvdDMb2z+c6uDUeblUGqqJgz3QFsdlA0IJvHziPmWtQg==", + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=0.10" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/postcss/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "node_modules/replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", "dev": true, "dependencies": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" }, "engines": { - "node": ">=6" + "node": ">= 0.10" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, "bin": { - "prettier": "bin-prettier.js" + "resolve": "bin/resolve" }, - "engines": { - "node": ">=10.13.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "dependencies": { - "fast-diff": "^1.1.2" + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=0.10.0" } }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=4" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "node_modules/resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "dev": true, + "dependencies": { + "value-or-function": "^3.0.0" + }, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.10" } }, - "node_modules/process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise-retry": { + "node_modules/responselike": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "lowercase-keys": "^2.0.0" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" } }, - "node_modules/protobufjs": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", - "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, "engines": { - "node": ">=12.0.0" + "node": ">= 4" } }, - "node_modules/ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "devOptional": true, "dependencies": { - "event-stream": "=3.3.4" + "glob": "^7.1.3" }, "bin": { - "ps-tree": "bin/ps-tree.js" + "rimraf": "bin.js" }, - "engines": { - "node": ">= 0.10" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, + "optional": true, "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true, + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, "engines": { - "node": ">=0.4.x" + "node": ">=8.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -11187,1318 +11139,1241 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + ], + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "devOptional": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "dependencies": { - "safe-buffer": "^5.1.0" + "ret": "~0.1.10" } }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", "dev": true, "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", "dev": true, "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" + "truncate-utf8-bytes": "^1.0.0" } }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dependencies": { "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "devOptional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "optional": true + }, + "node_modules/semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "sver-compat": "^1.5.0" }, - "peerDependencies": { - "react": "^18.2.0" + "engines": { + "node": ">= 0.10" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "optional": true, "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", - "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" + "type-fest": "^0.13.1" }, - "peerDependencies": { - "react": "^16.8.3 || ^17 || ^18" + "engines": { + "node": ">=10" }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-redux/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" }, - "peerDependencies": { - "react": ">=15" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-router/node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "node_modules/react-router/node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "node_modules/react-router/node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, - "node_modules/read-config-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.2.0.tgz", - "integrity": "sha512-gx7Pgr5I56JtYz+WuqEbQHj/xWo+5Vwua2jhb1VwM4Wid5PqYmZ4i00ZB0YEGIfkVBsCv9UrjgyqCiQfS/Oosg==", + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "dependencies": { - "dotenv": "^9.0.2", - "dotenv-expand": "^5.1.0", - "js-yaml": "^4.1.0", - "json5": "^2.2.0", - "lazy-val": "^1.0.4" - }, - "engines": { - "node": ">=12.0.0" + "randombytes": "^2.1.0" } }, - "node_modules/read-only-stream": { + "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", - "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.2" - } + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "devOptional": true }, - "node_modules/read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "dependencies": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "dependencies": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "is-extendable": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" } }, - "node_modules/readable-stream/node_modules/isarray": { + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "node_modules/shasum-object": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } }, - "node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "node_modules/shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.9.2" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + "node_modules/signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "devOptional": true }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", "dev": true, "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "node_modules/simple-get/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "mimic-response": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/simple-get/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true, + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" + "semver": "^7.5.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "node_modules/sinon": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.1.tgz", + "integrity": "sha512-JhJ0jCiyBWVAHDS+YSjgEbDn7Wgz9iIjA1/RK+eseJN0vAAWIWiXBdrnb92ELPyjsfreCYntD1ORtLSfIrlvSQ==", "dev": true, "dependencies": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^6.1.1", + "diff": "^5.0.0", + "nise": "^5.1.1", + "supports-color": "^7.2.0" }, - "engines": { - "node": ">= 0.10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, + "optional": true, "engines": { - "node": ">= 0.10" + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/replace-homedir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", - "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "dependencies": { - "homedir-polyfill": "^1.0.1", - "is-absolute": "^1.0.0", - "remove-trailing-separator": "^1.1.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/require-directory": { + "node_modules/snapdragon-node": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "is-descriptor": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "kind-of": "^6.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/resolve-options": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "dependencies": { - "value-or-function": "^3.0.0" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { - "lowercase-keys": "^2.0.0" + "is-buffer": "^1.1.5" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, "engines": { - "node": ">=0.12" + "node": ">=0.10.0" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, "engines": { - "node": ">= 4" + "node": ">=0.10.0" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true, "engines": { - "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "devOptional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "node_modules/source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "optional": true, "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "devOptional": true + "node_modules/source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "node_modules/sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", "dev": true, - "dependencies": { - "ret": "~0.1.10" + "engines": { + "node": ">= 0.10" } }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "node_modules/spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "node_modules/spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true }, - "node_modules/sanitize-filename": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "node_modules/spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "dependencies": { - "truncate-utf8-bytes": "^1.0.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "node_modules/spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", "dev": true }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "devOptional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "optional": true - }, - "node_modules/semver-greatest-satisfied-range": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", - "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "dependencies": { - "sver-compat": "^1.5.0" + "through": "2" }, "engines": { - "node": ">= 0.10" + "node": "*" } }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, - "optional": true, "dependencies": { - "type-fest": "^0.13.1" + "extend-shallow": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", "dev": true, - "optional": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "dev": true, - "dependencies": { - "randombytes": "^2.1.0" + "engines": { + "node": ">= 6" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "devOptional": true - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "is-descriptor": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, - "bin": { - "sha.js": "bin.js" + "engines": { + "node": ">= 6" } }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } }, - "node_modules/shasum-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", - "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "dev": true, "dependencies": { - "fast-safe-stringify": "^2.0.7" + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "node_modules/stream-http": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz", + "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==", "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/stream-http/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", - "dev": true - }, - "node_modules/side-channel": { + "node_modules/stream-meter": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "resolved": "https://registry.npmjs.org/stream-meter/-/stream-meter-1.0.4.tgz", + "integrity": "sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "readable-stream": "^2.1.4" } }, - "node_modules/signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "devOptional": true - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "node_modules/stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "node_modules/stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", "dev": true, "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" } }, - "node_modules/simple-get/node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "devOptional": true, "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" + "safe-buffer": "~5.1.0" } }, - "node_modules/simple-get/node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "node_modules/string-argv": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz", + "integrity": "sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.6.19" } }, - "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "devOptional": true, "dependencies": { - "semver": "~7.0.0" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" }, "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/sinon": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.1.tgz", - "integrity": "sha512-JhJ0jCiyBWVAHDS+YSjgEbDn7Wgz9iIjA1/RK+eseJN0vAAWIWiXBdrnb92ELPyjsfreCYntD1ORtLSfIrlvSQ==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" + "node": ">=0.10.0" } }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "optional": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "optional": true, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "devOptional": true, "dependencies": { - "color-convert": "^2.0.1" + "number-is-nan": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", "dev": true, - "optional": true, "dependencies": { - "color-name": "~1.1.4" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, - "optional": true - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "devOptional": true, "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "is-descriptor": "^1.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/snapdragon-node/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "dependencies": { - "kind-of": "^6.0.0" + "is-utf8": "^0.2.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, - "dependencies": { - "kind-of": "^3.2.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, + "node_modules/styled-components": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.0.tgz", + "integrity": "sha512-VWNfYYBuXzuLS/QYEeoPgMErP26WL+dX9//rEh80B2mmlS1yRxRxuL5eax4m6ybYEUoHWlTy2XOU32767mlMkg==", "dependencies": { - "is-buffer": "^1.1.5" + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/unitless": "^0.8.0", + "@types/stylis": "^4.0.2", + "css-to-react-native": "^3.2.0", + "csstype": "^3.1.2", + "postcss": "^8.4.31", + "shallowequal": "^1.1.0", + "stylis": "^4.3.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" + "node_modules/styled-components/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, + "node_modules/styled-components/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "is-extendable": "^0.1.0" + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" }, - "node_modules/source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "node_modules/subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", "dev": true, "dependencies": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "minimist": "^1.1.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "debug": "^4.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 8.0" } }, - "node_modules/source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "node_modules/sparkles": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "node_modules/sumchecker/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "node_modules/sumchecker/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "through": "2" + "has-flag": "^4.0.0" }, "engines": { - "node": "*" - } - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.0" + "node": ">=10" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stat-mode": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", - "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "node_modules/sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", "dev": true, - "engines": { - "node": ">= 6" + "dependencies": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" } }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "node_modules/syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", "dev": true, "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "acorn-node": "^1.2.0" } }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "devOptional": true, "dependencies": { - "is-descriptor": "^0.1.0" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" } }, - "node_modules/stream-browserify/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "readable-stream": "^3.1.1" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1" + "node": ">=6" } }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/stream-exhaust": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", - "dev": true - }, - "node_modules/stream-http": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz", - "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==", + "node_modules/tar-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "xtend": "^4.0.2" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/stream-http/node_modules/readable-stream": { + "node_modules/tar-stream/node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", @@ -12512,1438 +12387,1046 @@ "node": ">= 6" } }, - "node_modules/stream-meter": { + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stream-meter/-/stream-meter-1.0.4.tgz", - "integrity": "sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ==", - "dev": true, - "dependencies": { - "readable-stream": "^2.1.4" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "devOptional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "node_modules/stream-splicer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", - "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" } }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "devOptional": true, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "node_modules/string-argv": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz", - "integrity": "sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==", + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, - "engines": { - "node": ">=0.6.19" + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" } }, - "node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "devOptional": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/string-width/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "devOptional": true, + "node_modules/timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, "dependencies": { - "number-is-nan": "^1.0.0" + "process": "~0.11.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.6.0" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "es5-ext": "~0.10.46", + "next-tick": "1" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "node_modules/tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.14" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tmp": "^0.2.0" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "devOptional": true, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" + "kind-of": "^3.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { - "is-utf8": "^0.2.0" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/styled-components": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.0.tgz", - "integrity": "sha512-VWNfYYBuXzuLS/QYEeoPgMErP26WL+dX9//rEh80B2mmlS1yRxRxuL5eax4m6ybYEUoHWlTy2XOU32767mlMkg==", + "node_modules/to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "dev": true, "dependencies": { - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/unitless": "^0.8.0", - "@types/stylis": "^4.0.2", - "css-to-react-native": "^3.2.0", - "csstype": "^3.1.2", - "postcss": "^8.4.31", - "shallowequal": "^1.1.0", - "stylis": "^4.3.0", - "tslib": "^2.5.0" + "through2": "^2.0.3" }, "engines": { - "node": ">= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/styled-components" - }, - "peerDependencies": { - "react": ">= 16.8.0", - "react-dom": ">= 16.8.0" + "node": ">= 0.10" } }, - "node_modules/styled-components/node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/styled-components/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/styled-components/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/styled-components/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/stylis": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", - "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "devOptional": true }, - "node_modules/subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "dev": true, "dependencies": { - "minimist": "^1.1.0" + "utf8-byte-length": "^1.0.1" } }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { - "debug": "^4.1.0" + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/sumchecker/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" }, - "engines": { - "node": ">=6.0" + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" }, "peerDependenciesMeta": { - "supports-color": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { "optional": true } } }, - "node_modules/sumchecker/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/ts-node/node_modules/acorn": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=0.4.0" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.4.0" } }, - "node_modules/sver-compat": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", - "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "dependencies": { - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "engines": { + "node": ">=0.3.1" } }, - "node_modules/syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "node_modules/tsc-watch": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-5.0.3.tgz", + "integrity": "sha512-Hz2UawwELMSLOf0xHvAFc7anLeMw62cMVXr1flYmhRuOhOyOljwmb1l/O60ZwRyy1k7N1iC1mrn1QYM2zITfuw==", "dev": true, "dependencies": { - "acorn-node": "^1.2.0" + "cross-spawn": "^7.0.3", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.1.1", + "strip-ansi": "^6.0.0" + }, + "bin": { + "tsc-watch": "index.js" + }, + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "typescript": "*" } }, - "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "devOptional": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, + "node_modules/tsc-watch/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "node_modules/tsc-watch/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "tslib": "^1.8.1" }, "engines": { - "node": ">=6" + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/tar-stream/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/tar-stream/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "devOptional": true, - "bin": { - "mkdirp": "bin/cmd.js" + "safe-buffer": "^5.0.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/temp-file": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", - "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", - "dev": true, - "dependencies": { - "async-exit-hook": "^2.0.1", - "fs-extra": "^10.0.0" + "node": "*" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "dependencies": { - "through2": "~2.0.0", - "xtend": "~4.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/timers-browserify": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", - "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", "dev": true, "dependencies": { - "process": "~0.11.0" + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" }, - "engines": { - "node": ">=0.6.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "node_modules/tiny-invariant": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", - "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "node_modules/uglify-js": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.6.tgz", + "integrity": "sha512-rRprLwl8RVaS+Qvx3Wh5hPfPBn9++G6xkGlUupya0s5aDmNjI7z3lnRLB3u7sN4OmbB0pWgzhM9BEJyiWAwtAA==", "dev": true, - "dependencies": { - "rimraf": "^3.0.0" + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, "engines": { - "node": ">=8.17.0" + "node": ">=0.8.0" } }, - "node_modules/tmp-promise": { + "node_modules/umd": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", "dev": true, - "dependencies": { - "tmp": "^0.2.0" + "bin": { + "umd": "bin/cli.js" } }, - "node_modules/to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "node_modules/undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "undeclared-identifiers": "bin.js" } }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/undertaker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", "dev": true, - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "node_modules/undertaker/node_modules/fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "dev": true + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/to-through": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, "dependencies": { - "through2": "^2.0.3" - }, - "engines": { - "node": ">= 0.10" + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "devOptional": true - }, - "node_modules/truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "dependencies": { - "utf8-byte-length": "^1.0.1" + "engines": { + "node": ">= 4.0.0" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ts-node/node_modules/acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=0.10.0" } }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=0.10.0" } }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=0.10.0" } }, - "node_modules/tsc-watch": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-5.0.3.tgz", - "integrity": "sha512-Hz2UawwELMSLOf0xHvAFc7anLeMw62cMVXr1flYmhRuOhOyOljwmb1l/O60ZwRyy1k7N1iC1mrn1QYM2zITfuw==", + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/unzip-crx-3": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz", + "integrity": "sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "node-cleanup": "^2.1.2", - "ps-tree": "^1.2.0", - "string-argv": "^0.1.1", - "strip-ansi": "^6.0.0" - }, - "bin": { - "tsc-watch": "index.js" - }, - "engines": { - "node": ">=8.17.0" - }, - "peerDependencies": { - "typescript": "*" + "jszip": "^3.1.0", + "mkdirp": "^0.5.1", + "yaku": "^0.16.6" } }, - "node_modules/tsc-watch/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4", + "yarn": "*" } }, - "node_modules/tsc-watch/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" + "punycode": "^2.1.0" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "dev": true, "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "punycode": "1.3.2", + "querystring": "0.2.0" } }, - "node_modules/tty-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "dev": true }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "node_modules/utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", "dev": true }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "devOptional": true }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "homedir-polyfill": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.10" } }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "node_modules/typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "node_modules/value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, "engines": { - "node": ">=12.20" + "node": ">= 0.10" } }, - "node_modules/uglify-js": { - "version": "3.13.6", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.6.tgz", - "integrity": "sha512-rRprLwl8RVaS+Qvx3Wh5hPfPBn9++G6xkGlUupya0s5aDmNjI7z3lnRLB3u7sN4OmbB0pWgzhM9BEJyiWAwtAA==", + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" }, "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/umd": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", - "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", - "dev": true, - "bin": { - "umd": "bin/cli.js" + "node": ">=0.6.0" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/undeclared-identifiers": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", - "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "node_modules/vinyl-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz", + "integrity": "sha1-lsGjR5uMU5JULGEgKQE7Wyf4i78=", "dev": true, "dependencies": { - "acorn-node": "^1.3.0", - "dash-ast": "^1.0.0", - "get-assigned-identifiers": "^1.2.0", - "simple-concat": "^1.0.0", - "xtend": "^4.0.1" - }, - "bin": { - "undeclared-identifiers": "bin.js" + "bl": "^1.2.1", + "through2": "^2.0.3" } }, - "node_modules/undertaker": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", - "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", + "node_modules/vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", "dev": true, "dependencies": { - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "bach": "^1.0.0", - "collection-map": "^1.0.0", - "es6-weak-map": "^2.0.1", - "fast-levenshtein": "^1.0.0", - "last-run": "^1.1.0", - "object.defaults": "^1.0.0", - "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" }, "engines": { "node": ">= 0.10" } }, - "node_modules/undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "node_modules/vinyl-source-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz", + "integrity": "sha1-84pa+53R6Ttl1VBGmsYYKsT1S44=", "dev": true, - "engines": { - "node": ">= 0.10" + "dependencies": { + "through2": "^2.0.3", + "vinyl": "^2.1.0" } }, - "node_modules/undertaker/node_modules/fast-levenshtein": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", - "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", - "dev": true - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "node_modules/vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", "dev": true, "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/unique-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "node_modules/vinyl-sourcemap/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "dependencies": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "devOptional": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "devOptional": true, "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "isexe": "^2.0.0" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "which": "bin/which" } }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "dependencies": { - "isarray": "1.0.0" + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unset-value/node_modules/isarray": { + "node_modules/which-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, - "node_modules/unzip-crx-3": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz", - "integrity": "sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==", + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "dev": true, "dependencies": { - "jszip": "^3.1.0", - "mkdirp": "^0.5.1", - "yaku": "^0.16.6" - } - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "devOptional": true, "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "string-width": "^1.0.2 || 2" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/utf8-byte-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, - "node_modules/util": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", - "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "devOptional": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, - "node_modules/v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "dependencies": { - "homedir-polyfill": "^1.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/value-or-function": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "optional": true, "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.6.0" + "node": ">=8" } }, - "node_modules/vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vinyl-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz", - "integrity": "sha1-lsGjR5uMU5JULGEgKQE7Wyf4i78=", - "dev": true, - "dependencies": { - "bl": "^1.2.1", - "through2": "^2.0.3" - } - }, - "node_modules/vinyl-fs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", - "dev": true, - "dependencies": { - "fs-mkdirp-stream": "^1.0.0", - "glob-stream": "^6.1.0", - "graceful-fs": "^4.0.0", - "is-valid-glob": "^1.0.0", - "lazystream": "^1.0.0", - "lead": "^1.0.0", - "object.assign": "^4.0.4", - "pumpify": "^1.3.5", - "readable-stream": "^2.3.3", - "remove-bom-buffer": "^3.0.0", - "remove-bom-stream": "^1.2.0", - "resolve-options": "^1.1.0", - "through2": "^2.0.0", - "to-through": "^2.0.0", - "value-or-function": "^3.0.0", - "vinyl": "^2.0.0", - "vinyl-sourcemap": "^1.1.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vinyl-source-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz", - "integrity": "sha1-84pa+53R6Ttl1VBGmsYYKsT1S44=", - "dev": true, - "dependencies": { - "through2": "^2.0.3", - "vinyl": "^2.1.0" - } - }, - "node_modules/vinyl-sourcemap": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", - "dev": true, - "dependencies": { - "append-buffer": "^1.0.2", - "convert-source-map": "^1.5.0", - "graceful-fs": "^4.1.6", - "normalize-path": "^2.1.1", - "now-and-later": "^2.0.0", - "remove-bom-buffer": "^3.0.0", - "vinyl": "^2.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vinyl-sourcemap/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "devOptional": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "devOptional": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "devOptional": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/wrappy": { @@ -14074,20 +13557,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/yargs/node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -14101,22 +13570,6 @@ "node": ">=12" } }, - "node_modules/yargs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/yargs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/yargs/node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -14211,27 +13664,100 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - } - }, - "dependencies": { - "@babel/generator": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", - "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", "dev": true, - "requires": { - "@babel/types": "^7.18.2", - "@jridgewell/gen-mapping": "^0.3.0", - "jsesc": "^2.5.1" + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" } }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/parser": { + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + } + }, + "dependencies": { + "@babel/generator": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", + "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", + "dev": true, + "requires": { + "@babel/types": "^7.18.2", + "@jridgewell/gen-mapping": "^0.3.0", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/parser": { "version": "7.18.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", @@ -14274,6 +13800,17 @@ "ajv-keywords": "^3.4.1" } }, + "@electron/asar": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.9.tgz", + "integrity": "sha512-Vu2P3X2gcZ3MY9W7yH72X9+AMXwUQZEJBrsPIbX0JsdllLtoh62/Q8Wg370/DawIEVKOyfD6KtTLo645ezqxUA==", + "dev": true, + "requires": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + } + }, "@electron/get": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", @@ -14334,9 +13871,9 @@ } }, "@electron/notarize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.1.0.tgz", - "integrity": "sha512-Q02xem1D0sg4v437xHgmBLxI2iz/fc0D4K7fiVWHa/AnW8o7D751xyKNXgziA6HrTOme9ul1JfWN5ark8WH1xA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", "dev": true, "requires": { "debug": "^4.1.1", @@ -14379,16 +13916,53 @@ } } }, + "@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "requires": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "@electron/universal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", - "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", "dev": true, "requires": { + "@electron/asar": "^3.2.1", "@malept/cross-spawn-promise": "^1.1.0", - "asar": "^3.1.0", "debug": "^4.3.1", - "dir-compare": "^2.4.0", + "dir-compare": "^3.0.0", "fs-extra": "^9.0.1", "minimatch": "^3.0.4", "plist": "^3.0.4" @@ -14422,9 +13996,9 @@ "dev": true }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } @@ -14488,15 +14062,6 @@ "ms": "2.1.2" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -14615,15 +14180,6 @@ "ms": "2.1.2" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -14644,6 +14200,71 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -14732,9 +14353,9 @@ "dev": true }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } @@ -14875,6 +14496,13 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@playwright/test": { "version": "1.41.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.1.tgz", @@ -15055,9 +14683,9 @@ } }, "@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "requires": { "@types/ms": "*" @@ -15159,9 +14787,9 @@ "dev": true }, "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", "dev": true }, "@types/node": { @@ -15182,9 +14810,9 @@ "dev": true }, "@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, "optional": true, "requires": { @@ -15293,27 +14921,12 @@ } }, "@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", + "version": "1.10.10", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", + "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", "dev": true, "optional": true }, - "@types/yargs": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", - "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, "@types/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", @@ -15491,16 +15104,16 @@ "eslint-visitor-keys": "^3.3.0" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "dev": true }, "7zip-bin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", - "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true }, "abbrev": { @@ -15616,6 +15229,14 @@ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "devOptional": true }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, "ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", @@ -15650,39 +15271,49 @@ "dev": true }, "app-builder-lib": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-23.6.0.tgz", - "integrity": "sha512-dQYDuqm/rmy8GSCE6Xl/3ShJg6Ab4bZJMT8KaTKGzT436gl1DN4REP3FCWfXoh75qGTJ+u+WsdnnpO9Jl8nyMA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", "dev": true, "requires": { "@develar/schema-utils": "~2.6.5", - "@electron/universal": "1.2.1", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", "@malept/flatpak-bundler": "^0.4.0", - "7zip-bin": "~5.1.1", + "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.4", - "ejs": "^3.1.7", - "electron-osx-sign": "^0.6.0", - "electron-publish": "23.6.0", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", - "isbinaryfile": "^4.0.10", + "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", - "minimatch": "^3.1.2", - "read-config-file": "6.2.0", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", "sanitize-filename": "^1.6.3", - "semver": "^7.3.7", - "tar": "^6.1.11", + "semver": "^7.3.8", + "tar": "^6.1.12", "temp-file": "^3.4.0" }, "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -15692,17 +15323,6 @@ "ms": "2.1.2" } }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -15713,12 +15333,12 @@ } }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" } }, "ms": { @@ -15726,12 +15346,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true } } }, @@ -15750,6 +15364,55 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "devOptional": true }, + "archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "peer": true, + "requires": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + } + }, "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", @@ -15945,27 +15608,6 @@ "get-intrinsic": "^1.1.3" } }, - "asar": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", - "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "chromium-pickle-js": "^0.2.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "dependencies": { - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - } - } - }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -16040,9 +15682,9 @@ "optional": true }, "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "async-done": { @@ -16479,22 +16121,6 @@ "ieee754": "^1.1.4" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -16507,12 +16133,6 @@ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", "dev": true }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "dev": true - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -16526,23 +16146,22 @@ "dev": true }, "builder-util": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-23.6.0.tgz", - "integrity": "sha512-QiQHweYsh8o+U/KNCZFSvISRnvRctb8m/2rB2I1JdByzvNKxPeFLlHFRPQRXab6aYeXc18j9LpsDLJ3sGQmWTQ==", + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", "dev": true, "requires": { "@types/debug": "^4.1.6", - "@types/fs-extra": "^9.0.11", - "7zip-bin": "~5.1.1", + "7zip-bin": "~5.2.0", "app-builder-bin": "4.0.0", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", - "fs-extra": "^10.0.0", + "fs-extra": "^10.1.0", "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", "source-map-support": "^0.5.19", @@ -16550,40 +16169,6 @@ "temp-file": "^3.4.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -16598,22 +16183,13 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "builder-util-runtime": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.1.1.tgz", - "integrity": "sha512-azRhYLEoDvRDR8Dhis4JatELC/jUvYjm4cVSj7n9dauGTOM2eeNn9KS0z6YA6oDsjI1xphjNbY6PZZeHPzzqaw==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", "dev": true, "requires": { "debug": "^4.3.4", @@ -16744,6 +16320,27 @@ "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", "dev": true }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -16783,9 +16380,9 @@ "dev": true }, "ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true }, "cipher-base": { @@ -16939,18 +16536,25 @@ "object-visit": "^1.0.0" } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "devOptional": true }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "dev": true - }, "combine-source-map": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", @@ -16980,6 +16584,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true + }, "compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -16992,6 +16602,33 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, + "compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "peer": true, + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -17010,6 +16647,55 @@ "typedarray": "^0.0.6" } }, + "config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "requires": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true + } + } + }, "console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", @@ -17082,6 +16768,38 @@ "buffer": "^5.1.0" } }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "peer": true + }, + "crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "peer": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -17523,26 +17241,13 @@ } }, "dir-compare": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz", - "integrity": "sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", "dev": true, "requires": { - "buffer-equal": "1.0.0", - "colors": "1.0.3", - "commander": "2.9.0", - "minimatch": "3.0.4" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } - } + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" } }, "dir-glob": { @@ -17563,16 +17268,16 @@ } }, "dmg-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-23.6.0.tgz", - "integrity": "sha512-jFZvY1JohyHarIAlTbfQOk+HnceGjjAdFjVn3n8xlDWKsYNqbO4muca6qXEZTfGXeQMG7TYim6CeS5XKSfSsGA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", "dev": true, "requires": { - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", "dmg-license": "^1.0.11", - "fs-extra": "^10.0.0", + "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" } @@ -17647,547 +17352,108 @@ "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } - }, - "each-props": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" - } - }, - "ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", - "dev": true, - "requires": { - "jake": "^10.8.5" - } - }, - "electron": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.3.tgz", - "integrity": "sha512-NSFyTo6SndTPXzU18XRePv4LnjmuM9rF5GMKta1/kPmi02ISoSRonnD7wUlWXD2x53XyJ6d/TbSVesMW6sXkEQ==", - "dev": true, - "requires": { - "@electron/get": "^2.0.0", - "@types/node": "^18.11.18", - "extract-zip": "^2.0.1" - } - }, - "electron-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-23.6.0.tgz", - "integrity": "sha512-y8D4zO+HXGCNxFBV/JlyhFnoQ0Y0K7/sFH+XwIbj47pqaW8S6PGYQbjoObolKBR1ddQFPt4rwp4CnwMJrW3HAw==", - "dev": true, - "requires": { - "@types/yargs": "^17.0.1", - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "dmg-builder": "23.6.0", - "fs-extra": "^10.0.0", - "is-ci": "^3.0.0", - "lazy-val": "^1.0.5", - "read-config-file": "6.2.0", - "simple-update-notifier": "^1.0.7", - "yargs": "^17.5.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "electron-devtools-installer": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/electron-devtools-installer/-/electron-devtools-installer-3.2.0.tgz", - "integrity": "sha512-t3UczsYugm4OAbqvdImMCImIMVdFzJAHgbwHpkl5jmfu1izVgUcP/mnrPqJIpEeCK1uZGpt+yHgWEN+9EwoYhQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.2", - "semver": "^7.2.1", - "tslib": "^2.1.0", - "unzip-crx-3": "^0.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "dev": true - } - } - }, - "electron-mocha": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/electron-mocha/-/electron-mocha-11.0.2.tgz", - "integrity": "sha512-fOk+zUgSIsmL2cuIrd7IlK4eRhGVi1PYIB3QvqiBO+6f6AP8XLkYkT9eORlL2xwaS3yAAk02Y+4OTuhtqHPkEQ==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1", - "electron-window": "^0.8.0", - "fs-extra": "^10.0.0", - "mocha": "^9.1.1", - "which": "^2.0.2", - "yargs": "^16.2.0" - }, - "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" } }, - "electron-osx-sign": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz", - "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==", + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "requires": { - "bluebird": "^3.5.0", - "compare-version": "^0.1.2", - "debug": "^2.6.8", - "isbinaryfile": "^3.0.2", - "minimist": "^1.2.0", - "plist": "^3.0.1" - }, - "dependencies": { - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - } + "jake": "^10.8.5" } }, - "electron-publish": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-23.6.0.tgz", - "integrity": "sha512-jPj3y+eIZQJF/+t5SLvsI5eS4mazCbNYqatv5JihbqOstIM13k0d1Z3vAWntvtt13Itl61SO6seicWdioOU5dg==", + "electron": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.3.tgz", + "integrity": "sha512-NSFyTo6SndTPXzU18XRePv4LnjmuM9rF5GMKta1/kPmi02ISoSRonnD7wUlWXD2x53XyJ6d/TbSVesMW6sXkEQ==", "dev": true, "requires": { - "@types/fs-extra": "^9.0.11", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "fs-extra": "^10.0.0", + "@electron/get": "^2.0.0", + "@types/node": "^18.11.18", + "extract-zip": "^2.0.1" + } + }, + "electron-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "dev": true, + "requires": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", "lazy-val": "^1.0.5", - "mime": "^2.5.2" + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + } + }, + "electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "dev": true, + "peer": true, + "requires": { + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" + } + }, + "electron-devtools-installer": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/electron-devtools-installer/-/electron-devtools-installer-3.2.0.tgz", + "integrity": "sha512-t3UczsYugm4OAbqvdImMCImIMVdFzJAHgbwHpkl5jmfu1izVgUcP/mnrPqJIpEeCK1uZGpt+yHgWEN+9EwoYhQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.2", + "semver": "^7.2.1", + "tslib": "^2.1.0", + "unzip-crx-3": "^0.2.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, - "electron-window": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/electron-window/-/electron-window-0.8.1.tgz", - "integrity": "sha1-FsoYfrSHCwZ5J0/IKZxZYOarLF4=", + "electron-publish": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", "dev": true, "requires": { - "is-electron-renderer": "^2.0.0" + "@types/fs-extra": "^9.0.11", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" } }, "elliptic": { @@ -18445,40 +17711,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -18526,15 +17758,6 @@ "is-glob": "^4.0.3" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -18561,15 +17784,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, @@ -18605,15 +17819,6 @@ "string.prototype.matchall": "^4.0.8" }, "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "resolve": { "version": "2.0.0-next.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", @@ -19115,9 +18320,9 @@ } }, "minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -19245,6 +18450,24 @@ "for-in": "^1.0.1" } }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -19288,9 +18511,9 @@ "dev": true }, "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "requires": { "graceful-fs": "^4.2.0", @@ -19701,24 +18924,12 @@ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true - }, "grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "grpc_tools_node_protoc_ts": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-5.3.2.tgz", @@ -20154,9 +19365,9 @@ "dev": true }, "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "devOptional": true, "requires": { "agent-base": "6", @@ -20473,12 +19684,6 @@ } } }, - "is-electron-renderer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-electron-renderer/-/is-electron-renderer-2.0.1.tgz", - "integrity": "sha1-pGnQVvl1aXxYyYxgI+sKp5r4laI=", - "dev": true - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -20686,9 +19891,9 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", + "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", "dev": true }, "isexe": { @@ -20703,61 +19908,26 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, "requires": { "async": "^3.2.3", "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "filelist": "^1.0.4", + "minimatch": "^3.1.2" } }, "js-sdsl": { @@ -21021,11 +20191,39 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "peer": true + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "peer": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "peer": true + }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "peer": true + }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", @@ -21038,6 +20236,13 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "peer": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -21046,51 +20251,6 @@ "requires": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "long": { @@ -21357,9 +20517,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "devOptional": true, "requires": { "brace-expansion": "^1.1.7" @@ -21462,19 +20622,10 @@ "dev": true }, "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "anymatch": { "version": "3.1.2", @@ -21537,21 +20688,6 @@ "wrap-ansi": "^7.0.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -22343,6 +21479,30 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, + "path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true + }, + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true + } + } + }, "path-to-regexp": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", @@ -22451,40 +21611,6 @@ "stream-meter": "^1.0.4" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -22497,15 +21623,6 @@ "universalify": "^2.0.0" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -22536,25 +21653,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -22566,21 +21664,6 @@ "wrap-ansi": "^7.0.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -22619,15 +21702,6 @@ "ansi-regex": "^5.0.1" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -22694,11 +21768,12 @@ "dev": true }, "plist": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.6.tgz", - "integrity": "sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, "requires": { + "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } @@ -23063,11 +22138,12 @@ } }, "read-config-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.2.0.tgz", - "integrity": "sha512-gx7Pgr5I56JtYz+WuqEbQHj/xWo+5Vwua2jhb1VwM4Wid5PqYmZ4i00ZB0YEGIfkVBsCv9UrjgyqCiQfS/Oosg==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", "dev": true, "requires": { + "config-file-ts": "^0.2.4", "dotenv": "^9.0.2", "dotenv-expand": "^5.1.0", "js-yaml": "^4.1.0", @@ -23128,6 +22204,38 @@ } } }, + "readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "peer": true, + "requires": { + "minimatch": "^5.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "peer": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "peer": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -23408,9 +22516,9 @@ } }, "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", "dev": true }, "scheduler": { @@ -23606,20 +22714,12 @@ } }, "simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "requires": { - "semver": "~7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "semver": "^7.5.3" } }, "sinon": { @@ -23663,35 +22763,6 @@ "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "optional": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "optional": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "optional": true - } } }, "smart-buffer": { @@ -24085,6 +23156,34 @@ } } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -24143,6 +23242,23 @@ "ansi-regex": "^2.0.0" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + } + } + }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -24282,19 +23398,25 @@ } }, "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "devOptional": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "dependencies": { + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true + }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -24448,13 +23570,10 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true }, "tmp-promise": { "version": "3.0.3", @@ -24693,9 +23812,9 @@ "dev": true }, "typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true }, "uglify-js": { @@ -25159,6 +24278,45 @@ "strip-ansi": "^3.0.1" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -25224,14 +24382,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -25242,19 +24392,6 @@ "wrap-ansi": "^7.0.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -25353,6 +24490,66 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "peer": true, + "requires": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "peer": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } } } } diff --git a/gui/package.json b/gui/package.json index fa1d8dbff4ef..92093b842ddb 100644 --- a/gui/package.json +++ b/gui/package.json @@ -56,7 +56,7 @@ "chai-spies": "^1.0.0", "cross-env": "^7.0.3", "electron": "^28.1.3", - "electron-builder": "^23.6.0", + "electron-builder": "^24.13.3", "electron-devtools-installer": "^3.2.0", "eslint": "^8.36.0", "eslint-plugin-prettier": "^4.2.1", From c16ef1f7ca74b60e18ee9f4090e3312a795fac0b Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 23 Apr 2024 08:45:23 +0200 Subject: [PATCH 158/214] Update dependencies with npm audit fix --- gui/package-lock.json | 660 ++++++++++++++++++++---------------------- 1 file changed, 315 insertions(+), 345 deletions(-) diff --git a/gui/package-lock.json b/gui/package-lock.json index 036ed8159fc5..47d1447d57dd 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -97,6 +97,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", @@ -130,12 +139,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz", - "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -982,15 +992,6 @@ "node": ">=10" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@mapbox/node-pre-gyp/node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -2182,7 +2183,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "devOptional": true + "optional": true }, "node_modules/archiver": { "version": "5.3.2", @@ -2246,16 +2247,6 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, - "node_modules/are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2493,21 +2484,20 @@ } }, "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "minimalistic-assert": "^1.0.0" } }, "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, "node_modules/assert": { @@ -2804,9 +2794,9 @@ } }, "node_modules/bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "dev": true }, "node_modules/boolean": { @@ -3007,34 +2997,37 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", "dev": true, "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.5", + "hash-base": "~3.0", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" } }, - "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/browserify-sign/node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" }, "engines": { - "node": ">= 6" + "node": ">=4" } }, "node_modules/browserify-sign/node_modules/safe-buffer": { @@ -3796,7 +3789,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "devOptional": true + "optional": true }, "node_modules/constants-browserify": { "version": "1.0.0", @@ -4336,7 +4329,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "devOptional": true + "optional": true }, "node_modules/deps-sort": { "version": "2.0.1", @@ -4373,15 +4366,12 @@ } }, "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "devOptional": true, "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/detect-newline": { @@ -4703,9 +4693,9 @@ } }, "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", + "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", "dev": true, "dependencies": { "bn.js": "^4.11.9", @@ -4858,14 +4848,19 @@ } }, "node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, + "hasInstallScript": true, "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" } }, "node_modules/es6-error": { @@ -5216,6 +5211,27 @@ "node": ">=8" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, "node_modules/espree": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", @@ -6108,22 +6124,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "dev": true, - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, "node_modules/get-assigned-identifiers": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", @@ -6901,7 +6901,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "devOptional": true + "optional": true }, "node_modules/has-value": { "version": "1.0.0", @@ -8474,12 +8474,6 @@ "timers-ext": "^0.1.7" } }, - "node_modules/memoizee/node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -9207,9 +9201,9 @@ "dev": true }, "node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, "node_modules/nise": { @@ -9226,21 +9220,15 @@ } }, "node_modules/node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "version": "3.61.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.61.0.tgz", + "integrity": "sha512-dYDO1rxzvMXjEMi37PBeFuYgwh3QZpsw/jt+qOmnRSwiV4z4c+OLoRlTa3V8ID4TrkSQpzCVc9OI2sstFaINfQ==", "dev": true, "dependencies": { - "semver": "^5.4.1" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" } }, "node_modules/node-addon-api": { @@ -9338,18 +9326,6 @@ "node": ">= 0.10" } }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, "node_modules/nseventmonitor": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/nseventmonitor/-/nseventmonitor-1.0.4.tgz", @@ -9710,18 +9686,55 @@ } }, "node_modules/parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", "dev": true, "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-asn1/node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" } }, + "node_modules/parse-asn1/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -9928,9 +9941,9 @@ } }, "node_modules/pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, "dependencies": { "create-hash": "^1.1.2", @@ -9998,14 +10011,14 @@ } }, "node_modules/pkg": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.0.tgz", - "integrity": "sha512-8h9PUDYFi+LOMLbIyGRdP21g08mAtHidSpofSrf8LWhxUWGHymaRzcopEGiynB5EhQmZUKM6PQ9kCImV2TpdjQ==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.1.tgz", + "integrity": "sha512-CjBWtFStCfIiT4Bde9QpJy0KeH19jCfwZRJqHFDFXfhUklCx8JoFmMj3wgnEYIwGmZVNkhsStPHEOnrtrQhEXA==", "dev": true, "dependencies": { "@babel/generator": "7.18.2", "@babel/parser": "7.18.4", - "@babel/types": "7.18.4", + "@babel/types": "7.19.0", "chalk": "^4.1.2", "fs-extra": "^9.1.0", "globby": "^11.1.0", @@ -10014,7 +10027,7 @@ "minimist": "^1.2.6", "multistream": "^4.1.0", "pkg-fetch": "3.4.2", - "prebuild-install": "6.1.4", + "prebuild-install": "7.1.1", "resolve": "^1.22.0", "stream-meter": "^1.0.4" }, @@ -10316,22 +10329,21 @@ } }, "node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", "dev": true, "dependencies": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", + "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", - "simple-get": "^3.0.3", + "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, @@ -10339,7 +10351,7 @@ "prebuild-install": "bin.js" }, "engines": { - "node": ">=6" + "node": ">=10" } }, "node_modules/prelude-ls": { @@ -10765,9 +10777,9 @@ } }, "node_modules/readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", @@ -11384,7 +11396,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "devOptional": true + "optional": true }, "node_modules/simple-concat": { "version": "1.0.1", @@ -11393,40 +11405,30 @@ "dev": true }, "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "decompress-response": "^4.2.0", + "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, - "node_modules/simple-get/node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/simple-get/node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -12105,7 +12107,7 @@ "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -13337,7 +13339,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "devOptional": true, + "optional": true, "dependencies": { "string-width": "^1.0.2 || 2" } @@ -13751,6 +13753,12 @@ "jsesc": "^2.5.1" } }, + "@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true + }, "@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", @@ -13772,12 +13780,13 @@ } }, "@babel/types": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz", - "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } }, @@ -14393,12 +14402,6 @@ "readable-stream": "^3.6.0" } }, - "detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "optional": true - }, "gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -15362,7 +15365,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "devOptional": true + "optional": true }, "archiver": { "version": "5.3.2", @@ -15419,16 +15422,6 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, - "are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -15609,21 +15602,20 @@ } }, "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "minimalistic-assert": "^1.0.0" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -15869,9 +15861,9 @@ } }, "bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "dev": true }, "boolean": { @@ -16067,31 +16059,31 @@ } }, "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", "dev": true, "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.5", + "hash-base": "~3.0", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" }, "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "safe-buffer": { @@ -16706,7 +16698,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "devOptional": true + "optional": true }, "constants-browserify": { "version": "1.0.0", @@ -17155,7 +17147,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "devOptional": true + "optional": true }, "deps-sort": { "version": "2.0.1", @@ -17186,10 +17178,10 @@ "dev": true }, "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "devOptional": true }, "detect-newline": { "version": "2.1.0", @@ -17457,9 +17449,9 @@ } }, "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", + "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", "dev": true, "requires": { "bn.js": "^4.11.9", @@ -17596,14 +17588,15 @@ } }, "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" } }, "es6-error": { @@ -17869,6 +17862,26 @@ "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + } + } + }, "espree": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", @@ -18589,22 +18602,6 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, "get-assigned-identifiers": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", @@ -19194,7 +19191,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "devOptional": true + "optional": true }, "has-value": { "version": "1.0.0", @@ -20422,14 +20419,6 @@ "lru-queue": "^0.1.0", "next-tick": "^1.1.0", "timers-ext": "^0.1.7" - }, - "dependencies": { - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - } } }, "merge2": { @@ -20980,9 +20969,9 @@ "dev": true }, "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, "nise": { @@ -20999,20 +20988,12 @@ } }, "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "version": "3.61.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.61.0.tgz", + "integrity": "sha512-dYDO1rxzvMXjEMi37PBeFuYgwh3QZpsw/jt+qOmnRSwiV4z4c+OLoRlTa3V8ID4TrkSQpzCVc9OI2sstFaINfQ==", "dev": true, "requires": { - "semver": "^5.4.1" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } + "semver": "^7.3.5" } }, "node-addon-api": { @@ -21086,18 +21067,6 @@ "once": "^1.3.2" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, "nseventmonitor": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/nseventmonitor/-/nseventmonitor-1.0.4.tgz", @@ -21369,16 +21338,35 @@ } }, "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", "dev": true, "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "dependencies": { + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } } }, "parse-filepath": { @@ -21538,9 +21526,9 @@ } }, "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -21590,14 +21578,14 @@ } }, "pkg": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.0.tgz", - "integrity": "sha512-8h9PUDYFi+LOMLbIyGRdP21g08mAtHidSpofSrf8LWhxUWGHymaRzcopEGiynB5EhQmZUKM6PQ9kCImV2TpdjQ==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.1.tgz", + "integrity": "sha512-CjBWtFStCfIiT4Bde9QpJy0KeH19jCfwZRJqHFDFXfhUklCx8JoFmMj3wgnEYIwGmZVNkhsStPHEOnrtrQhEXA==", "dev": true, "requires": { "@babel/generator": "7.18.2", "@babel/parser": "7.18.4", - "@babel/types": "7.18.4", + "@babel/types": "7.19.0", "chalk": "^4.1.2", "fs-extra": "^9.1.0", "globby": "^11.1.0", @@ -21606,7 +21594,7 @@ "minimist": "^1.2.6", "multistream": "^4.1.0", "pkg-fetch": "3.4.2", - "prebuild-install": "6.1.4", + "prebuild-install": "7.1.1", "resolve": "^1.22.0", "stream-meter": "^1.0.4" }, @@ -21826,22 +21814,21 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", "dev": true, "requires": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", + "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", - "simple-get": "^3.0.3", + "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" } @@ -22182,9 +22169,9 @@ } }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -22677,7 +22664,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "devOptional": true + "optional": true }, "simple-concat": { "version": "1.0.1", @@ -22686,31 +22673,14 @@ "dev": true }, "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "dev": true, "requires": { - "decompress-response": "^4.2.0", + "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" - }, - "dependencies": { - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true - } } }, "simple-update-notifier": { @@ -23277,7 +23247,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true }, "styled-components": { @@ -24245,7 +24215,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "devOptional": true, + "optional": true, "requires": { "string-width": "^1.0.2 || 2" } From 0bd70fbeac85492503592202fd118d6e90199769 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Wed, 24 Apr 2024 12:08:05 +0200 Subject: [PATCH 159/214] Add guard to prevent running preinstall and postinstall a second time --- dist-assets/pkg-scripts/postinstall | 12 ++++++++++-- dist-assets/pkg-scripts/preinstall | 11 ++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/dist-assets/pkg-scripts/postinstall b/dist-assets/pkg-scripts/postinstall index d4c42a605912..9ef4822b5f08 100755 --- a/dist-assets/pkg-scripts/postinstall +++ b/dist-assets/pkg-scripts/postinstall @@ -2,6 +2,16 @@ set -eu +INSTALL_DIR=$2 + +# Workaround for issue in electron-builder where the pkg-scripts are run twice, once with the +# correct install dir and once with an incorrect one. This guard prevents running the script when +# called the second time. This can be reverted when the following issue has been fixed: +# https://github.com/electron-userland/electron-builder/issues/8166 +if [[ $INSTALL_DIR == *"Mullvad VPN.app" ]]; then + exit 0 +fi + LOG_DIR=/var/log/mullvad-vpn mkdir -p $LOG_DIR @@ -9,8 +19,6 @@ exec > $LOG_DIR/postinstall.log 2>&1 echo "Running postinstall at $(date)" -INSTALL_DIR=$2 - # Run CLI to force macOS to check the certificate, and shut down the already running daemon, if one # exists. # This is a temporary workaround. After 2023.3, we switched from signing with Amagicom AB to diff --git a/dist-assets/pkg-scripts/preinstall b/dist-assets/pkg-scripts/preinstall index c7b1cf13fb36..1b61ff3ed518 100755 --- a/dist-assets/pkg-scripts/preinstall +++ b/dist-assets/pkg-scripts/preinstall @@ -2,9 +2,18 @@ set -eux -LOG_DIR=/var/log/mullvad-vpn INSTALL_DIR=$2 +# Workaround for issue in electron-builder where the pkg-scripts are run twice, once with the +# correct install dir and once with an incorrect one. This guard prevents running the script when +# called the second time. This can be reverted when the following issue has been fixed: +# https://github.com/electron-userland/electron-builder/issues/8166 +if [[ $INSTALL_DIR == *"Mullvad VPN.app" ]]; then + exit 0 +fi + +LOG_DIR=/var/log/mullvad-vpn + mkdir -p $LOG_DIR chmod 755 $LOG_DIR exec > $LOG_DIR/preinstall.log 2>&1 From c94fc08e0a9d65953f1866ed4463009b7d63d2d9 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Wed, 24 Apr 2024 15:00:49 +0200 Subject: [PATCH 160/214] Fix notarization issues after upgrading electron-builder to 24.13.3 To fix issues with electron-builder and @electron/notarize this commit: * Replaces old notarization code with built-in solution in electron-builder * Moves notarization of pkg to build.sh --- build.sh | 12 ++++++++++++ gui/package-lock.json | 1 - gui/package.json | 1 - gui/tasks/distribution.js | 22 +--------------------- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/build.sh b/build.sh index 75a62755fbcf..1329c2cf5ca6 100755 --- a/build.sh +++ b/build.sh @@ -354,6 +354,18 @@ if [[ "$SIGN" == "true" && "$(uname -s)" == "MINGW"* ]]; then done fi +# notarize installer on macOS +if [[ "$NOTARIZE" == "true" && "$(uname -s)" == "Darwin" ]]; then + log_info "Notarizing pkg" + xcrun notarytool submit dist/*"$PRODUCT_VERSION"*.pkg \ + --keychain "$NOTARIZE_KEYCHAIN" \ + --keychain-profile "$NOTARIZE_KEYCHAIN_PROFILE" \ + --wait + + log_info "Stapling pkg" + xcrun stapler staple dist/*"$PRODUCT_VERSION"*.pkg +fi + log_success "**********************************" log_success "" log_success " The build finished successfully! " diff --git a/gui/package-lock.json b/gui/package-lock.json index 47d1447d57dd..dc8686cd428c 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -25,7 +25,6 @@ "styled-components": "^6.1.0" }, "devDependencies": { - "@electron/notarize": "^2.1.0", "@playwright/test": "^1.41.1", "@types/chai": "^4.3.3", "@types/chai-as-promised": "^7.1.5", diff --git a/gui/package.json b/gui/package.json index 92093b842ddb..75892af5ce32 100644 --- a/gui/package.json +++ b/gui/package.json @@ -31,7 +31,6 @@ "nseventmonitor": "^1.0.4" }, "devDependencies": { - "@electron/notarize": "^2.1.0", "@playwright/test": "^1.41.1", "@types/chai": "^4.3.3", "@types/chai-as-promised": "^7.1.5", diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index 7087d71301ed..ada829057bd4 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -2,7 +2,6 @@ const path = require('path'); const fs = require('fs'); const builder = require('electron-builder'); const { Arch } = require('electron-builder'); -const { notarize } = require('@electron/notarize'); const { execFileSync } = require('child_process'); const noCompression = process.argv.includes('--no-compression'); @@ -88,6 +87,7 @@ const config = { artifactName: 'MullvadVPN-${version}.${ext}', category: 'public.app-category.tools', icon: distAssets('icon-macos.icns'), + notarize: shouldNotarize, extendInfo: { LSUIElement: true, NSUserNotificationAlertStyle: 'banner', @@ -315,11 +315,6 @@ function packMac() { return Promise.resolve(); }, afterAllArtifactBuild: async (buildResult) => { - if (shouldNotarize) { - // buildResult.artifactPaths[0] contains the path to the pkg. - await notarizeMac(buildResult.artifactPaths[0]); - } - // Remove the folder that contains the unpacked app. Electron builder cleans up some of // these directories and it's changed between versions without a mention in the changelog. for (const dir of appOutDirs) { @@ -331,26 +326,11 @@ function packMac() { afterSign: (context) => { const appOutDir = context.appOutDir; appOutDirs.push(appOutDir); - - if (shouldNotarize) { - const appName = context.packager.appInfo.productFilename; - return notarizeMac(path.join(appOutDir, `${appName}.app`)); - } }, }, }); } -function notarizeMac(notarizePath) { - console.log('Notarizing ' + notarizePath); - return notarize({ - appBundleId: config.appId, - appPath: notarizePath, - keychain: process.env.NOTARIZE_KEYCHAIN, - keychainProfile: process.env.NOTARIZE_KEYCHAIN_PROFILE, - }); -} - function packLinux() { if (noCompression) { config.rpm.fpm.unshift('--rpm-compression', 'none'); From 59ab06f532541d2327a86f34428fe820889febb0 Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Wed, 24 Apr 2024 09:28:28 +0200 Subject: [PATCH 161/214] Fix iPad location selection entries not being unmarked as selected --- .../SelectLocation/LocationDataSource.swift | 24 +++++++++++++------ .../LocationViewController.swift | 6 ++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift index 3272f0e65b4e..d6460343e04a 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationDataSource.swift @@ -136,6 +136,12 @@ final class LocationDataSource: ], reloadExisting: true) } + func scrollToSelectedRelay() { + indexPathForSelectedRelay().flatMap { + tableView.scrollToRow(at: $0, at: .middle, animated: false) + } + } + // Called from `LocationDiffableDataSourceProtocol`. func nodeShowsChildren(_ node: LocationNode) -> Bool { node.showsChildren @@ -277,12 +283,22 @@ extension LocationDataSource: UITableViewDelegate { func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if let item = itemIdentifier(for: indexPath), item == selectedItem { - cell.setSelected(true, animated: false) + tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) } } + func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { + if let indexPath = indexPathForSelectedRelay() { + tableView.deselectRow(at: indexPath, animated: false) + selectedItem = nil + } + + return indexPath + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let item = itemIdentifier(for: indexPath) else { return } + selectedItem = item var customListSelection: UserSelectedRelays.CustomListSelection? if let topmostNode = item.node.root as? CustomListLocationNode { @@ -303,12 +319,6 @@ extension LocationDataSource: UITableViewDelegate { private func scrollToTop(animated: Bool) { tableView.setContentOffset(.zero, animated: animated) } - - private func scrollToSelectedRelay() { - indexPathForSelectedRelay().flatMap { - tableView.scrollToRow(at: $0, at: .middle, animated: false) - } - } } extension LocationDataSource: LocationCellDelegate { diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift index 11b5c0bbb70c..99f7f514889a 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift @@ -96,9 +96,13 @@ final class LocationViewController: UIViewController { } } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + dataSource?.scrollToSelectedRelay() + } + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - tableView.flashScrollIndicators() } From dbc13918b1398ea43c9ccc80165f37ff627390ed Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 17 Apr 2024 16:22:58 +0200 Subject: [PATCH 162/214] Add a test to delete, edit and create a custom list --- ios/MullvadVPN.xcodeproj/project.pbxproj | 26 ++- .../Classes/AccessbilityIdentifier.swift | 19 +- .../AddLocationsViewController.swift | 3 +- .../CustomListCellConfiguration.swift | 3 + .../CustomListViewController.swift | 7 +- .../ListCustomListViewController.swift | 4 + .../Coordinators/LocationCoordinator.swift | 7 +- .../SelectLocation/LocationCell.swift | 1 + .../LocationSectionHeaderView.swift | 5 +- .../LocationViewController.swift | 1 + ios/MullvadVPNUITests/CustomListsTests.swift | 173 ++++++++++++++++++ .../Pages/CustomListPage.swift | 59 ++++++ .../Pages/EditCustomListLocationsPage.swift | 53 ++++++ .../Pages/ListCustomListsPage.swift | 33 ++++ ios/MullvadVPNUITests/Pages/Page.swift | 5 + .../Pages/SelectLocationPage.swift | 44 +++++ .../XCUIElementQuery+Extensions.swift | 4 + 17 files changed, 435 insertions(+), 12 deletions(-) create mode 100644 ios/MullvadVPNUITests/CustomListsTests.swift create mode 100644 ios/MullvadVPNUITests/Pages/CustomListPage.swift create mode 100644 ios/MullvadVPNUITests/Pages/EditCustomListLocationsPage.swift create mode 100644 ios/MullvadVPNUITests/Pages/ListCustomListsPage.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index f178e1cdcd7f..fc6766d83f29 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -703,6 +703,8 @@ A988A3E22AFE54AC0008D2C7 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; }; A988DF272ADE86ED00D807EF /* WireGuardObfuscationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */; }; A988DF2A2ADE880300D807EF /* TunnelSettingsV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */; }; + A998DA812BD147AD001D61A2 /* ListCustomListsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A998DA802BD147AD001D61A2 /* ListCustomListsPage.swift */; }; + A998DA832BD2B055001D61A2 /* EditCustomListLocationsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A998DA822BD2B055001D61A2 /* EditCustomListLocationsPage.swift */; }; A99E5EE02B7628150033F241 /* ProblemReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A99E5EDF2B7628150033F241 /* ProblemReportViewModel.swift */; }; A99E5EE22B762ED30033F241 /* ProblemReportViewController+ViewManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = A99E5EE12B762ED30033F241 /* ProblemReportViewController+ViewManagement.swift */; }; A9A1DE792AD5708E0073F689 /* TransportStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A1DE782AD5708E0073F689 /* TransportStrategy.swift */; }; @@ -808,6 +810,8 @@ A9B6AC1B2ADEA3AD00F7802A /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BDEB9C2A98F69E00F578F2 /* MemoryCache.swift */; }; A9BA08312BA32FA9005A7A2D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A92962582B1F4FDB00DFB93B /* PrivacyInfo.xcprivacy */; }; A9BA08322BA32FB6005A7A2D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A92962582B1F4FDB00DFB93B /* PrivacyInfo.xcprivacy */; }; + A9BFAFFF2BD004ED00F2BCA1 /* CustomListsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BFAFFE2BD004ED00F2BCA1 /* CustomListsTests.swift */; }; + A9BFB0012BD00B7F00F2BCA1 /* CustomListPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */; }; A9C342C12ACC37E30045F00E /* TunnelStatusBlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */; }; A9C342C32ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C342C22ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift */; }; A9C342C52ACC42130045F00E /* ServerRelaysResponse+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C342C42ACC42130045F00E /* ServerRelaysResponse+Stubs.swift */; }; @@ -1966,6 +1970,8 @@ A98502022B627B120061901E /* LocalNetworkProbe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNetworkProbe.swift; sourceTree = ""; }; A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuardObfuscationSettings.swift; sourceTree = ""; }; A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV3.swift; sourceTree = ""; }; + A998DA802BD147AD001D61A2 /* ListCustomListsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCustomListsPage.swift; sourceTree = ""; }; + A998DA822BD2B055001D61A2 /* EditCustomListLocationsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCustomListLocationsPage.swift; sourceTree = ""; }; A99E5EDF2B7628150033F241 /* ProblemReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportViewModel.swift; sourceTree = ""; }; A99E5EE12B762ED30033F241 /* ProblemReportViewController+ViewManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProblemReportViewController+ViewManagement.swift"; sourceTree = ""; }; A9A1DE782AD5708E0073F689 /* TransportStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportStrategy.swift; sourceTree = ""; }; @@ -1973,6 +1979,8 @@ A9A8A8EA2A262AB30086D569 /* FileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCache.swift; sourceTree = ""; }; A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationManagerTests.swift; sourceTree = ""; }; A9B6AC192ADE8FBB00F7802A /* InMemorySettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemorySettingsStore.swift; sourceTree = ""; }; + A9BFAFFE2BD004ED00F2BCA1 /* CustomListsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsTests.swift; sourceTree = ""; }; + A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListPage.swift; sourceTree = ""; }; A9C342C22ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RelayCacheTracker+Stubs.swift"; sourceTree = ""; }; A9C342C42ACC42130045F00E /* ServerRelaysResponse+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ServerRelaysResponse+Stubs.swift"; sourceTree = ""; }; A9CF11FC2A0518E7001D9565 /* AddressCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheTests.swift; sourceTree = ""; }; @@ -3623,19 +3631,20 @@ 852969262B4D9C1F007EAD4C /* MullvadVPNUITests */ = { isa = PBXGroup; children = ( - 85B267602B849ADB0098E3CD /* mullvad-api.h */, 852969272B4D9C1F007EAD4C /* AccountTests.swift */, 8556EB532B9A1D7100D26DD4 /* BridgingHeader.h */, 85557B112B594FC900795FE1 /* ConnectivityTests.swift */, + A9BFAFFE2BD004ED00F2BCA1 /* CustomListsTests.swift */, 852969372B4ED20E007EAD4C /* Info.plist */, + 85B267602B849ADB0098E3CD /* mullvad-api.h */, 85557B0C2B591B0F00795FE1 /* Networking */, 852969312B4E9220007EAD4C /* Pages */, 850201DA2B503D7700EF8C96 /* RelayTests.swift */, + 85D039972BA4711800940E7F /* SettingsMigrationTests.swift */, + 85C7A2E82B89024B00035D5A /* SettingsTests.swift */, 8518F6392B601910009EB113 /* Test base classes */, - 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */, 85A42B852BB1D627007BABF7 /* XCUIElement+Extensions.swift */, - 85C7A2E82B89024B00035D5A /* SettingsTests.swift */, - 85D039972BA4711800940E7F /* SettingsMigrationTests.swift */, + 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */, ); path = MullvadVPNUITests; sourceTree = ""; @@ -3647,9 +3656,12 @@ 85557B1F2B5FBBD700795FE1 /* AccountPage.swift */, 8529693B2B4F0257007EAD4C /* Alert.swift */, 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */, - 852A26452BA9C9CB006EB9C8 /* DNSSettingsPage.swift */, + A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */, 85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */, + 852A26452BA9C9CB006EB9C8 /* DNSSettingsPage.swift */, + A998DA822BD2B055001D61A2 /* EditCustomListLocationsPage.swift */, 85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */, + A998DA802BD147AD001D61A2 /* ListCustomListsPage.swift */, 852969342B4E9270007EAD4C /* LoginPage.swift */, 85139B2C2B84B4A700734217 /* OutOfTimePage.swift */, 852969322B4E9232007EAD4C /* Page.swift */, @@ -5639,6 +5651,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A9BFB0012BD00B7F00F2BCA1 /* CustomListPage.swift in Sources */, 8556EB522B9A1C6900D26DD4 /* MullvadApi.swift in Sources */, 85EC620C2B838D10005AFFB5 /* MullvadAPIWrapper.swift in Sources */, A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */, @@ -5655,7 +5668,9 @@ 8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */, 85557B202B5FBBD700795FE1 /* AccountPage.swift in Sources */, 852969352B4E9270007EAD4C /* LoginPage.swift in Sources */, + A998DA832BD2B055001D61A2 /* EditCustomListLocationsPage.swift in Sources */, 8556EB562B9B0AC500D26DD4 /* RevokedDevicePage.swift in Sources */, + A9BFAFFF2BD004ED00F2BCA1 /* CustomListsTests.swift in Sources */, 85557B102B59215F00795FE1 /* FirewallRule.swift in Sources */, 85557B0E2B591B2600795FE1 /* FirewallAPIClient.swift in Sources */, 852969282B4D9C1F007EAD4C /* AccountTests.swift in Sources */, @@ -5668,6 +5683,7 @@ 8532E6872B8CCED600ACECD1 /* ProblemReportSubmittedPage.swift in Sources */, 85FB5A0C2B6903990015DCED /* WelcomePage.swift in Sources */, 852A26462BA9C9CB006EB9C8 /* DNSSettingsPage.swift in Sources */, + A998DA812BD147AD001D61A2 /* ListCustomListsPage.swift in Sources */, 850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */, 8542CE242B95F7B9006FCA14 /* VPNSettingsPage.swift in Sources */, 85557B1E2B5FB8C700795FE1 /* HeaderBar.swift in Sources */, diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index 174408f0afd8..9be9248b7d2b 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -37,12 +37,21 @@ public enum AccessibilityIdentifier: String { case restorePurchasesButton case secureConnectionButton case selectLocationButton + case closeSelectLocationButton case settingsButton case startUsingTheAppButton case problemReportAppLogsButton case problemReportSendButton case relayStatusCollapseButton case settingsDoneButton + case openCustomListsMenuButton + case addNewCustomListButton + case editCustomListButton + case saveCreateCustomListButton + case confirmDeleteCustomListButton + case cancelDeleteCustomListButton + case customListLocationCheckmarkButton + case listCustomListDoneButton // Cells case deviceCell @@ -60,6 +69,9 @@ public enum AccessibilityIdentifier: String { case wireGuardObfuscationCell case udpOverTCPPortCell case quantumResistantTunnelCell + case customListEditNameFieldCell + case customListEditAddOrEditLocationCell + case customListEditDeleteListCell // Labels case accountPagePaidUntilLabel @@ -71,7 +83,6 @@ public enum AccessibilityIdentifier: String { // Views case accountView - case addLocationsView case alertContainerView case alertTitle case changeLogAlert @@ -91,6 +102,12 @@ public enum AccessibilityIdentifier: String { case welcomeView case deleteAccountView case settingsContainerView + case newCustomListView + case customListEditTableView + case listCustomListsView + case listCustomListsTableView + case editCustomListEditLocationsView + case editCustomListEditLocationsTableView // Other UI elements case connectionPanelInAddressRow diff --git a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift index 1dbd7ac7ae8d..06245b5c90a3 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift @@ -27,7 +27,7 @@ class AddLocationsViewController: UIViewController { tableView.separatorInset = .zero tableView.rowHeight = 56 tableView.indicatorStyle = .white - tableView.accessibilityIdentifier = .addLocationsView + tableView.accessibilityIdentifier = .editCustomListEditLocationsTableView return tableView }() @@ -46,6 +46,7 @@ class AddLocationsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.accessibilityIdentifier = .editCustomListEditLocationsView tableView.backgroundColor = view.backgroundColor view.backgroundColor = .secondaryColor addConstraints() diff --git a/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift b/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift index 4731a74cc890..10f2c3ef7cb5 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/CustomListCellConfiguration.swift @@ -78,6 +78,7 @@ struct CustomListCellConfiguration { contentConfiguration.maxLength = 30 contentConfiguration.editingEvents.onChange = subject.bindTextAction(to: \.name) + cell.accessibilityIdentifier = AccessibilityIdentifier.customListEditNameFieldCell cell.contentConfiguration = contentConfiguration } @@ -86,6 +87,7 @@ struct CustomListCellConfiguration { contentConfiguration.text = itemIdentifier.text cell.contentConfiguration = contentConfiguration + cell.accessibilityIdentifier = AccessibilityIdentifier.customListEditAddOrEditLocationCell if let cell = cell as? CustomCellDisclosureHandling { cell.disclosureType = .chevron @@ -101,6 +103,7 @@ struct CustomListCellConfiguration { onDelete?() } + cell.accessibilityIdentifier = AccessibilityIdentifier.customListEditDeleteListCell cell.contentConfiguration = contentConfiguration } } diff --git a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift index a3a7518af8dc..baf4e8949a43 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/CustomListViewController.swift @@ -58,6 +58,7 @@ class CustomListViewController: UIViewController { } ) barButtonItem.style = .done + barButtonItem.accessibilityIdentifier = AccessibilityIdentifier.saveCreateCustomListButton return barButtonItem }() @@ -86,6 +87,7 @@ class CustomListViewController: UIViewController { navigationItem.rightBarButtonItem = saveBarButton view.directionalLayoutMargins = UIMetrics.contentLayoutMargins view.backgroundColor = .secondaryColor + view.accessibilityIdentifier = .newCustomListView isModalInPresentation = true addSubviews() @@ -102,6 +104,7 @@ class CustomListViewController: UIViewController { tableView.delegate = dataSourceConfiguration tableView.backgroundColor = .secondaryColor tableView.registerReusableViews(from: CustomListItemIdentifier.CellIdentifier.self) + tableView.accessibilityIdentifier = AccessibilityIdentifier.customListEditTableView } private func configureDataSource() { @@ -180,6 +183,7 @@ class CustomListViewController: UIViewController { comment: "" ), style: .destructive, + accessibilityId: .confirmDeleteCustomListButton, handler: { self.interactor.delete(id: self.subject.value.id) self.delegate?.customListDidDelete(self.subject.value.customList) @@ -192,7 +196,8 @@ class CustomListViewController: UIViewController { value: "Cancel", comment: "" ), - style: .default + style: .default, + accessibilityId: .cancelDeleteCustomListButton ), ] ) diff --git a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift index 74b185169ca3..3295124592d7 100644 --- a/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift +++ b/ios/MullvadVPN/Coordinators/CustomLists/ListCustomListViewController.swift @@ -50,6 +50,7 @@ class ListCustomListViewController: UIViewController { super.viewDidLoad() view.backgroundColor = .secondaryColor + view.accessibilityIdentifier = .listCustomListsView addSubviews() configureNavigationItem() @@ -88,6 +89,7 @@ class ListCustomListViewController: UIViewController { tableView.separatorStyle = .singleLine tableView.rowHeight = UIMetrics.SettingsCell.customListsCellHeight tableView.registerReusableViews(from: CellReuseIdentifier.self) + tableView.accessibilityIdentifier = .listCustomListsTableView } private func configureNavigationItem() { @@ -104,6 +106,8 @@ class ListCustomListViewController: UIViewController { self?.didFinish?() }) ) + + navigationItem.rightBarButtonItem?.accessibilityIdentifier = .listCustomListDoneButton } private func configureDataSource() { diff --git a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift index 30fe23d8e3fa..3ccfbd1c70b9 100644 --- a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift @@ -200,7 +200,7 @@ extension LocationCoordinator: LocationViewControllerDelegate { actionSheet.overrideUserInterfaceStyle = .dark actionSheet.view.tintColor = UIColor(red: 0.0, green: 0.59, blue: 1.0, alpha: 1) - actionSheet.addAction(UIAlertAction( + let addCustomListAction = UIAlertAction( title: NSLocalizedString( "CUSTOM_LIST_ACTION_SHEET_ADD_LIST_BUTTON", tableName: "CustomLists", @@ -211,7 +211,9 @@ extension LocationCoordinator: LocationViewControllerDelegate { handler: { [weak self] _ in self?.showAddCustomList(nodes: nodes) } - )) + ) + addCustomListAction.accessibilityIdentifier = AccessibilityIdentifier.addNewCustomListButton + actionSheet.addAction(addCustomListAction) let editAction = UIAlertAction( title: NSLocalizedString( "CUSTOM_LIST_ACTION_SHEET_EDIT_LISTS_BUTTON", @@ -225,6 +227,7 @@ extension LocationCoordinator: LocationViewControllerDelegate { } ) editAction.isEnabled = !customListRepository.fetchAll().isEmpty + editAction.accessibilityIdentifier = AccessibilityIdentifier.editCustomListButton actionSheet.addAction(editAction) diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift index 63ff5bcaeede..ba4de11c17ed 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationCell.swift @@ -314,6 +314,7 @@ extension LocationCell { locationLabel.text = item.node.name showsCollapseControl = !item.node.children.isEmpty isExpanded = item.node.showsChildren + checkboxButton.accessibilityIdentifier = .customListLocationCheckmarkButton checkboxButton.isSelected = item.isSelected checkboxButton.tintColor = item.isSelected ? .successColor : .white diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift index 220df6323df1..c84fea9097af 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationSectionHeaderView.swift @@ -16,9 +16,8 @@ class LocationSectionHeaderView: UIView, UIContentView { } set { guard let newConfiguration = newValue as? Configuration, actualConfiguration != newConfiguration else { return } - let previousConfiguration = actualConfiguration actualConfiguration = newConfiguration - apply(configuration: previousConfiguration) + apply(configuration: newConfiguration) } } @@ -66,7 +65,9 @@ class LocationSectionHeaderView: UIView, UIContentView { let isActionHidden = configuration.primaryAction == nil nameLabel.text = configuration.name actionButton.isHidden = isActionHidden + actionButton.accessibilityIdentifier = nil actualConfiguration.primaryAction.flatMap { [weak self] action in + self?.actionButton.accessibilityIdentifier = .openCustomListsMenuButton self?.actionButton.addAction(action, for: .touchUpInside) } } diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift index 99f7f514889a..6d82f5e1b92e 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift @@ -83,6 +83,7 @@ final class LocationViewController: UIViewController { self?.didFinish?() }) ) + navigationItem.rightBarButtonItem?.accessibilityIdentifier = .closeSelectLocationButton setUpDataSources() setUpTableView() diff --git a/ios/MullvadVPNUITests/CustomListsTests.swift b/ios/MullvadVPNUITests/CustomListsTests.swift new file mode 100644 index 000000000000..eb579b1b5546 --- /dev/null +++ b/ios/MullvadVPNUITests/CustomListsTests.swift @@ -0,0 +1,173 @@ +// +// CustomListsTests.swift +// MullvadVPNUITests +// +// Created by Marco Nikic on 2024-04-17. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import XCTest + +class CustomListsTests: LoggedInWithTimeUITestCase { + func testCreateCustomListPersistAfterAppRestarts() throws { + TunnelControlPage(app) + .tapSelectLocationButton() + + let customListName = createCustomListName() + createCustomList(named: customListName) + // Custom lists are persisted across app sessions, guarantee that the next test starts in a clean state + addTeardownBlock { + self.deleteCustomList(named: customListName) + } + + app.terminate() + app.launch() + + TunnelControlPage(app) + .tapSelectLocationButton() + + SelectLocationPage(app) + .tapWhereStatusBarShouldBeToScrollToTopMostPosition() + + XCTAssertTrue(app.staticTexts[customListName].exists) + } + + func testDeleteCustomList() throws { + TunnelControlPage(app) + .tapSelectLocationButton() + + let customListName = createCustomListName() + createCustomList(named: customListName) + workaroundOpenCustomListMenuBug() + deleteCustomList(named: customListName) + + SelectLocationPage(app) + .tapWhereStatusBarShouldBeToScrollToTopMostPosition() + + XCTAssertFalse(app.staticTexts[customListName].exists) + } + + func testEditCustomListLocations() throws { + TunnelControlPage(app) + .tapSelectLocationButton() + + let customListName = createCustomListName() + createCustomList(named: customListName) + + addTeardownBlock { + self.workaroundOpenCustomListMenuBug() + self.deleteCustomList(named: customListName) + } + + workaroundOpenCustomListMenuBug() + startEditingCustomList(named: customListName) + + EditCustomListLocationsPage(app) + .scrollToLocationWith(identifier: "se") + .toggleLocationCheckmarkWith(identifier: "se") + .pressBackButton() + + CustomListPage(app) + .tapSaveListButton() + + ListCustomListsPage(app) + .tapDoneButton() + + XCTAssertTrue(app.staticTexts[customListName].exists) + } + + func testAddSingleLocationToCustomList() throws { + TunnelControlPage(app) + .tapSelectLocationButton() + + let customListName = createCustomListName() + createCustomList(named: customListName) + + addTeardownBlock { + self.workaroundOpenCustomListMenuBug() + self.deleteCustomList(named: customListName) + } + + workaroundOpenCustomListMenuBug() + startEditingCustomList(named: customListName) + + EditCustomListLocationsPage(app) + .scrollToLocationWith(identifier: "se") + .unfoldLocationwith(identifier: "se") + .unfoldLocationwith(identifier: "se-got") + .toggleLocationCheckmarkWith(identifier: "se-got-wg-001") + .pressBackButton() + + CustomListPage(app) + .tapSaveListButton() + + ListCustomListsPage(app) + .tapDoneButton() + + SelectLocationPage(app) + .tapLocationCellExpandButton(withName: customListName) + let customListLocationName = "\(customListName)-se-got-wg-001" + let customListLocationCell = SelectLocationPage(app).cellWithIdentifier(identifier: customListLocationName) + XCTAssertTrue(customListLocationCell.exists) + } + + func createCustomList(named name: String) { + SelectLocationPage(app) + .tapWhereStatusBarShouldBeToScrollToTopMostPosition() + .tapCustomListEllipsisButton() + .tapAddNewCustomList() + + // When creating a new custom list, the "create" button should be disabled until the list has a name at minimum + CustomListPage(app) + .verifyCreateButtonIs(enabled: false) + .renameCustomList(name: name) + .verifyCreateButtonIs(enabled: true) + .tapCreateListButton() + } + + func workaroundOpenCustomListMenuBug() { + // In order to avoid a bug where the open custom list button cannot be found, the location view is closed and then reopened + SelectLocationPage(app) + .closeSelectLocationPage() + TunnelControlPage(app) + .tapSelectLocationButton() + } + + func startEditingCustomList(named customListName: String) { + SelectLocationPage(app) + .tapWhereStatusBarShouldBeToScrollToTopMostPosition() + .tapCustomListEllipsisButton() + .editExistingCustomLists() + + ListCustomListsPage(app) + .selectCustomListToEdit(named: customListName) + + CustomListPage(app) + .addOrEditLocations() + + EditCustomListLocationsPage(app) + } + + func deleteCustomList(named customListName: String) { + SelectLocationPage(app) + .tapWhereStatusBarShouldBeToScrollToTopMostPosition() + .tapCustomListEllipsisButton() + .editExistingCustomLists() + + ListCustomListsPage(app) + .selectCustomListToEdit(named: customListName) + + CustomListPage(app) + .deleteCustomList(named: customListName) + } + + /// Creates a unique name for a custom list + /// + /// The name will be used as an accessibility identifier + /// Those are lower case and case sensitive. + func createCustomListName() -> String { + let customListOriginalName = UUID().uuidString + let index = customListOriginalName.index(customListOriginalName.startIndex, offsetBy: 30) + return String(customListOriginalName.prefix(upTo: index)).lowercased() + } +} diff --git a/ios/MullvadVPNUITests/Pages/CustomListPage.swift b/ios/MullvadVPNUITests/Pages/CustomListPage.swift new file mode 100644 index 000000000000..17ebf363c343 --- /dev/null +++ b/ios/MullvadVPNUITests/Pages/CustomListPage.swift @@ -0,0 +1,59 @@ +// +// CustomListPage.swift +// MullvadVPNUITests +// +// Created by Marco Nikic on 2024-04-17. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import XCTest + +class CustomListPage: Page { + @discardableResult override init(_ app: XCUIApplication) { + super.init(app) + + self.pageAccessibilityIdentifier = .newCustomListView + waitForPageToBeShown() + } + + @discardableResult func verifyCreateButtonIs(enabled: Bool) -> Self { + let saveOrCreateButton = app.buttons[.saveCreateCustomListButton] + XCTAssertTrue(saveOrCreateButton.isEnabled == enabled) + return self + } + + @discardableResult func tapCreateListButton() -> Self { + let saveOrCreateButton = app.buttons[.saveCreateCustomListButton] + saveOrCreateButton.tap() + return self + } + + // It's the same button, the difference is just for semantics + @discardableResult func tapSaveListButton() -> Self { + tapCreateListButton() + } + + @discardableResult func renameCustomList(name: String) -> Self { + let editCustomListNameCell = app.cells[.customListEditNameFieldCell] + // Activate the text field + editCustomListNameCell.tap() + // Select the entire text with a triple tap + editCustomListNameCell.tap(withNumberOfTaps: 3, numberOfTouches: 1) + // Tap the "delete" key on the on-screen keyboard, the case is sensitive + app.keys["delete"].tap() + editCustomListNameCell.typeText(name) + return self + } + + @discardableResult func deleteCustomList(named customListName: String) -> Self { + let deleteCustomListCell = app.cells[.customListEditDeleteListCell] + deleteCustomListCell.tap() + app.buttons[.confirmDeleteCustomListButton].tap() + return self + } + + @discardableResult func addOrEditLocations() -> Self { + app.cells[.customListEditAddOrEditLocationCell].tap() + return self + } +} diff --git a/ios/MullvadVPNUITests/Pages/EditCustomListLocationsPage.swift b/ios/MullvadVPNUITests/Pages/EditCustomListLocationsPage.swift new file mode 100644 index 000000000000..08d3d6fa9fef --- /dev/null +++ b/ios/MullvadVPNUITests/Pages/EditCustomListLocationsPage.swift @@ -0,0 +1,53 @@ +// +// EditCustomListLocationsPage.swift +// MullvadVPNUITests +// +// Created by Marco Nikic on 2024-04-19. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import XCTest + +class EditCustomListLocationsPage: Page { + @discardableResult override init(_ app: XCUIApplication) { + super.init(app) + + self.pageAccessibilityIdentifier = .editCustomListEditLocationsView + waitForPageToBeShown() + } + + @discardableResult func scrollToLocationWith(identifier: String) -> Self { + let tableView = app.tables[.editCustomListEditLocationsTableView] + tableView.cells[identifier].tap() + return self + } + + @discardableResult func toggleLocationCheckmarkWith(identifier: String) -> Self { + let locationCell = app.tables[.editCustomListEditLocationsTableView].cells[identifier] + locationCell.buttons[.customListLocationCheckmarkButton].tap() + return self + } + + @discardableResult func unfoldLocationwith(identifier: String) -> Self { + let locationCell = app.tables[.editCustomListEditLocationsTableView].cells[identifier] + let expandCellButton = locationCell.buttons["expandButton"] + if expandCellButton.exists { + expandCellButton.tap() + } + return self + } + + @discardableResult func collapseLocationwith(identifier: String) -> Self { + let locationCell = app.tables[.editCustomListEditLocationsTableView].cells[identifier] + let collapseCellButton = locationCell.buttons["collapseButton"] + if collapseCellButton.exists { + collapseCellButton.tap() + } + return self + } + + @discardableResult func pressBackButton() -> Self { + app.navigationBars["Edit locations"].buttons.firstMatch.tap() + return self + } +} diff --git a/ios/MullvadVPNUITests/Pages/ListCustomListsPage.swift b/ios/MullvadVPNUITests/Pages/ListCustomListsPage.swift new file mode 100644 index 000000000000..2af421be7a20 --- /dev/null +++ b/ios/MullvadVPNUITests/Pages/ListCustomListsPage.swift @@ -0,0 +1,33 @@ +// +// ListCustomListsPage.swift +// MullvadVPNUITests +// +// Created by Marco Nikic on 2024-04-18. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import XCTest + +class ListCustomListsPage: Page { + @discardableResult override init(_ app: XCUIApplication) { + super.init(app) + + self.pageAccessibilityIdentifier = .listCustomListsView + waitForPageToBeShown() + } + + /// This function taps on a given custom list in the Edit Custom List page. + /// + /// This functions assumes that all the custom lists are visible on a single page + /// No scrolling will be attempted to scroll to find a custom list + /// - Parameter customListName: The custom list to edit + @discardableResult func selectCustomListToEdit(named customListName: String) -> Self { + app.tables[.listCustomListsTableView].staticTexts[customListName].tap() + return self + } + + @discardableResult func tapDoneButton() -> Self { + app.buttons[.listCustomListDoneButton].tap() + return self + } +} diff --git a/ios/MullvadVPNUITests/Pages/Page.swift b/ios/MullvadVPNUITests/Pages/Page.swift index 77fbebadaed6..9ac69f8ce093 100644 --- a/ios/MullvadVPNUITests/Pages/Page.swift +++ b/ios/MullvadVPNUITests/Pages/Page.swift @@ -46,4 +46,9 @@ class Page { app.toolbars.buttons["Done"].tap() return self } + + @discardableResult func tapWhereStatusBarShouldBeToScrollToTopMostPosition() -> Self { + app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0)).tap() + return self + } } diff --git a/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift b/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift index ad0ee06c4b37..a1f46b7ea875 100644 --- a/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift +++ b/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift @@ -33,8 +33,52 @@ class SelectLocationPage: Page { return self } + @discardableResult func tapLocationCellCollapseButton(withName name: String) -> Self { + let table = app.tables[AccessibilityIdentifier.selectLocationTableView] + let matchingCells = table.cells.containing(.any, identifier: name) + let buttons = matchingCells.buttons + let collapseButton = buttons[AccessibilityIdentifier.collapseButton] + + collapseButton.tap() + + return self + } + + @discardableResult func closeSelectLocationPage() -> Self { + let doneButton = app.buttons[.closeSelectLocationButton] + doneButton.tap() + return self + } + + @discardableResult func tapCustomListEllipsisButton() -> Self { + let customListEllipsisButton = app.buttons[AccessibilityIdentifier.openCustomListsMenuButton] + customListEllipsisButton.tap() + return self + } + + @discardableResult func tapAddNewCustomList() -> Self { + let addNewCustomListButton = app.buttons[AccessibilityIdentifier.addNewCustomListButton] + addNewCustomListButton.tap() + return self + } + + @discardableResult func editExistingCustomLists() -> Self { + let editCustomListsButton = app.buttons[AccessibilityIdentifier.editCustomListButton] + editCustomListsButton.tap() + return self + } + + @discardableResult func cellWithIdentifier(identifier: String) -> XCUIElement { + app.tables[AccessibilityIdentifier.selectLocationTableView].cells[identifier] + } + func locationCellIsExpanded(_ name: String) -> Bool { let matchingCells = app.cells.containing(.any, identifier: name) return matchingCells.buttons[AccessibilityIdentifier.expandButton].exists ? false : true } + + func verifyEditCustomListsButtonIs(enabled: Bool) { + let editCustomListsButton = app.buttons[AccessibilityIdentifier.editCustomListButton] + XCTAssertTrue(editCustomListsButton.isEnabled == enabled) + } } diff --git a/ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift b/ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift index 8500d7c08c2c..509ec7d0daa2 100644 --- a/ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift +++ b/ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift @@ -13,4 +13,8 @@ extension XCUIElementQuery { subscript(key: any RawRepresentable) -> XCUIElement { self[key.rawValue] } + + subscript(key: AccessibilityIdentifier) -> XCUIElement { + self[key.rawValue] + } } From 360ab162380360a29ad0aca19a07da9c930f9d80 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Tue, 16 Apr 2024 10:09:07 +0200 Subject: [PATCH 163/214] Update relay-locations.pot --- gui/locales/relay-locations.pot | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/gui/locales/relay-locations.pot b/gui/locales/relay-locations.pot index 48c7ae1e2487..8d6a8d382a6d 100644 --- a/gui/locales/relay-locations.pot +++ b/gui/locales/relay-locations.pot @@ -38,6 +38,14 @@ msgstr "" msgid "Austria" msgstr "" +#. TH BKK +msgid "Bangkok" +msgstr "" + +#. ES BCN +msgid "Barcelona" +msgstr "" + #. BE msgid "Belgium" msgstr "" @@ -178,6 +186,10 @@ msgstr "" msgid "Germany" msgstr "" +#. GB GLW +msgid "Glasgow" +msgstr "" + #. SE GOT msgid "Gothenburg" msgstr "" @@ -254,6 +266,10 @@ msgstr "" msgid "Lisbon" msgstr "" +#. SI LJU +msgid "Ljubljana" +msgstr "" + #. GB LON msgid "London" msgstr "" @@ -286,6 +302,10 @@ msgstr "" msgid "Marseille" msgstr "" +#. US TXC +msgid "McAllen, TX" +msgstr "" + #. AU MEL msgid "Melbourne" msgstr "" @@ -462,6 +482,10 @@ msgstr "" msgid "Slovakia" msgstr "" +#. SI +msgid "Slovenia" +msgstr "" + #. BG SOF msgid "Sofia" msgstr "" @@ -510,6 +534,10 @@ msgstr "" msgid "Tel Aviv" msgstr "" +#. TH +msgid "Thailand" +msgstr "" + #. AL TIA msgid "Tirana" msgstr "" @@ -538,6 +566,10 @@ msgstr "" msgid "United Arab Emirates" msgstr "" +#. ES VLC +msgid "Valencia" +msgstr "" + #. CA VAN msgid "Vancouver" msgstr "" From 9a3acaddb00f1d5e86f2c6514a0cc7010cc02224 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 25 Apr 2024 10:40:03 +0200 Subject: [PATCH 164/214] Download translations --- .../src/main/res/values-da/plurals.xml | 4 + .../src/main/res/values-da/strings.xml | 52 +++- .../src/main/res/values-de/plurals.xml | 4 + .../src/main/res/values-de/strings.xml | 50 ++++ .../src/main/res/values-es/plurals.xml | 4 + .../src/main/res/values-es/strings.xml | 52 +++- .../src/main/res/values-fi/plurals.xml | 4 + .../src/main/res/values-fi/strings.xml | 50 ++++ .../src/main/res/values-fr/plurals.xml | 4 + .../src/main/res/values-fr/strings.xml | 50 ++++ .../src/main/res/values-it/plurals.xml | 4 + .../src/main/res/values-it/strings.xml | 52 +++- .../src/main/res/values-ja/plurals.xml | 3 + .../src/main/res/values-ja/strings.xml | 52 +++- .../src/main/res/values-ko/plurals.xml | 3 + .../src/main/res/values-ko/strings.xml | 50 ++++ .../src/main/res/values-my/plurals.xml | 3 + .../src/main/res/values-my/strings.xml | 52 +++- .../src/main/res/values-nb/plurals.xml | 4 + .../src/main/res/values-nb/strings.xml | 50 ++++ .../src/main/res/values-nl/plurals.xml | 4 + .../src/main/res/values-nl/strings.xml | 50 ++++ .../src/main/res/values-pl/plurals.xml | 6 + .../src/main/res/values-pl/strings.xml | 52 +++- .../src/main/res/values-pt/plurals.xml | 4 + .../src/main/res/values-pt/strings.xml | 52 +++- .../src/main/res/values-ru/plurals.xml | 6 + .../src/main/res/values-ru/strings.xml | 50 ++++ .../src/main/res/values-sv/plurals.xml | 4 + .../src/main/res/values-sv/strings.xml | 50 ++++ .../src/main/res/values-th/plurals.xml | 3 + .../src/main/res/values-th/strings.xml | 52 +++- .../src/main/res/values-tr/plurals.xml | 4 + .../src/main/res/values-tr/strings.xml | 52 +++- .../src/main/res/values-zh-rCN/plurals.xml | 3 + .../src/main/res/values-zh-rCN/strings.xml | 50 ++++ .../src/main/res/values-zh-rTW/plurals.xml | 3 + .../src/main/res/values-zh-rTW/strings.xml | 52 +++- gui/locales/da/messages.po | 232 ++++++++++++++++- gui/locales/da/relay-locations.po | 34 ++- gui/locales/de/messages.po | 230 ++++++++++++++++- gui/locales/de/relay-locations.po | 34 ++- gui/locales/es/messages.po | 232 ++++++++++++++++- gui/locales/es/relay-locations.po | 34 ++- gui/locales/fi/messages.po | 230 ++++++++++++++++- gui/locales/fi/relay-locations.po | 34 ++- gui/locales/fr/messages.po | 230 ++++++++++++++++- gui/locales/fr/relay-locations.po | 34 ++- gui/locales/it/messages.po | 232 ++++++++++++++++- gui/locales/it/relay-locations.po | 34 ++- gui/locales/ja/messages.po | 231 ++++++++++++++++- gui/locales/ja/relay-locations.po | 34 ++- gui/locales/ko/messages.po | 229 ++++++++++++++++- gui/locales/ko/relay-locations.po | 34 ++- gui/locales/my/messages.po | 231 ++++++++++++++++- gui/locales/my/relay-locations.po | 34 ++- gui/locales/nb/messages.po | 230 ++++++++++++++++- gui/locales/nb/relay-locations.po | 34 ++- gui/locales/nl/messages.po | 230 ++++++++++++++++- gui/locales/nl/relay-locations.po | 34 ++- gui/locales/pl/messages.po | 234 ++++++++++++++++- gui/locales/pl/relay-locations.po | 34 ++- gui/locales/pt/messages.po | 232 ++++++++++++++++- gui/locales/pt/relay-locations.po | 34 ++- gui/locales/ru/messages.po | 232 ++++++++++++++++- gui/locales/ru/relay-locations.po | 34 ++- gui/locales/sv/messages.po | 230 ++++++++++++++++- gui/locales/sv/relay-locations.po | 34 ++- gui/locales/th/messages.po | 235 +++++++++++++++++- gui/locales/th/relay-locations.po | 34 ++- gui/locales/tr/messages.po | 232 ++++++++++++++++- gui/locales/tr/relay-locations.po | 34 ++- gui/locales/zh-CN/messages.po | 229 ++++++++++++++++- gui/locales/zh-CN/relay-locations.po | 34 ++- gui/locales/zh-TW/messages.po | 231 ++++++++++++++++- gui/locales/zh-TW/relay-locations.po | 34 ++- 76 files changed, 5851 insertions(+), 231 deletions(-) diff --git a/android/lib/resource/src/main/res/values-da/plurals.xml b/android/lib/resource/src/main/res/values-da/plurals.xml index ea9520fe3147..5b3d885c9620 100644 --- a/android/lib/resource/src/main/res/values-da/plurals.xml +++ b/android/lib/resource/src/main/res/values-da/plurals.xml @@ -16,6 +16,10 @@ %1$d dag %1$d dage + + %1$d placering + %1$d placeringer + %1$d måned %1$d måneder diff --git a/android/lib/resource/src/main/res/values-da/strings.xml b/android/lib/resource/src/main/res/values-da/strings.xml index 0afd16e0561b..851b2047d8a6 100644 --- a/android/lib/resource/src/main/res/values-da/strings.xml +++ b/android/lib/resource/src/main/res/values-da/strings.xml @@ -11,6 +11,8 @@ Tilføj 30 dages tid (%1$s) Tilføj en server Tilføj DNS-server + Føj %1$s til listen + Tilføj placeringer Køb enten kredit på vores hjemmeside, eller indløs en kupon. %1$s blev føjet til din konto. Accepter og fortsæt @@ -34,6 +36,8 @@ 3. For at aktivere Lockdown-tilstand skal du klikke på knappen ud for <b>Bloker forbindelser uden VPN</b>. Lockdown-tilstand blokerer al internetadgang, hvis VPN-tunnelen afbrydes manuelt. <br/><b>Advarsel: Denne indstilling blokerer opdelte apps og funktionen Lokal netværksdeling</b>. Opret automatisk forbindelse til en server, når appen starter. + Brug systemindstillingen <b>Altid til</b> i stedet ved at følge vejledningen i <b>%1$s</b> ovenfor. + Automatisk forbindelse (ældre metode) Automatisk Tilbage Annoncer @@ -61,7 +65,9 @@ Kopierede Mullvad-kontonummer til udklipsholder Kopieret til udklipsholder Kopiér kontonummer + Opret Opret konto + Opret ny liste Oprettet: %1$s Opretter konto... OPRETTER SIKKER FORBINDELSE @@ -75,9 +81,11 @@ Fjern brugerdefineret port Indstil port Brugerdefineret WireGuard-port - Gyldige intervaller: %1$s + Gyldige områder: %1$s Kunne ikke fortolke værten for den tilpassede tunnel. Prøv at ændre dine indstillinger. Slet + Vil du slette \"%1$s\"? + \"%1$s\" blev slettet Slet liste Du har fjernet denne enhed. For at oprette forbindelse igen skal du logge ind igen. Enheden er inaktiv @@ -86,6 +94,8 @@ Dette er det navn, der er tildelt enheden. Hver enhed, der er logget på en Mullvad-konto, får et unikt navn, der hjælper dig med at identificere den, når du administrerer dine enheder i appen eller på webstedet. Du kan have op til 5 enheder logget ind på én Mullvad-konto. Hvis du logger ud, fjernes enheden og enhedsnavnet. Når du logger på igen, får enheden et nyt navn. + Kassér + Vil du kassere ændringer? Afbryd forbindelse Afbryder Afvis @@ -96,7 +106,12 @@ Dette kan forårsage problemer på visse websteder, tjenester og apps. Har du ikke noget kontonummer? Denne adresse er allerede blevet indtastet. + Rediger tilpassede lister + Rediger liste + Rediger lister + Rediger placeringer Rediger meddelelse + Rediger navn Aktiver Brug brugerdefineret DNS-server Indtast MTU @@ -121,6 +136,8 @@ Her er dit kontonummer. Gem det! Skjul kontonummer Standard + Indsæt eller skriv tilsidesættelser, der skal importeres + Import af nye tilsidesættelser vil muligvis erstatte nogle tidligere importerede tilsidesættelser. Importer Importer via tekst Ind @@ -131,11 +148,16 @@ mindre end én dag tilbage mindre end et minut siden mindre end én dag + Listenavn Tilslutter... Bekræfter køb... Lokal netværksdeling Det opnås ved at tillade netværkskommunikation uden for tunnelen til lokale multicast- og broadcast-områder samt til og fra disse private IP-intervaller: Denne funktion giver adgang til andre enheder på det lokale netværk, såsom til deling, udskrivning, streaming osv. + %1$s (tilføjet) + %1$s blev tilføjet til \"%2$s\" + Placeringer + Placeringer blev ændret for \"%1$s\" Log af Gyldigt kontonummer Logget ind @@ -154,12 +176,17 @@ For mange enheder Mullvad-kontonummer Kun ejet af Mullvad + Navnet blev ændret til %1$s Velkommen! Denne enhed hedder nu <b>%1$s</b>. Se info-knappen i Konto for at flere oplysninger. NY ENHED OPRETTET + Ny liste + Ingen brugerdefinerede lister tilgængelige Ingen internetforbindelse + Ingen placeringer fundet Ingen servere matcher dine indstillinger. Prøv at ændre server eller andre indstillinger. Gyldig WireGuard-nøgle mangler. Administrer nøgler under Avancerede indstillinger. DU LÆKKER MÅSKE NETVÆRKSTRAFIK + Ikke fundet Udbydere: %1$d Tilsløring skjuler WireGuard-trafikken inden i en anden protokol. Det kan bruges til at hjælpe med at omgå censur og andre typer filtrering, hvor en almindelig WireGuard-forbindelse ville blive blokeret. Til (UDP-over-TCP) @@ -168,9 +195,11 @@ Til Ud Tid udløbet + Tilsidesættelser ryddet Ejet Ejerskab Betalt indtil + Patch matcher ikke specifikationen For at begynde at bruge appen skal du først føje tid til din konto. Vi kunne ikke starte betalingsprocessen. Sørg for, at du har den nyeste version af Google Play. Google Play er ikke tilgængelig @@ -216,29 +245,49 @@ Hvis det er nødvendigt, kontakter vi dig på %1$s Tak! Server IP tilsidesættelse + Tilsidesættelser aktive + Importer nye tilsidesættelser med + Fil + Tekst + Tilsidesættelser inaktive På nogle netværk, hvor der bruges forskellige typer censur, er vores server IP-adresser nogle gange blokeret. For at omgå dette kan du importere en fil eller en tekst, leveret af vores supportteam, med nye IP-adresser, der tilsidesætter standardadresserne på serverne i visningen Vælg placering. Hvis du har problemer med at oprette forbindelse til VPN-servere, bedes du kontakte support. + Nulstil tilsidesættelser + Alle tilsidesættelser vil blive nulstillet, og servernes IP-adresser i visningen Vælg placering vil blive sat tilbage til standard. + Nulstil + Nulstil alle tilsidesættelser Kan ikke indstille systemets DNS-server. Indsend en problemrapport. Kan ikke anvende firewallregler. Fejlfind eller send en problemrapport. Indstillinger Konto Ændringer af DNS-relaterede indstillinger træder muligvis ikke i kraft med det samme på grund af cachelagrede resultater. DNS-indstillinger træder muligvis ikke i kraft med det samme + Det lykkedes ikke at anvende patch + Ugyldig eller manglende værdi \"%1$s\" + Rekursionsgrænse + Kan ikke fortolke patch + Ukendt eller forbudt nøgle \"%1$s\" + Importen lykkedes, tilsidesættelser aktive Indstillinger VPN-indstillinger Vis kontonummer Vis systemapps + Giver dig mulighed for at vælge apps, der skal have direkte adgang til internettet uden at gå gennem VPN-tunnelen. + Bemærk: Split tunneling udgør en privatlivsrisiko. Kan ikke starte tunnelforbindelse. Indsend en problemrapport. Indsend Skift placering TCP + Tryk på \" ︙ \" eller tryk langvarigt på et land, en by eller en server for at tilføje placeringer til en liste. + Tryk på \" ︙ \" for at oprette en brugerdefineret liste Slå VPN til/fra Enhedsnavn: %1$s Resterende tid: %1$s Prøv igen UDP Hvilken TCP-port UDP-over-TCP tilsløringsprotokollen skal forbinde til på VPN-serveren. + Fortryd Ikke sikret IKKE-SIKRET FORBINDELSE IKKE-UNDERSTØTTET VERSION @@ -248,6 +297,7 @@ Installer Mullvad VPN (%1$s) for at holde dig opdateret Opdatering tilgængelig, download den for at forblive sikker. Opdater DNS-server + Opdater listenavn Din e-mail (valgfrit) For at vi bedre kan hjælpe dig, bedes du skrive på engelsk eller svensk og nævne hvilket land, du befinder dig i. Bekræfter kupon… diff --git a/android/lib/resource/src/main/res/values-de/plurals.xml b/android/lib/resource/src/main/res/values-de/plurals.xml index f81a681f6459..76d6654e3e75 100644 --- a/android/lib/resource/src/main/res/values-de/plurals.xml +++ b/android/lib/resource/src/main/res/values-de/plurals.xml @@ -16,6 +16,10 @@ %1$d Tag %1$d Tage + + %1$d Standort + %1$d Standorte + %1$d Monat %1$d Monate diff --git a/android/lib/resource/src/main/res/values-de/strings.xml b/android/lib/resource/src/main/res/values-de/strings.xml index 6109cb4d464d..c284775b30c7 100644 --- a/android/lib/resource/src/main/res/values-de/strings.xml +++ b/android/lib/resource/src/main/res/values-de/strings.xml @@ -11,6 +11,8 @@ 30 Tage Zeit hinzufügen (%1$s) Server hinzufügen DNS-Server hinzufügen + %1$s zur Liste hinzufügen + Standorte hinzufügen Kaufen Sie entweder Guthaben über unsere Seite oder lösen Sie einen Gutschein ein. %1$s wurde zu Ihrem Konto hinzugefügt. Akzeptieren und weiter @@ -34,6 +36,8 @@ 3. Um den Sperrmodus zu aktivieren, klicken Sie auf den Kippschalter neben <b>Verbindungen ohne VPN sperren</b>. Der Sperrmodus blockiert den gesamten Internetzugang, wenn die Verbindung zum VPN-Tunnel manuell unterbrochen wird. <br/><b>Warnung: Diese Einstellung blockiert geteilte Anwendungen und das Teilen im lokalen Netzwerk</b>. Stellt automatisch eine Verbindung zum Server her, wenn die App startet. + Bitte verwenden Sie stattdessen die Systemeinstellung <b>Always-on (Durchgehend aktiv)</b>, indem Sie die Anleitung oben unter <b>%1$s</b> befolgen. + Automatische Verbindung (veraltet) Automatisch Zurück Werbung @@ -61,7 +65,9 @@ Mullvad-Kontonummer wurde in die Zwischenablage kopiert In die Zwischenablage kopiert Kontonummer kopieren + Erstellen Konto erstellen + Neue Liste erstellen Erstellt: %1$s Konto wird erstellt ... SICHERE VERBINDUNG WIRD ERSTELLT @@ -78,6 +84,8 @@ Gültige Bereiche: %1$s Der Host des benutzerdefinierten Tunnels konnte nicht aufgelöst werden. Versuchen Sie, Ihre Einstellungen zu ändern. Löschen + „%1$s“ löschen? + „%1$s“ wurde gelöscht Liste löschen Sie haben dieses Gerät entfernt. Um sich erneut zu verbinden, müssen Sie sich erneut anmelden. Gerät ist inaktiv @@ -86,6 +94,8 @@ Dies ist der dem Gerät zugewiesene Name. Jedes Gerät, das in einem Mullvad-Konto angemeldet ist, erhält einen eindeutigen Namen, mit dem Sie es identifizieren können, wenn Sie Ihre Geräte in der App oder auf der Website verwalten. Es sind pro Mullvad-Konto bis zu 5 angemeldete Geräte möglich. Wenn Sie sich abmelden, werden das Gerät und der Gerätename entfernt. Wenn Sie sich wieder anmelden, erhält das Gerät einen neuen Namen. + Verwerfen + Änderungen verwerfen? Verbindung trennen Verbindung wird getrennt Ausblenden @@ -96,7 +106,12 @@ Dies kann bei bestimmten Websites, Diensten und Apps zu Problemen führen. Sie haben keine Kontonummer? Diese Adresse wurde bereits eingetragen. + Eigene Listen bearbeiten + Liste bearbeiten + Listen bearbeiten + Standorte bearbeiten Nachricht bearbeiten + Name bearbeiten Aktivieren Benutzerdefinierten DNS-Server verwenden MTU eingeben @@ -121,6 +136,8 @@ Hier ist Ihre Kontonummer. Verlieren Sie sie nicht! Kontonummer verbergen Standard + Zu importierende Überschreibungen einfügen oder eingeben + Der Import neuer Überschreibungen kann einige zuvor importierte Überschreibungen ersetzen. Importieren Import via Text Eingehend @@ -131,11 +148,16 @@ weniger als ein Tag übrig vor weniger als einer Minute weniger als ein Tag + Name der Liste Verbinden … Kauf verifizieren … Teilen im lokalen Netzwerk Das geschieht, indem die Netzwerkkommunikation außerhalb des Tunnels zu lokalen Multicast- und Broadcast-Bereichen sowie zu und von diesen privaten IP-Bereichen zugelassen wird: Diese Funktion ermöglicht den Zugriff auf andere Geräte im lokalen Netzwerk, z. B. zum Teilen, Drucken, Streamen usw. + %1$s (hinzugefügt) + %1$s wurde zu „%2$s“ hinzugefügt + Standorte + Standorte wurden geändert für „%1$s“ Abmelden Gültige Kontonummer Angemeldet @@ -154,12 +176,17 @@ Zu viele Geräte Mullvad-Kontonummer Nur im Besitz von Mullvad + Name wurde geändert in %1$s Dieses Gerät heißt jetzt <b>%1$s</b>. Weitere Details finden Sie über die Info-Schaltfläche in Ihrem Konto. NEUES GERÄT ERSTELLT + Neue Liste + Keine eigenen Listen verfügbar Keine Internetverbindung + Keine Standorte gefunden Kein Server entspricht Ihren Einstellungen. Versuchen Sie, den Server oder andere Einstellungen zu ändern. Gültiger WireGuard-Schlüssel fehlt. Sie können Ihre Schlüssel in den erweiterten Einstellungen verwalten. MÖGLICHERWEISE IST IHR NETZWERKVERKEHR UNSICHER + Nicht gefunden Provider: %1$d Bei der Verschleierung wird der WireGuard-Datenverkehr in einem anderen Protokoll versteckt. Sie kann dazu verwendet werden, Zensur und andere Arten von Filtern zu umgehen, bei denen eine reine WireGuard-Verbindung blockiert würde. An (UDP über TCP) @@ -168,9 +195,11 @@ Ein Ausgehend Zeit abgelaufen + Überschreibungen entfernt In Besitz Eigentümerschaft Bezahlt bis + Patch entspricht nicht der Spezifikation Um mit der Nutzung dieser App zu beginnen, müssen Sie erst einmal Zeit zu Ihrem Konto hinzufügen. Wir konnten den Zahlungsvorgang nicht starten. Bitte vergewissern Sie sich, dass Sie die neueste Version von Google Play haben. Google Play nicht verfügbar @@ -216,29 +245,49 @@ Bei Bedarf werden wir Sie über %1$s kontaktieren Danke! Server-IP überschreiben + Überschreibungen aktiv + Neue Überschreibungen importieren via + Datei + Text + Überschreibungen inaktiv In einigen Netzwerken, in denen verschiedene Arten der Zensur eingesetzt werden, werden die IP-Adressen unserer Server manchmal blockiert. Um dies zu umgehen, können Sie eine Datei oder einen von unserem Support-Team bereitgestellten Text mit neuen IP-Adressen importieren, die die Standardadressen der Server in der Ortsauswahl außer Kraft setzen. Wenn Sie Probleme mit der Verbindung zu VPN-Servern haben, wenden Sie sich bitte an den Support. + Überschreibungen zurücksetzen + Alle Überschreibungen werden zurückgesetzt und die IP-Adressen der Server in der Standortauswahl werden auf die Standardwerte zurückgesetzt. + Zurücksetzen + Alle Überschreibungen zurücksetzen Der DNS-Server des Systems konnte nicht eingestellt werden. Bitte senden Sie einen Problembericht. Firewall-Regeln können nicht angewendet werden. Bitte beheben Sie das Problem oder senden Sie einen Problembericht. Einstellungen Konto Änderungen an DNS-Einstellungen werden aufgrund von zwischengespeicherten Daten möglicherweise nicht sofort wirksam. Die DNS-Einstellungen werden möglicherweise nicht sofort wirksam + Patch konnte nicht angewendet werden + Ungültiger oder fehlender Wert „%1$s“ + Rekursionslimit + Patch konnte nicht geparst werden + Unbekannter oder verbotener Schlüssel „%1$s“ + Import erfolgreich, Überschreibungen aktiv Präferenzen VPN-Einstellungen Kontonummer anzeigen System-Apps anzeigen + Hiermit können Sie Apps auswählen, die ohne den VPN-Tunnel direkt auf das Internet zugreifen sollen. + Achtung! Split Tunneling birgt ein Datenschutzrisiko. Die Tunnelverbindung konnte nicht gestartet werden. Bitte senden Sie einen Problembericht. Absenden Ort wechseln TCP + Um Standorte zu einer Liste hinzuzufügen, drücken Sie auf „︙“ oder drücken Sie lange auf ein Land, eine Stadt oder einen Server. + Um eine eigene Liste zu erstellen, drücken Sie auf „︙“ VPN umschalten Gerätename: %1$s Verbleibende Zeit: %1$s Erneut versuchen UDP Mit welchem TCP-Port sich das UDP-über-TCP-Verschleierungsprotokoll auf dem VPN-Server verbinden soll. + Rückgängig Ungesichert UNGESICHERTE VERBINDUNG NICHT UNTERSTÜTZTE VERSION @@ -248,6 +297,7 @@ Installieren Sie Mullvad VPN (%1$s), um auf dem neuesten Stand zu bleiben Update verfügbar, laden Sie es herunter, um sicher zu bleiben. DNS-Server aktualisieren + Name der Liste ändern Ihre E-Mail-Adresse (optional) Um Ihnen besser weiterhelfen zu können, schreiben Sie uns bitte auf Englisch oder Schwedisch und geben Sie an, aus welchem Land Sie die Verbindung herstellen. Gutschein verifizieren … diff --git a/android/lib/resource/src/main/res/values-es/plurals.xml b/android/lib/resource/src/main/res/values-es/plurals.xml index e4ec66e8c6a0..a683e7c1ebc9 100644 --- a/android/lib/resource/src/main/res/values-es/plurals.xml +++ b/android/lib/resource/src/main/res/values-es/plurals.xml @@ -16,6 +16,10 @@ %1$d día %1$d días + + %1$d ubicación + %1$d ubicaciones + %1$d mes %1$d meses diff --git a/android/lib/resource/src/main/res/values-es/strings.xml b/android/lib/resource/src/main/res/values-es/strings.xml index ed211d74a546..88aa16daee8f 100644 --- a/android/lib/resource/src/main/res/values-es/strings.xml +++ b/android/lib/resource/src/main/res/values-es/strings.xml @@ -11,8 +11,10 @@ Añadir 30 días (%1$s) Añadir un servidor Añadir servidor DNS + Añadir %1$s a la lista + Añadir ubicaciones Compre crédito en nuestro sitio web o canjee un cupón. - Se ha(n) añadido %1$s a su cuenta. + Se ha añadido %1$s a su cuenta. Aceptar y continuar Todas las aplicaciones Todas las ubicaciones @@ -34,6 +36,8 @@ 3. Para habilitar el modo de bloqueo, haga clic en el botón situado junto a <b>Bloquear conexiones sin VPN</b>. El modo de bloqueo bloquea todo el acceso a Internet si el túnel de la VPN está desconectado manualmente. <br/><b>Advertencia: Este ajuste bloquea las aplicaciones divididas y la opción Uso compartido de la red local</b>. Al iniciarse la aplicación, se conecta automáticamente a un servidor. + En su lugar, utilice el ajuste de sistema <b>Siempre activado</b> siguiendo arriba la guía en <b>%1$s</b>. + Conexión automática (heredada) Automático Volver Anuncios @@ -61,7 +65,9 @@ El número de cuenta de Mullvad se copió en el Portapapeles Copiado en el Portapapeles Copiar número de cuenta + Crear Crear cuenta + Crear nueva lista Creado: %1$s Creando cuenta… CREANDO CONEXIÓN SEGURA @@ -78,6 +84,8 @@ Intervalos válidos: %1$s No se puede resolver el host del túnel personalizado. Pruebe a cambiar la configuración. Eliminar + ¿Eliminar «%1$s»? + Se ha eliminado «%1$s» Eliminar lista Ha quitado este dispositivo. Vuelva a iniciar la sesión para conectarse. El dispositivo está inactivo @@ -86,6 +94,8 @@ Este es el nombre asignado al dispositivo. Cada dispositivo conectado a una cuenta de Mullvad recibe un nombre único que ayuda a identificarlo cuando gestiona sus dispositivos en la aplicación o en el sitio web. Puede tener hasta 5 dispositivos conectados a una cuenta de Mullvad. Si cierra sesión, se quita el dispositivo y el nombre del dispositivo. Cuando vuelve a iniciar sesión, el dispositivo recibirá un nombre nuevo. + Descartar + ¿Descartar los cambios? Desconectar Desconectando Descartar @@ -96,7 +106,12 @@ Esto podría causar problemas con determinados sitios web, servicios y aplicaciones. ¿No tiene un número de cuenta? Esta dirección ya se ha introducido. + Editar listas personalizadas + Editar lista + Editar listas + Editar ubicaciones Editar mensaje + Editar nombre Habilitar Usar servidor DNS personalizado Introducir MTU @@ -121,6 +136,8 @@ Este es un número de cuenta. ¡Guárdelo bien! Ocultar número de cuenta Predeterminado + Pegue o escriba las anulaciones que se deben importar + Importar nuevas anulaciones podría reemplazar algunas anulaciones anteriormente importadas. Importar Importación a través de texto Entrada @@ -131,11 +148,16 @@ queda menos de un día hace menos de un minuto menos de un día + Nombre de la lista Conectando... Verificando la compra… Uso compartido de la red local Lo realiza permitiendo la comnunicación de red fuera del túnel hacia rangos de emisión y multidifusión locales y también hacia y desde estos rangos de IP privados: Esta característica permite el acceso a otros dispositivos de la red local, como para compartir, imprimir, transmitir, etc. + %1$s (añadida) + %1$s se ha añadido a «%2$s» + Ubicaciones + Se han cambiado las ubicaciones para «%1$s» Cerrar sesión Número de cuenta válido Sesión iniciada @@ -154,12 +176,17 @@ Demasiados dispositivos Número de cuenta de Mullvad Solo propiedad de Mullvad + Se ha cambiado el nombre a %1$s Hola, este dispositivo se llama ahora <b>%1$s</b>. Para más información, consulte el botón de información en la Cuenta. NUEVO DISPOSITIVO CREADO + Nueva lista + No hay listas personalizadas disponibles No hay conexión a Internet + No se ha encontrado ninguna ubicación Ningún servidor coincide con su configuración, pruebe con otro servidor u otra configuración. Falta una clave de WireGuard válida. Para administrar las claves, vaya a Configuración avanzada. PUEDE QUE SE ESTÉ FILTRANDO EL TRÁFICO DE RED + No encontrado Proveedores: %1$d La ofuscación oculta el tráfico de WireGuard dentro de otro protocolo. Puede usarse para sortear la censura y otros tipos de filtrado donde podría bloquearse una conexión de WireGuard normal. Activado (UDP sobre TCP) @@ -168,9 +195,11 @@ Activado Salida Tiempo agotado + Anulaciones borradas Propios Propiedad Pagado hasta + El parche no coincide con la especificación Para empezar a usar la aplicación, primero necesita agregar tiempo a su cuenta. No hemos podido iniciar el proceso de pago. Asegúrese de tener la última versión de Google Play. Google Play no disponible @@ -216,29 +245,49 @@ Si es necesario, le enviaremos un correo electrónico a %1$s ¡Gracias! Anulación de IP de servidor + Anulaciones activas + Importar nuevas anulaciones por + Archivo + Texto + Anulaciones inactivas En algunas redes, donde se aplican diversos tipos de censura, a veces se bloquean las direcciones IP de nuestro servidor. Para eludir esto, puede importar un archivo o texto, suministrado por nuestro equipo de asistencia, con nuevas direcciones IP que anulan las direcciones predeterminadas de los servidores en la vista Seleccionar ubicación. Si tiene problemas para conectarse a los servidores VPN, póngase en contacto con el servicio de asistencia. + Restablecer anulaciones + Todas las anulaciones se restablecerán y las direcciones IP de los servidores, en la vista Seleccionar ubicación, volverán a sus valores predeterminados. + Restablecer + Restablecer todas las anulaciones No se puede configurar el servidor DNS del sistema. Envíe un informe de problemas. No se pueden aplicar las reglas del firewall. Intente solucionar el problema o envíe un informe de problemas. Configuración Cuenta Los cambios en la configuración relacionada con el DNS no surtirán efecto inmediatamente debido a los resultados en caché. La configuración de DNS podría no surtir efecto inmediatamente + Error al aplicar el parche + El valor «%1$s» falta o no es válido + Límite de recursión + No se puede analizar el parche + Clave «%1$s» desconocida o prohibida + Importación realizada correctamente. Anulaciones activas Preferencias Configuración de VPN Mostrar número de cuenta Mostrar aplicaciones del sistema + Le permite seleccionar aplicaciones que deberían acceder a Internet directamente sin pasar por el túnel VPN. + Atención: La utilización dividida es un riesgo para la privacidad. No se puede iniciar la conexión del túnel. Envíe un informe de problemas. Enviar Cambiar ubicación TCP + Para añadir ubicaciones a una lista, pulse «︙» o mantenga pulsado unos segundos un país, ciudad o servidor. + Para crear una lista personalizada, pulse «︙» Alternar VPN Nombre del dispositivo: %1$s Tiempo restante: %1$s Volver a intentarlo UDP El puerto TCP al que se conectará el protocolo de ofuscación de UDP sobre TCP en el servidor VPN. + Deshacer No protegido CONEXIÓN NO SEGURA VERSIÓN NO ADMITIDA @@ -248,6 +297,7 @@ Instale Mullvad VPN (%1$s) para seguir actualizado Hay una actualización disponible, descárguela para seguir protegido. Actualizar servidor DNS + Actualizar nombre de la lista Su correo electrónico (opcional) Para ayudarle mejor, escriba en inglés o sueco e indique desde qué país se está conectando. Verificando el cupón… diff --git a/android/lib/resource/src/main/res/values-fi/plurals.xml b/android/lib/resource/src/main/res/values-fi/plurals.xml index f8232300e55c..7d58779e413e 100644 --- a/android/lib/resource/src/main/res/values-fi/plurals.xml +++ b/android/lib/resource/src/main/res/values-fi/plurals.xml @@ -16,6 +16,10 @@ %1$d päivä %1$d päivää + + %1$d sijainti + %1$d sijaintia + %1$d kuukausi %1$d kuukautta diff --git a/android/lib/resource/src/main/res/values-fi/strings.xml b/android/lib/resource/src/main/res/values-fi/strings.xml index 52df0e8d1635..aeafd887bb65 100644 --- a/android/lib/resource/src/main/res/values-fi/strings.xml +++ b/android/lib/resource/src/main/res/values-fi/strings.xml @@ -11,6 +11,8 @@ Lisää 30 päivää käyttöaikaa (%1$s) Lisää palvelin Lisää DNS-palvelin + Lisää %1$s luetteloon + Lisää sijainteja Osta käyttöaikaa verkkosivustoltamme tai lunasta kuponki. Tilillesi lisättiin %1$s käyttöaikaa. Hyväksy ja jatka @@ -34,6 +36,8 @@ 3. Ota lukitustila käyttöön napsauttamalla <b>Estä yhteydet ilman VPN:ää</b> -kohdan vierestä löytyvää valintapainiketta. Lukitustila estää kaikki verkkoyhteydet, jos yhteys VPN-tunneliin katkaistaan manuaalisesti. <br/><b>Varoitus: asetus estää sovelluskohtaisesti yhdistettävät sovellukset ja paikallisen verkon jakamisominaisuuden</b>. Muodosta yhteys palvelimeen automaattisesti, kun sovellus avataan. + Käytä <b>aina käytössä</b> -järjestelmäasetusta seuraamalla ylhäältä löytyvää opasta aiheesta <b>%1$s</b>. + Autom. yhdistäminen (vanha järjestelmä) Automaattinen Takaisin Mainokset @@ -61,7 +65,9 @@ Mullvad-tilin numero kopioitu leikepöydälle Kopioitu leikepöydälle Kopioi tilin numero + Luo Luo tili + Luo uusi luettelo Luotu: %1$s Luodaan tiliä... LUODAAN SUOJATTU YHTEYS @@ -78,6 +84,8 @@ Kelvolliset portit: %1$s Muokatun tunnelin isännän selvittäminen ei onnistu. Kokeile muuttaa asetuksiasi. Poista + Poistetaanko \"%1$s\"? + \"%1$s\" poistettiin Poista luettelo Olet poistanut tämän laitteen. Jos haluat muodostaa yhteyden uudelleen, sinun täytyy kirjautua takaisin sisään. Laite ei ole aktiivinen @@ -86,6 +94,8 @@ Tämä on laitteelle annettu nimi. Jokainen Mullvad-tilille kirjautunut laite saa yksilöllisen nimen, jonka avulla sen voi tunnistaa laitteiden hallinnassa sovelluksessa tai verkkosivustolla. Yhdelle Mullvad-tilille voi olla kirjautuneena enintään viisi laitetta. Jos kirjaudut ulos, laite ja laitteen nimi poistetaan. Kun kirjaudut sisään uudelleen, laitteelle annetaan uusi nimi. + Hylkää + Hylätäänkö muokkaukset? Katkaise yhteys Katkaistaan yhteyttä Jätä huomiotta @@ -96,7 +106,12 @@ Tämä voi aiheuttaa ongelmia tietyillä verkkosivustoilla, palveluissa ja sovelluksissa. Eikö sinulla ole tilinumeroa? Tämä osoite on annettu jo. + Muokkaa mukautettuja luetteloita + Muokkaa luetteloa + Muokkaa luetteloita + Muokkaa sijainteja Muokkaa viestiä + Muokkaa nimeä Ota käyttöön Käytä mukautettua DNS-palvelinta Syötä MTU @@ -121,6 +136,8 @@ Tässä tulee tilisi numero. Laita se talteen! Piilota tilin numero Oletus + Liitä tai kirjoita ohitukset, jotka haluat tuoda + Uusien ohitusten tuonti saattaa korvata joitain aiemmin tuotuja ohituksia. Tuo Tuo tekstinä Saapuva @@ -131,11 +148,16 @@ alle vuorokausi jäljellä alle minuutti sitten alle vuorokausi + Luettelon nimi Yhdistetään... Ostosta vahvistetaan... Paikallisen verkon jakaminen Ominaisuus siis sallii verkkoviestinnän tunnelin ulkopuolella paikallisille lähetys- ja monilähetysalueille sekä näillä yksityisillä IP-alueilla kumpaankin suuntaan: Tämä ominaisuus mahdollistaa pääsyn muihin paikallisverkon laitteisiin esimerkiksi jakamisen, tulostamisen, suoratoiston jne. tarpeisiin. + %1$s (lisätty) + %1$s lisättiin luetteloon \"%2$s\" + Sijainnit + Kohteen \"%1$s\" sijainteja vaihdettiin Kirjaudu ulos Oikea tilin numero Kirjautuneena sisään @@ -154,12 +176,17 @@ Liikaa laitteita Mullvad-tilin numero Vain Mullvadin omistamat + Nimeksi vaihdettiin \"%1$s\" Tervetuloa! Tämän laitteen nimi on nyt <b>%1$s</b>. Katso lisätietoja tilin infopainikkeesta. UUSI LAITE LUOTIIN + Uusi luettelo + Mukautettuja luetteloita ei löytynyt Ei verkkoyhteyttä + Sijainteja ei löytynyt Mikään palvelin ei vastaa asetuksiasi. Kokeile vaihtaa palvelinta tai muuttaa muita asetuksia. Käypä WireGuard-avain puuttuu. Voit hallinnoida avaimia lisäasetuksissa. VERKKOLIIKENTEESI SAATTAA VUOTAA + Ei löydy Palveluntarjoajat: %1$d Hämäysteknologian käyttäminen piilottaa WireGuard-liikenteen toisen protokollan sisään. Sitä voidaan käyttää kiertämään sensuuria ja muita suodatuksia niissä tapauksissa, kun tavallinen WireGuard-yhteys muutoin estettäisi. Käytössä (UDP TCP:n kautta) @@ -168,9 +195,11 @@ Päällä Lähtevä Ei käyttöaikaa + Ohitukset on poistettu Omistettu Omistajuus Maksu ennen + Muutostiedosto ei vastaa määritelmää Voit aloittaa sovelluksen käyttämisen lisäämällä ensin aikaa tilillesi. Emme pystyneet aloittamaan maksun käsittelyä. Varmista, että käytät Google Playn uusinta versiota. Google Play ei ole käytettävissä @@ -216,29 +245,49 @@ Tarvittaessa otamme sinuun yhteyttä osoitteeseen %1$s Kiitos! Palvelimen IP-osoitteen ohitus + Ohituksia on käytössä + Uusien ohitusten tuontitapa + Tiedosto + Teksti + Ohituksia ei ole käytössä Palvelimiemme IP-osoitteet estetään toisinaan joissakin useita erityyppistä sensurointimenetelmiä käyttävissä verkoissa. Voit kiertää estot tuomalla tukitiimimme toimittaman tiedoston tai tekstin, josta löytyy uusia, palvelimien oletusosoitteet sijainnin valintanäkymässä ohittavia IP-osoitteita. Jos sinulla on ongelmia yhteyden muodostamisessa VPN-palvelimiin, ota yhteyttä tukeen. + Nollaa ohitukset + Kaikki ohitukset nollataan ja palvelinten IP-osoitteet palautuvat oletusarvoihin sijainnin valintanäkymässä. + Nollaa + Nollaa kaikki ohitukset Järjestelmän DNS-palvelimen asettaminen ei onnistu. Lähetä ongelmaraportti. Palomuurisääntöjä ei voida käyttää. Suorita vianetsintä tai lähetä ongelmaraportti. Asetukset Tili DNS-asetuksiin tehdyt muutokset eivät välttämättä astu voimaan välittömästi välimuistissa olevien tulosten vuoksi. Uudet DNS-asetukset eivät välttämättä astu voimaan välittömästi + Muutostiedoston käyttöönotto ei onnistunut + Arvo \"%1$s\" puuttuu tai on virheellinen + Toistorajoitus + Muutostiedoston jäsentäminen ei onnistu + Tuntematon tai kielletty avain \"%1$s\" + Tuonti onnistui ja ohituksia on käytössä Asetukset VPN-asetukset Näytä tilin numero Näytä järjestelmäsovellukset + Voit valita sovelluksia, joiden pitäisi muodostaa yhteys Internetiin suoraan kulkematta VPN-tunnelin läpi. + Huomio: sovelluskohtainen yhdistäminen on tietosuojariski. Tunneliyhteyden muodostaminen ei onnistu. Lähetä ongelmaraportti. Lähetä Vaihda sijaintia TCP + Jos haluat lisätä luetteloon sijainteja, paina \"︙\" tai paina pitkään maata, kaupunkia tai palvelinta. + Voit luoda mukautetun luettelon painamalla \"︙\" Vaihda VPN:ää Laitteen nimi: %1$s Aikaa jäljellä: %1$s Yritä uudelleen UDP Määrittää, mihin VPN-palvelimen TCP-porttiin \"UDP TCP:n kautta\" -hämäysteknologia-protokollan tulee muodostaa yhteys. + Kumoa Suojaamaton SUOJAAMATON YHTEYS EI-TUETTU VERSIO @@ -248,6 +297,7 @@ Asenna Mullvad VPN (%1$s) pysyäksesi ajan tasalla Päivitys saatavilla. Lataa se pysyäksesi suojattuna. Päivitä DNS-palvelin + Päivitä luettelon nimi Sähköpostisi (valinnainen) Jotta voisimme avustaa sinua paremmin, kirjoita englanniksi tai ruotsiksi ja mainitse, mistä maasta muodostat yhteyden. Kuponkia vahvistetaan… diff --git a/android/lib/resource/src/main/res/values-fr/plurals.xml b/android/lib/resource/src/main/res/values-fr/plurals.xml index 67bb7839d379..d3ab1fc0ab36 100644 --- a/android/lib/resource/src/main/res/values-fr/plurals.xml +++ b/android/lib/resource/src/main/res/values-fr/plurals.xml @@ -16,6 +16,10 @@ %1$d jour %1$d jours + + %1$d localisation + %1$d localisations + %1$d mois %1$d mois diff --git a/android/lib/resource/src/main/res/values-fr/strings.xml b/android/lib/resource/src/main/res/values-fr/strings.xml index 584c700bf311..657cd366d8a2 100644 --- a/android/lib/resource/src/main/res/values-fr/strings.xml +++ b/android/lib/resource/src/main/res/values-fr/strings.xml @@ -11,6 +11,8 @@ Ajouter 30 jours de temps (%1$s) Ajouter un serveur Ajouter un serveur DNS + Ajouter %1$s à la liste + Ajouter des localisations Achetez du crédit sur notre site web ou échangez un bon. %1$s ajouté(s) à votre compte. Accepter et continuer @@ -34,6 +36,8 @@ 3. Pour activer le mode Verrouillage, cliquez sur l\'interrupteur situé à côté de <b>Bloquer les connexions sans VPN</b>. Le mode Verrouillage bloque tout accès à Internet si le tunnel VPN est déconnecté manuellement. <br/><b>Avertissement : Ce paramètre bloque les applications fractionnées et la fonctionnalité de partage du réseau local</b>. Connexion automatique à un serveur dès le démarrage de l\'application. + Veuillez utiliser le paramètre système <b>Toujours activé</b> en suivant le guide dans <b>%1$s</b> ci-dessus. + Connexion automatique (obsolète) Automatique Retour Publicités @@ -61,7 +65,9 @@ Numéro de compte Mullvad copié dans le presse-papiers Copié dans le presse-papiers Copier le numéro de compte + Créer Créer un compte + Créer une nouvelle liste Création : %1$s Création du compte… CRÉATION D\'UNE CONNEXION SÉCURISÉE @@ -78,6 +84,8 @@ Plages valides : %1$s Échec de la résolution de l\'hôte du tunnel personnalisé. Essayez de modifier vos paramètres. Supprimer + Supprimer « %1$s » ? + « %1$s » a été supprimé Supprimer la liste Vous avez supprimé cet appareil. Vous devrez vous reconnecter pour connecter cet appareil à nouveau. L\'appareil est inactif @@ -86,6 +94,8 @@ Il s\'agit du nom attribué à l\'appareil. Chaque appareil connecté à un compte Mullvad reçoit un nom unique qui vous aide à l\'identifier lorsque vous gérez vos appareils dans l\'application ou sur le site Web. Vous pouvez connecter jusqu\'à 5 appareils au même compte Mullvad. Si vous vous déconnectez, l\'appareil et son nom sont supprimés. Lorsque vous vous reconnectez, l\'appareil reçoit un nouveau nom. + Annuler + Annuler les modifications ? Déconnexion Déconnexion en cours Ignorer @@ -96,7 +106,12 @@ Cela peut causer des problèmes sur certains sites Web, services et applications. Vous n\'avez pas de numéro de compte ? Cette adresse a déjà été saisie. + Modifier les listes personnalisées + Modifier la liste + Modifier les listes + Modifier les localisations Modifier le message + Modifier le nom Activer Utiliser un serveur DNS personnalisé Saisir le MTU @@ -121,6 +136,8 @@ Voici votre numéro de compte. Gardez-le ! Masquer le numéro de compte Par défaut + Collez ou saisissez les substitutions à importer + L\'importation de nouvelles substitutions peut remplacer certaines substitutions importées précédemment. Importer Importer par texte Entrante @@ -131,11 +148,16 @@ moins d\'un jour restant il y a moins d\'une minute moins d\'un jour + Nom de la liste Connexion... Vérification de l\'achat... Partage réseau local Pour ce faire, il autorise la communication réseau à l\'extérieur du tunnel vers les plages locales de multidiffusion et de diffusion, ainsi que vers et à partir de ces plages IP privées : Cette fonctionnalité permet d\'accéder à d\'autres appareils sur le réseau local, par exemple pour le partage, l\'impression, le streaming, etc. + %1$s (ajouté) + %1$s a été ajouté à « %2$s » + Localisations + Les localisations ont été modifiées pour « %1$s » Déconnexion Numéro de compte valide Connecté @@ -154,12 +176,17 @@ Trop d\'appareils Numéro de compte Mullvad Propriété de Mullvad uniquement + Le nom a été changé en %1$s Bienvenue, cet appareil s\'appelle désormais <b>%1$s</b>. Pour plus d\'informations, consultez le bouton d\'information sous Compte. NOUVEL APPAREIL CRÉÉ + Nouvelle liste + Aucune liste personnalisée disponible Aucune connexion à Internet + Aucune localisation trouvée Aucun serveur ne correspond à vos paramètres, essayez de modifier les paramètres du serveur ou d\'autres paramètres. Une clé WireGuard valide manque. Gérez les clés dans les paramètres avancés. VOUS POURRIEZ AVOIR DES FUITES DE TRAFIC RÉSEAU + Introuvable Fournisseurs : %1$d La dissimulation cache le trafic WireGuard à l\'intérieur d\'un autre protocole. Elle peut être utilisée pour aider à contourner la censure et d\'autres types de filtrage, où une connexion WireGuard simple serait bloquée. Activé (UDP sur TCP) @@ -168,9 +195,11 @@ Activé Sortante Plus de temps + Substitutions supprimées Possédé Propriété Payé jusqu\'au + Le correctif ne correspond pas à la spécification Pour commencer à utiliser l\'application, vous devez d\'abord ajouter du temps à votre compte. Nous n\'avons pas pu lancer le processus de paiement, merci de vérifier que vous disposez de la dernière version de Google Play. Google Play indisponible @@ -216,29 +245,49 @@ Si nécessaire, nous vous contacterons à l\'adresse %1$s Merci ! Substitution d\'IP de serveur + Substitutions actives + Importer les nouvelles substitutions depuis + Fichier + Texte + Substitutions inactives Sur certains réseaux, où divers types de censure sont utilisés, les adresses IP de notre serveur sont parfois bloquées. Pour contourner ce problème, vous pouvez importer un fichier ou du texte fourni par notre équipe d\'assistance, avec de nouvelles adresses IP qui remplacent les adresses par défaut des serveurs dans la vue Sélectionner un emplacement. Si vous rencontrez des problèmes de connexion aux serveurs VPN, veuillez contacter l\'assistance. + Réinitialiser les substitutions + Toutes les substitutions seront réinitialisées et les adresses IP des serveurs, dans la vue Sélectionner une localisation, reviendront à leur valeur par défaut. + Réinitialiser + Réinitialiser toutes les substitutions Impossible de définir le serveur DNS système. Veuillez envoyer un rapport de problème. Impossible d\'appliquer les règles du pare-feu. Merci de résoudre le problème ou d\'envoyer un rapport de problème. Paramètres Compte Les modifications apportées aux paramètres liés au DNS peuvent ne pas prendre effet immédiatement en raison de la mise en cache des résultats. Les paramètres DNS peuvent ne pas être immédiatement pris en compte + Impossible d\'appliquer le correctif + Valeur « %1$s » non valide ou manquante + Limite de récursion + Impossible d\'analyser le correctif + Clé « %1$s » inconnue ou interdite + Importation réussie, les substitutions sont actives Préférences Paramètres VPN Afficher le numéro de compte Afficher les applications système + Vous permet de sélectionner les applications qui doivent accéder directement à Internet sans passer par le tunnel VPN. + Attention : le split tunneling est un risque de confidentialité. Impossible de démarrer la connexion au tunnel. Veuillez envoyer un rapport de problème. Envoyer Changer de localisation TCP + Pour ajouter des localisations à une liste, appuyez sur la touche « ︙ » ou appuyez longuement sur un pays, une ville ou un serveur. + Appuyez sur « ︙ » pour créer une liste personnalisée Activer/désactiver le VPN Nom de l\'appareil : %1$s Temps restant : %1$s Réessayer UDP Le port TCP auquel le protocole de dissimulation UDP sur TCP doit se connecter sur le serveur VPN. + Annuler Non sécurisé CONNEXION NON SÉCURISÉE VERSION NON PRISE EN CHARGE @@ -248,6 +297,7 @@ Installez Mullvad VPN (%1$s) pour rester à jour Mise à jour disponible. Téléchargez-la pour rester en sécurité. Mettre à jour le serveur DNS + Mettre à jour le nom de la liste Votre e-mail (facultatif) Pour nous permettre de mieux vous assister, merci d\'écrire en anglais ou en suédois et d\'indiquer le pays à partir duquel vous vous connectez. Vérification du bon… diff --git a/android/lib/resource/src/main/res/values-it/plurals.xml b/android/lib/resource/src/main/res/values-it/plurals.xml index 5bccffaeee7b..15f393fe6d0f 100644 --- a/android/lib/resource/src/main/res/values-it/plurals.xml +++ b/android/lib/resource/src/main/res/values-it/plurals.xml @@ -16,6 +16,10 @@ %1$d giorno %1$d giorni + + %1$d posizione + %1$d posizioni + %1$d mese %1$d mesi diff --git a/android/lib/resource/src/main/res/values-it/strings.xml b/android/lib/resource/src/main/res/values-it/strings.xml index 5ec9034143cc..cc5a2884c8b0 100644 --- a/android/lib/resource/src/main/res/values-it/strings.xml +++ b/android/lib/resource/src/main/res/values-it/strings.xml @@ -11,8 +11,10 @@ Aggiungi 30 giorni di tempo (%1$s) Aggiungi un server Aggiungi server DNS + Aggiungi %1$s all\'elenco + Aggiungi posizioni Acquista credito sul nostro sito web o riscatta un voucher. - %1$s aggiunti al tuo account. + %1$s aggiunto al tuo account. Accetta e continua Tutte le applicazioni Tutti i luoghi @@ -34,6 +36,8 @@ 3. Per abilitare la modalità di blocco, clicca sull\'interruttore accanto a <b>Blocca connessioni senza VPN</b>. La modalità Blocco blocca tutti gli accessi a Internet se il tunnel VPN viene disconnesso manualmente. <br/><b>Avviso: questa impostazione blocca le app divise e la funzione di condivisione della rete locale</b>. Connettiti automaticamente al server all\'avvio dell\'app. + Utilizza piuttosto l\'impostazione di sistema <b>Sempre attivo</b> seguendo la guida in <b>%1$s</b> sopra. + Connessione automatica (precedente) Automatico Indietro Annunci @@ -61,7 +65,9 @@ Numero account di Mullvad copiato negli appunti Copiato negli appunti Copia numero di account + Crea Crea account + Crea nuovo elenco Creazione: %1$s Creazione account... CREAZIONE CONNESSIONE PROTETTA @@ -78,6 +84,8 @@ Intervalli validi: %1$s Impossibile risolvere l\'host del tunnel personalizzato. Prova a modificare le impostazioni. Elimina + Eliminare \"%1$s\"? + \"%1$s\" eliminato Elimina elenco Hai rimosso questo dispositivo. Per riconnetterti, dovrai effettuare nuovamente il login. Il dispositivo è inattivo @@ -86,6 +94,8 @@ Questo è il nome assegnato al dispositivo. Ogni dispositivo connesso a un account Mullvad riceve un nome univoco che ti aiuta a identificarlo quando gestisci i tuoi dispositivi nell\'app o sul sito web. Puoi avere fino a 5 dispositivi registrati su un account Mullvad. Se ti disconnetti, il dispositivo e il relativo nome verranno rimossi. Quando accedi nuovamente, il dispositivo assumerà un nuovo nome. + Ignora modifiche + Ignorare le modifiche? Disconnetti Disconnessione Ignora @@ -96,7 +106,12 @@ Ciò potrebbe causare problemi su determinati siti web, servizi e app. Non hai un numero di account? Questo indirizzo è già stato inserito. + Modifica elenchi personalizzati + Modifica elenco + Modifica elenchi + Modifica posizioni Modifica messaggio + Modifica nome Abilita Usa un server DNS personalizzato Inserisci MTU @@ -121,6 +136,8 @@ Ecco il tuo numero di account. Salvalo! Nascondi numero di account Predefinito + Incolla o scrivi le sovrascritture da importare + L\'importazione di nuove sovrascritture potrebbe sostituire alcune sovrascritture precedentemente importate. Importa Importa tramite testo Ricezione @@ -131,11 +148,16 @@ meno di un giorno rimanente meno di un minuto fa meno di un giorno + Nome dell\'elenco Connessione... Verifica dell\'acquisto... Condivisione rete locale Lo fa consentendo la comunicazione di rete al di fuori del tunnel verso intervalli multicast e broadcast locali, nonché da e verso questi intervalli IP privati: Questa funzione consente l\'accesso ad altri dispositivi sulla rete locale, ad esempio per la condivisione, la stampa, lo streaming, ecc. + %1$s (aggiunto) + %1$s aggiunto a \"%2$s\" + Posizioni + Le posizioni sono state modificate per \"%1$s\" Esci Numero di account valido Accesso effettuato @@ -154,12 +176,17 @@ Troppi dispositivi Numero di account Mullvad Solo di proprietà di Mullvad + Il nome è stato modificato in %1$s Benvenuto, questo dispositivo ora si chiama <b>%1$s</b>. Per maggiori dettagli, premi il pulsante delle informazioni in Account. NUOVO DISPOSITIVO CREATO + Nuovo elenco + Nessun elenco personalizzato disponibile Nessuna connessione Internet + Nessuna posizione trovata Nessun server corrispondente alle tue impostazioni, prova a cambiare server o impostazioni. Manca una chiave WireGuard valida. Gestisci le chiavi da Impostazioni avanzate. POSSIBILI PERDITE NEL TRAFFICO DI RETE + Non trovato Fornitori: %1$d L\'offuscamento nasconde il traffico WireGuard all\'interno di un altro protocollo. Può essere utilizzato per aggirare la censura e altri tipi di filtraggio, in cui una semplice connessione WireGuard verrebbe bloccata. On (UDP-over-TCP) @@ -168,9 +195,11 @@ On Invio Scaduto + Sovrascritture cancellate Di proprietà Proprietà Pagato fino al + La patch non corrisponde alle specifiche Per iniziare a utilizzare l\'app, devi prima aggiungere tempo al tuo account. Non siamo riusciti ad avviare il processo di pagamento, assicurati di avere la versione più recente di Google Play. Google Play non disponibile @@ -216,29 +245,49 @@ Se necessario, ti contatteremo all\'indirizzo %1$s Grazie! Sovrascritture IP server + Sovrascritture attive + Importa nuove sovrascritture da + File + Testo + Sovrascritture inattive Su alcune reti, dove vengono utilizzati vari tipi di censura, gli indirizzi IP dei nostri server vengono talvolta bloccati. Per aggirare questo problema, puoi importare un file o un testo, fornito dal nostro team di supporto, con nuovi indirizzi IP che sovrascrivono gli indirizzi predefiniti dei server nella vista Seleziona posizione. Se riscontri problemi di connessione ai server VPN, contatta l\'assistenza. + Reimposta sovrascritture + Tutte le sovrascritture verranno ripristinate e gli indirizzi IP dei server nella vista Seleziona posizione torneranno ai valori predefiniti. + Reimposta + Reimposta tutte le sovrascritture Impossibile impostare il server DNS di sistema. Invia una segnalazione del problema. Impossibile applicare le regole del firewall. Consulta la risoluzione dei problemi o invia una segnalazione del problema. Impostazioni Account Le modifiche alle impostazioni relative al DNS potrebbero non avere effetto immediato a causa dei risultati memorizzati nella cache. Le impostazioni DNS potrebbero non avere effetto immediato + Impossibile applicare la patch + Valore \"%1$s\" non valido o mancante + Limite di ricorsione + Impossibile analizzare la patch + Chiave \"%1$s\" sconosciuta o vietata + Importazione riuscita, sovrascritture attiva Preferenze Impostazioni VPN Mostra numero di account Mostra app di sistema + Ti consente di selezionare le app che devono accedere direttamente a Internet senza passare attraverso il tunnel VPN. + Attenzione: lo split tunneling rappresenta un rischio per la privacy. Impossibile avviare la connessione tunnel. Invia una segnalazione del problema. Invia Cambia posizione TCP + Per aggiungere posizioni a un elenco, premi \"︙\" o tieni premuto su un Paese, una città o un server. + Per creare un elenco personalizzato, premi \"︙\" Attiva/disattiva VPN Nome del dispositivo: %1$s Tempo rimasto: %1$s Riprova UDP A quale porta TCP deve connettersi il protocollo di offuscamento UDP-over-TCP sul server VPN. + Annulla Non protetto CONNESSIONE NON PROTETTA VERSIONE NON SUPPORTATA @@ -248,6 +297,7 @@ Installa Mullvad VPN (%1$s) per rimanere aggiornato Aggiornamento disponibile; esegui il download per rimanere protetto. Aggiorna server DNS + Aggiorna nome elenco La tua e-mail (opzionale) Per consentirci di assisterti meglio, scrivi in ​​inglese o in svedese e indica da quale Paese ti stai connettendo. Verifica del voucher… diff --git a/android/lib/resource/src/main/res/values-ja/plurals.xml b/android/lib/resource/src/main/res/values-ja/plurals.xml index 4350a8d33e34..7397e7c25d6a 100644 --- a/android/lib/resource/src/main/res/values-ja/plurals.xml +++ b/android/lib/resource/src/main/res/values-ja/plurals.xml @@ -12,6 +12,9 @@ %1$d日 + + %1$d 件の場所 + %1$dヶ月 diff --git a/android/lib/resource/src/main/res/values-ja/strings.xml b/android/lib/resource/src/main/res/values-ja/strings.xml index 67ecf7f2a907..842c9b9a92a4 100644 --- a/android/lib/resource/src/main/res/values-ja/strings.xml +++ b/android/lib/resource/src/main/res/values-ja/strings.xml @@ -11,8 +11,10 @@ 30日分を追加する (%1$s) サーバーを追加 DNS サーバーを追加 + %1$s をリストに追加する + 場所の追加 当社ウェブサイトでクレジットを購入するか、バウチャーを使用してください。 - %1$sがアカウントに追加されました。 + %1$sがご利用のアカウントに追加されました。 同意して続行 すべてのアプリケーション すべての場所 @@ -34,6 +36,8 @@ 3. ロックダウンモードを有効にするには、[<b>VPNを使用していない接続をブロック</b>] の横のトグルボタンをクリックします。 ロックダウンモードはVPNトンネルが手動で切断された場合にすべてのインターネットアクセスをブロックします。<br/><b>警告: この設定は分割アプリとローカルネットワーク共有機能をブロックします。</b> アプリ起動時に自動的にサーバーに接続します。 + 上の <b>%1$s</b> のガイドに従い、<b>Always-on</b> のシステム設定を代わりに使用してください。 + 自動接続 (レガシー) 自動 戻る 広告 @@ -61,7 +65,9 @@ Mullvadアカウント番号をクリップボードにコピーしました クリップボードにコピーしました アカウント番号のコピー + 作成 アカウントを作成する + リストの新規作成 作成日: %1$s アカウントを作成中... セキュリティ保護接続を確立中 @@ -78,6 +84,8 @@ 有効な範囲: %1$s カスタムトンネルのホストを解決できません。設定を変更してみてください。 削除 + \"%1$s\" を削除しますか? + \"%1$s\" は削除されました リストを削除 このデバイスを削除しました。再度接続するには、ログインし直す必要があります。 デバイスが無効です @@ -86,6 +94,8 @@ これはデバイスに割り当てられる名前です。Mullvadアカウントにログインするデバイスごとに一意の名前が付けられるため、アプリまたはウェブサイトでデバイスを管理する際にデバイスを区別しやすくなります。 1つのMullvadアカウントで最大5台のデバイスにログインできます。 ログアウトすると、デバイスとデバイス名が削除されます。もう一度ログインすると、デバイスに新しい名前が付けられます。 + 破棄 + 変更内容を破棄しますか? 接続解除 接続解除中 閉じる @@ -96,7 +106,12 @@ これにより、特定のWebサイト、サービス、アプリで問題が発生する可能性があります。 アカウント番号を持っていませんか? このアドレスは入力済みです。 + カスタムリストを編集する + リストの編集 + リストを編集 + 場所の編集 メッセージを編集する + 名前を編集 有効にする カスタムDNSサーバーを使う MTU を入力 @@ -121,6 +136,8 @@ これがあなたのアカウント番号です。保存してください! アカウント番号の非表示 デフォルト + インポートするオーバーライドを貼り付けるか、または記入してください + 新しいオーバーライドをインポートすると、以前インポートしたオーバーライドが置き換えられる可能性があります。 インポート テキストでインポート 内側 @@ -131,11 +148,16 @@ 残り1日未満 1分未満前 1日未満 + リスト名 接続中... 購入を確認中... ローカルネットワーク共有 これは、トンネル外のネットワーク通信をローカルのマルチキャストおよびブロードキャスト範囲、および以下のプライベート IP 範囲との間で許可することによって行われます。 この機能は、共有、印刷、ストリーミングなどのため、ローカルネットワーク上の他デバイスへのアクセスを許可します。 + %1$s (追加済み) + %1$s が \"%2$s\" に追加されました + 場所 + \"%1$s\" の場所が変更されました ログアウト 有効なアカウント番号 ログインしました @@ -154,12 +176,17 @@ デバイスが多すぎます Mullvadアカウント番号 Mullvad 所有サーバーのみ + 名前が %1$s に変更されました ようこそ。このデバイスの名前は<b>%1$s</b>です。詳細はアカウントの情報ボタンで確認してください。 新しいデバイスが作成されました + 新規リスト + 利用可能なカスタムリストはありません インターネット接続がありません + 場所が見つかりませんでした 設定に一致するサーバーはありません。サーバーまたは他の設定を変更してみてください。 有効なWireGuard鍵が見つかりません。詳細設定で鍵を管理してください。 ネットワーク通信が漏洩している可能性があります + 見つかりませんでした プロバイダ数: %1$d 難読化は、WireGuardトラフィックを別のプロトコル内に隠します。プレーンなWireGuard接続がブロックされる検閲やその他のフィルタリングを回避するために使用できます。 オン (UDP-over-TCP) @@ -168,9 +195,11 @@ オン 外側 時間切れ + オーバーライドがクリアされました 自社サーバー 所有権 次の日時まで支払い済み + パッチが仕様に一致していません アプリを使い始めるには、まずはアカウントに時間を追加する必要があります。 決済処理を開始できませんでした。最新バージョンのGoogle Playを使用していることを確認してください。 Google Playを使用できません @@ -216,29 +245,49 @@ 必要に応じて %1$s 宛にご連絡します  ありがとうございます! サーバーIPのオーバーライド + オーバーライドは有効です + 新しいオーバーライドのインポート + ファイル + テキスト + オーバーライドは無効です 各種の検閲が使用されている一部のネットワークでは、サーバーIPアドレスがブロックされる場合があります。 これを回避するには、「場所を選択」ビューでサポートチームが提供したサーバーのデフォルトアドレスをオーバーライドする新しいIPアドレスを含むファイルまたはテキストをインポートできます。 VPNサーバーへの接続に問題が生じている場合は、サポートにお問い合わせください。 + オーバーライドをリセット + すべてのオーバーライドはリセットされ、場所の選択ビューのサーバーの IP アドレスはデフォルトに戻ります。 + リセット + すべてのオーバーライドをリセット システムのDNSサーバーを設定できません。問題の報告を送信してください。 ファイアウォールのルールを適用できません。問題に対処するか、問題の報告を送信してください。 設定 アカウント 結果がキャッシュされているため、DNS関連の設定の変更はすぐには適用されない可能性があります。 DNS設定はすぐに適用されない可能性があります + パッチを適用できませんでした + 値 \"%1$s\" は無効であるか、または見つかりません + 繰り返しの制限 + パッチを解析できません + キー \"%1$s\" は不明であるか、または禁止されています + インポートが正常に完了しました。オーバーライドは有効です 環境設定 VPN設定 アカウント番号の表示 システムアプリの表示 + VPN トンネルを経由せずに、直接インターネットにアクセスするアプリを選択できます。 + 注意: スプリットトンネリングには、プライバシーが公開されるリスクがあります。 トンネル接続を開始できません。問題の報告を送信してください。 送信 場所を切り替える TCP + リストに場所を追加するには、\"︙\" を押すか、または、国、都市、サーバーを長押ししてください。 + カスタムリストを作成するには、\"︙\" を押してください VPNの切り替え デバイス名: %1$s 残り時間: %1$s 再試行 UDP UDP-over-TCP難読化プロトコルで接続する必要のあるVPNサーバーのTCPポートです。 + 元に戻す セキュリティ保護されていません セキュリティ保護されていない接続 未対応のバージョン @@ -248,6 +297,7 @@ Mullvad VPN (%1$s) をインストールして常に最新の状態を保ちましょう アップデートできます。セキュリティを維持するにはダウンロードしてしてください。 DNS サーバーを更新 + リスト名の更新 あなたのメールアドレス (任意) 最適なサポートを提供するため、英語またはスウェーデン語でご入力ください。また、接続元の国をお知らせください。 バウチャーを確認中… diff --git a/android/lib/resource/src/main/res/values-ko/plurals.xml b/android/lib/resource/src/main/res/values-ko/plurals.xml index dc604c60ddad..9174284ab53a 100644 --- a/android/lib/resource/src/main/res/values-ko/plurals.xml +++ b/android/lib/resource/src/main/res/values-ko/plurals.xml @@ -12,6 +12,9 @@ %1$d일 + + 위치 %1$d개 + %1$d개월 diff --git a/android/lib/resource/src/main/res/values-ko/strings.xml b/android/lib/resource/src/main/res/values-ko/strings.xml index 25f7213cb6f1..a68034e3bfb6 100644 --- a/android/lib/resource/src/main/res/values-ko/strings.xml +++ b/android/lib/resource/src/main/res/values-ko/strings.xml @@ -11,6 +11,8 @@ 30일 시간 추가(%1$s) 서버 추가 DNS 서버 추가 + 목록에 %1$s 추가 + 위치 추가 웹 사이트에서 크레딧을 구매하거나 바우처를 사용하세요. %1$s이(가) 계정에 추가되었습니다. 동의하고 계속하기 @@ -34,6 +36,8 @@ 3. 잠금 모드를 활성화하려면 <b>VPN 없이 연결 차단</b> 옆에 있는 토글을 클릭합니다. VPN 터널이 수동으로 연결 해제되면 잠금 모드는 모든 인터넷 액세스를 차단합니다. <br/><b>경고: 이 설정은 분할 앱과 로컬 네트워크 공유 기능을 차단합니다</b>. 앱이 시작되면 자동으로 서버에 연결합니다. + 위의 <b>%1$s</b> 가이드에 따라 <b>상시 접속</b> 시스템 설정을 대신 사용하세요. + 자동 연결(레거시) 자동 뒤로 광고 @@ -61,7 +65,9 @@ 클립보드에 Mullvad 계정 번호 복사됨 클립보드에 복사됨 계정 번호 복사 + 생성 계정 생성 + 새 목록 생성 생성 날짜: %1$s 계정 생성 중... 보안 연결 생성 중 @@ -78,6 +84,8 @@ 유효한 범위: %1$s 사용자 지정 터널의 호스트를 확인할 수 없습니다. 설정을 변경해 보세요. 삭제 + \"%1$s\"을(를) 삭제하시겠습니까? + \"%1$s\"이(가) 삭제되었습니다 목록 삭제 이 장치를 제거했습니다. 다시 연결하려면 다시 로그인해야 합니다. 장치가 비활성 상태입니다. @@ -86,6 +94,8 @@ 이것은 장치에 할당된 이름입니다. Mullvad 계정에 로그인된 각 장치에는 앱이나 웹사이트에서 장치를 관리할 때 장치를 보다 쉽게 식별할 수 있는 고유한 이름이 부여됩니다. 최대 5개의 장치에서 하나의 Mullvad 계정에 로그인할 수 있습니다. 로그아웃하면 해당 장치와 장치 이름이 제거됩니다. 다시 로그인하면 장치에 새 이름이 부여됩니다. + 취소 + 변경 사항을 취소하시겠습니까? 연결 끊기 연결 해제 중 해제 @@ -96,7 +106,12 @@ 이 기능을 사용하면 특정 웹 사이트, 서비스 및 앱에서 문제가 발생할 수 있습니다. 계정 번호가 없으신가요? 이 주소는 이미 입력되었습니다. + 사용자 지정 목록 편집 + 목록 편집 + 목록 편집 + 위치 편집 메시지 편집 + 이름 편집 사용 사용자 지정 DNS 서버 사용 MTU 입력 @@ -121,6 +136,8 @@ 계정 번호는 다음과 같습니다. 저장하세요! 계정 번호 숨기기 기본값 + 가져올 재정의를 붙여넣거나 작성하세요 + 새로운 재정의를 가져오면 이전에 가져온 일부 재정의가 교체될 수 있습니다. 가져오기 텍스트를 통해 가져오기 @@ -131,11 +148,16 @@ 1일 이내 1분 이내 1일 미만 + 목록 이름 연결 중... 구매 확인 중... 로컬 네트워크 공유 이를 위해 터널 외부에서 로컬 멀티캐스트 및 브로드캐스트 범위는 물론 다음 개인 IP 범위와의 네트워크 통신을 허용합니다. 이 기능을 사용하면 공유, 인쇄, 스트리밍 등을 위해 로컬 네트워크의 다른 장치에 액세스할 수 있습니다. + %1$s(추가됨) + %1$s이(가) \"%2$s\"에 추가되었습니다 + 위치 + \"%1$s\"에 대해 위치가 변경되었습니다 로그아웃 유효한 계정 번호 다음으로 로그인 @@ -154,12 +176,17 @@ 장치가 너무 많음 Mullvad 계정 번호 Mullvad 소유만 + 이름이 %1$s(으)로 변경되었습니다 환영합니다! 이제 이 장치의 이름은 <b>%1$s</b>입니다. 자세한 내용을 보려면 계정의 정보 버튼을 누르세요. 새 장치가 생성됨 + 새 목록 + 이용 가능한 사용자 지정 목록 없음 인터넷에 연결되지 않음 + 위치를 찾을 수 없음 설정과 일치하는 서버가 없습니다. 서버 또는 기타 설정을 변경해 보세요. 유효한 WireGuard 키가 없습니다. 고급 설정에서 키를 관리하세요. 네트워크 트래픽이 유출될 수 있습니다. + 찾을 수 없음 제공업체: %1$d 난독 처리는 다른 프로토콜 내에서 WireGuard 트래픽을 숨깁니다. 일반 WireGuard 연결이 차단되는 상황에서 검열 및 기타 유형의 필터링을 우회하는 데 사용할 수 있습니다. 켜기(UDP-over-TCP) @@ -168,9 +195,11 @@ 켜기 아웃 시간 초과 + 재정의 지워짐 소유 소유권 유효 기간 + 사양과 일치하지 않는 패치 앱 사용을 시작하려면, 먼저 계정에 시간을 추가해야 합니다. 결제 프로세스를 시작할 수 없습니다. Google Play가 최신 버전인지 확인하세요. Google Play 사용 불가 @@ -216,29 +245,49 @@ 필요한 경우 %1$s(으)로 연락드리겠습니다. 감사합니다! 서버 IP 재정의 + 재정의 활성화 + 다음으로 새로운 재정의 가져오기: + 파일 + 텍스트 + 재정의 비활성화 다양한 유형의 검열이 사용되고 있는 일부 네트워크에서는 때때로 당사 서버 IP 주소가 차단됩니다. 이를 우회하려면 \'위치 선택\' 보기에서 서버의 기본 주소를 재정의하는 새 IP 주소를 사용하여 지원 팀에서 제공한 파일이나 텍스트를 가져올 수 있습니다. VPN 서버 연결에 문제가 있는 경우 지원 팀에 문의하세요. + 재정의 초기화 + 모든 재정의가 초기화되며 위치 선택 보기의 서버 IP 주소는 기본값으로 돌아갑니다. + 초기화 + 모든 재정의 초기화 시스템 DNS 서버를 설정할 수 없습니다. 문제 보고서를 보내주세요. 방화벽 규칙을 적용할 수 없습니다. 문제를 해결하거나 문제 보고서를 보내주세요. 설정 계정 DNS 관련 설정에 대한 변경 사항은 캐시된 결과로 인해 즉시 적용되지 않을 수도 있습니다. DNS 설정이 즉시 적용되지 않을 수도 있습니다 + 패치 적용 실패 + \"%1$s\"에 대해 유효하지 않거나 누락된 값 + 반복 제한 + 패치를 파싱할 수 없음 + 알 수 없거나 금지된 키 \"%1$s\" + 가져오기 성공, 재정의 활성화 환경 설정 VPN 설정 계정 번호 표시 시스템 앱 표시 + VPN 터널을 거치지 않고 인터넷에서 바로 액세스할 앱을 선택할 수 있습니다. + 주의: 분할 터널링 시 개인 정보가 위험할 수 있습니다. 터널 연결을 시작할 수 없습니다. 문제 보고서를 보내주세요. 제출 위치 전환 TCP + 목록에 위치를 추가하려면 \"︙\"를 누르거나 국가, 도시 또는 서버를 길게 누릅니다. + 사용자 지정 목록을 생성하려면 \"︙\"를 누릅니다 VPN 전환 장치 이름: %1$s 남은 시간: %1$s 다시 시도 UDP UDP-over-TCP 난독 처리 프로토콜이 VPN 서버에서 연결해야 하는 TCP 포트입니다. + 실행 취소 안전하지 않음 비보안 연결 지원되지 않는 버전 @@ -248,6 +297,7 @@ Mullvad VPN(%1$s)을 설치하여 최신 상태로 유지하세요. 업데이트를 사용할 수 있습니다. 안전을 유지하기 위해 다운로드하세요. DNS 서버 업데이트 + 목록 이름 업데이트 이메일(선택 사항) 더 나은 지원을 위해 영어 또는 스웨덴어로 메시지를 작성하고 연결 국가를 포함하세요. 바우처 확인 중… diff --git a/android/lib/resource/src/main/res/values-my/plurals.xml b/android/lib/resource/src/main/res/values-my/plurals.xml index 375825b40f93..8883c0f9c724 100644 --- a/android/lib/resource/src/main/res/values-my/plurals.xml +++ b/android/lib/resource/src/main/res/values-my/plurals.xml @@ -12,6 +12,9 @@ %1$d ရက် + + %1$d တည်နေရာများ + %1$d လ diff --git a/android/lib/resource/src/main/res/values-my/strings.xml b/android/lib/resource/src/main/res/values-my/strings.xml index 1ec1d2c14315..8676fd38d774 100644 --- a/android/lib/resource/src/main/res/values-my/strings.xml +++ b/android/lib/resource/src/main/res/values-my/strings.xml @@ -11,8 +11,10 @@ အချိန် ရက် 30 ကို‌ ပေါင်းထည့်ရန် (%1$s) ဆာဗာ ပေါင်းထည့်ရန် DNS ဆာဗာကို ပေါင်းထည့်ရန် + စာရင်းထဲသို့ %1$s ပေါင်းထည့်ရန် + တည်နေရာများ ပေါင်းထည့်ရန် ကျွန်ုပ်တို့၏ ဝက်ဘ်ဆိုက်တွင် ခရက်ဒစ် ဝယ်ယူပါ သို့မဟုတ် ဘောက်ချာဖြင့် လဲယူပါ။ - သင့် အကောင့်ထဲသို့ %1$s ကို ပေါင်းထည့်ထားပါသည်။ + သင့်အကောင့်သို့ %1$s ကို ပေါင်းထည့်ပြီးပါပြီ။ သဘောတူပြီး ဆက်လုပ်ရန် အပလီကေးရှင်း အားလုံး တည်နေရာအာလုံး @@ -34,6 +36,8 @@ 3. လော့ခ်ဒေါင်းစနစ်ကို ဖွင့်ရန် <b>VPN မပါဘဲ ချိတ်ဆက်မှုများ ပိတ်ဆို့ရန်</b> ဘေးရှိ ဖွင့်/ပိတ် ခလုတ်ကို နှိပ်ပါ။. VPN Tunnel ကို ချိတ်ဆက်မှုမှ ကိုယ်တိုင် ဖြုတ်လိုက်ပါက လော့ခ်ဒေါင်းစနစ်သည် အင်တာနက် ရယူသုံးစွဲခွင့် အာလုံးကို ပိတ်ဆို့ပါသည်။ <br/><b>သတိပြုရန်- ဤဆက်တင်သည် Split အက်ပ်များနှင့် ဒေသတွင်း ကွန်ရက် ဝေမျှမှု လုပ်ဆောင်ချက်ကို ပိတ်ဆို့ပါမည်</b>။ အက်ပ် စတင်ဆောင်ရွက်ချိန်တွင် ဆာဗာနှင့် အော်တို ချိတ်ဆက်သွားပါမည်။ + အထက်ပါ <b>%1$s</b> တွင် အောက်ပါ လမ်းညွှန်အတိုင်း လိုက်နာခြင်းဖြင့် <b>အမြဲဖွင့်</b> စနစ်ဆက်တင်ကို ‌အစားထိုး သုံးပါ။ + အော်တို ချိတ်ဆက်ရန် (ပင်မ) အလိုအလျောက် နောက်သို့ ကြောငြာများ @@ -61,7 +65,9 @@ Mullvad အကောင့်နံပါတ်ကို ကလစ်ဘုတ်တွင် ကူးထားပါသည် ကလစ်ဘုတ်တွင် ကူးယူပြီး အကောင့်နံပါတ်ကို ကူးရန် + ဖန်တီးမည် အကောင့် ဖန်တီးရန် + စာရင်းသစ် ဖန်တီးမည် ဖန်တီးသည့် အချိန်- %1$s အကောင့် ဖန်တီးနေဆဲ... လုံခြုံသည့် ချိတ်ဆက်မှုကို ဖန်တီးနေပါသည် @@ -78,6 +84,8 @@ အကျုံးဝင်သည့် အပိုင်းအခြား- %1$s စိတ်ကြိုက်ပြုလုပ်ထားသည့် Tunnel ၏ Host ကို ဖြေရှင်း၍ မရနိုင်ပါ။ သင့်ဆက်တင်ကို ပြောင်းကြည့်ပါ။ ဖျက်ရန် + \"%1$s\" ကို ဖျက်မည်လား။ + \"%1$s\" ကို ဖျက်ပြီးပါပြီ စာရင်းကို ဖျက်ရန် ဤစက်ကို ဖယ်ရှားပြီး ဖြစ်သည်။ ထပ်မံချိတ်ဆက်ရန်အတွက် ပြန်လည် ဝင်ရောက်ရန် လိုပါသည်။ စက်သည် သက်ဝင်လုပ်ဆောင်မှု မရှိပါ @@ -86,6 +94,8 @@ ဤအမည်မှာ စက်အတွက် သတ်မှတ်ထားသော အမည် ဖြစ်ပါသည်။ Mullvad အကောင့်တစ်ခုတွင် ဝင်ရောက်ထားသည့် စက်တစ်ခုစီသည် တစ်မူထူးခြားသည့် အမည်တစ်ခု ရရှိမည်ဖြစ်ပြီး အက်ပ် သို့မဟုတ် ဝက်ဘ်ဆိုက်ပေါ်တွင် သင့်စက်များကို စီမံသည့်အခါ သင်အနေဖြင့် ကွဲကွဲပြားပြားသိရှိအောင် ကူညီပေးပါသည်။ Mullvad အကောင့်တစ်ခုတွင် စက် 5 ခုအထိ ဝင်ရောက်ထားနိုင်ပါသည်။ ထွက်လိုက်ပါက စက်နှင့် စက်အမည်ကို ဖယ်ရှားပါသည်။ နောက်တစ်ကြိမ် ပြန်ဝင်ရောက်သည့်အခါ စက်သည် အမည်သစ်တစ်ခု ရရှိပါမည်။ + ပယ်ဖျက်မည် + အပြောင်းအလဲများကို ပယ်ဖျက်မည်လား။ ချိတ်ဆက်မှုဖြုတ်ရန် ချိတ်ဆက်မှုဖြုတ်နေပါသည် ဖယ်ပစ်ရန် @@ -96,7 +106,12 @@ ၎င်းသည် အချို့သော ဝဘ်ဆိုက်များ၊ ဝန်ဆောင်မှုများနှင့် အက်ပ်များတွင် ပြဿနာများကို ဖြစ်စေနိုင်သည်။ အကောင့်နံပါတ် မရှိ ဖြစ်နေပါသလား။ ဤလိပ်စာကို ရိုက်ထည့်ထားပြီး ဖြစ်ပါသည်။ + စိတ်ကြိုက် စာရင်းများ ဖန်တီးရန် + စာရင်းကို တည်းဖြတ်ရန် + စာရင်းများကို တည်းဖြတ်ရန် + တည်နေရာများကို တည်းဖြတ်ရန် မက်ဆေ့ချ် တည်းဖြတ်ရန် + အမည် တည်းဖြတ်ရန် ဖွင့်ရန် စိတ်ကြိုက် DNS ဆာဗာကို သုံးရန် MTU ကို ရိုက်ထည့်ရန် @@ -121,6 +136,8 @@ ဤသည်မှာ သင့်အကောင့်နံပါတ် ဖြစ်ပါသည်။ သိမ်းမှတ်ထားပါ။ အကောင့်နံပါတ်ကို ဝှက်ရန် ပုံသေ + ကျော်လွန် ပယ်ဖျက်မှုများကို ထည့်သွင်းရန် ကူးထည့်ပါ သို့မဟုတ် ရေးပါ + ကျော်လွန် ပယ်ဖျက်မှု အသစ်များကို ထည့်သွင်းခြင်းသည် ယခင်ထည့်သွင်းထားသော ကျော်လွန် ပယ်ဖျက်မှု အချို့ကို အစားထိုးနိုင်ပါသည်။ ထည့်ရန် စာသားမှတစ်ဆင့် ထည့်သွင်းရန် အဝင် @@ -131,11 +148,16 @@ တစ်ရက်အောက်သာ ကျန်တော့သည် လွန်ခဲ့သော စက္ကန့်ပိုင်း တစ်ရက်အောက် နည်းသည်။ + စာရင်း အမည် ချိတ်ဆက်နေဆဲ... ဝယ်ယူမှုကို စစ်ဆေးနေဆဲ... လိုကယ် ကွန်ရက် ဝေမျှမှု ဒေသတွင်း Multicast နှင့် Broadcast အပိုင်းအခြားများဆီသို့ သာမက ဤသီးသန့် IP အပိုင်းအခြားများထံမှနှင့် ၎င်းတို့ဆီသို့ Tunnel ပြင်ပ ကွန်ရက် ဆက်သွယ်မှုကို ခွင့်ပြုခြင်းဖြင့် ဤသည်ကို လုပ်ဆောင်ပါသည်။ ဤလုပ်ဆောင်ချက်သည် မျှဝေခြင်း၊ ပရင့်ထုတ်ခြင်း၊ တိုက်ရိုက်ကြည့်ရှုနားဆင်ခြင်းအစရှိသည်တို့အတွက် ဒေသတွင်း ကွန်ရက်ရှိ အခြားစက်များကို ရယူသုံးစွဲခွင့်ပေးပါသည်။ + %1$s (ပေါင်းထည့်ပြီး) + \"%1$s\" သို့ %2$s ကို ပေါင်းထည့်ပြီးပါပြီ + တည်နေရာများ + \"%1$s\" အတွက် တည်နေရာများကို ပြောင်းလိုက်ပါသည် ထွက်ရန် မှန်ကန်သည့် အကောင့်နံပါတ် ဝင်ရောက်ထားပြီး @@ -154,12 +176,17 @@ စက်များလွန်းနေသည် Mullvad အကောင့်နံပါတ် Mullvad ပိုင်ဆိုင်သည်များသာ + အမည်ကို %1$s သို့ ပြောင်းလိုက်ပါသည် ကြိုဆိုပါသည်၊ ယခုမှစ၍ ဤစက်ကို <b>%1$s</b> ဟု ခေါ်ဆိုပါမည်။ နောက်ထပ်အသေးစိတ်တို့အတွက် အကောင့်တွင် အချက်အလက် ခလုတ်ကို နှိပ်၍ ကြည့်နိုင်သည်။ စက်အသစ် ဖန်တီးထားသည် + စာရင်းသစ် + စိတ်ကြိုက် စာရင်းများကို မရရှိနိုင်ပါ အင်တာနက် ချိတ်ဆက်မှု မရှိပါ + တည်နေရာများကို ရှာမတွေ့ပါ သင့်ဆက်တင်နှင့် ကိုက်ညီသော ဆာဗာများ မရှိပါ၊ ဆာဗာ သို့မဟုတ် အခြားဆက်တင်တို့ကို ပြောင်းလဲရန် ကြိုးစားကြည့်ပါ။ အကျုံးဝင်သည့် WireGuard ကီး မရှိပါ။ အဆင့်မြင့်ဆက်တင် အောက်တွင် ကီးများကို စီမံခန့်ခွဲပါ။ ကွန်ရက် ကူးလူးမှု ပေါက်ကြားနေနိုင်ပါသည် + ရှာမတွေ့ပါ ပံ့ပိုးသူများ- %1$d Obfuscation သည် အခြားပရိုတိုကောလ်အတွင်းရှိ WireGuard ကူးလူးမှုကို ဝှက်ထားပေးပါသည်။ သာမန် WireGuard ချိတ်ဆက်မှုကို ပိတ်ဆို့မည့် အခြားသော စစ်ထုတ်မှု အမျိုးအစားများနှင့် ဆင်ဆာဖြတ်တောက်ခြင်းကို ရှောင်လွှဲနိုင်စေရာတွင် ကူညီနိုင်စေရန် ဤသည်ကို သုံးနိုင်ပါသည်။ ဖွင့် (UDP-over-TCP) @@ -168,9 +195,11 @@ ဖွင့် အထွက် အချိန်စေ့သွားပါပြီ + ကျော်လွန် ပယ်ဖျက်မှုများ ရှင်းပြီးပါပြီ အပိုင် ပိုင်ဆိုင်မှု ဖော်ပြပါအထိ ပေးချေထားပြီး + Patch သည် သတ်မှတ်ချက်နှင့် မကိုက်ညီပါ အက်ပ်ကို စသုံးရန်အတွက် ဦးစွာ သင့်အကောင့်တွင် အချိန်ပေါင်းထည့်ပေးရန် လိုအပ်ပါသည်။ လက်ရှိတွင် ပေးချေမှု လုပ်ငန်းစဉ်ကို စတင်၍ မရနိုင်ပါ၊ Google Play နောက်ဆုံး ဗားရှင်း သင့်တွင်ရှိနေကြောင်း သေချာပါစေ။ Google Play ကို မရရှိနိုင်ပါ @@ -216,29 +245,49 @@ လိုအပ်ပါက %1$s မှတစ်ဆင့် ကျွန်ုပ်တို့ထံ ဆက်သွယ်ပါ ကျေးဇူးတင်ပါသည်။ ဆာဗာ IP ကျော်လွန် ပယ်ဖျက်မှု + ကျော်လွန် ပယ်ဖျက်မှုများ သက်ဝင်နေသည် + ဖော်ပြပါဖြင့် ကျော်လွန် ပယ်ဖျက်မှု အသစ်များကို ထည့်သွင်းရန် + ဖိုင် + စာသား + ကျော်လွန် ပယ်ဖျက်မှုများ သက်ဝင်မှု မရှိပါ အမျိုးအမျိုးသော စိစစ်ဖြတ်တောက်မှု အမျိုးအစားများ အသုံးပြုသည့် ကွန်ရက်အချို့တွင် ကျွန်ုပ်တို့၏ ဆာဗာ IP လိပ်စာများကို တစ်ခါတစ်ရံ ပိတ်ဆို့ထားပါသည်။ ဤသည်ကို ရှောင်လွှဲရန် ကျွန်ုပ်တို့ အကူအညီပေးရေးအဖွဲ့မှ ပေးထားသော တည်နေရာ ရွေးရန် ပြသမှုအတွင်းရှိ ဆာဗာများ၏ ပုံသေ လိပ်စာများကို ကျော်လွန် ပယ်ဖျက်သည့် IP လိပ်စာအသစ်များဖြင့် ဖိုင် သို့မဟုတ် စာသားကို သင် ထည့်သွင်းနိုင်ပါသည်။ VPN ဆာဗာများကို ချိတ်ဆက်ရာတွင် ပြဿနာများရှိနေပါက အကူအညီပေးရေးအဖွဲ့ကို ဆက်သွယ်ပါ။ + ကျော်လွန် ပယ်ဖျက်မှုများကို ပြန်လည်သတ်မှတ်ရန် + ‌ကျော်လွန် ပယ်ဖျက်မှုအားလုံးကို ပြန်လည်သတ်မှတ်မည်ဖြစ်ပြီး တည်နေရာ ရွေးချယ်ရန် ပြသမှုတွင် ဆာဗာများ၏ IP လိပ်စာများကို ပုံသေအဖြစ် ပြန်ပြောင်းလိုက်ပါမည်။ + ပြန်လည်သတ်မှတ်ရန် + ကျော်လွန် ပယ်ဖျက်မှု အားလုံးကို ပြန်လည်သတ်မှတ်ရန် စနစ် DNS ဆာဗာကို သတ်မှတ်၍ မရနိုင်ပါ။ ပြဿနာ ရီပို့တ်တစ်ခု ပေးပို့ပေးပါ။ Firewall စည်းမျဉ်းများကို အသုံးချ၍ မရနိုင်ပါ။ ပြစ်ချက် ရှာဖွေဖယ်ရှာပေးပါ သို့မဟုတ် ပြဿနာ ရီပို့တ် ပေးပို့ပေးပါ။ ဆက်တင် အကောင့် DNS နှင့်ဆက်စပ်သော ဆက်တင်များ၌ ပြုလုပ်သည့် ပြောင်းလဲမှုများသည် ယာယီသိမ်းထားသော ရလဒ်များကြောင့် ချက်ချင်း အကျိုးမသက်ရောက်နိုင်ပါ။ DNS ဆက်တင်ကို ချက်ချင်း အကျိုးမရောက်နိုင်ပါ။ + ပတ်(ချ်) သုံးခြင်း မအောင်မြင်ပါ + \"%1$s\" တန်ဖိုး မမှန်ကန်ပါ သို့မဟုတ် မရှိပါ + ထပ်တလဲလဲ လုပ်ဆောင်မှု ကန့်သတ်ချက် + Patch ကို ခွဲခြမ်းစိတ်ဖြာ၍ မရပါ + မသိသော သို့မဟုတ် ပိတ်ပင်ထားသော ကီး \"%1$s\" + ထည့်သွင်းမှု အောင်မြင်ပါသည်၊ ကျော်လွန် ပယ်ဖျက်မှု သက်ဝင်ပါသည် လိုလားမှုများ VPN ဆက်တင်များ အကောင့်နံပါတ်ကို ပြရန် စနစ်အက်ပ်များ ပြရန် + VPN Tunnel ကို မဝင်ရောက်ဘဲ အင်တာနက်ကို တိုက်ရိုက် ဝင်ရောက်သုံးစွဲသင့်သည့် အက်ပ်များကို ရွေးချယ်ပေးပါ။ + သတိပြုရန်- Split tunneling သည် ကိုယ်ရေးအချက်အလက်လုံခြုံရေး အန္တရာယ်ရှိနေပါသည်။ Tunnel ချိတ်ဆက်မှုကို စတင်၍ မရနိုင်ပါ။ ပြဿနာ ရီပို့တ်တစ်ခု ပေးပို့ပေးပါ။ ပေးပို့ရန် တည်နေရာ ပြောင်းရန် TCP + စာရင်းထဲသို့ တည်နေရာများကို ပေါင်းထည့်ရန် \"︙\" ကို နှိပ်ပါ သို့မဟုတ် နိုင်ငံ၊ မြို့၊ ဆာဗာကို နှိပ်ပါ။ + စိတ်ကြိုက် စာရင်းများကို ဖန်တီးရန် \"︙\" ကို နှိပ်ပါ VPN ရွေးသုံးရန် စက်အမည်- %1$s ကျန်သည့် အချိန်- %1$s ထပ်ကြိုးစားရန် UDP VPN ဆာဗာကို ဖွင့်ရန် ၎င်း TCP ပေါ့တ် UDP-over-TCP Obfuscation ပရိုတိုကောလ်နှင့် ချိတ်ဆက်ထားသင့်ပါသည်။ + မလုပ်တော့ပါ မလုံခြုံပါ မလုံခြုံသည့် ချိတ်ဆက်မှု တွဲဖက်မလုပ်ဆောင်နိုင်သည့် ဗားရှင်း @@ -248,6 +297,7 @@ အပ်ဒိတ် ဖြစ်နေစေရန် Mullvad VPN (%1$s) ကို ထည့်သွင်းပါ အပ်ဒိတ် ရရှိနိုင်ပါပြီ၊ ဆက်လက် လုံခြုံစေရန် ဒေါင်းလုဒ်လုပ်ပါ။ DNS ဆာဗာကို အပ်ဒိတ်လုပ်ရန် + စာရင်း အမည်ကို အပ်ဒိတ်လုပ်ရန် သင့်အီးမေးလ် (မဖြည့်လည်း ရပါသည်) သင့်အား ပို၍ အကူအညီပေးနိုင်ရန် အင်္ဂလိပ်ဘာသာ သို့မဟုတ် ဆွီဒင်ဘာသာဖြင့် ရေးပြီး မည်သည့်နိုင်ငံမှ သင်ချိတ်ဆက်နေသည်ကို ထည့်သွင်းဖော်ပြပါ။ ဘောက်ချာကို စစ်ဆေးနေဆဲ… diff --git a/android/lib/resource/src/main/res/values-nb/plurals.xml b/android/lib/resource/src/main/res/values-nb/plurals.xml index ead0fb3ad455..a89cd08f3fa0 100644 --- a/android/lib/resource/src/main/res/values-nb/plurals.xml +++ b/android/lib/resource/src/main/res/values-nb/plurals.xml @@ -16,6 +16,10 @@ %1$d dag %1$d dager + + %1$d plassering + %1$d plasseringer + %1$d måned %1$d måneder diff --git a/android/lib/resource/src/main/res/values-nb/strings.xml b/android/lib/resource/src/main/res/values-nb/strings.xml index ebc5ef76215f..0782ddc74d02 100644 --- a/android/lib/resource/src/main/res/values-nb/strings.xml +++ b/android/lib/resource/src/main/res/values-nb/strings.xml @@ -11,6 +11,8 @@ Legg til 30 dager (%1$s) Legg til en server Legg til DNS-server + Legg til %1$s i listen + Legg til plasseringer Du kan enten kjøpe kreditt på nettsiden vår eller løse inn en kupong. %1$s ble lagt til kontoen din. Godta og fortsett @@ -34,6 +36,8 @@ 3. For å aktivere låsemodus trykker du på bryteren ved siden av <b>Blokker tilkoblinger uten VPN</b>. Låsemodusen blokkerer all internettilgang når VPN-tunnelen er manuelt frakoblet. <br/><b>Advarsel: Innstillingen blokkerer delte apper og funksjonen Deling over lokalt nettverk</b>. Kobler automatisk til en server når appen starter. + Bruk systeminnstillingen <b>alltid på</b> i stedet for å følge veiledningen i <b>%1$s</b> ovenfor. + Automatisk tilkobling (eldre) Automatisk Tilbake Annonser @@ -61,7 +65,9 @@ Kopierte Mullvad-kontonummer til utklippstavlen Kopiert til utklippstavlen Kopier kontonummer + Opprett Opprett konto + Opprett ny liste Opprettet: %1$s Oppretter konto ... OPPRETTER SIKKER TILKOBLING @@ -78,6 +84,8 @@ Gyldige verdiområder: %1$s Kunne ikke løse vert for egendefinert tunnel. Forsøk å endre innstillingene dine. Slett + Slette «%1$s»? + «%1$s» ble slettet Slett liste Du har fjernet denne enheten. For å koble til igjen, må du logge inn på nytt. Enheten er inaktiv @@ -86,6 +94,8 @@ Dette er navnet som er tildelt enheten. Enhver enhet som er logget inn på en Mullvad-konto, får et unikt navn som gjør det enklere for deg å identifisere den når du administrerer enheten i appen eller på nettsiden. Du kan ha opptil fem enheter logget inn på samme Mullvad-konto. Hvis du logger ut, vil enheten og enhetsnavnet bli fjernet. Når du logger inn igjen, vil enheten få et nytt navn. + Forkast + Forkaste endringer? Koble fra Kobler fra Ignorer @@ -96,7 +106,12 @@ Dette kan føre til problemer på enkelte nettsteder, tjenester og apper. Har du ikke et kontonummer? Denne adressen er allerede skrevet inn. + Endre egendefinerte lister + Endre liste + Endre lister + Endre plasseringer Rediger melding + Endre navn Aktiver Bruk egendefinert DNS-server Angi MTU @@ -121,6 +136,8 @@ Dette er kontonummeret ditt. Ta vare på det! Skjul kontonummer Standard + Lim eller skriv inn overstyringer som skal importeres + Importering av nye overstyringer kan erstatte tidligere importerte overstyringer. Importer Importer via tekst Inngående @@ -131,11 +148,16 @@ mindre enn én dag igjen mindre enn ett minutt siden mindre enn én dag + Listenavn Kobler til ... Bekrefter kjøp ... Deling over lokalt nettverk Den gjør det ved å tillate nettverkkommunikasjon utenfor tunnelen til lokale multicast- og sendingsintervall, samt mellom følgende privat IP-intervall: Funksjonen gir tilgang til andre enheter på det lokale nettverket for ting som deling, utskrift, strømming osv. + %1$s (lagt til) + %1$s ble lagt til «%2$s» + Plasseringer + Plasseringer ble endret for «%1$s» Logg ut Gyldig kontonummer Du er logget inn @@ -154,12 +176,17 @@ For mange enheter Mullvad-kontonummer Kun eid av Mullvad + Navn ble endret til %1$s Velkommen. Denne enheten har fått navnet <b>%1$s</b>. For å finne ut mer kan du bruke informasjonsknappen under Konto. NY ENHET OPPRETTET + Ny liste + Ingen tilgjengelige egendefinerte lister Ingen internettforbindelse + Ingen plasseringer funnet Ingen servere passer til innstillingene dine. Prøv å endre server eller andre innstillinger. Det mangler en gyldig WireGuard-nøkkel. Du kan behandle nøklene under avanserte innstillinger. DET KAN VÆRE EN NETTVERKSLEKKASJE HOS DEG + Ikke funnet Leverandører: %1$d Tilsløring skjuler WireGuard-trafikken i en annen protokoll. Man kan på den måten omgå sensur og andre typer filter i tilfeller der en vanlig WireGuard-tilkobling ville blitt blokkert. På (UDP-over-TCP) @@ -168,9 +195,11 @@ Utgående Tiden har utløpt + Overstyringer fjernet Eid Eierskap Betalt fram til + Oppdateringen samsvarer ikke med spesifikasjon For å starte bruken av appen, må du først legge til tid til kontoen. Vi kunne ikke starte betalingsprosessen. Kontroller om du har siste versjon av Google Play. Google Play utilgjengelig @@ -216,29 +245,49 @@ Vi vil kontakte deg på %1$s ved behov Takk! Overstyring av server-IP + Overstyringer aktive + Importer nye overstyringer via + Fil + Tekst + Overstyringer inaktive På enkelte nettverk der det brukes ulike typer sensur, kan server-IP-adressene av og til være blokkerte. For å omgå dette kan du importere en fil eller tekst, som du har fått fra kundestøtteteamet, med nye IP-adresser som overstyrer standardadressene til serverne i «Velg plassering». Hvis du har mistet tilkoblingen til VPN-serverne, kan du ta kontak med kundestøtten. + Tilbakestill overstyringer + Alle overstyringer tilbakestilles, og servernes IP-adresser i visningen «Velg plassering» går tilbake til standardoppsett. + Tilbakestill + Tilbakestill alle overstyringer Kunne ikke angi DNS-server for systemet. Send inn en problemrapport. Kunne ikke bruke brannmur-regler. Feilsøk eller send inn en problemrapport. Innstillinger Konto Endringer til DNS-relaterte innstillinger vil kanskje ikke tre i kraft umiddelbart på grunn av bufrede resultater. DNS-innstillinger vil kanskje ikke tre i kraft umiddelbart + Kunne ikke bruke oppdatering + Ugyldig eller mangler verdien «%1$s» + Rekursjonsgrense + Kunne ikke analysere oppdatering + Ukjent eller forbudt nøkkel «%1$s» + Importering vellykket. Overstyringer aktive Preferanser VPN-innstillinger Vis kontonummer Vis systemapper + Lar deg velge apper som skal få tilgang til internett direkte uten å gå gjennom VPN-tunnelen. + OBS: Delt tunnelering utgjør en risiko mot personvernet. Kunne ikke starte tunneltilkobling. Send inn en problemrapport. Send inn Bytt plassering TCP + Hvis du vil legge til plasseringer i en liste, trykker du på ︙ eller trykker på og holder inne et land, en by eller en server. + For å opprette en egendefinert liste trykker du på ︙ Velg VPN Enhetsnavn: %1$s Tid igjen: %1$s Prøv på nytt UDP TCP-porten som UDP-over-TCP-tilsløringen skal koble til på VPN-serveren. + Angre Usikret USIKKER TILKOBLING VERSJON UTEN STØTTE @@ -248,6 +297,7 @@ Installer Mullvad VPN (%1$s) for å holde deg oppdatert Oppdatering tilgjengelig. Last ned for å oppdatere sikkerheten. Oppdater DNS-serveren + Oppdater listenavn E-post (valgfritt) For at vi skal kunne hjelpe deg bedre, ber vi deg om å skrive på engelsk eller svensk og fortelle hvilket land du befinner deg i. Bekrefter kupong … diff --git a/android/lib/resource/src/main/res/values-nl/plurals.xml b/android/lib/resource/src/main/res/values-nl/plurals.xml index f303dff91f7c..14f4057a4521 100644 --- a/android/lib/resource/src/main/res/values-nl/plurals.xml +++ b/android/lib/resource/src/main/res/values-nl/plurals.xml @@ -16,6 +16,10 @@ %1$d dag %1$d dagen + + %1$d locatie + %1$d locaties + %1$d maand %1$d maanden diff --git a/android/lib/resource/src/main/res/values-nl/strings.xml b/android/lib/resource/src/main/res/values-nl/strings.xml index 9402bf2a4cb2..bd7789d09b16 100644 --- a/android/lib/resource/src/main/res/values-nl/strings.xml +++ b/android/lib/resource/src/main/res/values-nl/strings.xml @@ -11,6 +11,8 @@ 30 dagen tijd toevoegen (%1$s) Server toevoegen DNS-server toevoegen + Voeg %1$s toe aan lijst + Voeg locaties toe Koop krediet op onze website of wissel een voucher in. %1$s is toegevoegd aan uw account. Akkoord en doorgaan @@ -34,6 +36,8 @@ 3. Klik om Lockdownmodus in te schakelen op de schakelaar naast <b>Verbindingen zonder VPN blokkeren</b>. De Lockdownmodus blokkeert alle internettoegang als de VPN-tunnel handmatig wordt verbroken. <br><b>Waarschuwing: deze instelling blokkeert gesplitste apps en de functie Lokaal netwerk delen</b>. Automatisch verbinden met een server wanneer de app wordt gestart. + Gebruik in plaats hiervan de systeeminstelling <b>Altijd aan</b> door de aanwijzingen hierboven in <b>%1$s</b> te volgen. + Automatisch verbinden (verouderd) Automatisch Terug Advertenties @@ -61,7 +65,9 @@ Mullvad-accountnummer gekopieerd naar klembord Gekopieerd naar klembord Accountnummer kopiëren + Maken Account aanmaken + Nieuwe lijst maken Gemaakt: %1$s Account aanmaken... BEVEILIGDE VERBINDING AANMAKEN @@ -78,6 +84,8 @@ Geldige bereiken: %1$s Kan host van aangepaste tunnel niet omzetten. Probeer uw instellingen te wijzigen. Verwijderen + \"%1$s\" verwijderen? + \"%1$s\" is verwijderd Lijst verwijderen U hebt dit apparaat verwijderd. U moet zich opnieuw aanmelden om het opnieuw te verbinden. Apparaat is niet actief @@ -86,6 +94,8 @@ Dit is de naam die aan het apparaat is toegewezen. Elk apparaat dat is aangemeld op een Mullvad-account, krijgt een unieke naam waarmee u het kunt identificeren wanneer u uw apparaten beheert in de app of op de website. U kunt maximaal 5 apparaten aangemeld hebben op één Mullvad-account. Als u zich afmeldt, worden het apparaat en de apparaatnaam verwijderd. Wanneer u zich weer aanmeldt, krijgt het apparaat een nieuwe naam. + Verwerpen + Wijzigingen verwerpen? Verbinding verbreken Verbinding wordt verbroken Negeren @@ -96,7 +106,12 @@ Dit kan problemen veroorzaken met bepaalde websites, diensten en apps. Hebt u geen accountnummer? Dit adres is al ingevoerd. + Aangepaste lijsten bewerken + Lijst bewerken + Lijsten bewerken + Locaties bewerken Bericht bewerken + Naam bewerken Inschakelen Aangepaste DNS-server gebruiken Voer MTU in @@ -121,6 +136,8 @@ Hier is uw accountnummer. Sla het op! Accountnummer verbergen Standaard + Plak of schrijf overschrijvingen om te importeren + Het importeren van nieuwe overschrijvingen kan sommige eerder geïmporteerde overschrijvingen vervangen. Importeren Importeren via tekst In @@ -131,11 +148,16 @@ minder dan een dag over minder dan een minuut geleden minder dan één dag + Lijstnaam Verbinden ... Aankoop controleren ... Delen op lokaal netwerk De functie doet dit door netwerkcommunicatie buiten de tunnel naar lokale multicast- en broadcastbereiken toe te staan, alsmede van en naar deze private IP-adresbereiken: Deze functie geeft toegang tot andere apparaten op het lokale netwerk, bijvoorbeeld voor delen, afdrukken, streamen en dergelijke + %1$s (toegevoegd) + %1$s is toegevoegd aan \"%2$s\" + Locaties + Locaties zijn gewijzigd voor \"%1$s\" Afmelden Geldig accountnummer Aangemeld @@ -154,12 +176,17 @@ Te veel apparaten Mullvad-accountnummer Alleen in eigendom van Multivad + Naam is gewijzigd in %1$s Welkom, dit apparaat heet nu <b>%1$s</b>. Zie voor meer informatie de infoknop in Account. NIEUW APPARAAT GEMAAKT + Nieuwe lijst + Geen aangepaste lijsten beschikbaar Geen internetverbinding + Geen locaties gevonden Er zijn geen servers die overeenkomen met uw instellingen. Probeer een andere server of andere instellingen. Geldige WireGuard-sleutel ontbreekt. Beheer sleutels onder Geavanceerde instellingen. U LEKT MOGELIJK NETWERKVERKEER + Niet gevonden Providers: %1$d Obfuscatie verbergt het WireGuard-verkeer in een ander protocol. Het kan worden gebruikt om censuur en andere soorten filtering te omzeilen, waar een gewone WireGuard-verbinding zou worden geblokkeerd. Aan (UDP-over-TCP) @@ -168,9 +195,11 @@ Aan Uit Geen tijd meer + Overschrijvingen gewist In eigendom Eigendom Betaald tot + Patch komt niet overeen met specificatie Om de app te gebruiken, moet u eerst tijd toevoegen aan uw account. We kunnen het betalingsproces niet starten. Controleer of u de nieuwste versie van Google Play hebt. Google Play niet beschikbaar @@ -216,29 +245,49 @@ Indien nodig nemen we u contact op via %1$s Bedankt! Overschrijving van server-IP-adressen + Overschrijvingen actief + Nieuwe overschrijvingen importeren vanuit + Bestand + Tekst + Overschrijvingen inactief Op sommige netwerken, waar verschillende soorten censuur worden gebruikt, worden onze server-IP-adressen soms geblokkeerd. Om dit te omzeilen, kunt u een door ons ondersteuningsteam verstrekt bestand of tekst importeren, met nieuwe IP-adressen die de standaardadressen van de servers in de weergave Locatie selecteren overschrijven. Als u problemen hebt met het verbinden met VPN-servers, neem dan contact op met de ondersteuning. + Overschrijvingen resetten + Alle overschrijvingen worden gereset en IP-adressen van servers in Locatie selecteren worden teruggezet naar de standaardwaarden. + Resetten + Alle overschrijvingen resetten Kan DNS-server van systeem niet instellen. Stuur een probleemrapport. Kan firewallregels niet toepassen. Los problemen op of stuur een probleemmelding. Instellingen Account Wijzigingen in DNS-gerelateerde instellingen worden mogelijk niet onmiddellijk van kracht vanwege gecachete resultaten. DNS-instellingen worden mogelijk niet onmiddellijk van kracht + Patch toepassen mislukt + Ongeldige of ontbrekende waarde \"%1$s\" + Recursielimiet + Kan patch niet parsen + Onbekende of verboden sleutel \"%1$s\" + Importeren geslaagd, overschrijvingen actief Voorkeuren VPN-instellingen Accountnummer weergeven Systeemapps weergeven + Hier kunt u apps selecteren die rechtstreeks toegang tot het internet moeten hebben zonder via de VPN-tunnel te gaan. + Let op: split tunneling is een privacyrisico. Kan de tunnelverbinding niet starten. Stuur een probleemrapport. Verzenden Locatie wijzigen TCP + Druk op de \"︙\" of druk lang op een land, plaats of server om locaties toe te voegen. + Druk op de \"︙\" om een aangepaste lijst te maken VPN in-/uitschakelen Apparaatnaam: %1$s Resterende tijd: %1$s Probeer het opnieuw UDP Met welke TCP-poort moet het UDP-over-TCP-obfuscatieprotocol verbinding maken op de VPN-server. + Ongedaan maken Niet beveiligd NIET-BEVEILIGDE VERBINDING NIET-ONDERSTEUNDE VERSIE @@ -248,6 +297,7 @@ Installeer Mullvad VPN (%1$s) om up-to-date te blijven Update beschikbaar, download deze om veilig te blijven. DNS-server bijwerken + Lijstnaam bijwerken Uw e-mailadres (optioneel) Om u beter te kunnen helpen, kunt u in het Engels of Zweeds schrijven. Vermeld uit welk land u komt. Voucher verifiëren … diff --git a/android/lib/resource/src/main/res/values-pl/plurals.xml b/android/lib/resource/src/main/res/values-pl/plurals.xml index b6c444c80c80..75ffdd9650bf 100644 --- a/android/lib/resource/src/main/res/values-pl/plurals.xml +++ b/android/lib/resource/src/main/res/values-pl/plurals.xml @@ -24,6 +24,12 @@ %1$d dni %1$d dnia + + %1$d lokalizacja + %1$d lokalizacje + %1$d lokalizacji + %1$d lokalizacji + %1$d miesiąc %1$d miesiące diff --git a/android/lib/resource/src/main/res/values-pl/strings.xml b/android/lib/resource/src/main/res/values-pl/strings.xml index 1ab45e2d9cbd..e02e7fa33ae8 100644 --- a/android/lib/resource/src/main/res/values-pl/strings.xml +++ b/android/lib/resource/src/main/res/values-pl/strings.xml @@ -11,8 +11,10 @@ Dodaj 30 dni (%1$s) Dodaj serwer Dodaj serwer DNS + Dodaj lokalizację %1$s do listy + Dodaj lokalizacje Doładuj w naszej witrynie internetowej lub zrealizuj kupon. - Do Twojego konta dodano %1$s. + Do konta dodano %1$s. Zaakceptuj i kontynuuj Wszystkie aplikacje Wszystkie lokalizacje @@ -34,6 +36,8 @@ 3. Aby włączyć tryb Lockdown, kliknij przełącznik opcji <b>Blokuj połączenia bez VPN</b>. Jeśli tunel VPN jest ręcznie odłączony, wówczas tryb Lockdown całkowicie blokuje dostęp do Internetu. <br/><b>Ostrzeżenie: to ustawienie blokuje aplikacje dzielące i udostępnianie sieci lokalnej</b>. Automatycznie łącz z serwerem po uruchomieniu aplikacji. + Zamiast tego użyj ustawienia systemowego <b>Zawsze włączone</b>, postępując zgodnie z poradnikiem w powyższej sekcji <b>%1$s</b>. + Automatyczne łączenie (starsza wersja) Automatycznie Wstecz Reklamy @@ -61,7 +65,9 @@ Skopiowano numer konta Mullvad do schowka Skopiowano do schowka Kopiuj numer konta + Utwórz Utwórz konto + Utwórz nową listę Utworzono: %1$s Tworzenie konta... TWORZENIE BEZPIECZNEGO POŁĄCZENIA @@ -78,6 +84,8 @@ Prawidłowe zakresy: %1$s Nie można rozpoznać hosta tunelu niestandardowego. Spróbuj zmienić ustawienia. Usuń + Usunąć „%1$s”? + Usunięto „%1$s” Usuń listę Urządzenie usunięto. Aby połączyć się ponownie, musisz się ponownie zalogować. Urządzenie nieaktywne @@ -86,6 +94,8 @@ Jest to nazwa przypisana do urządzenia. Każde urządzenie zalogowane na koncie Mullvad otrzymuje unikalną nazwę, która pozwala zidentyfikować je podczas zarządzania urządzeniami w aplikacji lub za pośrednictwem witryny internetowej. Na jednym koncie Mullvad może być zalogowanych maksymalnie 5 urządzeń. Przy wylogowaniu urządzenie i jego nazwa zostają usunięte. Po ponownym zalogowaniu urządzenie otrzymuje nową nazwę. + Odrzuć + Odrzucić zmiany? Rozłącz Rozłączanie Odrzuć @@ -96,7 +106,12 @@ Może to powodować problemy z niektórymi witrynami internetowymi, usługami i aplikacjami. Nie masz numeru konta? Ten adres został już wprowadzony. + Edytuj listy niestandardowe + Edytuj listę + Edytuj listy + Edytuj lokalizacje Edytuj wiadomość + Edytuj nazwę Włącz Użyj niestandardowego serwera DNS Wprowadź MTU @@ -121,6 +136,8 @@ Oto Twój numer konta. Zachowaj go! Ukryj numer konta Domyślnie + Wklej lub wpisz zastąpienia do zaimportowania + Wskutek importu nowych zastąpień zastąpione mogą zostać niektóre wcześniej zaimportowane zastąpienia. Importuj Import tekstowy Wejście @@ -131,11 +148,16 @@ pozostał mniej niż jeden dzień mniej niż minutę temu mniej niż jeden dzień + Nazwa listy Łączenie... Weryfikowanie zakupu... Udostępnianie sieci lokalnej Zapewnia to poprzez umożliwienie komunikacji sieciowej poza tunelem do lokalnych zakresów adresów IP multiemisji i emisji, a także do i z następujących prywatnych zakresów adresów IP: Funkcja ta umożliwia dostęp do innych urządzeń w sieci lokalnej, np. w celu udostępniania, drukowania, transmisji strumieniowej itd. + %1$s (dodano) + Lokalizację %1$s dodano do „%2$s” + Lokalizacje + Zmieniono lokalizacje dla „%1$s” Wyloguj się Prawidłowy numer konta Zalogowano jako @@ -154,12 +176,17 @@ Zbyt wiele urządzeń Numer konta Mullvad Wyłącznie firmy Mullvad + Nazwę zmieniono na %1$s Witaj, to urządzenie nazywa się teraz <b>%1$s</b>. Więcej szczegółów znajdziesz, korzystając z przycisku Informacje na koncie. UTWORZONO NOWE URZĄDZENIE + Nowa lista + Brak dostępnych list niestandardowych Brak połączenia z Internetem + Nie znaleziono lokalizacji Żaden serwer nie odpowiada ustawieniom. Spróbuj zmienić serwer lub inne ustawienia. Brak prawidłowego klucza WireGuard. Zarządzaj kluczami w Ustawieniach zaawansowanych. TWÓJ RUCH SIECIOWY MOŻE WYCIEKAĆ + Nie znaleziono Dostawcy: %1$d Zaciemnianie ukrywa ruch WireGuard w innym protokole. Można go użyć do obchodzenia cenzury i innych typów filtrowania, w których zwykłe połączenie WireGuard byłoby blokowane. Wł. (UDP-przez-TCP) @@ -168,9 +195,11 @@ Wł. Wyjście Koniec czasu + Usunięto zastąpienia Posiadane Własność Płatne do + Poprawka niezgodna ze specyfikacją Aby rozpocząć korzystanie z aplikacji, musisz najpierw dodać czas do swojego konta. Nie mogliśmy rozpocząć procesu płatności. Upewnij się, że masz najnowszą wersję aplikacji Google Play. Sklep Google Play jest niedostępny @@ -216,29 +245,49 @@ W razie potrzeby skontaktujemy się z Tobą pod adresem %1$s Dziękujemy! Zastąpienie adresu IP serwera + Zastąpienia aktywne + Importuj nowe zastąpienia z + Plik + Tekst + Zastąpienia nieaktywne W niektórych sieciach, w których stosowane są różnego rodzaju cenzury, adresy IP naszych serwerów są czasami blokowane. Aby obejść ten problem, można zaimportować plik lub tekst dostarczony przez nasz zespół pomocy technicznej, zawierający nowe adresy IP, które zastępują domyślne adresy serwerów w widoku Wybierz lokalizację. Jeśli masz problemy z łączeniem się z serwerami VPN, skontaktuj się z pomocą techniczną. + Resetuj zastąpienia + Wszystkie zastąpienia zostaną zresetowane i przywrócone zostaną domyślne adresy IP serwerów w widoku Wybierz lokalizację. + Resetuj + Resetuj wszystkie zastąpienia Nie można ustawić systemowego serwera DNS. Wyślij zgłoszenie problemu. Nie można zastosować reguł zapory. Rozwiąż problem lub wyślij zgłoszenie problemu. Ustawienia Konto Zmiany w ustawieniach związanych z usługą DNS mogą nie zostać wprowadzone natychmiast ze względu na zbuforowane wyniki. Ustawienia usługi DNS mogą nie zostać zastosowane natychmiast + Nie udało się zastosować poprawki + Nieprawidłowa lub brakująca wartość „%1$s” + Limit rekurencji + Nie można przetworzyć poprawki + Nieznany lub zabroniony klucz „%1$s” + Import zakończony powodzeniem, zastąpienia są aktywne Preferencje Ustawienia VPN Pokaż numer konta Pokaż aplikacje systemowe + Umożliwia wybranie aplikacji, które powinny uzyskiwać bezpośredni dostęp do Internetu bez przechodzenia przez tunel VPN. + Uwaga: dzielone tunelowanie stwarza ryzyko dla prywatności. Nie można uruchomić połączenia tunelowego. Wyślij zgłoszenie problemu. Prześlij Zmień lokalizację TCP + Aby dodać lokalizacje do listy, naciśnij przycisk „︙” lub naciśnij i przytrzymaj kraj, miasto albo serwer. + Aby utworzyć listę niestandardową, naciśnij przycisk „︙” Przełącz VPN Nazwa urządzenia: %1$s Pozostało: %1$s Spróbuj ponownie UDP Port TCP, z którym powinien łączyć się protokół zaciemniania UDP-przez-TCP na serwerze VPN. + Cofnij Niezabezpieczone NIEZABEZPIECZONE POŁĄCZENIE WERSJA NIEOBSŁUGIWANA @@ -248,6 +297,7 @@ Aby być na bieżąco, zainstaluj Mullvad VPN (%1$s) Dostępna jest aktualizacja. Aby zachować bezpieczeństwo, pobierz ją. Zaktualizuj serwer DNS + Zaktualizuj nazwę listy Twój adres e-mail (opcjonalnie) Abyśmy mogli lepiej Ci pomóc, napisz w języku angielskim lub szwedzkim i podaj kraj, z którego się łączysz. Weryfikowanie kuponu… diff --git a/android/lib/resource/src/main/res/values-pt/plurals.xml b/android/lib/resource/src/main/res/values-pt/plurals.xml index 09c99255611c..f4e20c19f137 100644 --- a/android/lib/resource/src/main/res/values-pt/plurals.xml +++ b/android/lib/resource/src/main/res/values-pt/plurals.xml @@ -16,6 +16,10 @@ %1$d dia %1$d dias + + %1$d localização + %1$d localizações + %1$d mês %1$d meses diff --git a/android/lib/resource/src/main/res/values-pt/strings.xml b/android/lib/resource/src/main/res/values-pt/strings.xml index fde7b4e0b19b..c7660b463668 100644 --- a/android/lib/resource/src/main/res/values-pt/strings.xml +++ b/android/lib/resource/src/main/res/values-pt/strings.xml @@ -11,8 +11,10 @@ Adicionar 30 dias (%1$s) Adicionar um servidor Adicionar servidor DNS + Adicionar %1$s à lista + Adicionar localizações Compre crédito no nosso sítio da web ou reclame um voucher. - %1$s adicionado(s) à sua conta. + %1$s foi adicionado à sua conta. Concordar e continuar Todas as aplicações Todas as localizações @@ -34,6 +36,8 @@ 3. Para ativar o modo de bloqueio, clique no botão de alternar junto a <b>Bloquear ligações sem VPN</b>. O modo de bloqueio bloqueia todo o acesso à internet se o túnel VPN for desligado manualmente. <br/><b>Aviso: esta definição bloqueia as apps divididas e a funcionalidade de partilha de rede local</b>. Liga-se automaticamente a um servidor quando a app inicia. + Alternativamente, utilize a definição de sistema <b>Sempre ligada</b>, seguindo o guia em <b>%1$s</b> acima. + Ligação automática (método antigo) Automático Retroceder Anúncios @@ -61,7 +65,9 @@ Número de conta Mullvad copiado para a área de transferência Copiado para a área de transferência Copiar número de conta + Criar Criar conta + Criar nova lista Criado: %1$s A criar conta... A CRIAR LIGAÇÃO SEGURA @@ -78,6 +84,8 @@ Intervalos válidos: %1$s Não foi possível resolver o anfitrião do túnel personalizado. Experimente alterar as suas definições. Eliminar + Eliminar \"%1$s\"? + \"%1$s\" foi eliminada Eliminar lista Removeu este dispositivo. Para voltar a ligar o dispositivo, terá de voltar a iniciar a sessão. O dispositivo está desativado @@ -86,6 +94,8 @@ Este é o nome atribuído ao dispositivo. Cada dispositivo com sessão iniciada numa conta Mullvad recebe um nome único que lhe ajuda a identificá-lo quando gere os seus dispositivos na app ou no site. Pode ter até 5 dispositivos com sessão iniciada numa só conta Mullvad. Se terminar a sessão, o dispositivo e o nome do dispositivo são removidos. Quando voltar a iniciar sessão, o dispositivo recebe um novo nome. + Descartar + Descartar alterações? Desligar A desligar Dispensar @@ -96,7 +106,12 @@ Pode causar problemas em alguns sites, serviços e apps. Não tem um número de conta? Este endereço já foi introduzido. + Editar listas personalizadas + Editar lista + Editar listas + Editar localizações Editar mensagem + Editar nome Ativar Usar servidor DNS personalizado Introduzir MTU @@ -121,6 +136,8 @@ Aqui tem o seu número de conta. Guarde-o! Ocultar número de conta Padrão + Cole ou introduza substituições a importar + A importação de novas substituições pode substituir algumas substituições importadas anteriormente. Importar Importar através de texto Entrada @@ -131,11 +148,16 @@ menos de um dia restante há menos de um minuto menos de um dia + Nome da lista A ligar... A verificar compra... Partilha de rede local Para tal, permite a comunicação de rede fora do túnel para intervalos locais de multicast e difusão, bem como de e para estes intervalos de IPs privados: Esta funcionalidade permite o acesso a outros dispositivos na rede local, por exemplo, para partilha, impressão, streaming, etc. + %1$s (adicionado) + %1$s foi adicionado a \"%2$s\" + Localizações + As localizações foram alteradas para \"%1$s\" Terminar sessão Número de conta válido Sessão iniciada @@ -154,12 +176,17 @@ Demasiados dispositivos Número de conta Mullvad Apenas propriedade de Mullvad + O nome foi alterado para %1$s Bem-vindo, este dispositivo é agora chamado <b>%1$s</b>. Para mais detalhes consulte o botão de informação na Conta. NOVO DISPOSITIVO CRIADO + Nova lista + Nenhuma lista personalizada disponível Sem ligação à internet + Nenhuma localização encontrada Nenhum servidor corresponde às suas definições. Tente alterar o servidor ou outras definições. Chave WireGuard válida em falta. Faça a gestão das chaves em Definições Avançadas. PODERÁ ESTAR A PERDER TRÁFEGO DE REDE + Não encontrada Fornecedores: %1$d A ofuscação oculta o tráfego do WireGuard dentro de outro protocolo. Pode ser utilizado para ajudar a contornar a censura e outros tipos de filtragem, onde uma simples ligação WireGuard seria bloqueada. Ligado (UDP sobre TCP) @@ -168,9 +195,11 @@ Ligado Saída Sem tempo + Substituições eliminadas Propriedade de Propriedade Pago até + O patch não corresponde à especificação Para começar a utilizar a aplicação, primeiro tem de adicionar tempo à sua conta. Não foi possível iniciar o processo de pagamento. Verifique se tem a versão mais recente do Google Play. Google Play indisponível @@ -216,29 +245,49 @@ Se necessário, iremos contactá-lo através de %1$s Obrigado! Substituição de IP de servidor + Substituições ativas + Importar novas substituições através de + Ficheiro + Texto + Substituições inativas Em algumas redes, onde são utilizados vários tipos de censura, os endereços IP dos nossos servidores são por vezes bloqueados. Para contornar esta situação, pode importar um ficheiro ou texto, fornecido pela nossa equipa de apoio, com novos endereços IP que substituem os endereços padrão dos servidores na vista Selecionar local. Se tiver problemas em ligar-se aos servidores VPN, contacte o apoio. + Repor substituições + Todas as substituições serão repostas e os endereços IP dos servidores, na vista Selecionar local, voltarão às predefinições. + Repor + Repor todas as substituições Não foi possível definir o servidor DNS do sistema. Envie um relatório do problema. Não foi possível aplicar as regras de firewall. Experimente a resolução de problemas ou envie um relatório do problema. Definições Conta As alterações às definições relacionadas com o DNS podem não fazer efeito imediatamente devido aos resultados em cache. As definições de DNS podem não fazer efeito imediatamente + Erro ao aplicar patch + Valor inválido ou em falta \"%1$s\" + Limite de recursão + Não foi possível analisar a correção + Chave \"%1$s\" desconhecida ou proibida + Importação bem-sucedida: substituições ativas Preferências Definições de VPN Mostrar número de conta Mostrar aplicações do sistema + Permite-lhe selecionar aplicações que devem aceder diretamente à Internet sem passar pelo túnel VPN. + Atenção: a divisão do túnel é um risco para a privacidade. Não foi possível iniciar a ligação de túnel. Envie um relatório do problema. Enviar Alterar local TCP + Para adicionar localizações a uma lista, prima o botão \"︙\" ou mantenha premido um país, uma cidade ou um servidor. + Para criar uma lista personalizada prima o botão \"︙\" Alternar VPN Nome do dispositivo: %1$s Tempo restante: %1$s Tentar novamente UDP A que porta TCP o protocolo de ofuscação UDP sobre TCP deve ligar-se no servidor VPN. + Anular Inseguro LIGAÇÃO INSEGURA VERSÃO NÃO SUPORTADA @@ -248,6 +297,7 @@ Instalar o Mullvad VPN (%1$s) para ficar atualizado Atualização disponível, transfira-a para ficar seguro. Atualizar servidor DNS + Atualizar nome da lista O seu email (opcional) Para o ajudar melhor, escreva em inglês ou sueco e indique o país de onde está a efetuar a ligação. A verificar voucher… diff --git a/android/lib/resource/src/main/res/values-ru/plurals.xml b/android/lib/resource/src/main/res/values-ru/plurals.xml index 8334afa7aad5..1f92a172dade 100644 --- a/android/lib/resource/src/main/res/values-ru/plurals.xml +++ b/android/lib/resource/src/main/res/values-ru/plurals.xml @@ -24,6 +24,12 @@ %1$d суток %1$d суток + + %1$d местоположение + %1$d местоположения + %1$d местоположений + %1$d местоположения + %1$d месяц %1$d месяца diff --git a/android/lib/resource/src/main/res/values-ru/strings.xml b/android/lib/resource/src/main/res/values-ru/strings.xml index 03bd3a85af7e..0d56c3c5bcbc 100644 --- a/android/lib/resource/src/main/res/values-ru/strings.xml +++ b/android/lib/resource/src/main/res/values-ru/strings.xml @@ -11,6 +11,8 @@ Добавить 30 дней (%1$s) Добавить сервер Добавить DNS-сервер + Добавить местоположение %1$s к списку + Добавление местоположений Пополните баланс у нас на сайте или погасите ваучер. На учетную запись добавлено время: %1$s. Согласиться и продолжить @@ -34,6 +36,8 @@ 3. Чтобы включить режим блокировки, нажмите на переключатель возле <b>Блокировать соединения без VPN</b>. Режим блокировки блокирует весь доступ к Интернету, если туннель VPN отключен вручную. <br/><b>Внимание: эта опция блокирует приложения с раздельным туннелированием и функцию обмена данными в локальной сети</b>. Автоматически подключаться к серверу при запуске приложения. + Вместо этого используйте системную настройку <b>Постоянная VPN</b>, следуя инструкциям в разделе <b>%1$s</b> выше. + Автоподключение (устаревшее) Автоматически Назад Реклама @@ -61,7 +65,9 @@ Номер учетной записи Mullvad скопирован в буфер обмена Скопировано в буфер обмена Копировать номер учетной записи + Создать Создать учетную запись + Создание нового списка Создано: %1$s Создание учетной записи... СОЗДАНИЕ ЗАЩИЩЕННОГО ПОДКЛЮЧЕНИЯ @@ -78,6 +84,8 @@ Допустимые диапазоны: %1$s Не удалось преобразовать имя узла пользовательского туннеля. Попробуйте изменить настройки. Удалить + Удалить список «%1$s»? + Список «%1$s» удален Удалить список Вы удалили это устройство. Чтобы снова подключиться, нужно будет выполнить вход. Устройство неактивно @@ -86,6 +94,8 @@ Это имя, присвоенное устройству. Каждое устройство, подключенное к учетной записи Mullvad, получает уникальное имя, которое помогает вам идентифицировать его при управлении устройствами в приложении или на сайте. К одной учетной записи Mullvad можно подключить до 5 устройств. Если вы выйдете, устройство и его имя удалятся. При повторном входе устройство получит новое имя. + Сбросить + Сбросить изменения? Отключить Отключение Закрыть @@ -96,7 +106,12 @@ Это может привести к проблемам при использовании некоторых сайтов, служб и приложений. У вас нет номера учетной записи? Этот адрес уже введен. + Изменение своих списков + Изменение списка + Изменить списки + Изменить местоположения Изменить сообщение + Изменить имя Включить Пользовательский DNS-сервер Введите MTU @@ -121,6 +136,8 @@ Вот номер вашей учетной записи. Сохраните его! Скрыть номер учетной записи По умолчанию + Вставьте или напишите переопределения для импорта + Импорт новых переопределений может заменить некоторые ранее импортированные переопределения. Импортировать Импортировать через текст Вход @@ -131,11 +148,16 @@ осталось менее суток менее минуты назад менее суток + Имя списка Идет подключение... Идет проверка оплаты... Обмен данными в локальной сети Это достигается путем разрешения сетевого взаимодействия вне туннеля с локальными многоадресными и широковещательными диапазонами, а также со следующими диапазонами частных IP-адресов: Эта функция позволяет получить доступ к другим устройствам в локальной сети, например, для организации общего доступа, печати, потоковой передачи и т. д. + %1$s (добавлено) + Местоположение %1$s добавлено к списку «%2$s» + Местоположения + Местоположения были изменены для «%1$s» Выйти Действительный номер учетной записи Вход выполнен @@ -154,12 +176,17 @@ Слишком много устройств Номер учетной записи Mullvad Только принадлежащие Mullvad + Имя изменено на «%1$s» Добро пожаловать, теперь это устройство называется <b>%1$s</b>. Для получения более подробной нажмите на кнопку «Информация» в учетной записи. СОЗДАНО НОВОЕ УСТРОЙСТВО + Новый список + Нет своих списков Нет подключения к Интернету + Местоположения не найдены Нет серверов, соответствующих вашим настройкам. Попробуйте изменить сервер или задайте другие настройки. Не найден действительный ключ WireGuard. Управлять ключами можно в дополнительных настройках. ВОЗМОЖНА УТЕЧКА СЕТЕВОГО ТРАФИКА + Не найдено провайдеры: %1$d Обфускация скрывает трафик WireGuard внутри другого протокола. Это может использоваться для обхода цензуры и других видов фильтрации, когда обычное соединение WireGuard было бы заблокировано. Вкл. (UDP через TCP) @@ -168,9 +195,11 @@ Включен Выход Закончилось время + Переопределения удалены Во владении Собственность Оплачено до + Патч не соответствует спецификации Чтобы пользоваться приложением, нужно добавить время на учетную запись. Не удалось начать процесс оплаты — убедитесь, что у вас установлена последняя версия Google Play. Google Play недоступен @@ -216,29 +245,49 @@ При необходимости мы свяжемся с вами по адресу %1$s Спасибо! Переопределение IP-адреса сервера + Переопределения активны + Импорт новых переопределений: + Файл + Текст + Переопределения неактивны В некоторых сетях, где используются различные виды цензуры, IP-адреса наших серверов иногда блокируются. Чтобы обойти эту проблему, можно импортировать файл или текст, предоставленный нашей службой поддержки, с новыми IP-адресами, которые заменяют адреса серверов по умолчанию в представлении «Выбор местоположения». Если у вас возникли проблемы с подключением к VPN-серверам, обратитесь в службу поддержки. + Сбросить переопределения + Все переопределения будут сброшены, а IP-адреса серверов в представлении «Выбор местоположения­» вернутся к значениям по умолчанию. + Сбросить + Сброс всех переопределений Не удалось установить системный DNS-сервер. Отправьте сообщение о проблеме. Невозможно применить правила брандмауэра. Устраните неполадки или отправьте сообщение о проблеме. Настройки Учетная запись Изменения в настройках, связанные с DNS, могут не сразу вступить в силу из-за кешированных результатов. Настройки DNS могут не сразу вступить в силу + Не удалось применить патч + Недопустимое или отсутствующее значение «%1$s» + Предел рекурсии + Не удалось проанализировать патч + Неизвестный или запрещенный ключ «%1$s» + Импортировано, переопределения активны Параметры Настройки VPN Показать номер учетной записи Показывать системные приложения + Позволяет выбрать приложения, которые должны получать доступ к Интернету напрямую, не проходя через VPN-туннель. + Внимание: раздельное туннелирование — это риск для конфиденциальности. Не удалось запустить туннельное подключение. Отправьте сообщение о проблеме. Отправить Сменить местоположение TCP + Чтобы добавить местоположения в список, нажмите «︙» или нажмите и удерживайте страну, город или сервер. + Чтобы создать свой список, нажмите «︙» Включение VPN Имя устройства: %1$s Осталось времени: %1$s Повторить попытку UDP TCP-порт, к которому должен подключаться протокол обфускации UDP через TCP на VPN-сервере. + Отменить Подключение не защищено НЕЗАЩИЩЕННОЕ ПОДКЛЮЧЕНИЕ НЕПОДДЕРЖИВАЕМАЯ ВЕРСИЯ @@ -248,6 +297,7 @@ Пользуйтесь актуальной версией — установите Mullvad VPN (%1$s) Вышло обновление. Установите его, чтобы защитить подключения. Обновить DNS-сервер + Обновление названия списка Ваша электронная почта (необязательно) Чтобы мы могли быстрее решить проблему, напишите нам на английском или шведском и укажите, из какой страны вы подключаетесь. Идет проверка ваучера… diff --git a/android/lib/resource/src/main/res/values-sv/plurals.xml b/android/lib/resource/src/main/res/values-sv/plurals.xml index 2622d12f68a2..e556fecabfb4 100644 --- a/android/lib/resource/src/main/res/values-sv/plurals.xml +++ b/android/lib/resource/src/main/res/values-sv/plurals.xml @@ -16,6 +16,10 @@ %1$d dag %1$d dagar + + %1$d plats + %1$d platser + %1$d månad %1$d månader diff --git a/android/lib/resource/src/main/res/values-sv/strings.xml b/android/lib/resource/src/main/res/values-sv/strings.xml index fbe8433920ec..972207b53396 100644 --- a/android/lib/resource/src/main/res/values-sv/strings.xml +++ b/android/lib/resource/src/main/res/values-sv/strings.xml @@ -11,6 +11,8 @@ Lägg till 30 dagar (%1$s) Lägg till en server Lägg till DNS-server + Lägg till %1$s i listan + Lägg till platser Du kan antingen köpa kredit på vår webbplats eller lösa in en kupong. %1$s har lagts till på ditt konto. Godkänn och fortsätt @@ -34,6 +36,8 @@ 3. Om du vill aktivera Låsningsläge klickar du på växlingsknappen bredvid <b>Blockera anslutningar utan VPN</b>. Låsningsläget blockerar all internetåtkomst om VPN-tunneln kopplas från manuellt. <br/><b>Varning! Den här inställningen blockerar delade appar och funktionen Lokal nätverksdelning (Local Network Sharing)</b>. Anslut automatiskt till en server när appen startas. + Använd systeminställningen <b>Alltid aktiverat</b> istället genom att följa guiden i <b>%1$s</b> ovan. + Anslut automatiskt (äldre) Automatisk Tillbaka Annonser @@ -61,7 +65,9 @@ Kopierade Mullvad-kontonummer till urklipp Kopierat till urklipp Kopiera kontonummer + Skapa Skapa konto + Skapa ny lista Skapades: %1$s Skapar konto... SKAPAR SÄKER ANSLUTNING @@ -78,6 +84,8 @@ Giltiga intervall: %1$s Det går inte att lösa värd för anpassad tunnel. Försök att ändra inställningarna. Ta bort + Ta bort \"%1$s\"? + \"%1$s\" har tagits bort Ta bort lista Du har tagit bort den här enheten. Du måste logga in igen för att återansluta. Enheten är inaktiv @@ -86,6 +94,8 @@ Det här är namnet som tilldelas enheten. Varje enhet som är inloggad på ett Mullvad-konto får ett unikt namn som hjälper dig att identifiera den när du hanterar dina enheter i appen eller på webbplatsen. Upp till fem enheter kan vara inloggade på ett Mullvad-konto. Om du loggar ut tas enheten och enhetsnamnet bort. När du loggar in igen får enheten ett nytt namn. + Ignorera + Ignorera ändringarna? Koppla från Kopplar från Ignorera @@ -96,7 +106,12 @@ Detta kan orsaka problem på vissa webbplatser, tjänster och appar. Har du inget kontonummer? Adressen har redan angetts. + Redigera anpassade listor + Redigera lista + Redigera listor + Redigera platser Redigera meddelande + Redigera namn Aktivera Använd anpassad DNS-server Ange MTU @@ -121,6 +136,8 @@ Här är ditt kontonummer. Spara det! Dölj kontonummer Standard + Klistra in eller skriv åsidosättningar som ska importeras + Om du importerar nya åsidosättningar kan tidigare importerade åsidosättningar ersättas. Importera Importera via text In @@ -131,11 +148,16 @@ mindre än en dag kvar mindre än en minut sedan mindre än en dag + Listnamn Ansluter ... Verifierar köp ... Lokal nätverksdelning Den gör det genom att tillåta nätverkskommunikation utanför tunneln till lokala multicast- och sändningsintervall, samt till och från dessa privata IP-intervall: Funktionen tillåter åtkomst till andra enheter på det lokala nätverket, t.ex. för att dela, skriva ut, streama osv. + %1$s (har lagts till) + %1$s har lagts till i \"%2$s\" + Platser + Platserna har ändrats för \"%1$s\" Logga ut Giltigt kontonummer Inloggad @@ -154,12 +176,17 @@ För många enheter Mullvad-kontonummer Endast Mullvad-ägd + Namnet har ändrats till %1$s Välkommen! Den här enheten heter nu <b>%1$s</b>. Använd informationsknappen i Konto för mer information. NY ENHET HAR SKAPATS + Ny lista + Inga anpassade listor är tillgängliga Ingen internetanslutning + Inga platser hittades Inga servrar matchar dina inställningar. Försök att byta server eller ändra inställningarna. Giltig WireGuard-nyckel saknas. Hantera nycklar i Avancerade inställningar. DU KANSKE HAR LÄCKAGE I NÄTVERKSTRAFIKEN + Hittades inte Leverantörer: %1$d Obfuskering döljer WireGuard-trafik inne i ett annat protokoll. Det kan användas för att kringgå censur och andra filtertyper där en vanlig WireGuard-anslutning skulle blockeras. På (UDP över TCP) @@ -168,9 +195,11 @@ Ut Ingen tid kvar + Åsidosättningar rensade Ägd Ägarskap Betalat till + Korrigering överensstämmer inte med specifikationen Om du vill börja använda appen måste du först lägga till tid i ditt konto. Vi kunde inte starta betalningsprocessen. Se till att du har den senaste versionen av Google Play. Google Play är inte tillgängligt @@ -216,29 +245,49 @@ Om det behövs kontaktar vi dig på %1$s Tack! Åsidosättning av server-IP + Åsidosättningar aktiva + Importera nya åsidosättningar med + Fil + Text + Åsidosättningar inaktiva På vissa nätverk där olika typer av censureringar används blockeras blir ibland vår servers IP-adresser blockerade. För att kringgå detta kan du importera en fil eller text, som tillhandahålls av vårt supportteam, med nya IP-adresser som åsidosätter servrarnas standardadresser i Välj platsvy. Kontakta supporten om du har problem med att ansluta till VPN-servrar. + Återställ åsidosättningar + Alla åsidosättningar återställs och servrarnas IP-adresser i vyn Välj plats återgår till standard. + Återställ + Återställ alla åsidosättningar Det går inte att konfigurera DNS-server. Skicka en problemrapport. Det går inte att tillämpa brandväggsregler. Felsök eller skicka en problemrapport. Inställningar Konto Ändringar i DNS-relaterade inställningar kanske inte börjar gälla direkt på grund av cachade resultat. DNS-inställningarna kanske inte börjar gälla direkt + Det gick inte att tillämpa korrigering + Värdet \"%1$s\" är ogiltigt eller saknas + Rekursionsgräns + Det går inte att parsa korrigering + Okänd eller förbjuden nyckel \"%1$s\" + Har importerats, åsidosättningar aktiva Inställningar VPN-inställningar Visa kontonummer Visa systemappar + Låter dig välja appar som ska ge dig direktåtkomst till internet utan att gå igenom VPN-tunneln. + Obs! Split tunneling är en sekretessrisk. Det går inte att starta tunnelanslutning. Skicka en problemrapport. Skicka Växla plats TCP + Om du vill lägga till platser i en lista kan du trycka på \"︙\" eller trycka länge på ett land, en stad eller en server. + Om du vill skapa en anpassad lista trycker du på \"︙\" Växla VPN Enhetsnamn: %1$s Tid kvar: %1$s Försök igen UDP Vilken TCP-port som UDP-över-TCP-obfuskeringsprotokoll bör ansluta till på VPN-servern. + Ångra Oskyddad OSÄKER ANSLUTNING VERSION UTAN STÖD @@ -248,6 +297,7 @@ Installera Mullvad VPN (%1$s) för att hålla dig uppdaterad Uppdatering tillgänglig. Ladda ned för att vara säker. Uppdatera DNS-server + Uppdatera listnamn Din e-postadress (valfritt) Skriv på engelska eller svenska och ange från vilket land du är ansluten så att vi kan hjälpa dig bättre. Verifierar kupong … diff --git a/android/lib/resource/src/main/res/values-th/plurals.xml b/android/lib/resource/src/main/res/values-th/plurals.xml index 9d0e66030f80..6afb878ef081 100644 --- a/android/lib/resource/src/main/res/values-th/plurals.xml +++ b/android/lib/resource/src/main/res/values-th/plurals.xml @@ -12,6 +12,9 @@ %1$d วัน + + %1$d ตำแหน่งที่ตั้ง + %1$d เดือน diff --git a/android/lib/resource/src/main/res/values-th/strings.xml b/android/lib/resource/src/main/res/values-th/strings.xml index 0ad6b06a53ae..33408d150399 100644 --- a/android/lib/resource/src/main/res/values-th/strings.xml +++ b/android/lib/resource/src/main/res/values-th/strings.xml @@ -11,6 +11,8 @@ เพิ่มเวลา 30 วัน (%1$s) เพิ่มเซิร์ฟเวอร์ เพิ่มเซิร์ฟเวอร์ DNS + เพิ่ม %1$s ลงในรายการ + เพิ่มตำแหน่งที่ตั้ง ซื้อเครดิตบนเว็บไซต์ของเรา หรือแลกรับบัตรกำนัล %1$s ถูกเพิ่มลงในบัญชีของคุณแล้ว ยอมรับและดำเนินการต่อ @@ -34,6 +36,8 @@ 3. ในการเปิดใช้โหมดล็อกดาวน์ ให้คลิกปุ่มเปิด/ปิดที่อยู่ถัดจาก<b>ปิดกั้นการเชื่อมต่อโดยไม่ใช้ VPN</b> โหมดล็อกดาวน์ปิดกั้นการเข้าถึงอินเทอร์เน็ตทั้งหมด หากช่องทาง VPN ถูกตัดการเชื่อมต่อด้วยตนเอง <br/><b>คำเตือน: การตั้งค่านี้จะบล็อกแอปที่มีการแยก และคุณลักษณะการแชร์เครือข่ายท้องถิ่น</b> เชื่อมต่อเซิร์ฟเวอร์โดยอัตโนมัติทันทีที่เปิดแอป + โปรดใช้การตั้งค่าระบบ<b>เปิดใช้งานเสมอ</b>แทน โดยทำตามคำแนะนำใน <b>%1$s</b> ที่อยู่ด้านบน + เชื่อมต่ออัตโนมัติ (เดิม) อัตโนมัติ ย้อนกลับ โฆษณา @@ -61,7 +65,9 @@ คัดลอกหมายเลขบัญชี Mullvad ไปยังคลิปบอร์ดแล้ว คัดลอกไปยังคลิปบอร์ดแล้ว คัดลอกหมายเลขบัญชี + สร้าง สร้างบัญชี + สร้างรายการใหม่ วันที่สร้าง: %1$s กำลังสร้างบัญชี... กำลังสร้างการเชื่อมต่อที่ปลอดภัย @@ -78,6 +84,8 @@ ช่วงที่ใช้ได้: %1$s ไม่พบโฮสต์ของช่องทางแบบกำหนดเอง กรุณาลองเปลี่ยนการตั้งค่าของคุณ ลบ + ลบ \"%1$s\" หรือไม่ + \"%1$s\" ถูกลบแล้ว ลบรายการ คุณได้ลบอุปกรณ์เครื่องนี้แล้ว หากต้องการเชื่อมต่ออีกครั้ง คุณจะต้องเข้าสู่ระบบใหม่อีกครั้ง อุปกรณ์ไม่ได้ใช้งาน @@ -86,6 +94,8 @@ นี่เป็นชื่อที่มอบหมายให้กับอุปกรณ์ อุปกรณ์แต่ละเครื่องที่ลงชื่อเข้าใช้บนบัญชี Mullvad จะได้รับชื่อเฉพาะ ที่จะช่วยคุณระบุอุปกรณ์ ในขณะที่คุณจัดการอุปกรณ์ของคุณในแอปหรือบนเว็บไซต์ คุณสามารถลงชื่อเข้าใช้อุปกรณ์ได้สูงสุด 5 เครื่อง กับบัญชี Mullvad หนึ่งบัญชี หากคุณออกจากระบบ อุปกรณ์และชื่ออุปกรณ์จะถูกลบออก และเมื่อคุณลงชื่อเข้าใช้อีกครั้ง อุปกรณ์จะได้รับชื่อใหม่ + ละทิ้ง + ละทิ้งการเปลี่ยนแปลงหรือไม่ ตัดการเชื่อมต่อ กำลังตัดการเชื่อมต่อ ละทิ้ง @@ -96,7 +106,12 @@ นี่อาจก่อให้เกิดปัญหากับบางเว็บไซต์ บริการ และแอปได้ ยังไม่มีหมายเลขบัญชีใช่ไหม ที่อยู่นี้ได้รับการป้อนไปแล้ว + แก้ไขรายการแบบกำหนดเอง + แก้ไขรายการ + แก้ไขรายการ + แก้ไขตำแหน่งที่ตั้ง แก้ไขข้อความ + แก้ไขชื่อ เปิดใช้งาน ใช้เซิร์ฟเวอร์ DNS แบบกำหนดเอง ป้อน MTU @@ -121,6 +136,8 @@ นี่คือหมายเลขบัญชีของคุณ จดบันทึกไว้ด้วยนะ! ซ่อนหมายเลขบัญชี ค่าเริ่มต้น + วางหรือพิมพ์โอเวอร์ไรด์เพื่อนำเข้า + การนำเข้าโอเวอร์ไรด์ใหม่ อาจแทนที่โอเวอร์ไรด์ที่นำเข้าก่อนหน้านี้ นำเข้า นำเข้าผ่านข้อความ เข้า @@ -131,11 +148,16 @@ เหลือเวลาน้อยกว่าหนึ่งวัน น้อยกว่าหนึ่งนาทีก่อน น้อยกว่าหนึ่งวัน + ชื่อรายการ กำลังเชื่อมต่อ... กำลังตรวจสอบยืนยันการซื้อ... การแชร์ในเครือข่ายท้องถิ่น ทำเช่นนี้ได้โดยการอนุญาตให้มีการสื่อสารเครือข่ายนอกอุโมงค์ ไปยังมัลติคาสต์และช่วงการเผยแพร่ในท้องถิ่น รวมถึงการสื่อสารไปมาในช่วง IP ส่วนตัวเหล่านี้: คุณลักษณะนี้จะช่วยให้สามารถเข้าถึงอุปกรณ์อื่นๆ บนเครือข่ายท้องถิ่น เช่น การแชร์ การพิมพ์ การสตรีม ฯลฯ + %1$s (เพิ่มแล้ว) + %1$s ถูกเพิ่มลงใน \"%2$s\" + ตำแหน่งที่ตั้ง + ตำแหน่งที่ตั้งสำหรับ \"%1$s\" มีการเปลี่ยนแปลง ลงชื่อออก หมายเลขบัญชีที่ถูกต้อง เข้าสู่ระบบแล้ว @@ -154,12 +176,17 @@ มีอุปกรณ์มากเกินไป หมายเลขบัญชี Mullvad ของ Mullvad เท่านั้น + ชื่อถูกเปลี่ยนเป็น %1$s ยินดีต้อนรับ ขณะนี้อุปกรณ์นี้จะมีชื่อว่า <b>%1$s</b> สำหรับข้อมูลเพิ่มเติม โปรดกดปุ่มข้อมูลในบัญชี สร้างอุปกรณ์ใหม่แล้ว + รายการใหม่ + ไม่มีรายการแบบกำหนดเองที่พร้อมใช้งาน ไม่มีการเชื่อมต่ออินเทอร์เน็ต + ไม่พบตำแหน่งที่ตั้ง ไม่มีเซิร์ฟเวอร์ที่ตรงกับการตั้งค่าของคุณ โปรดลองเปลี่ยนเซิร์ฟเวอร์ หรือการตั้งค่าอื่นๆ คีย์ WireGuard ที่ใช้ได้ขาดหายไป จัดการคีย์ภายใต้การตั้งค่าขั้นสูง คุณอาจมีการรับส่งข้อมูลทางเครือข่ายที่รั่วไหลอยู่ + ไม่พบ ผู้ให้บริการ: %1$d การทำให้ข้อมูลยุ่งเหยิง จะซ่อนการรับส่งข้อมูล WireGuard ภายในอีกโพรโทคอลหนึ่ง ซึ่งใช้เพื่อช่วยหลบเลี่ยงการเซ็นเซอร์ และการกรองประเภทอื่นๆ ที่การเชื่อมต่อ WireGuard แบบธรรมดาจะถูกบล็อกได้ เปิด (UDP-ผ่าน-TCP) @@ -168,9 +195,11 @@ เปิด ออก หมดเวลา + ล้างโอเวอร์ไรด์แล้ว เป็นเจ้าของ ความเป็นเจ้าของ ชำระเงินแล้วจนถึง + แพตช์ไม่ตรงกับข้อมูลจำเพาะ คุณจำเป็นต้องเพิ่มเวลาไปยังบัญชีของคุณก่อน เพื่อที่จะเริ่มใช้งานแอป เราไม่สามารถเริ่มกระบวนการชำระเงินได้ โปรดตรวจสอบให้แน่ใจว่า คุณมี Google Play เวอร์ชันล่าสุด Google Play ไม่พร้อมใช้งาน @@ -215,30 +244,50 @@ ส่ง เราจะติดต่อคุณไปทาง %1$s ในกรณีจำเป็น ขอบคุณ! - โอเวอร์ไรด์เซิร์ฟเวอร์ IP + โอเวอร์ไรด์ IP เซิร์ฟเวอร์ + เปิดใช้โอเวอร์ไรด์อยู่ + นำเข้าโอเวอร์ไรด์ใหม่โดยใช้ + ไฟล์ + ข้อความ + ปิดใช้โอเวอร์ไรด์อยู่ บางครั้งที่อยู่ IP เซิร์ฟเวอร์ของเราอาจถูกบล็อก ในบางเครือข่ายที่มีการใช้งานเซ็นเซอร์หลายประเภท ในการหลีกเลี่ยงปัญหานี้ คุณสามารถนำเข้าไฟล์หรือข้อความที่ได้รับจากทีมสนับสนุนของเรา พร้อมด้วยที่อยู่ IP ใหม่ที่โอเวอร์ไรด์ที่อยู่เริ่มต้นของเซิร์ฟเวอร์ในมุมมองเลือกตำแหน่งที่ตั้ง หากคุณประสบปัญหาในการเชื่อมต่อกับเซิร์ฟเวอร์ VPN โปรดติดต่อฝ่ายสนับสนุน + รีเซ็ตโอเวอร์ไรด์ + โอเวอร์ไรด์ทั้งหมดจะถูกรีเซ็ต และที่อยู่ IP ของเซิร์ฟเวอร์ในมุมมองตำแหน่งที่ตั้งที่เลือก จะกลับไปเป็นค่าเริ่มต้น + รีเซ็ต + รีเซ็ตโอเวอร์ไรด์ทั้งหมด ไม่สามารถตั้งค่าเซิร์ฟเวอร์ DNS ของระบบได้ โปรดส่งรายงานปัญหา ไม่สามารถใช้กฎไฟร์วอลล์ได้ โปรดแก้ไขปัญหา หรือส่งรายงานปัญหา การตั้งค่า บัญชี การเปลี่ยนแปลงการตั้งค่าที่เกี่ยวข้องกับ DNS อาจไม่มีผลทันที เนื่องจากผลลัพธ์ที่แคชไว้ การตั้งค่า DNS อาจไม่มีผลทันที + ล้มเหลวในการปรับใช้แพตช์ + ค่า \"%1$s\" ไม่ถูกต้องหรือหายไป + ขีดจำกัดการเรียกซ้ำ + ไม่สามารถแยกวิเคราะห์แพตช์ได้ + \"%1$s\" ที่ไม่รู้จัก หรือมีคีย์ต้องห้าม + นำเข้าสำเร็จ เปิดใช้โอเวอร์ไรด์อยู่ การกำหนดค่า การตั้งค่า VPN แสดงหมายเลขบัญชี แสดงแอประบบ + ช่วยให้คุณเลือกแอปที่เข้าถึงอินเทอร์เน็ตได้โดยตรง โดยไม่ต้องผ่านอุโมงค์ VPN + โปรดทราบ: การแยกอุโมงค์เป็นความเสี่ยงด้านความเป็นส่วนตัว ไม่สามารถเริ่มการเชื่อมต่ออุโมงค์ได้ โปรดส่งรายงานปัญหา ส่ง สลับตำแหน่ง TCP + กด \"︙\" หรือกดประเทศ เมือง หรือเซิร์ฟเวอร์ค้างไว้ เพื่อเพิ่มตำแหน่งที่ตั้งลงในรายการ + กด \"︙\" เพื่อสร้างรายการแบบกำหนดเอง เปิด/ปิด VPN ชื่ออุปกรณ์: %1$s เหลือเวลา: %1$s ลองอีกครั้ง UDP พอร์ต TCP ใดที่โพรโทคอลการทำให้ข้อมูลยุ่งเหยิง UDP-ผ่าน-TCP ควรเชื่อมต่อบนเซิร์ฟเวอร์ VPN + เลิกทำ ไม่ปลอดภัย การเชื่อมต่อที่ไม่ปลอดภัย เวอร์ชันที่ไม่รองรับ @@ -248,6 +297,7 @@ ติดตั้ง Mullvad VPN (%1$s) เพื่อรับอัปเดตล่าสุดอยู่เสมอ มีอัปเดตพร้อมใช้งาน ดาวน์โหลดเพื่อคงความปลอดภัยไว้ อัปเดตเซิร์ฟเวอร์ DNS + อัปเดตชื่อรายการ อีเมลของคุณ (ไม่บังคับ) โปรดเขียนเป็นภาษาอังกฤษหรือสวีเดน พร้อมระบุประเทศต้นทางที่คุณเชื่อมต่อ เพื่อให้รับความช่วยเหลือได้ดียิ่งขึ้น กำลังตรวจสอบยืนยันบัตรกำนัล… diff --git a/android/lib/resource/src/main/res/values-tr/plurals.xml b/android/lib/resource/src/main/res/values-tr/plurals.xml index 1c288bef616e..84e1e851703f 100644 --- a/android/lib/resource/src/main/res/values-tr/plurals.xml +++ b/android/lib/resource/src/main/res/values-tr/plurals.xml @@ -16,6 +16,10 @@ %1$d gün %1$d gün + + %1$d konum + %1$d konum + %1$d ay %1$d ay diff --git a/android/lib/resource/src/main/res/values-tr/strings.xml b/android/lib/resource/src/main/res/values-tr/strings.xml index d7d534209d8e..ae351484da36 100644 --- a/android/lib/resource/src/main/res/values-tr/strings.xml +++ b/android/lib/resource/src/main/res/values-tr/strings.xml @@ -11,8 +11,10 @@ 30 gün süre ekleyin (%1$s) Sunucu ekle DNS sunucusu ekle + %1$s konumunu listeye ekle + Konum ekle Web sitemizden kredi satın alın veya kupon kullanın. - Hesabınıza %1$s eklendi. + %1$s, hesabınıza eklendi. Kabul et ve devam et Tüm uygulamalar Tüm konumlar @@ -34,6 +36,8 @@ 3. Kilitleme modunu etkinleştirmek için <b>VPN olmadığında bağlantıları engelle</b> seçeneğinin yanındaki düğmeye tıklayın. Kilitleme modu, VPN tünelinin bağlantısı manuel olarak kesilirse tüm internet erişimini engeller. <br/><b>Uyarı: Bu ayar, bölünmüş uygulamaları ve Yerel Ağ Paylaşımı özelliğini engeller</b>. Uygulama başladığında bir sunucuya otomatik olarak bağlan. + Lütfen yukarıdaki <b>%1$s</b> bölümündeki kılavuzu izleyerek <b>Her zaman açık</b> sistem ayarını kullanın. + Otomatik bağlan (eski) Otomatik Geri Reklamlar @@ -61,7 +65,9 @@ Mullvad hesap numarası panoya kopyalandı Panoya kopyalandı Hesap numarasını kopyala + Oluştur Hesap oluştur + Yeni liste oluştur Oluşturma tarihi: %1$s Hesap oluşturuluyor... GÜVENLİ BAĞLANTI OLUŞTURULUYOR @@ -78,6 +84,8 @@ Geçerli aralıklar: %1$s Özel tünel ana bilgisayarı çözülemedi. Ayarlarınızı değiştirmeyi deneyin. Sil + \"%1$s\" silinsin mi\"? + \"%1$s\" silindi Listeyi sil Bu cihazı kaldırdın. Tekrar bağlanmak için yeniden giriş yapmanız gerekecek. Cihaz etkin değil @@ -86,6 +94,8 @@ Bu, cihaza atanan addır. Mullvad hesabında oturum açan her cihaza, uygulamadaki veya web sitesindeki cihazlarınızı yönetirken tanımlamanıza yardımcı olacak benzersiz bir ad verilir. Bir Mullvad hesabı ile en fazla 5 cihazda oturum açabilirsiniz. Oturumu kapatırsanız cihaz ve cihaz adı kaldırılır. Tekrar oturum açtığınızda cihaza yeni bir ad verilir. + İptal et + Değişiklikler iptal edilsin mi? Bağlantıyı Kes Bağlantı kesiliyor Reddet @@ -96,7 +106,12 @@ Bu, belirli web sitelerinde, hizmetlerde ve uygulamalarda sorunlara neden olabilir. Hesap numaranız yok mu? Bu adres zaten girilmiş. + Özel listeleri düzenle + Listeyi düzenle + Listeleri düzenle + Konumları düzenle Mesajı düzenle + Adı düzenle Etkinleştir Özel DNS sunucusu kullanın MTU\'yu girin @@ -121,6 +136,8 @@ İşte hesap numaranız. Kaydedin! Hesap numarasını gizle Varsayılan + İçe aktarılacak geçersiz kılmaları yapıştırın veya yazın + Yeni geçersiz kılmaların içe aktarılması, önceden içe aktarılan bazı geçersiz kılmaların yerine geçebilir. İçe aktar Metin yoluyla içe aktar Giriş @@ -131,11 +148,16 @@ bir günden az kaldı bir dakikadan az bir günden az + Liste adı Bağlanılıyor... Satın alma işlemi doğrulanıyor... Yerel ağ paylaşımı Bunu, yerel çok noktaya yayın ve yayın aralıklarının yanı sıra aşağıdaki özel IP aralıkları arasında tünel dışı ağ iletişimine izin vererek yapar: Bu özellik; paylaşım, yazdırma, akış sağlama gibi özellikler için yerel ağdaki diğer cihazlara erişim izni verir. + %1$s (eklendi) + %1$s, \"%2$s\" listesine eklendi + Konumlar + \"%1$s\" için konumlar değiştirildi Oturumu kapat Geçerli hesap numarası Oturum açıldı @@ -154,12 +176,17 @@ Cihaz sayısı çok fazla Mullvad hesap numarası Sadece Mullvad\'a ait olanlar + Ad, %1$s olarak değiştirildi Hoş geldiniz, bu cihazın adı artık <b>%1$s</b>. Daha fazla ayrıntı için Hesap içinden bilgi düğmesine bakın. YENİ CİHAZ OLUŞTURULDU + Yeni liste + Özel listeler mevcut değil İnternet bağlantısı yok + Konum bulunamadı Ayarlarınızla eşleşen sunucu yok. Sunucuyu veya diğer ayarları değiştirmeyi deneyin. Geçerli WireGuard anahtarı eksik. Gelişmiş ayarlardan anahtarları yönetin. AĞ TRAFİĞİNİZDE SIZINTI OLABİLİR + Bulunamadı Hizmet sağlayıcılar: %1$d Gizleme, WireGuard trafiğini başka bir protokolün içinde gizler. Normal bir WireGuard bağlantısının engelleneceği sansürü ve diğer filtreleme türlerini aşmaya yardımcı olmak için kullanılabilir. Açık (TCP üzerinden UDP) @@ -168,9 +195,11 @@ Açık Çıkış Süre doldu + Geçersiz kılmalar temizlendi Tarafımıza ait olanlar Sahiplik durumu Şu tarihe kadar ödendi: + Yama spesifikasyonla eşleşmiyor Uygulamayı kullanmaya başlamak için önce hesabınıza süre eklemeniz gerekir. Ödeme işlemini başlatamadık. Lütfen Google Play\'in en son sürümüne sahip olduğunuzdan emin olun. Google Play kullanılamıyor @@ -216,29 +245,49 @@ Gerektiğinde sizinle %1$s adresinden iletişime geçeceğiz Teşekkürler! Sunucu IP\'sini geçersiz kılma + Geçersiz kılmalar etkin + Şuradaki yeni geçersiz kılmaları içe aktar: + Dosya + Metin + Geçersiz kılmalar etkin değil Farklı sansür türlerinin kullanıldığı bazı ağlarda sunucu IP adreslerimiz zaman zaman engellenir. Bu sınırlamadan kaçınmak için Konum Seç görünümündeki varsayılan sunucu adreslerini geçersiz kılan yeni IP adreslerine sahip bir dosyayı veya metni (destek ekibimiz tarafından sağlanır) içe aktarabilirsiniz. VPN sunucularına bağlanırken sorun yaşıyorsanız lütfen destek ekibiyle iletişime geçin. + Geçersiz kılmaları sıfırla + Tüm geçersiz kılmalar sıfırlanacak ve Konum seç görünümündeki sunucuların IP adresleri varsayılana döndürülecek. + Sıfırla + Tüm geçersiz kılmaları sıfırla Sistem DNS sunucusu ayarlanamıyor. Lütfen bir hata raporu gönderin. Güvenlik duvarı kuralları uygulanamıyor. Lütfen sorunu çözmeye çalışın veya bir hata raporu gönderin. Ayarlar Hesap DNS ile ilgili ayarlarda yapılan değişiklikler, önbelleğe alınan sonuçlar nedeniyle hemen etkili olmayabilir. DNS ayarları hemen etkili olmayabilir + Yama uygulanamadı + Geçersiz veya eksik \"%1$s\" değeri + Yineleme sınırı + Yama ayrıştırılamıyor + Bilinmeyen veya yasaklanmış \"%1$s\" anahtarı + İçe aktarma başarılı, geçersiz kılmalar etkin Tercihler VPN ayarları Hesap numarasını göster Sistem uygulamalarını göster + VPN tünelinden geçmeden doğrudan internete erişmesi gereken uygulamaları seçmenizi sağlar. + Dikkat: Bölünmüş tünelleme gizlilik riski taşır. Tünel bağlantısı başlatılamıyor. Lütfen bir hata raporu gönderin. Gönder Konum değiştir TCP + Listeye konum eklemek için \"︙\" düğmesine basın veya bir ülke, şehir veya sunucunun üzerine uzun basın. + Özel bir liste oluşturmak için \"︙\" düğmesine basın VPN\'i aç/kapat Cihaz adı: %1$s Kalan süre: %1$s Tekrar dene UDP TCP üzerinden UDP gizleme protokolünün VPN sunucusunda hangi TCP portuna bağlanması gerekiyor. + Geri al Güvenli değil GÜVENLİ OLMAYAN BAĞLANTI DESTEKLENMEYEN SÜRÜM @@ -248,6 +297,7 @@ Güncel kalmak için Mullvad VPN (%1$s) yükleyin Güncelleme mevcut, güvende kalmak için güncellemeyi indirin. DNS sunucusunu güncelle + Liste adını güncelle E-posta adresiniz (isteğe bağlı) Size daha iyi yardımcı olabilmemiz için lütfen mesajınızı İngilizce veya İsveççe olarak yazın ve hangi ülkeden bağlandığınızı belirtin. Kupon doğrulanıyor… diff --git a/android/lib/resource/src/main/res/values-zh-rCN/plurals.xml b/android/lib/resource/src/main/res/values-zh-rCN/plurals.xml index 19d390b317e1..4a044bd50848 100644 --- a/android/lib/resource/src/main/res/values-zh-rCN/plurals.xml +++ b/android/lib/resource/src/main/res/values-zh-rCN/plurals.xml @@ -12,6 +12,9 @@ %1$d 天 + + %1$d 个位置 + %1$d 个月 diff --git a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml index 0b14a5ba0ec6..0a4c63a1d573 100644 --- a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml @@ -11,6 +11,8 @@ 增加 30 天 (%1$s) 添加服务器 添加 DNS 服务器 + 将%1$s添加到列表中 + 添加位置 在我们的网站上购买额度或兑换优惠券。 %1$s已添加到您的帐户中。 同意并继续 @@ -34,6 +36,8 @@ 3. 要启用锁定模式,请点击<b>在没有 VPN 的情况下阻止连接</b>旁边的开关。 如果手动断开 VPN 隧道,锁定模式会阻止所有互联网访问。<br/><b>警告:此设置会阻止拆分应用和“本地网络共享”功能</b>。 在应用启动时自动连接到服务器。 + 请按照上述<b>%1$s</b>中的指南操作,改用<b>始终开启</b>系统设置。 + 自动连接(旧) 自动 返回 广告 @@ -61,7 +65,9 @@ 已将 Mullvad 帐号复制到剪贴板 已复制到剪贴板 复制帐号 + 创建 创建帐户 + 创建新列表 创建日期:%1$s 正在创建帐户… 正在创建安全连接 @@ -78,6 +84,8 @@ 有效范围:%1$s 无法解析自定义隧道的主机。请尝试更改您的设置。 删除 + 删除“%1$s”? + “%1$s”已被删除 删除列表 您已移除此设备。要重新连接,您需要重新登录。 设备处于非活动状态 @@ -86,6 +94,8 @@ 这是为设备分配的名称。每台登录 Mulvad 帐户的设备都会获得一个唯一名称,有助于您在应用或网站上管理设备时识别各个设备。 一个 Mulvad 帐户最多可以登录 5 台设备。 如果您退出登录,设备和设备名称将被移除。当您再次登录时,设备将获得新名称。 + 舍弃 + 舍弃更改? 断开连接 正在断开连接 关闭 @@ -96,7 +106,12 @@ 这可能会导致某些网站、服务和应用出现问题。 没有帐号? 此地址已输入过。 + 编辑自定义列表 + 编辑列表 + 编辑列表 + 编辑位置 编辑消息 + 编辑名称 启用 使用自定义 DNS 服务器 输入 MTU @@ -121,6 +136,8 @@ 以下是您的帐号。请妥善保存! 隐藏帐号 默认 + 粘贴或编写要导入的覆盖设置 + 导入新覆盖设置可能会替换一些先前导入的覆盖设置。 导入 通过文本导入 内部 @@ -131,11 +148,16 @@ 剩余时间不足 1 天 不到 1 分钟前 不足 1 天 + 列表名称 正在连接… 正在验证购买… 本地网络共享 此功能通过允许隧道以外到本地组播和广播范围以及与以下私有 IP 范围的网络通信来实现访问。 此功能允许访问本地网络中的其他设备,以实现共享、打印、流式传输等 + %1$s(已添加) + %1$s已添加到“%2$s”中 + 位置 + 已更改“%1$s”的位置 退出 有效帐号 已登录 @@ -154,12 +176,17 @@ 设备过多 Mullvad 帐号 仅 Mullvad 自有 + 名称已更改为“%1$s” 欢迎,此设备现在名为 <b>%1$s</b>。有关详情,请点击“帐户”中的信息按钮。 已创建新设备 + 新建列表 + 有可用的自定义列表 没有互联网连接 + 找不到位置 没有与您的设置匹配的服务器,请尝试更改服务器或其他设置。 缺少有效的 WireGuard 密钥。在“高级”设置下管理密钥。 您的网络流量可能在泄露 + 找不到 提供商:%1$d 混淆将 WireGuard 流量隐藏在另一个协议中。它可用于帮助规避审查和其他类型的过滤,在这些过滤中,普通的 WireGuard 连接将被阻止。 开 (UDP-over-TCP) @@ -168,9 +195,11 @@ 外部 已没有时间 + 覆盖设置已清除 自有 所有权 到期时间 + 补丁与规范不匹配 要开始使用本应用,您首先需要向帐户中充入时间。 我们无法启动付款流程,请确保拥有最新版本的 Google Play。 Google Play 不可用 @@ -216,29 +245,49 @@ 如果需要,我们将通过 %1$s 与您联系 谢谢! 服务器 IP 覆盖 + 覆盖设置已激活 + 导入新覆盖设置的方式 + 文件 + 文本 + 覆盖设置未激活 在某些使用各类审查的网络上,我们的服务器 IP 地址有时会被阻止。 为了避免这种情况,您可以导入由我们的支持团队提供的文件或文本,其中的新 IP 地址会覆盖“选择位置”视图中服务器的默认地址。 如果您在连接到 VPN 服务器时遇到问题,请联系支持团队。 + 重置覆盖设置 + 所有覆盖设置都会被重置,并且在“选择位置”视图中,服务器 IP 地址将恢复为默认设置。 + 重置 + 重置所有覆盖设置 无法设置系统 DNS 服务器。请发送问题报告。 无法应用防火墙规则。请排查问题或发送问题报告。 设置 帐户 由于缓存结果,对 DNS 相关设置的更改可能不会立即生效。 DNS 设置可能不会立即生效 + 无法应用补丁 + 值“%1$s”无效或者缺失 + 递归限制 + 无法解析补丁 + 未知或禁止的密钥“%1$s” + 导入成功,覆盖设置已激活 偏好设置 VPN 设置 显示帐号 显示系统应用 + 允许您选择不经过 VPN 隧道而直接访问互联网的应用。 + 注意:拆分隧道存在隐私风险。 无法启动隧道连接。请发送问题报告。 提交 切换位置 TCP + 要将位置添加到列表中,请按“︙”或长按国家/地区、城市或服务器。 + 要创建自定义列表,请按“︙” 切换 VPN 设备名称:%1$s 剩余时间:%1$s 重试 UDP UDP-over-TCP 混淆协议应连接到 VPN 服务器上的哪个 TCP 端口。 + 撤消 未受保护 未受保护的连接 不受支持的版本 @@ -248,6 +297,7 @@ 安装 Mullvad VPN (%1$s) 以保持最新状态 有可用更新,请下载以保持安全。 更新 DNS 服务器 + 更新列表名称 您的电子邮件(可选) 为了更好地帮助您,请用英语或瑞典语书写,并包含您连接时所在的国家/地区。 正在验证优惠券… diff --git a/android/lib/resource/src/main/res/values-zh-rTW/plurals.xml b/android/lib/resource/src/main/res/values-zh-rTW/plurals.xml index b17574801cd5..461e6276ae33 100644 --- a/android/lib/resource/src/main/res/values-zh-rTW/plurals.xml +++ b/android/lib/resource/src/main/res/values-zh-rTW/plurals.xml @@ -12,6 +12,9 @@ %1$d 天 + + %1$d 個位置 + %1$d 個月 diff --git a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml index 2b27c6493058..94af847ec258 100644 --- a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml @@ -11,8 +11,10 @@ 增加 30 天時間 (%1$s) 新增伺服器 新增 DNS 伺服器 + 將 %1$s 新增至清單 + 新增位置 在我們網站上購買點數或兌換憑證。 - %1$s已新增至您的帳戶。 + %1$s 已新增至您的帳戶。 同意並繼續 所有應用程式 所有位置 @@ -34,6 +36,8 @@ 3. 若要啟用鎖定模式,請按一下<b>在沒有 VPN 的情況下封鎖連線</b>旁邊的開關。 如果手動中斷與 VPN 通道的連線,鎖定模式便會封鎖所有的網際網路存取。<br/><b>警告:此設定會封鎖分割應用程式和「本機網路共享」功能</b>。 啟動應用程式時,自動連線伺服器。 + 請依照前述 <b>%1$s</b> 中的指南,改用<b>一律啟用</b>系統設定。 + 自動連線 (舊) 自動 返回 廣告 @@ -61,7 +65,9 @@ 已將 Mullvad 帳號複製到剪貼簿 已複製到剪貼簿 複製帳號 + 建立 建立帳戶 + 建立新清單 建立日期:%1$s 正在建立帳戶… 建立安全連線 @@ -78,6 +84,8 @@ 有效範圍:%1$s 無法解析自訂通道的主機。請嘗試變更您的設定。 刪除 + 要刪除「%1$s」嗎? + 「%1$s」已刪除 刪除清單 您已移除此裝置。若要重新連線,您需要重新登入。 裝置處於非活動狀態 @@ -86,6 +94,8 @@ 這是系統指派給裝置的名稱。每台登入 Mulvad 帳戶的裝置都會獲得一個獨特名稱,有助於您在應用程式或網站上管理裝置時識別各台裝置。 一個 Mulvad 帳戶最多可以登入 5 台裝置。 如果您登出,則系統會移除裝置和裝置名稱。當您再次登入時,裝置則會獲得新名稱。 + 捨棄 + 要捨棄變更嗎? 中斷連線 正在中斷連線 取消 @@ -96,7 +106,12 @@ 這可能會導致某些網站、服務和應用程式出現問題。 沒有帳號? 此地址已輸入過。 + 編輯自訂清單 + 編輯清單 + 編輯清單 + 編輯位置 編輯訊息 + 編輯名稱 啟用 使用自訂 DNS 伺服器 輸入 MTU @@ -121,6 +136,8 @@ 以下是您的帳號。請妥善保管! 隱藏帳號 預設 + 貼上或撰寫要匯入的覆寫設定 + 匯入新的覆寫設定,可能會取代一部分先前已匯入的覆寫設定。 匯入 透過文字匯入 入境 @@ -131,11 +148,16 @@ 剩餘時間不足 1 天 不到 1 分鐘前 不足 1 天 + 清單名稱 正在連線… 正在驗證購買… 本機網路分享 其原理是允許通道外的網路與本機網路進行通訊,包括以下私人 IP 範圍內的多點傳送和廣播: 此功能可允許存取本機網路上的其他裝置,以便用於分享、列印、串流等。 + %1$s (已新增) + 已將 %1$s 新增至「%2$s」 + 位置 + 已變更「%1$s」的位置 登出 有效帳號 已登入 @@ -154,12 +176,17 @@ 裝置過多 Mullvad 帳號 僅 Mullvad 自有 + 名稱已變更為「%1$s」 歡迎,此裝置現在稱為 <b>%1$s</b>。如需詳細資訊,請點按「帳戶」中的資訊按鈕。 已建立新裝置 + 新清單 + 有可用的自訂清單 沒有網際網路連線 + 找不到位置 沒有與您的設定相符的伺服器,請嘗試變更伺服器或其他設定。 缺少有效的 WireGuard 金鑰。在「進階」設定下管理金鑰。 您的網路流量可能正在洩露 + 找不到 供應商:%1$d 藉由混淆,WireGuard 的流量能隱藏在另一個通訊協定中。這有助於規避審查或其他類型的篩選。在這類篩選中,普通 WireGuard 連線將遭到封鎖。 開 (UDP-over-TCP) @@ -168,9 +195,11 @@ 開啟 出境 逾時 + 覆寫設定已清除 自有 所有權狀態 支付至 + 修補檔與規範不相符 需先在帳戶中加時,才能開始使用本應用程式。 我們無法啟動付款流程,請確認您是否擁有最新版本的 Google Play。 Google Play 無法使用 @@ -216,29 +245,49 @@ 如有需要,我們將透過 %1$s 與您聯絡 謝謝! 伺服器 IP 覆寫 + 覆寫設定已啟用 + 匯入新覆寫設定的方式 + 檔案 + 文字 + 覆寫設定未啟用 在某些採用了各類審查功能的網路上,我們的伺服器 IP 位址有時會遭到封鎖。 為了避免這種情況,您可以匯入由我們支援團隊所提供的檔案或文字,其中的新 IP 位址會覆蓋「選取位置」視圖中伺服器的預設位址。 如果您在連線至 VPN 伺服器時遇到問題,請聯絡支援人員。 + 重設覆寫設定 + 所有覆寫內容都將重設,並且在「選擇位置」視圖中,伺服器 IP 位址也將回到預設設定。 + 重設 + 重設所有覆寫設定 無法設定系統 DNS 伺服器。請傳送問題回報。 無法套用防火牆規則。請排除故障或傳送問題回報。 設定 帳戶 由於快取的結果,對 DNS 相關設定所做的變更可能不會立即生效。 DNS 設定可能不會立即生效 + 無法套用修補檔 + 值「%1$s」無效或是遺失 + 遞迴限制 + 無法剖析修補檔 + 未知或遭到禁用的金鑰「%1$s」 + 匯入成功,覆寫設定已啟用 喜好設定 VPN 設定 顯示帳號 顯示系統應用程式 + 允許您選擇不經過 VPN 通道而直接存取網際網路的應用程式。 + 注意:分割通道存在隱私風險。 無法啟動通道連線。請傳送問題回報。 提交 切換位置 TCP + 若要在清單中新增位置,請按下「︙」,或是長按下國家/地區、城市或伺服器。 + 若要建立自訂清單,請按下「︙」 切換 VPN 裝置名稱:%1$s 剩餘時間:%1$s 再試一次 UDP UDP-over-TCP 混淆通訊協定應連線到 VPN 伺服器上的哪個 TCP 連接埠。 + 復原 不安全 不安全的連線 不支援的版本 @@ -248,6 +297,7 @@ 安裝 Mullvad VPN (%1$s) 以維持最新狀態 更新可用,請下載以維持安全。 更新 DNS 伺服器 + 更新清單名稱 您的電子郵件 (選填) 為了給您提供更完善的協助,請您使用英語或瑞典語書寫,並註明您是從哪個國家/地區進行連線。 正在驗證優惠券… diff --git a/gui/locales/da/messages.po b/gui/locales/da/messages.po index c9fb5c19a36f..e192ef1a111a 100644 --- a/gui/locales/da/messages.po +++ b/gui/locales/da/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Danish\n" "Language: da_DK\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s blev tilføjet, konto betalt indtil %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Afbryder" msgid "Dismiss" msgstr "Afvis" +msgid "Edit" +msgstr "Rediger" + +msgid "Enable" +msgstr "Aktiver" + msgid "Enable anyway" msgstr "Aktivér alligevel" @@ -231,6 +237,11 @@ msgstr "Systemstandard" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Test" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Denne indstilling øger latensen. Brug den kun, hvis det er nødvendigt." @@ -253,6 +264,9 @@ msgstr "Fjern blokering" msgid "UNSECURED CONNECTION" msgstr "IKKE-SIKRET FORBINDELSE" +msgid "Use" +msgstr "Brug" + msgid "Username" msgstr "Brugernavn" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Godkendelse" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Ved at klikke på \"Gem\" ændres metoden i brug." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Ved at klikke på \"%(save)s\" ændres den brugte metode." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s via %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s via brugerdefineret bro" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Ud" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Tilføj brugerdefineret bro" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Vil du slette brugerdefineret bro?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Sletning af den brugerdefinerede bro fører dig tilbage til den valgte placeringsvisning, og indstillingen Automatisk vil blive valgt i stedet." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Rediger tilpasset bro" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Brotilstand" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Aktiver brotilstand?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "For at aktivere UDP skal du ændre Brotilstand til Automatisk eller Fra." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "For at vælge en bestemt broserver skal du gå til visningen Vælg placering." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Transportprotokol" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "En brugerdefineret broserver kan bruges til at omgå censur, når almindelige Mullvad-broservere ikke virker." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Land" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Brugerdefineret bro" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Brugerdefinerede lister" @@ -2014,7 +2067,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "Hvilken TCP-port UDP-over-TCP tilsløringsprotokollen skal forbinde til på VPN-serveren." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (tilføjet)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s blev tilføjet til \"%s\"" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s blev føjet til din konto." @@ -2039,6 +2100,10 @@ msgstr "Kontokredit udløber snart" msgid "Account time reminders" msgstr "Påmindelser om kontotid" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Føj %s til listen" + msgid "Add 30 days time" msgstr "Tilføj 30 dages tid" @@ -2048,12 +2113,18 @@ msgstr "Tilføj 30 dages tid (%s)" msgid "Add DNS server" msgstr "Tilføj DNS-server" +msgid "Add locations" +msgstr "Tilføj placeringer" + msgid "Agree and continue" msgstr "Accepter og fortsæt" msgid "All applications" msgstr "Alle applikationer" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Alle tilsidesættelser vil blive nulstillet, og servernes IP-adresser i visningen Vælg placering vil blive sat tilbage til standard." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Giver adgang til andre enheder på det samme netværk til deling, udskrivning osv." @@ -2063,6 +2134,9 @@ msgstr "Altid-til VPN tildelt en anden app" msgid "Always-on VPN might be enabled for another app" msgstr "Altid-til VPN er måske aktiveret for en anden app" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Bemærk: Split tunneling udgør en privatlivsrisiko." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Bemærk: Denne indstilling kan ikke bruges i kombination med Brug tilpasset DNS-server." @@ -2072,6 +2146,9 @@ msgstr "Auto-tilslutning og Lockdown-tilstand" msgid "Auto-connect & \\nLockdown mode" msgstr "Auto-tilslutning og \\nLockdown-tilstand" +msgid "Auto-connect (legacy)" +msgstr "Automatisk forbindelse (ældre metode)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Auto-tilslutning kaldes Altid-til VPN i Android-systemindstillingerne, og det sørger for, at du konstant er forbundet til VPN-tunnelen og automatisk forbinder efter genstart." @@ -2093,6 +2170,12 @@ msgstr "Kopierede logfiler til udklipsholder" msgid "Copied to clipboard" msgstr "Kopieret til udklipsholder" +msgid "Create" +msgstr "Opret" + +msgid "Create new list" +msgstr "Opret ny liste" + msgid "Critical error (your attention is required)" msgstr "Kritisk fejl (som kræver din opmærksomhed)" @@ -2102,11 +2185,32 @@ msgstr "Tilpassede DNS-serveradresser %s er ugyldige" msgid "DNS settings might not go into effect immediately" msgstr "DNS-indstillinger træder muligvis ikke i kraft med det samme" +msgid "Delete \"%s\"?" +msgstr "Vil du slette \"%s\"?" + msgid "Disable all %s above to activate this setting." msgstr "Deaktiver alle %s ovenfor for at aktivere denne indstilling." -msgid "Enable" -msgstr "Aktiver" +msgid "Discard" +msgstr "Kassér" + +msgid "Discard changes?" +msgstr "Vil du kassere ændringer?" + +msgid "Edit custom lists" +msgstr "Rediger tilpassede lister" + +msgid "Edit list" +msgstr "Rediger liste" + +msgid "Edit lists" +msgstr "Rediger lister" + +msgid "Edit locations" +msgstr "Rediger placeringer" + +msgid "Edit name" +msgstr "Rediger navn" msgid "Enter MTU" msgstr "Indtast MTU" @@ -2114,6 +2218,12 @@ msgstr "Indtast MTU" msgid "Excluded applications" msgstr "Ekskluderede applikationer" +msgid "Failed to apply patch" +msgstr "Det lykkedes ikke at anvende patch" + +msgid "File" +msgstr "Fil" + msgid "Go to VPN settings" msgstr "Gå til VPN-indstillinger" @@ -2129,9 +2239,34 @@ msgstr "Google Play er ikke tilgængelig" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Hvis funktionen split tunneling bruges, beder appen dit system om en liste over alle installerede programmer. Denne liste hentes kun i split tunnel-visningen. Listen over installerede programmer sendes aldrig fra enheden." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importer nye tilsidesættelser med" + +msgid "Import successful, overrides active" +msgstr "Importen lykkedes, tilsidesættelser aktive" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Import af nye tilsidesættelser vil muligvis erstatte nogle tidligere importerede tilsidesættelser." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Installer Mullvad VPN (%s) for at holde dig opdateret" +msgid "Invalid or missing value \"%s\"" +msgstr "Ugyldig eller manglende værdi \"%s\"" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Giver dig mulighed for at vælge apps, der skal have direkte adgang til internettet uden at gå gennem VPN-tunnelen." + +msgid "List name" +msgstr "Listenavn" + +msgid "Locations" +msgstr "Placeringer" + +msgid "Locations were changed for \"%s\"" +msgstr "Placeringer blev ændret for \"%s\"" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Sørger for, at enheden altid er på VPN-tunnelen." @@ -2144,9 +2279,43 @@ msgstr "Mullvad-kontonummer" msgid "Mullvad services unavailable" msgstr "Mullvad-tjenester er ikke tilgængelige" +msgid "Name was changed to %s" +msgstr "Navnet blev ændret til %s" + +msgid "New list" +msgstr "Ny liste" + +msgid "No custom lists available" +msgstr "Ingen brugerdefinerede lister tilgængelige" + msgid "No internet connection" msgstr "Ingen internetforbindelse" +msgid "No locations found" +msgstr "Ingen placeringer fundet" + +msgid "Not found" +msgstr "Ikke fundet" + +msgid "Overrides active" +msgstr "Tilsidesættelser aktive" + +msgid "Overrides cleared" +msgstr "Tilsidesættelser ryddet" + +msgid "Overrides inactive" +msgstr "Tilsidesættelser inaktive" + +msgid "Paste or write overrides to be imported" +msgstr "Indsæt eller skriv tilsidesættelser, der skal importeres" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Patch matcher ikke specifikationen" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Brug systemindstillingen Altid til i stedet ved at følge vejledningen i %s ovenfor." + msgid "Preferences" msgstr "Indstillinger" @@ -2156,18 +2325,33 @@ msgstr "Privatliv" msgid "Privacy policy" msgstr "Fortrolighedspolitik" +msgid "Recursion limit" +msgstr "Rekursionsgrænse" + msgid "Remove" msgstr "Fjern" msgid "Remove custom port" msgstr "Fjern brugerdefineret port" +msgid "Reset" +msgstr "Nulstil" + +msgid "Reset all overrides" +msgstr "Nulstil alle tilsidesættelser" + +msgid "Reset overrides" +msgstr "Nulstil tilsidesættelser" + msgid "Reset to default" msgstr "Nulstil til standard" msgid "Secured" msgstr "Sikret" +msgid "Server Ip overrides" +msgstr "Server IP-tilsidesættelser" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Indstil WireGuard MTU-værdi. Gyldigt område: %d - %d." @@ -2183,12 +2367,12 @@ msgstr "Viser den aktuelle VPN-tunnelstatus" msgid "Shows reminders when the account time is about to expire" msgstr "Viser påmindelser, når kontotiden er ved at udløbe" -msgid "Split tunneling is disabled." -msgstr "Split-tunneling er deaktiveret." - msgid "Submit" msgstr "Indsend" +msgid "Text" +msgstr "Tekst" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Indstillingerne for automatisk forbindelse og Lockdown-tilstand kan findes i Android- systemindstillingerne. Følg denne vejledning for at aktivere en eller begge." @@ -2204,6 +2388,12 @@ msgstr "Der er ingen VPN-indstillinger på din enhed" msgid "This address has already been entered." msgstr "Denne adresse er allerede blevet indtastet." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Tryk på \" ︙ \" eller tryk langvarigt på et land, en by eller en server for at tilføje placeringer til en liste." + +msgid "To create a custom list press the \"︙\"" +msgstr "Tryk på \" ︙ \" for at oprette en brugerdefineret liste" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "For at sørge for, at du har den mest sikre version og for at informere dig om eventuelle problemer med den aktuelle version, der kører, udfører appen automatisk versionstjek. Dette sender app-versionen og Android-systemversionen til Mullvad-servere. Mullvad holder tal på antallet af brugte app-versioner og Android-versioner. Dataene bliver aldrig opbevaret eller brugt på nogen måde, der kan identificere dig." @@ -2213,9 +2403,19 @@ msgstr "Slå VPN til/fra" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Kan ikke anvende firewallregler. Fejlfind eller send en problemrapport." +msgid "Unable to parse patch" +msgstr "Kan ikke fortolke patch" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Kunne ikke starte tunnelforbindelse. Deaktiver Altid-til VPN for %s." +msgid "Undo" +msgstr "Fortryd" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Ukendt eller forbudt nøgle \"%s\"" + msgid "Unsecured" msgstr "Ikke sikret" @@ -2225,6 +2425,9 @@ msgstr "Opdater DNS-server" msgid "Update available, download to remain safe." msgstr "Opdatering tilgængelig, download den for at forblive sikker." +msgid "Update list name" +msgstr "Opdater listenavn" + msgid "VPN permission error" msgstr "VPN-tilladelsesfejl" @@ -2237,9 +2440,8 @@ msgstr "VPN-tunnelstatus" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Gyldig WireGuard-nøgle mangler. Administrer nøgler under Avancerede indstillinger." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" -msgstr "Gyldige intervaller: %s" +msgstr "Gyldige områder: %s" msgid "Verifying purchase" msgstr "Bekræfter køb" @@ -2283,6 +2485,9 @@ msgstr "DU LÆKKER MÅSKE NETVÆRKSTRAFIK" msgid "You are running an unsupported app version." msgstr "Du kører en ikke-understøttet appversion." +msgid "\"%s\" was deleted" +msgstr "\"%s\" blev slettet" + msgid "less than a minute ago" msgstr "mindre end et minut siden" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d dag" msgstr[1] "%d dage" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d placering" +msgstr[1] "%d placeringer" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d måned" diff --git a/gui/locales/da/relay-locations.po b/gui/locales/da/relay-locations.po index f924cf35819c..a742af4dee03 100644 --- a/gui/locales/da/relay-locations.po +++ b/gui/locales/da/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Danish\n" "Language: da_DK\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australien" msgid "Austria" msgstr "Østrig" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Belgien" @@ -188,6 +196,10 @@ msgstr "Frankfurt" msgid "Germany" msgstr "Tyskland" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Gøteborg" @@ -264,6 +276,10 @@ msgstr "Letland" msgid "Lisbon" msgstr "Lissabon" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "London" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marseille" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slovakiet" +#. SI +msgid "Slovenia" +msgstr "Slovenien" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thailand" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ukraine" msgid "United Arab Emirates" msgstr "Forenede Arabiske Emirater" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/de/messages.po b/gui/locales/de/messages.po index d8100e236ad1..626b8c6c4a17 100644 --- a/gui/locales/de/messages.po +++ b/gui/locales/de/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: German\n" "Language: de_DE\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s hinzugefügt. Konto bezahlt bis %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Verbindung wird getrennt" msgid "Dismiss" msgstr "Ausblenden" +msgid "Edit" +msgstr "Bearbeiten" + +msgid "Enable" +msgstr "Aktivieren" + msgid "Enable anyway" msgstr "Trotzdem aktivieren" @@ -231,6 +237,11 @@ msgstr "Systemstandard" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Test" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Diese Einstellung erhöht die Latenz und sollte nur bei Bedarf verwendet werden." @@ -253,6 +264,9 @@ msgstr "Entsperren" msgid "UNSECURED CONNECTION" msgstr "UNGESICHERTE VERBINDUNG" +msgid "Use" +msgstr "Verwenden" + msgid "Username" msgstr "Benutzername" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Authentifizierung" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Durch Klicken auf „Speichern“ wird die verwendete Methode geändert." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Durch Klicken auf „%(save)s“ wird die verwendete Methode geändert." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s über %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s via eigener Brücke" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Ausgehend" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Eigene Brücke hinzufügen" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Eigene Brücke löschen?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Durch Löschung der eigenen Brücke kehren Sie zur Standortauswahl zurück und wählen stattdessen die automatische Option." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Eigene Brücke bearbeiten" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Brückenmodus" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Brückenmodus aktivieren?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Um UDP zu aktivieren, stellen Sie den Brückenmodus auf Automatisch oder Aus." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Um einen bestimmten Brückenserver auszuwählen, gehen Sie zur Standortsauswahl." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Transport-Protokoll" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Ein eigener Brückenserver kann verwendet werden, um Zensur zu umgehen, bei der die regulären Mullvad-Brückenserver nicht funktionieren." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Land" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Eigene Brücke" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Eigene Listen" @@ -2015,7 +2068,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "Mit welchem TCP-Port sich das UDP-über-TCP-Verschleierungsprotokoll auf dem VPN-Server verbinden soll." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (hinzugefügt)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s wurde zu „%s“ hinzugefügt" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s wurde zu Ihrem Konto hinzugefügt." @@ -2040,6 +2101,10 @@ msgstr "Kontoguthaben läuft bald ab" msgid "Account time reminders" msgstr "Erinnerungen an die Kontozeit" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "%s zur Liste hinzufügen" + msgid "Add 30 days time" msgstr "30 Tage Zeit hinzufügen" @@ -2049,12 +2114,18 @@ msgstr "30 Tage Zeit hinzufügen (%s)" msgid "Add DNS server" msgstr "DNS-Server hinzufügen" +msgid "Add locations" +msgstr "Standorte hinzufügen" + msgid "Agree and continue" msgstr "Akzeptieren und weiter" msgid "All applications" msgstr "Alle Anwendungen" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Alle Überschreibungen werden zurückgesetzt und die IP-Adressen der Server in der Standortauswahl werden auf die Standardwerte zurückgesetzt." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Ermöglicht den Zugriff auf andere Geräte im selben Netzwerk zum Teilen von Dateien, Drucken etc." @@ -2064,6 +2135,9 @@ msgstr "Always-on VPN ist einer anderen App zugeordnet" msgid "Always-on VPN might be enabled for another app" msgstr "Always-on VPN könnte für eine andere App aktiviert sein" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Achtung! Split Tunneling birgt ein Datenschutzrisiko." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Achtung: Diese Einstellung kann nicht in Kombination mit der Option Benutzerdefinierten DNS-Server verwenden verwendet werden." @@ -2073,6 +2147,9 @@ msgstr "Automatische Verbindung & Sperrmodus" msgid "Auto-connect & \\nLockdown mode" msgstr "Automatische Verbindung & \\nSperrmodus" +msgid "Auto-connect (legacy)" +msgstr "Automatische Verbindung (veraltet)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Die automatische Verbindung wird in den Android-Systemeinstellungen als „Always-on VPN“ bezeichnet und sorgt dafür, dass Sie ständig mit dem VPN-Tunnel verbunden sind und sich nach einem Neustart automatisch verbinden." @@ -2094,6 +2171,12 @@ msgstr "Protokolle in Zwischenablage kopiert" msgid "Copied to clipboard" msgstr "In die Zwischenablage kopiert" +msgid "Create" +msgstr "Erstellen" + +msgid "Create new list" +msgstr "Neue Liste erstellen" + msgid "Critical error (your attention is required)" msgstr "Kritischer Fehler (Ihre Aufmerksamkeit ist erforderlich)" @@ -2103,11 +2186,32 @@ msgstr "Eigene DNS-Server Adressen %s sind ungültig" msgid "DNS settings might not go into effect immediately" msgstr "Die DNS-Einstellungen werden möglicherweise nicht sofort wirksam" +msgid "Delete \"%s\"?" +msgstr "„%s“ löschen?" + msgid "Disable all %s above to activate this setting." msgstr "Deaktivieren Sie oben alle %s, um diese Einstellung zu aktivieren." -msgid "Enable" -msgstr "Aktivieren" +msgid "Discard" +msgstr "Verwerfen" + +msgid "Discard changes?" +msgstr "Änderungen verwerfen?" + +msgid "Edit custom lists" +msgstr "Eigene Listen bearbeiten" + +msgid "Edit list" +msgstr "Liste bearbeiten" + +msgid "Edit lists" +msgstr "Listen bearbeiten" + +msgid "Edit locations" +msgstr "Standorte bearbeiten" + +msgid "Edit name" +msgstr "Name bearbeiten" msgid "Enter MTU" msgstr "MTU eingeben" @@ -2115,6 +2219,12 @@ msgstr "MTU eingeben" msgid "Excluded applications" msgstr "Ausgeschlossene Anwendungen" +msgid "Failed to apply patch" +msgstr "Patch konnte nicht angewendet werden" + +msgid "File" +msgstr "Datei" + msgid "Go to VPN settings" msgstr "VPN-Einstellungen öffnen" @@ -2130,9 +2240,34 @@ msgstr "Google Play nicht verfügbar" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Wenn Split Tunneling verwendet wird, fragt die App Ihr System nach einer Liste aller installierten Anwendungen ab. Diese Liste wird nur in der Split-Tunneling-Ansicht abgerufen. Die Liste der installierten Anwendungen wird nie vom Gerät gesendet." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Neue Überschreibungen importieren via" + +msgid "Import successful, overrides active" +msgstr "Import erfolgreich, Überschreibungen aktiv" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Der Import neuer Überschreibungen kann einige zuvor importierte Überschreibungen ersetzen." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Installieren Sie Mullvad VPN (%s), um auf dem neuesten Stand zu bleiben" +msgid "Invalid or missing value \"%s\"" +msgstr "Ungültiger oder fehlender Wert „%s“" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Hiermit können Sie Apps auswählen, die ohne den VPN-Tunnel direkt auf das Internet zugreifen sollen." + +msgid "List name" +msgstr "Name der Liste" + +msgid "Locations" +msgstr "Standorte" + +msgid "Locations were changed for \"%s\"" +msgstr "Standorte wurden geändert für „%s“" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Gewährleistet, dass sich das Gerät immer im VPN-Tunnel befindet." @@ -2145,9 +2280,43 @@ msgstr "Mullvad-Kontonummer" msgid "Mullvad services unavailable" msgstr "Mullvad-Dienste nicht verfügbar" +msgid "Name was changed to %s" +msgstr "Name wurde geändert in %s" + +msgid "New list" +msgstr "Neue Liste" + +msgid "No custom lists available" +msgstr "Keine eigenen Listen verfügbar" + msgid "No internet connection" msgstr "Keine Internetverbindung" +msgid "No locations found" +msgstr "Keine Standorte gefunden" + +msgid "Not found" +msgstr "Nicht gefunden" + +msgid "Overrides active" +msgstr "Überschreibungen aktiv" + +msgid "Overrides cleared" +msgstr "Überschreibungen entfernt" + +msgid "Overrides inactive" +msgstr "Überschreibungen inaktiv" + +msgid "Paste or write overrides to be imported" +msgstr "Zu importierende Überschreibungen einfügen oder eingeben" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Patch entspricht nicht der Spezifikation" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Bitte verwenden Sie stattdessen die Systemeinstellung Always-on (Durchgehend aktiv), indem Sie die Anleitung oben unter %s befolgen." + msgid "Preferences" msgstr "Präferenzen" @@ -2157,18 +2326,33 @@ msgstr "Datenschutz" msgid "Privacy policy" msgstr "Datenschutzrichtlinie" +msgid "Recursion limit" +msgstr "Rekursionslimit" + msgid "Remove" msgstr "Entfernen" msgid "Remove custom port" msgstr "Eigenen Port entfernen" +msgid "Reset" +msgstr "Zurücksetzen" + +msgid "Reset all overrides" +msgstr "Alle Überschreibungen zurücksetzen" + +msgid "Reset overrides" +msgstr "Überschreibungen zurücksetzen" + msgid "Reset to default" msgstr "Auf Standard zurücksetzen" msgid "Secured" msgstr "Gesichert" +msgid "Server Ip overrides" +msgstr "Überschreibungen der Server-IP" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "WireGuard-MTU-Wert einstellen. Gültiger Bereich: %d – %d." @@ -2184,12 +2368,12 @@ msgstr "Zeigt den aktuellen Status des VPN-Tunnels an" msgid "Shows reminders when the account time is about to expire" msgstr "Erinnerungen anzeigen, wenn die Kontozeit bald abläuft" -msgid "Split tunneling is disabled." -msgstr "Split-Tunneling ist deaktiviert." - msgid "Submit" msgstr "Absenden" +msgid "Text" +msgstr "Text" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Die Einstellungen für die automatische Verbindung und den Sperrmodus finden Sie in den Android-Systemeinstellungen. Folgen Sie dieser Anleitung, um eine oder beide Einstellungen zu aktivieren." @@ -2205,6 +2389,12 @@ msgstr "Es gibt keine VPN-Einstellungen auf Ihrem Gerät" msgid "This address has already been entered." msgstr "Diese Adresse wurde bereits eingetragen." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Um Standorte zu einer Liste hinzuzufügen, drücken Sie auf „︙“ oder drücken Sie lange auf ein Land, eine Stadt oder einen Server." + +msgid "To create a custom list press the \"︙\"" +msgstr "Um eine eigene Liste zu erstellen, drücken Sie auf „︙“" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Um zu gewährleisten, dass Sie die sicherste Version haben und um Sie über eventuelle Probleme mit der aktuellen Version zu informieren, führt die App automatisch Versionsprüfungen durch. Dabei werden die App-Version und die Android-Systemversion an die Mullvad-Server gesendet. Mullvad führt Zähler für die Anzahl der verwendeten App-Versionen und Android-Versionen. Die Daten werden niemals gespeichert oder in einer Weise verwendet, die Sie identifizieren könnte." @@ -2214,9 +2404,19 @@ msgstr "VPN umschalten" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Firewall-Regeln können nicht angewendet werden. Bitte beheben Sie das Problem oder senden Sie einen Problembericht." +msgid "Unable to parse patch" +msgstr "Patch konnte nicht geparst werden" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Tunnelverbindung kann nicht gestartet werden. Bitte deaktivieren Sie Always-on VPN für %s, bevor Sie Mullvad VPN verwenden." +msgid "Undo" +msgstr "Rückgängig" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Unbekannter oder verbotener Schlüssel „%s“" + msgid "Unsecured" msgstr "Ungesichert" @@ -2226,6 +2426,9 @@ msgstr "DNS-Server aktualisieren" msgid "Update available, download to remain safe." msgstr "Update verfügbar, laden Sie es herunter, um sicher zu bleiben." +msgid "Update list name" +msgstr "Name der Liste ändern" + msgid "VPN permission error" msgstr "VPN-Berechtigungsfehler" @@ -2238,7 +2441,6 @@ msgstr "Status des VPN-Tunnels" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Gültiger WireGuard-Schlüssel fehlt. Sie können Ihre Schlüssel in den erweiterten Einstellungen verwalten." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Gültige Bereiche: %s" @@ -2284,6 +2486,9 @@ msgstr "MÖGLICHERWEISE IST IHR NETZWERKVERKEHR UNSICHER" msgid "You are running an unsupported app version." msgstr "Sie verwenden eine nicht unterstützte Version der App. " +msgid "\"%s\" was deleted" +msgstr "„%s“ wurde gelöscht" + msgid "less than a minute ago" msgstr "vor weniger als einer Minute" @@ -2295,6 +2500,11 @@ msgid_plural "%d days" msgstr[0] "%d Tag" msgstr[1] "%d Tage" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d Standort" +msgstr[1] "%d Standorte" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d Monat" diff --git a/gui/locales/de/relay-locations.po b/gui/locales/de/relay-locations.po index ba7b722651c0..689790cba7b1 100644 --- a/gui/locales/de/relay-locations.po +++ b/gui/locales/de/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: German\n" "Language: de_DE\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australien" msgid "Austria" msgstr "Österreich" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Belgien" @@ -188,6 +196,10 @@ msgstr "Frankfurt am Main" msgid "Germany" msgstr "Deutschland" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Lettland" msgid "Lisbon" msgstr "Lissabon" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "London" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marseille" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slowakei" +#. SI +msgid "Slovenia" +msgstr "Slowenien" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thailand" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ukraine" msgid "United Arab Emirates" msgstr "Vereinigte Arabische Emirate" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/es/messages.po b/gui/locales/es/messages.po index 5e2bfa450e8c..441b0a744284 100644 --- a/gui/locales/es/messages.po +++ b/gui/locales/es/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Spanish\n" "Language: es_ES\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "Tiempo añadido: %(duration)s. Cuenta pagada hasta el %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Desconectando" msgid "Dismiss" msgstr "Descartar" +msgid "Edit" +msgstr "Editar" + +msgid "Enable" +msgstr "Habilitar" + msgid "Enable anyway" msgstr "Habilitar de todos modos" @@ -231,6 +237,11 @@ msgstr "Valor predeterminado del sistema" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Probar" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Este ajuste incrementa la latencia. Solo use esta opción si es necesario." @@ -253,6 +264,9 @@ msgstr "Desbloquear" msgid "UNSECURED CONNECTION" msgstr "CONEXIÓN NO SEGURA" +msgid "Use" +msgstr "Usar" + msgid "Username" msgstr "Nombre de usuario" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Autenticación" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Hacer clic en «Guardar» cambia el método en uso." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Hacer clic en «%(save)s» cambia el método en uso." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s a través de %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s a través de puente personalizado" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Salida" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Añadir puente personalizado" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "¿Eliminar puente personalizado?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Eliminar el puente personalizado le llevará de vuelta a la vista Seleccionar ubicación y en su lugar se seleccionará la opción Automático." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Editar puente personalizado" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Modo puente" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "¿Habilitar modo puente?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Para activar el protocolo UDP, cambie el modo Puente a Automático o Desactivado." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Para seleccionar un servidor puente específico, vaya a la vista Seleccionar ubicación." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Protocolo de transporte" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Se puede usar un servidor puente personalizado para eludir la censura cuando no funcionan los servidores puente normales de Mullvad." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "País" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Puente personalizado" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Listas personalizadas" @@ -2014,9 +2067,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "El puerto TCP al que se conectará el protocolo de ofuscación de UDP sobre TCP en el servidor VPN." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (añadida)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s se ha añadido a «%s»" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "Se ha(n) añadido %s a su cuenta." +msgstr "Se ha añadido %s a su cuenta." msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. Tras hacer clic en el botón Ir a la configuración de VPN de abajo, haga clic en el icono de engranaje situado junto al nombre de la Mullvad VPN." @@ -2039,6 +2100,10 @@ msgstr "El crédito de la cuenta caduca pronto" msgid "Account time reminders" msgstr "Recordatorios de tiempo de la cuenta" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Añadir %s a la lista" + msgid "Add 30 days time" msgstr "Añadir 30 días" @@ -2048,12 +2113,18 @@ msgstr "Añadir 30 días (%s)" msgid "Add DNS server" msgstr "Añadir servidor DNS" +msgid "Add locations" +msgstr "Añadir ubicaciones" + msgid "Agree and continue" msgstr "Aceptar y continuar" msgid "All applications" msgstr "Todas las aplicaciones" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Todas las anulaciones se restablecerán y las direcciones IP de los servidores, en la vista Seleccionar ubicación, volverán a sus valores predeterminados." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Permite el acceso a otros dispositivos de la misma red para compartir, imprimir, etc." @@ -2063,6 +2134,9 @@ msgstr "La VPN siempre activa se ha asignado a otra aplicación" msgid "Always-on VPN might be enabled for another app" msgstr "La VPN siempre activa podría estar habilitada en otra aplicación" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Atención: La utilización dividida es un riesgo para la privacidad." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Atención: este ajuste no se puede utilizar en combinación con Usar servidor DNS personalizado." @@ -2072,6 +2146,9 @@ msgstr "Conexión automática y modo de bloqueo" msgid "Auto-connect & \\nLockdown mode" msgstr "Conexión automática y \\nmodo de bloqueo" +msgid "Auto-connect (legacy)" +msgstr "Conexión automática (heredada)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "La conexión automática se llama VPN siempre activa en la configuración del sistema Android y asegura estar conectado constantemente al túnel de la VPN y conectarse automáticamente después de cada reinicio." @@ -2093,6 +2170,12 @@ msgstr "Registros copiados en el portapapeles" msgid "Copied to clipboard" msgstr "Copiado en el Portapapeles" +msgid "Create" +msgstr "Crear" + +msgid "Create new list" +msgstr "Crear nueva lista" + msgid "Critical error (your attention is required)" msgstr "Error crítico (precisa su atención)" @@ -2102,11 +2185,32 @@ msgstr "Las direcciones del servidor DNS personalizado %s no son válidas" msgid "DNS settings might not go into effect immediately" msgstr "La configuración de DNS podría no surtir efecto inmediatamente" +msgid "Delete \"%s\"?" +msgstr "¿Eliminar «%s»?" + msgid "Disable all %s above to activate this setting." msgstr "Deshabilite todos los %s de arriba para activar este ajuste." -msgid "Enable" -msgstr "Habilitar" +msgid "Discard" +msgstr "Descartar" + +msgid "Discard changes?" +msgstr "¿Descartar los cambios?" + +msgid "Edit custom lists" +msgstr "Editar listas personalizadas" + +msgid "Edit list" +msgstr "Editar lista" + +msgid "Edit lists" +msgstr "Editar listas" + +msgid "Edit locations" +msgstr "Editar ubicaciones" + +msgid "Edit name" +msgstr "Editar nombre" msgid "Enter MTU" msgstr "Introducir MTU" @@ -2114,6 +2218,12 @@ msgstr "Introducir MTU" msgid "Excluded applications" msgstr "Aplicaciones excluidas" +msgid "Failed to apply patch" +msgstr "Error al aplicar el parche" + +msgid "File" +msgstr "Archivo" + msgid "Go to VPN settings" msgstr "Ir a la configuración de VPN" @@ -2129,9 +2239,34 @@ msgstr "Google Play no disponible" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Si se utiliza la característica de túnel dividido, la aplicación consultará a su sistema la lista de todas las aplicaciones instaladas. Esta lista solo se recupera en la vista de túnel dividido. La lista de aplicaciones instaladas nunca se envía desde el dispositivo." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importar nuevas anulaciones por" + +msgid "Import successful, overrides active" +msgstr "Importación realizada correctamente. Anulaciones activas" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Importar nuevas anulaciones podría reemplazar algunas anulaciones anteriormente importadas." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Instale Mullvad VPN (%s) para seguir actualizado" +msgid "Invalid or missing value \"%s\"" +msgstr "El valor «%s» falta o no es válido" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Le permite seleccionar aplicaciones que deberían acceder a Internet directamente sin pasar por el túnel VPN." + +msgid "List name" +msgstr "Nombre de la lista" + +msgid "Locations" +msgstr "Ubicaciones" + +msgid "Locations were changed for \"%s\"" +msgstr "Se han cambiado las ubicaciones para «%s»" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Asegura que el dispositivo esté siempre activado en el túnel de la VPN." @@ -2144,9 +2279,43 @@ msgstr "Número de cuenta de Mullvad" msgid "Mullvad services unavailable" msgstr "Servicios de Mullvad no disponibles" +msgid "Name was changed to %s" +msgstr "Se ha cambiado el nombre a %s" + +msgid "New list" +msgstr "Nueva lista" + +msgid "No custom lists available" +msgstr "No hay listas personalizadas disponibles" + msgid "No internet connection" msgstr "No hay conexión a Internet" +msgid "No locations found" +msgstr "No se ha encontrado ninguna ubicación" + +msgid "Not found" +msgstr "No encontrado" + +msgid "Overrides active" +msgstr "Anulaciones activas" + +msgid "Overrides cleared" +msgstr "Anulaciones borradas" + +msgid "Overrides inactive" +msgstr "Anulaciones inactivas" + +msgid "Paste or write overrides to be imported" +msgstr "Pegue o escriba las anulaciones que se deben importar" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "El parche no coincide con la especificación" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "En su lugar, utilice el ajuste de sistema Siempre activado siguiendo arriba la guía en %s." + msgid "Preferences" msgstr "Preferencias" @@ -2156,18 +2325,33 @@ msgstr "Privacidad" msgid "Privacy policy" msgstr "Política de privacidad" +msgid "Recursion limit" +msgstr "Límite de recursión" + msgid "Remove" msgstr "Quitar" msgid "Remove custom port" msgstr "Quitar puerto personalizado" +msgid "Reset" +msgstr "Restablecer" + +msgid "Reset all overrides" +msgstr "Restablecer todas las anulaciones" + +msgid "Reset overrides" +msgstr "Restablecer anulaciones" + msgid "Reset to default" msgstr "Restablecer a valores predeterminados" msgid "Secured" msgstr "Protegido" +msgid "Server Ip overrides" +msgstr "Anulaciones de IP de servidor" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Establezca el valor de MTU de WireGuard. Intervalo válido: %d-%d." @@ -2183,12 +2367,12 @@ msgstr "Muestra el estado actual del túnel VPN" msgid "Shows reminders when the account time is about to expire" msgstr "Muestra avisos cuando el tiempo de la cuenta está a punto de caducar" -msgid "Split tunneling is disabled." -msgstr "La tunelización dividida está deshabilitada." - msgid "Submit" msgstr "Enviar" +msgid "Text" +msgstr "Texto" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "La configuración del modo de bloqueo y de la conexión automática puede encontrarse en la configuración del sistema Android, siga esta guía para habilitar uno o ambos." @@ -2204,6 +2388,12 @@ msgstr "No hay configuración de VPN en su dispositivo" msgid "This address has already been entered." msgstr "Esta dirección ya se ha introducido." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Para añadir ubicaciones a una lista, pulse «︙» o mantenga pulsado unos segundos un país, ciudad o servidor." + +msgid "To create a custom list press the \"︙\"" +msgstr "Para crear una lista personalizada, pulse «︙»" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Para garantizar que tiene la versión más segura e informarle de cualquier problema con la versión actual que está ejecutando, la aplicación realiza verificaciones de versiones automáticamente. Esto envía la versión de la aplicación y la versión del sistema Android a los servidores de Mullvad. Mullvad mantiene contadores del número de versiones de la aplicación y versiones de Android usadas. Los datos nunca se almacenan ni utilizan de forma que puedan identificarle." @@ -2213,9 +2403,19 @@ msgstr "Alternar VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "No se pueden aplicar las reglas del firewall. Intente solucionar el problema o envíe un informe de problemas." +msgid "Unable to parse patch" +msgstr "No se puede analizar el parche" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "No se puede iniciar la conexión de túnel. Deshabilite la VPN siempre activa en %s antes de utilizar la VPN de Mullvad." +msgid "Undo" +msgstr "Deshacer" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Clave «%s» desconocida o prohibida" + msgid "Unsecured" msgstr "No protegido" @@ -2225,6 +2425,9 @@ msgstr "Actualizar servidor DNS" msgid "Update available, download to remain safe." msgstr "Hay una actualización disponible, descárguela para seguir protegido." +msgid "Update list name" +msgstr "Actualizar nombre de la lista" + msgid "VPN permission error" msgstr "Error en la autorización de la VPN" @@ -2237,7 +2440,6 @@ msgstr "Estado del túnel VPN" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Falta una clave de WireGuard válida. Para administrar las claves, vaya a Configuración avanzada." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Intervalos válidos: %s" @@ -2283,6 +2485,9 @@ msgstr "PUEDE QUE SE ESTÉ FILTRANDO EL TRÁFICO DE RED" msgid "You are running an unsupported app version." msgstr "Ejecuta una versión de la aplicación que no es compatible." +msgid "\"%s\" was deleted" +msgstr "Se ha eliminado «%s»" + msgid "less than a minute ago" msgstr "hace menos de un minuto" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d día" msgstr[1] "%d días" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d ubicación" +msgstr[1] "%d ubicaciones" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d mes" diff --git a/gui/locales/es/relay-locations.po b/gui/locales/es/relay-locations.po index 9ab11ebc12cc..e2200ae8ce3e 100644 --- a/gui/locales/es/relay-locations.po +++ b/gui/locales/es/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Spanish\n" "Language: es_ES\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australia" msgid "Austria" msgstr "Austria" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Bélgica" @@ -188,6 +196,10 @@ msgstr "Fráncfort" msgid "Germany" msgstr "Alemania" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Gotemburgo" @@ -264,6 +276,10 @@ msgstr "Letonia" msgid "Lisbon" msgstr "Lisboa" +#. SI LJU +msgid "Ljubljana" +msgstr "Liubliana" + #. GB LON msgid "London" msgstr "Londres" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marsella" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopie" msgid "Slovakia" msgstr "Eslovaquia" +#. SI +msgid "Slovenia" +msgstr "Eslovenia" + #. BG SOF msgid "Sofia" msgstr "Sofía" @@ -520,6 +544,10 @@ msgstr "Tallin" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Tailandia" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ucrania" msgid "United Arab Emirates" msgstr "Emiratos Árabes Unidos" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/fi/messages.po b/gui/locales/fi/messages.po index 5d00c4f843ef..20fbac89a543 100644 --- a/gui/locales/fi/messages.po +++ b/gui/locales/fi/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Finnish\n" "Language: fi_FI\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s lisättiin: tilin käyttö maksettu %(expiry)s asti." @@ -139,6 +139,12 @@ msgstr "Katkaistaan yhteyttä" msgid "Dismiss" msgstr "Jätä huomiotta" +msgid "Edit" +msgstr "Muokkaa" + +msgid "Enable" +msgstr "Ota käyttöön" + msgid "Enable anyway" msgstr "Ota silti käyttöön" @@ -231,6 +237,11 @@ msgstr "Järjestelmän oletusarvo" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Testaa" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Tämä asetus lisää viivettä. Käytä vain tarvittaessa." @@ -253,6 +264,9 @@ msgstr "Poista esto" msgid "UNSECURED CONNECTION" msgstr "SUOJAAMATON YHTEYS" +msgid "Use" +msgstr "Käytä" + msgid "Username" msgstr "Käyttäjätunnus" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Todennus" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Jos napsautat \"Tallenna\", käytössä oleva menetelmä vaihtuu." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Jos napsautat \"%(save)s\", käytössä oleva menetelmä vaihtuu." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s, yhteys: %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s mukautetun siltauksen kautta" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Lähtevä" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Lisää mukautettu siltaus" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Poistetaanko mukautettu siltaus?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Mukautun siltauksen poistaminen vie sinut takaisin valintanäkymään, ja siltauksen asemesta valitaan automaattinen vaihtoehto." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Mukautetun siltauksen muokkaus" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Siltaus" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Otetaanko siltaustila käyttöön?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Aktivoi UDP muuttamalla Siltaustila-asetukseksi Automaattinen tai Pois." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Jos haluat valita tietyn siltauspalvelimen, siirry sijainnin valintanäkymään." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Siirtoprotokolla" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Mukautettua siltauspalvelinta voidaan käyttää sensuurin kiertämiseen silloin kun tavanomaiset Mullvad-siltauspalvelimet eivät toimi." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Maa" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Mukautettu siltaus" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Mukautetut luettelot" @@ -2014,7 +2067,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "Määrittää, mihin VPN-palvelimen TCP-porttiin \"UDP TCP:n kautta\" -hämäysteknologia-protokollan tulee muodostaa yhteys." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (lisätty)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s lisättiin luetteloon \"%s\"" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "Tilillesi lisättiin %s käyttöaikaa." @@ -2039,6 +2100,10 @@ msgstr "Tilin käyttöaika päättyy pian" msgid "Account time reminders" msgstr "Muistutukset tilin käyttöajasta" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Lisää %s luetteloon" + msgid "Add 30 days time" msgstr "Lisää 30 päivää käyttöaikaa" @@ -2048,12 +2113,18 @@ msgstr "Lisää 30 päivää käyttöaikaa (%s)" msgid "Add DNS server" msgstr "Lisää DNS-palvelin" +msgid "Add locations" +msgstr "Lisää sijainteja" + msgid "Agree and continue" msgstr "Hyväksy ja jatka" msgid "All applications" msgstr "Kaikki sovellukset" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Kaikki ohitukset nollataan ja palvelinten IP-osoitteet palautuvat oletusarvoihin sijainnin valintanäkymässä." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Sallii jakamisen, tulostuksen ym. saman verkon muille laitteille." @@ -2063,6 +2134,9 @@ msgstr "Aina päällä oleva VPN on määritetty toiselle sovellukselle" msgid "Always-on VPN might be enabled for another app" msgstr "Aina päällä oleva VPN on mahdollisesti otettu käyttöön toiselle sovellukselle" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Huomio: sovelluskohtainen yhdistäminen on tietosuojariski." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Huomio: asetusta ei voi käyttää yhdessä \"Käytä mukautettua DNS-palvelinta\" -asetuksen kanssa." @@ -2072,6 +2146,9 @@ msgstr "Automaattinen yhdistäminen ja lukitustila" msgid "Auto-connect & \\nLockdown mode" msgstr "Automaattinen yhdistäminen \\nja lukitustila" +msgid "Auto-connect (legacy)" +msgstr "Autom. yhdistäminen (vanha järjestelmä)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Automaattista yhdistämistä kutsutaan aina päällä olevaksi VPN-yhteydeksi Androidin järjestelmäasetuksissa. Se varmistaa, että olet jatkuvasti yhteydessä VPN-tunneliin, sekä muodostaa yhteyden automaattisesti aina uudelleenkäynnistyksen jälkeen." @@ -2093,6 +2170,12 @@ msgstr "Lokit kopioitu leikepöydälle" msgid "Copied to clipboard" msgstr "Kopioitu leikepöydälle" +msgid "Create" +msgstr "Luo" + +msgid "Create new list" +msgstr "Luo uusi luettelo" + msgid "Critical error (your attention is required)" msgstr "Vakava virhe (vaatii huomiotasi)" @@ -2102,11 +2185,32 @@ msgstr "Mukautetut DNS-palvelimen osoitteet %s ovat virheellisiä" msgid "DNS settings might not go into effect immediately" msgstr "Uudet DNS-asetukset eivät välttämättä astu voimaan välittömästi" +msgid "Delete \"%s\"?" +msgstr "Poistetaanko \"%s\"?" + msgid "Disable all %s above to activate this setting." msgstr "Ota tämä asetus käyttöön poistamalla kaikki %s käytöstä yllä." -msgid "Enable" -msgstr "Ota käyttöön" +msgid "Discard" +msgstr "Hylkää" + +msgid "Discard changes?" +msgstr "Hylätäänkö muokkaukset?" + +msgid "Edit custom lists" +msgstr "Muokkaa mukautettuja luetteloita" + +msgid "Edit list" +msgstr "Muokkaa luetteloa" + +msgid "Edit lists" +msgstr "Muokkaa luetteloita" + +msgid "Edit locations" +msgstr "Muokkaa sijainteja" + +msgid "Edit name" +msgstr "Muokkaa nimeä" msgid "Enter MTU" msgstr "Syötä MTU" @@ -2114,6 +2218,12 @@ msgstr "Syötä MTU" msgid "Excluded applications" msgstr "Poissuljetut sovellukset" +msgid "Failed to apply patch" +msgstr "Muutostiedoston käyttöönotto ei onnistunut" + +msgid "File" +msgstr "Tiedosto" + msgid "Go to VPN settings" msgstr "Siirry VPN-asetuksiin" @@ -2129,9 +2239,34 @@ msgstr "Google Play ei ole käytettävissä" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Jos käytetään sovelluskohtaista yhdistämistä, sovellus pyytää järjestelmältäsi luettelon kaikista siihen asennetuista sovelluksista. Luettelo noudetaan vain sovelluskohtaisen yhdistämisen näkymässä. Asennettujen sovellusten luetteloa ei koskaan lähetetä laitteelta." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Uusien ohitusten tuontitapa" + +msgid "Import successful, overrides active" +msgstr "Tuonti onnistui ja ohituksia on käytössä" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Uusien ohitusten tuonti saattaa korvata joitain aiemmin tuotuja ohituksia." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Asenna Mullvad VPN (%s) pysyäksesi ajan tasalla" +msgid "Invalid or missing value \"%s\"" +msgstr "Arvo \"%s\" puuttuu tai on virheellinen" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Voit valita sovelluksia, joiden pitäisi muodostaa yhteys Internetiin suoraan kulkematta VPN-tunnelin läpi." + +msgid "List name" +msgstr "Luettelon nimi" + +msgid "Locations" +msgstr "Sijainnit" + +msgid "Locations were changed for \"%s\"" +msgstr "Kohteen \"%s\" sijainteja vaihdettiin" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Varmistaa, että laite on aina yhteydessä VPN-tunnelin kautta." @@ -2144,9 +2279,43 @@ msgstr "Mullvad-tilin numero" msgid "Mullvad services unavailable" msgstr "Mullvad-palvelut eivät ole käytettävissä" +msgid "Name was changed to %s" +msgstr "Nimeksi vaihdettiin \"%s\"" + +msgid "New list" +msgstr "Uusi luettelo" + +msgid "No custom lists available" +msgstr "Mukautettuja luetteloita ei löytynyt" + msgid "No internet connection" msgstr "Ei verkkoyhteyttä" +msgid "No locations found" +msgstr "Sijainteja ei löytynyt" + +msgid "Not found" +msgstr "Ei löydy" + +msgid "Overrides active" +msgstr "Ohituksia on käytössä" + +msgid "Overrides cleared" +msgstr "Ohitukset on poistettu" + +msgid "Overrides inactive" +msgstr "Ohituksia ei ole käytössä" + +msgid "Paste or write overrides to be imported" +msgstr "Liitä tai kirjoita ohitukset, jotka haluat tuoda" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Muutostiedosto ei vastaa määritelmää" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Käytä aina käytössä -järjestelmäasetusta seuraamalla ylhäältä löytyvää opasta aiheesta %s." + msgid "Preferences" msgstr "Asetukset" @@ -2156,18 +2325,33 @@ msgstr "Tietosuoja" msgid "Privacy policy" msgstr "Tietosuojakäytäntö" +msgid "Recursion limit" +msgstr "Toistorajoitus" + msgid "Remove" msgstr "Poista" msgid "Remove custom port" msgstr "Poista mukautettu portti" +msgid "Reset" +msgstr "Nollaa" + +msgid "Reset all overrides" +msgstr "Nollaa kaikki ohitukset" + +msgid "Reset overrides" +msgstr "Nollaa ohitukset" + msgid "Reset to default" msgstr "Palauta oletusarvo" msgid "Secured" msgstr "Suojattu" +msgid "Server Ip overrides" +msgstr "Palvelimen IP-osoitteen ohitus" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Aseta WireGuardin MTU-arvo väliltä %d–%d." @@ -2183,12 +2367,12 @@ msgstr "Näyttää VPN-tunnelin nykyisen tilan" msgid "Shows reminders when the account time is about to expire" msgstr "Näyttää muistutuksia, kun tilin käyttöaika on umpeutumassa" -msgid "Split tunneling is disabled." -msgstr "Sovelluskohtainen yhdistäminen on poistettu käytöstä." - msgid "Submit" msgstr "Lähetä" +msgid "Text" +msgstr "Teksti" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Automaattisen yhdistämisen ja lukitustilan asetukset löytyvät Androidin järjestelmäasetuksista. Voit ottaa jommankumman tai molemmat käyttöön tämän oppaan ohjeilla." @@ -2204,6 +2388,12 @@ msgstr "Laitteessasi ei ole VPN-asetuksia" msgid "This address has already been entered." msgstr "Tämä osoite on annettu jo." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Jos haluat lisätä luetteloon sijainteja, paina \"︙\" tai paina pitkään maata, kaupunkia tai palvelinta." + +msgid "To create a custom list press the \"︙\"" +msgstr "Voit luoda mukautetun luettelon painamalla \"︙\"" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Sovellus suorittaa versiotarkistuksia automaattisesti varmistaakseen, että käytät kaikkein turvallisinta versiota, ja ilmoittaakseen sinulle kaikista parhaillaan käynnissä olevan version ongelmista. Se lähettää Mullvadin palvelimille tiedot sovellusversiosta ja Android-järjestelmäversiosta. Mullvad pitää lukua käytettyjen sovellusversioiden ja Android-versioiden määristä. Tietoja ei koskaan tallenneta tai käytetä tavalla, joka voisi mahdollistaa tunnistamisesi." @@ -2213,9 +2403,19 @@ msgstr "Vaihda VPN:ää" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Palomuurisääntöjä ei voida käyttää. Suorita vianetsintä tai lähetä ongelmaraportti." +msgid "Unable to parse patch" +msgstr "Muutostiedoston jäsentäminen ei onnistu" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Tunneliyhteyden käynnistäminen ei onnistu. Poista aina päällä oleva VPN käytöstä sovellukselle %s ennen Mullvad VPN:n käyttämistä." +msgid "Undo" +msgstr "Kumoa" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Tuntematon tai kielletty avain \"%s\"" + msgid "Unsecured" msgstr "Suojaamaton" @@ -2225,6 +2425,9 @@ msgstr "Päivitä DNS-palvelin" msgid "Update available, download to remain safe." msgstr "Päivitys saatavilla. Lataa se pysyäksesi suojattuna." +msgid "Update list name" +msgstr "Päivitä luettelon nimi" + msgid "VPN permission error" msgstr "VPN-lupavirhe" @@ -2237,7 +2440,6 @@ msgstr "VPN-tunnelin tila" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Käypä WireGuard-avain puuttuu. Voit hallinnoida avaimia lisäasetuksissa." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Kelvolliset portit: %s" @@ -2283,6 +2485,9 @@ msgstr "VERKKOLIIKENTEESI SAATTAA VUOTAA" msgid "You are running an unsupported app version." msgstr "Sovellusversiotasi ei tueta." +msgid "\"%s\" was deleted" +msgstr "\"%s\" poistettiin" + msgid "less than a minute ago" msgstr "alle minuutti sitten" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d päivä" msgstr[1] "%d päivää" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d sijainti" +msgstr[1] "%d sijaintia" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d kuukausi" diff --git a/gui/locales/fi/relay-locations.po b/gui/locales/fi/relay-locations.po index 3bf2b0d6281c..c8b6383f6266 100644 --- a/gui/locales/fi/relay-locations.po +++ b/gui/locales/fi/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Finnish\n" "Language: fi_FI\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australia" msgid "Austria" msgstr "Itävalta" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Belgia" @@ -188,6 +196,10 @@ msgstr "Frankfurt" msgid "Germany" msgstr "Saksa" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Latvia" msgid "Lisbon" msgstr "Lissabon" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "Lontoo" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marseille" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slovakia" +#. SI +msgid "Slovenia" +msgstr "Slovenia" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinna" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thaimaa" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ukraina" msgid "United Arab Emirates" msgstr "Yhdistyneet Arabiemiirikunnat" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/fr/messages.po b/gui/locales/fr/messages.po index 6a0c7d564394..2790cd4676e6 100644 --- a/gui/locales/fr/messages.po +++ b/gui/locales/fr/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: French\n" "Language: fr_FR\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s ont été ajouté(es), compte payé jusqu’au %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Déconnexion en cours" msgid "Dismiss" msgstr "Ignorer" +msgid "Edit" +msgstr "Modifier" + +msgid "Enable" +msgstr "Activer" + msgid "Enable anyway" msgstr "Activer quand même" @@ -231,6 +237,11 @@ msgstr "Valeur par défaut du système" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Test" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Ce paramètre augmente la latence. Utilisez-le uniquement si nécessaire." @@ -253,6 +264,9 @@ msgstr "Débloquer" msgid "UNSECURED CONNECTION" msgstr "CONNEXION NON SÉCURISÉE" +msgid "Use" +msgstr "Utiliser" + msgid "Username" msgstr "Nom d'utilisateur" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Authentification" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Cliquer sur « Enregistrer » change le mode utilisé." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Cliquer sur « %(save)s » change le mode utilisé." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s via %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s via un pont personnalisé" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Sortante" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Ajouter un pont personnalisé" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Supprimer le pont personnalisé ?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Si vous supprimez le pont personnalisé, vous revenez à la vue de sélection de la localisation et l'option Automatique est sélectionnée à la place." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Modifier le pont personnalisé" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Mode pont" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Activer le mode pont ?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Changez le paramètre Mode Pont de Automatique à Off pour activer UDP." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Pour sélectionner un serveur de pont spécifique, accédez à la vue Sélectionner une localisation." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Protocole de transport" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Un serveur pont personnalisé peut être utilisé pour contourner la censure lorsque les serveurs ponts Mullvad habituels ne fonctionnent pas." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Pays" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Pont personnalisé" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Listes personnalisées" @@ -2014,7 +2067,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "Le port TCP auquel le protocole de dissimulation UDP sur TCP doit se connecter sur le serveur VPN." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (ajouté)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s a été ajouté à « %s »" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s ajouté(s) à votre compte." @@ -2039,6 +2100,10 @@ msgstr "Les crédits du compte expirent bientôt" msgid "Account time reminders" msgstr "Rappels de temps pour le compte" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Ajouter %s à la liste" + msgid "Add 30 days time" msgstr "Ajouter 30 jours de temps" @@ -2048,12 +2113,18 @@ msgstr "Ajouter 30 jours de temps (%s)" msgid "Add DNS server" msgstr "Ajouter un serveur DNS" +msgid "Add locations" +msgstr "Ajouter des localisations" + msgid "Agree and continue" msgstr "Accepter et continuer" msgid "All applications" msgstr "Toutes les applications" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Toutes les substitutions seront réinitialisées et les adresses IP des serveurs, dans la vue Sélectionner une localisation, reviendront à leur valeur par défaut." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Autorise l'accès aux autres appareils sur le même réseau pour partager, imprimer, etc." @@ -2063,6 +2134,9 @@ msgstr "« Toujours exiger un VPN » est assigné à une autre application" msgid "Always-on VPN might be enabled for another app" msgstr "« Toujours exiger un VPN » est peut-être activé pour une autre application" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Attention : le split tunneling est un risque de confidentialité." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Attention : ce paramètre ne peut pas être utilisé en combinaison avec Utiliser un serveur DNS personnalisé." @@ -2072,6 +2146,9 @@ msgstr "Connexion automatique et mode Verrouillage" msgid "Auto-connect & \\nLockdown mode" msgstr "Connexion automatique et \\nmode Verrouillage" +msgid "Auto-connect (legacy)" +msgstr "Connexion automatique (obsolète)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "La connexion automatique est appelée VPN toujours actif dans les paramètres du système Android et elle s'assure que vous êtes constamment connecté au tunnel VPN et se connecte automatiquement après le redémarrage." @@ -2093,6 +2170,12 @@ msgstr "Journaux copiés dans le presse-papiers" msgid "Copied to clipboard" msgstr "Copié dans le presse-papiers" +msgid "Create" +msgstr "Créer" + +msgid "Create new list" +msgstr "Créer une nouvelle liste" + msgid "Critical error (your attention is required)" msgstr "Erreur critique (votre attention est requise)" @@ -2102,11 +2185,32 @@ msgstr "Les adresses de serveur DNS personnalisées %s ne sont pas valides" msgid "DNS settings might not go into effect immediately" msgstr "Les paramètres DNS peuvent ne pas être immédiatement pris en compte" +msgid "Delete \"%s\"?" +msgstr "Supprimer « %s » ?" + msgid "Disable all %s above to activate this setting." msgstr "Désactivez tous les %s ci-dessus pour activer ce paramètre." -msgid "Enable" -msgstr "Activer" +msgid "Discard" +msgstr "Annuler" + +msgid "Discard changes?" +msgstr "Annuler les modifications ?" + +msgid "Edit custom lists" +msgstr "Modifier les listes personnalisées" + +msgid "Edit list" +msgstr "Modifier la liste" + +msgid "Edit lists" +msgstr "Modifier les listes" + +msgid "Edit locations" +msgstr "Modifier les localisations" + +msgid "Edit name" +msgstr "Modifier le nom" msgid "Enter MTU" msgstr "Saisir le MTU" @@ -2114,6 +2218,12 @@ msgstr "Saisir le MTU" msgid "Excluded applications" msgstr "Applications exclues" +msgid "Failed to apply patch" +msgstr "Impossible d'appliquer le correctif" + +msgid "File" +msgstr "Fichier" + msgid "Go to VPN settings" msgstr "Aller aux paramètres VPN" @@ -2129,9 +2239,34 @@ msgstr "Google Play indisponible" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Si la fonctionnalité de split tunneling est utilisée, l'application demande à votre système la liste de toutes les applications installées. Cette liste n'est récupérée que dans la vue de split tunneling. La liste des applications installées n'est jamais envoyée par l'appareil." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importer les nouvelles substitutions depuis" + +msgid "Import successful, overrides active" +msgstr "Importation réussie, les substitutions sont actives" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "L'importation de nouvelles substitutions peut remplacer certaines substitutions importées précédemment." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Installez Mullvad VPN (%s) pour rester à jour" +msgid "Invalid or missing value \"%s\"" +msgstr "Valeur « %s » non valide ou manquante" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Vous permet de sélectionner les applications qui doivent accéder directement à Internet sans passer par le tunnel VPN." + +msgid "List name" +msgstr "Nom de la liste" + +msgid "Locations" +msgstr "Localisations" + +msgid "Locations were changed for \"%s\"" +msgstr "Les localisations ont été modifiées pour « %s »" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Permet de s'assurer que l'appareil est toujours sur le tunnel VPN." @@ -2144,9 +2279,43 @@ msgstr "Numéro de compte Mullvad" msgid "Mullvad services unavailable" msgstr "Services Mullvad indisponibles" +msgid "Name was changed to %s" +msgstr "Le nom a été changé en %s" + +msgid "New list" +msgstr "Nouvelle liste" + +msgid "No custom lists available" +msgstr "Aucune liste personnalisée disponible" + msgid "No internet connection" msgstr "Aucune connexion à Internet" +msgid "No locations found" +msgstr "Aucune localisation trouvée" + +msgid "Not found" +msgstr "Introuvable" + +msgid "Overrides active" +msgstr "Substitutions actives" + +msgid "Overrides cleared" +msgstr "Substitutions supprimées" + +msgid "Overrides inactive" +msgstr "Substitutions inactives" + +msgid "Paste or write overrides to be imported" +msgstr "Collez ou saisissez les substitutions à importer" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Le correctif ne correspond pas à la spécification" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Veuillez utiliser le paramètre système Toujours activé en suivant le guide dans %s ci-dessus." + msgid "Preferences" msgstr "Préférences" @@ -2156,18 +2325,33 @@ msgstr "Confidentialité" msgid "Privacy policy" msgstr "Politique de confidentialité" +msgid "Recursion limit" +msgstr "Limite de récursion" + msgid "Remove" msgstr "Supprimer" msgid "Remove custom port" msgstr "Supprimer le port personnalisé" +msgid "Reset" +msgstr "Réinitialiser" + +msgid "Reset all overrides" +msgstr "Réinitialiser toutes les substitutions" + +msgid "Reset overrides" +msgstr "Réinitialiser les substitutions" + msgid "Reset to default" msgstr "Réinitialiser à la valeur par défaut" msgid "Secured" msgstr "Sécurisé" +msgid "Server Ip overrides" +msgstr "Substitutions d'IP de serveur" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Définir la valeur MTU WireGuard. Plage valide : %d - %d." @@ -2183,12 +2367,12 @@ msgstr "Affiche l'état actuel du tunnel VPN" msgid "Shows reminders when the account time is about to expire" msgstr "Affiche des rappels lorsque le temps du compte va expirer" -msgid "Split tunneling is disabled." -msgstr "Le split tunneling est désactivé." - msgid "Submit" msgstr "Envoyer" +msgid "Text" +msgstr "Texte" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Les paramètres de la connexion automatique et du mode Verrouillage se trouvent dans les paramètres du système Android, suivez ce guide pour activer l'un, l'autre ou les deux." @@ -2204,6 +2388,12 @@ msgstr "Votre appareil n'a pas de paramètres VPN" msgid "This address has already been entered." msgstr "Cette adresse a déjà été saisie." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Pour ajouter des localisations à une liste, appuyez sur la touche « ︙ » ou appuyez longuement sur un pays, une ville ou un serveur." + +msgid "To create a custom list press the \"︙\"" +msgstr "Appuyez sur « ︙ » pour créer une liste personnalisée" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Pour vous assurer que vous disposez de la version la plus sécurisée et pour vous informer de tout problème lié à la version en cours d'exécution, l'application effectue automatiquement des vérifications de version. Elle envoie ainsi la version de l'application et la version du système Android aux serveurs de Mullvad. Mullvad tient des compteurs sur le nombre de versions d'applications et de versions d'Android utilisées. Les données ne sont jamais stockées ou utilisées de manière à vous identifier." @@ -2213,9 +2403,19 @@ msgstr "Activer/désactiver le VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Impossible d'appliquer les règles du pare-feu. Merci de résoudre le problème ou d'envoyer un rapport de problème." +msgid "Unable to parse patch" +msgstr "Impossible d'analyser le correctif" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Impossible de démarrer la connexion au tunnel. Veuillez désactiver « Toujours exiger un VPN « pour %s avant d'utiliser Mullvad VPN." +msgid "Undo" +msgstr "Annuler" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Clé « %s » inconnue ou interdite" + msgid "Unsecured" msgstr "Non sécurisé" @@ -2225,6 +2425,9 @@ msgstr "Mettre à jour le serveur DNS" msgid "Update available, download to remain safe." msgstr "Mise à jour disponible. Téléchargez-la pour rester en sécurité." +msgid "Update list name" +msgstr "Mettre à jour le nom de la liste" + msgid "VPN permission error" msgstr "Erreur de permission VPN" @@ -2237,7 +2440,6 @@ msgstr "État du tunnel VPN" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Une clé WireGuard valide manque. Gérez les clés dans les paramètres avancés." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Plages valides : %s" @@ -2283,6 +2485,9 @@ msgstr "VOUS POURRIEZ AVOIR DES FUITES DE TRAFIC RÉSEAU" msgid "You are running an unsupported app version." msgstr "Vous utilisez une version de l'application non prise en charge." +msgid "\"%s\" was deleted" +msgstr "« %s » a été supprimé" + msgid "less than a minute ago" msgstr "il y a moins d'une minute" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d jour" msgstr[1] "%d jours" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d localisation" +msgstr[1] "%d localisations" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d mois" diff --git a/gui/locales/fr/relay-locations.po b/gui/locales/fr/relay-locations.po index 0ca719fa7ab0..fbcbda6e691f 100644 --- a/gui/locales/fr/relay-locations.po +++ b/gui/locales/fr/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: French\n" "Language: fr_FR\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australie" msgid "Austria" msgstr "Autriche" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelone" + #. BE msgid "Belgium" msgstr "Belgique" @@ -188,6 +196,10 @@ msgstr "Francfort-sur-le-Main" msgid "Germany" msgstr "Allemagne" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Lettonie" msgid "Lisbon" msgstr "Lisbonne" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "Londres" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marseille" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slovaquie" +#. SI +msgid "Slovenia" +msgstr "Slovénie" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thaïlande" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ukraine" msgid "United Arab Emirates" msgstr "Émirats arabes unis" +#. ES VLC +msgid "Valencia" +msgstr "Valence" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/it/messages.po b/gui/locales/it/messages.po index f0b862c1c564..d09a73a502d9 100644 --- a/gui/locales/it/messages.po +++ b/gui/locales/it/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Italian\n" "Language: it_IT\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "Aggiunta di %(duration)s effettuata, account pagato fino al %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Disconnessione" msgid "Dismiss" msgstr "Ignora" +msgid "Edit" +msgstr "Modifica" + +msgid "Enable" +msgstr "Abilita" + msgid "Enable anyway" msgstr "Abilita comunque" @@ -231,6 +237,11 @@ msgstr "Predefinito dal sistema" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Prova" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Questa impostazione aumenta la latenza. Usala solo se necessario." @@ -253,6 +264,9 @@ msgstr "Sblocca" msgid "UNSECURED CONNECTION" msgstr "CONNESSIONE NON PROTETTA" +msgid "Use" +msgstr "Usa" + msgid "Username" msgstr "Nome utente" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Autenticazione" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Cliccando su \"Salva\" si modifica il metodo in uso." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Cliccando su \"%(save)s\" si modifica il metodo in uso." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s tramite %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s tramite bridge personalizzato" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Invio" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Aggiungi bridge personalizzato" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Eliminare il bridge personalizzato?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "L'eliminazione del bridge personalizzato ti riporterà alla vista Seleziona posizione e verrà invece selezionata l'opzione Automatico." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Modifica bridge personalizzato" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Modalità bridge" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Abilitare la modalità bridge?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Per attivare UDP, cambia Modalità Bridge in Automatico o Off." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Per selezionare un server di bridge specifico, vai alla vista Seleziona posizione." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Protocollo di trasporto" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Un server di bridge personalizzato può essere utilizzato per aggirare la censura quando i normali server di bridge di Mullvad non funzionano." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Nazione" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Bridge personalizzato" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Elenchi personalizzati" @@ -2014,9 +2067,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "A quale porta TCP deve connettersi il protocollo di offuscamento UDP-over-TCP sul server VPN." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (aggiunto)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s aggiunto a \"%s\"" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "%s aggiunti al tuo account." +msgstr "%s aggiunto al tuo account." msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. Dopo aver cliccato sul pulsante Vai alle impostazioni VPN in basso, clicca sulla rotella accanto al nome Mullvad VPN." @@ -2039,6 +2100,10 @@ msgstr "Il credito dell'account scadrà presto" msgid "Account time reminders" msgstr "Promemoria temporali per l'account" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Aggiungi %s all'elenco" + msgid "Add 30 days time" msgstr "Aggiungi 30 giorni di tempo" @@ -2048,12 +2113,18 @@ msgstr "Aggiungi 30 giorni di tempo (%s)" msgid "Add DNS server" msgstr "Aggiungi server DNS" +msgid "Add locations" +msgstr "Aggiungi posizioni" + msgid "Agree and continue" msgstr "Accetta e continua" msgid "All applications" msgstr "Tutte le applicazioni" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Tutte le sovrascritture verranno ripristinate e gli indirizzi IP dei server nella vista Seleziona posizione torneranno ai valori predefiniti." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Consenti l'accesso ad altri dispositivi sulla stessa rete per condividere, stampare e altro." @@ -2063,6 +2134,9 @@ msgstr "VPN sempre attiva assegnata a un'altra app" msgid "Always-on VPN might be enabled for another app" msgstr "La VPN sempre attiva potrebbe essere abilitata per un'altra app" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Attenzione: lo split tunneling rappresenta un rischio per la privacy." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Attenzione: questa impostazione non può essere utilizzata in combinazione con Usa un server DNS personalizzato." @@ -2072,6 +2146,9 @@ msgstr "Modalità di connessione automatica e blocco" msgid "Auto-connect & \\nLockdown mode" msgstr "Modalità di connessione\\nautomatica e blocco" +msgid "Auto-connect (legacy)" +msgstr "Connessione automatica (precedente)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "La connessione automatica è denominata VPN sempre attiva nelle impostazioni del sistema Android e garantisce di essere costantemente connessi al tunnel VPN e di connettersi automaticamente dopo il riavvio." @@ -2093,6 +2170,12 @@ msgstr "Log copiati negli appunti" msgid "Copied to clipboard" msgstr "Copiato negli appunti" +msgid "Create" +msgstr "Crea" + +msgid "Create new list" +msgstr "Crea nuovo elenco" + msgid "Critical error (your attention is required)" msgstr "Errore critico (è necessario intervenire)" @@ -2102,11 +2185,32 @@ msgstr "Gli indirizzi del server DNS personalizzato %s non sono validi" msgid "DNS settings might not go into effect immediately" msgstr "Le impostazioni DNS potrebbero non avere effetto immediato" +msgid "Delete \"%s\"?" +msgstr "Eliminare \"%s\"?" + msgid "Disable all %s above to activate this setting." msgstr "Disabilita tutti i %s sopra per attivare questa impostazione." -msgid "Enable" -msgstr "Abilita" +msgid "Discard" +msgstr "Ignora modifiche" + +msgid "Discard changes?" +msgstr "Ignorare le modifiche?" + +msgid "Edit custom lists" +msgstr "Modifica elenchi personalizzati" + +msgid "Edit list" +msgstr "Modifica elenco" + +msgid "Edit lists" +msgstr "Modifica elenchi" + +msgid "Edit locations" +msgstr "Modifica posizioni" + +msgid "Edit name" +msgstr "Modifica nome" msgid "Enter MTU" msgstr "Inserisci MTU" @@ -2114,6 +2218,12 @@ msgstr "Inserisci MTU" msgid "Excluded applications" msgstr "Applicazioni escluse" +msgid "Failed to apply patch" +msgstr "Impossibile applicare la patch" + +msgid "File" +msgstr "File" + msgid "Go to VPN settings" msgstr "Vai alle impostazioni VPN" @@ -2129,9 +2239,34 @@ msgstr "Google Play non disponibile" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Se viene utilizzata la funzionalità di split tunneling, l'app richiede al tuo sistema un elenco di tutte le applicazioni installate. Questo elenco viene recuperato solo nella vista split tunneling. L'elenco delle applicazioni installate non viene mai inviato dal dispositivo." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importa nuove sovrascritture da" + +msgid "Import successful, overrides active" +msgstr "Importazione riuscita, sovrascritture attiva" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "L'importazione di nuove sovrascritture potrebbe sostituire alcune sovrascritture precedentemente importate." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Installa Mullvad VPN (%s) per rimanere aggiornato" +msgid "Invalid or missing value \"%s\"" +msgstr "Valore \"%s\" non valido o mancante" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Ti consente di selezionare le app che devono accedere direttamente a Internet senza passare attraverso il tunnel VPN." + +msgid "List name" +msgstr "Nome dell'elenco" + +msgid "Locations" +msgstr "Posizioni" + +msgid "Locations were changed for \"%s\"" +msgstr "Le posizioni sono state modificate per \"%s\"" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Assicurati che il dispositivo sia sempre collegato al tunnel VPN." @@ -2144,9 +2279,43 @@ msgstr "Numero di account Mullvad" msgid "Mullvad services unavailable" msgstr "Servizi Mullvad non disponibili" +msgid "Name was changed to %s" +msgstr "Il nome è stato modificato in %s" + +msgid "New list" +msgstr "Nuovo elenco" + +msgid "No custom lists available" +msgstr "Nessun elenco personalizzato disponibile" + msgid "No internet connection" msgstr "Nessuna connessione Internet" +msgid "No locations found" +msgstr "Nessuna posizione trovata" + +msgid "Not found" +msgstr "Non trovato" + +msgid "Overrides active" +msgstr "Sovrascritture attive" + +msgid "Overrides cleared" +msgstr "Sovrascritture cancellate" + +msgid "Overrides inactive" +msgstr "Sovrascritture inattive" + +msgid "Paste or write overrides to be imported" +msgstr "Incolla o scrivi le sovrascritture da importare" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "La patch non corrisponde alle specifiche" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Utilizza piuttosto l'impostazione di sistema Sempre attivo seguendo la guida in %s sopra." + msgid "Preferences" msgstr "Preferenze" @@ -2156,18 +2325,33 @@ msgstr "Privacy" msgid "Privacy policy" msgstr "Informativa sulla privacy" +msgid "Recursion limit" +msgstr "Limite di ricorsione" + msgid "Remove" msgstr "Rimuovi" msgid "Remove custom port" msgstr "Rimuovi porta personalizzata" +msgid "Reset" +msgstr "Reimposta" + +msgid "Reset all overrides" +msgstr "Reimposta tutte le sovrascritture" + +msgid "Reset overrides" +msgstr "Reimposta sovrascritture" + msgid "Reset to default" msgstr "Ripristina predefiniti" msgid "Secured" msgstr "Protetto" +msgid "Server Ip overrides" +msgstr "Sovrascritture IP server" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Imposta il valore MTU WireGuard. Intervallo valido: %d - %d." @@ -2183,12 +2367,12 @@ msgstr "Mostra lo stato attuale del tunnel VPN" msgid "Shows reminders when the account time is about to expire" msgstr "Mostra promemoria quando il tempo dell'account sta per scadere" -msgid "Split tunneling is disabled." -msgstr "Lo split tunneling è disabilitato." - msgid "Submit" msgstr "Invia" +msgid "Text" +msgstr "Testo" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Le impostazioni della modalità di Connessione automatica e di Blocco si trovano nelle impostazioni del sistema Android. Segui questa guida per abilitare una o entrambe." @@ -2204,6 +2388,12 @@ msgstr "Non ci sono impostazioni VPN sul tuo dispositivo" msgid "This address has already been entered." msgstr "Questo indirizzo è già stato inserito." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Per aggiungere posizioni a un elenco, premi \"︙\" o tieni premuto su un Paese, una città o un server." + +msgid "To create a custom list press the \"︙\"" +msgstr "Per creare un elenco personalizzato, premi \"︙\"" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Per assicurarti di avere la versione più sicura e per informarti di eventuali problemi con la versione attualmente in esecuzione, l'app esegue automaticamente i controlli di versione. Questa operazione invia la versione dell'app e la versione del sistema Android ai server Mullvad. Mullvad mantiene traccia dei numeri di versione dell'app utilizzati e delle versioni di Android. I dati non vengono mai archiviati o utilizzati in alcun modo che possa identificarti." @@ -2213,9 +2403,19 @@ msgstr "Attiva/disattiva VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Impossibile applicare le regole del firewall. Consulta la risoluzione dei problemi o invia una segnalazione del problema." +msgid "Unable to parse patch" +msgstr "Impossibile analizzare la patch" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Impossibile avviare la connessione tunnel. Disabilita VPN sempre attiva per %s prima di utilizzare Mullvad VPN." +msgid "Undo" +msgstr "Annulla" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Chiave \"%s\" sconosciuta o vietata" + msgid "Unsecured" msgstr "Non protetto" @@ -2225,6 +2425,9 @@ msgstr "Aggiorna server DNS" msgid "Update available, download to remain safe." msgstr "Aggiornamento disponibile; esegui il download per rimanere protetto." +msgid "Update list name" +msgstr "Aggiorna nome elenco" + msgid "VPN permission error" msgstr "Errore di autorizzazione VPN" @@ -2237,7 +2440,6 @@ msgstr "Stato del tunnel VPN" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Manca una chiave WireGuard valida. Gestisci le chiavi da Impostazioni avanzate." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Intervalli validi: %s" @@ -2283,6 +2485,9 @@ msgstr "POSSIBILI PERDITE NEL TRAFFICO DI RETE" msgid "You are running an unsupported app version." msgstr "Stai eseguendo una versione dell'app non supportata." +msgid "\"%s\" was deleted" +msgstr "\"%s\" eliminato" + msgid "less than a minute ago" msgstr "meno di un minuto fa" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d giorno" msgstr[1] "%d giorni" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d posizione" +msgstr[1] "%d posizioni" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d mese" diff --git a/gui/locales/it/relay-locations.po b/gui/locales/it/relay-locations.po index 3685468a2ff3..7b9930c33c05 100644 --- a/gui/locales/it/relay-locations.po +++ b/gui/locales/it/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Italian\n" "Language: it_IT\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australia" msgid "Austria" msgstr "Austria" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcellona" + #. BE msgid "Belgium" msgstr "Belgio" @@ -188,6 +196,10 @@ msgstr "Francoforte sul Meno" msgid "Germany" msgstr "Germania" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Lettonia" msgid "Lisbon" msgstr "Lisbona" +#. SI LJU +msgid "Ljubljana" +msgstr "Lubiana" + #. GB LON msgid "London" msgstr "Londra" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marsiglia" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slovakia" +#. SI +msgid "Slovenia" +msgstr "Slovenia" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thailandia" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ucraina" msgid "United Arab Emirates" msgstr "Emirati Arabi Uniti" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/ja/messages.po b/gui/locales/ja/messages.po index a4a4c705d82e..70efe8d182a9 100644 --- a/gui/locales/ja/messages.po +++ b/gui/locales/ja/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Japanese\n" "Language: ja_JP\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)sが追加され、アカウントは%(expiry)sまでに支払われます。" @@ -133,6 +133,12 @@ msgstr "接続解除中" msgid "Dismiss" msgstr "閉じる" +msgid "Edit" +msgstr "編集" + +msgid "Enable" +msgstr "有効にする" + msgid "Enable anyway" msgstr "それでも有効化する" @@ -225,6 +231,11 @@ msgstr "システムデフォルト" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "テスト" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "この設定を有効にすると、遅延が増加します。必要な場合にのみご利用ください。" @@ -247,6 +258,9 @@ msgstr "ブロックを解除" msgid "UNSECURED CONNECTION" msgstr "セキュリティ保護されていない接続" +msgid "Use" +msgstr "使用" + msgid "Username" msgstr "ユーザー名" @@ -382,9 +396,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "認証" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "[保存] をクリックすると使用中の方法が変更されます。" +msgid "Clicking “%(save)s” changes the in use method." +msgstr "[%(save)s] をクリックすると、使用中の方法が変更されます。" msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -627,6 +642,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s (%(entry)s経由)" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "カスタムブリッジを経由した %(relay)s" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -643,6 +662,22 @@ msgctxt "connection-info" msgid "Out" msgstr "外側" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "カスタムブリッジの追加" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "カスタムブリッジを削除しますか?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "カスタムブリッジを削除すると場所の選択ビューに戻り、代わりに自動オプションが選択されます。" + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "カスタムブリッジの編集" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1151,6 +1186,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "ブリッジモード" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "ブリッジモードを有効にしますか?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1197,6 +1236,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "UDPを有効にするには、ブリッジモード自動またはオフに変更してください。" +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "特定のブリッジサーバーを選択するには、場所の選択ビューに移動します。" + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "転送プロトコル" @@ -1260,6 +1305,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "カスタムブリッジサーバーは、通常の Mullvad ブリッジサーバーが機能しない場合に検閲を回避するために使用される可能性があります。" + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1279,6 +1328,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "国" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "カスタムブリッジ" + msgctxt "select-location-view" msgid "Custom lists" msgstr "カスタムリスト" @@ -2008,9 +2061,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "UDP-over-TCP難読化プロトコルで接続する必要のあるVPNサーバーのTCPポートです。" -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (追加済み)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s が \"%s\" に追加されました" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "%sがアカウントに追加されました。" +msgstr "%sがご利用のアカウントに追加されました。" msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. 下の [VPN設定に移動] ボタンをクリックし、Mullvad VPN名の横にある歯車をクリックします。" @@ -2033,6 +2094,10 @@ msgstr "アカウントのクレジットがもうすぐ無効になります" msgid "Account time reminders" msgstr "アカウント時間のリマインダー" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "%s をリストに追加する" + msgid "Add 30 days time" msgstr "30日分を追加する" @@ -2042,12 +2107,18 @@ msgstr "30日分を追加する (%s)" msgid "Add DNS server" msgstr "DNS サーバーを追加" +msgid "Add locations" +msgstr "場所の追加" + msgid "Agree and continue" msgstr "同意して続行" msgid "All applications" msgstr "すべてのアプリケーション" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "すべてのオーバーライドはリセットされ、場所の選択ビューのサーバーの IP アドレスはデフォルトに戻ります。" + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "共有や印刷などのため、同一ネットワーク上の他のデバイスへのアクセスを許可します。" @@ -2057,6 +2128,9 @@ msgstr "Always-on VPNは他のアプリに割り当てられています" msgid "Always-on VPN might be enabled for another app" msgstr "Always-on VPNが別のアプリで有効になっている可能性があります" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "注意: スプリットトンネリングには、プライバシーが公開されるリスクがあります。" + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "注意: この設定を「カスタムDNSサーバーを使う」と一緒に使用することはできません。" @@ -2066,6 +2140,9 @@ msgstr "自動接続とロックダウンモード" msgid "Auto-connect & \\nLockdown mode" msgstr "自動接続と\\nロックダウンモード" +msgid "Auto-connect (legacy)" +msgstr "自動接続 (レガシー)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "自動接続はAndroidシステム設定で「Always-on VPN」と呼ばれており、確実に一貫してVPNトンネルに接続し、再起動の後に自動接続を行う機能です。" @@ -2087,6 +2164,12 @@ msgstr "ログをクリップボードにコピーしました" msgid "Copied to clipboard" msgstr "クリップボードにコピーしました" +msgid "Create" +msgstr "作成" + +msgid "Create new list" +msgstr "リストの新規作成" + msgid "Critical error (your attention is required)" msgstr "重大なエラー (ご注意ください)" @@ -2096,11 +2179,32 @@ msgstr "カスタムDNSサーバーアドレス %s は無効です" msgid "DNS settings might not go into effect immediately" msgstr "DNS設定はすぐに適用されない可能性があります" +msgid "Delete \"%s\"?" +msgstr "\"%s\" を削除しますか?" + msgid "Disable all %s above to activate this setting." msgstr "この設定を有効にするには、上記のすべての%sを無効にしてください。" -msgid "Enable" -msgstr "有効にする" +msgid "Discard" +msgstr "破棄" + +msgid "Discard changes?" +msgstr "変更内容を破棄しますか?" + +msgid "Edit custom lists" +msgstr "カスタムリストを編集する" + +msgid "Edit list" +msgstr "リストの編集" + +msgid "Edit lists" +msgstr "リストを編集" + +msgid "Edit locations" +msgstr "場所の編集" + +msgid "Edit name" +msgstr "名前を編集" msgid "Enter MTU" msgstr "MTU を入力" @@ -2108,6 +2212,12 @@ msgstr "MTU を入力" msgid "Excluded applications" msgstr "除外対象アプリケーション" +msgid "Failed to apply patch" +msgstr "パッチを適用できませんでした" + +msgid "File" +msgstr "ファイル" + msgid "Go to VPN settings" msgstr "VPN設定に移動" @@ -2123,9 +2233,34 @@ msgstr "Google Playを使用できません" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "スプリットトンネリング機能が使用されている場合、アプリはシステムにインストール済みの全アプリケーションのリストを照会します。このリストはスプリットトンネリングビューでのみ取得されます。インストール済みアプリケーションのリストがデバイスから送信されることはありません。" +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "新しいオーバーライドのインポート" + +msgid "Import successful, overrides active" +msgstr "インポートが正常に完了しました。オーバーライドは有効です" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "新しいオーバーライドをインポートすると、以前インポートしたオーバーライドが置き換えられる可能性があります。" + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Mullvad VPN (%s) をインストールして常に最新の状態を保ちましょう" +msgid "Invalid or missing value \"%s\"" +msgstr "値 \"%s\" は無効であるか、または見つかりません" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "VPN トンネルを経由せずに、直接インターネットにアクセスするアプリを選択できます。" + +msgid "List name" +msgstr "リスト名" + +msgid "Locations" +msgstr "場所" + +msgid "Locations were changed for \"%s\"" +msgstr "\"%s\" の場所が変更されました" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "デバイスが必ずVPNトンネルを使用するようにします。" @@ -2138,9 +2273,43 @@ msgstr "Mullvadアカウント番号" msgid "Mullvad services unavailable" msgstr "Mullvadサービスを使用できません" +msgid "Name was changed to %s" +msgstr "名前が %s に変更されました" + +msgid "New list" +msgstr "新規リスト" + +msgid "No custom lists available" +msgstr "利用可能なカスタムリストはありません" + msgid "No internet connection" msgstr "インターネット接続がありません" +msgid "No locations found" +msgstr "場所が見つかりませんでした" + +msgid "Not found" +msgstr "見つかりませんでした" + +msgid "Overrides active" +msgstr "オーバーライドは有効です" + +msgid "Overrides cleared" +msgstr "オーバーライドがクリアされました" + +msgid "Overrides inactive" +msgstr "オーバーライドは無効です" + +msgid "Paste or write overrides to be imported" +msgstr "インポートするオーバーライドを貼り付けるか、または記入してください" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "パッチが仕様に一致していません" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "上の %s のガイドに従い、Always-on のシステム設定を代わりに使用してください。" + msgid "Preferences" msgstr "環境設定" @@ -2150,18 +2319,33 @@ msgstr "プライバシー" msgid "Privacy policy" msgstr "プライバシーポリシー" +msgid "Recursion limit" +msgstr "繰り返しの制限" + msgid "Remove" msgstr "削除" msgid "Remove custom port" msgstr "カスタムポートを削除" +msgid "Reset" +msgstr "リセット" + +msgid "Reset all overrides" +msgstr "すべてのオーバーライドをリセット" + +msgid "Reset overrides" +msgstr "オーバーライドをリセット" + msgid "Reset to default" msgstr "デフォルトにリセット" msgid "Secured" msgstr "セキュリティ保護されています" +msgid "Server Ip overrides" +msgstr "サーバー IP のオーバーライド" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "WireGuard MTUの値を設定します。有効範囲: %d ~ %d" @@ -2177,12 +2361,12 @@ msgstr "現在のVPNトンネルのステータスを表示します" msgid "Shows reminders when the account time is about to expire" msgstr "アカウントの期限切れが迫っているときにリマインダーを表示します" -msgid "Split tunneling is disabled." -msgstr "スプリットトンネリングは無効です。" - msgid "Submit" msgstr "送信" +msgid "Text" +msgstr "テキスト" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "自動接続とロックダウンモードの設定はAndroidシステム設定にあります。どちらか、または両方をこのガイドに従って有効にしてください。" @@ -2198,6 +2382,12 @@ msgstr "デバイスにVPN設定がありません" msgid "This address has already been entered." msgstr "このアドレスは入力済みです。" +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "リストに場所を追加するには、\"︙\" を押すか、または、国、都市、サーバーを長押ししてください。" + +msgid "To create a custom list press the \"︙\"" +msgstr "カスタムリストを作成するには、\"︙\" を押してください" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "このアプリは最も安全なバージョンを使用していることを確認し、実行中の現在のバージョンに関する問題を通知できるよう、バージョンチェックを自動的に実行します。このチェックによってアプリのバージョンとAndroidシステムのバージョンがMullvadサーバーに送信されます。Mullvadは使用されているアプリのバージョンとAndroidのバージョンの数字を記憶しています。このデータがユーザーを特定できる方法で保存されたり、使用されたりすることはありません。" @@ -2207,9 +2397,19 @@ msgstr "VPNの切り替え" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "ファイアウォールのルールを適用できません。問題に対処するか、問題の報告を送信してください。" +msgid "Unable to parse patch" +msgstr "パッチを解析できません" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "トンネル接続を開始できません。Mullvad VPNを使用する前に%sのAlways-on VPNを無効にしてください。" +msgid "Undo" +msgstr "元に戻す" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "キー \"%s\" は不明であるか、または禁止されています" + msgid "Unsecured" msgstr "セキュリティ保護されていません" @@ -2219,6 +2419,9 @@ msgstr "DNS サーバーを更新" msgid "Update available, download to remain safe." msgstr "アップデートできます。セキュリティを維持するにはダウンロードしてしてください。" +msgid "Update list name" +msgstr "リスト名の更新" + msgid "VPN permission error" msgstr "VPN許可エラー" @@ -2231,7 +2434,6 @@ msgstr "VPNトンネルのステータス" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "有効なWireGuard鍵が見つかりません。詳細設定で鍵を管理してください。" -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "有効な範囲: %s" @@ -2277,6 +2479,9 @@ msgstr "ネットワーク通信が漏洩している可能性があります" msgid "You are running an unsupported app version." msgstr "サポート対象外バージョンのアプリを実行しています。" +msgid "\"%s\" was deleted" +msgstr "\"%s\" は削除されました" + msgid "less than a minute ago" msgstr "1分未満前" @@ -2287,6 +2492,10 @@ msgid "%d day" msgid_plural "%d days" msgstr[0] "%d日" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d 件の場所" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%dヶ月" diff --git a/gui/locales/ja/relay-locations.po b/gui/locales/ja/relay-locations.po index 38519aa21be5..36ffb8468795 100644 --- a/gui/locales/ja/relay-locations.po +++ b/gui/locales/ja/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Japanese\n" "Language: ja_JP\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "オーストラリア" msgid "Austria" msgstr "オーストリア" +#. TH BKK +msgid "Bangkok" +msgstr "バンコク" + +#. ES BCN +msgid "Barcelona" +msgstr "バルセロナ" + #. BE msgid "Belgium" msgstr "ベルギー" @@ -188,6 +196,10 @@ msgstr "フランクフルト" msgid "Germany" msgstr "ドイツ" +#. GB GLW +msgid "Glasgow" +msgstr "グラスゴー" + #. SE GOT msgid "Gothenburg" msgstr "ヨーテボリ" @@ -264,6 +276,10 @@ msgstr "ラトビア" msgid "Lisbon" msgstr "リスボン" +#. SI LJU +msgid "Ljubljana" +msgstr "リュブリャナ" + #. GB LON msgid "London" msgstr "ロンドン" @@ -296,6 +312,10 @@ msgstr "マンチェスター" msgid "Marseille" msgstr "マルセイユ" +#. US TXC +msgid "McAllen, TX" +msgstr "マッカレン、TX" + #. AU MEL msgid "Melbourne" msgstr "メルボルン" @@ -472,6 +492,10 @@ msgstr "スコピエ" msgid "Slovakia" msgstr "スロバキア" +#. SI +msgid "Slovenia" +msgstr "スロベニア" + #. BG SOF msgid "Sofia" msgstr "ソフィア" @@ -520,6 +544,10 @@ msgstr "タリン" msgid "Tel Aviv" msgstr "テルアビブ" +#. TH +msgid "Thailand" +msgstr "タイ" + #. AL TIA msgid "Tirana" msgstr "ティラナ" @@ -548,6 +576,10 @@ msgstr "ウクライナ" msgid "United Arab Emirates" msgstr "アラブ首長国連邦" +#. ES VLC +msgid "Valencia" +msgstr "バレンシア" + #. CA VAN msgid "Vancouver" msgstr "バンクーバー" diff --git a/gui/locales/ko/messages.po b/gui/locales/ko/messages.po index 3bc402c2956c..5e8200082e08 100644 --- a/gui/locales/ko/messages.po +++ b/gui/locales/ko/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Korean\n" "Language: ko_KR\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s 추가됨, %(expiry)s까지 계정 결제함" @@ -133,6 +133,12 @@ msgstr "연결 해제 중" msgid "Dismiss" msgstr "해제" +msgid "Edit" +msgstr "편집" + +msgid "Enable" +msgstr "사용" + msgid "Enable anyway" msgstr "그래도 사용" @@ -225,6 +231,11 @@ msgstr "시스템 기본값" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "테스트" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "이 설정을 사용하면 대기 시간이 길어집니다. 필요한 경우에만 사용하세요." @@ -247,6 +258,9 @@ msgstr "차단 해제" msgid "UNSECURED CONNECTION" msgstr "비보안 연결" +msgid "Use" +msgstr "사용" + msgid "Username" msgstr "사용자 이름" @@ -382,9 +396,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "인증" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "“저장”을 클릭하면 사용 중인 방법이 변경됩니다." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "“%(save)s”을 클릭하면 사용 중인 방법이 변경됩니다." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -627,6 +642,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(entry)s을(를) 통한 %(relay)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "사용자 지정 브리지를 통한 %(relay)s" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -643,6 +662,22 @@ msgctxt "connection-info" msgid "Out" msgstr "아웃" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "사용자 지정 브리지 추가" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "사용자 지정 브리지를 삭제하시겠습니까?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "사용자 지정 브리지를 삭제하면 위치 선택 보기로 돌아가며 자동 옵션이 대신 선택됩니다." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "사용자 지정 브리지 편집" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1151,6 +1186,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "브리지 모드" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "브리지 모드를 활성화하시겠습니까?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1197,6 +1236,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "UDP를 활성화하려면 브리지 모드자동 또는 끄기로 변경합니다." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "특정 브리지 서버를 선택하려면 위치 선택 보기로 이동합니다." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "전송 프로토콜" @@ -1260,6 +1305,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s(%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "사용자 지정 브리지 서버는 일반 Mullvad 브리지 서버가 작동되지 않을 때 검열을 우회하기 위해 사용할 수 있습니다." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1279,6 +1328,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "국가" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "사용자 지정 브리지" + msgctxt "select-location-view" msgid "Custom lists" msgstr "사용자 지정 목록" @@ -2008,7 +2061,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "UDP-over-TCP 난독 처리 프로토콜이 VPN 서버에서 연결해야 하는 TCP 포트입니다." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s(추가됨)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s이(가) \"%s\"에 추가되었습니다" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s이(가) 계정에 추가되었습니다." @@ -2033,6 +2094,10 @@ msgstr "계정 크레딧이 곧 만료됨" msgid "Account time reminders" msgstr "계정 시간 알림" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "목록에 %s 추가" + msgid "Add 30 days time" msgstr "30일 시간 추가" @@ -2042,12 +2107,18 @@ msgstr "30일 시간 추가(%s)" msgid "Add DNS server" msgstr "DNS 서버 추가" +msgid "Add locations" +msgstr "위치 추가" + msgid "Agree and continue" msgstr "동의하고 계속하기" msgid "All applications" msgstr "모든 애플리케이션" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "모든 재정의가 초기화되며 위치 선택 보기의 서버 IP 주소는 기본값으로 돌아갑니다." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "공유, 인쇄 등을 위해 동일한 네트워크의 다른 장치에 액세스할 수 있습니다." @@ -2057,6 +2128,9 @@ msgstr "상시 접속 VPN이 다른 앱에 할당됨" msgid "Always-on VPN might be enabled for another app" msgstr "상시 접속 VPN이 다른 앱에 활성화되었을 수 있습니다." +msgid "Attention: Split tunneling is a privacy risk." +msgstr "주의: 분할 터널링 시 개인 정보가 위험할 수 있습니다." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "주의: 이 설정은 사용자 지정 DNS 서버 사용과 함께 사용할 수 없습니다." @@ -2066,6 +2140,9 @@ msgstr "자동 연결 및 잠금 모드" msgid "Auto-connect & \\nLockdown mode" msgstr "자동 연결 및 \\n잠금 모드" +msgid "Auto-connect (legacy)" +msgstr "자동 연결(레거시)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "자동 연결은 Android 시스템 설정에서 상시 접속 VPN이라고 하며 VPN 터널에 지속적으로 연결되고 다시 시작한 후 자동 연결되게 해줍니다." @@ -2087,6 +2164,12 @@ msgstr "로그를 클립보드에 복사했습니다." msgid "Copied to clipboard" msgstr "클립보드에 복사됨" +msgid "Create" +msgstr "생성" + +msgid "Create new list" +msgstr "새 목록 생성" + msgid "Critical error (your attention is required)" msgstr "심각한 오류(주의가 필요함)" @@ -2096,11 +2179,32 @@ msgstr "사용자 지정 DNS 서버 주소 %s이(가) 잘못되었습니다." msgid "DNS settings might not go into effect immediately" msgstr "DNS 설정이 즉시 적용되지 않을 수도 있습니다" +msgid "Delete \"%s\"?" +msgstr "\"%s\"을(를) 삭제하시겠습니까?" + msgid "Disable all %s above to activate this setting." msgstr "이 설정을 활성화하려면 위의 모든 %s을(를) 비활성화하세요." -msgid "Enable" -msgstr "사용" +msgid "Discard" +msgstr "취소" + +msgid "Discard changes?" +msgstr "변경 사항을 취소하시겠습니까?" + +msgid "Edit custom lists" +msgstr "사용자 지정 목록 편집" + +msgid "Edit list" +msgstr "목록 편집" + +msgid "Edit lists" +msgstr "목록 편집" + +msgid "Edit locations" +msgstr "위치 편집" + +msgid "Edit name" +msgstr "이름 편집" msgid "Enter MTU" msgstr "MTU 입력" @@ -2108,6 +2212,12 @@ msgstr "MTU 입력" msgid "Excluded applications" msgstr "제외된 애플리케이션" +msgid "Failed to apply patch" +msgstr "패치 적용 실패" + +msgid "File" +msgstr "파일" + msgid "Go to VPN settings" msgstr "VPN 설정으로 이동" @@ -2123,9 +2233,34 @@ msgstr "Google Play 사용 불가" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "분할 터널링 기능을 사용하는 경우, 이 앱은 설치된 모든 애플리케이션 목록을 시스템에 쿼리합니다. 이 목록은 분할 터널링 보기에서만 검색됩니다. 설치된 애플리케이션 목록은 장치에서 전송되지 않습니다." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "다음으로 새로운 재정의 가져오기:" + +msgid "Import successful, overrides active" +msgstr "가져오기 성공, 재정의 활성화" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "새로운 재정의를 가져오면 이전에 가져온 일부 재정의가 교체될 수 있습니다." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Mullvad VPN(%s)을 설치하여 최신 상태로 유지하세요." +msgid "Invalid or missing value \"%s\"" +msgstr "\"%s\"에 대해 유효하지 않거나 누락된 값" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "VPN 터널을 거치지 않고 인터넷에서 바로 액세스할 앱을 선택할 수 있습니다." + +msgid "List name" +msgstr "목록 이름" + +msgid "Locations" +msgstr "위치" + +msgid "Locations were changed for \"%s\"" +msgstr "\"%s\"에 대해 위치가 변경되었습니다" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "장치가 항상 VPN 터널에 있어야 합니다." @@ -2138,9 +2273,43 @@ msgstr "Mullvad 계정 번호" msgid "Mullvad services unavailable" msgstr "Mullvad 서비스 사용 불가" +msgid "Name was changed to %s" +msgstr "이름이 %s(으)로 변경되었습니다" + +msgid "New list" +msgstr "새 목록" + +msgid "No custom lists available" +msgstr "이용 가능한 사용자 지정 목록 없음" + msgid "No internet connection" msgstr "인터넷에 연결되지 않음" +msgid "No locations found" +msgstr "위치를 찾을 수 없음" + +msgid "Not found" +msgstr "찾을 수 없음" + +msgid "Overrides active" +msgstr "재정의 활성화" + +msgid "Overrides cleared" +msgstr "재정의 지워짐" + +msgid "Overrides inactive" +msgstr "재정의 비활성화" + +msgid "Paste or write overrides to be imported" +msgstr "가져올 재정의를 붙여넣거나 작성하세요" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "사양과 일치하지 않는 패치" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "위의 %s 가이드에 따라 상시 접속 시스템 설정을 대신 사용하세요." + msgid "Preferences" msgstr "환경 설정" @@ -2150,18 +2319,33 @@ msgstr "개인 정보 보호" msgid "Privacy policy" msgstr "개인정보 보호정책" +msgid "Recursion limit" +msgstr "반복 제한" + msgid "Remove" msgstr "제거" msgid "Remove custom port" msgstr "사용자 지정 포트 제거" +msgid "Reset" +msgstr "초기화" + +msgid "Reset all overrides" +msgstr "모든 재정의 초기화" + +msgid "Reset overrides" +msgstr "재정의 초기화" + msgid "Reset to default" msgstr "기본값으로 재설정" msgid "Secured" msgstr "안전함" +msgid "Server Ip overrides" +msgstr "서버 IP 재정의" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "WireGuard MTU 값을 설정하세요. 유효 범위: %d ~ %d" @@ -2177,12 +2361,12 @@ msgstr "현재 VPN 터널 상태 표시" msgid "Shows reminders when the account time is about to expire" msgstr "계정 시간이 만료되려고 할 때 알림 표시" -msgid "Split tunneling is disabled." -msgstr "분할 터널링이 비활성화되었습니다." - msgid "Submit" msgstr "제출" +msgid "Text" +msgstr "텍스트" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "자동 연결 및 잠금 모드 설정은 Android 시스템 설정에서 찾을 수 있습니다. 이 가이드에 따라 하나 또는 둘 다를 활성화하세요." @@ -2198,6 +2382,12 @@ msgstr "장치에 VPN 설정이 없습니다" msgid "This address has already been entered." msgstr "이 주소는 이미 입력되었습니다." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "목록에 위치를 추가하려면 \"︙\"를 누르거나 국가, 도시 또는 서버를 길게 누릅니다." + +msgid "To create a custom list press the \"︙\"" +msgstr "사용자 지정 목록을 생성하려면 \"︙\"를 누릅니다" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "가장 안전한 버전인지 확인하고 현재 실행 중인 버전에 문제가 있으면 알려드리기 위해 앱에서 자동으로 버전 확인을 수행합니다. 그러면 앱 버전과 Android 시스템 버전이 Mullvad 서버로 전송됩니다. Mullvad는 사용된 앱 버전 및 Android 버전 수에 대한 카운터를 유지합니다. 이 데이터는 귀하를 식별할 수 있는 어떤 방식으로도 저장되거나 사용되지 않습니다." @@ -2207,9 +2397,19 @@ msgstr "VPN 전환" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "방화벽 규칙을 적용할 수 없습니다. 문제를 해결하거나 문제 보고서를 보내주세요." +msgid "Unable to parse patch" +msgstr "패치를 파싱할 수 없음" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "터널 연결을 시작할 수 없습니다. Mullvad VPN을 사용하기 전에 %s에 대한 상시 접속 VPN을 비활성화하세요." +msgid "Undo" +msgstr "실행 취소" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "알 수 없거나 금지된 키 \"%s\"" + msgid "Unsecured" msgstr "안전하지 않음" @@ -2219,6 +2419,9 @@ msgstr "DNS 서버 업데이트" msgid "Update available, download to remain safe." msgstr "업데이트를 사용할 수 있습니다. 안전을 유지하기 위해 다운로드하세요." +msgid "Update list name" +msgstr "목록 이름 업데이트" + msgid "VPN permission error" msgstr "VPN 권한 오류" @@ -2231,7 +2434,6 @@ msgstr "VPN 터널 상태" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "유효한 WireGuard 키가 없습니다. 고급 설정에서 키를 관리하세요." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "유효한 범위: %s" @@ -2277,6 +2479,9 @@ msgstr "네트워크 트래픽이 유출될 수 있습니다." msgid "You are running an unsupported app version." msgstr "지원되지 않는 앱 버전을 실행 중입니다." +msgid "\"%s\" was deleted" +msgstr "\"%s\"이(가) 삭제되었습니다" + msgid "less than a minute ago" msgstr "1분 이내" @@ -2287,6 +2492,10 @@ msgid "%d day" msgid_plural "%d days" msgstr[0] "%d일" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "위치 %d개" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d개월" diff --git a/gui/locales/ko/relay-locations.po b/gui/locales/ko/relay-locations.po index eca77cdbb476..71f07d1f40a6 100644 --- a/gui/locales/ko/relay-locations.po +++ b/gui/locales/ko/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Korean\n" "Language: ko_KR\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "오스트레일리아" msgid "Austria" msgstr "오스트리아" +#. TH BKK +msgid "Bangkok" +msgstr "방콕" + +#. ES BCN +msgid "Barcelona" +msgstr "바르셀로나" + #. BE msgid "Belgium" msgstr "벨기에" @@ -188,6 +196,10 @@ msgstr "프랑크푸르트" msgid "Germany" msgstr "독일" +#. GB GLW +msgid "Glasgow" +msgstr "글래스고" + #. SE GOT msgid "Gothenburg" msgstr "예테보리" @@ -264,6 +276,10 @@ msgstr "라트비아" msgid "Lisbon" msgstr "리스본" +#. SI LJU +msgid "Ljubljana" +msgstr "류블랴나" + #. GB LON msgid "London" msgstr "런던" @@ -296,6 +312,10 @@ msgstr "맨체스터" msgid "Marseille" msgstr "마르세이유" +#. US TXC +msgid "McAllen, TX" +msgstr "맥앨런, 텍사스" + #. AU MEL msgid "Melbourne" msgstr "멜버른" @@ -472,6 +492,10 @@ msgstr "스코페" msgid "Slovakia" msgstr "슬로바키아" +#. SI +msgid "Slovenia" +msgstr "슬로베니아" + #. BG SOF msgid "Sofia" msgstr "소피아" @@ -520,6 +544,10 @@ msgstr "탈린" msgid "Tel Aviv" msgstr "텔아비브" +#. TH +msgid "Thailand" +msgstr "태국" + #. AL TIA msgid "Tirana" msgstr "티라나" @@ -548,6 +576,10 @@ msgstr "우크라이나" msgid "United Arab Emirates" msgstr "아랍에미리트" +#. ES VLC +msgid "Valencia" +msgstr "발렌시아" + #. CA VAN msgid "Vancouver" msgstr "밴쿠버" diff --git a/gui/locales/my/messages.po b/gui/locales/my/messages.po index f49c502658d1..8a96173c2148 100644 --- a/gui/locales/my/messages.po +++ b/gui/locales/my/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Burmese\n" "Language: my_MM\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s ထပ်ပေါင်းပြီးပါပြီ၊ %(expiry)s မတိုင်ခင်ထိ အကောင့် ငွေပေးချေပြီးပါပြီ။" @@ -133,6 +133,12 @@ msgstr "ချိတ်ဆက်မှုဖြုတ်နေပါသည်" msgid "Dismiss" msgstr "ဖယ်ပစ်ရန်" +msgid "Edit" +msgstr "တည်းဖြတ်ရန်" + +msgid "Enable" +msgstr "ဖွင့်ရန်" + msgid "Enable anyway" msgstr "မည်သို့ပင်ဖြစ်စေ ဖွင့်ရန်" @@ -225,6 +231,11 @@ msgstr "စနစ် ပုံသေ" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "စမ်းသပ်ရန်" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "ဤဆက်တင်သည် ကြန့်ကြာချိန်ကို တိုးစေပါသည်။ လိုအပ်မှသာ သုံးပါ။" @@ -247,6 +258,9 @@ msgstr "ပိတ်ဆို့မှုမှ ဖယ်ရန်" msgid "UNSECURED CONNECTION" msgstr "မလုံခြုံသည့် ချိတ်ဆက်မှု" +msgid "Use" +msgstr "သုံးရန်" + msgid "Username" msgstr "သုံးစွဲသူအမည်" @@ -382,9 +396,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "စစ်မှန်ကြောင်း အတည်ပြုခြင်း" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "“သိမ်းရန်” ကို နှိပ်ခြင်းသည် သုံးနေသော နည်းလမ်းကို ပြောင်းစေပါသည်။" +msgid "Clicking “%(save)s” changes the in use method." +msgstr "\"%(save)s\" ကို နှိပ်ခြင်းသည် သုံးနေသော နည်းလမ်းကို ပြောင်းစေပါသည်။" msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -627,6 +642,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(entry)s မှတစ်ဆင့် %(relay)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "စိတ်ကြိုက် ပေါင်းကူးမှတစ်ဆင့် %(relay)s" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -643,6 +662,22 @@ msgctxt "connection-info" msgid "Out" msgstr "အထွက်" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "စိတ်ကြိုက် ပေါင်းကူးကို ပေါင်းထည့်ရန်" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "စိတ်ကြိုက် ပေါင်းကူးကို ဖျက်မည်လား။" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "စိတ်ကြိုက် ပေါင်းကူးကို ဖျက်ခြင်းသည် တည်နေရာ ရွေးရန် ပြသမှုကို ပြန်လည် ရောက်ရှိသွားမည်ဖြစ်ကာ အလိုအလျောက် ရွေးချယ်မှုကို အစားထိုး ရွေးချယ်ပါမည်။" + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "စိတ်ကြိုက် ပေါင်းကူးကို တည်းဖြတ်ရန်" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1151,6 +1186,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "ပေါင်းကူး မုဒ်" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "ပေါင်းကူး မုဒ်ကို ဖွင့်မည်လား။" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1197,6 +1236,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "UDP ကို သက်ဝင်လုပ်ဆောင်ရန် ပေါင်းကူး မုဒ် ကို အော်တိုမက်တစ် သို့မဟုတ် ပိတ် သို့ ပြောင်းပါ။" +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "သီးသန့် ပေါင်းကူး ဆာဗာကို ရွေးရန်အတွက် တည်နေရာ ရွေးရန် ပြသမှုသို့ သွားပါ။" + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "ပို့ဆောင်မှု ပရိုတိုကော" @@ -1260,6 +1305,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "ပုံမှန် Mullvad ပေါင်းကူး ဆာဗာသည် အလုပ် မဖြစ်သည့်အခါ စိစစ်ဖြတ်တောက်မှုကို ရှောင်လွှဲရန် စိတ်ကြိုက် ပေါင်းကူး ဆာဗာကို သုံးပါသည်။" + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1279,6 +1328,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "နိုင်ငံ" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "စိတ်ကြိုက် ပေါင်းကူး" + msgctxt "select-location-view" msgid "Custom lists" msgstr "စိတ်ကြိုက်စာရင်းများ" @@ -2008,9 +2061,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "VPN ဆာဗာကို ဖွင့်ရန် ၎င်း TCP ပေါ့တ် UDP-over-TCP Obfuscation ပရိုတိုကောလ်နှင့် ချိတ်ဆက်ထားသင့်ပါသည်။" -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (ပေါင်းထည့်ပြီး)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "\"%s\" သို့ %s ကို ပေါင်းထည့်ပြီးပါပြီ" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "သင့် အကောင့်ထဲသို့ %s ကို ပေါင်းထည့်ထားပါသည်။" +msgstr "သင့်အကောင့်သို့ %s ကို ပေါင်းထည့်ပြီးပါပြီ။" msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. အောက်ရှိ VPN ဆက်တင်များသို့ သွားရန် ခလုတ်ကို နှိပ်ပြီးနောက် Mullvad VPN အမည်ဘေးရှိ ခွေးသွားစိတ်ပုံကို နှိပ်ပါ။" @@ -2033,6 +2094,10 @@ msgstr "မကြာမီ အကောင့် ခရက်ဒစ် သက် msgid "Account time reminders" msgstr "အကောင့်အချိန် သတိပေးချက်များ" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "စာရင်းထဲသို့ %s ပေါင်းထည့်ရန်" + msgid "Add 30 days time" msgstr "အချိန် ရက် 30 ကို‌ ပေါင်းထည့်ရန်" @@ -2042,12 +2107,18 @@ msgstr "အချိန် ရက် 30 ကို‌ ပေါင်းထည့ msgid "Add DNS server" msgstr "DNS ဆာဗာကို ပေါင်းထည့်ရန်" +msgid "Add locations" +msgstr "တည်နေရာများ ပေါင်းထည့်ရန်" + msgid "Agree and continue" msgstr "သဘောတူပြီး ဆက်လုပ်ရန်" msgid "All applications" msgstr "အပလီကေးရှင်း အားလုံး" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "‌ကျော်လွန် ပယ်ဖျက်မှုအားလုံးကို ပြန်လည်သတ်မှတ်မည်ဖြစ်ပြီး တည်နေရာ ရွေးချယ်ရန် ပြသမှုတွင် ဆာဗာများ၏ IP လိပ်စာများကို ပုံသေအဖြစ် ပြန်ပြောင်းလိုက်ပါမည်။" + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "ဝေမျှရန်၊ ပရင့်ထုတ်ရန်စသည်တို့အတွက် တူညီသည့် ကွန်ရက်ရှိ အခြားစက်များကို ရယူသုံးစွဲခွင့်ပြုပေးပါသည်။" @@ -2057,6 +2128,9 @@ msgstr "အမြဲဖွင့် VPN ကို အခြားအက်ပ် msgid "Always-on VPN might be enabled for another app" msgstr "အမြဲဖွင့် VPN ကို နောက်ထပ်အက်ပ်အတွက် ဖွင့်ထားနိုင်ပါသည်" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "သတိပြုရန်- Split tunneling သည် ကိုယ်ရေးအချက်အလက်လုံခြုံရေး အန္တရာယ်ရှိနေပါသည်။" + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "သတိပြုရန်- ဤဆက်တင်ကို စိတ်ကြိုက် DNS ဆာဗာကို သုံးရန်နှင့်အတူ ပေါင်းစပ်၍ မသုံးစွဲနိုင်ပါ။" @@ -2066,6 +2140,9 @@ msgstr "အော်တို ချိတ်ဆက်မှု နှင့် msgid "Auto-connect & \\nLockdown mode" msgstr "အော်တို ချိတ်ဆက်မှု နှင့် \\nလော့ခ်ဒေါင်းစနစ်" +msgid "Auto-connect (legacy)" +msgstr "အော်တို ချိတ်ဆက်ရန် (ပင်မ)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Android စနစ် ဆက်တင်တွင် အော်တိုချိတ်ဆက်မှုကို အမြဲဖွင့် VPN ဟုခေါ်ပြီး VPN Tunnel ကို အမြဲချိတ်ဆက်ထားကြောင်း သေချာစေပြီး အစမှ ပြန်ဖွင့်ပြီးနောက် အော်တို ချိတ်ဆက်ပါသည်။" @@ -2087,6 +2164,12 @@ msgstr "ကလစ်ဘုတ်တွင် မှတ်တမ်းမျာ msgid "Copied to clipboard" msgstr "ကလစ်ဘုတ်တွင် ကူးယူပြီး" +msgid "Create" +msgstr "ဖန်တီးမည်" + +msgid "Create new list" +msgstr "စာရင်းသစ် ဖန်တီးမည်" + msgid "Critical error (your attention is required)" msgstr "အလွန်အရေးပါသည့် ချို့ယွင်းချက် (သင့်အာရုံစိုက်မှု လိုအပ်ပါသည်)" @@ -2096,11 +2179,32 @@ msgstr "စိတ်ကြိုက် DNS ဆာဗာလိပ်စာမျ msgid "DNS settings might not go into effect immediately" msgstr "DNS ဆက်တင်ကို ချက်ချင်း အကျိုးမရောက်နိုင်ပါ။" +msgid "Delete \"%s\"?" +msgstr "\"%s\" ကို ဖျက်မည်လား။" + msgid "Disable all %s above to activate this setting." msgstr "ဤဆက်တင်ကို သက်ဝင်လုပ်ဆောင်ရန် အထက်ရှိ %s အားလုံးကို ပိတ်ပါ။" -msgid "Enable" -msgstr "ဖွင့်ရန်" +msgid "Discard" +msgstr "ပယ်ဖျက်မည်" + +msgid "Discard changes?" +msgstr "အပြောင်းအလဲများကို ပယ်ဖျက်မည်လား။" + +msgid "Edit custom lists" +msgstr "စိတ်ကြိုက် စာရင်းများ ဖန်တီးရန်" + +msgid "Edit list" +msgstr "စာရင်းကို တည်းဖြတ်ရန်" + +msgid "Edit lists" +msgstr "စာရင်းများကို တည်းဖြတ်ရန်" + +msgid "Edit locations" +msgstr "တည်နေရာများကို တည်းဖြတ်ရန်" + +msgid "Edit name" +msgstr "အမည် တည်းဖြတ်ရန်" msgid "Enter MTU" msgstr "MTU ကို ရိုက်ထည့်ရန်" @@ -2108,6 +2212,12 @@ msgstr "MTU ကို ရိုက်ထည့်ရန်" msgid "Excluded applications" msgstr "အပလီကေးရှင်းများ ဖယ်ထားပြီး" +msgid "Failed to apply patch" +msgstr "ပတ်(ချ်) သုံးခြင်း မအောင်မြင်ပါ" + +msgid "File" +msgstr "ဖိုင်" + msgid "Go to VPN settings" msgstr "VPN ဆက်တင်များသို့ သွားရန်" @@ -2123,9 +2233,34 @@ msgstr "Google Play ကို မရရှိနိုင်ပါ" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Split Tunneling လုပ်ဆောင်ချက်ကို အသုံးပြုထားပါက အက်ပ်သည် ထည့်သွင်းထားသော အက်ပလီကေးရှင်းများအားလုံး၏ စာရင်းကို မေးမြန်းပါသည်။ ဤစာရင်းကို Split Tunneling ပြသမှုတွင်သာ ပြန်ရယူပါသည်။ ထည့်သွင်းထားသော အက်ပလီကေးရှင်းများ၏ စာရင်းကို စက်မှ မည်သည့်အခါမျှ မပေးပို့ပါ။" +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "ဖော်ပြပါဖြင့် ကျော်လွန် ပယ်ဖျက်မှု အသစ်များကို ထည့်သွင်းရန်" + +msgid "Import successful, overrides active" +msgstr "ထည့်သွင်းမှု အောင်မြင်ပါသည်၊ ကျော်လွန် ပယ်ဖျက်မှု သက်ဝင်ပါသည်" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "ကျော်လွန် ပယ်ဖျက်မှု အသစ်များကို ထည့်သွင်းခြင်းသည် ယခင်ထည့်သွင်းထားသော ကျော်လွန် ပယ်ဖျက်မှု အချို့ကို အစားထိုးနိုင်ပါသည်။" + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "အပ်ဒိတ် ဖြစ်နေစေရန် Mullvad VPN (%s) ကို ထည့်သွင်းပါ" +msgid "Invalid or missing value \"%s\"" +msgstr "\"%s\" တန်ဖိုး မမှန်ကန်ပါ သို့မဟုတ် မရှိပါ" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "VPN Tunnel ကို မဝင်ရောက်ဘဲ အင်တာနက်ကို တိုက်ရိုက် ဝင်ရောက်သုံးစွဲသင့်သည့် အက်ပ်များကို ရွေးချယ်ပေးပါ။" + +msgid "List name" +msgstr "စာရင်း အမည်" + +msgid "Locations" +msgstr "တည်နေရာများ" + +msgid "Locations were changed for \"%s\"" +msgstr "\"%s\" အတွက် တည်နေရာများကို ပြောင်းလိုက်ပါသည်" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "စက်သည် VPN Tunnel ကို အမြဲဖွင့်ထားကြောင်း သေချာပါစေ။" @@ -2138,9 +2273,43 @@ msgstr "Mullvad အကောင့်နံပါတ်" msgid "Mullvad services unavailable" msgstr "Mullvad ဝန်ဆောင်မှုများကို မရရှိနိုင်ပါ" +msgid "Name was changed to %s" +msgstr "အမည်ကို %s သို့ ပြောင်းလိုက်ပါသည်" + +msgid "New list" +msgstr "စာရင်းသစ်" + +msgid "No custom lists available" +msgstr "စိတ်ကြိုက် စာရင်းများကို မရရှိနိုင်ပါ" + msgid "No internet connection" msgstr "အင်တာနက် ချိတ်ဆက်မှု မရှိပါ" +msgid "No locations found" +msgstr "တည်နေရာများကို ရှာမတွေ့ပါ" + +msgid "Not found" +msgstr "ရှာမတွေ့ပါ" + +msgid "Overrides active" +msgstr "ကျော်လွန် ပယ်ဖျက်မှုများ သက်ဝင်နေသည်" + +msgid "Overrides cleared" +msgstr "ကျော်လွန် ပယ်ဖျက်မှုများ ရှင်းပြီးပါပြီ" + +msgid "Overrides inactive" +msgstr "ကျော်လွန် ပယ်ဖျက်မှုများ သက်ဝင်မှု မရှိပါ" + +msgid "Paste or write overrides to be imported" +msgstr "ကျော်လွန် ပယ်ဖျက်မှုများကို ထည့်သွင်းရန် ကူးထည့်ပါ သို့မဟုတ် ရေးပါ" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Patch သည် သတ်မှတ်ချက်နှင့် မကိုက်ညီပါ" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "အထက်ပါ %s တွင် အောက်ပါ လမ်းညွှန်အတိုင်း လိုက်နာခြင်းဖြင့် အမြဲဖွင့် စနစ်ဆက်တင်ကို ‌အစားထိုး သုံးပါ။" + msgid "Preferences" msgstr "လိုလားမှုများ" @@ -2150,18 +2319,33 @@ msgstr "ကိုယ်ရေးအချက်အလက် လုံခြု msgid "Privacy policy" msgstr "ကိုယ်ပိုင်အချက်အလက် မူဝါဒ" +msgid "Recursion limit" +msgstr "ထပ်တလဲလဲ လုပ်ဆောင်မှု ကန့်သတ်ချက်" + msgid "Remove" msgstr "ဖယ်ရှားရန်" msgid "Remove custom port" msgstr "စိတ်ကြိုက် ပေါ့တ်ကို ဖယ်ရှားရန်" +msgid "Reset" +msgstr "ပြန်လည်သတ်မှတ်ရန်" + +msgid "Reset all overrides" +msgstr "ကျော်လွန် ပယ်ဖျက်မှု အားလုံးကို ပြန်လည်သတ်မှတ်ရန်" + +msgid "Reset overrides" +msgstr "ကျော်လွန် ပယ်ဖျက်မှုများကို ပြန်လည်သတ်မှတ်ရန်" + msgid "Reset to default" msgstr "ပုံသေသို့ ပြန်လည်သတ်မှတ်ရန်" msgid "Secured" msgstr "လုံခြုံပါသည်" +msgid "Server Ip overrides" +msgstr "ဆာဗာ IP ကျော်လွန် ပယ်ဖျက်မှုများ" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "WireGuard MTU တန်ဖိုးကို သတ်မှတ်ပါ။ အကျုံးဝင်သည့် အပိုင်းအခြား- %d - %d ။" @@ -2177,12 +2361,12 @@ msgstr "လက်ရှိ VPN Tunnel အခြေအနေကို ပြသ msgid "Shows reminders when the account time is about to expire" msgstr "အကောင့်အချိန် သက်တမ်းကုန်ခါနီးချိန်၌ သတိပေးချက်များ ပြသပေးပါသည်" -msgid "Split tunneling is disabled." -msgstr "Split Tunneling ကို ပိတ်ထားပါသည်။" - msgid "Submit" msgstr "ပေးပို့ရန်" +msgid "Text" +msgstr "စာသား" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "အော်တို ချိတ်ဆက်မှုနှင့် လော့ခ်ဒေါင်းစနစ် ဆက်တင်များကို Android စနစ် ဆက်တင်များတွင် ရှာတွေ့နိုင်ပြီး တစ်ခု သို့မဟုတ် နှစ်ခုလုံးကို ဖွင့်ရန် ဤလမ်းညွှန်ချက်ကို လိုက်နာပါ။" @@ -2198,6 +2382,12 @@ msgstr "သင့်စက်တွင် VPN ဆက်တင် မရှိပ msgid "This address has already been entered." msgstr "ဤလိပ်စာကို ရိုက်ထည့်ထားပြီး ဖြစ်ပါသည်။" +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "စာရင်းထဲသို့ တည်နေရာများကို ပေါင်းထည့်ရန် \"︙\" ကို နှိပ်ပါ သို့မဟုတ် နိုင်ငံ၊ မြို့၊ ဆာဗာကို နှိပ်ပါ။" + +msgid "To create a custom list press the \"︙\"" +msgstr "စိတ်ကြိုက် စာရင်းများကို ဖန်တီးရန် \"︙\" ကို နှိပ်ပါ" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "အလုံခြုံဆုံး ဗားရှင်းကို ရရှိကြောင်း သေချာစေရန်နှင့် လုပ်ဆောင်နေသည့် လက်ရှိဗားရှင်းနှင့်ပတ်သက်သော ပြဿနာများအကြောင်း သင့်အား အသိပေးရန် အက်ပ်သည် ဗားရှင်း စစ်ဆေးမှုများကို အော်တိုလုပ်ဆောင်ပါသည်။ ၎င်းသည် Mullvad ဆာဗာများသို့ အက်ပ် ဗားရှင်းနှင့် Android စနစ်ဗားရှင်းတို့ကို ပေးပို့ပါသည်။ အသုံးပြုထားသည့် အက်ပ် ဗားရှင်းများနှင့် Android ဗားရှင်းများ၏ အရေအတွက်ကို Mullvad က ရေတွက်ထားပါသည်။ ဒေတာကို မည်သည့်အခါမျှ မသိမ်းဆည်းထားပါ သို့မဟုတ် သင့်အား ခွဲခြားဖော်ထုတ်နိုင်သော မည်သည့်နည်းလမ်းတွင်မျှ အသုံးမပြုထားပါ။" @@ -2207,9 +2397,19 @@ msgstr "VPN ရွေးသုံးရန်" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Firewall စည်းမျဉ်းများကို အသုံးချ၍ မရနိုင်ပါ။ ပြစ်ချက် ရှာဖွေဖယ်ရှာပေးပါ သို့မဟုတ် ပြဿနာ ရီပို့တ် ပေးပို့ပေးပါ။" +msgid "Unable to parse patch" +msgstr "Patch ကို ခွဲခြမ်းစိတ်ဖြာ၍ မရပါ" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Tunnel ချိတ်ဆက်မှုကို စတင်၍ မရနိုင်ပါ။ Mullvad VPN ကို မသုံးမီ %s အတွက် VPN အမြဲဖွင့်ထားမှုကို ပိတ်ပေးပါ။" +msgid "Undo" +msgstr "မလုပ်တော့ပါ" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "မသိသော သို့မဟုတ် ပိတ်ပင်ထားသော ကီး \"%s\"" + msgid "Unsecured" msgstr "မလုံခြုံပါ" @@ -2219,6 +2419,9 @@ msgstr "DNS ဆာဗာကို အပ်ဒိတ်လုပ်ရန်" msgid "Update available, download to remain safe." msgstr "အပ်ဒိတ် ရရှိနိုင်ပါပြီ၊ ဆက်လက် လုံခြုံစေရန် ဒေါင်းလုဒ်လုပ်ပါ။" +msgid "Update list name" +msgstr "စာရင်း အမည်ကို အပ်ဒိတ်လုပ်ရန်" + msgid "VPN permission error" msgstr "VPN ခွင့်ပြုချက် ချို့ယွင်းချက်" @@ -2231,7 +2434,6 @@ msgstr "VPN Tunnel အခြေအနေ" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "အကျုံးဝင်သည့် WireGuard ကီး မရှိပါ။ အဆင့်မြင့်ဆက်တင် အောက်တွင် ကီးများကို စီမံခန့်ခွဲပါ။" -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "အကျုံးဝင်သည့် အပိုင်းအခြား- %s" @@ -2277,6 +2479,9 @@ msgstr "ကွန်ရက် ကူးလူးမှု ပေါက်ကြ msgid "You are running an unsupported app version." msgstr "တွဲဖက်မလုပ်ဆောင်နိုင်သည့် အက်ပ်ဗားရှင်းဖြင့် လုပ်ဆောင်နေပါသည်။" +msgid "\"%s\" was deleted" +msgstr "\"%s\" ကို ဖျက်ပြီးပါပြီ" + msgid "less than a minute ago" msgstr "လွန်ခဲ့သော စက္ကန့်ပိုင်း" @@ -2287,6 +2492,10 @@ msgid "%d day" msgid_plural "%d days" msgstr[0] "%d ရက်" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d တည်နေရာများ" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d လ" diff --git a/gui/locales/my/relay-locations.po b/gui/locales/my/relay-locations.po index 5192d15271b3..e7777725b325 100644 --- a/gui/locales/my/relay-locations.po +++ b/gui/locales/my/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Burmese\n" "Language: my_MM\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "သြစတြေးလျ" msgid "Austria" msgstr "သြစတြီးယား" +#. TH BKK +msgid "Bangkok" +msgstr "ဘန်ကောက်" + +#. ES BCN +msgid "Barcelona" +msgstr "ဘာစီလိုနာ" + #. BE msgid "Belgium" msgstr "ဘယ်လ်ဂျီယံ" @@ -188,6 +196,10 @@ msgstr "ဖရန့်ဖတ်" msgid "Germany" msgstr "ဂျာမနီ" +#. GB GLW +msgid "Glasgow" +msgstr "ဂလပ်စဂို" + #. SE GOT msgid "Gothenburg" msgstr "ဂိုသန်ဘက်" @@ -264,6 +276,10 @@ msgstr "လတ်ဗီးယား" msgid "Lisbon" msgstr "လစ္စဘွန်း" +#. SI LJU +msgid "Ljubljana" +msgstr "လစ်ဘီယာနာ" + #. GB LON msgid "London" msgstr "လန်ဒန်" @@ -296,6 +312,10 @@ msgstr "မန်ချက်စတာ" msgid "Marseille" msgstr "မာဆေး" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "မဲလ်ဘုန်း" @@ -472,6 +492,10 @@ msgstr "စကော့ပ်ရာ" msgid "Slovakia" msgstr "Slovakia" +#. SI +msgid "Slovenia" +msgstr "ဆလိုဗေးနီးယား" + #. BG SOF msgid "Sofia" msgstr "ဆိုဖီယာ" @@ -520,6 +544,10 @@ msgstr "တောလ်လင်း" msgid "Tel Aviv" msgstr "တဲလ်အဗစ်" +#. TH +msgid "Thailand" +msgstr "ထိုင်း" + #. AL TIA msgid "Tirana" msgstr "တေရားနား" @@ -548,6 +576,10 @@ msgstr "ယူကရိန်း" msgid "United Arab Emirates" msgstr "အာရတ်စော်ဘွားများ ပြည်ထောင်စု" +#. ES VLC +msgid "Valencia" +msgstr "ဗလန်စီယာ" + #. CA VAN msgid "Vancouver" msgstr "ဗန်ကူးဗား" diff --git a/gui/locales/nb/messages.po b/gui/locales/nb/messages.po index d419cc49119b..8fe124545ee8 100644 --- a/gui/locales/nb/messages.po +++ b/gui/locales/nb/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Norwegian Bokmal\n" "Language: nb_NO\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s ble lagt til, konto betalt til %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Kobler fra" msgid "Dismiss" msgstr "Ignorer" +msgid "Edit" +msgstr "Endre" + +msgid "Enable" +msgstr "Aktiver" + msgid "Enable anyway" msgstr "Aktiver uansett" @@ -231,6 +237,11 @@ msgstr "Systemstandard" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Test" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Denne innstillingen øker ventetiden. Bruk kun ved behov." @@ -253,6 +264,9 @@ msgstr "Fjern blokkering" msgid "UNSECURED CONNECTION" msgstr "USIKKER TILKOBLING" +msgid "Use" +msgstr "Bruk" + msgid "Username" msgstr "Brukernavn" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Autentisering" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Når du trykker på «Lagre», vil det endre hvilken metode som brukes." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Når du trykker på «%(save)s», vil det endre hvilken metode som brukes." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s via %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s via egendefinert bro" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Utgående" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Legg til egendefinert bro" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Slette egendefinert bro?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Hvis du sletter den egendefinerte broen, blir du sendt tilbake til der du velger plassering, og alternativet Automatisk velges i stedet." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Endre egendefinert bro" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Bromodus" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Aktivere bromodus?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "For å aktivere UDP, endre Bromodus til Automatisk eller Av." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Hvis du vil velge en bestemt broserver, går du til visningen «Velg plassering»." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Transportprotokoll" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "En egendefinert bro-server kan brukes til å omgå sensur når vanlige Mullvad-broservere ikke fungerer." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Land" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Egendefinert bro" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Tilpassede lister" @@ -2014,7 +2067,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "TCP-porten som UDP-over-TCP-tilsløringen skal koble til på VPN-serveren." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (lagt til)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s ble lagt til «%s»" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s ble lagt til kontoen din." @@ -2039,6 +2100,10 @@ msgstr "Kontokreditt utløper snart" msgid "Account time reminders" msgstr "Påminnelser om tidsavbrudd for konto" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Legg til %s i listen" + msgid "Add 30 days time" msgstr "Legg til 30 dager" @@ -2048,12 +2113,18 @@ msgstr "Legg til 30 dager (%s)" msgid "Add DNS server" msgstr "Legg til DNS-server" +msgid "Add locations" +msgstr "Legg til plasseringer" + msgid "Agree and continue" msgstr "Godta og fortsett" msgid "All applications" msgstr "Alle applikasjoner" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Alle overstyringer tilbakestilles, og servernes IP-adresser i visningen «Velg plassering» går tilbake til standardoppsett." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Gir tilgang til andre enheter på samme nettverk for deling, utskrift osv." @@ -2063,6 +2134,9 @@ msgstr "VPN som alltid er på, er tilordnet en annen app" msgid "Always-on VPN might be enabled for another app" msgstr "VPN som alltid er på, kan være aktivert for en annen app" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "OBS: Delt tunnelering utgjør en risiko mot personvernet." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "OBS: Denne innstillingen kan ikke brukes sammen med Bruk egendefinert DNS-server." @@ -2072,6 +2146,9 @@ msgstr "Automatisk tilkobling og låsemodus" msgid "Auto-connect & \\nLockdown mode" msgstr "Automatisk tilkobling og \\nlåsemodus" +msgid "Auto-connect (legacy)" +msgstr "Automatisk tilkobling (eldre)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Automatisk tilkobling heter «Alltid-på-VPN» i Androids systeminnstillinger og sørger for at du alltid er tilkoblet VPN-tunnelen og automatisk tilkobling etter omstart." @@ -2093,6 +2170,12 @@ msgstr "Logger kopiert til utklippstavlen" msgid "Copied to clipboard" msgstr "Kopiert til utklippstavlen" +msgid "Create" +msgstr "Opprett" + +msgid "Create new list" +msgstr "Opprett ny liste" + msgid "Critical error (your attention is required)" msgstr "Kritisk feil (krever din oppmerksomhet)" @@ -2102,11 +2185,32 @@ msgstr "Egendefinerte DNS-serveradresser %s er ugyldige" msgid "DNS settings might not go into effect immediately" msgstr "DNS-innstillinger vil kanskje ikke tre i kraft umiddelbart" +msgid "Delete \"%s\"?" +msgstr "Slette «%s»?" + msgid "Disable all %s above to activate this setting." msgstr "Deaktiver alle %s ovenfor for å aktivere denne innstillingen." -msgid "Enable" -msgstr "Aktiver" +msgid "Discard" +msgstr "Forkast" + +msgid "Discard changes?" +msgstr "Forkaste endringer?" + +msgid "Edit custom lists" +msgstr "Endre egendefinerte lister" + +msgid "Edit list" +msgstr "Endre liste" + +msgid "Edit lists" +msgstr "Endre lister" + +msgid "Edit locations" +msgstr "Endre plasseringer" + +msgid "Edit name" +msgstr "Endre navn" msgid "Enter MTU" msgstr "Angi MTU" @@ -2114,6 +2218,12 @@ msgstr "Angi MTU" msgid "Excluded applications" msgstr "Ekskluder applikasjoner" +msgid "Failed to apply patch" +msgstr "Kunne ikke bruke oppdatering" + +msgid "File" +msgstr "Fil" + msgid "Go to VPN settings" msgstr "Gå til VPN-innstillinger" @@ -2129,9 +2239,34 @@ msgstr "Google Play utilgjengelig" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Hvis funksjonen delt tunnelering brukes, vil appen be systemet ditt om en liste over alle installerte applikasjoner. Denne listen hentes bare i delt tunnelering-visningen. Listen over installerte apper sendes aldri ut fra enheten." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importer nye overstyringer via" + +msgid "Import successful, overrides active" +msgstr "Importering vellykket. Overstyringer aktive" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Importering av nye overstyringer kan erstatte tidligere importerte overstyringer." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Installer Mullvad VPN (%s) for å holde deg oppdatert" +msgid "Invalid or missing value \"%s\"" +msgstr "Ugyldig eller mangler verdien «%s»" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Lar deg velge apper som skal få tilgang til internett direkte uten å gå gjennom VPN-tunnelen." + +msgid "List name" +msgstr "Listenavn" + +msgid "Locations" +msgstr "Plasseringer" + +msgid "Locations were changed for \"%s\"" +msgstr "Plasseringer ble endret for «%s»" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Sørger for at enheten alltid er på VPN-tunnelen." @@ -2144,9 +2279,43 @@ msgstr "Mullvad-kontonummer" msgid "Mullvad services unavailable" msgstr "Mullvad-tjenester utilgjengelig" +msgid "Name was changed to %s" +msgstr "Navn ble endret til %s" + +msgid "New list" +msgstr "Ny liste" + +msgid "No custom lists available" +msgstr "Ingen tilgjengelige egendefinerte lister" + msgid "No internet connection" msgstr "Ingen internettforbindelse" +msgid "No locations found" +msgstr "Ingen plasseringer funnet" + +msgid "Not found" +msgstr "Ikke funnet" + +msgid "Overrides active" +msgstr "Overstyringer aktive" + +msgid "Overrides cleared" +msgstr "Overstyringer fjernet" + +msgid "Overrides inactive" +msgstr "Overstyringer inaktive" + +msgid "Paste or write overrides to be imported" +msgstr "Lim eller skriv inn overstyringer som skal importeres" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Oppdateringen samsvarer ikke med spesifikasjon" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Bruk systeminnstillingen alltid på i stedet for å følge veiledningen i %s ovenfor." + msgid "Preferences" msgstr "Preferanser" @@ -2156,18 +2325,33 @@ msgstr "Personvern" msgid "Privacy policy" msgstr "Retningslinjer for personvern" +msgid "Recursion limit" +msgstr "Rekursjonsgrense" + msgid "Remove" msgstr "Fjern" msgid "Remove custom port" msgstr "Fjern tilpasset port" +msgid "Reset" +msgstr "Tilbakestill" + +msgid "Reset all overrides" +msgstr "Tilbakestill alle overstyringer" + +msgid "Reset overrides" +msgstr "Tilbakestill overstyringer" + msgid "Reset to default" msgstr "Tilbakestill til standard" msgid "Secured" msgstr "Sikret" +msgid "Server Ip overrides" +msgstr "Overstyringer av server-IP" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Angi WireGuard MTU-verdi. Verdiområde: %d–%d." @@ -2183,12 +2367,12 @@ msgstr "Viser gjeldende VPN-tunnelstatus" msgid "Shows reminders when the account time is about to expire" msgstr "Viser påminnelser når tidsavbrudd for kontoen er i ferd med å inntreffe" -msgid "Split tunneling is disabled." -msgstr "Delt tunnelering er deaktivert." - msgid "Submit" msgstr "Send inn" +msgid "Text" +msgstr "Tekst" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Du finner innstillingene for automatisk tilkobling og låsemodus i Androids systeminnstillinger. Følg denne veiledningen for å aktivere én eller begge." @@ -2204,6 +2388,12 @@ msgstr "Det er ingen VPN-innstillinger på enheten din" msgid "This address has already been entered." msgstr "Denne adressen er allerede skrevet inn." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Hvis du vil legge til plasseringer i en liste, trykker du på ︙ eller trykker på og holder inne et land, en by eller en server." + +msgid "To create a custom list press the \"︙\"" +msgstr "For å opprette en egendefinert liste trykker du på ︙" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "For å sørge for at du har den sikreste appversjonen, og informere deg om eventuelle problemer med den versjonen som kjøres, vil appen automatisk sjekke appversjonen. Appversjonen og Android-systemversjonen blir da sendt til Mullvad-servere. Mullvad registrerer antallet brukte appversjoner og Android-versjoner. Dataen blir aldri lagret eller brukt på noen som helst måte som kan identifisere deg." @@ -2213,9 +2403,19 @@ msgstr "Velg VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Kunne ikke bruke brannmur-regler. Feilsøk eller send inn en problemrapport." +msgid "Unable to parse patch" +msgstr "Kunne ikke analysere oppdatering" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Kunne ikke starte tunneltilkobling. Deaktiver VPN som alltid er på, for %s før du bruker Mullvad VPN." +msgid "Undo" +msgstr "Angre" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Ukjent eller forbudt nøkkel «%s»" + msgid "Unsecured" msgstr "Usikret" @@ -2225,6 +2425,9 @@ msgstr "Oppdater DNS-serveren" msgid "Update available, download to remain safe." msgstr "Oppdatering tilgjengelig. Last ned for å oppdatere sikkerheten." +msgid "Update list name" +msgstr "Oppdater listenavn" + msgid "VPN permission error" msgstr "Feil med VPN-tillatelse" @@ -2237,7 +2440,6 @@ msgstr "VPN-tunnelstatus" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Det mangler en gyldig WireGuard-nøkkel. Du kan behandle nøklene under avanserte innstillinger." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Gyldige verdiområder: %s" @@ -2283,6 +2485,9 @@ msgstr "DET KAN VÆRE EN NETTVERKSLEKKASJE HOS DEG" msgid "You are running an unsupported app version." msgstr "Du kjører en appversjon som ikke støttes." +msgid "\"%s\" was deleted" +msgstr "«%s» ble slettet" + msgid "less than a minute ago" msgstr "mindre enn ett minutt siden" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d dag" msgstr[1] "%d dager" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d plassering" +msgstr[1] "%d plasseringer" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d måned" diff --git a/gui/locales/nb/relay-locations.po b/gui/locales/nb/relay-locations.po index 80ffe3690fbc..ddd118355994 100644 --- a/gui/locales/nb/relay-locations.po +++ b/gui/locales/nb/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Norwegian Bokmal\n" "Language: nb_NO\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australia" msgid "Austria" msgstr "Østerrike" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Belgia" @@ -188,6 +196,10 @@ msgstr "Frankfurt" msgid "Germany" msgstr "Tyskland" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Latvia" msgid "Lisbon" msgstr "Lisboa" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "London" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marseille" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slovakia" +#. SI +msgid "Slovenia" +msgstr "Slovenia" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thailand" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ukraina" msgid "United Arab Emirates" msgstr "De forente arabiske emirater" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/nl/messages.po b/gui/locales/nl/messages.po index f1ae577a1825..62e4488c912e 100644 --- a/gui/locales/nl/messages.po +++ b/gui/locales/nl/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Dutch\n" "Language: nl_NL\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s is toegevoegd, account betaald tot %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Verbinding wordt verbroken" msgid "Dismiss" msgstr "Negeren" +msgid "Edit" +msgstr "Bewerken" + +msgid "Enable" +msgstr "Inschakelen" + msgid "Enable anyway" msgstr "Toch inschakelen" @@ -231,6 +237,11 @@ msgstr "Systeemstandaard" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Test" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Deze instelling zorgt voor meer latentie. Gebruik hem alleen als het nodig is." @@ -253,6 +264,9 @@ msgstr "Deblokkeren" msgid "UNSECURED CONNECTION" msgstr "NIET-BEVEILIGDE VERBINDING" +msgid "Use" +msgstr "Gebruiken" + msgid "Username" msgstr "Gebruikersnaam" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Authenticatie" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Door op \"Opslaan\" te klikken, wordt de gebruikte methode gewijzigd." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Door op \"%(save)s\" te klikken, wordt de gebruikte methode gewijzigd." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s via %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s via aangepaste bridge" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Uit" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Aangepaste bridge toevoegen" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Aangepaste bridge verwijderen?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Als u de aangepaste bridge verwijdert, gaat u terug naar Locatie selecteren en wordt in plaats daarvan de optie Automatisch geselecteerd." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Aangepaste bridge bewerken" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Bridge-modus" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Bridgemodus inschakelen?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Om UDP te activeren, moet u Bridge-modus wijzigen naar Automatisch of Uit." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Ga naar Locatie selecteren om een speccifieke bridgeserver te selecteren." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Transportprotocol" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Een aangepaste bridgeserver kan worden gebruikt om censuur te omzeilen wanneer reguliere Mullvad-bridgeservers niet werken." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Land" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Aangepaste bridge" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Aangepaste lijsten" @@ -2014,7 +2067,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "Met welke TCP-poort moet het UDP-over-TCP-obfuscatieprotocol verbinding maken op de VPN-server." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (toegevoegd)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s is toegevoegd aan \"%s\"" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s is toegevoegd aan uw account." @@ -2039,6 +2100,10 @@ msgstr "Accountkrediet verloopt binnenkort" msgid "Account time reminders" msgstr "Accounttijdherinneringen" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Voeg %s toe aan lijst" + msgid "Add 30 days time" msgstr "30 dagen tijd toevoegen" @@ -2048,12 +2113,18 @@ msgstr "30 dagen tijd toevoegen (%s)" msgid "Add DNS server" msgstr "DNS-server toevoegen" +msgid "Add locations" +msgstr "Voeg locaties toe" + msgid "Agree and continue" msgstr "Akkoord en doorgaan" msgid "All applications" msgstr "Alle toepassingen" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Alle overschrijvingen worden gereset en IP-adressen van servers in Locatie selecteren worden teruggezet naar de standaardwaarden." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Biedt toegang tot andere apparaten op hetzelfde netwerk voor delen, afdrukken en dergelijke" @@ -2063,6 +2134,9 @@ msgstr "Altijd-aan VPN toegewezen aan andere app" msgid "Always-on VPN might be enabled for another app" msgstr "Altijd-aan VPN is mogelijk ingeschakeld voor een andere app" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Let op: split tunneling is een privacyrisico." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Let op: deze instelling kan niet worden gebruikt in combinatie met Aangepaste DNS-server gebruiken." @@ -2072,6 +2146,9 @@ msgstr "Automatisch verbinden en lockdownmodus" msgid "Auto-connect & \\nLockdown mode" msgstr "Automatisch verbinden en \\nlockdownmodus" +msgid "Auto-connect (legacy)" +msgstr "Automatisch verbinden (verouderd)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Automatisch verbinden heet Altijd-aan VPN in de Android-systeeminstellingen en zorgt ervoor dat u altijd verbonden bent met de VPN-tunnel en automatisch verbinding maakt na een herstart." @@ -2093,6 +2170,12 @@ msgstr "Logs gekopieerd naar klembord" msgid "Copied to clipboard" msgstr "Gekopieerd naar klembord" +msgid "Create" +msgstr "Maken" + +msgid "Create new list" +msgstr "Nieuwe lijst maken" + msgid "Critical error (your attention is required)" msgstr "Kritieke fout (uw aandacht is vereist)" @@ -2102,11 +2185,32 @@ msgstr "Aangepaste DNS-serveradressen %s zijn ongeldig" msgid "DNS settings might not go into effect immediately" msgstr "DNS-instellingen worden mogelijk niet onmiddellijk van kracht" +msgid "Delete \"%s\"?" +msgstr "\"%s\" verwijderen?" + msgid "Disable all %s above to activate this setting." msgstr "Schakel alle %s hierboven uit om deze instelling te activeren." -msgid "Enable" -msgstr "Inschakelen" +msgid "Discard" +msgstr "Verwerpen" + +msgid "Discard changes?" +msgstr "Wijzigingen verwerpen?" + +msgid "Edit custom lists" +msgstr "Aangepaste lijsten bewerken" + +msgid "Edit list" +msgstr "Lijst bewerken" + +msgid "Edit lists" +msgstr "Lijsten bewerken" + +msgid "Edit locations" +msgstr "Locaties bewerken" + +msgid "Edit name" +msgstr "Naam bewerken" msgid "Enter MTU" msgstr "Voer MTU in" @@ -2114,6 +2218,12 @@ msgstr "Voer MTU in" msgid "Excluded applications" msgstr "Uitgesloten toepassingen" +msgid "Failed to apply patch" +msgstr "Patch toepassen mislukt" + +msgid "File" +msgstr "Bestand" + msgid "Go to VPN settings" msgstr "Naar VPN-instellingen" @@ -2129,9 +2239,34 @@ msgstr "Google Play niet beschikbaar" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Als de split-tunnelingfunctie wordt gebruikt, vraagt de app het systeem om een lijst van alle geïnstalleerde toepassingen. Deze lijst wordt alleen opgehaald in de split-tunnelingweergave. De lijst van geïnstalleerde toepassingen wordt nooit verzonden vanaf het apparaat." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Nieuwe overschrijvingen importeren vanuit" + +msgid "Import successful, overrides active" +msgstr "Importeren geslaagd, overschrijvingen actief" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Het importeren van nieuwe overschrijvingen kan sommige eerder geïmporteerde overschrijvingen vervangen." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Installeer Mullvad VPN (%s) om up-to-date te blijven" +msgid "Invalid or missing value \"%s\"" +msgstr "Ongeldige of ontbrekende waarde \"%s\"" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Hier kunt u apps selecteren die rechtstreeks toegang tot het internet moeten hebben zonder via de VPN-tunnel te gaan." + +msgid "List name" +msgstr "Lijstnaam" + +msgid "Locations" +msgstr "Locaties" + +msgid "Locations were changed for \"%s\"" +msgstr "Locaties zijn gewijzigd voor \"%s\"" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Zorg dat het apparaat altijd de VPN-tunnel gebruikt." @@ -2144,9 +2279,43 @@ msgstr "Mullvad-accountnummer" msgid "Mullvad services unavailable" msgstr "Mullvad-diensten niet beschikbaar" +msgid "Name was changed to %s" +msgstr "Naam is gewijzigd in %s" + +msgid "New list" +msgstr "Nieuwe lijst" + +msgid "No custom lists available" +msgstr "Geen aangepaste lijsten beschikbaar" + msgid "No internet connection" msgstr "Geen internetverbinding" +msgid "No locations found" +msgstr "Geen locaties gevonden" + +msgid "Not found" +msgstr "Niet gevonden" + +msgid "Overrides active" +msgstr "Overschrijvingen actief" + +msgid "Overrides cleared" +msgstr "Overschrijvingen gewist" + +msgid "Overrides inactive" +msgstr "Overschrijvingen inactief" + +msgid "Paste or write overrides to be imported" +msgstr "Plak of schrijf overschrijvingen om te importeren" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Patch komt niet overeen met specificatie" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Gebruik in plaats hiervan de systeeminstelling Altijd aan door de aanwijzingen hierboven in %s te volgen." + msgid "Preferences" msgstr "Voorkeuren" @@ -2156,18 +2325,33 @@ msgstr "Privacy" msgid "Privacy policy" msgstr "Privacybeleid" +msgid "Recursion limit" +msgstr "Recursielimiet" + msgid "Remove" msgstr "Verwijderen" msgid "Remove custom port" msgstr "Aangepaste poort verwijderen" +msgid "Reset" +msgstr "Resetten" + +msgid "Reset all overrides" +msgstr "Alle overschrijvingen resetten" + +msgid "Reset overrides" +msgstr "Overschrijvingen resetten" + msgid "Reset to default" msgstr "Standaardwaarde herstellen" msgid "Secured" msgstr "Beveiligd" +msgid "Server Ip overrides" +msgstr "Overschrijvingen van server-IP-adressen" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Stel de MTU-waarde voor WireGuard in. Geldig bereik: %d - %d." @@ -2183,12 +2367,12 @@ msgstr "Toont de huidige status van de VPN-tunnel" msgid "Shows reminders when the account time is about to expire" msgstr "Toont herinneringen wanneer de accounttijd op het punt staat te verlopen" -msgid "Split tunneling is disabled." -msgstr "Gesplitste tunneling is uitgeschakeld." - msgid "Submit" msgstr "Verzenden" +msgid "Text" +msgstr "Tekst" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "De instellingen Automatisch verbinden en Lockdownmodus zijn te vinden in de Android-systeeminstellingen, volg deze gids om een of beide in te schakelen." @@ -2204,6 +2388,12 @@ msgstr "Er zijn geen VPN-instellingen op uw apparaat" msgid "This address has already been entered." msgstr "Dit adres is al ingevoerd." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Druk op de \"︙\" of druk lang op een land, plaats of server om locaties toe te voegen." + +msgid "To create a custom list press the \"︙\"" +msgstr "Druk op de \"︙\" om een aangepaste lijst te maken" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Om zeker te zijn dat u de meest veilige versie hebt en om u te informeren over eventuele problemen met de huidige geactiveerde versie, voert de app automatisch versiecontroles uit. Hierbij worden de appversie en de Android-systeemversie naar de servers van Mullvad gestuurd. Mullvad houdt tellers bij voor gebruikte appversies en Android-versies. De gegevens worden nooit opgeslagen of gebruikt op een manier die u kan identificeren." @@ -2213,9 +2403,19 @@ msgstr "VPN in-/uitschakelen" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Kan firewallregels niet toepassen. Los problemen op of stuur een probleemmelding." +msgid "Unable to parse patch" +msgstr "Kan patch niet parsen" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Kan de tunnelverbinding niet starten. Schakel Altijd-aan VPN uit voor %s voordat u Mullvad VPN gebruikt." +msgid "Undo" +msgstr "Ongedaan maken" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Onbekende of verboden sleutel \"%s\"" + msgid "Unsecured" msgstr "Niet beveiligd" @@ -2225,6 +2425,9 @@ msgstr "DNS-server bijwerken" msgid "Update available, download to remain safe." msgstr "Update beschikbaar, download deze om veilig te blijven." +msgid "Update list name" +msgstr "Lijstnaam bijwerken" + msgid "VPN permission error" msgstr "VPN-machtigingsfout" @@ -2237,7 +2440,6 @@ msgstr "Status VPN-tunnel" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Geldige WireGuard-sleutel ontbreekt. Beheer sleutels onder Geavanceerde instellingen." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Geldige bereiken: %s" @@ -2283,6 +2485,9 @@ msgstr "U LEKT MOGELIJK NETWERKVERKEER" msgid "You are running an unsupported app version." msgstr "U gebruikt een niet-ondersteunde versie van de app." +msgid "\"%s\" was deleted" +msgstr "\"%s\" is verwijderd" + msgid "less than a minute ago" msgstr "minder dan een minuut geleden" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d dag" msgstr[1] "%d dagen" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d locatie" +msgstr[1] "%d locaties" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d maand" diff --git a/gui/locales/nl/relay-locations.po b/gui/locales/nl/relay-locations.po index 8fa26f532e52..3b999304dfcd 100644 --- a/gui/locales/nl/relay-locations.po +++ b/gui/locales/nl/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Dutch\n" "Language: nl_NL\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australië" msgid "Austria" msgstr "Oostenrijk" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "België" @@ -188,6 +196,10 @@ msgstr "Frankfurt am Main" msgid "Germany" msgstr "Duitsland" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Letland" msgid "Lisbon" msgstr "Lissabon" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "Londen" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marseille" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slowakije" +#. SI +msgid "Slovenia" +msgstr "Slovenië" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thailand" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Oekraïne" msgid "United Arab Emirates" msgstr "Verenigde Arabische Emiraten" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/pl/messages.po b/gui/locales/pl/messages.po index 6c658ad87702..514b5c519266 100644 --- a/gui/locales/pl/messages.po +++ b/gui/locales/pl/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Polish\n" "Language: pl_PL\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "Dodano %(duration)s, konto opłacone do %(expiry)s." @@ -151,6 +151,12 @@ msgstr "Rozłączanie" msgid "Dismiss" msgstr "Odrzuć" +msgid "Edit" +msgstr "Edytuj" + +msgid "Enable" +msgstr "Włącz" + msgid "Enable anyway" msgstr "Mimo to włącz" @@ -243,6 +249,11 @@ msgstr "Domyślne ustawienie systemowe" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Testuj" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "To ustawienie zwiększa latencję. Używaj go tylko w razie konieczności." @@ -265,6 +276,9 @@ msgstr "Odblokuj" msgid "UNSECURED CONNECTION" msgstr "NIEZABEZPIECZONE POŁĄCZENIE" +msgid "Use" +msgstr "Użyj" + msgid "Username" msgstr "Nazwa użytkownika" @@ -400,9 +414,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Uwierzytelnianie" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Kliknięcie przycisku „Zapisz” zmienia używaną metodę." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Kliknięcie opcji „%(save)s” zmienia używaną metodę." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -645,6 +660,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s przez %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s przez most niestandardowy" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -661,6 +680,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Wyjście" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Dodaj most niestandardowy" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Usunąć most niestandardowy?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Usunięcie mostu niestandardowego spowoduje powrót do widoku wybranej lokalizacji, a zamiast niego zostanie wybrana opcja Automatycznie." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Edytuj most niestandardowy" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1169,6 +1204,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Tryb mostu" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Włączyć tryb mostu?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1215,6 +1254,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Aby aktywować protokół UDP, zmień Tryb mostu na Automatyczny lub Wył." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Aby wybrać określony serwer mostowy, przejdź do widoku Wybierz lokalizację." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Protokół transportowy" @@ -1278,6 +1323,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Niestandardowego serwera mostowego można użyć do obejścia cenzury, gdy nie działają zwykłe serwery mostowe Mullvad." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1297,6 +1346,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Kraj" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Most niestandardowy" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Listy niestandardowe" @@ -2026,9 +2079,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "Port TCP, z którym powinien łączyć się protokół zaciemniania UDP-przez-TCP na serwerze VPN." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (dodano)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "Lokalizację %s dodano do „%s”" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "Do Twojego konta dodano %s." +msgstr "Do konta dodano %s." msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. Po kliknięciu poniższego przycisku Przejdź do ustawień VPN kliknij koło zębate obok nazwy Mullvad VPN." @@ -2051,6 +2112,10 @@ msgstr "Doładowanie konta wkrótce wygasa" msgid "Account time reminders" msgstr "Przypomnienia o czasie na koncie" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Dodaj lokalizację %s do listy" + msgid "Add 30 days time" msgstr "Dodaj 30 dni" @@ -2060,12 +2125,18 @@ msgstr "Dodaj 30 dni (%s)" msgid "Add DNS server" msgstr "Dodaj serwer DNS" +msgid "Add locations" +msgstr "Dodaj lokalizacje" + msgid "Agree and continue" msgstr "Zaakceptuj i kontynuuj" msgid "All applications" msgstr "Wszystkie aplikacje" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Wszystkie zastąpienia zostaną zresetowane i przywrócone zostaną domyślne adresy IP serwerów w widoku Wybierz lokalizację." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Umożliwia dostęp do innych urządzeń w tej samej sieci w celu udostępniania, drukowania itd." @@ -2075,6 +2146,9 @@ msgstr "Opcja „Zawsze włączony VPN” przypisana jest do innej aplikacji" msgid "Always-on VPN might be enabled for another app" msgstr "Opcja „Zawsze włączony VPN” może być włączona dla innej aplikacji" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Uwaga: dzielone tunelowanie stwarza ryzyko dla prywatności." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Uwaga: tego ustawienia nie można używać w połączeniu z opcją Użyj niestandardowego serwera DNS." @@ -2084,6 +2158,9 @@ msgstr "Automatyczne łączenie i tryb Lockdown" msgid "Auto-connect & \\nLockdown mode" msgstr "Automatyczne łączenie \\ni tryb Lockdown" +msgid "Auto-connect (legacy)" +msgstr "Automatyczne łączenie (starsza wersja)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Automatyczne łączenie w ustawieniach systemu Android nazywa się „Zawsze włączony VPN” i zapewnia stałe połączenie z tunelem VPN oraz automatyczne łączenie po ponownym uruchomieniu." @@ -2105,6 +2182,12 @@ msgstr "Skopiowano dzienniki do schowka" msgid "Copied to clipboard" msgstr "Skopiowano do schowka" +msgid "Create" +msgstr "Utwórz" + +msgid "Create new list" +msgstr "Utwórz nową listę" + msgid "Critical error (your attention is required)" msgstr "Błąd krytyczny (wymagana uwaga)" @@ -2114,11 +2197,32 @@ msgstr "Niestandardowe adresy serwerów DNS %s są nieprawidłowe" msgid "DNS settings might not go into effect immediately" msgstr "Ustawienia usługi DNS mogą nie zostać zastosowane natychmiast" +msgid "Delete \"%s\"?" +msgstr "Usunąć „%s”?" + msgid "Disable all %s above to activate this setting." msgstr "Aby aktywować to ustawienie, wyłącz powyżej wszystkie %s." -msgid "Enable" -msgstr "Włącz" +msgid "Discard" +msgstr "Odrzuć" + +msgid "Discard changes?" +msgstr "Odrzucić zmiany?" + +msgid "Edit custom lists" +msgstr "Edytuj listy niestandardowe" + +msgid "Edit list" +msgstr "Edytuj listę" + +msgid "Edit lists" +msgstr "Edytuj listy" + +msgid "Edit locations" +msgstr "Edytuj lokalizacje" + +msgid "Edit name" +msgstr "Edytuj nazwę" msgid "Enter MTU" msgstr "Wprowadź MTU" @@ -2126,6 +2230,12 @@ msgstr "Wprowadź MTU" msgid "Excluded applications" msgstr "Wykluczone aplikacje" +msgid "Failed to apply patch" +msgstr "Nie udało się zastosować poprawki" + +msgid "File" +msgstr "Plik" + msgid "Go to VPN settings" msgstr "Przejdź do ustawień VPN" @@ -2141,9 +2251,34 @@ msgstr "Sklep Google Play jest niedostępny" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Jeśli używana jest funkcja dzielonego tunelowania, aplikacja wysyła do systemu zapytanie o listę wszystkich zainstalowanych aplikacji. Lista ta jest pobierana wyłącznie w widoku dzielonego tunelowania. Lista zainstalowanych aplikacji nigdy nie jest wysyłana z urządzenia." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importuj nowe zastąpienia z" + +msgid "Import successful, overrides active" +msgstr "Import zakończony powodzeniem, zastąpienia są aktywne" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Wskutek importu nowych zastąpień zastąpione mogą zostać niektóre wcześniej zaimportowane zastąpienia." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Aby być na bieżąco, zainstaluj Mullvad VPN (%s)" +msgid "Invalid or missing value \"%s\"" +msgstr "Nieprawidłowa lub brakująca wartość „%s”" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Umożliwia wybranie aplikacji, które powinny uzyskiwać bezpośredni dostęp do Internetu bez przechodzenia przez tunel VPN." + +msgid "List name" +msgstr "Nazwa listy" + +msgid "Locations" +msgstr "Lokalizacje" + +msgid "Locations were changed for \"%s\"" +msgstr "Zmieniono lokalizacje dla „%s”" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Zapewnia, że urządzenie zawsze działa za pośrednictwem tunelu VPN." @@ -2156,9 +2291,43 @@ msgstr "Numer konta Mullvad" msgid "Mullvad services unavailable" msgstr "Usługi Mullvad są niedostępne" +msgid "Name was changed to %s" +msgstr "Nazwę zmieniono na %s" + +msgid "New list" +msgstr "Nowa lista" + +msgid "No custom lists available" +msgstr "Brak dostępnych list niestandardowych" + msgid "No internet connection" msgstr "Brak połączenia z Internetem" +msgid "No locations found" +msgstr "Nie znaleziono lokalizacji" + +msgid "Not found" +msgstr "Nie znaleziono" + +msgid "Overrides active" +msgstr "Zastąpienia aktywne" + +msgid "Overrides cleared" +msgstr "Usunięto zastąpienia" + +msgid "Overrides inactive" +msgstr "Zastąpienia nieaktywne" + +msgid "Paste or write overrides to be imported" +msgstr "Wklej lub wpisz zastąpienia do zaimportowania" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Poprawka niezgodna ze specyfikacją" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Zamiast tego użyj ustawienia systemowego Zawsze włączone, postępując zgodnie z poradnikiem w powyższej sekcji %s." + msgid "Preferences" msgstr "Preferencje" @@ -2168,18 +2337,33 @@ msgstr "Prywatność" msgid "Privacy policy" msgstr "Polityka prywatności" +msgid "Recursion limit" +msgstr "Limit rekurencji" + msgid "Remove" msgstr "Usuń" msgid "Remove custom port" msgstr "Usuń port niestandardowy" +msgid "Reset" +msgstr "Resetuj" + +msgid "Reset all overrides" +msgstr "Resetuj wszystkie zastąpienia" + +msgid "Reset overrides" +msgstr "Resetuj zastąpienia" + msgid "Reset to default" msgstr "Przywróć domyślne" msgid "Secured" msgstr "Zabezpieczone" +msgid "Server Ip overrides" +msgstr "Zastąpienia adresu IP serwera" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Ustaw wartość MTU WireGuard. Prawidłowy zakres: %d–%d." @@ -2195,12 +2379,12 @@ msgstr "Pokazuje bieżący status tunelu VPN" msgid "Shows reminders when the account time is about to expire" msgstr "Pokazuje przypomnienia, gdy kończy się czas na koncie" -msgid "Split tunneling is disabled." -msgstr "Dzielone tunelowanie jest wyłączone." - msgid "Submit" msgstr "Prześlij" +msgid "Text" +msgstr "Tekst" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Ustawienia trybu automatycznego łączenia i trybu Lockdown można znaleźć w ustawieniach systemu Android. Aby włączyć jeden lub oba tryby, postępuj zgodnie z niniejszym poradnikiem." @@ -2216,6 +2400,12 @@ msgstr "Na Twoim urządzeniu nie ma ustawień VPN" msgid "This address has already been entered." msgstr "Ten adres został już wprowadzony." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Aby dodać lokalizacje do listy, naciśnij przycisk „︙” lub naciśnij i przytrzymaj kraj, miasto albo serwer." + +msgid "To create a custom list press the \"︙\"" +msgstr "Aby utworzyć listę niestandardową, naciśnij przycisk „︙”" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Aby upewnić się, że masz najbezpieczniejszą wersję i poinformować Cię o wszelkich problemach z aktualnie uruchomioną wersją, aplikacja automatycznie sprawdza wersję. Powoduje to wysłanie wersji aplikacji i wersji systemu Android na serwery Mullvad. Mullvad prowadzi liczniki używanych wersji aplikacji i wersji systemu Android. Dane te nigdy nie są przechowywane ani wykorzystywane w żaden sposób, który umożliwiłby zidentyfikowanie Ciebie jako osoby." @@ -2225,9 +2415,19 @@ msgstr "Przełącz VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Nie można zastosować reguł zapory. Rozwiąż problem lub wyślij zgłoszenie problemu." +msgid "Unable to parse patch" +msgstr "Nie można przetworzyć poprawki" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Nie można uruchomić połączenia tunelowego. Przed rozpoczęciem użytkowania usługi Mullvad VPN wyłącz opcję „Zawsze włączony VPN” w %s." +msgid "Undo" +msgstr "Cofnij" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Nieznany lub zabroniony klucz „%s”" + msgid "Unsecured" msgstr "Niezabezpieczone" @@ -2237,6 +2437,9 @@ msgstr "Zaktualizuj serwer DNS" msgid "Update available, download to remain safe." msgstr "Dostępna jest aktualizacja. Aby zachować bezpieczeństwo, pobierz ją." +msgid "Update list name" +msgstr "Zaktualizuj nazwę listy" + msgid "VPN permission error" msgstr "Błąd uprawnienia VPN" @@ -2249,7 +2452,6 @@ msgstr "Status tunelu VPN" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Brak prawidłowego klucza WireGuard. Zarządzaj kluczami w Ustawieniach zaawansowanych." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Prawidłowe zakresy: %s" @@ -2295,6 +2497,9 @@ msgstr "TWÓJ RUCH SIECIOWY MOŻE WYCIEKAĆ" msgid "You are running an unsupported app version." msgstr "Używasz nieobsługiwanej wersji aplikacji." +msgid "\"%s\" was deleted" +msgstr "Usunięto „%s”" + msgid "less than a minute ago" msgstr "mniej niż minutę temu" @@ -2308,6 +2513,13 @@ msgstr[1] "%d dni" msgstr[2] "%d dni" msgstr[3] "%d dnia" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d lokalizacja" +msgstr[1] "%d lokalizacje" +msgstr[2] "%d lokalizacji" +msgstr[3] "%d lokalizacji" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d miesiąc" diff --git a/gui/locales/pl/relay-locations.po b/gui/locales/pl/relay-locations.po index 504b340261b3..c15a71577b3f 100644 --- a/gui/locales/pl/relay-locations.po +++ b/gui/locales/pl/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Polish\n" "Language: pl_PL\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australia" msgid "Austria" msgstr "Austria" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Belgia" @@ -188,6 +196,10 @@ msgstr "Frankfurt" msgid "Germany" msgstr "Niemcy" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Łotwa" msgid "Lisbon" msgstr "Lizbona" +#. SI LJU +msgid "Ljubljana" +msgstr "Lublana" + #. GB LON msgid "London" msgstr "Londyn" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marsylia" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Słowacja" +#. SI +msgid "Slovenia" +msgstr "Słowenia" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallin" msgid "Tel Aviv" msgstr "Tel Awiw" +#. TH +msgid "Thailand" +msgstr "Tajlandia" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ukraina" msgid "United Arab Emirates" msgstr "Zjednoczone Emiraty Arabskie" +#. ES VLC +msgid "Valencia" +msgstr "Walencja" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/pt/messages.po b/gui/locales/pt/messages.po index 4cd384f754a8..c1390bdf6147 100644 --- a/gui/locales/pt/messages.po +++ b/gui/locales/pt/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Portuguese\n" "Language: pt_PT\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "foi adicionado %(duration)s, conta paga até %(expiry)s." @@ -139,6 +139,12 @@ msgstr "A desligar" msgid "Dismiss" msgstr "Dispensar" +msgid "Edit" +msgstr "Editar" + +msgid "Enable" +msgstr "Ativar" + msgid "Enable anyway" msgstr "Ativar mesmo assim" @@ -231,6 +237,11 @@ msgstr "Predefinição do sistema" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Testar" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Esta definição aumenta a latência. Utilizar apenas se necessário." @@ -253,6 +264,9 @@ msgstr "Desbloquear" msgid "UNSECURED CONNECTION" msgstr "LIGAÇÃO INSEGURA" +msgid "Use" +msgstr "Utilizar" + msgid "Username" msgstr "Nome de utilizador" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Autenticação" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Clicar em \"Guardar\" altera o método em uso." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Clicar em \"%(save)s\" altera o método em uso." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s via %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s através de ponte personalizada" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Saída" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Adicionar ponte personalizada" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Eliminar ponte personalizada?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "A eliminação da ponte personalizada leva-o de volta à vista Selecionar local, e a opção Automático será selecionada." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Editar ponte personalizada" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Modo de ponte" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Ativar modo ponte?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Para ativar o UDP, altere o Modo ponte para Automático ou Desligado." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Para selecionar um servidor de ponte específico, aceda à vista Selecionar local." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Protocolo de transporte" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Um servidor de ponte personalizado pode ser utilizado para contornar a censura quando os servidores de ponte Mullvad regulares não funcionam." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "País" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Ponte personalizada" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Listas personalizadas" @@ -2014,9 +2067,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "A que porta TCP o protocolo de ofuscação UDP sobre TCP deve ligar-se no servidor VPN." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (adicionado)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s foi adicionado a \"%s\"" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "%s adicionado(s) à sua conta." +msgstr "%s foi adicionado à sua conta." msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. Depois de clicar no botão Ir para as definições de VPN abaixo, clique na roda dentada junto ao nome da Mullvad VPN." @@ -2039,6 +2100,10 @@ msgstr "O crédito da conta expira brevemente" msgid "Account time reminders" msgstr "Lembretes de tempo da conta" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Adicionar %s à lista" + msgid "Add 30 days time" msgstr "Adicionar 30 dias" @@ -2048,12 +2113,18 @@ msgstr "Adicionar 30 dias (%s)" msgid "Add DNS server" msgstr "Adicionar servidor DNS" +msgid "Add locations" +msgstr "Adicionar localizações" + msgid "Agree and continue" msgstr "Concordar e continuar" msgid "All applications" msgstr "Todas as aplicações" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Todas as substituições serão repostas e os endereços IP dos servidores, na vista Selecionar local, voltarão às predefinições." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Permite o acesso a outros dispositivos na mesma rede para partilha, impressão, etc." @@ -2063,6 +2134,9 @@ msgstr "VPN sempre ligada atribuída a outra app" msgid "Always-on VPN might be enabled for another app" msgstr "A VPN sempre ligada pode estar ativada para outra app" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Atenção: a divisão do túnel é um risco para a privacidade." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Atenção: esta definição não pode ser usada em combinação com Usar servidor DNS personalizado." @@ -2072,6 +2146,9 @@ msgstr "Ligação automática e modo de bloqueio" msgid "Auto-connect & \\nLockdown mode" msgstr "Ligação automática e\\nmodo de bloqueio" +msgid "Auto-connect (legacy)" +msgstr "Ligação automática (método antigo)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "A ligação automática é designada por VPN sempre ligada nas definições do sistema Android e assegura que está constantemente ligado ao túnel VPN e ligar-se-á automaticamente após o reinício." @@ -2093,6 +2170,12 @@ msgstr "Registos copiados para a área de transferência" msgid "Copied to clipboard" msgstr "Copiado para a área de transferência" +msgid "Create" +msgstr "Criar" + +msgid "Create new list" +msgstr "Criar nova lista" + msgid "Critical error (your attention is required)" msgstr "Erro crítico (é necessária a sua atenção)" @@ -2102,11 +2185,32 @@ msgstr "Os endereços do servidor DNS personalizado %s são inválidos" msgid "DNS settings might not go into effect immediately" msgstr "As definições de DNS podem não fazer efeito imediatamente" +msgid "Delete \"%s\"?" +msgstr "Eliminar \"%s\"?" + msgid "Disable all %s above to activate this setting." msgstr "Desative todos os %s abaixo para ativar esta definição." -msgid "Enable" -msgstr "Ativar" +msgid "Discard" +msgstr "Descartar" + +msgid "Discard changes?" +msgstr "Descartar alterações?" + +msgid "Edit custom lists" +msgstr "Editar listas personalizadas" + +msgid "Edit list" +msgstr "Editar lista" + +msgid "Edit lists" +msgstr "Editar listas" + +msgid "Edit locations" +msgstr "Editar localizações" + +msgid "Edit name" +msgstr "Editar nome" msgid "Enter MTU" msgstr "Introduzir MTU" @@ -2114,6 +2218,12 @@ msgstr "Introduzir MTU" msgid "Excluded applications" msgstr "Aplicações excluídas" +msgid "Failed to apply patch" +msgstr "Erro ao aplicar patch" + +msgid "File" +msgstr "Ficheiro" + msgid "Go to VPN settings" msgstr "Ir para as definições de VPN" @@ -2129,9 +2239,34 @@ msgstr "Google Play indisponível" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Se for utilizada a funcionalidade de divisão do túnel, a app consulta o seu sistema para obter uma lista de todas as aplicações instaladas. Esta lista só é recuperada na vista de divisão do túnel. A lista de aplicações instaladas nunca é enviada pelo dispositivo." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importar novas substituições através de" + +msgid "Import successful, overrides active" +msgstr "Importação bem-sucedida: substituições ativas" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "A importação de novas substituições pode substituir algumas substituições importadas anteriormente." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Instalar o Mullvad VPN (%s) para ficar atualizado" +msgid "Invalid or missing value \"%s\"" +msgstr "Valor inválido ou em falta \"%s\"" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Permite-lhe selecionar aplicações que devem aceder diretamente à Internet sem passar pelo túnel VPN." + +msgid "List name" +msgstr "Nome da lista" + +msgid "Locations" +msgstr "Localizações" + +msgid "Locations were changed for \"%s\"" +msgstr "As localizações foram alteradas para \"%s\"" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Assegura que o dispositivo está sempre no túnel VPN." @@ -2144,9 +2279,43 @@ msgstr "Número de conta Mullvad" msgid "Mullvad services unavailable" msgstr "Serviços Mullvad indisponíveis" +msgid "Name was changed to %s" +msgstr "O nome foi alterado para %s" + +msgid "New list" +msgstr "Nova lista" + +msgid "No custom lists available" +msgstr "Nenhuma lista personalizada disponível" + msgid "No internet connection" msgstr "Sem ligação à internet" +msgid "No locations found" +msgstr "Nenhuma localização encontrada" + +msgid "Not found" +msgstr "Não encontrada" + +msgid "Overrides active" +msgstr "Substituições ativas" + +msgid "Overrides cleared" +msgstr "Substituições eliminadas" + +msgid "Overrides inactive" +msgstr "Substituições inativas" + +msgid "Paste or write overrides to be imported" +msgstr "Cole ou introduza substituições a importar" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "O patch não corresponde à especificação" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Alternativamente, utilize a definição de sistema Sempre ligada, seguindo o guia em %s acima." + msgid "Preferences" msgstr "Preferências" @@ -2156,18 +2325,33 @@ msgstr "Privacidade" msgid "Privacy policy" msgstr "Política de privacidade" +msgid "Recursion limit" +msgstr "Limite de recursão" + msgid "Remove" msgstr "Remover" msgid "Remove custom port" msgstr "Remover porta personalizada" +msgid "Reset" +msgstr "Repor" + +msgid "Reset all overrides" +msgstr "Repor todas as substituições" + +msgid "Reset overrides" +msgstr "Repor substituições" + msgid "Reset to default" msgstr "Repor para as predefinições" msgid "Secured" msgstr "Seguro" +msgid "Server Ip overrides" +msgstr "Substituições de IP de servidor" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Definir o valor WireGuard MTU. Intervalo válido: %d - %d." @@ -2183,12 +2367,12 @@ msgstr "Indica o estado atual do túnel VPN" msgid "Shows reminders when the account time is about to expire" msgstr "Mostra lembretes quando o tempo da conta está prestes a expirar" -msgid "Split tunneling is disabled." -msgstr "A divisão do túnel está desativada." - msgid "Submit" msgstr "Enviar" +msgid "Text" +msgstr "Texto" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "As definições da ligação automática e do modo de bloqueio podem ser encontradas nas definições do sistema Android. Siga este guia para ativar uma ou ambas as funcionalidades." @@ -2204,6 +2388,12 @@ msgstr "Não existem definições de VPN no seu dispositivo" msgid "This address has already been entered." msgstr "Este endereço já foi introduzido." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Para adicionar localizações a uma lista, prima o botão \"︙\" ou mantenha premido um país, uma cidade ou um servidor." + +msgid "To create a custom list press the \"︙\"" +msgstr "Para criar uma lista personalizada prima o botão \"︙\"" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Para se certificar de que tem a versão mais segura e para o informar de quaisquer problemas com a versão atual em execução, a app efetua verificações de versão automaticamente. Esta envia a versão da app e a versão do sistema Android para os servidores da Mullvad. A Mullvad mantém um registo do número de versões da app e de versões do Android utilizadas. Os dados nunca são armazenados ou utilizados de forma a identificar o utilizador." @@ -2213,9 +2403,19 @@ msgstr "Alternar VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Não foi possível aplicar as regras de firewall. Experimente a resolução de problemas ou envie um relatório do problema." +msgid "Unable to parse patch" +msgstr "Não foi possível analisar a correção" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Não foi possível iniciar a ligação de túnel. Desative a VPN sempre ligada para %s antes de utilizar a Mullvad VPN." +msgid "Undo" +msgstr "Anular" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Chave \"%s\" desconhecida ou proibida" + msgid "Unsecured" msgstr "Inseguro" @@ -2225,6 +2425,9 @@ msgstr "Atualizar servidor DNS" msgid "Update available, download to remain safe." msgstr "Atualização disponível, transfira-a para ficar seguro." +msgid "Update list name" +msgstr "Atualizar nome da lista" + msgid "VPN permission error" msgstr "Erro de permissão da VPN" @@ -2237,7 +2440,6 @@ msgstr "Estado do túnel VPN" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Chave WireGuard válida em falta. Faça a gestão das chaves em Definições Avançadas." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Intervalos válidos: %s" @@ -2283,6 +2485,9 @@ msgstr "PODERÁ ESTAR A PERDER TRÁFEGO DE REDE" msgid "You are running an unsupported app version." msgstr "Está a executar uma versão da aplicação não suportada." +msgid "\"%s\" was deleted" +msgstr "\"%s\" foi eliminada" + msgid "less than a minute ago" msgstr "há menos de um minuto" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d dia" msgstr[1] "%d dias" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d localização" +msgstr[1] "%d localizações" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d mês" diff --git a/gui/locales/pt/relay-locations.po b/gui/locales/pt/relay-locations.po index b4825f5b4dcd..ceb2b8a9f380 100644 --- a/gui/locales/pt/relay-locations.po +++ b/gui/locales/pt/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Portuguese\n" "Language: pt_PT\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Austrália" msgid "Austria" msgstr "Áustria" +#. TH BKK +msgid "Bangkok" +msgstr "Banguecoque" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Bélgica" @@ -188,6 +196,10 @@ msgstr "Frankfurt" msgid "Germany" msgstr "Alemanha" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Gotemburgo" @@ -264,6 +276,10 @@ msgstr "Letónia" msgid "Lisbon" msgstr "Lisboa" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "Londres" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marselha" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Eslováquia" +#. SI +msgid "Slovenia" +msgstr "Eslovénia" + #. BG SOF msgid "Sofia" msgstr "Sófia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Telavive" +#. TH +msgid "Thailand" +msgstr "Tailândia" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ucrânia" msgid "United Arab Emirates" msgstr "Emirados Árabes Unidos" +#. ES VLC +msgid "Valencia" +msgstr "Valência" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/ru/messages.po b/gui/locales/ru/messages.po index 2ff0e21f1824..732a0e81633f 100644 --- a/gui/locales/ru/messages.po +++ b/gui/locales/ru/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Russian\n" "Language: ru_RU\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "Добавлено: %(duration)s. Учетная запись оплачена до %(expiry)s." @@ -151,6 +151,12 @@ msgstr "Отключение" msgid "Dismiss" msgstr "Закрыть" +msgid "Edit" +msgstr "Изменить" + +msgid "Enable" +msgstr "Включить" + msgid "Enable anyway" msgstr "Всё равно включить" @@ -243,6 +249,11 @@ msgstr "Системные настройки по умолчанию" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Проверить" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Этот параметр увеличивает задержку. Используйте только в случае необходимости." @@ -265,6 +276,9 @@ msgstr "Разблокировать" msgid "UNSECURED CONNECTION" msgstr "НЕЗАЩИЩЕННОЕ ПОДКЛЮЧЕНИЕ" +msgid "Use" +msgstr "Использовать" + msgid "Username" msgstr "Имя пользователя" @@ -400,9 +414,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Аутентификация" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Нажав «Сохранить», вы измените используемый метод." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Нажав «%(save)s», вы измените используемый метод." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -645,6 +660,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s через %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s через свой мост" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -661,6 +680,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Выход" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Добавление своего моста" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Удалить свой мост?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Удаление своего моста вернет вас к представлению «Выбор местоположения» с установленным параметром «Автоматически»." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Изменение своего моста" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1169,6 +1204,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Режим моста" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Включить режим моста?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1215,6 +1254,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "Чтобы активировать UDP, измените Режим моста на Автоматически или Выключен." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Чтобы выбрать конкретный сервер моста, перейдите к представлению «Выбор местоположения»." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Транспортный протокол" @@ -1278,6 +1323,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Свой сервер-мост можно использовать для обхода цензуры, когда обычные серверы-мосты Mullvad не работают." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1297,6 +1346,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Страна" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Свой мост" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Свои списки" @@ -2026,7 +2079,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "TCP-порт, к которому должен подключаться протокол обфускации UDP через TCP на VPN-сервере." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (добавлено)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "Местоположение %s добавлено к списку «%s»" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "На учетную запись добавлено время: %s." @@ -2051,6 +2112,10 @@ msgstr "Баланс учетной записи скоро закончится msgid "Account time reminders" msgstr "Напоминания о времени на учетной записи" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Добавить местоположение %s к списку" + msgid "Add 30 days time" msgstr "Добавить 30 дней" @@ -2060,12 +2125,18 @@ msgstr "Добавить 30 дней (%s)" msgid "Add DNS server" msgstr "Добавить DNS-сервер" +msgid "Add locations" +msgstr "Добавление местоположений" + msgid "Agree and continue" msgstr "Согласиться и продолжить" msgid "All applications" msgstr "Все приложения" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Все переопределения будут сброшены, а IP-адреса серверов в представлении «Выбор местоположения­» вернутся к значениям по умолчанию." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Разрешить доступ к другим устройствам в той же сети для организации общего доступа, печати и т. д." @@ -2075,6 +2146,9 @@ msgstr "Опция «Постоянная VPN» назначена другом msgid "Always-on VPN might be enabled for another app" msgstr "Опцию «Постоянная VPN» может быть включена для другого приложения" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Внимание: раздельное туннелирование — это риск для конфиденциальности." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Внимание: этот параметр нельзя использовать вместе с параметром Пользовательский DNS-сервер." @@ -2084,6 +2158,9 @@ msgstr "Автоподключение и режим блокировки" msgid "Auto-connect & \\nLockdown mode" msgstr "Автоподключение и \\nрежим блокировки" +msgid "Auto-connect (legacy)" +msgstr "Автоподключение (устаревшее)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "В системных настройках Android автоподключение называется «Постоянная VPN». Эта опция обеспечивает постоянное подключение к туннелю VPN и автоподключение после перезапуска." @@ -2105,6 +2182,12 @@ msgstr "Журналы скопированы в буфер обмена" msgid "Copied to clipboard" msgstr "Скопировано в буфер обмена" +msgid "Create" +msgstr "Создать" + +msgid "Create new list" +msgstr "Создание нового списка" + msgid "Critical error (your attention is required)" msgstr "Критическая ошибка (требуется ваше участие)" @@ -2114,11 +2197,32 @@ msgstr "Пользовательские адреса DNS-серверов %s н msgid "DNS settings might not go into effect immediately" msgstr "Настройки DNS могут не сразу вступить в силу" +msgid "Delete \"%s\"?" +msgstr "Удалить список «%s»?" + msgid "Disable all %s above to activate this setting." msgstr "Чтобы включить эту настройку, отключите все %s выше." -msgid "Enable" -msgstr "Включить" +msgid "Discard" +msgstr "Сбросить" + +msgid "Discard changes?" +msgstr "Сбросить изменения?" + +msgid "Edit custom lists" +msgstr "Изменение своих списков" + +msgid "Edit list" +msgstr "Изменение списка" + +msgid "Edit lists" +msgstr "Изменить списки" + +msgid "Edit locations" +msgstr "Изменить местоположения" + +msgid "Edit name" +msgstr "Изменить имя" msgid "Enter MTU" msgstr "Введите MTU" @@ -2126,6 +2230,12 @@ msgstr "Введите MTU" msgid "Excluded applications" msgstr "Исключенные приложения" +msgid "Failed to apply patch" +msgstr "Не удалось применить патч" + +msgid "File" +msgstr "Файл" + msgid "Go to VPN settings" msgstr "Перейти к настройкам VPN" @@ -2141,9 +2251,34 @@ msgstr "Google Play недоступен" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Если используется функция раздельного туннелирования, то приложение запрашивает в системе список всех установленных приложений. Этот список запрашивается только на экране настройки раздельного туннелирования. Список установленных приложений никогда не передается за пределы устройства." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Импорт новых переопределений:" + +msgid "Import successful, overrides active" +msgstr "Импортировано, переопределения активны" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Импорт новых переопределений может заменить некоторые ранее импортированные переопределения." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Пользуйтесь актуальной версией — установите Mullvad VPN (%s)" +msgid "Invalid or missing value \"%s\"" +msgstr "Недопустимое или отсутствующее значение «%s»" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Позволяет выбрать приложения, которые должны получать доступ к Интернету напрямую, не проходя через VPN-туннель." + +msgid "List name" +msgstr "Имя списка" + +msgid "Locations" +msgstr "Местоположения" + +msgid "Locations were changed for \"%s\"" +msgstr "Местоположения были изменены для «%s»" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Обеспечивает постоянное подключение устройства к туннелю VPN." @@ -2156,9 +2291,43 @@ msgstr "Номер учетной записи Mullvad" msgid "Mullvad services unavailable" msgstr "Службы Mullvad недоступны" +msgid "Name was changed to %s" +msgstr "Имя изменено на «%s»" + +msgid "New list" +msgstr "Новый список" + +msgid "No custom lists available" +msgstr "Нет своих списков" + msgid "No internet connection" msgstr "Нет подключения к Интернету" +msgid "No locations found" +msgstr "Местоположения не найдены" + +msgid "Not found" +msgstr "Не найдено" + +msgid "Overrides active" +msgstr "Переопределения активны" + +msgid "Overrides cleared" +msgstr "Переопределения удалены" + +msgid "Overrides inactive" +msgstr "Переопределения неактивны" + +msgid "Paste or write overrides to be imported" +msgstr "Вставьте или напишите переопределения для импорта" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Патч не соответствует спецификации" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Вместо этого используйте системную настройку Постоянная VPN, следуя инструкциям в разделе %s выше." + msgid "Preferences" msgstr "Параметры" @@ -2168,18 +2337,33 @@ msgstr "Конфиденциальность" msgid "Privacy policy" msgstr "Политика конфиденциальности" +msgid "Recursion limit" +msgstr "Предел рекурсии" + msgid "Remove" msgstr "Удалить" msgid "Remove custom port" msgstr "Удалить пользовательский порт" +msgid "Reset" +msgstr "Сбросить" + +msgid "Reset all overrides" +msgstr "Сброс всех переопределений" + +msgid "Reset overrides" +msgstr "Сбросить переопределения" + msgid "Reset to default" msgstr "Восстановить значение по умолчанию" msgid "Secured" msgstr "Подключение защищено" +msgid "Server Ip overrides" +msgstr "Переопределения IP-адреса сервера" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Установите значение MTU для WireGuard. Диапазон значений: %d–%d." @@ -2195,12 +2379,12 @@ msgstr "Показывает текущее состояние VPN-туннел msgid "Shows reminders when the account time is about to expire" msgstr "Показывает уведомления, когда время на учетной записи скоро закончится" -msgid "Split tunneling is disabled." -msgstr "Раздельное туннелирование отключено." - msgid "Submit" msgstr "Отправить" +msgid "Text" +msgstr "Текст" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Настройки автоподключения и режима блокировки можно найти в системных настройках Android. Чтобы включить их, следуйте этой инструкции." @@ -2216,6 +2400,12 @@ msgstr "На устройстве нет настроек VPN" msgid "This address has already been entered." msgstr "Этот адрес уже введен." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Чтобы добавить местоположения в список, нажмите «︙» или нажмите и удерживайте страну, город или сервер." + +msgid "To create a custom list press the \"︙\"" +msgstr "Чтобы создать свой список, нажмите «︙»" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Чтобы удостовериться, что вы используете наиболее безопасную версию приложения, и сообщить вам о возможных проблемах в ней, приложение автоматически проверяет свою версию. При этом на серверы Mullvad передаются данные о версии приложения и системы Android. Mullvad ведет учет количества пользователей с определенными версиями приложения и ОС Android. Эти данные не хранятся и не используются каким бы то ни было образом, позволяющим вас идентифицировать." @@ -2225,9 +2415,19 @@ msgstr "Включение VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Невозможно применить правила брандмауэра. Устраните неполадки или отправьте сообщение о проблеме." +msgid "Unable to parse patch" +msgstr "Не удалось проанализировать патч" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Не удалось запустить туннельное подключение. Перед использованием Mullvad VPN отключите опцию «Постоянная VPN» для приложения %s." +msgid "Undo" +msgstr "Отменить" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Неизвестный или запрещенный ключ «%s»" + msgid "Unsecured" msgstr "Подключение не защищено" @@ -2237,6 +2437,9 @@ msgstr "Обновить DNS-сервер" msgid "Update available, download to remain safe." msgstr "Вышло обновление. Установите его, чтобы защитить подключения." +msgid "Update list name" +msgstr "Обновление названия списка" + msgid "VPN permission error" msgstr "Ошибка разрешения для VPN" @@ -2249,7 +2452,6 @@ msgstr "Состояние туннеля VPN" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Не найден действительный ключ WireGuard. Управлять ключами можно в дополнительных настройках." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Допустимые диапазоны: %s" @@ -2295,6 +2497,9 @@ msgstr "ВОЗМОЖНА УТЕЧКА СЕТЕВОГО ТРАФИКА" msgid "You are running an unsupported app version." msgstr "Версия приложения, с которой вы работаете, не поддерживается." +msgid "\"%s\" was deleted" +msgstr "Список «%s» удален" + msgid "less than a minute ago" msgstr "менее минуты назад" @@ -2308,6 +2513,13 @@ msgstr[1] "%d суток" msgstr[2] "%d суток" msgstr[3] "%d суток" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d местоположение" +msgstr[1] "%d местоположения" +msgstr[2] "%d местоположений" +msgstr[3] "%d местоположения" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d месяц" diff --git a/gui/locales/ru/relay-locations.po b/gui/locales/ru/relay-locations.po index 4a54402aa82e..37e54efab608 100644 --- a/gui/locales/ru/relay-locations.po +++ b/gui/locales/ru/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Russian\n" "Language: ru_RU\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Австралия" msgid "Austria" msgstr "Австрия" +#. TH BKK +msgid "Bangkok" +msgstr "Бангкок" + +#. ES BCN +msgid "Barcelona" +msgstr "Барселона" + #. BE msgid "Belgium" msgstr "Бельгия" @@ -188,6 +196,10 @@ msgstr "Франкфурт-на-Майне" msgid "Germany" msgstr "Германия" +#. GB GLW +msgid "Glasgow" +msgstr "Глазго" + #. SE GOT msgid "Gothenburg" msgstr "Гётеборг" @@ -264,6 +276,10 @@ msgstr "Латвия" msgid "Lisbon" msgstr "Лиссабон" +#. SI LJU +msgid "Ljubljana" +msgstr "Любляна" + #. GB LON msgid "London" msgstr "Лондон" @@ -296,6 +312,10 @@ msgstr "Манчестер" msgid "Marseille" msgstr "Марсель" +#. US TXC +msgid "McAllen, TX" +msgstr "Мак-Аллен, Техас" + #. AU MEL msgid "Melbourne" msgstr "Мельбурн" @@ -472,6 +492,10 @@ msgstr "Скопье" msgid "Slovakia" msgstr "Словакия" +#. SI +msgid "Slovenia" +msgstr "Словения" + #. BG SOF msgid "Sofia" msgstr "София" @@ -520,6 +544,10 @@ msgstr "Таллин" msgid "Tel Aviv" msgstr "Тель-Авив" +#. TH +msgid "Thailand" +msgstr "Таиланд" + #. AL TIA msgid "Tirana" msgstr "Тирана" @@ -548,6 +576,10 @@ msgstr "Украина" msgid "United Arab Emirates" msgstr "Объединённые Арабские Эмираты" +#. ES VLC +msgid "Valencia" +msgstr "Валенсия" + #. CA VAN msgid "Vancouver" msgstr "Ванкувер" diff --git a/gui/locales/sv/messages.po b/gui/locales/sv/messages.po index 05872bd92a74..f2ca39b4c252 100644 --- a/gui/locales/sv/messages.po +++ b/gui/locales/sv/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Swedish\n" "Language: sv_SE\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s har lagts till. Kontot är betalt till och med %(expiry)s." @@ -139,6 +139,12 @@ msgstr "Kopplar från" msgid "Dismiss" msgstr "Ignorera" +msgid "Edit" +msgstr "Redigera" + +msgid "Enable" +msgstr "Aktivera" + msgid "Enable anyway" msgstr "Aktivera ändå" @@ -231,6 +237,11 @@ msgstr "Systemstandard" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Testa" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Den här inställningen ökar fördröjningen. Använd bara vid behov." @@ -253,6 +264,9 @@ msgstr "Avblockera" msgid "UNSECURED CONNECTION" msgstr "OSÄKER ANSLUTNING" +msgid "Use" +msgstr "Använd" + msgid "Username" msgstr "Användarnamn" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Autentisering" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "Om du klickar på \"Spara\", ändras metoden som används." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "Om du klickar på \"%(save)s\" ändras den använda metoden." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s via %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s via anpassad brygga" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Ut" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Lägg till anpassad brygga" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Ta bort anpassad brygga?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Om du tar bort den anpassade bryggan tas du tillbaka till vyn Välj plats och alternativet Automatisk väljs istället." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Redigera anpassad brygga" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Broläge" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Aktivera bryggläge?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "För att aktivera UDP ska du ändra Bryggläge till Automatisk eller Av." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Gå till vyn Välj plats för att välja en specifik bryggserver." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Transportprotokoll" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "En anpassad bryggserver kan användas för att kringgå censur när Mullvads vanliga bryggservrar inte fungerar." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Land" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Anpassad brygga" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Anpassade listor" @@ -2014,7 +2067,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "Vilken TCP-port som UDP-över-TCP-obfuskeringsprotokoll bör ansluta till på VPN-servern." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (har lagts till)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s har lagts till i \"%s\"" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s har lagts till på ditt konto." @@ -2039,6 +2100,10 @@ msgstr "Kontokrediten slutar snart gälla" msgid "Account time reminders" msgstr "Påminnelser om kontotid" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "Lägg till %s i listan" + msgid "Add 30 days time" msgstr "Lägg till 30 dagar" @@ -2048,12 +2113,18 @@ msgstr "Lägg till 30 dagar (%s)" msgid "Add DNS server" msgstr "Lägg till DNS-server" +msgid "Add locations" +msgstr "Lägg till platser" + msgid "Agree and continue" msgstr "Godkänn och fortsätt" msgid "All applications" msgstr "Alla applikationer" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Alla åsidosättningar återställs och servrarnas IP-adresser i vyn Välj plats återgår till standard." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Tillåter åtkomst till andra enheter på samma nätverk för delning, utskrift osv." @@ -2063,6 +2134,9 @@ msgstr "VPN som alltid är på har tilldelats till annan app" msgid "Always-on VPN might be enabled for another app" msgstr "VPN som alltid är på kan ha aktiverats för annan app" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Obs! Split tunneling är en sekretessrisk." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Obs! Den här inställningen kan inte användas tillsammans med Använd anpassad DNS-server." @@ -2072,6 +2146,9 @@ msgstr "Anslut automatiskt och Låsningsläge" msgid "Auto-connect & \\nLockdown mode" msgstr "Anslut automatiskt och \\nLåsningsläge" +msgid "Auto-connect (legacy)" +msgstr "Anslut automatiskt (äldre)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Anslut automatiskt kallas Alltid på-VPN i Androids systeminställningar och ser till att du är ansluten till VPN-tunneln oavbrutet och ansluter automatiskt efter omstart." @@ -2093,6 +2170,12 @@ msgstr "Loggarna har kopierats till urklipp" msgid "Copied to clipboard" msgstr "Kopierat till urklipp" +msgid "Create" +msgstr "Skapa" + +msgid "Create new list" +msgstr "Skapa ny lista" + msgid "Critical error (your attention is required)" msgstr "Kritiskt fel (kräver din uppmärksamhet)" @@ -2102,11 +2185,32 @@ msgstr "Anpassade DNS-serveradresser %s är ogiltiga" msgid "DNS settings might not go into effect immediately" msgstr "DNS-inställningarna kanske inte börjar gälla direkt" +msgid "Delete \"%s\"?" +msgstr "Ta bort \"%s\"?" + msgid "Disable all %s above to activate this setting." msgstr "Inaktivera alla %s ovan för att aktivera inställningen." -msgid "Enable" -msgstr "Aktivera" +msgid "Discard" +msgstr "Ignorera" + +msgid "Discard changes?" +msgstr "Ignorera ändringarna?" + +msgid "Edit custom lists" +msgstr "Redigera anpassade listor" + +msgid "Edit list" +msgstr "Redigera lista" + +msgid "Edit lists" +msgstr "Redigera listor" + +msgid "Edit locations" +msgstr "Redigera platser" + +msgid "Edit name" +msgstr "Redigera namn" msgid "Enter MTU" msgstr "Ange MTU" @@ -2114,6 +2218,12 @@ msgstr "Ange MTU" msgid "Excluded applications" msgstr "Exkluderade applikationer" +msgid "Failed to apply patch" +msgstr "Det gick inte att tillämpa korrigering" + +msgid "File" +msgstr "Fil" + msgid "Go to VPN settings" msgstr "Gå till VPN-inställningar" @@ -2129,9 +2239,34 @@ msgstr "Google Play är inte tillgängligt" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Om delade tunnlar används, frågar appen ditt system om en lista med alla installerade applikationer. Listan hämtas bara i vyn för delade tunnlar. Listan med installerade applikationer skickas aldrig från enheten." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Importera nya åsidosättningar med" + +msgid "Import successful, overrides active" +msgstr "Har importerats, åsidosättningar aktiva" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Om du importerar nya åsidosättningar kan tidigare importerade åsidosättningar ersättas." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Installera Mullvad VPN (%s) för att hålla dig uppdaterad" +msgid "Invalid or missing value \"%s\"" +msgstr "Värdet \"%s\" är ogiltigt eller saknas" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "Låter dig välja appar som ska ge dig direktåtkomst till internet utan att gå igenom VPN-tunneln." + +msgid "List name" +msgstr "Listnamn" + +msgid "Locations" +msgstr "Platser" + +msgid "Locations were changed for \"%s\"" +msgstr "Platserna har ändrats för \"%s\"" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Ser till att enheten alltid är i VPN-tunneln." @@ -2144,9 +2279,43 @@ msgstr "Mullvad-kontonummer" msgid "Mullvad services unavailable" msgstr "Mullvad-tjänster är inte tillgängliga" +msgid "Name was changed to %s" +msgstr "Namnet har ändrats till %s" + +msgid "New list" +msgstr "Ny lista" + +msgid "No custom lists available" +msgstr "Inga anpassade listor är tillgängliga" + msgid "No internet connection" msgstr "Ingen internetanslutning" +msgid "No locations found" +msgstr "Inga platser hittades" + +msgid "Not found" +msgstr "Hittades inte" + +msgid "Overrides active" +msgstr "Åsidosättningar aktiva" + +msgid "Overrides cleared" +msgstr "Åsidosättningar rensade" + +msgid "Overrides inactive" +msgstr "Åsidosättningar inaktiva" + +msgid "Paste or write overrides to be imported" +msgstr "Klistra in eller skriv åsidosättningar som ska importeras" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Korrigering överensstämmer inte med specifikationen" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Använd systeminställningen Alltid aktiverat istället genom att följa guiden i %s ovan." + msgid "Preferences" msgstr "Inställningar" @@ -2156,18 +2325,33 @@ msgstr "Sekretess" msgid "Privacy policy" msgstr "Sekretesspolicy" +msgid "Recursion limit" +msgstr "Rekursionsgräns" + msgid "Remove" msgstr "Ta bort" msgid "Remove custom port" msgstr "Ta bort anpassad port" +msgid "Reset" +msgstr "Återställ" + +msgid "Reset all overrides" +msgstr "Återställ alla åsidosättningar" + +msgid "Reset overrides" +msgstr "Återställ åsidosättningar" + msgid "Reset to default" msgstr "Återställ till standard" msgid "Secured" msgstr "Skyddad" +msgid "Server Ip overrides" +msgstr "Åsidosättningar av server-IP" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "Ange WireGuard MTU-värde. Giltigt intervall: %d–%d." @@ -2183,12 +2367,12 @@ msgstr "Visar nuvarande status för VPN-tunnel" msgid "Shows reminders when the account time is about to expire" msgstr "Visar påminnelser när kontots tidsgräns uppnås" -msgid "Split tunneling is disabled." -msgstr "Delade tunnlar är inaktiverat." - msgid "Submit" msgstr "Skicka" +msgid "Text" +msgstr "Text" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Inställningar för Anslut automatiskt och Låsningsläge finns i Androids systeminställningar. Följ guiden för att aktivera en av dem eller båda." @@ -2204,6 +2388,12 @@ msgstr "Det finns inga VPN-inställningar på din enhet" msgid "This address has already been entered." msgstr "Adressen har redan angetts." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Om du vill lägga till platser i en lista kan du trycka på \"︙\" eller trycka länge på ett land, en stad eller en server." + +msgid "To create a custom list press the \"︙\"" +msgstr "Om du vill skapa en anpassad lista trycker du på \"︙\"" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Appen genomför automatiska versionskontroller för att se till att du har den säkraste versionen och för att informera dig om problem med den nuvarande versionen. Appversionen och Android-systemversionen skickas till Mullvad-servrarna. Mullvad registrerar antalet använda appversioner och Android-versioner. Dessa data lagras aldrig och används inte på något sätt som kan identifiera dig." @@ -2213,9 +2403,19 @@ msgstr "Växla VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Det går inte att tillämpa brandväggsregler. Felsök eller skicka en problemrapport." +msgid "Unable to parse patch" +msgstr "Det går inte att parsa korrigering" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Det går inte att starta tunnelanslutning. Aktivera VPN som alltid är på för %s innan du använder Mullvad VPN." +msgid "Undo" +msgstr "Ångra" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Okänd eller förbjuden nyckel \"%s\"" + msgid "Unsecured" msgstr "Oskyddad" @@ -2225,6 +2425,9 @@ msgstr "Uppdatera DNS-server" msgid "Update available, download to remain safe." msgstr "Uppdatering tillgänglig. Ladda ned för att vara säker." +msgid "Update list name" +msgstr "Uppdatera listnamn" + msgid "VPN permission error" msgstr "Behörighetsfel med VPN" @@ -2237,7 +2440,6 @@ msgstr "VPN-tunnelstatus" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Giltig WireGuard-nyckel saknas. Hantera nycklar i Avancerade inställningar." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Giltiga intervall: %s" @@ -2283,6 +2485,9 @@ msgstr "DU KANSKE HAR LÄCKAGE I NÄTVERKSTRAFIKEN" msgid "You are running an unsupported app version." msgstr "Du kör en appversion som inte stöds." +msgid "\"%s\" was deleted" +msgstr "\"%s\" har tagits bort" + msgid "less than a minute ago" msgstr "mindre än en minut sedan" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d dag" msgstr[1] "%d dagar" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d plats" +msgstr[1] "%d platser" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d månad" diff --git a/gui/locales/sv/relay-locations.po b/gui/locales/sv/relay-locations.po index 0d9a34dbfeb6..d60afb106182 100644 --- a/gui/locales/sv/relay-locations.po +++ b/gui/locales/sv/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Swedish\n" "Language: sv_SE\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Australien" msgid "Austria" msgstr "Österrike" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barcelona" + #. BE msgid "Belgium" msgstr "Belgien" @@ -188,6 +196,10 @@ msgstr "Frankfurt am Main" msgid "Germany" msgstr "Tyskland" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Lettland" msgid "Lisbon" msgstr "Lissabon" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "London" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marseille" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, TX" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slovakien" +#. SI +msgid "Slovenia" +msgstr "Slovenien" + #. BG SOF msgid "Sofia" msgstr "Sofia" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Thailand" + #. AL TIA msgid "Tirana" msgstr "Tirana" @@ -548,6 +576,10 @@ msgstr "Ukraina" msgid "United Arab Emirates" msgstr "Förenade Arabemiraten" +#. ES VLC +msgid "Valencia" +msgstr "Valencia" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/th/messages.po b/gui/locales/th/messages.po index 787a62493b41..25a3f451769c 100644 --- a/gui/locales/th/messages.po +++ b/gui/locales/th/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Thai\n" "Language: th_TH\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "มีการเพิ่ม %(duration)s แล้ว บัญชีได้ชำระเงินจนถึง %(expiry)s" @@ -133,6 +133,12 @@ msgstr "กำลังตัดการเชื่อมต่อ" msgid "Dismiss" msgstr "ละทิ้ง" +msgid "Edit" +msgstr "แก้ไข" + +msgid "Enable" +msgstr "เปิดใช้งาน" + msgid "Enable anyway" msgstr "เปิดใช้งานต่อไป" @@ -225,6 +231,11 @@ msgstr "ค่าเริ่มต้นของระบบ" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "ทดสอบ" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "การตั้งค่านี้จะเพิ่มความหน่วงแฝง ใช้เมื่อจำเป็นเท่านั้น" @@ -247,6 +258,9 @@ msgstr "ปลดบล็อก" msgid "UNSECURED CONNECTION" msgstr "การเชื่อมต่อที่ไม่ปลอดภัย" +msgid "Use" +msgstr "ใช้" + msgid "Username" msgstr "ชื่อผู้ใช้" @@ -382,9 +396,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "การรับรองความถูกต้อง" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "การคลิก “บันทึก” จะเปลี่ยนวิธีที่ใช้งานอยู่" +msgid "Clicking “%(save)s” changes the in use method." +msgstr "การคลิก “%(save)s” จะเปลี่ยนวิธีที่ใช้งานอยู่" msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -627,6 +642,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s ผ่าน %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s ผ่าน Bridge แบบกำหนดเอง" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -643,6 +662,22 @@ msgctxt "connection-info" msgid "Out" msgstr "ออก" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "เพิ่ม Bridge แบบกำหนดเอง" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "ลบ Bridge แบบกำหนดเองหรือไม่" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "การลบ Bridge แบบกำหนดเอง จะนำคุณกำลังไปยังมุมมองตำแหน่งที่เลือก และตัวเลือกอัตโนมัติจะถูกเลือกแทน" + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "แก้ไข Bridge แบบกำหนดเอง" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1151,6 +1186,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "โหมดบริดจ์" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "เปิดใช้โหมด Bridge หรือไม่" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1197,6 +1236,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "ในการเปิดใช้งาน UDP ให้เปลี่ยน โหมดบริดจ์ เป็น อัตโนมัติ หรือ ปิด" +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "ไปที่มุมมองตำแหน่งที่เลือก เพื่อเลือกเซิร์ฟเวอร์ Bridge เฉพาะ" + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "โพรโทคอลการส่งข้อมูล" @@ -1260,6 +1305,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "เซิร์ฟเวอร์ Bridge แบบกำหนดเองสามารถนำมาใช้เพื่อหลีกเลี่ยงการเซ็นเซอร์ได้ เมื่อเซิร์ฟเวอร์บริดจ์ Mullvad ตามปกติไม่ทำงาน" + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1279,6 +1328,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "ประเทศ" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Bridge แบบกำหนดเอง" + msgctxt "select-location-view" msgid "Custom lists" msgstr "รายการกำหนดเอง" @@ -1341,7 +1394,7 @@ msgstr "ล้างโอเวอร์ไรด์ทั้งหมดหร msgctxt "settings-import" msgid "Clearing the imported overrides changes the server IPs, in the Select location view, back to default." -msgstr "การล้างการโอเวอร์ไรด์ที่นำเข้า จะเปลี่ยน IP ของเซิร์ฟเวอร์ ในมุมมองเลือกตำแหน่งที่ตั้ง ให้กลับไปเป็นค่าเริ่มต้น" +msgstr "การล้างโอเวอร์ไรด์ที่นำเข้า จะเป็นการเปลี่ยน IP ของเซิร์ฟเวอร์ ในมุมมองเลือกตำแหน่งที่ตั้ง ให้กลับไปเป็นค่าเริ่มต้น" msgctxt "settings-import" msgid "If you are having issues connecting to VPN servers, please contact support." @@ -1396,7 +1449,7 @@ msgstr "เปิดใช้โอเวอร์ไรด์อยู่" #. users import server IP settings. msgctxt "settings-import" msgid "Server IP override" -msgstr "โอเวอร์ไรด์เซิร์ฟเวอร์ IP" +msgstr "โอเวอร์ไรด์ IP เซิร์ฟเวอร์" msgctxt "settings-import" msgid "To circumvent this you can import a file or a text, provided by our support team, with new IP addresses that override the default addresses of the servers in the Select location view." @@ -1848,7 +1901,7 @@ msgstr "มัลแวร์" msgctxt "vpn-settings-view" msgid "Server IP override" -msgstr "โอเวอร์ไรด์เซิร์ฟเวอร์ IP" +msgstr "โอเวอร์ไรด์ IP เซิร์ฟเวอร์" #. Label for settings that enables block of social media. msgctxt "vpn-settings-view" @@ -2008,7 +2061,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "พอร์ต TCP ใดที่โพรโทคอลการทำให้ข้อมูลยุ่งเหยิง UDP-ผ่าน-TCP ควรเชื่อมต่อบนเซิร์ฟเวอร์ VPN" -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (เพิ่มแล้ว)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s ถูกเพิ่มลงใน \"%s\"" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s ถูกเพิ่มลงในบัญชีของคุณแล้ว" @@ -2033,6 +2094,10 @@ msgstr "เครดิตของบัญชีจะหมดอายุใ msgid "Account time reminders" msgstr "การแจ้งเตือนเวลาบัญชี" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "เพิ่ม %s ลงในรายการ" + msgid "Add 30 days time" msgstr "เพิ่มเวลา 30 วัน" @@ -2042,12 +2107,18 @@ msgstr "เพิ่มเวลา 30 วัน (%s)" msgid "Add DNS server" msgstr "เพิ่มเซิร์ฟเวอร์ DNS" +msgid "Add locations" +msgstr "เพิ่มตำแหน่งที่ตั้ง" + msgid "Agree and continue" msgstr "ยอมรับและดำเนินการต่อ" msgid "All applications" msgstr "แอปพลิเคชันทั้งหมด" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "โอเวอร์ไรด์ทั้งหมดจะถูกรีเซ็ต และที่อยู่ IP ของเซิร์ฟเวอร์ในมุมมองตำแหน่งที่ตั้งที่เลือก จะกลับไปเป็นค่าเริ่มต้น" + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "อนุญาตให้เข้าถึงอุปกรณ์อื่นๆ บนเครือข่ายเดียวกัน เพื่อแชร์ พิมพ์ ฯลฯ" @@ -2057,6 +2128,9 @@ msgstr "Always-on VPN ได้รับการมอบหมายไปย msgid "Always-on VPN might be enabled for another app" msgstr "Always-on VPN อาจได้รับการเปิดใช้งานสำหรับแอปอื่น" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "โปรดทราบ: การแยกอุโมงค์เป็นความเสี่ยงด้านความเป็นส่วนตัว" + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "โปรดทราบ: การตั้งค่านี้ไม่สามารถใช้ร่วมกับการใช้เซิร์ฟเวอร์ DNS แบบกำหนดเองได้" @@ -2066,6 +2140,9 @@ msgstr "เชื่อมต่ออัตโนมัติและโหม msgid "Auto-connect & \\nLockdown mode" msgstr "เชื่อมต่ออัตโนมัติและ\\nโหมดล็อกดาวน์" +msgid "Auto-connect (legacy)" +msgstr "เชื่อมต่ออัตโนมัติ (เดิม)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "การเชื่อมต่ออัตโนมัติเรียกว่า Always-on VPN ในการตั้งค่าระบบ Android ซึ่งจะช่วยให้คุณมั่นใจได้ว่า คุณเชื่อมต่อกับช่องทาง VPN อย่างต่อเนื่อง และมีการเชื่อมต่ออัตโนมัติหลังจากรีสตาร์ต" @@ -2087,6 +2164,12 @@ msgstr "คัดลอกบันทึกล็อกไปยังคลิ msgid "Copied to clipboard" msgstr "คัดลอกไปยังคลิปบอร์ดแล้ว" +msgid "Create" +msgstr "สร้าง" + +msgid "Create new list" +msgstr "สร้างรายการใหม่" + msgid "Critical error (your attention is required)" msgstr "ข้อผิดพลาดร้ายแรง (คุณจำเป็นต้องตรวจสอบ)" @@ -2096,11 +2179,32 @@ msgstr "ที่อยู่เซิร์ฟเวอร์ DNS %s ที่ msgid "DNS settings might not go into effect immediately" msgstr "การตั้งค่า DNS อาจไม่มีผลทันที" +msgid "Delete \"%s\"?" +msgstr "ลบ \"%s\" หรือไม่" + msgid "Disable all %s above to activate this setting." msgstr "ปิดใช้งาน %s ด้านบนทั้งหมด เพื่อเปิดใช้การตั้งค่านี้" -msgid "Enable" -msgstr "เปิดใช้งาน" +msgid "Discard" +msgstr "ละทิ้ง" + +msgid "Discard changes?" +msgstr "ละทิ้งการเปลี่ยนแปลงหรือไม่" + +msgid "Edit custom lists" +msgstr "แก้ไขรายการแบบกำหนดเอง" + +msgid "Edit list" +msgstr "แก้ไขรายการ" + +msgid "Edit lists" +msgstr "แก้ไขรายการ" + +msgid "Edit locations" +msgstr "แก้ไขตำแหน่งที่ตั้ง" + +msgid "Edit name" +msgstr "แก้ไขชื่อ" msgid "Enter MTU" msgstr "ป้อน MTU" @@ -2108,6 +2212,12 @@ msgstr "ป้อน MTU" msgid "Excluded applications" msgstr "แอปพลิเคชันที่แยกออก" +msgid "Failed to apply patch" +msgstr "ล้มเหลวในการปรับใช้แพตช์" + +msgid "File" +msgstr "ไฟล์" + msgid "Go to VPN settings" msgstr "ไปที่การตั้งค่า VPN" @@ -2123,9 +2233,34 @@ msgstr "Google Play ไม่พร้อมใช้งาน" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "หากมีการใช้งานคุณลักษณะการแยกอุโมงค์ แอปจะขอให้ระบบของคุณระบุแอปพลิเคชันที่ติดตั้งไว้ทั้งหมด รายการนี้จะได้รับการเรียกใช้งานในมุมมองการแยกอุโมงค์เท่านั้น รายการแอปพลิเคชันที่ติดตั้งไว้ทั้งหมด จะไม่ถูกส่งออกจากอุปกรณ์" +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "นำเข้าโอเวอร์ไรด์ใหม่โดยใช้" + +msgid "Import successful, overrides active" +msgstr "นำเข้าสำเร็จ เปิดใช้โอเวอร์ไรด์อยู่" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "การนำเข้าโอเวอร์ไรด์ใหม่ อาจแทนที่โอเวอร์ไรด์ที่นำเข้าก่อนหน้านี้" + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "ติดตั้ง Mullvad VPN (%s) เพื่อรับอัปเดตล่าสุดอยู่เสมอ" +msgid "Invalid or missing value \"%s\"" +msgstr "ค่า \"%s\" ไม่ถูกต้องหรือหายไป" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "ช่วยให้คุณเลือกแอปที่เข้าถึงอินเทอร์เน็ตได้โดยตรง โดยไม่ต้องผ่านอุโมงค์ VPN" + +msgid "List name" +msgstr "ชื่อรายการ" + +msgid "Locations" +msgstr "ตำแหน่งที่ตั้ง" + +msgid "Locations were changed for \"%s\"" +msgstr "ตำแหน่งที่ตั้งสำหรับ \"%s\" มีการเปลี่ยนแปลง" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "ตรวจสอบให้แน่ใจว่า อุปกรณ์อยู่ในช่องทาง VPN เสมอ" @@ -2138,9 +2273,43 @@ msgstr "หมายเลขบัญชี Mullvad" msgid "Mullvad services unavailable" msgstr "บริการ Mullavad ไม่พร้อมใช้งาน" +msgid "Name was changed to %s" +msgstr "ชื่อถูกเปลี่ยนเป็น %s" + +msgid "New list" +msgstr "รายการใหม่" + +msgid "No custom lists available" +msgstr "ไม่มีรายการแบบกำหนดเองที่พร้อมใช้งาน" + msgid "No internet connection" msgstr "ไม่มีการเชื่อมต่ออินเทอร์เน็ต" +msgid "No locations found" +msgstr "ไม่พบตำแหน่งที่ตั้ง" + +msgid "Not found" +msgstr "ไม่พบ" + +msgid "Overrides active" +msgstr "เปิดใช้โอเวอร์ไรด์อยู่" + +msgid "Overrides cleared" +msgstr "ล้างโอเวอร์ไรด์แล้ว" + +msgid "Overrides inactive" +msgstr "ปิดใช้โอเวอร์ไรด์อยู่" + +msgid "Paste or write overrides to be imported" +msgstr "วางหรือพิมพ์โอเวอร์ไรด์เพื่อนำเข้า" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "แพตช์ไม่ตรงกับข้อมูลจำเพาะ" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "โปรดใช้การตั้งค่าระบบเปิดใช้งานเสมอแทน โดยทำตามคำแนะนำใน %s ที่อยู่ด้านบน" + msgid "Preferences" msgstr "การกำหนดค่า" @@ -2150,18 +2319,33 @@ msgstr "ความเป็นส่วนตัว" msgid "Privacy policy" msgstr "นโยบายความเป็นส่วนตัว" +msgid "Recursion limit" +msgstr "ขีดจำกัดการเรียกซ้ำ" + msgid "Remove" msgstr "ลบ" msgid "Remove custom port" msgstr "นำพอร์ตแบบกำหนดเองออก" +msgid "Reset" +msgstr "รีเซ็ต" + +msgid "Reset all overrides" +msgstr "รีเซ็ตโอเวอร์ไรด์ทั้งหมด" + +msgid "Reset overrides" +msgstr "รีเซ็ตโอเวอร์ไรด์" + msgid "Reset to default" msgstr "รีเซ็ตเป็นค่าเริ่มต้น" msgid "Secured" msgstr "ปลอดภัย" +msgid "Server Ip overrides" +msgstr "โอเวอร์ไรด์ IP เซิร์ฟเวอร์" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "ตั้งค่า WireGuard MTU ช่วงที่ใช้ได้: %d - %d" @@ -2177,12 +2361,12 @@ msgstr "แสดงสถานะอุโมงค์ VPN ในปัจจ msgid "Shows reminders when the account time is about to expire" msgstr "แสดงการแจ้งเตือน ในขณะที่เวลาบัญชีใกล้หมดอายุ" -msgid "Split tunneling is disabled." -msgstr "ปิดใช้งานการแยกอุโมงค์อยู่" - msgid "Submit" msgstr "ส่ง" +msgid "Text" +msgstr "ข้อความ" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "การตั้งค่าการเชื่อมต่ออัตโนมัติและโหมดล็อกดาวน์ อยู่ในการตั้งค่าระบบ Android โปรดทำตามคำแนะนำนี้ เพื่อเปิดใช้งานโหมดใดโหมดหนึ่ง หรือทั้งสองโหมด" @@ -2198,6 +2382,12 @@ msgstr "ไม่มีการตั้งค่า VPN บนอุปกร msgid "This address has already been entered." msgstr "ที่อยู่นี้ได้รับการป้อนไปแล้ว" +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "กด \"︙\" หรือกดประเทศ เมือง หรือเซิร์ฟเวอร์ค้างไว้ เพื่อเพิ่มตำแหน่งที่ตั้งลงในรายการ" + +msgid "To create a custom list press the \"︙\"" +msgstr "กด \"︙\" เพื่อสร้างรายการแบบกำหนดเอง" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "แอปจะตรวจสอบเวอร์ชันโดยอัตโนมัติ เพื่อให้แน่ใจว่า คุณใช้งานเวอร์ชันที่ปลอดภัยที่สุด และเพื่อแจ้งปัญหาใดๆ ของเวอร์ชันปัจจุบันที่ใช้งานอยู่ให้คุณทราบ โดยระบบจะส่งเวอร์ชันแอปและเวอร์ชันระบบ Android ไปยังเซิร์ฟเวอร์ Mullavid ซึ่ง Mullvad จะติดตามจำนวนเวอร์ชันแอปที่ใช้และเวอร์ชัน Android ทั้งนี้ข้อมูลจะไม่ถูกจัดเก็บ หรือนำไปใช้ในลักษณะใดๆ ที่สามารถระบุตัวตนคุณได้" @@ -2207,9 +2397,19 @@ msgstr "เปิด/ปิด VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "ไม่สามารถใช้กฎไฟร์วอลล์ได้ โปรดแก้ไขปัญหา หรือส่งรายงานปัญหา" +msgid "Unable to parse patch" +msgstr "ไม่สามารถแยกวิเคราะห์แพตช์ได้" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "ไม่สามารถเริ่มการเชื่อมต่ออุโมงค์ได้ โปรดปิดใช้งาน Always-on VPN เป็นเวลา %s ก่อนที่จะใช้งาน Mullvad VPN" +msgid "Undo" +msgstr "เลิกทำ" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "\"%s\" ที่ไม่รู้จัก หรือมีคีย์ต้องห้าม" + msgid "Unsecured" msgstr "ไม่ปลอดภัย" @@ -2219,6 +2419,9 @@ msgstr "อัปเดตเซิร์ฟเวอร์ DNS" msgid "Update available, download to remain safe." msgstr "มีอัปเดตพร้อมใช้งาน ดาวน์โหลดเพื่อคงความปลอดภัยไว้" +msgid "Update list name" +msgstr "อัปเดตชื่อรายการ" + msgid "VPN permission error" msgstr "เกิดข้อผิดพลาดในการอนุญาต VPN" @@ -2231,7 +2434,6 @@ msgstr "สถานะอุโมงค์ VPN" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "คีย์ WireGuard ที่ใช้ได้ขาดหายไป จัดการคีย์ภายใต้การตั้งค่าขั้นสูง" -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "ช่วงที่ใช้ได้: %s" @@ -2277,6 +2479,9 @@ msgstr "คุณอาจมีการรับส่งข้อมูลท msgid "You are running an unsupported app version." msgstr "คุณกำลังใช้งานเวอร์ชันแอปที่ไม่ได้รับการสนับสนุน" +msgid "\"%s\" was deleted" +msgstr "\"%s\" ถูกลบแล้ว" + msgid "less than a minute ago" msgstr "น้อยกว่าหนึ่งนาทีก่อน" @@ -2287,6 +2492,10 @@ msgid "%d day" msgid_plural "%d days" msgstr[0] "%d วัน" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d ตำแหน่งที่ตั้ง" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d เดือน" diff --git a/gui/locales/th/relay-locations.po b/gui/locales/th/relay-locations.po index f6f080fb4e51..a7420a60f763 100644 --- a/gui/locales/th/relay-locations.po +++ b/gui/locales/th/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Thai\n" "Language: th_TH\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "ออสเตรเลีย" msgid "Austria" msgstr "ออสเตรีย" +#. TH BKK +msgid "Bangkok" +msgstr "กรุงเทพ" + +#. ES BCN +msgid "Barcelona" +msgstr "บาร์เซโลนา" + #. BE msgid "Belgium" msgstr "เบลเยียม" @@ -188,6 +196,10 @@ msgstr "แฟรงก์เฟิร์ต" msgid "Germany" msgstr "เยอรมนี" +#. GB GLW +msgid "Glasgow" +msgstr "กลาสโกว์" + #. SE GOT msgid "Gothenburg" msgstr "กอเทนเบิร์ก" @@ -264,6 +276,10 @@ msgstr " ลัตเวีย" msgid "Lisbon" msgstr "ลิสบอน" +#. SI LJU +msgid "Ljubljana" +msgstr "ลูบลิยานา" + #. GB LON msgid "London" msgstr "ลอนดอน" @@ -296,6 +312,10 @@ msgstr "แมนเชสเตอร์" msgid "Marseille" msgstr "มาร์แซย์" +#. US TXC +msgid "McAllen, TX" +msgstr "แมคอัลเลน เท็กซัส" + #. AU MEL msgid "Melbourne" msgstr "เมลเบิร์น" @@ -472,6 +492,10 @@ msgstr "สโกเปีย" msgid "Slovakia" msgstr "สโลวาเกีย" +#. SI +msgid "Slovenia" +msgstr "สโลวีเนีย" + #. BG SOF msgid "Sofia" msgstr "โซเฟีย" @@ -520,6 +544,10 @@ msgstr "ทาลลินน์" msgid "Tel Aviv" msgstr "เทลอาวีฟ" +#. TH +msgid "Thailand" +msgstr "ไทย" + #. AL TIA msgid "Tirana" msgstr "ติรานา" @@ -548,6 +576,10 @@ msgstr "ยูเครน" msgid "United Arab Emirates" msgstr "สหรัฐอาหรับเอมิเรตส์" +#. ES VLC +msgid "Valencia" +msgstr "บาเลนเซีย" + #. CA VAN msgid "Vancouver" msgstr "แวนคูเวอร์" diff --git a/gui/locales/tr/messages.po b/gui/locales/tr/messages.po index da22f94ed53d..b6fb5057e81c 100644 --- a/gui/locales/tr/messages.po +++ b/gui/locales/tr/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Turkish\n" "Language: tr_TR\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "%(duration)s eklendi, hesap için %(expiry)s tarihine kadar ödeme yapıldı." @@ -139,6 +139,12 @@ msgstr "Bağlantı kesiliyor" msgid "Dismiss" msgstr "Reddet" +msgid "Edit" +msgstr "Düzenle" + +msgid "Enable" +msgstr "Etkinleştir" + msgid "Enable anyway" msgstr "Yine de etkinleştir" @@ -231,6 +237,11 @@ msgstr "Sistem varsayılanı" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "Test et" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "Bu ayar gecikmeyi artırır. Sadece gerekiyorsa kullanın." @@ -253,6 +264,9 @@ msgstr "Engeli kaldır" msgid "UNSECURED CONNECTION" msgstr "GÜVENLİ OLMAYAN BAĞLANTI" +msgid "Use" +msgstr "Kullan" + msgid "Username" msgstr "Kullanıcı adı" @@ -388,9 +402,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "Yetkilendirme" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "\"Kaydet\" düğmesine tıklamak kullanımdaki yöntemi değiştirir." +msgid "Clicking “%(save)s” changes the in use method." +msgstr "\"%(save)s\" düğmesine tıklamak kullanılan yöntemi değiştirir." msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -633,6 +648,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(entry)s aracılığıyla %(relay)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "Özel köprü üzerinden %(relay)s" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -649,6 +668,22 @@ msgctxt "connection-info" msgid "Out" msgstr "Çıkış" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "Özel köprü ekle" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "Özel köprü silinsin mi?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "Özel köprünün silinmesi sizi konum seçme görünümüne geri götürür ve bunun yerine Otomatik seçeneği belirlenir." + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "Özel köprüyü düzenle" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1157,6 +1192,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "Köprü modu" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "Köprü modu etkinleştirilsin mi?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1203,6 +1242,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "UDP'yi etkinleştirmek için Köprü modu'nu Otomatik veya Kapalı olarak değiştirin." +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "Belirli bir köprü sunucusunu seçmek için Konum seç görünümüne gidin." + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "Taşıma protokolü" @@ -1266,6 +1311,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "Normal Mullvad köprü sunucuları çalışmadığında sansürü aşmak için özel bir köprü sunucusu kullanılabilir." + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1285,6 +1334,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "Ülke" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "Özel köprü" + msgctxt "select-location-view" msgid "Custom lists" msgstr "Özel listeler" @@ -2014,9 +2067,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "TCP üzerinden UDP gizleme protokolünün VPN sunucusunda hangi TCP portuna bağlanması gerekiyor." -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (eklendi)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s, \"%s\" listesine eklendi" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "Hesabınıza %s eklendi." +msgstr "%s, hesabınıza eklendi." msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. Aşağıdaki VPN ayarlarına git düğmesine tıkladıktan sonra Mullvad VPN'nin yanındaki dişli çarka tıklayın." @@ -2039,6 +2100,10 @@ msgstr "Hesap kredisinin süresi yakında doluyor" msgid "Account time reminders" msgstr "Hesap süresi hatırlatıcıları" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "%s konumunu listeye ekle" + msgid "Add 30 days time" msgstr "30 gün süre ekleyin" @@ -2048,12 +2113,18 @@ msgstr "30 gün süre ekleyin (%s)" msgid "Add DNS server" msgstr "DNS sunucusu ekle" +msgid "Add locations" +msgstr "Konum ekle" + msgid "Agree and continue" msgstr "Kabul et ve devam et" msgid "All applications" msgstr "Tüm uygulamalar" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "Tüm geçersiz kılmalar sıfırlanacak ve Konum seç görünümündeki sunucuların IP adresleri varsayılana döndürülecek." + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "Paylaşım, yazdırma gibi özellikler için aynı ağdaki diğer cihazlara erişim izni verir." @@ -2063,6 +2134,9 @@ msgstr "Her zaman açık VPN başka bir uygulamaya atandı" msgid "Always-on VPN might be enabled for another app" msgstr "Her zaman açık VPN başka bir uygulama için etkinleştirilebilir" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "Dikkat: Bölünmüş tünelleme gizlilik riski taşır." + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "Dikkat: Bu ayar Özel DNS sunucusu kullan seçeneği ile birlikte kullanılamaz." @@ -2072,6 +2146,9 @@ msgstr "Otomatik bağlantı ve Kilitleme modu" msgid "Auto-connect & \\nLockdown mode" msgstr "Otomatik bağlantı ve \\nKilitleme modu" +msgid "Auto-connect (legacy)" +msgstr "Otomatik bağlan (eski)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "Otomatik bağlantı, Android sistem ayarlarında VPN her zaman açık olarak gösterilir ve VPN tüneline sürekli bağlı olmanızı ve yeniden başlatma sonrası otomatik olarak bağlanmanızı sağlar." @@ -2093,6 +2170,12 @@ msgstr "Günlükler panoya kopyalandı" msgid "Copied to clipboard" msgstr "Panoya kopyalandı" +msgid "Create" +msgstr "Oluştur" + +msgid "Create new list" +msgstr "Yeni liste oluştur" + msgid "Critical error (your attention is required)" msgstr "Kritik hata (Lütfen dikkatli olun)" @@ -2102,11 +2185,32 @@ msgstr "Özel DNS sunucu adresleri (%s) geçersiz" msgid "DNS settings might not go into effect immediately" msgstr "DNS ayarları hemen etkili olmayabilir" +msgid "Delete \"%s\"?" +msgstr "\"%s\" silinsin mi\"?" + msgid "Disable all %s above to activate this setting." msgstr "Bu ayarı etkinleştirmek için yukarıdaki %s öğelerinin tümünü devre dışı bırakın." -msgid "Enable" -msgstr "Etkinleştir" +msgid "Discard" +msgstr "İptal et" + +msgid "Discard changes?" +msgstr "Değişiklikler iptal edilsin mi?" + +msgid "Edit custom lists" +msgstr "Özel listeleri düzenle" + +msgid "Edit list" +msgstr "Listeyi düzenle" + +msgid "Edit lists" +msgstr "Listeleri düzenle" + +msgid "Edit locations" +msgstr "Konumları düzenle" + +msgid "Edit name" +msgstr "Adı düzenle" msgid "Enter MTU" msgstr "MTU'yu girin" @@ -2114,6 +2218,12 @@ msgstr "MTU'yu girin" msgid "Excluded applications" msgstr "Hariç tutulan uygulamalar" +msgid "Failed to apply patch" +msgstr "Yama uygulanamadı" + +msgid "File" +msgstr "Dosya" + msgid "Go to VPN settings" msgstr "VPN ayarlarına git" @@ -2129,9 +2239,34 @@ msgstr "Google Play kullanılamıyor" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "Bölünmüş tünelleme özelliği kullanılıyorsa uygulama, yüklü tüm uygulamaların bir listesi için sisteminizi sorgular. Bu liste yalnızca bölünmüş tünelleme görünümünde alınır. Yüklü uygulamaların listesi hiçbir zaman cihazdan gönderilmez." +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "Şuradaki yeni geçersiz kılmaları içe aktar:" + +msgid "Import successful, overrides active" +msgstr "İçe aktarma başarılı, geçersiz kılmalar etkin" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "Yeni geçersiz kılmaların içe aktarılması, önceden içe aktarılan bazı geçersiz kılmaların yerine geçebilir." + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "Güncel kalmak için Mullvad VPN (%s) yükleyin" +msgid "Invalid or missing value \"%s\"" +msgstr "Geçersiz veya eksik \"%s\" değeri" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "VPN tünelinden geçmeden doğrudan internete erişmesi gereken uygulamaları seçmenizi sağlar." + +msgid "List name" +msgstr "Liste adı" + +msgid "Locations" +msgstr "Konumlar" + +msgid "Locations were changed for \"%s\"" +msgstr "\"%s\" için konumlar değiştirildi" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "Cihazın her zaman VPN tünelinde olduğundan emin olun." @@ -2144,9 +2279,43 @@ msgstr "Mullvad hesap numarası" msgid "Mullvad services unavailable" msgstr "Mullvad hizmetleri kullanılamıyor" +msgid "Name was changed to %s" +msgstr "Ad, %s olarak değiştirildi" + +msgid "New list" +msgstr "Yeni liste" + +msgid "No custom lists available" +msgstr "Özel listeler mevcut değil" + msgid "No internet connection" msgstr "İnternet bağlantısı yok" +msgid "No locations found" +msgstr "Konum bulunamadı" + +msgid "Not found" +msgstr "Bulunamadı" + +msgid "Overrides active" +msgstr "Geçersiz kılmalar etkin" + +msgid "Overrides cleared" +msgstr "Geçersiz kılmalar temizlendi" + +msgid "Overrides inactive" +msgstr "Geçersiz kılmalar etkin değil" + +msgid "Paste or write overrides to be imported" +msgstr "İçe aktarılacak geçersiz kılmaları yapıştırın veya yazın" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "Yama spesifikasyonla eşleşmiyor" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "Lütfen yukarıdaki %s bölümündeki kılavuzu izleyerek Her zaman açık sistem ayarını kullanın." + msgid "Preferences" msgstr "Tercihler" @@ -2156,18 +2325,33 @@ msgstr "Gizlilik" msgid "Privacy policy" msgstr "Gizlilik politikası" +msgid "Recursion limit" +msgstr "Yineleme sınırı" + msgid "Remove" msgstr "Kaldır" msgid "Remove custom port" msgstr "Özel portu kaldır" +msgid "Reset" +msgstr "Sıfırla" + +msgid "Reset all overrides" +msgstr "Tüm geçersiz kılmaları sıfırla" + +msgid "Reset overrides" +msgstr "Geçersiz kılmaları sıfırla" + msgid "Reset to default" msgstr "Varsayılana sıfırla" msgid "Secured" msgstr "Güvenli" +msgid "Server Ip overrides" +msgstr "Sunucu IP geçersiz kılmaları" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "WireGuard MTU değerini ayarlayın. Geçerli aralık: %d - %d." @@ -2183,12 +2367,12 @@ msgstr "Mevcut VPN tünelinin durumunu gösterir" msgid "Shows reminders when the account time is about to expire" msgstr "Hesap süresinin dolmak üzere olduğunu bildiren hatırlatıcıları gösterir" -msgid "Split tunneling is disabled." -msgstr "Bölünmüş tünelleme devre dışı." - msgid "Submit" msgstr "Gönder" +msgid "Text" +msgstr "Metin" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "Otomatik Bağlantı ve Kilitleme modu ayarları Android sistem ayarlarında yer alır. Bu ayarlardan birini veya her ikisini de etkinleştirmek için bu kılavuza göz atın." @@ -2204,6 +2388,12 @@ msgstr "Cihazınızda VPN ayarları belirlenmemiş" msgid "This address has already been entered." msgstr "Bu adres zaten girilmiş." +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "Listeye konum eklemek için \"︙\" düğmesine basın veya bir ülke, şehir veya sunucunun üzerine uzun basın." + +msgid "To create a custom list press the \"︙\"" +msgstr "Özel bir liste oluşturmak için \"︙\" düğmesine basın" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "Uygulama, en güvenli sürüme sahip olduğunuzdan emin olmak ve çalışan mevcut sürümle ilgili sorunları size bildirmek için sürüm kontrollerini otomatik olarak gerçekleştirir. Bu işlem, uygulama sürümünü ve Android sistem sürümünü Mullvad sunucularına gönderir. Mullvad, kullanılan uygulama sürümlerinin ve Android sürümlerinin numarasını kaydeder. Veriler asla sizi tanımlayacak şekilde saklanmaz veya kullanılmaz." @@ -2213,9 +2403,19 @@ msgstr "VPN'i aç/kapat" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "Güvenlik duvarı kuralları uygulanamıyor. Lütfen sorunu çözmeye çalışın veya bir hata raporu gönderin." +msgid "Unable to parse patch" +msgstr "Yama ayrıştırılamıyor" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "Tünel bağlantısı başlatılamıyor. Mullvad VPN'i kullanmadan önce lütfen Her zaman açık VPN'i %s için devre dışı bırakın." +msgid "Undo" +msgstr "Geri al" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "Bilinmeyen veya yasaklanmış \"%s\" anahtarı" + msgid "Unsecured" msgstr "Güvenli değil" @@ -2225,6 +2425,9 @@ msgstr "DNS sunucusunu güncelle" msgid "Update available, download to remain safe." msgstr "Güncelleme mevcut, güvende kalmak için güncellemeyi indirin." +msgid "Update list name" +msgstr "Liste adını güncelle" + msgid "VPN permission error" msgstr "VPN izin hatası" @@ -2237,7 +2440,6 @@ msgstr "VPN tüneli durumu" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "Geçerli WireGuard anahtarı eksik. Gelişmiş ayarlardan anahtarları yönetin." -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "Geçerli aralıklar: %s" @@ -2283,6 +2485,9 @@ msgstr "AĞ TRAFİĞİNİZDE SIZINTI OLABİLİR" msgid "You are running an unsupported app version." msgstr "Desteklenmeyen bir uygulama sürümünü kullanıyorsunuz." +msgid "\"%s\" was deleted" +msgstr "\"%s\" silindi" + msgid "less than a minute ago" msgstr "bir dakikadan az" @@ -2294,6 +2499,11 @@ msgid_plural "%d days" msgstr[0] "%d gün" msgstr[1] "%d gün" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d konum" +msgstr[1] "%d konum" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d ay" diff --git a/gui/locales/tr/relay-locations.po b/gui/locales/tr/relay-locations.po index 1c2fe67c3cef..8d837d831323 100644 --- a/gui/locales/tr/relay-locations.po +++ b/gui/locales/tr/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Turkish\n" "Language: tr_TR\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "Avustralya" msgid "Austria" msgstr "Avusturya" +#. TH BKK +msgid "Bangkok" +msgstr "Bangkok" + +#. ES BCN +msgid "Barcelona" +msgstr "Barselona" + #. BE msgid "Belgium" msgstr "Belçika" @@ -188,6 +196,10 @@ msgstr "Frankfurt" msgid "Germany" msgstr "Almanya" +#. GB GLW +msgid "Glasgow" +msgstr "Glasgow" + #. SE GOT msgid "Gothenburg" msgstr "Göteborg" @@ -264,6 +276,10 @@ msgstr "Letonya" msgid "Lisbon" msgstr "Lizbon" +#. SI LJU +msgid "Ljubljana" +msgstr "Ljubljana" + #. GB LON msgid "London" msgstr "Londra" @@ -296,6 +312,10 @@ msgstr "Manchester" msgid "Marseille" msgstr "Marsilya" +#. US TXC +msgid "McAllen, TX" +msgstr "McAllen, Teksas" + #. AU MEL msgid "Melbourne" msgstr "Melbourne" @@ -472,6 +492,10 @@ msgstr "Skopje" msgid "Slovakia" msgstr "Slovakya" +#. SI +msgid "Slovenia" +msgstr "Slovenya" + #. BG SOF msgid "Sofia" msgstr "Sofya" @@ -520,6 +544,10 @@ msgstr "Tallinn" msgid "Tel Aviv" msgstr "Tel Aviv" +#. TH +msgid "Thailand" +msgstr "Tayland" + #. AL TIA msgid "Tirana" msgstr "Tiran" @@ -548,6 +576,10 @@ msgstr "Ukrayna" msgid "United Arab Emirates" msgstr "Birleşik Arap Emirlikleri" +#. ES VLC +msgid "Valencia" +msgstr "Valensiya" + #. CA VAN msgid "Vancouver" msgstr "Vancouver" diff --git a/gui/locales/zh-CN/messages.po b/gui/locales/zh-CN/messages.po index cbc0220ac1a3..1805ab97c422 100644 --- a/gui/locales/zh-CN/messages.po +++ b/gui/locales/zh-CN/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "已添加 %(duration)s,帐户到期时间为 %(expiry)s。" @@ -133,6 +133,12 @@ msgstr "正在断开连接" msgid "Dismiss" msgstr "关闭" +msgid "Edit" +msgstr "编辑" + +msgid "Enable" +msgstr "启用" + msgid "Enable anyway" msgstr "仍然启用" @@ -225,6 +231,11 @@ msgstr "系统默认值" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "测试" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "此设置会增大延迟。仅在需要时使用。" @@ -247,6 +258,9 @@ msgstr "解除阻止" msgid "UNSECURED CONNECTION" msgstr "未受保护的连接" +msgid "Use" +msgstr "使用" + msgid "Username" msgstr "用户名" @@ -382,9 +396,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "身份验证" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "点击“保存”更改正在使用的方法。" +msgid "Clicking “%(save)s” changes the in use method." +msgstr "点击“%(save)s”将更改正在使用的方法。" msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -627,6 +642,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s,经由 %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s,经由自定义网桥" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -643,6 +662,22 @@ msgctxt "connection-info" msgid "Out" msgstr "外部" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "添加自定义网桥" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "删除自定义网桥?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "删除自定义网桥会将您带回“选择位置”视图,并且将改为选择“自动”选项。" + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "编辑自定义网桥" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1151,6 +1186,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "桥接模式" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "启用桥接模式?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1197,6 +1236,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "要激活 UDP,请将桥接模式更改为自动。" +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "要选择特定桥接服务器,请转到“选择位置”视图。" + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "传输协议" @@ -1260,6 +1305,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s(%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "当常规的 Mullvad 桥接服务器无法使用时,可以使用自定义桥接服务器来绕过审查。" + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1279,6 +1328,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "国家/地区" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "自定义网桥" + msgctxt "select-location-view" msgid "Custom lists" msgstr "自定义列表" @@ -2008,7 +2061,15 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "UDP-over-TCP 混淆协议应连接到 VPN 服务器上的哪个 TCP 端口。" -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s(已添加)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "%s已添加到“%s”中" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." msgstr "%s已添加到您的帐户中。" @@ -2033,6 +2094,10 @@ msgstr "帐户额度即将到期" msgid "Account time reminders" msgstr "帐户时间提醒" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "将%s添加到列表中" + msgid "Add 30 days time" msgstr "增加 30 天" @@ -2042,12 +2107,18 @@ msgstr "增加 30 天 (%s)" msgid "Add DNS server" msgstr "添加 DNS 服务器" +msgid "Add locations" +msgstr "添加位置" + msgid "Agree and continue" msgstr "同意并继续" msgid "All applications" msgstr "所有应用程序" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "所有覆盖设置都会被重置,并且在“选择位置”视图中,服务器 IP 地址将恢复为默认设置。" + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "允许访问同一网络上的其他设备,以进行共享、打印等" @@ -2057,6 +2128,9 @@ msgstr "“始终开启的 VPN”已分配给其他应用" msgid "Always-on VPN might be enabled for another app" msgstr "可能为另一个应用启用了“始终开启的 VPN”" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "注意:拆分隧道存在隐私风险。" + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "注意:此设置不能与使用自定义 DNS 服务器结合使用。" @@ -2066,6 +2140,9 @@ msgstr "自动连接和锁定模式" msgid "Auto-connect & \\nLockdown mode" msgstr "自动连接和\\n锁定模式" +msgid "Auto-connect (legacy)" +msgstr "自动连接(旧)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "自动连接在 Android 系统设置中称为“始终开启的 VPN”,此功能可确保您始终连接到 VPN 隧道并在重新启动后自动连接。" @@ -2087,6 +2164,12 @@ msgstr "已将日志复制到剪贴板" msgid "Copied to clipboard" msgstr "已复制到剪贴板" +msgid "Create" +msgstr "创建" + +msgid "Create new list" +msgstr "创建新列表" + msgid "Critical error (your attention is required)" msgstr "严重错误(需要注意)" @@ -2096,11 +2179,32 @@ msgstr "自定义 DNS 服务器地址 %s 无效" msgid "DNS settings might not go into effect immediately" msgstr "DNS 设置可能不会立即生效" +msgid "Delete \"%s\"?" +msgstr "删除“%s”?" + msgid "Disable all %s above to activate this setting." msgstr "禁用上方的所有 %s以激活此设置。" -msgid "Enable" -msgstr "启用" +msgid "Discard" +msgstr "舍弃" + +msgid "Discard changes?" +msgstr "舍弃更改?" + +msgid "Edit custom lists" +msgstr "编辑自定义列表" + +msgid "Edit list" +msgstr "编辑列表" + +msgid "Edit lists" +msgstr "编辑列表" + +msgid "Edit locations" +msgstr "编辑位置" + +msgid "Edit name" +msgstr "编辑名称" msgid "Enter MTU" msgstr "输入 MTU" @@ -2108,6 +2212,12 @@ msgstr "输入 MTU" msgid "Excluded applications" msgstr "排除的应用程序" +msgid "Failed to apply patch" +msgstr "无法应用补丁" + +msgid "File" +msgstr "文件" + msgid "Go to VPN settings" msgstr "前往“VPN 设置”" @@ -2123,9 +2233,34 @@ msgstr "Google Play 不可用" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "如果使用拆分隧道功能,应用会向您的系统查询所有已安装应用程序的列表。此列表仅在拆分隧道视图中检索。有关已安装应用程序的信息永远不会离开设备。" +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "导入新覆盖设置的方式" + +msgid "Import successful, overrides active" +msgstr "导入成功,覆盖设置已激活" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "导入新覆盖设置可能会替换一些先前导入的覆盖设置。" + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "安装 Mullvad VPN (%s) 以保持最新状态" +msgid "Invalid or missing value \"%s\"" +msgstr "值“%s”无效或者缺失" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "允许您选择不经过 VPN 隧道而直接访问互联网的应用。" + +msgid "List name" +msgstr "列表名称" + +msgid "Locations" +msgstr "位置" + +msgid "Locations were changed for \"%s\"" +msgstr "已更改“%s”的位置" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "确保设备始终位于 VPN 隧道上。" @@ -2138,9 +2273,43 @@ msgstr "Mullvad 帐号" msgid "Mullvad services unavailable" msgstr "Mullvad 服务不可用" +msgid "Name was changed to %s" +msgstr "名称已更改为“%s”" + +msgid "New list" +msgstr "新建列表" + +msgid "No custom lists available" +msgstr "有可用的自定义列表" + msgid "No internet connection" msgstr "没有互联网连接" +msgid "No locations found" +msgstr "找不到位置" + +msgid "Not found" +msgstr "找不到" + +msgid "Overrides active" +msgstr "覆盖设置已激活" + +msgid "Overrides cleared" +msgstr "覆盖设置已清除" + +msgid "Overrides inactive" +msgstr "覆盖设置未激活" + +msgid "Paste or write overrides to be imported" +msgstr "粘贴或编写要导入的覆盖设置" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "补丁与规范不匹配" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "请按照上述%s中的指南操作,改用始终开启系统设置。" + msgid "Preferences" msgstr "偏好设置" @@ -2150,18 +2319,33 @@ msgstr "隐私" msgid "Privacy policy" msgstr "隐私政策" +msgid "Recursion limit" +msgstr "递归限制" + msgid "Remove" msgstr "移除" msgid "Remove custom port" msgstr "移除自定义端口" +msgid "Reset" +msgstr "重置" + +msgid "Reset all overrides" +msgstr "重置所有覆盖设置" + +msgid "Reset overrides" +msgstr "重置覆盖设置" + msgid "Reset to default" msgstr "重置为默认值" msgid "Secured" msgstr "已受保护" +msgid "Server Ip overrides" +msgstr "服务器 IP 覆盖设置" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "设置 WireGuard MTU 值。有效范围:%d - %d。" @@ -2177,12 +2361,12 @@ msgstr "显示当前的 VPN 隧道状态" msgid "Shows reminders when the account time is about to expire" msgstr "在帐户时间即将到期时显示提醒" -msgid "Split tunneling is disabled." -msgstr "拆分隧道被禁用。" - msgid "Submit" msgstr "提交" +msgid "Text" +msgstr "文本" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "自动连接和锁定模式设置可以在 Android 系统设置中找到,请按照本指南启用其中一项或两项。" @@ -2198,6 +2382,12 @@ msgstr "您的设备上没有 VPN 设置" msgid "This address has already been entered." msgstr "此地址已输入过。" +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "要将位置添加到列表中,请按“︙”或长按国家/地区、城市或服务器。" + +msgid "To create a custom list press the \"︙\"" +msgstr "要创建自定义列表,请按“︙”" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "为确保您使用的是最安全的版本并通知您当前运行的版本存在的任何问题,应用会自动执行版本检查。此操作会将应用版本和 Android 系统版本发送到 Mullvad 服务器。Mullvad 会统计使用的应用版本和 Android 版本。数据永远不会通过任何可以识别您身份的方式存储或使用。" @@ -2207,9 +2397,19 @@ msgstr "切换 VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "无法应用防火墙规则。请排查问题或发送问题报告。" +msgid "Unable to parse patch" +msgstr "无法解析补丁" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "无法启动隧道连接。在使用 Mullvad VPN 之前,请为 %s 禁用“始终开启的 VPN”。" +msgid "Undo" +msgstr "撤消" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "未知或禁止的密钥“%s”" + msgid "Unsecured" msgstr "未受保护" @@ -2219,6 +2419,9 @@ msgstr "更新 DNS 服务器" msgid "Update available, download to remain safe." msgstr "有可用更新,请下载以保持安全。" +msgid "Update list name" +msgstr "更新列表名称" + msgid "VPN permission error" msgstr "VPN 权限错误" @@ -2231,7 +2434,6 @@ msgstr "VPN 隧道状态" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "缺少有效的 WireGuard 密钥。在“高级”设置下管理密钥。" -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "有效范围:%s" @@ -2277,6 +2479,9 @@ msgstr "您的网络流量可能在泄露" msgid "You are running an unsupported app version." msgstr "您正在运行不受支持的应用版本。" +msgid "\"%s\" was deleted" +msgstr "“%s”已被删除" + msgid "less than a minute ago" msgstr "不到 1 分钟前" @@ -2287,6 +2492,10 @@ msgid "%d day" msgid_plural "%d days" msgstr[0] "%d 天" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d 个位置" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d 个月" diff --git a/gui/locales/zh-CN/relay-locations.po b/gui/locales/zh-CN/relay-locations.po index 976ba8d63d12..bc1ca334f85a 100644 --- a/gui/locales/zh-CN/relay-locations.po +++ b/gui/locales/zh-CN/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "澳大利亚" msgid "Austria" msgstr "奥地利" +#. TH BKK +msgid "Bangkok" +msgstr "曼谷" + +#. ES BCN +msgid "Barcelona" +msgstr "巴塞罗那" + #. BE msgid "Belgium" msgstr "比利时" @@ -188,6 +196,10 @@ msgstr "法兰克福" msgid "Germany" msgstr "德国" +#. GB GLW +msgid "Glasgow" +msgstr "格拉斯哥" + #. SE GOT msgid "Gothenburg" msgstr "哥德堡" @@ -264,6 +276,10 @@ msgstr "拉脱维亚" msgid "Lisbon" msgstr "里斯本" +#. SI LJU +msgid "Ljubljana" +msgstr "卢布尔雅那" + #. GB LON msgid "London" msgstr "伦敦" @@ -296,6 +312,10 @@ msgstr "曼彻斯特" msgid "Marseille" msgstr "马赛" +#. US TXC +msgid "McAllen, TX" +msgstr "得克萨斯州麦卡伦" + #. AU MEL msgid "Melbourne" msgstr "墨尔本" @@ -472,6 +492,10 @@ msgstr "斯科普里" msgid "Slovakia" msgstr "斯洛伐克" +#. SI +msgid "Slovenia" +msgstr "斯洛文尼亚" + #. BG SOF msgid "Sofia" msgstr "索非亚" @@ -520,6 +544,10 @@ msgstr "塔林" msgid "Tel Aviv" msgstr "特拉维夫" +#. TH +msgid "Thailand" +msgstr "泰国" + #. AL TIA msgid "Tirana" msgstr "地拉那" @@ -548,6 +576,10 @@ msgstr "乌克兰" msgid "United Arab Emirates" msgstr "阿拉伯联合酋长国" +#. ES VLC +msgid "Valencia" +msgstr "瓦伦西亚" + #. CA VAN msgid "Vancouver" msgstr "温哥华" diff --git a/gui/locales/zh-TW/messages.po b/gui/locales/zh-TW/messages.po index 3f00244d4847..87b075f57e96 100644 --- a/gui/locales/zh-TW/messages.po +++ b/gui/locales/zh-TW/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Chinese Traditional\n" "Language: zh_TW\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" msgid "%(duration)s was added, account paid until %(expiry)s." msgstr "已新增 %(duration)s,帳戶到期時間為 %(expiry)s。" @@ -133,6 +133,12 @@ msgstr "正在中斷連線" msgid "Dismiss" msgstr "取消" +msgid "Edit" +msgstr "編輯" + +msgid "Enable" +msgstr "啟用" + msgid "Enable anyway" msgstr "仍然啟用" @@ -225,6 +231,11 @@ msgstr "系統預設" msgid "TCP" msgstr "TCP" +msgid "Test" +msgstr "測試" + +#. Warning shown in dialog to users when they enable setting that increases +#. network latency (decreases performance). #. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "此設定會助長延遲。請僅在需要時使用。" @@ -247,6 +258,9 @@ msgstr "解除封鎖" msgid "UNSECURED CONNECTION" msgstr "不安全的連線" +msgid "Use" +msgstr "使用" + msgid "Username" msgstr "使用者名稱" @@ -382,9 +396,10 @@ msgctxt "api-access-methods-view" msgid "Authentication" msgstr "驗證" +#. %(save)s - Will be replaced with the translation for the word "Save". msgctxt "api-access-methods-view" -msgid "Clicking “Save” changes the in use method." -msgstr "按一下「儲存」,即可變更正在使用的方式。" +msgid "Clicking “%(save)s” changes the in use method." +msgstr "按一下「%(save)s」,即可變更正在使用的方式。" msgctxt "api-access-methods-view" msgid "Delete %(name)s?" @@ -627,6 +642,10 @@ msgctxt "connection-info" msgid "%(relay)s via %(entry)s" msgstr "%(relay)s,經由 %(entry)s" +msgctxt "connection-info" +msgid "%(relay)s via Custom bridge" +msgstr "%(relay)s,經由自訂橋接" + #. The tunnel type line displayed below the hostname line on the main screen #. Available placeholders: #. %(tunnelType)s - the tunnel type, i.e OpenVPN @@ -643,6 +662,22 @@ msgctxt "connection-info" msgid "Out" msgstr "出境" +msgctxt "custom-bridge" +msgid "Add custom bridge" +msgstr "新增自訂橋接" + +msgctxt "custom-bridge" +msgid "Delete custom bridge?" +msgstr "要刪除自訂橋接嗎?" + +msgctxt "custom-bridge" +msgid "Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead." +msgstr "刪除自訂橋接會將您帶回選擇位置的視圖,並且將改為選擇「自動」選項。" + +msgctxt "custom-bridge" +msgid "Edit custom bridge" +msgstr "編輯自訂橋接" + #. Text displayed above button which logs out another device. #. The text enclosed in "" will appear bold. #. Available placeholders: @@ -1151,6 +1186,10 @@ msgctxt "openvpn-settings-view" msgid "Bridge mode" msgstr "橋接模式" +msgctxt "openvpn-settings-view" +msgid "Enable bridge mode?" +msgstr "要啟用橋接模式嗎?" + #. This is used as a description for the bridge mode #. setting. #. Available placeholders: @@ -1197,6 +1236,12 @@ msgctxt "openvpn-settings-view" msgid "To activate UDP, change Bridge mode to Automatic or Off." msgstr "若要啟動 UDP,請將橋接模式變更為自動關閉。" +#. This text is shown beneath the bridge mode setting to instruct users how to +#. configure the feature further. +msgctxt "openvpn-settings-view" +msgid "To select a specific bridge server, go to the Select location view." +msgstr "若要選擇特定橋接伺服器,請前往「選擇位置」視圖。" + msgctxt "openvpn-settings-view" msgid "Transport protocol" msgstr "傳輸通訊協定" @@ -1260,6 +1305,10 @@ msgctxt "select-location-view" msgid "%(location)s (%(info)s)" msgstr "%(location)s (%(info)s)" +msgctxt "select-location-view" +msgid "A custom bridge server can be used to circumvent censorship when regular Mullvad bridge servers don’t work." +msgstr "當正規 Mullvad 橋接伺服器無法使用時,可以改用自訂橋接伺服器來繞過審查。" + #. This is a label shown above a list of options. #. Available placeholder: #. %(locationType) - Could be either "Country", "City" and "Relay" @@ -1279,6 +1328,10 @@ msgctxt "select-location-view" msgid "Country" msgstr "國家/地區" +msgctxt "select-location-view" +msgid "Custom bridge" +msgstr "自訂橋接" + msgctxt "select-location-view" msgid "Custom lists" msgstr "自訂清單" @@ -2008,9 +2061,17 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "UDP-over-TCP 混淆通訊協定應連線到 VPN 伺服器上的哪個 TCP 連接埠。" -msgctxt "Message to indicate how much time was added to an account as part of an in-app purchase. Example: \"30 days was added to your account.\"" +msgctxt "Indicator of that the currently selected element already has been added (directly or indirectly) to a named list (\"%s\")" +msgid "%s (added)" +msgstr "%s (已新增)" + +msgctxt "Message notifying the user that a location (first %s) was added to a named list (second %s)." +msgid "%s was added to \"%s\"" +msgstr "已將 %s 新增至「%s」" + +msgctxt "Message shown after successfully adding time to the account. %s could be either day(s) or months, for example \"1 day\" or \"6 months\"" msgid "%s was added to your account." -msgstr "%s已新增至您的帳戶。" +msgstr "%s 已新增至您的帳戶。" msgid "1. After clicking on the Go to VPN settings button below, click on the cogwheel next to the Mullvad VPN name." msgstr "1. 按一下底下前往 VPN 設定按鈕後,請按一下 Mullvad VPN 名稱旁邊的齒輪。" @@ -2033,6 +2094,10 @@ msgstr "帳戶點數即將到期" msgid "Account time reminders" msgstr "帳戶時間提醒" +msgctxt "Title of the prompt where the user is asked to add a location (%s) to a list. " +msgid "Add %s to list" +msgstr "將 %s 新增至清單" + msgid "Add 30 days time" msgstr "增加 30 天時間" @@ -2042,12 +2107,18 @@ msgstr "增加 30 天時間 (%s)" msgid "Add DNS server" msgstr "新增 DNS 伺服器" +msgid "Add locations" +msgstr "新增位置" + msgid "Agree and continue" msgstr "同意並繼續" msgid "All applications" msgstr "所有應用程式" +msgid "All overrides will be reset and servers IP addresses, in the Select location view, will go back to default." +msgstr "所有覆寫內容都將重設,並且在「選擇位置」視圖中,伺服器 IP 位址也將回到預設設定。" + msgid "Allows access to other devices on the same network for sharing, printing etc." msgstr "允許存取同一網路上的其他裝置,以進行分享、列印等。" @@ -2057,6 +2128,9 @@ msgstr "「始終啟用 VPN」已指派給其他應用程式" msgid "Always-on VPN might be enabled for another app" msgstr "可能已為另一個應用程式啟用了「始終啟用 VPN」" +msgid "Attention: Split tunneling is a privacy risk." +msgstr "注意:分割通道存在隱私風險。" + msgid "Attention: this setting cannot be used in combination with Use custom DNS server." msgstr "注意:此設定不能與使用自訂 DNS 伺服器結合使用。" @@ -2066,6 +2140,9 @@ msgstr "自動連線和鎖定模式" msgid "Auto-connect & \\nLockdown mode" msgstr "自動連線和\\n鎖定模式" +msgid "Auto-connect (legacy)" +msgstr "自動連線 (舊)" + msgid "Auto-connect is called Always-on VPN in the Android system settings and it makes sure you are constantly connected to the VPN tunnel and auto connects after restart." msgstr "自動連線在 Android 系統設定中稱為「始終啟用 VPN」,此功能可確保您始終與 VPN 通道連線,並且會在重新啟動後自動連線。" @@ -2087,6 +2164,12 @@ msgstr "已將記錄複製到剪貼簿" msgid "Copied to clipboard" msgstr "已複製到剪貼簿" +msgid "Create" +msgstr "建立" + +msgid "Create new list" +msgstr "建立新清單" + msgid "Critical error (your attention is required)" msgstr "嚴重錯誤 (需注意)" @@ -2096,11 +2179,32 @@ msgstr "自訂 DNS 伺服器位址 %s 無效" msgid "DNS settings might not go into effect immediately" msgstr "DNS 設定可能不會立即生效" +msgid "Delete \"%s\"?" +msgstr "要刪除「%s」嗎?" + msgid "Disable all %s above to activate this setting." msgstr "停用上方所有 %s以啟動此設定。" -msgid "Enable" -msgstr "啟用" +msgid "Discard" +msgstr "捨棄" + +msgid "Discard changes?" +msgstr "要捨棄變更嗎?" + +msgid "Edit custom lists" +msgstr "編輯自訂清單" + +msgid "Edit list" +msgstr "編輯清單" + +msgid "Edit lists" +msgstr "編輯清單" + +msgid "Edit locations" +msgstr "編輯位置" + +msgid "Edit name" +msgstr "編輯名稱" msgid "Enter MTU" msgstr "輸入 MTU" @@ -2108,6 +2212,12 @@ msgstr "輸入 MTU" msgid "Excluded applications" msgstr "已排除的應用程式" +msgid "Failed to apply patch" +msgstr "無法套用修補檔" + +msgid "File" +msgstr "檔案" + msgid "Go to VPN settings" msgstr "前往「VPN 設定」" @@ -2123,9 +2233,34 @@ msgstr "Google Play 無法使用" msgid "If the split tunneling feature is used, then the app queries your system for a list of all installed applications. This list is only retrieved in the split tunneling view. The list of installed applications is never sent from the device." msgstr "如果使用了分割通道功能,該應用程式會查詢您的系統,以取得所有已安裝應用程式的清單。我們只會在分割通道視圖中擷取這份清單,絕不會傳送至他處。" +msgctxt "Title of the bottom sheet prompt shown when importing a patch/configuration. The string itself is follwed by a list of import sources." +msgid "Import new overrides by" +msgstr "匯入新覆寫設定的方式" + +msgid "Import successful, overrides active" +msgstr "匯入成功,覆寫設定已啟用" + +msgid "Importing new overrides might replace some previously imported overrides." +msgstr "匯入新的覆寫設定,可能會取代一部分先前已匯入的覆寫設定。" + msgid "Install Mullvad VPN (%s) to stay up to date" msgstr "安裝 Mullvad VPN (%s) 以維持最新狀態" +msgid "Invalid or missing value \"%s\"" +msgstr "值「%s」無效或是遺失" + +msgid "Lets you select apps that should access the Internet directly without going through the VPN tunnel." +msgstr "允許您選擇不經過 VPN 通道而直接存取網際網路的應用程式。" + +msgid "List name" +msgstr "清單名稱" + +msgid "Locations" +msgstr "位置" + +msgid "Locations were changed for \"%s\"" +msgstr "已變更「%s」的位置" + msgid "Makes sure the device is always on the VPN tunnel." msgstr "請確認裝置始終位於 VPN 通道上。" @@ -2138,9 +2273,43 @@ msgstr "Mullvad 帳號" msgid "Mullvad services unavailable" msgstr "Mullvad 服務無法使用" +msgid "Name was changed to %s" +msgstr "名稱已變更為「%s」" + +msgid "New list" +msgstr "新清單" + +msgid "No custom lists available" +msgstr "有可用的自訂清單" + msgid "No internet connection" msgstr "沒有網際網路連線" +msgid "No locations found" +msgstr "找不到位置" + +msgid "Not found" +msgstr "找不到" + +msgid "Overrides active" +msgstr "覆寫設定已啟用" + +msgid "Overrides cleared" +msgstr "覆寫設定已清除" + +msgid "Overrides inactive" +msgstr "覆寫設定未啟用" + +msgid "Paste or write overrides to be imported" +msgstr "貼上或撰寫要匯入的覆寫設定" + +msgctxt "Message shown if the provided configuration file/text (\"patch\") format doesn't match the requirements and therefore couldn't be applied." +msgid "Patch not matching specification" +msgstr "修補檔與規範不相符" + +msgid "Please use the Always-on system setting instead by following the guide in %s above." +msgstr "請依照前述 %s 中的指南,改用一律啟用系統設定。" + msgid "Preferences" msgstr "喜好設定" @@ -2150,18 +2319,33 @@ msgstr "隱私權" msgid "Privacy policy" msgstr "隱私權政策" +msgid "Recursion limit" +msgstr "遞迴限制" + msgid "Remove" msgstr "移除" msgid "Remove custom port" msgstr "移除自訂連接埠" +msgid "Reset" +msgstr "重設" + +msgid "Reset all overrides" +msgstr "重設所有覆寫設定" + +msgid "Reset overrides" +msgstr "重設覆寫設定" + msgid "Reset to default" msgstr "重設為預設值" msgid "Secured" msgstr "安全" +msgid "Server Ip overrides" +msgstr "伺服器 IP 覆寫" + msgid "Set WireGuard MTU value. Valid range: %d - %d." msgstr "設定 WireGuard MTU 值。有效範圍:%d - %d。" @@ -2177,12 +2361,12 @@ msgstr "顯示目前的 VPN 通道狀態" msgid "Shows reminders when the account time is about to expire" msgstr "在帳戶時間即將到期時顯示提醒" -msgid "Split tunneling is disabled." -msgstr "分割通道已停用。" - msgid "Submit" msgstr "提交" +msgid "Text" +msgstr "文字" + msgid "The Auto-connect and Lockdown mode settings can be found in the Android system settings, follow this guide to enable one or both." msgstr "自動連線和鎖定模式設定皆可在 Android 系統設定中找到,請按照本指南的指示,啟用其中一項或兩項設定。" @@ -2198,6 +2382,12 @@ msgstr "您的裝置上沒有 VPN 設定" msgid "This address has already been entered." msgstr "此地址已輸入過。" +msgid "To add locations to a list, press the \"︙\" or long press on a country, city, or server." +msgstr "若要在清單中新增位置,請按下「︙」,或是長按下國家/地區、城市或伺服器。" + +msgid "To create a custom list press the \"︙\"" +msgstr "若要建立自訂清單,請按下「︙」" + msgid "To make sure that you have the most secure version and to inform you of any issues with the current version that is running, the app performs version checks automatically. This sends the app version and Android system version to Mullvad servers. Mullvad keeps counters on number of used app versions and Android versions. The data is never stored or used in any way that can identify you." msgstr "為確保您擁有最安全的版本,並告知您目前執行的版本有無任何問題,該應用程式會自動進行版本檢查。為此,應用程式會將其版本和 Android 系統版本傳送至 Mullvad 伺服器,以便讓 Mullvad 統計已使用的應用程式版本和 Android 版本的數量。這些資料永遠不會以任何方式儲存或用來辨識您的身分。" @@ -2207,9 +2397,19 @@ msgstr "切換 VPN" msgid "Unable to apply firewall rules. Please troubleshoot or send a problem report." msgstr "無法套用防火牆規則。請排除故障或傳送問題回報。" +msgid "Unable to parse patch" +msgstr "無法剖析修補檔" + msgid "Unable to start tunnel connection. Please disable Always-on VPN for %s before using Mullvad VPN." msgstr "無法啟動通道連線。在使用 Mullvad VPN 之前,請先為 %s 停用「始終啟用 VPN」。" +msgid "Undo" +msgstr "復原" + +msgctxt "Error message shown when failing to parse a configuration file/text. The key variable (%s) is a an identifier used within the configuration file/text. " +msgid "Unknown or prohibited key \"%s\"" +msgstr "未知或遭到禁用的金鑰「%s」" + msgid "Unsecured" msgstr "不安全" @@ -2219,6 +2419,9 @@ msgstr "更新 DNS 伺服器" msgid "Update available, download to remain safe." msgstr "更新可用,請下載以維持安全。" +msgid "Update list name" +msgstr "更新清單名稱" + msgid "VPN permission error" msgstr "VPN 權限錯誤" @@ -2231,7 +2434,6 @@ msgstr "VPN 通道狀態" msgid "Valid WireGuard key is missing. Manage keys under Advanced settings." msgstr "缺少有效的 WireGuard 金鑰。在「進階」設定下管理金鑰。" -msgctxt "Help text to explain which numbers are valid to configure. The user should enter a single number/integer that either matches one of the listed numbers or is within one of the listed ranges. Example: \"Valid ranges: 53, 123, 4000-33433\"" msgid "Valid ranges: %s" msgstr "有效範圍:%s" @@ -2277,6 +2479,9 @@ msgstr "您的網路流量可能正在洩露" msgid "You are running an unsupported app version." msgstr "您所執行的應用程式版本不受支援。" +msgid "\"%s\" was deleted" +msgstr "「%s」已刪除" + msgid "less than a minute ago" msgstr "不到 1 分鐘前" @@ -2287,6 +2492,10 @@ msgid "%d day" msgid_plural "%d days" msgstr[0] "%d 天" +msgid "%d location" +msgid_plural "%d locations" +msgstr[0] "%d 個位置" + msgid "%d month" msgid_plural "%d months" msgstr[0] "%d 個月" diff --git a/gui/locales/zh-TW/relay-locations.po b/gui/locales/zh-TW/relay-locations.po index fe3e9167e095..403e750731e0 100644 --- a/gui/locales/zh-TW/relay-locations.po +++ b/gui/locales/zh-TW/relay-locations.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: mullvad-app\n" "Language-Team: Chinese Traditional\n" "Language: zh_TW\n" -"PO-Revision-Date: 2024-03-13 08:24\n" +"PO-Revision-Date: 2024-04-25 08:25\n" #. AU ADL msgid "Adelaide" @@ -48,6 +48,14 @@ msgstr "澳大利亞" msgid "Austria" msgstr "奧地利" +#. TH BKK +msgid "Bangkok" +msgstr "曼谷" + +#. ES BCN +msgid "Barcelona" +msgstr "巴塞隆納" + #. BE msgid "Belgium" msgstr "比利時" @@ -188,6 +196,10 @@ msgstr "法蘭克福" msgid "Germany" msgstr "德國" +#. GB GLW +msgid "Glasgow" +msgstr "格拉斯哥" + #. SE GOT msgid "Gothenburg" msgstr "哥特堡" @@ -264,6 +276,10 @@ msgstr "拉脫維亞" msgid "Lisbon" msgstr "里斯本" +#. SI LJU +msgid "Ljubljana" +msgstr "盧比安納" + #. GB LON msgid "London" msgstr "倫敦" @@ -296,6 +312,10 @@ msgstr "曼徹斯特" msgid "Marseille" msgstr "馬賽" +#. US TXC +msgid "McAllen, TX" +msgstr "德州麥克亞連" + #. AU MEL msgid "Melbourne" msgstr "墨爾本" @@ -472,6 +492,10 @@ msgstr "斯高彼亞" msgid "Slovakia" msgstr "斯洛伐克" +#. SI +msgid "Slovenia" +msgstr "斯洛維尼亞" + #. BG SOF msgid "Sofia" msgstr "索菲亞" @@ -520,6 +544,10 @@ msgstr "塔林" msgid "Tel Aviv" msgstr "特拉維夫" +#. TH +msgid "Thailand" +msgstr "泰國" + #. AL TIA msgid "Tirana" msgstr "地拉那" @@ -548,6 +576,10 @@ msgstr "烏克蘭" msgid "United Arab Emirates" msgstr "阿拉伯聯合大公國" +#. ES VLC +msgid "Valencia" +msgstr "瓦倫西亞" + #. CA VAN msgid "Vancouver" msgstr "溫哥華" From ab4b01bb938db97d967b74ce90838def63c32bc9 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 24 Apr 2024 13:19:24 +0200 Subject: [PATCH 165/214] Run cargo update + use new base64 --- Cargo.lock | 1244 +++++++++++++++-------------- talpid-types/Cargo.toml | 2 +- talpid-types/src/net/wireguard.rs | 13 +- talpid-wireguard/Cargo.toml | 1 - 4 files changed, 668 insertions(+), 592 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 185af07aa73a..23e66cbecc12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -72,9 +72,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -114,9 +114,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -128,26 +128,26 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -162,15 +162,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" @@ -203,25 +203,25 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "axum" @@ -234,9 +234,9 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", "itoa", "matchit", "memchr", @@ -260,8 +260,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -285,21 +285,21 @@ dependencies = [ [[package]] name = "base16ct" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.13.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.21.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" [[package]] name = "base64ct" @@ -330,22 +330,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "blake3" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "digest", ] [[package]] @@ -359,9 +358,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte_string" @@ -371,15 +370,25 @@ checksum = "11aade7a05aa8c3a351cedc44c3fc45806430543382fcc4743a9b757a2a0b4ed" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "camellia" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" +dependencies = [ + "byteorder", + "cipher", +] [[package]] name = "cbindgen" @@ -387,7 +396,7 @@ version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" dependencies = [ - "heck", + "heck 0.4.1", "indexmap 1.9.3", "log", "proc-macro2", @@ -401,12 +410,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cesu8" @@ -452,15 +458,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.30" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -476,9 +482,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -486,42 +492,42 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim 0.11.1", ] [[package]] name = "clap_complete" -version = "4.4.8" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf7dcb7c21d8ca1a2482ee0f1d341f437c9a7af6ca6da359dc5e1b164e98215" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "classic-mceliece-rust" @@ -553,9 +559,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -563,9 +569,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" @@ -575,9 +581,9 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -585,9 +591,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core2" @@ -600,9 +606,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -618,28 +624,24 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-bigint" -version = "0.4.9" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -669,12 +671,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.1" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ - "nix 0.27.1", - "windows-sys 0.48.0", + "nix 0.28.0", + "windows-sys 0.52.0", ] [[package]] @@ -695,13 +697,13 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -752,7 +754,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "lock_api", "once_cell", "parking_lot_core", @@ -760,9 +762,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "dbus" @@ -777,9 +779,9 @@ dependencies = [ [[package]] name = "der" -version = "0.6.1" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -787,9 +789,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] [[package]] name = "derive-try-from-primitive" @@ -861,9 +866,9 @@ dependencies = [ [[package]] name = "duct" -version = "0.13.6" +version = "0.13.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ae3fc31835f74c2a7ceda3aeede378b0ae2e74c8f1c36559fcc9ae2a4e7d3e" +checksum = "e4ab5718d1224b63252cd0c6f74f6480f9ffeb117438a2e0f5cf6d9a4798929c" dependencies = [ "libc", "once_cell", @@ -873,9 +878,9 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.14.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "elliptic-curve", @@ -884,30 +889,32 @@ dependencies = [ [[package]] name = "ed25519" -version = "1.5.3" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ + "pkcs8", "signature", ] [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elliptic-curve" -version = "0.12.3" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "der", "digest", + "ff", "generic-array", + "group", "rand_core 0.6.4", "sec1", "subtle", @@ -920,10 +927,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -938,9 +945,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -1018,9 +1025,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fern" @@ -1032,22 +1039,32 @@ dependencies = [ "log", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fiat-crypto" -version = "0.2.1" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] @@ -1064,9 +1081,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1082,9 +1099,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1097,9 +1114,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1107,15 +1124,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1124,38 +1141,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1177,6 +1194,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1192,9 +1210,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -1203,9 +1221,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", @@ -1213,9 +1231,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -1223,6 +1241,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.26" @@ -1234,8 +1263,27 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.0.0", + "http 0.2.12", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.1.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1259,9 +1307,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -1269,11 +1317,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1294,7 +1348,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna", + "idna 0.4.0", "ipnet", "once_cell", "rand 0.8.5", @@ -1351,9 +1405,9 @@ dependencies = [ [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] @@ -1369,11 +1423,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1389,9 +1443,9 @@ dependencies = [ [[package]] name = "htmlize" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6507eaed4d57bf58786aabd4ebc91a7d702d1fdace5ccc6479de1aee666765dd" +checksum = "4e81e415f6d22240930e82ae0f541b2dd494ca37daaf10c1d7b32546f3b1159f" dependencies = [ "memchr", "paste", @@ -1402,9 +1456,20 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1413,12 +1478,35 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1442,35 +1530,56 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.4", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-timeout" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper", + "hyper 0.14.28", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -1478,16 +1587,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1515,6 +1624,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1527,12 +1646,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", ] [[package]] @@ -1589,7 +1708,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -1604,7 +1723,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.3", + "socket2", "widestring", "windows-sys 0.48.0", "winreg 0.50.0", @@ -1612,9 +1731,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "ipnetwork" @@ -1665,9 +1784,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jni" @@ -1707,7 +1826,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "002f4dfe6d97ae88c33f3489c0d31ffc6f81d9a492de98ff113b127d73bafff8" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 1.0.109", @@ -1715,9 +1834,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1735,9 +1854,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -1813,6 +1932,16 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1827,9 +1956,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1837,9 +1966,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "log-panics" @@ -1882,15 +2011,15 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "matchit" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "maybenot" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc2e64fe3f5fb1e247110a9a408449eff2259cc272cf57bad6f161e801ac962" +checksum = "94ed977e86fc65a7ffae967a6a973e6f7a90b5d747ebd755703d5718804f7c16" dependencies = [ "byteorder", "hex", @@ -1904,18 +2033,19 @@ dependencies = [ [[package]] name = "md-5" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ + "cfg-if", "digest", ] [[package]] name = "memchr" -version = "2.6.3" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" @@ -1928,9 +2058,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -1943,9 +2073,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -1990,8 +2120,8 @@ dependencies = [ "cbindgen", "chrono", "futures", - "http", - "hyper", + "http 0.2.12", + "hyper 0.14.28", "ipnetwork", "libc", "log", @@ -2164,7 +2294,7 @@ dependencies = [ "clap", "dirs", "duct", - "env_logger 0.10.0", + "env_logger 0.10.2", "log", "mullvad-api", "mullvad-paths", @@ -2203,7 +2333,7 @@ name = "mullvad-setup" version = "0.0.0" dependencies = [ "clap", - "env_logger 0.10.0", + "env_logger 0.10.2", "mullvad-api", "mullvad-daemon", "mullvad-management-interface", @@ -2245,9 +2375,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "natord" @@ -2310,9 +2440,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" dependencies = [ "bytes", "futures", @@ -2368,28 +2498,17 @@ dependencies = [ "libc", ] -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.0", - "cfg-if", - "libc", -] - [[package]] name = "nix" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "cfg-if", "cfg_aliases", "libc", - "memoffset 0.9.0", + "memoffset 0.9.1", ] [[package]] @@ -2404,7 +2523,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -2417,11 +2536,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -2458,24 +2583,24 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openvpn-plugin" @@ -2496,12 +2621,12 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "os_pipe" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2517,9 +2642,9 @@ dependencies = [ [[package]] name = "p256" -version = "0.11.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa", "elliptic-curve", @@ -2527,12 +2652,13 @@ dependencies = [ [[package]] name = "p384" -version = "0.11.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" dependencies = [ "ecdsa", "elliptic-curve", + "primeorder", ] [[package]] @@ -2561,13 +2687,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", "windows-targets 0.48.5", ] @@ -2580,15 +2706,15 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.3" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -2597,9 +2723,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.3" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" dependencies = [ "pest", "pest_generator", @@ -2607,22 +2733,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.3" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "pest_meta" -version = "2.7.3" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", @@ -2636,7 +2762,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.0", + "indexmap 2.2.6", ] [[package]] @@ -2693,29 +2819,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2725,9 +2851,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", @@ -2735,51 +2861,51 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "platforms" -version = "3.1.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "pnet_base" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872e46346144ebf35219ccaa64b1dffacd9c6f188cd7d012bd6977a2a838f42e" +checksum = "fe4cf6fb3ab38b68d01ab2aea03ed3d1132b4868fa4e06285f29f16da01c5f4c" dependencies = [ "no-std-net", ] [[package]] name = "pnet_macros" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a780e80005c2e463ec25a6e9f928630049a10b43945fea83207207d4a7606f4" +checksum = "688b17499eee04a0408aca0aa5cba5fc86401d7216de8a63fdf7a4c227871804" dependencies = [ "proc-macro2", "quote", "regex", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] name = "pnet_macros_support" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d932134f32efd7834eb8b16d42418dac87086347d1bc7d142370ef078582bc" +checksum = "eea925b72f4bd37f8eab0f221bbe4c78b63498350c983ffa9dd4bcde7e030f56" dependencies = [ "pnet_base", ] [[package]] name = "pnet_packet" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bde678bbd85cb1c2d99dc9fc596e57f03aa725f84f3168b0eaf33eeccb41706" +checksum = "a9a005825396b7fe7a38a8e288dbc342d5034dac80c15212436424fef8ea90ba" dependencies = [ "glob", "pnet_base", @@ -2800,9 +2926,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", @@ -2810,6 +2936,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2828,12 +2960,21 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", - "syn 2.0.51", + "syn 2.0.60", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", ] [[package]] @@ -2862,9 +3003,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -2877,13 +3018,13 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.0", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -2891,9 +3032,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8473a65b88506c106c28ae905ca4a2b83a2993640467a41bb3080627ddfd2c" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", "prost-derive", @@ -2901,13 +3042,13 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d3e647e9eb04ddfef78dfee2d5b3fefdf94821c84b710a3d8ebc89ede8b164" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", - "heck", - "itertools 0.10.5", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -2916,29 +3057,28 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.51", + "syn 2.0.60", "tempfile", - "which", ] [[package]] name = "prost-derive" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56075c27b20ae524d00f247b8a4dc333e5784f889fe63099f8e626bc8d73486c" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "prost-types" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cebe0a918c97f86c217b0f76fd754e966f8b9f41595095cf7d74cb4e59d730f6" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ "prost", ] @@ -2961,9 +3101,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -3027,7 +3167,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.14", ] [[package]] @@ -3060,67 +3200,52 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", + "getrandom 0.2.14", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.7.5", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "resolv-conf" @@ -3155,7 +3280,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.10", + "getrandom 0.2.14", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -3164,20 +3289,20 @@ dependencies = [ [[package]] name = "ring-compat" -version = "0.5.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333b9bf6765e0141324d95b5375bb1aa5267865bb4bc0281c22aff22f5d37746" +checksum = "ccce7bae150b815f0811db41b8312fcb74bffa4cab9cee5429ee00f356dd5bd4" dependencies = [ "aead", "digest", "ecdsa", "ed25519", "generic-array", - "opaque-debug", "p256", "p384", "pkcs8", - "ring 0.16.20", + "rand_core 0.6.4", + "ring 0.17.8", "signature", ] @@ -3225,11 +3350,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "errno 0.3.8", "libc", "linux-raw-sys", @@ -3250,11 +3375,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.3", + "base64 0.21.7", ] [[package]] @@ -3269,9 +3394,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "rusty-fork" @@ -3287,9 +3412,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -3308,19 +3433,19 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] name = "sec1" -version = "0.3.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -3331,9 +3456,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "sendfd" @@ -3347,29 +3472,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.188" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -3390,9 +3515,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -3401,9 +3526,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -3422,18 +3547,19 @@ dependencies = [ [[package]] name = "shadowsocks" -version = "1.16.0" +version = "1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d4aadc3b1b38e760533d4060a1aa53a2d754f073389f5aafe6bf7b579c4f97" +checksum = "ed5edddeff89d9874fa59366cfc506b53525410f129bbf13064ab36de15374e6" dependencies = [ "arc-swap", "async-trait", - "base64 0.21.3", + "base64 0.22.0", "blake3", "byte_string", "bytes", "cfg-if", "futures", + "hickory-resolver", "libc", "log", "notify", @@ -3445,24 +3571,24 @@ dependencies = [ "serde_json", "serde_urlencoded", "shadowsocks-crypto", - "socket2 0.5.3", + "socket2", "spin 0.9.8", "thiserror", "tokio", "tokio-tfo", - "trust-dns-resolver", "url", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "shadowsocks-crypto" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb488687e398030dd9c9396e119ddbc6952bdeaefe2168943b5b2ddaa54f2e6" +checksum = "65da645ff4a6440ba1b52a9d6b4c8792054860ac135cb87f8ad3d2c7a78d41b5" dependencies = [ "aes", "aes-gcm", + "camellia", "cfg-if", "chacha20", "chacha20poly1305", @@ -3487,9 +3613,9 @@ dependencies = [ [[package]] name = "shadowsocks-service" -version = "1.16.1" +version = "1.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7782cbb1b1e3743b03dd99165750990cca1b4cd181b2a0e91ddeeccc3f77d8cd" +checksum = "7be0ae9c02adf5fb2a91cdee6b3d6e3610d88411114080280e816d817fe437c8" dependencies = [ "arc-swap", "async-trait", @@ -3498,27 +3624,27 @@ dependencies = [ "bytes", "cfg-if", "futures", - "hyper", - "idna", + "http-body-util", + "hyper 1.3.1", + "idna 0.5.0", "ipnet", "iprange", "json5", "libc", "log", "lru_time_cache", - "nix 0.27.1", + "nix 0.28.0", "once_cell", "pin-project", "rand 0.8.5", "regex", "serde", "shadowsocks", - "socket2 0.5.3", + "socket2", "spin 0.9.8", "thiserror", "tokio", - "tower", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3539,18 +3665,18 @@ checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "signature" -version = "1.6.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "rand_core 0.6.4", ] @@ -3588,28 +3714,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3629,9 +3745,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.6.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -3645,9 +3761,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subslice" @@ -3666,15 +3782,15 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "surge-ping" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af341b2be485d647b5dc4cfb2da99efac35b5c95748a08fb7233480fedc5ead3" +checksum = "efbf95ce4c7c5b311d2ce3f088af2b93edef0f09727fa50fbe03c7a979afce77" dependencies = [ "hex", "parking_lot", "pnet_packet", "rand 0.8.5", - "socket2 0.5.3", + "socket2", "thiserror", "tokio", "tracing", @@ -3693,9 +3809,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.51" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -3840,7 +3956,7 @@ dependencies = [ name = "talpid-openvpn-plugin" version = "0.0.0" dependencies = [ - "env_logger 0.10.0", + "env_logger 0.10.2", "log", "mullvad-version", "openvpn-plugin", @@ -3869,7 +3985,7 @@ dependencies = [ name = "talpid-routing" version = "0.0.0" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "futures", "ipnetwork", "libc", @@ -3939,7 +4055,7 @@ dependencies = [ name = "talpid-types" version = "0.0.0" dependencies = [ - "base64 0.13.1", + "base64 0.22.0", "ipnetwork", "jnix", "log", @@ -3954,7 +4070,7 @@ name = "talpid-windows" version = "0.0.0" dependencies = [ "futures", - "socket2 0.5.3", + "socket2", "talpid-types", "thiserror", "windows-sys 0.48.0", @@ -3964,7 +4080,6 @@ dependencies = [ name = "talpid-wireguard" version = "0.0.0" dependencies = [ - "base64 0.13.1", "bitflags 1.3.2", "byteorder", "chrono", @@ -3986,7 +4101,7 @@ dependencies = [ "proptest", "rand 0.8.5", "rtnetlink", - "socket2 0.5.3", + "socket2", "surge-ping", "talpid-dbus", "talpid-routing", @@ -4005,62 +4120,63 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "time" -version = "0.3.28" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", + "num-conv", + "powerfmt", "serde", "time-core", ] [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tinyvec" @@ -4079,9 +4195,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -4091,7 +4207,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.3", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] @@ -4108,13 +4224,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -4141,9 +4257,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -4162,7 +4278,7 @@ dependencies = [ "log", "once_cell", "pin-project", - "socket2 0.5.3", + "socket2", "tokio", "windows-sys 0.48.0", ] @@ -4192,19 +4308,19 @@ dependencies = [ [[package]] name = "tonic" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5469afaf78a11265c343a88969045c1568aa8ecc6c787dbf756e92e70f199861" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.3", + "base64 0.21.7", "bytes", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-timeout", "percent-encoding", "pin-project", @@ -4219,15 +4335,15 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b477abbe1d18c0b08f56cd01d1bc288668c5b5cfd19b2ae1886bbf599c546f1" +checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ "prettyplease", "proc-macro2", "prost-build", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -4264,11 +4380,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -4277,20 +4392,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -4313,57 +4428,11 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce148eae0d1a376c1b94ae651fc3261d9cb8294788b962b7382066376503a2d1" -[[package]] -name = "trust-dns-proto" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc775440033cb114085f6f2437682b194fa7546466024b1037e82a48a052a69" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "once_cell", - "rand 0.8.5", - "smallvec", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff7aed33ef3e8bf2c9966fccdfed93f93d46f432282ea875cd66faabc6ef2f" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand 0.8.5", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "tracing", - "trust-dns-proto", -] - [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tun" @@ -4399,9 +4468,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" @@ -4430,21 +4499,21 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -4479,12 +4548,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -4497,11 +4566,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.14", "serde", ] @@ -4522,9 +4591,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -4553,9 +4622,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4563,24 +4632,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4588,28 +4657,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -4629,9 +4698,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -4651,11 +4720,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4665,12 +4734,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -4708,7 +4777,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -4743,17 +4812,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -4770,9 +4840,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -4788,9 +4858,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -4806,9 +4876,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -4824,9 +4900,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4842,9 +4918,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4860,9 +4936,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -4878,9 +4954,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winreg" @@ -4940,14 +5016,14 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -4960,5 +5036,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] diff --git a/talpid-types/Cargo.toml b/talpid-types/Cargo.toml index 7582387b6860..87a9504bbdea 100644 --- a/talpid-types/Cargo.toml +++ b/talpid-types/Cargo.toml @@ -13,7 +13,7 @@ workspace = true [dependencies] serde = { version = "1.0", features = ["derive"] } ipnetwork = "0.16" -base64 = "0.13" +base64 = "0.22.0" x25519-dalek = { version = "2.0.1", features = ["static_secrets", "zeroize", "getrandom"] } thiserror = { workspace = true } zeroize = "1.5.7" diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs index f7212236e2f9..4f1298323fec 100644 --- a/talpid-types/src/net/wireguard.rs +++ b/talpid-types/src/net/wireguard.rs @@ -1,4 +1,5 @@ use crate::net::{Endpoint, GenericTunnelOptions, TransportProtocol}; +use base64::{engine::general_purpose::STANDARD, Engine}; use ipnetwork::IpNetwork; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{ @@ -105,7 +106,7 @@ impl PrivateKey { } pub fn to_base64(&self) -> String { - base64::encode(self.0.to_bytes()) + STANDARD.encode(self.0.to_bytes()) } pub fn from_base64(key: &str) -> Result { @@ -135,7 +136,7 @@ impl fmt::Debug for PrivateKey { impl fmt::Display for PrivateKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", &base64::encode((self.0).to_bytes())) + write!(f, "{}", &STANDARD.encode((self.0).to_bytes())) } } @@ -177,7 +178,7 @@ impl PublicKey { } pub fn to_base64(&self) -> String { - base64::encode(self.as_bytes()) + STANDARD.encode(self.as_bytes()) } pub fn from_base64(key: &str) -> Result { @@ -272,7 +273,7 @@ impl From> for PresharedKey { impl fmt::Debug for PresharedKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", &base64::encode(self.as_bytes())) + write!(f, "{}", &STANDARD.encode(self.as_bytes())) } } @@ -280,7 +281,7 @@ fn serialize_key(key: &[u8; 32], serializer: S) -> Result where S: Serializer, { - serializer.serialize_str(&base64::encode(key)) + serializer.serialize_str(&STANDARD.encode(key)) } fn deserialize_key<'de, D, K>(deserializer: D) -> Result @@ -295,7 +296,7 @@ where } fn key_from_base64>(key: &str) -> Result { - let bytes = base64::decode(key).map_err(InvalidKey::Format)?; + let bytes = STANDARD.decode(key).map_err(InvalidKey::Format)?; if bytes.len() != 32 { return Err(InvalidKey::Length(bytes.len())); } diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml index c2562d212c30..c1eb33576638 100644 --- a/talpid-wireguard/Cargo.toml +++ b/talpid-wireguard/Cargo.toml @@ -12,7 +12,6 @@ workspace = true [dependencies] thiserror = { workspace = true } -base64 = "0.13" futures = "0.3.15" hex = "0.4" ipnetwork = "0.16" From 4836fcf56bee03f8f736d550316ca1f6b7832a7c Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 24 Apr 2024 13:40:19 +0200 Subject: [PATCH 166/214] Fix chrono deprecation warnings --- .../src/types/conversions/account.rs | 17 +++++++------ .../src/types/conversions/device.rs | 25 +++++++++---------- .../src/types/conversions/wireguard.rs | 9 ++++--- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/mullvad-management-interface/src/types/conversions/account.rs b/mullvad-management-interface/src/types/conversions/account.rs index c98166ba8084..fa5b7aeff55f 100644 --- a/mullvad-management-interface/src/types/conversions/account.rs +++ b/mullvad-management-interface/src/types/conversions/account.rs @@ -1,5 +1,5 @@ use crate::types; -use chrono::TimeZone; +use chrono::DateTime; use mullvad_types::account::{AccountData, VoucherSubmission}; use super::FromProtobufTypeError; @@ -23,12 +23,12 @@ impl TryFrom for VoucherSubmission { let new_expiry = submission .new_expiry .ok_or(FromProtobufTypeError::InvalidArgument("missing expiry"))?; - let ndt = - chrono::NaiveDateTime::from_timestamp_opt(new_expiry.seconds, new_expiry.nanos as u32) - .unwrap(); + + let new_expiry = DateTime::from_timestamp(new_expiry.seconds, new_expiry.nanos as u32) + .ok_or(FromProtobufTypeError::InvalidArgument("invalid timestamp"))?; Ok(VoucherSubmission { - new_expiry: chrono::Utc.from_utc_datetime(&ndt), + new_expiry, time_added: submission.seconds_added, }) } @@ -53,12 +53,13 @@ impl TryFrom for AccountData { let expiry = data .expiry .ok_or(FromProtobufTypeError::InvalidArgument("missing expiry"))?; - let ndt = - chrono::NaiveDateTime::from_timestamp_opt(expiry.seconds, expiry.nanos as u32).unwrap(); + + let expiry = DateTime::from_timestamp(expiry.seconds, expiry.nanos as u32) + .ok_or(FromProtobufTypeError::InvalidArgument("invalid timestamp"))?; Ok(AccountData { id: data.id, - expiry: chrono::Utc.from_utc_datetime(&ndt), + expiry, }) } } diff --git a/mullvad-management-interface/src/types/conversions/device.rs b/mullvad-management-interface/src/types/conversions/device.rs index 08dc2e52fbe9..b43c47ee996d 100644 --- a/mullvad-management-interface/src/types/conversions/device.rs +++ b/mullvad-management-interface/src/types/conversions/device.rs @@ -1,28 +1,27 @@ use crate::types::{conversions::bytes_to_pubkey, proto, FromProtobufTypeError}; -use chrono::TimeZone; +use chrono::DateTime; use prost_types::Timestamp; impl TryFrom for mullvad_types::device::Device { type Error = FromProtobufTypeError; fn try_from(device: proto::Device) -> Result { + let created_seconds = device + .created + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing 'created' field", + ))? + .seconds; + + let created = DateTime::from_timestamp(created_seconds, 0) + .ok_or(FromProtobufTypeError::InvalidArgument("invalid timestamp"))?; + Ok(mullvad_types::device::Device { id: device.id, name: device.name, pubkey: bytes_to_pubkey(&device.pubkey)?, hijack_dns: device.hijack_dns, - created: chrono::Utc.from_utc_datetime( - &chrono::NaiveDateTime::from_timestamp_opt( - device - .created - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing 'created' field", - ))? - .seconds, - 0, - ) - .unwrap(), - ), + created, }) } } diff --git a/mullvad-management-interface/src/types/conversions/wireguard.rs b/mullvad-management-interface/src/types/conversions/wireguard.rs index 4a4341339cfc..1ef378ef5e37 100644 --- a/mullvad-management-interface/src/types/conversions/wireguard.rs +++ b/mullvad-management-interface/src/types/conversions/wireguard.rs @@ -1,6 +1,6 @@ use super::FromProtobufTypeError; use crate::types::proto; -use chrono::TimeZone; +use chrono::DateTime; use prost_types::Timestamp; impl From for proto::PublicKey { @@ -24,13 +24,14 @@ impl TryFrom for mullvad_types::wireguard::PublicKey { .ok_or(FromProtobufTypeError::InvalidArgument( "missing 'created' timestamp", ))?; - let ndt = chrono::NaiveDateTime::from_timestamp_opt(created.seconds, created.nanos as u32) - .unwrap(); + + let created = DateTime::from_timestamp(created.seconds, created.nanos as u32) + .ok_or(FromProtobufTypeError::InvalidArgument("invalid timestamp"))?; Ok(mullvad_types::wireguard::PublicKey { key: talpid_types::net::wireguard::PublicKey::try_from(public_key.key.as_slice()) .map_err(|_| FromProtobufTypeError::InvalidArgument("invalid wireguard key"))?, - created: chrono::Utc.from_utc_datetime(&ndt), + created, }) } } From 548f3913cfd7c590d564f3a65df6e50a3374234f Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 25 Apr 2024 13:35:18 +0100 Subject: [PATCH 167/214] Update windows-sys --- Cargo.lock | 26 +- Cargo.toml | 2 +- mullvad-daemon/Cargo.toml | 2 +- mullvad-daemon/src/migrations/mod.rs | 5 +- mullvad-paths/src/windows.rs | 10 +- talpid-core/src/dns/windows/iphlpapi.rs | 6 +- .../src/split_tunnel/windows/windows.rs | 3 +- talpid-openvpn/src/wintun.rs | 6 +- talpid-wireguard/src/wireguard_nt/mod.rs | 6 +- test/Cargo.lock | 1170 +++++++++-------- test/Cargo.toml | 2 +- 11 files changed, 616 insertions(+), 622 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23e66cbecc12..d39c7fd3639b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2158,7 +2158,7 @@ dependencies = [ "talpid-types", "thiserror", "tokio", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "winres", ] @@ -2202,7 +2202,7 @@ dependencies = [ "tokio-stream", "winapi", "windows-service", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "winres", ] @@ -2284,7 +2284,7 @@ dependencies = [ "once_cell", "thiserror", "widestring", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2306,7 +2306,7 @@ dependencies = [ "thiserror", "tokio", "uuid", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "winres", ] @@ -3899,7 +3899,7 @@ dependencies = [ "which", "widestring", "windows-service", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "winreg 0.51.0", ] @@ -3948,7 +3948,7 @@ dependencies = [ "triggered", "uuid", "widestring", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "winreg 0.51.0", ] @@ -3968,7 +3968,7 @@ dependencies = [ "tonic", "tonic-build", "tower", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "winres", ] @@ -3978,7 +3978,7 @@ version = "0.0.0" dependencies = [ "rs-release", "talpid-dbus", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4001,7 +4001,7 @@ dependencies = [ "thiserror", "tokio", "widestring", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4029,7 +4029,7 @@ dependencies = [ "thiserror", "tokio", "tun", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4047,7 +4047,7 @@ dependencies = [ "tonic", "tonic-build", "tower", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "zeroize", ] @@ -4073,7 +4073,7 @@ dependencies = [ "socket2", "talpid-types", "thiserror", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4114,7 +4114,7 @@ dependencies = [ "tokio-stream", "tunnel-obfuscation", "widestring", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "zeroize", ] diff --git a/Cargo.toml b/Cargo.toml index b0c28f016aa8..aa3b9145c884 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ log = "0.4" shadowsocks = { version = "1.16" } shadowsocks-service = { version = "1.16" } -windows-sys = "0.48.0" +windows-sys = "0.52.0" chrono = { version = "0.4.26", default-features = false} clap = { version = "4.4.18", features = ["cargo", "derive"] } diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index 4f7217056134..86767ff18d3e 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -65,7 +65,7 @@ objc = { version = "0.2.7", features = ["exception", "verify_message"] } [target.'cfg(windows)'.dependencies] ctrlc = "3.0" windows-service = "0.6.0" -winapi = { version = "0.3", features = ["winnt", "excpt"] } +winapi = { version = "0.3", features = ["winnt", "excpt", "winerror"] } dirs = "5.0.1" talpid-windows = { path = "../talpid-windows" } diff --git a/mullvad-daemon/src/migrations/mod.rs b/mullvad-daemon/src/migrations/mod.rs index ac66a95c3b2a..9455b97597d6 100644 --- a/mullvad-daemon/src/migrations/mod.rs +++ b/mullvad-daemon/src/migrations/mod.rs @@ -208,13 +208,12 @@ mod windows { use talpid_types::ErrorExt; use tokio::fs; use windows_sys::Win32::{ - Foundation::{ERROR_SUCCESS, HANDLE, PSID}, + Foundation::{LocalFree, ERROR_SUCCESS, HLOCAL, PSID}, Security::{ Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT, SE_OBJECT_TYPE}, IsWellKnownSid, WinBuiltinAdministratorsSid, WinLocalSystemSid, OWNER_SECURITY_INFORMATION, SECURITY_DESCRIPTOR, SID, WELL_KNOWN_SID_TYPE, }, - System::Memory::LocalFree, }; #[allow(non_camel_case_types)] @@ -386,7 +385,7 @@ mod windows { impl Drop for SecurityInformation { fn drop(&mut self) { - unsafe { LocalFree(self.security_descriptor as HANDLE) }; + unsafe { LocalFree(self.security_descriptor as HLOCAL) }; } } diff --git a/mullvad-paths/src/windows.rs b/mullvad-paths/src/windows.rs index 660a54ff031b..2898e76ba8c1 100644 --- a/mullvad-paths/src/windows.rs +++ b/mullvad-paths/src/windows.rs @@ -12,8 +12,8 @@ use windows_sys::{ core::{GUID, PWSTR}, Win32::{ Foundation::{ - CloseHandle, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS, GENERIC_ALL, GENERIC_READ, - HANDLE, INVALID_HANDLE_VALUE, LUID, S_OK, + CloseHandle, LocalFree, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS, GENERIC_ALL, + GENERIC_READ, HANDLE, INVALID_HANDLE_VALUE, LUID, S_OK, }, Security::{ self, AdjustTokenPrivileges, @@ -31,7 +31,6 @@ use windows_sys::{ Storage::FileSystem::MAX_SID_SIZE, System::{ Com::CoTaskMemFree, - Memory::LocalFree, ProcessStatus::EnumProcesses, Threading::{ GetCurrentThread, OpenProcess, OpenProcessToken, OpenThreadToken, @@ -244,7 +243,7 @@ fn set_security_permissions(path: &Path) -> Result<()> { ) }; - unsafe { LocalFree(new_dacl as isize) }; + unsafe { LocalFree(new_dacl.cast()) }; if result != ERROR_SUCCESS { Err(Error::SetDirPermissionFailed( @@ -402,7 +401,8 @@ fn get_known_folder_path( user_token: HANDLE, ) -> std::io::Result { let mut folder_path: PWSTR = ptr::null_mut(); - let status = unsafe { SHGetKnownFolderPath(folder_id, flags, user_token, &mut folder_path) }; + let status = + unsafe { SHGetKnownFolderPath(folder_id, flags as u32, user_token, &mut folder_path) }; let result = if status == S_OK { let path = unsafe { WideCStr::from_ptr_str(folder_path) }; Ok(PathBuf::from(path.to_os_string())) diff --git a/talpid-core/src/dns/windows/iphlpapi.rs b/talpid-core/src/dns/windows/iphlpapi.rs index fa47a695c15f..994b888d901a 100644 --- a/talpid-core/src/dns/windows/iphlpapi.rs +++ b/talpid-core/src/dns/windows/iphlpapi.rs @@ -18,14 +18,12 @@ use windows_sys::{ core::GUID, s, w, Win32::{ - Foundation::{ERROR_PROC_NOT_FOUND, WIN32_ERROR}, + Foundation::{FreeLibrary, ERROR_PROC_NOT_FOUND, WIN32_ERROR}, NetworkManagement::IpHelper::{ DNS_INTERFACE_SETTINGS, DNS_INTERFACE_SETTINGS_VERSION1, DNS_SETTING_IPV6, DNS_SETTING_NAMESERVER, }, - System::LibraryLoader::{ - FreeLibrary, GetProcAddress, LoadLibraryExW, LOAD_LIBRARY_SEARCH_SYSTEM32, - }, + System::LibraryLoader::{GetProcAddress, LoadLibraryExW, LOAD_LIBRARY_SEARCH_SYSTEM32}, }, }; diff --git a/talpid-core/src/split_tunnel/windows/windows.rs b/talpid-core/src/split_tunnel/windows/windows.rs index c20a83708960..739048b9a31c 100644 --- a/talpid-core/src/split_tunnel/windows/windows.rs +++ b/talpid-core/src/split_tunnel/windows/windows.rs @@ -10,11 +10,10 @@ use std::{ }; use windows_sys::Win32::{ Foundation::{CloseHandle, ERROR_INSUFFICIENT_BUFFER, FILETIME, HANDLE}, - Storage::FileSystem::{GetFinalPathNameByHandleW, QueryDosDeviceW}, + Storage::FileSystem::{GetFinalPathNameByHandleW, QueryDosDeviceW, VOLUME_NAME_NT}, System::{ ProcessStatus::GetProcessImageFileNameW, Threading::{GetProcessTimes, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION}, - WindowsProgramming::VOLUME_NAME_NT, }, }; diff --git a/talpid-openvpn/src/wintun.rs b/talpid-openvpn/src/wintun.rs index 32d9ddc283c0..742d4a910be7 100644 --- a/talpid-openvpn/src/wintun.rs +++ b/talpid-openvpn/src/wintun.rs @@ -5,13 +5,11 @@ use widestring::{U16CStr, U16CString}; use windows_sys::{ core::GUID, Win32::{ - Foundation::HMODULE, + Foundation::{FreeLibrary, HMODULE}, NetworkManagement::{IpHelper::ConvertInterfaceLuidToGuid, Ndis::NET_LUID_LH}, System::{ Com::StringFromGUID2, - LibraryLoader::{ - FreeLibrary, GetProcAddress, LoadLibraryExW, LOAD_WITH_ALTERED_SEARCH_PATH, - }, + LibraryLoader::{GetProcAddress, LoadLibraryExW, LOAD_WITH_ALTERED_SEARCH_PATH}, Registry::REG_SAM_FLAGS, }, }, diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs index b914164b2f08..375f28844a48 100644 --- a/talpid-wireguard/src/wireguard_nt/mod.rs +++ b/talpid-wireguard/src/wireguard_nt/mod.rs @@ -27,14 +27,12 @@ use widestring::{U16CStr, U16CString}; use windows_sys::{ core::GUID, Win32::{ - Foundation::{BOOL, ERROR_MORE_DATA, HMODULE}, + Foundation::{FreeLibrary, BOOL, ERROR_MORE_DATA, HMODULE}, NetworkManagement::Ndis::NET_LUID_LH, Networking::WinSock::{ ADDRESS_FAMILY, AF_INET, AF_INET6, IN6_ADDR, IN_ADDR, SOCKADDR_INET, }, - System::LibraryLoader::{ - FreeLibrary, GetProcAddress, LoadLibraryExW, LOAD_WITH_ALTERED_SEARCH_PATH, - }, + System::LibraryLoader::{GetProcAddress, LoadLibraryExW, LOAD_WITH_ALTERED_SEARCH_PATH}, }, }; diff --git a/test/Cargo.lock b/test/Cargo.lock index 4060ab9977eb..b72379e9a093 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -98,46 +98,46 @@ checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" dependencies = [ "backtrace", ] [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" @@ -170,7 +170,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -185,20 +185,20 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "axum" @@ -247,9 +247,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -262,9 +262,9 @@ dependencies = [ [[package]] name = "base16ct" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" @@ -274,9 +274,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" [[package]] name = "base64ct" @@ -292,15 +298,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec", @@ -320,9 +326,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte_string" @@ -331,10 +337,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11aade7a05aa8c3a351cedc44c3fc45806430543382fcc4743a9b757a2a0b4ed" [[package]] -name = "bytes" +name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "camellia" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" +dependencies = [ + "byteorder", + "cipher", +] [[package]] name = "cbindgen" @@ -342,7 +364,7 @@ version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" dependencies = [ - "heck", + "heck 0.4.1", "indexmap 1.9.3", "log", "proc-macro2", @@ -356,12 +378,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cesu8" @@ -401,15 +420,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -425,9 +444,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.6" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -435,9 +454,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -447,27 +466,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -508,9 +527,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -526,14 +545,14 @@ dependencies = [ "ping", "reqwest", "serde", - "socket2 0.5.4", + "socket2 0.5.6", ] [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" @@ -543,9 +562,9 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -553,43 +572,39 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-bigint" -version = "0.4.9" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -635,26 +650,26 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "data-encoding-macro" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -662,9 +677,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" dependencies = [ "data-encoding", "syn 1.0.109", @@ -683,9 +698,9 @@ dependencies = [ [[package]] name = "der" -version = "0.6.1" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -734,9 +749,9 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.14.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "elliptic-curve", @@ -745,10 +760,11 @@ dependencies = [ [[package]] name = "ed25519" -version = "1.5.3" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ + "pkcs8", "signature", ] @@ -766,21 +782,22 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elliptic-curve" -version = "0.12.3" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "der", "digest", + "ff", "generic-array", + "group", "rand_core 0.6.4", "sec1", "subtle", @@ -789,9 +806,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -802,10 +819,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -818,7 +835,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -893,9 +910,9 @@ dependencies = [ [[package]] name = "fast-socks5" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcc731f3c17a5053e07e6a2290918da75cd8b9b1217b419721f715674ac520c" +checksum = "f89f36d4ee12370d30d57b16c7e190950a1a916e7dbbb5fd5a412f5ef913fe84" dependencies = [ "anyhow", "async-trait", @@ -907,26 +924,36 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "ff" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] [[package]] name = "fiat-crypto" -version = "0.2.1" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", ] [[package]] @@ -943,9 +970,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -961,9 +988,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -976,9 +1003,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -986,15 +1013,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1003,38 +1030,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1056,6 +1083,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1071,9 +1099,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -1082,9 +1110,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", @@ -1092,9 +1120,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -1102,6 +1130,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.26" @@ -1129,9 +1168,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -1139,11 +1178,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1151,11 +1196,56 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand 0.8.5", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot 0.12.1", + "rand 0.8.5", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] @@ -1169,15 +1259,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - [[package]] name = "hostname" version = "0.3.1" @@ -1191,9 +1272,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1202,9 +1283,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1231,9 +1312,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1246,7 +1327,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -1255,9 +1336,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -1267,7 +1348,7 @@ dependencies = [ "rustls-native-certs", "tokio", "tokio-rustls", - "webpki-roots 0.23.1", + "webpki-roots", ] [[package]] @@ -1284,16 +1365,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1315,6 +1396,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indenter" version = "0.3.3" @@ -1338,7 +1429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.3", ] [[package]] @@ -1385,7 +1476,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -1396,9 +1487,9 @@ checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "io-kit-sys" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4769cb30e5dcf1710fc6730d3e94f78c47723a014a567de385e113c737394640" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" dependencies = [ "core-foundation-sys", "mach2", @@ -1416,7 +1507,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.4", + "socket2 0.5.6", "widestring", "windows-sys 0.48.0", "winreg", @@ -1424,9 +1515,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "ipnetwork" @@ -1455,15 +1546,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -1475,9 +1557,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jni" @@ -1517,7 +1599,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "002f4dfe6d97ae88c33f3489c0d31ffc6f81d9a492de98ff113b127d73bafff8" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 1.0.109", @@ -1525,9 +1607,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1583,6 +1665,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "libssh2-sys" version = "0.3.0" @@ -1599,9 +1691,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", "libc", @@ -1629,9 +1721,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1685,9 +1777,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" @@ -1715,9 +1807,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -1762,7 +1854,7 @@ dependencies = [ "mullvad-fs", "mullvad-types", "once_cell", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "serde", "serde_json", "shadowsocks", @@ -1815,7 +1907,7 @@ dependencies = [ "once_cell", "thiserror", "widestring", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1854,9 +1946,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "nix" @@ -1910,7 +2002,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1942,19 +2034,18 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -1971,24 +2062,24 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" @@ -1998,9 +2089,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -2041,9 +2132,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "p256" -version = "0.11.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa", "elliptic-curve", @@ -2051,12 +2142,13 @@ dependencies = [ [[package]] name = "p384" -version = "0.11.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" dependencies = [ "ecdsa", "elliptic-curve", + "primeorder", ] [[package]] @@ -2091,7 +2183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.8", + "parking_lot_core 0.9.9", ] [[package]] @@ -2110,13 +2202,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -2140,9 +2232,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" @@ -2156,29 +2248,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2193,15 +2285,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "122ee1f5a6843bec84fcbd5c6ba3622115337a6b8965b93a61aad347648f4e8d" dependencies = [ "rand 0.8.5", - "socket2 0.4.9", + "socket2 0.4.10", "thiserror", ] [[package]] name = "pkcs8" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", @@ -2209,15 +2301,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "platforms" -version = "3.1.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "plist" @@ -2225,7 +2317,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "indexmap 2.2.6", "line-wrap", "quick-xml", @@ -2244,9 +2336,9 @@ dependencies = [ [[package]] name = "pnet_base" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872e46346144ebf35219ccaa64b1dffacd9c6f188cd7d012bd6977a2a838f42e" +checksum = "fe4cf6fb3ab38b68d01ab2aea03ed3d1132b4868fa4e06285f29f16da01c5f4c" dependencies = [ "no-std-net", ] @@ -2265,14 +2357,14 @@ dependencies = [ [[package]] name = "pnet_macros" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a780e80005c2e463ec25a6e9f928630049a10b43945fea83207207d4a7606f4" +checksum = "688b17499eee04a0408aca0aa5cba5fc86401d7216de8a63fdf7a4c227871804" dependencies = [ "proc-macro2", "quote", "regex", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -2286,11 +2378,11 @@ dependencies = [ [[package]] name = "pnet_macros_support" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d932134f32efd7834eb8b16d42418dac87086347d1bc7d142370ef078582bc" +checksum = "eea925b72f4bd37f8eab0f221bbe4c78b63498350c983ffa9dd4bcde7e030f56" dependencies = [ - "pnet_base 0.33.0", + "pnet_base 0.34.0", ] [[package]] @@ -2307,14 +2399,14 @@ dependencies = [ [[package]] name = "pnet_packet" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bde678bbd85cb1c2d99dc9fc596e57f03aa725f84f3168b0eaf33eeccb41706" +checksum = "a9a005825396b7fe7a38a8e288dbc342d5034dac80c15212436424fef8ea90ba" dependencies = [ "glob", - "pnet_base 0.33.0", - "pnet_macros 0.33.0", - "pnet_macros_support 0.33.0", + "pnet_base 0.34.0", + "pnet_macros 0.34.0", + "pnet_macros_support 0.34.0", ] [[package]] @@ -2330,9 +2422,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", @@ -2354,28 +2446,37 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", - "syn 2.0.51", + "syn 2.0.60", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", ] [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", "prost-derive", @@ -2383,13 +2484,13 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", - "heck", - "itertools 0.11.0", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -2398,29 +2499,28 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.51", + "syn 2.0.60", "tempfile", - "which", ] [[package]] name = "prost-derive" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "prost-types" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ "prost", ] @@ -2442,9 +2542,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -2508,7 +2608,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.14", ] [[package]] @@ -2531,29 +2631,29 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", + "getrandom 0.2.14", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.9.6" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -2563,9 +2663,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -2574,17 +2674,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.24" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -2602,7 +2702,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", @@ -2615,7 +2715,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.4", + "webpki-roots", "winreg", ] @@ -2629,21 +2729,6 @@ dependencies = [ "quick-error", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.8" @@ -2652,29 +2737,29 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.10", + "getrandom 0.2.14", "libc", - "spin 0.9.8", - "untrusted 0.9.0", + "spin", + "untrusted", "windows-sys 0.52.0", ] [[package]] name = "ring-compat" -version = "0.5.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333b9bf6765e0141324d95b5375bb1aa5267865bb4bc0281c22aff22f5d37746" +checksum = "ccce7bae150b815f0811db41b8312fcb74bffa4cab9cee5429ee00f356dd5bd4" dependencies = [ "aead", "digest", "ecdsa", "ed25519", "generic-array", - "opaque-debug", "p256", "p384", "pkcs8", - "ring 0.16.20", + "rand_core 0.6.4", + "ring", "signature", ] @@ -2701,11 +2786,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "errno 0.3.8", "libc", "linux-raw-sys", @@ -2719,8 +2804,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", - "ring 0.17.8", - "rustls-webpki 0.101.7", + "ring", + "rustls-webpki", "sct", ] @@ -2731,7 +2816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "schannel", "security-framework", ] @@ -2747,21 +2832,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.4", -] - -[[package]] -name = "rustls-webpki" -version = "0.100.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "base64 0.21.7", ] [[package]] @@ -2770,21 +2845,21 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -2797,11 +2872,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2812,19 +2887,19 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring", + "untrusted", ] [[package]] name = "sec1" -version = "0.3.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -2835,9 +2910,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -2848,9 +2923,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -2858,9 +2933,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "sendfd" @@ -2874,29 +2949,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -2921,7 +2996,7 @@ version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5a15d0be940df84846264b09b51b10b931fb2f275becb80934e3568a016828" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "cfg-if", "core-foundation-sys", "io-kit-sys", @@ -2946,18 +3021,19 @@ dependencies = [ [[package]] name = "shadowsocks" -version = "1.16.0" +version = "1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d4aadc3b1b38e760533d4060a1aa53a2d754f073389f5aafe6bf7b579c4f97" +checksum = "ed5edddeff89d9874fa59366cfc506b53525410f129bbf13064ab36de15374e6" dependencies = [ "arc-swap", "async-trait", - "base64 0.21.4", + "base64 0.22.0", "blake3", "byte_string", "bytes", "cfg-if", "futures", + "hickory-resolver", "libc", "log", "notify", @@ -2969,24 +3045,24 @@ dependencies = [ "serde_json", "serde_urlencoded", "shadowsocks-crypto", - "socket2 0.5.4", - "spin 0.9.8", + "socket2 0.5.6", + "spin", "thiserror", "tokio", "tokio-tfo", - "trust-dns-resolver", "url", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "shadowsocks-crypto" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb488687e398030dd9c9396e119ddbc6952bdeaefe2168943b5b2ddaa54f2e6" +checksum = "65da645ff4a6440ba1b52a9d6b4c8792054860ac135cb87f8ad3d2c7a78d41b5" dependencies = [ "aes", "aes-gcm", + "camellia", "cfg-if", "chacha20", "chacha20poly1305", @@ -3000,27 +3076,27 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "signature" -version = "1.6.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "rand_core 0.6.4", ] @@ -3036,15 +3112,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -3052,12 +3128,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3071,12 +3147,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -3088,9 +3158,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.6.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -3116,9 +3186,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -3128,15 +3198,15 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "surge-ping" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af341b2be485d647b5dc4cfb2da99efac35b5c95748a08fb7233480fedc5ead3" +checksum = "efbf95ce4c7c5b311d2ce3f088af2b93edef0f09727fa50fbe03c7a979afce77" dependencies = [ "hex", "parking_lot 0.12.1", - "pnet_packet 0.33.0", + "pnet_packet 0.34.0", "rand 0.8.5", - "socket2 0.5.4", + "socket2 0.5.6", "thiserror", "tokio", "tracing", @@ -3155,9 +3225,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.51" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -3209,7 +3279,7 @@ version = "0.0.0" dependencies = [ "rs-release", "talpid-dbus", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3224,7 +3294,7 @@ dependencies = [ name = "talpid-types" version = "0.0.0" dependencies = [ - "base64 0.13.1", + "base64 0.22.0", "ipnetwork 0.16.0", "jnix", "log", @@ -3239,10 +3309,10 @@ name = "talpid-windows" version = "0.0.0" dependencies = [ "futures", - "socket2 0.5.4", + "socket2 0.5.6", "talpid-types", "thiserror", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3282,15 +3352,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3383,7 +3452,7 @@ dependencies = [ "rs-release", "serde", "serde_json", - "socket2 0.5.4", + "socket2 0.5.6", "surge-ping", "talpid-platform-metadata", "talpid-windows", @@ -3411,29 +3480,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3441,9 +3510,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -3462,9 +3531,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -3487,9 +3556,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -3499,7 +3568,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.6", "tokio-macros", "windows-sys 0.48.0", ] @@ -3516,13 +3585,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -3577,9 +3646,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -3598,16 +3667,16 @@ dependencies = [ "log", "once_cell", "pin-project", - "socket2 0.5.4", + "socket2 0.5.6", "tokio", "windows-sys 0.48.0", ] [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -3636,7 +3705,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.4", + "base64 0.21.7", "bytes", "h2", "http", @@ -3664,7 +3733,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] @@ -3701,11 +3770,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -3714,20 +3782,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -3758,66 +3826,20 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "sharded-slab", "thread_local", "tracing-core", ] -[[package]] -name = "trust-dns-proto" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc775440033cb114085f6f2437682b194fa7546466024b1037e82a48a052a69" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "once_cell", - "rand 0.8.5", - "smallvec", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff7aed33ef3e8bf2c9966fccdfed93f93d46f432282ea875cd66faabc6ef2f" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot 0.12.1", - "rand 0.8.5", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "tracing", - "trust-dns-proto", -] - [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tun" @@ -3847,9 +3869,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -3859,9 +3881,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -3876,12 +3898,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -3890,12 +3906,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] @@ -3907,11 +3923,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.14", "serde", ] @@ -3935,9 +3951,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -3966,9 +3982,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3976,24 +3992,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -4003,9 +4019,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4013,65 +4029,44 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - [[package]] name = "webpki-roots" version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -4091,11 +4086,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4105,12 +4100,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -4161,7 +4156,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -4196,17 +4191,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -4223,9 +4219,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -4247,9 +4243,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -4271,9 +4267,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -4295,9 +4297,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4319,9 +4321,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4337,9 +4339,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -4361,9 +4363,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winreg" @@ -4389,9 +4391,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "63381fa6624bf92130a6b87c0d07380116f80b565c42cf0d754136f0238359ef" dependencies = [ "zeroize_derive", ] @@ -4404,5 +4406,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.60", ] diff --git a/test/Cargo.toml b/test/Cargo.toml index 4730c72e78a5..c0939eac971d 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -67,7 +67,7 @@ colored = "2.0.0" shadowsocks = { version = "1.16" } shadowsocks-service = { version = "1.16" } -windows-sys = "0.48.0" +windows-sys = "0.52.0" chrono = { version = "0.4.26", default-features = false } clap = { version = "4.2.7", features = ["cargo", "derive"] } From 33063c8634cfbc4b0c5e0ce5787c2442f8129bd2 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 25 Apr 2024 14:37:06 +0200 Subject: [PATCH 168/214] Fix use of deprecated socket2 function --- talpid-wireguard/src/ping_monitor/icmp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/talpid-wireguard/src/ping_monitor/icmp.rs b/talpid-wireguard/src/ping_monitor/icmp.rs index ff709e4532c5..0e5d73942527 100644 --- a/talpid-wireguard/src/ping_monitor/icmp.rs +++ b/talpid-wireguard/src/ping_monitor/icmp.rs @@ -90,7 +90,7 @@ impl Pinger { // Asserting that `index` is non-zero since otherwise `if_nametoindex` would have return // an error socket - .bind_device_by_index(std::num::NonZeroU32::new(index)) + .bind_device_by_index_v4(std::num::NonZeroU32::new(index)) .map_err(Error::BindSocketByDevice)?; Ok(()) From 5002832c576f4154dbbb8f120bc5a9365b92c2c2 Mon Sep 17 00:00:00 2001 From: Albin Date: Wed, 17 Apr 2024 09:33:27 +0200 Subject: [PATCH 169/214] Update changelog for android/2024.2-beta1 --- android/CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index dcb2a01618a2..e25d01f58d8b 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -22,9 +22,21 @@ Line wrap the file at 100 chars. Th * **Security**: in case of vulnerabilities. ## [Unreleased] + + +## [android/2024.2-beta1] - 2024-04-17 ### Added - Add the ability to create and manage custom lists of relays. -- Add Server IP overrides feature. +- Add the ability to import server IP overrides using text or file. + +### Changed +- Change [default retry connection attempts][`relay selector defaults`]. + +[`relay selector defaults`]: docs/relay-selector.md#default-constraints-for-tunnel-endpoints + +### Fixed +- Fix pointless API access method rotations for concurrent requests. +- Fix broken IPv6 connectivity by making sure the relay selector attempts IPv6 connections. ## [android/2024.1] - 2024-04-05 From e61a348d1ec028441fb180c2725c8c0988dbdae4 Mon Sep 17 00:00:00 2001 From: Albin Date: Tue, 23 Apr 2024 10:45:41 +0200 Subject: [PATCH 170/214] Update android app version to 2024.2-beta1 --- dist-assets/android-product-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist-assets/android-product-version.txt b/dist-assets/android-product-version.txt index 702b928acc8b..9fb1ab01cafd 100644 --- a/dist-assets/android-product-version.txt +++ b/dist-assets/android-product-version.txt @@ -1 +1 @@ -2024.1 +2024.2-beta1 From 463fb4b9fad05d95533e9973f69d57690cf69917 Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 25 Apr 2024 13:24:25 +0200 Subject: [PATCH 171/214] Update changelog with changes for 2024.2 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb13636d8260..1149d9627160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,12 @@ Line wrap the file at 100 chars. Th ### Added - Add custom bridge settings in GUI. + +## [2024.2] - 2024-04-29 ### Fixed +- Fix bug where the app would fail to select an existing relay in some scenarios, causing the + user to wrongly end up in a blocked state. + #### macOS - DNS was not properly restored in some cases when using custom DNS. From 52e40a2b2e4e6f4966e124a10e757b3f5f2cab6c Mon Sep 17 00:00:00 2001 From: Oskar Nyberg Date: Thu, 25 Apr 2024 13:27:53 +0200 Subject: [PATCH 172/214] Update desktop app version to 2024.2 --- dist-assets/desktop-product-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist-assets/desktop-product-version.txt b/dist-assets/desktop-product-version.txt index 9fb1ab01cafd..725ee7385be5 100644 --- a/dist-assets/desktop-product-version.txt +++ b/dist-assets/desktop-product-version.txt @@ -1 +1 @@ -2024.2-beta1 +2024.2 From cf980c865877f7ab5820527ef8a137b847e1d3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls?= Date: Tue, 6 Feb 2024 23:07:37 +0100 Subject: [PATCH 173/214] Add FFI to talpid-tunnel-config-client --- Cargo.lock | 23 + Cargo.toml | 3 +- ios/MullvadPostQuantum/MullvadPostQuantum.h | 19 + .../PacketTunnelProvider+TCPConnection.swift | 62 +++ .../PostQuantumKeyNegotiatior.swift | 35 ++ .../module.private.modulemap | 5 + .../talpid-tunnel-config-client/Cargo.toml | 46 ++ .../talpid-tunnel-config-client/build.rs | 18 + .../include/talpid_tunnel_config_client.h | 45 ++ .../src/ios_ffi.rs | 52 +++ .../src/ios_tcp_connection.rs | 123 ++++++ .../talpid-tunnel-config-client/src/lib.rs | 131 ++++++ ios/MullvadVPN.xcodeproj/project.pbxproj | 397 ++++++++++++++++++ .../xcschemes/MullvadPostQuantum.xcscheme | 66 +++ .../NWTCPConnection+Async.swift | 42 ++ .../PacketTunnelProvider.swift | 80 +++- .../Actor/ConfigurationBuilder.swift | 18 +- .../Actor/ObservedState.swift | 2 + .../Actor/PacketTunnelActor.swift | 2 +- ios/build-rust-library.sh | 20 +- talpid-routing/src/unix/mod.rs | 2 +- talpid-tunnel-config-client/Cargo.toml | 15 +- talpid-tunnel-config-client/src/lib.rs | 1 - 23 files changed, 1186 insertions(+), 21 deletions(-) create mode 100644 ios/MullvadPostQuantum/MullvadPostQuantum.h create mode 100644 ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift create mode 100644 ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift create mode 100644 ios/MullvadPostQuantum/module.private.modulemap create mode 100644 ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml create mode 100644 ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs create mode 100644 ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h create mode 100644 ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs create mode 100644 ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs create mode 100644 ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs create mode 100644 ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantum.xcscheme create mode 100644 ios/PacketTunnel/PacketTunnelProvider/NWTCPConnection+Async.swift diff --git a/Cargo.lock b/Cargo.lock index d39c7fd3639b..f9fbf51afb08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4036,6 +4036,7 @@ dependencies = [ name = "talpid-tunnel-config-client" version = "0.0.0" dependencies = [ + "cbindgen", "classic-mceliece-rust", "libc", "log", @@ -4051,6 +4052,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "talpid-tunnel-config-client-proxy" +version = "0.0.0" +dependencies = [ + "cbindgen", + "classic-mceliece-rust", + "futures", + "libc", + "log", + "oslog", + "pqc_kyber", + "prost", + "rand 0.8.5", + "talpid-tunnel-config-client", + "talpid-types", + "tokio", + "tonic", + "tonic-build", + "tower", + "zeroize", +] + [[package]] name = "talpid-types" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index aa3b9145c884..173f8d8f6ebe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "android/translations-converter", "ios/MullvadREST/Transport/Shadowsocks/shadowsocks-proxy", "ios/TunnelObfuscation/tunnel-obfuscator-proxy", + "ios/MullvadPostQuantum/talpid-tunnel-config-client", "mullvad-api", "mullvad-cli", "mullvad-daemon", @@ -80,7 +81,7 @@ shadowsocks-service = { version = "1.16" } windows-sys = "0.52.0" -chrono = { version = "0.4.26", default-features = false} +chrono = { version = "0.4.26", default-features = false } clap = { version = "4.4.18", features = ["cargo", "derive"] } once_cell = "1.13" diff --git a/ios/MullvadPostQuantum/MullvadPostQuantum.h b/ios/MullvadPostQuantum/MullvadPostQuantum.h new file mode 100644 index 000000000000..f48ca77519ac --- /dev/null +++ b/ios/MullvadPostQuantum/MullvadPostQuantum.h @@ -0,0 +1,19 @@ +// +// MullvadPostQuantum.h +// MullvadPostQuantum +// +// Created by Marco Nikic on 2024-02-27. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +#import + +//! Project version number for MullvadPostQuantum. +FOUNDATION_EXPORT double MullvadPostQuantumVersionNumber; + +//! Project version string for MullvadPostQuantum. +FOUNDATION_EXPORT const unsigned char MullvadPostQuantumVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift new file mode 100644 index 000000000000..8796498cddbb --- /dev/null +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -0,0 +1,62 @@ +// +// PacketTunnelProvider+TCPConnection.swift +// PacketTunnel +// +// Created by Marco Nikic on 2024-02-15. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import MullvadTypes +import NetworkExtension +import TalpidTunnelConfigClientProxy +import WireGuardKitTypes + +@_cdecl("swift_nw_tcp_connection_send") +func tcpConnectionSend( + connection: UnsafeMutableRawPointer, + data: UnsafeMutableRawPointer, + dataLength: UInt, + sender: UnsafeMutableRawPointer +) { + let tcpConnection = Unmanaged.fromOpaque(connection).takeUnretainedValue() + let rawData = Data(bytes: data, count: Int(dataLength)) + + // The guarantee that no more than 2 writes happen in parallel is done by virtue of not returning the execution context + // to Rust before this closure is done executing. + tcpConnection.write(rawData, completionHandler: { maybeError in + if maybeError != nil { + handle_sent(0, sender) + } else { + handle_sent(dataLength, sender) + } + }) +} + +@_cdecl("swift_nw_tcp_connection_read") +func tcpConnectionReceive( + connection: UnsafeMutableRawPointer, + sender: UnsafeMutableRawPointer +) { + let tcpConnection = Unmanaged.fromOpaque(connection).takeUnretainedValue() + tcpConnection.readMinimumLength(0, maximumLength: Int(UInt16.max)) { data, maybeError in + if let data { + if maybeError != nil { + handle_recv(Data().map { $0 }, 0, sender) + } else { + handle_recv(data.map { $0 }, UInt(data.count), sender) + } + } + } +} + +@_cdecl("swift_post_quantum_key_ready") +func receivePostQuantumKey(rawPacketTunnel: UnsafeMutableRawPointer, rawPresharedKey: UnsafeMutableRawPointer) { + let packetTunnel = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() + // TODO: The `rawPresharedKey` pointer might be null, this means the key exchanged failed, and we should try from the start again + let presharedKey = Data(bytes: rawPresharedKey, count: 32) + if let postQuantumKeyReceiver = packetTunnel as? PostQuantumKeyReceiving, + let key = PreSharedKey(rawValue: presharedKey) { + postQuantumKeyReceiver.receivePostQuantumKey(key) + } +} diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift new file mode 100644 index 000000000000..5908312506ba --- /dev/null +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift @@ -0,0 +1,35 @@ +// +// PostQuantumKeyNegotiatior.swift +// PacketTunnel +// +// Created by Marco Nikic on 2024-02-16. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import NetworkExtension +import TalpidTunnelConfigClientProxy +import WireGuardKitTypes + +public struct PostQuantumKeyNegotiatior { + public init() {} + + public func negotiateKey( + gatewayIP: IPv4Address, + devicePublicKey: PublicKey, + presharedKey: PublicKey, + packetTunnel: NEPacketTunnelProvider, + tcpConnection: NWTCPConnection + ) { + let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel).toOpaque() + let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque() + + // TODO: Any non 0 return is considered a failure, and should be handled gracefully + negotiate_post_quantum_key( + devicePublicKey.rawValue.map { $0 }, + presharedKey.rawValue.map { $0 }, + packetTunnelPointer, + opaqueConnection + ) + } +} diff --git a/ios/MullvadPostQuantum/module.private.modulemap b/ios/MullvadPostQuantum/module.private.modulemap new file mode 100644 index 000000000000..ff5f63eee8d6 --- /dev/null +++ b/ios/MullvadPostQuantum/module.private.modulemap @@ -0,0 +1,5 @@ +framework module TalpidTunnelConfigClientProxy { + header "talpid_tunnel_config_client.h" + link "libtalpid_tunnel_config_client_proxy" + export * +} diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml b/ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml new file mode 100644 index 000000000000..902882201ab7 --- /dev/null +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "talpid-tunnel-config-client-proxy" +description = "Uses the relay RPC service to set up PQ-safe peers, etc." +authors.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] +talpid-tunnel-config-client = { path = "../../../talpid-tunnel-config-client" } +talpid-types = { path = "../../../talpid-types" } + +log = { workspace = true } +rand = "0.8" +tonic = { workspace = true } +futures = "0.3" +tower = { workspace = true } +prost = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +classic-mceliece-rust = { version = "2.0.0", features = [ + "mceliece460896f", + "zeroize", +] } +pqc_kyber = { version = "0.4.0", features = ["std", "kyber1024", "zeroize"] } +zeroize = "1.5.7" +libc = "0.2" + +[lib] +crate-type = ["rlib", "staticlib"] +bench = false + +[dev-dependencies] +tokio = { workspace = true, features = ["rt-multi-thread"] } +[build-dependencies] +tonic-build = { workspace = true, default-features = false, features = [ + "transport", + "prost", +] } +cbindgen = { version = "0.24.3", default-features = false } + +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +oslog = "0.2" diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs new file mode 100644 index 000000000000..3a4f92f421c5 --- /dev/null +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs @@ -0,0 +1,18 @@ +fn main() { + tonic_build::compile_protos("../../../talpid-tunnel-config-client/proto/tunnel_config.proto") + .unwrap(); + + match std::env::var("TARGET").unwrap().as_str() { + "aarch64-apple-ios" | "aarch64-apple-ios-sim" => { + println!("cargo:rerun-if-changed=include/talpid_tunnel_config_client.h"); + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_language(cbindgen::Language::C) + .generate() + .expect("failed to generate bindings") + .write_to_file("include/talpid_tunnel_config_client.h"); + } + &_ => (), + } +} diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h new file mode 100644 index 000000000000..19c9cd159f33 --- /dev/null +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +/** + * Callback to call when the TCP connection has written data. + */ +void handle_sent(uintptr_t bytes_sent, const void *sender); + +/** + * Callback to call when the TCP connection has received data. + */ +void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender); + +/** + * Entry point for exchanging post quantum keys on iOS. + * The TCP connection must be created to go through the tunnel. + */ +int32_t negotiate_post_quantum_key(const uint8_t *public_key, + const uint8_t *ephemeral_public_key, + const void *packet_tunnel, + const void *tcp_connection); + +/** + * Called when there is data to send on the TCP connection. + * The TCP connection must write data on the wire, then call the `handle_sent` function. + */ +extern void swift_nw_tcp_connection_send(const void *connection, + const void *data, + uintptr_t data_len, + const void *sender); + +/** + * Called when there is data to read on the TCP connection. + * The TCP connection must read data from the wire, then call the `handle_read` function. + */ +extern void swift_nw_tcp_connection_read(const void *connection, const void *sender); + +/** + * Called when the preshared post quantum key is ready. + * `raw_preshared_key` might be NULL if the key negotiation failed. + */ +extern void swift_post_quantum_key_ready(const void *raw_packet_tunnel, + const uint8_t *raw_preshared_key); diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs new file mode 100644 index 000000000000..32774c94d724 --- /dev/null +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs @@ -0,0 +1,52 @@ +use libc::c_void; +use tokio::sync::mpsc; + +use super::run_ios_runtime; + +use std::sync::Once; +static INIT_LOGGING: Once = Once::new(); + +/// Callback to call when the TCP connection has written data. +#[no_mangle] +pub unsafe extern "C" fn handle_sent(bytes_sent: usize, sender: *const c_void) { + let send_tx: Box> = unsafe { Box::from_raw(sender as _) }; + _ = send_tx.send(bytes_sent); +} + +/// Callback to call when the TCP connection has received data. +#[no_mangle] +pub unsafe extern "C" fn handle_recv(data: *const u8, data_len: usize, sender: *const c_void) { + let read_tx: Box>> = unsafe { Box::from_raw(sender as _) }; + let mut bytes = vec![0u8; data_len]; + if !data.is_null() { + std::ptr::copy_nonoverlapping(data, bytes.as_mut_ptr(), data_len); + } + _ = read_tx.send(bytes.into_boxed_slice()); +} + +/// Entry point for exchanging post quantum keys on iOS. +/// The TCP connection must be created to go through the tunnel. +#[no_mangle] +pub unsafe extern "C" fn negotiate_post_quantum_key( + public_key: *const u8, + ephemeral_public_key: *const u8, + packet_tunnel: *const libc::c_void, + tcp_connection: *const libc::c_void, +) -> i32 { + INIT_LOGGING.call_once(|| { + let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.TTCC") + .level_filter(log::LevelFilter::Trace) + .init(); + }); + + let pub_key_copy: [u8; 32] = unsafe { std::ptr::read(public_key as *const [u8; 32]) }; + let eph_pub_key_copy: [u8; 32] = + unsafe { std::ptr::read(ephemeral_public_key as *const [u8; 32]) }; + + run_ios_runtime( + pub_key_copy, + eph_pub_key_copy, + packet_tunnel, + tcp_connection, + ) +} diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs new file mode 100644 index 000000000000..a93eec0fa8a1 --- /dev/null +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs @@ -0,0 +1,123 @@ +use libc::c_void; +use std::io; +use std::io::Result; +use std::task::Poll; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::sync::mpsc; + +extern "C" { + /// Called when there is data to send on the TCP connection. + /// The TCP connection must write data on the wire, then call the `handle_sent` function. + pub fn swift_nw_tcp_connection_send( + connection: *const libc::c_void, + data: *const libc::c_void, + data_len: usize, + sender: *const libc::c_void, + ); + + /// Called when there is data to read on the TCP connection. + /// The TCP connection must read data from the wire, then call the `handle_read` function. + pub fn swift_nw_tcp_connection_read( + connection: *const libc::c_void, + sender: *const libc::c_void, + ); + + /// Called when the preshared post quantum key is ready. + /// `raw_preshared_key` might be NULL if the key negotiation failed. + pub fn swift_post_quantum_key_ready( + raw_packet_tunnel: *const c_void, + raw_preshared_key: *const u8, + ); +} + +unsafe impl Send for IosTcpProvider {} + +pub struct IosTcpProvider { + write_tx: mpsc::UnboundedSender, + write_rx: mpsc::UnboundedReceiver, + read_tx: mpsc::UnboundedSender>, + read_rx: mpsc::UnboundedReceiver>, + tcp_connection: *const c_void, +} + +impl IosTcpProvider { + pub unsafe fn new(tcp_connection: *const c_void) -> Self { + let (tx, rx) = mpsc::unbounded_channel(); + let (recv_tx, recv_rx) = mpsc::unbounded_channel(); + Self { + write_tx: tx, + write_rx: rx, + read_tx: recv_tx, + read_rx: recv_rx, + tcp_connection, + } + } +} + +impl AsyncWrite for IosTcpProvider { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let raw_sender = Box::into_raw(Box::new(self.write_tx.clone())); + + match self.write_rx.poll_recv(cx) { + std::task::Poll::Ready(Some(bytes_sent)) => Poll::Ready(Ok(bytes_sent)), + std::task::Poll::Ready(None) => { + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "sender dropped"))) + } + std::task::Poll::Pending => { + unsafe { + swift_nw_tcp_connection_send( + self.tcp_connection, + buf.as_ptr() as _, + buf.len(), + raw_sender as _, + ); + } + std::task::Poll::Pending + } + } + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } + + fn poll_shutdown( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } +} +impl AsyncRead for IosTcpProvider { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + let raw_sender = Box::into_raw(Box::new(self.read_tx.clone())); + + match self.read_rx.poll_recv(cx) { + std::task::Poll::Ready(Some(data)) => { + buf.put_slice(&data); + Poll::Ready(Ok(())) + } + std::task::Poll::Ready(None) => { + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "sender dropped"))) + } + std::task::Poll::Pending => { + unsafe { + swift_nw_tcp_connection_read(self.tcp_connection, raw_sender as _); + } + + std::task::Poll::Pending + } + } + } +} diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs new file mode 100644 index 000000000000..5f972db13a51 --- /dev/null +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -0,0 +1,131 @@ +use std::ptr; + +use libc::c_void; + +use std::io; + +mod ios_ffi; +pub use ios_ffi::negotiate_post_quantum_key; + +use crate::ios_tcp_connection::swift_post_quantum_key_ready; +use crate::ios_tcp_connection::IosTcpProvider; +mod ios_tcp_connection; +use talpid_tunnel_config_client::Error; +use talpid_tunnel_config_client::RelayConfigService; +use talpid_types::net::wireguard::PublicKey; +use tonic::transport::Endpoint; +use tower::service_fn; + +pub unsafe fn run_ios_runtime( + pub_key: [u8; 32], + ephemeral_pub_key: [u8; 32], + packet_tunnel: *const c_void, + tcp_connection: *const c_void, +) -> i32 { + match IOSRuntime::new(pub_key, ephemeral_pub_key, packet_tunnel, tcp_connection) { + Ok(runtime) => { + runtime.run(); + 0 + } + Err(err) => { + log::error!("Failed to create runtime {}", err); + err.raw_os_error().unwrap_or(-1) + } + } +} + +#[derive(Clone)] +struct SwiftContext { + pub packet_tunnel: *const c_void, + pub tcp_connection: *const c_void, +} + +unsafe impl Send for SwiftContext {} +unsafe impl Sync for SwiftContext {} + +struct IOSRuntime { + runtime: tokio::runtime::Runtime, + pub_key: [u8; 32], + ephemeral_public_key: [u8; 32], + packet_tunnel: SwiftContext, +} + +impl IOSRuntime { + pub unsafe fn new( + pub_key: [u8; 32], + ephemeral_public_key: [u8; 32], + packet_tunnel: *const libc::c_void, + tcp_connection: *const c_void, + ) -> io::Result { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(2) + .build()?; + + let context = SwiftContext { + packet_tunnel, + tcp_connection, + }; + + Ok(Self { + runtime, + pub_key, + ephemeral_public_key, + packet_tunnel: context, + }) + } + + pub fn run(self) { + std::thread::spawn(move || { + self.run_service_inner(); + }); + } + + async fn ios_tcp_client(ctx: SwiftContext) -> Result { + let endpoint = Endpoint::from_static("tcp://0.0.0.0:0"); + let conn = endpoint + .connect_with_connector(service_fn(move |_| { + let ctx = ctx.clone(); + let tcp_provider = unsafe { IosTcpProvider::new(ctx.tcp_connection) }; + async move { Ok::<_, Error>(tcp_provider) } + })) + .await + .map_err(Error::GrpcConnectError)?; + + Ok(RelayConfigService::new(conn)) + } + + fn run_service_inner(self) { + let Self { runtime, .. } = self; + + let packet_tunnel_ptr = self.packet_tunnel.packet_tunnel; + runtime.block_on(async move { + let mut async_provider = match Self::ios_tcp_client(self.packet_tunnel).await { + Ok(async_provider) => async_provider, + Err(error) => { + log::error!("Failed to create iOS TCP client: {error}"); + unsafe { + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null_mut()); + } + return; + } + }; + let preshared_key = talpid_tunnel_config_client::push_pq_inner( + &mut async_provider, + PublicKey::from(self.pub_key), + PublicKey::from(self.ephemeral_public_key), + ) + .await; + + match preshared_key { + Ok(key) => unsafe { + let bytes = key.as_bytes(); + swift_post_quantum_key_ready(packet_tunnel_ptr, bytes.as_ptr()); + }, + Err(_) => unsafe { + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null_mut()); + }, + } + }); + } +} diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index fc6766d83f29..81500e5d4309 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -657,6 +657,8 @@ A900E9BC2ACC609200C95F67 /* DevicesProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */; }; A900E9BE2ACC654100C95F67 /* APIProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */; }; A900E9C02ACC661900C95F67 /* AccessTokenManager+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */; }; + A906F94A2BA1E09A002BF22E /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = A906F9492BA1E09A002BF22E /* WireGuardKitTypes */; }; + A906F94C2BA1E09A002BF22E /* WireGuardKitTypes in Embed Frameworks */ = {isa = PBXBuildFile; productRef = A906F9492BA1E09A002BF22E /* WireGuardKitTypes */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; A90763B02B2857D50045ADF0 /* Socks5ConnectCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A02B2857D50045ADF0 /* Socks5ConnectCommand.swift */; }; A90763B12B2857D50045ADF0 /* Socks5Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A12B2857D50045ADF0 /* Socks5Endpoint.swift */; }; A90763B22B2857D50045ADF0 /* Socks5EndpointReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A22B2857D50045ADF0 /* Socks5EndpointReader.swift */; }; @@ -683,15 +685,26 @@ A917352129FAAA5200D5DCFD /* TransportStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */; }; A91D78E32B03BDF200FCD5D3 /* TunnelObfuscation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; }; A91D78E42B03C01600FCD5D3 /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; }; + A9259FD22B8E06E90032C82B /* MullvadPostQuantum.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A93181A12B727ED700E341D2 /* TunnelSettingsV4.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93181A02B727ED700E341D2 /* TunnelSettingsV4.swift */; }; A932D9EF2B5ADD0700999395 /* ProxyConfigurationTransportProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A932D9EE2B5ADD0700999395 /* ProxyConfigurationTransportProvider.swift */; }; A932D9F32B5EB61100999395 /* HeadRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A932D9F22B5EB61100999395 /* HeadRequestTests.swift */; }; A932D9F52B5EBB9D00999395 /* RESTTransportStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A932D9F42B5EBB9D00999395 /* RESTTransportStub.swift */; }; A935594C2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A935594B2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift */; }; + A944F25F2B8DEFDB00473F4C /* MullvadPostQuantum.h in Headers */ = {isa = PBXBuildFile; fileRef = A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A944F2622B8DEFDB00473F4C /* MullvadPostQuantum.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; }; + A944F2632B8DEFDB00473F4C /* MullvadPostQuantum.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiatior.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiatior.swift */; }; + A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client_proxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */; }; A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E22AA72AE9003D1918 /* WireGuardKitTypes */; }; A94D691B2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E72AA7399D003D1918 /* WireGuardKitTypes */; }; A95EEE362B722CD600A8A39B /* TunnelMonitorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */; }; A95EEE382B722DFC00A8A39B /* PingStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95EEE372B722DFC00A8A39B /* PingStats.swift */; }; + A9630E3C2B8E0E7B00A65999 /* talpid_tunnel_config_client.h in Headers */ = {isa = PBXBuildFile; fileRef = A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */; settings = {ATTRIBUTES = (Private, ); }; }; + A9630E432B8E10FB00A65999 /* MullvadTypes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; }; + A9630E442B8E115A00A65999 /* MullvadPostQuantum.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; }; + A9630E472B8E1BF700A65999 /* NWTCPConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9630E462B8E1BF700A65999 /* NWTCPConnection+Async.swift */; }; + A9630E492B921E6D00A65999 /* PacketTunnelProvider+TCPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */; }; A970C89D2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */; }; A97D25AE2B0BB18100946B2D /* ProtocolObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */; }; A97D25B02B0BB5C400946B2D /* ProtocolObfuscationStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */; }; @@ -1177,6 +1190,20 @@ remoteGlobalIDString = 5840231E2A406BF5007B27AC; remoteInfo = TunnelObfuscation; }; + A944F2602B8DEFDB00473F4C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = A944F25B2B8DEFDB00473F4C; + remoteInfo = MullvadPostQuantum; + }; + A9630E412B8E10F700A65999 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58D223D4294C8E5E0029F5F8; + remoteInfo = MullvadTypes; + }; A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58CE5E58224146200008646E /* Project object */; @@ -1202,6 +1229,7 @@ files = ( 58D223E7294C8F120029F5F8 /* MullvadTypes.framework in Embed Frameworks */, 58D223FA294C8FF10029F5F8 /* MullvadLogging.framework in Embed Frameworks */, + A944F2632B8DEFDB00473F4C /* MullvadPostQuantum.framework in Embed Frameworks */, 58B2FDDA2AA71D2A003EB5C6 /* MullvadSettings.framework in Embed Frameworks */, A9EC20F02A5D79ED0040D56E /* TunnelObfuscation.framework in Embed Frameworks */, 7ABCA5B42A9349F20044A708 /* Routing.framework in Embed Frameworks */, @@ -1283,6 +1311,28 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + A906F94B2BA1E09A002BF22E /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + A906F94C2BA1E09A002BF22E /* WireGuardKitTypes in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + A9259FD52B8E06E90032C82B /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + A9259FD22B8E06E90032C82B /* MullvadPostQuantum.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -1957,9 +2007,14 @@ A932D9F42B5EBB9D00999395 /* RESTTransportStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTTransportStub.swift; sourceTree = ""; }; A935594B2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIAvailabilityTestRequest.swift; sourceTree = ""; }; A935594D2B4E919F00D5D524 /* Api.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Api.xcconfig; sourceTree = ""; }; + A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadPostQuantum.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadPostQuantum.h; sourceTree = ""; }; + A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtalpid_tunnel_config_client_proxy.a; path = "../target/aarch64-apple-ios/debug/libtalpid_tunnel_config_client_proxy.a"; sourceTree = ""; }; A9467E7E2A29DEFE000DC21F /* RelayCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTests.swift; sourceTree = ""; }; A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorState.swift; sourceTree = ""; }; A95EEE372B722DFC00A8A39B /* PingStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingStats.swift; sourceTree = ""; }; + A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = talpid_tunnel_config_client.h; path = "talpid-tunnel-config-client/include/talpid_tunnel_config_client.h"; sourceTree = ""; }; + A9630E462B8E1BF700A65999 /* NWTCPConnection+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWTCPConnection+Async.swift"; sourceTree = ""; }; A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Socks5UsernamePasswordCommand.swift; sourceTree = ""; }; A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscator.swift; sourceTree = ""; }; A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscationStub.swift; sourceTree = ""; }; @@ -1975,6 +2030,7 @@ A99E5EDF2B7628150033F241 /* ProblemReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportViewModel.swift; sourceTree = ""; }; A99E5EE12B762ED30033F241 /* ProblemReportViewController+ViewManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProblemReportViewController+ViewManagement.swift"; sourceTree = ""; }; A9A1DE782AD5708E0073F689 /* TransportStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportStrategy.swift; sourceTree = ""; }; + A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelProvider+TCPConnection.swift"; sourceTree = ""; }; A9A5F9A12ACB003D0083449F /* TunnelManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelManagerTests.swift; sourceTree = ""; }; A9A8A8EA2A262AB30086D569 /* FileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCache.swift; sourceTree = ""; }; A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationManagerTests.swift; sourceTree = ""; }; @@ -1990,6 +2046,7 @@ A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelStore+Stubs.swift"; sourceTree = ""; }; A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusBlockObserver.swift; sourceTree = ""; }; A9E034632ABB302000E59A5A /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extensions.swift"; sourceTree = ""; }; + A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiatior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyNegotiatior.swift; sourceTree = ""; }; A9EC20E72A5D3A8C0040D56E /* CoordinatesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatesTests.swift; sourceTree = ""; }; A9F360332AAB626300F53531 /* VPNConnectionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConnectionProtocol.swift; sourceTree = ""; }; E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeViewController.swift; sourceTree = ""; }; @@ -2151,6 +2208,7 @@ 58F0974E2A20C31100DA2DAD /* WireGuardKitTypes in Frameworks */, 58C7A4492A863F490060C66F /* PacketTunnelCore.framework in Frameworks */, 58D223F9294C8FF00029F5F8 /* MullvadLogging.framework in Frameworks */, + A944F2622B8DEFDB00473F4C /* MullvadPostQuantum.framework in Frameworks */, 58D223E6294C8F120029F5F8 /* MullvadTypes.framework in Frameworks */, 7ABCA5B32A9349F20044A708 /* Routing.framework in Frameworks */, 58D223CC294C8BCB0029F5F8 /* Operations.framework in Frameworks */, @@ -2163,6 +2221,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A9630E442B8E115A00A65999 /* MullvadPostQuantum.framework in Frameworks */, 589C6A7D2A45B06800DAD3EF /* TunnelObfuscation.framework in Frameworks */, 58FE25C62AA72779003D1918 /* PacketTunnelCore.framework in Frameworks */, 58FE25CE2AA72802003D1918 /* MullvadSettings.framework in Frameworks */, @@ -2238,6 +2297,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A944F2592B8DEFDB00473F4C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A9630E432B8E10FB00A65999 /* MullvadTypes.framework in Frameworks */, + A906F94A2BA1E09A002BF22E /* WireGuardKitTypes in Frameworks */, + A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client_proxy.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -2758,6 +2827,7 @@ 584F991F2902CBDD001F858D /* Frameworks */ = { isa = PBXGroup; children = ( + A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */, 01EF6F332B6A590700125696 /* libmullvad_api.a */, 01EF6F312B6A58F000125696 /* debug */, 01EF6F2F2B6A588300125696 /* aarch64-apple-ios */, @@ -3201,6 +3271,7 @@ 58C7A4432A863F490060C66F /* PacketTunnelCoreTests */, 7A88DCCF2A8FABBE00D2FF0E /* Routing */, 7A88DCDD2A8FABBE00D2FF0E /* RoutingTests */, + A944F25D2B8DEFDB00473F4C /* MullvadPostQuantum */, 58CE5E61224146200008646E /* Products */, 584F991F2902CBDD001F858D /* Frameworks */, ); @@ -3227,6 +3298,7 @@ 7A88DCD72A8FABBE00D2FF0E /* RoutingTests.xctest */, 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */, 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */, + A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */, ); name = Products; sourceTree = ""; @@ -3474,6 +3546,7 @@ 58FF23A22AB09BEE003A2AF2 /* DeviceChecker.swift */, 58225D272A84F23B0083D7F1 /* PacketTunnelPathObserver.swift */, 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */, + A9630E462B8E1BF700A65999 /* NWTCPConnection+Async.swift */, ); path = PacketTunnelProvider; sourceTree = ""; @@ -3717,6 +3790,17 @@ path = Socks5; sourceTree = ""; }; + A944F25D2B8DEFDB00473F4C /* MullvadPostQuantum */ = { + isa = PBXGroup; + children = ( + A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */, + A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */, + A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiatior.swift */, + A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */, + ); + path = MullvadPostQuantum; + sourceTree = ""; + }; F028A5472A336E1900C0CAA3 /* RedeemVoucher */ = { isa = PBXGroup; children = ( @@ -3961,6 +4045,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A944F2572B8DEFDB00473F4C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A944F25F2B8DEFDB00473F4C /* MullvadPostQuantum.h in Headers */, + A9630E3C2B8E0E7B00A65999 /* talpid_tunnel_config_client.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXLegacyTarget section */ @@ -4179,6 +4272,7 @@ 58C7A4482A863F490060C66F /* PBXTargetDependency */, 7ABCA5B62A9349F20044A708 /* PBXTargetDependency */, 58B2FDD82AA71D2A003EB5C6 /* PBXTargetDependency */, + A944F2612B8DEFDB00473F4C /* PBXTargetDependency */, ); name = MullvadVPN; packageProductDependencies = ( @@ -4196,6 +4290,7 @@ 58CE5E75224146470008646E /* Sources */, 58CE5E76224146470008646E /* Frameworks */, 58CE5E77224146470008646E /* Resources */, + A9259FD52B8E06E90032C82B /* Embed Frameworks */, ); buildRules = ( ); @@ -4378,6 +4473,30 @@ productReference = 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */ = { + isa = PBXNativeTarget; + buildConfigurationList = A944F2682B8DEFDB00473F4C /* Build configuration list for PBXNativeTarget "MullvadPostQuantum" */; + buildPhases = ( + A944F2692B8DF00C00473F4C /* Build Talpid Tunnel Config Client */, + A944F2572B8DEFDB00473F4C /* Headers */, + A944F2582B8DEFDB00473F4C /* Sources */, + A944F2592B8DEFDB00473F4C /* Frameworks */, + A944F25A2B8DEFDB00473F4C /* Resources */, + A906F94B2BA1E09A002BF22E /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + A9630E422B8E10F700A65999 /* PBXTargetDependency */, + ); + name = MullvadPostQuantum; + packageProductDependencies = ( + A906F9492BA1E09A002BF22E /* WireGuardKitTypes */, + ); + productName = MullvadPostQuantum; + productReference = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -4464,6 +4583,9 @@ CreatedOnToolsVersion = 15.1; TestTargetID = 58CE5E5F224146200008646E; }; + A944F25B2B8DEFDB00473F4C = { + CreatedOnToolsVersion = 15.2; + }; }; }; buildConfigurationList = 58CE5E5B224146200008646E /* Build configuration list for PBXProject "MullvadVPN" */; @@ -4502,6 +4624,7 @@ 58B2FDD22AA71D2A003EB5C6 /* MullvadSettings */, 7A88DCCD2A8FABBE00D2FF0E /* Routing */, 7A88DCD62A8FABBE00D2FF0E /* RoutingTests */, + A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */, ); }; /* End PBXProject section */ @@ -4633,6 +4756,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A944F25A2B8DEFDB00473F4C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -4710,6 +4840,25 @@ shellPath = /bin/sh; shellScript = "CARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh tunnel-obfuscator-proxy\n"; }; + A944F2692B8DF00C00473F4C /* Build Talpid Tunnel Config Client */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Build Talpid Tunnel Config Client"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "CARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh talpid-tunnel-config-client-proxy\n"; + }; F05F39962B21C704006E60A7 /* Prebuild relays */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -5517,6 +5666,7 @@ 580D6B8E2AB33BBF00B2D6E0 /* BlockedStateErrorMapper.swift in Sources */, 06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */, 583FE02429C1ACB3006E85F9 /* RESTCreateApplePaymentResponse+Localization.swift in Sources */, + A9630E472B8E1BF700A65999 /* NWTCPConnection+Async.swift in Sources */, 58CE38C728992C8700A6D6E5 /* WireGuardAdapterError+Localization.swift in Sources */, 58E511E828DDDF2400B0BCDE /* CodingErrors+CustomErrorDescription.swift in Sources */, 582403822A827E1500163DE8 /* RelaySelectorWrapper.swift in Sources */, @@ -5693,6 +5843,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A944F2582B8DEFDB00473F4C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9630E492B921E6D00A65999 /* PacketTunnelProvider+TCPConnection.swift in Sources */, + A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiatior.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -5893,6 +6052,16 @@ target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */; targetProxy = A91D78E12B03BDE500FCD5D3 /* PBXContainerItemProxy */; }; + A944F2612B8DEFDB00473F4C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */; + targetProxy = A944F2602B8DEFDB00473F4C /* PBXContainerItemProxy */; + }; + A9630E422B8E10F700A65999 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */; + targetProxy = A9630E412B8E10F700A65999 /* PBXContainerItemProxy */; + }; A9EC20F22A5D79ED0040D56E /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */; @@ -6513,6 +6682,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = Debug; @@ -6533,6 +6703,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = Release; @@ -6553,6 +6724,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = Debug; @@ -6572,6 +6744,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = Release; @@ -7182,6 +7355,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = Staging; @@ -7220,6 +7394,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = Staging; @@ -7719,6 +7894,210 @@ }; name = MockRelease; }; + A944F2642B8DEFDB00473F4C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 4; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/debug"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + A944F2652B8DEFDB00473F4C /* Staging */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 4; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ""; + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/debug"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Staging; + }; + A944F2662B8DEFDB00473F4C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 4; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ""; + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + A944F2672B8DEFDB00473F4C /* MockRelease */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 4; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ""; + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = MockRelease; + }; A9E99CE12B5195E600869AF2 /* MockRelease */ = { isa = XCBuildConfiguration; buildSettings = { @@ -7794,6 +8173,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = MockRelease; @@ -7828,6 +8208,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; }; name = MockRelease; @@ -8500,6 +8881,17 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + A944F2682B8DEFDB00473F4C /* Build configuration list for PBXNativeTarget "MullvadPostQuantum" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A944F2642B8DEFDB00473F4C /* Debug */, + A944F2652B8DEFDB00473F4C /* Staging */, + A944F2662B8DEFDB00473F4C /* Release */, + A944F2672B8DEFDB00473F4C /* MockRelease */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -8572,6 +8964,11 @@ package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */; productName = WireGuardKitTypes; }; + A906F9492BA1E09A002BF22E /* WireGuardKitTypes */ = { + isa = XCSwiftPackageProductDependency; + package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */; + productName = WireGuardKitTypes; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 58CE5E58224146200008646E /* Project object */; diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantum.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantum.xcscheme new file mode 100644 index 000000000000..0d18c5e1d9f6 --- /dev/null +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantum.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/PacketTunnel/PacketTunnelProvider/NWTCPConnection+Async.swift b/ios/PacketTunnel/PacketTunnelProvider/NWTCPConnection+Async.swift new file mode 100644 index 000000000000..2b1575a48d76 --- /dev/null +++ b/ios/PacketTunnel/PacketTunnelProvider/NWTCPConnection+Async.swift @@ -0,0 +1,42 @@ +// +// NWTCPConnection+Async.swift +// PacketTunnel +// +// Created by Marco Nikic on 2024-02-27. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import NetworkExtension + +class KVOWrapper { + let observation: NSKeyValueObservation + + init(observation: NSKeyValueObservation) { + self.observation = observation + } + + deinit { + NSLog("Bye cruel life") + } +} + +extension NWTCPConnection { + var viability: AsyncStream { + AsyncStream { continuation in + let keyPath: KeyPath = \.isViable + + let isViableObserver = observe(keyPath, options: [.new]) { connection, _ in + continuation.yield(connection.isViable) + } + + let wrapper = KVOWrapper(observation: isViableObserver) + + continuation.onTermination = { @Sendable _ in + wrapper.observation.invalidate() + } + + continuation.yield(false) + } + } +} diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 628828f5282c..93ead4a257a3 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -8,6 +8,7 @@ import Foundation import MullvadLogging +import MullvadPostQuantum import MullvadREST import MullvadSettings import MullvadTypes @@ -25,6 +26,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private var appMessageHandler: AppMessageHandler! private var stateObserverTask: AnyTask? private var deviceChecker: DeviceChecker! + private var adapter: WgAdapter! + private var relaySelector: RelaySelectorWrapper! override init() { Self.configureLogging() @@ -48,7 +51,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { addressCache: addressCache ) - let adapter = WgAdapter(packetTunnelProvider: self) + adapter = WgAdapter(packetTunnelProvider: self) let tunnelMonitor = TunnelMonitor( eventQueue: internalQueue, @@ -65,6 +68,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { let devicesProxy = proxyFactory.createDevicesProxy() deviceChecker = DeviceChecker(accountsProxy: accountsProxy, devicesProxy: devicesProxy) + relaySelector = RelaySelectorWrapper(relayCache: ipOverrideWrapper) actor = PacketTunnelActor( timings: PacketTunnelActorTimings(), @@ -72,7 +76,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { tunnelMonitor: tunnelMonitor, defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self, eventQueue: internalQueue), blockedStateErrorMapper: BlockedStateErrorMapper(), - relaySelector: RelaySelectorWrapper(relayCache: ipOverrideWrapper), + relaySelector: relaySelector, settingsReader: SettingsReader(), protocolObfuscator: ProtocolObfuscator() ) @@ -110,6 +114,77 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } + // MARK: - Uncomment the next three functions to test Post Quantum Key exchange + +// override func startTunnel(options: [String: NSObject]? = nil) async throws { +// let startOptions = parseStartOptions(options ?? [:]) +// +// startObservingActorState() +// +// try await startPostQuantumKeyExchange() +// } +// +// func selectGothenburgRelay() throws -> MullvadEndpoint { +// let constraints = RelayConstraints( +// locations: .only(UserSelectedRelays(locations: [.city("se", "got")])) +// ) +// let relay = try relaySelector.selectRelay(with: constraints, connectionAttemptFailureCount: 0) +// return relay.endpoint +// } +// +// var pqTCPConnection: NWTCPConnection? +// +// func startPostQuantumKeyExchange() async throws { +// let settingsReader = SettingsReader() +// let settings: Settings = try settingsReader.read() +// let privateKey = settings.privateKey +// let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device +// +// let IPv4Gateway = IPv4Address("10.64.0.1")! +// let gothenburgRelay = try selectGothenburgRelay() +// +// let configurationBuilder = ConfigurationBuilder( +// privateKey: settings.privateKey, +// interfaceAddresses: settings.interfaceAddresses, +// dns: settings.dnsServers, +// endpoint: gothenburgRelay, +// allowedIPs: [ +// IPAddressRange(from: "10.64.0.1/8")!, +// ] +// ) +// +// try await adapter.start(configuration: configurationBuilder.makeConfiguration()) +// +// let negotiator = PostQuantumKeyNegotiatior() +// let gatewayEndpoint = NWHostEndpoint(hostname: "10.64.0.1", port: "1337") +// +// pqTCPConnection = createTCPConnectionThroughTunnel( +// to: gatewayEndpoint, +// enableTLS: false, +// tlsParameters: nil, +// delegate: nil +// ) +// guard let pqTCPConnection else { return } +// +// // This will work as long as there is a detached, top-level task here. +// // It might be due to the async runtime environment for `override func startTunnel(options: [String: NSObject]? = nil) async throws` +// // There is a strong chance that the function's async availability was not properly declared by Apple. +// Task.detached { +// for await isViable in pqTCPConnection.viability where isViable == true { +// negotiator.negotiateKey( +// gatewayIP: IPv4Gateway, +// devicePublicKey: privateKey.publicKey, +// presharedKey: postQuantumSharedKey.publicKey, +// packetTunnel: self, +// tcpConnection: self.pqTCPConnection! +// ) +// break +// } +// } +// } + + // MARK: - End testing Post Quantum key exchange + override func stopTunnel(with reason: NEProviderStopReason) async { providerLogger.debug("stopTunnel: \(reason)") @@ -279,7 +354,6 @@ extension PacketTunnelProvider { extension PacketTunnelProvider: PostQuantumKeyReceiving { func receivePostQuantumKey(_ key: PreSharedKey) { - // TODO: send the key to the actor actor.replacePreSharedKey(key) } } diff --git a/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift b/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift index 06b970a223b7..b389a168bcc9 100644 --- a/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift +++ b/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift @@ -21,14 +21,28 @@ public struct PublicKeyError: LocalizedError { } /// Struct building tunnel adapter configuration. -struct ConfigurationBuilder { +public struct ConfigurationBuilder { var privateKey: PrivateKey var interfaceAddresses: [IPAddressRange] var dns: SelectedDNSServers? var endpoint: MullvadEndpoint? var allowedIPs: [IPAddressRange] - func makeConfiguration() throws -> TunnelAdapterConfiguration { + public init( + privateKey: PrivateKey, + interfaceAddresses: [IPAddressRange], + dns: SelectedDNSServers? = nil, + endpoint: MullvadEndpoint? = nil, + allowedIPs: [IPAddressRange] + ) { + self.privateKey = privateKey + self.interfaceAddresses = interfaceAddresses + self.dns = dns + self.endpoint = endpoint + self.allowedIPs = allowedIPs + } + + public func makeConfiguration() throws -> TunnelAdapterConfiguration { return TunnelAdapterConfiguration( privateKey: privateKey, interfaceAddresses: interfaceAddresses, diff --git a/ios/PacketTunnelCore/Actor/ObservedState.swift b/ios/PacketTunnelCore/Actor/ObservedState.swift index 3f92b300e50c..75b125d424e7 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState.swift @@ -14,6 +14,8 @@ import Network /// A serializable representation of internal state. public enum ObservedState: Equatable, Codable { case initial + // TODO: Handle this maybe ? +// case exchangingPostQuantumKey(ObservedConnectionState) case connecting(ObservedConnectionState) case reconnecting(ObservedConnectionState) #if DEBUG diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index 81b3bfaff179..3cb8a6c4d014 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -43,7 +43,7 @@ public actor PacketTunnelActor { let tunnelMonitor: TunnelMonitorProtocol let defaultPathObserver: DefaultPathObserverProtocol let blockedStateErrorMapper: BlockedStateErrorMapperProtocol - let relaySelector: RelaySelectorProtocol + public let relaySelector: RelaySelectorProtocol let settingsReader: SettingsReaderProtocol let protocolObfuscator: ProtocolObfuscation diff --git a/ios/build-rust-library.sh b/ios/build-rust-library.sh index 5de58559b07c..94b8571a7859 100644 --- a/ios/build-rust-library.sh +++ b/ios/build-rust-library.sh @@ -9,6 +9,8 @@ then exit 1 fi + + # what to pass to cargo build -p, e.g. your_lib_ffi FFI_TARGET=$1 @@ -30,13 +32,17 @@ if [[ "$CONFIGURATION" == "MockRelease" ]]; then RELFLAG=--release fi -if [[ -n "${DEVELOPER_SDK_DIR:-}" ]]; then - # Assume we're in Xcode, which means we're probably cross-compiling. - # In this case, we need to add an extra library search path for build scripts and proc-macros, - # which run on the host instead of the target. - # (macOS Big Sur does not have linkable libraries in /usr/lib/.) - export LIBRARY_PATH="${DEVELOPER_SDK_DIR}/MacOSX.sdk/usr/lib:${LIBRARY_PATH:-}" -fi +# For whatever reason, Xcode includes its toolchain paths in the PATH variable such as +# +# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin +# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/appleinternal/bin +# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/bin +# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/libexec +# When this happens, cargo will be tricked into building for the wrong architecture, which will lead to linker issues down the line. +# cargo does not need to know about all this, therefore, set the path to the bare minimum +export PATH="${HOME}/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:" +# Since some of the dependencies come from homebrew, add it manually as well +export PATH="${PATH}:/opt/homebrew/bin:" IS_SIMULATOR=0 if [ "${LLVM_TARGET_TRIPLE_SUFFIX-}" = "-simulator" ]; then diff --git a/talpid-routing/src/unix/mod.rs b/talpid-routing/src/unix/mod.rs index 7fe7e9bf3137..83091fa94d40 100644 --- a/talpid-routing/src/unix/mod.rs +++ b/talpid-routing/src/unix/mod.rs @@ -16,7 +16,7 @@ use futures::stream::Stream; use std::net::IpAddr; #[allow(clippy::module_inception)] -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "ios"))] #[path = "macos/mod.rs"] pub mod imp; diff --git a/talpid-tunnel-config-client/Cargo.toml b/talpid-tunnel-config-client/Cargo.toml index 650f2ba566ff..6aa8e5e3fc79 100644 --- a/talpid-tunnel-config-client/Cargo.toml +++ b/talpid-tunnel-config-client/Cargo.toml @@ -18,19 +18,24 @@ tonic = { workspace = true } tower = { workspace = true } prost = { workspace = true } tokio = { workspace = true, features = ["macros"] } -classic-mceliece-rust = { version = "2.0.0", features = ["mceliece460896f", "zeroize"] } +classic-mceliece-rust = { version = "2.0.0", features = [ + "mceliece460896f", + "zeroize", +] } pqc_kyber = { version = "0.4.0", features = ["std", "kyber1024", "zeroize"] } zeroize = "1.5.7" libc = "0.2" [target.'cfg(windows)'.dependencies.windows-sys] workspace = true -features = [ - "Win32_Networking_WinSock" -] +features = ["Win32_Networking_WinSock"] [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread"] } [build-dependencies] -tonic-build = { workspace = true, default-features = false, features = ["transport", "prost"] } +tonic-build = { workspace = true, default-features = false, features = [ + "transport", + "prost", +] } +cbindgen = { version = "0.24.3", default-features = false } diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs index e272e794c776..d287dc400431 100644 --- a/talpid-tunnel-config-client/src/lib.rs +++ b/talpid-tunnel-config-client/src/lib.rs @@ -134,7 +134,6 @@ pub async fn request_ephemeral_peer( activate_daita: enable_daita, }); - let mut client = new_client(service_address).await?; let response = client .register_peer_v1(proto::EphemeralPeerRequestV1 { wg_parent_pubkey: parent_pubkey.as_bytes().to_vec(), From e10fece528e7e1da82bed8ceee5dd0cffe69fd1c Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 20 Mar 2024 12:38:45 +0100 Subject: [PATCH 174/214] Remove debug statements --- .../xcschemes/MullvadVPN.xcscheme | 2 +- .../Coordinators/ApplicationCoordinator.swift | 2 -- .../MapConnectionStatusOperation.swift | 2 -- .../TunnelManager/StopTunnelOperation.swift | 2 -- .../TunnelManager/TunnelManager.swift | 5 ++-- .../TunnelManager/TunnelState.swift | 8 ------ .../Tunnel/TunnelControlView.swift | 14 ++-------- .../Tunnel/TunnelViewController.swift | 2 -- .../VPNSettings/VPNSettingsCellFactory.swift | 2 -- .../VPNSettings/VPNSettingsDataSource.swift | 26 ------------------- .../PacketTunnelProvider.swift | 3 +-- .../Actor/ObservedState+Extensions.swift | 4 --- .../Actor/ObservedState.swift | 2 -- 13 files changed, 6 insertions(+), 68 deletions(-) diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme index e96e9b561d23..540a9b776de4 100644 --- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme @@ -271,7 +271,7 @@ { case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error: doShutDownTunnel() - #if DEBUG case .negotiatingKey: doShutDownTunnel() - #endif case .disconnected, .disconnecting, .pendingReconnect, .waitingForConnectivity(.noNetwork): finish(result: .success(())) diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift index cc4ce36da6c6..21e7bf546b84 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift @@ -675,10 +675,9 @@ final class TunnelManager: StorePaymentObserver { // while the tunnel process is trying to connect. startPollingTunnelStatus(interval: establishingTunnelStatusPollInterval) - #if DEBUG case .negotiatingKey: - startPollingTunnelStatus(interval: establishingTunnelStatusPollInterval) - #endif + // No need to poll the tunnel while negotiating post quantum keys, assume the connection will work + break case .connected, .waitingForConnectivity(.noConnection): // Start polling tunnel status to keep connectivity status up to date. diff --git a/ios/MullvadVPN/TunnelManager/TunnelState.swift b/ios/MullvadVPN/TunnelManager/TunnelState.swift index 08b889899ef7..6a62bf1b47d2 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelState.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelState.swift @@ -50,10 +50,8 @@ enum TunnelState: Equatable, CustomStringConvertible { /// Connecting the tunnel. case connecting(SelectedRelay?) - #if DEBUG /// Negotiating a key for post-quantum resistance case negotiatingKey(SelectedRelay) - #endif /// Connected the tunnel case connected(SelectedRelay) @@ -99,10 +97,8 @@ enum TunnelState: Equatable, CustomStringConvertible { "waiting for connectivity" case let .error(blockedStateReason): "error state: \(blockedStateReason)" - #if DEBUG case let .negotiatingKey(tunnelRelay): "negotiating key with \(tunnelRelay.hostname)" - #endif } } @@ -113,10 +109,8 @@ enum TunnelState: Equatable, CustomStringConvertible { true case .pendingReconnect, .disconnecting, .disconnected, .waitingForConnectivity(.noNetwork), .error: false - #if DEBUG case .negotiatingKey: false - #endif } } @@ -126,10 +120,8 @@ enum TunnelState: Equatable, CustomStringConvertible { relay case let .connecting(relay): relay - #if DEBUG case let .negotiatingKey(relay): relay - #endif case .disconnecting, .disconnected, .waitingForConnectivity, .pendingReconnect, .error: nil } diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift index 8635bc1b9d79..230f470da58e 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift @@ -457,10 +457,9 @@ private extension TunnelState { case .connecting, .reconnecting, .waitingForConnectivity(.noConnection): .white - #if DEBUG + // TODO: Is this the correct color ? case .negotiatingKey: .white - #endif case .connected: .successColor @@ -488,7 +487,7 @@ private extension TunnelState { comment: "" ) - #if DEBUG + // TODO: Is this the correct message here ? case .negotiatingKey: NSLocalizedString( "TUNNEL_STATE_NEGOTIATING_KEY", @@ -496,7 +495,6 @@ private extension TunnelState { value: "Negotiating key", comment: "" ) - #endif case .connected: NSLocalizedString( @@ -573,7 +571,6 @@ private extension TunnelState { comment: "" ) - #if DEBUG case .negotiatingKey: NSLocalizedString( "SWITCH_LOCATION_BUTTON_TITLE", @@ -581,7 +578,6 @@ private extension TunnelState { value: "Switch location", comment: "" ) - #endif } } @@ -595,7 +591,6 @@ private extension TunnelState { comment: "" ) - #if DEBUG case .negotiatingKey: NSLocalizedString( "TUNNEL_STATE_CONNECTING_ACCESSIBILITY_LABEL", @@ -603,7 +598,6 @@ private extension TunnelState { value: "Creating secure connection", comment: "" ) - #endif case let .connected(tunnelInfo): String( @@ -682,10 +676,8 @@ private extension TunnelState { .waitingForConnectivity(.noConnection): [.selectLocation, .cancel] - #if DEBUG case .negotiatingKey: [.selectLocation, .cancel] - #endif case .connected, .reconnecting, .error: [.selectLocation, .disconnect] @@ -700,10 +692,8 @@ private extension TunnelState { .waitingForConnectivity(.noConnection): [.cancel] - #if DEBUG case .negotiatingKey: [.cancel] - #endif case .connected, .reconnecting, .error: [.disconnect] } diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift index 7d68b3dd4136..db6773b24507 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift @@ -157,12 +157,10 @@ class TunnelViewController: UIViewController, RootContainment { contentView.setAnimatingActivity(true) mapViewController.setCenter(tunnelRelay.location.geoCoordinate, animated: animated) - #if DEBUG case let .negotiatingKey(tunnelRelay): mapViewController.removeLocationMarker() contentView.setAnimatingActivity(true) mapViewController.setCenter(tunnelRelay.location.geoCoordinate, animated: animated) - #endif case let .connected(tunnelRelay): let center = tunnelRelay.location.geoCoordinate diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift index bd3b42b76a85..2934e7d4571c 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift @@ -170,7 +170,6 @@ final class VPNSettingsCellFactory: CellFactoryProtocol { cell.accessibilityIdentifier = "\(item.accessibilityIdentifier.rawValue)\(portString)" cell.applySubCellStyling() - #if DEBUG case .quantumResistanceAutomatic: guard let cell = cell as? SelectableSettingsCell else { return } @@ -205,7 +204,6 @@ final class VPNSettingsCellFactory: CellFactoryProtocol { ) cell.accessibilityIdentifier = item.accessibilityIdentifier cell.applySubCellStyling() - #endif } } } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift index ed4f2727b3d0..38ed7120ef88 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift @@ -57,9 +57,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case wireGuardPorts case wireGuardObfuscation case wireGuardObfuscationPort - #if DEBUG case quantumResistance - #endif } enum Item: Hashable { @@ -71,11 +69,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case wireGuardObfuscationOn case wireGuardObfuscationOff case wireGuardObfuscationPort(_ port: UInt16) - #if DEBUG case quantumResistanceAutomatic case quantumResistanceOn case quantumResistanceOff - #endif static var wireGuardPorts: [Item] { let defaultPorts = VPNSettingsViewModel.defaultWireGuardPorts.map { @@ -92,11 +88,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< [.wireGuardObfuscationPort(0), wireGuardObfuscationPort(80), wireGuardObfuscationPort(5001)] } - #if DEBUG static var quantumResistance: [Item] { [.quantumResistanceAutomatic, .quantumResistanceOn, .quantumResistanceOff] } - #endif var accessibilityIdentifier: AccessibilityIdentifier { switch self { @@ -116,14 +110,12 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return .wireGuardObfuscationOff case .wireGuardObfuscationPort: return .wireGuardObfuscationPort - #if DEBUG case .quantumResistanceAutomatic: return .quantumResistanceAutomatic case .quantumResistanceOn: return .quantumResistanceOn case .quantumResistanceOff: return .quantumResistanceOff - #endif } } @@ -141,10 +133,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return .wireGuardObfuscation case .wireGuardObfuscationPort: return .wireGuardObfuscationPort - #if DEBUG case .quantumResistanceAutomatic, .quantumResistanceOn, .quantumResistanceOff: return .quantumResistance - #endif } } } @@ -167,30 +157,20 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case .off: .wireGuardObfuscationOff case .on: .wireGuardObfuscationOn } - #if DEBUG let quantumResistanceItem: Item = switch viewModel.quantumResistance { case .automatic: .quantumResistanceAutomatic case .off: .quantumResistanceOff case .on: .quantumResistanceOn } - #endif let obfuscationPortItem: Item = .wireGuardObfuscationPort(viewModel.obfuscationPort.portValue) - #if DEBUG return [ wireGuardPortItem, obfuscationStateItem, obfuscationPortItem, quantumResistanceItem, ].compactMap { indexPath(for: $0) } - #else - return [ - wireGuardPortItem, - obfuscationStateItem, - obfuscationPortItem, - ].compactMap { indexPath(for: $0) } - #endif } init(tableView: UITableView) { @@ -299,7 +279,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< selectObfuscationPort(port) delegate?.didChangeViewModel(viewModel) - #if DEBUG case .quantumResistanceAutomatic: selectQuantumResistance(.automatic) delegate?.didChangeViewModel(viewModel) @@ -309,7 +288,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case .quantumResistanceOff: selectQuantumResistance(.off) delegate?.didChangeViewModel(viewModel) - #endif default: break } @@ -347,11 +325,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case .wireGuardObfuscationPort: configureObfuscationPortHeader(view) return view - #if DEBUG case .quantumResistance: configureQuantumResistanceHeader(view) return view - #endif default: return nil @@ -539,7 +515,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< } } - #if DEBUG private func configureQuantumResistanceHeader(_ header: SettingsHeaderView) { let title = NSLocalizedString( "QUANTUM_RESISTANCE_HEADER_LABEL", @@ -568,7 +543,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< self.map { $0.delegate?.showInfo(for: .quantumResistance) } } } - #endif private func selectRow(at indexPath: IndexPath?, animated: Bool = false) { tableView?.selectRow(at: indexPath, animated: animated, scrollPosition: .none) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 93ead4a257a3..3531a7833a96 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -305,10 +305,9 @@ extension PacketTunnelProvider { // Cache last connection attempt to filter out repeating calls. lastConnectionAttempt = connectionAttempt - #if DEBUG case .negotiatingKey: + // TODO: Call the key negotiatior here ? break - #endif case .initial, .connected, .disconnecting, .disconnected, .error: break diff --git a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift index 1504f3e47ddf..30208d97b1b8 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift @@ -15,10 +15,8 @@ extension ObservedState { case let .connecting(connState), let .connected(connState), let .reconnecting(connState): connState.relayConstraints - #if DEBUG case let .negotiatingKey(connState): connState.relayConstraints - #endif case let .error(blockedState): blockedState.relayConstraints @@ -33,10 +31,8 @@ extension ObservedState { "Connected" case .connecting: "Connecting" - #if DEBUG case .negotiatingKey: "Negotiating key" - #endif case .reconnecting: "Reconnecting" case .disconnecting: diff --git a/ios/PacketTunnelCore/Actor/ObservedState.swift b/ios/PacketTunnelCore/Actor/ObservedState.swift index 75b125d424e7..a913894f0e0d 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState.swift @@ -18,9 +18,7 @@ public enum ObservedState: Equatable, Codable { // case exchangingPostQuantumKey(ObservedConnectionState) case connecting(ObservedConnectionState) case reconnecting(ObservedConnectionState) - #if DEBUG case negotiatingKey(ObservedConnectionState) - #endif case connected(ObservedConnectionState) case disconnecting(ObservedConnectionState) case disconnected From 87710df7ba0381cfb6df4f87687b9087c7b3d61d Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 20 Mar 2024 13:00:26 +0100 Subject: [PATCH 175/214] Regroup switch cases together --- ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift | 5 +---- ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift | 5 +---- ios/MullvadVPN/TunnelManager/TunnelState.swift | 4 +--- .../View controllers/Tunnel/TunnelControlView.swift | 6 +----- .../View controllers/Tunnel/TunnelViewController.swift | 7 +------ ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift | 4 +--- 6 files changed, 6 insertions(+), 25 deletions(-) diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift index e7b8b7d67fca..673f0602049e 100644 --- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift @@ -980,10 +980,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo guard tunnelManager.deviceState.isLoggedIn else { return false } switch tunnelManager.tunnelStatus.state { - case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error: - tunnelManager.reconnectTunnel(selectNewRelay: true) - - case .negotiatingKey: + case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error, .negotiatingKey: tunnelManager.reconnectTunnel(selectNewRelay: true) case .disconnecting, .disconnected: diff --git a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift index 89a67eb17b2c..0448de22fd49 100644 --- a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift @@ -35,10 +35,7 @@ class StopTunnelOperation: ResultOperation { finish(result: .success(())) - case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error: - doShutDownTunnel() - - case .negotiatingKey: + case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error, .negotiatingKey: doShutDownTunnel() case .disconnected, .disconnecting, .pendingReconnect, .waitingForConnectivity(.noNetwork): diff --git a/ios/MullvadVPN/TunnelManager/TunnelState.swift b/ios/MullvadVPN/TunnelManager/TunnelState.swift index 6a62bf1b47d2..7115c472d315 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelState.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelState.swift @@ -116,12 +116,10 @@ enum TunnelState: Equatable, CustomStringConvertible { var relay: SelectedRelay? { switch self { - case let .connected(relay), let .reconnecting(relay): + case let .connected(relay), let .reconnecting(relay), let .negotiatingKey(relay): relay case let .connecting(relay): relay - case let .negotiatingKey(relay): - relay case .disconnecting, .disconnected, .waitingForConnectivity, .pendingReconnect, .error: nil } diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift index 230f470da58e..84be14ae43d3 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift @@ -454,11 +454,7 @@ final class TunnelControlView: UIView { private extension TunnelState { var textColorForSecureLabel: UIColor { switch self { - case .connecting, .reconnecting, .waitingForConnectivity(.noConnection): - .white - - // TODO: Is this the correct color ? - case .negotiatingKey: + case .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .negotiatingKey: .white case .connected: diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift index db6773b24507..f257928386b6 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift @@ -152,12 +152,7 @@ class TunnelViewController: UIViewController, RootContainment { contentView.setAnimatingActivity(true) mapViewController.setCenter(tunnelRelay?.location.geoCoordinate, animated: animated) - case let .reconnecting(tunnelRelay): - mapViewController.removeLocationMarker() - contentView.setAnimatingActivity(true) - mapViewController.setCenter(tunnelRelay.location.geoCoordinate, animated: animated) - - case let .negotiatingKey(tunnelRelay): + case let .reconnecting(tunnelRelay), let .negotiatingKey(tunnelRelay): mapViewController.removeLocationMarker() contentView.setAnimatingActivity(true) mapViewController.setCenter(tunnelRelay.location.geoCoordinate, animated: animated) diff --git a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift index 30208d97b1b8..7c26ed5d1440 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift @@ -12,11 +12,9 @@ import MullvadTypes extension ObservedState { public var relayConstraints: RelayConstraints? { switch self { - case let .connecting(connState), let .connected(connState), let .reconnecting(connState): + case let .connecting(connState), let .connected(connState), let .reconnecting(connState), let .negotiatingKey(connState): connState.relayConstraints - case let .negotiatingKey(connState): - connState.relayConstraints case let .error(blockedState): blockedState.relayConstraints From 86e12c8d451dd03dc850d065dbff04185f829785 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 20 Mar 2024 15:16:24 +0100 Subject: [PATCH 176/214] Enable reading Quantum Resistance settings from SettingsReader --- .../PacketTunnelProvider/SettingsReader.swift | 3 ++- .../Actor/ObservedState+Extensions.swift | 3 ++- .../Protocols/SettingsReaderProtocol.swift | 6 +++++- .../Mocks/SettingsReaderStub.swift | 3 ++- .../PacketTunnelActorTests.swift | 3 ++- .../ProtocolObfuscatorTests.swift | 19 ++++++++++--------- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift b/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift index 10282b5ff32e..ade3b5a3afd2 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift @@ -21,7 +21,8 @@ struct SettingsReader: SettingsReaderProtocol { interfaceAddresses: [deviceData.ipv4Address, deviceData.ipv6Address], relayConstraints: settings.relayConstraints, dnsServers: settings.dnsSettings.selectedDNSServers, - obfuscation: settings.wireGuardObfuscation + obfuscation: settings.wireGuardObfuscation, + quantumResistance: settings.tunnelQuantumResistance ) } } diff --git a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift index 7c26ed5d1440..cc4a9c79a61a 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift @@ -12,7 +12,8 @@ import MullvadTypes extension ObservedState { public var relayConstraints: RelayConstraints? { switch self { - case let .connecting(connState), let .connected(connState), let .reconnecting(connState), let .negotiatingKey(connState): + case let .connecting(connState), let .connected(connState), let .reconnecting(connState), + let .negotiatingKey(connState): connState.relayConstraints case let .error(blockedState): diff --git a/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift b/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift index 9d75149b455b..ffe7cdcc2ffe 100644 --- a/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift +++ b/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift @@ -40,18 +40,22 @@ public struct Settings { /// Obfuscation settings public var obfuscation: WireGuardObfuscationSettings + public var quantumResistance: TunnelQuantumResistance + public init( privateKey: PrivateKey, interfaceAddresses: [IPAddressRange], relayConstraints: RelayConstraints, dnsServers: SelectedDNSServers, - obfuscation: WireGuardObfuscationSettings + obfuscation: WireGuardObfuscationSettings, + quantumResistance: TunnelQuantumResistance ) { self.privateKey = privateKey self.interfaceAddresses = interfaceAddresses self.relayConstraints = relayConstraints self.dnsServers = dnsServers self.obfuscation = obfuscation + self.quantumResistance = quantumResistance } } diff --git a/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift b/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift index c95133b09108..edb9e99e6d40 100644 --- a/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift +++ b/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift @@ -29,7 +29,8 @@ extension SettingsReaderStub { interfaceAddresses: [IPAddressRange(from: "127.0.0.1/32")!], relayConstraints: RelayConstraints(), dnsServers: .gateway, - obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic) + obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic), + quantumResistance: .automatic ) return SettingsReaderStub { diff --git a/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift b/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift index fb37ef6b0da1..9fa8b90258fd 100644 --- a/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift +++ b/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift @@ -208,7 +208,8 @@ final class PacketTunnelActorTests: XCTestCase { interfaceAddresses: [IPAddressRange(from: "127.0.0.1/32")!], relayConstraints: RelayConstraints(), dnsServers: .gateway, - obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic) + obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic), + quantumResistance: .automatic ) } } diff --git a/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift b/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift index dd644a74e14b..21f30991bbf5 100644 --- a/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift +++ b/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift @@ -34,14 +34,14 @@ final class ProtocolObfuscatorTests: XCTestCase { } func testObfuscateOffDoesNotChangeEndpoint() { - let settings = settings(.off, obfuscationPort: .automatic) + let settings = settings(.off, obfuscationPort: .automatic, quantumResistance: .automatic) let nonObfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings) XCTAssertEqual(endpoint, nonObfuscatedEndpoint) } func testObfuscateOnPort80() throws { - let settings = settings(.on, obfuscationPort: .port80) + let settings = settings(.on, obfuscationPort: .port80, quantumResistance: .automatic) let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings) let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub) @@ -49,7 +49,7 @@ final class ProtocolObfuscatorTests: XCTestCase { } func testObfuscateOnPort5001() throws { - let settings = settings(.on, obfuscationPort: .port5001) + let settings = settings(.on, obfuscationPort: .port5001, quantumResistance: .automatic) let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings) let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub) @@ -57,7 +57,7 @@ final class ProtocolObfuscatorTests: XCTestCase { } func testObfuscateOnPortAutomaticIsPort80OnEvenRetryAttempts() throws { - let settings = settings(.on, obfuscationPort: .automatic) + let settings = settings(.on, obfuscationPort: .automatic, quantumResistance: .automatic) let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 2) let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub) @@ -65,7 +65,7 @@ final class ProtocolObfuscatorTests: XCTestCase { } func testObfuscateOnPortAutomaticIsPort5001OnOddRetryAttempts() throws { - let settings = settings(.on, obfuscationPort: .automatic) + let settings = settings(.on, obfuscationPort: .automatic, quantumResistance: .automatic) let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 3) let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub) @@ -73,7 +73,7 @@ final class ProtocolObfuscatorTests: XCTestCase { } func testObfuscateAutomaticIsPort80EveryThirdAttempts() throws { - let settings = settings(.automatic, obfuscationPort: .automatic) + let settings = settings(.automatic, obfuscationPort: .automatic, quantumResistance: .automatic) let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 6) let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub) @@ -81,7 +81,7 @@ final class ProtocolObfuscatorTests: XCTestCase { } func testObfuscateAutomaticIsPort5001EveryFourthAttempts() throws { - let settings = settings(.automatic, obfuscationPort: .automatic) + let settings = settings(.automatic, obfuscationPort: .automatic, quantumResistance: .automatic) let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 7) let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub) @@ -100,7 +100,8 @@ final class ProtocolObfuscatorTests: XCTestCase { private func settings( _ obfuscationState: WireGuardObfuscationState, - obfuscationPort: WireGuardObfuscationPort + obfuscationPort: WireGuardObfuscationPort, + quantumResistance: TunnelQuantumResistance ) -> Settings { Settings( privateKey: PrivateKey(), @@ -110,7 +111,7 @@ final class ProtocolObfuscatorTests: XCTestCase { obfuscation: WireGuardObfuscationSettings( state: obfuscationState, port: obfuscationPort - ) + ), quantumResistance: quantumResistance ) } } From 6593cc90f4ed08e8b4975e274cb72310e7766b6e Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Tue, 19 Mar 2024 17:27:44 +0100 Subject: [PATCH 177/214] Add utility methods for mutating `State`'s data, replacing switch stmts --- .../PacketTunnelProvider.swift | 121 +++++++++--------- .../Actor/PacketTunnelActor+KeyPolicy.swift | 35 +++++ .../Actor/State+Extensions.swift | 1 + 3 files changed, 98 insertions(+), 59 deletions(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 3531a7833a96..f87eb25ebfee 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -108,6 +108,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider { if connectionState.connectionAttemptCount > 1 { return } + case let .negotiatingKey(connectionState): + try await startPostQuantumKeyExchange() + return default: break } @@ -123,65 +126,65 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // // try await startPostQuantumKeyExchange() // } -// -// func selectGothenburgRelay() throws -> MullvadEndpoint { -// let constraints = RelayConstraints( -// locations: .only(UserSelectedRelays(locations: [.city("se", "got")])) -// ) -// let relay = try relaySelector.selectRelay(with: constraints, connectionAttemptFailureCount: 0) -// return relay.endpoint -// } -// -// var pqTCPConnection: NWTCPConnection? -// -// func startPostQuantumKeyExchange() async throws { -// let settingsReader = SettingsReader() -// let settings: Settings = try settingsReader.read() -// let privateKey = settings.privateKey -// let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device -// -// let IPv4Gateway = IPv4Address("10.64.0.1")! -// let gothenburgRelay = try selectGothenburgRelay() -// -// let configurationBuilder = ConfigurationBuilder( -// privateKey: settings.privateKey, -// interfaceAddresses: settings.interfaceAddresses, -// dns: settings.dnsServers, -// endpoint: gothenburgRelay, -// allowedIPs: [ -// IPAddressRange(from: "10.64.0.1/8")!, -// ] -// ) -// -// try await adapter.start(configuration: configurationBuilder.makeConfiguration()) -// -// let negotiator = PostQuantumKeyNegotiatior() -// let gatewayEndpoint = NWHostEndpoint(hostname: "10.64.0.1", port: "1337") -// -// pqTCPConnection = createTCPConnectionThroughTunnel( -// to: gatewayEndpoint, -// enableTLS: false, -// tlsParameters: nil, -// delegate: nil -// ) -// guard let pqTCPConnection else { return } -// -// // This will work as long as there is a detached, top-level task here. -// // It might be due to the async runtime environment for `override func startTunnel(options: [String: NSObject]? = nil) async throws` -// // There is a strong chance that the function's async availability was not properly declared by Apple. -// Task.detached { -// for await isViable in pqTCPConnection.viability where isViable == true { -// negotiator.negotiateKey( -// gatewayIP: IPv4Gateway, -// devicePublicKey: privateKey.publicKey, -// presharedKey: postQuantumSharedKey.publicKey, -// packetTunnel: self, -// tcpConnection: self.pqTCPConnection! -// ) -// break -// } -// } -// } + + func selectGothenburgRelay() throws -> MullvadEndpoint { + let constraints = RelayConstraints( + locations: .only(UserSelectedRelays(locations: [.city("se", "got")])) + ) + let relay = try relaySelector.selectRelay(with: constraints, connectionAttemptFailureCount: 0) + return relay.endpoint + } + + var pqTCPConnection: NWTCPConnection? + + func startPostQuantumKeyExchange() async throws { + let settingsReader = SettingsReader() + let settings: Settings = try settingsReader.read() + let privateKey = settings.privateKey + let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device + + let IPv4Gateway = IPv4Address("10.64.0.1")! + let gothenburgRelay = try selectGothenburgRelay() + + let configurationBuilder = ConfigurationBuilder( + privateKey: settings.privateKey, + interfaceAddresses: settings.interfaceAddresses, + dns: settings.dnsServers, + endpoint: gothenburgRelay, + allowedIPs: [ + IPAddressRange(from: "10.64.0.1/8")!, + ] + ) + + try await adapter.start(configuration: configurationBuilder.makeConfiguration()) + + let negotiator = PostQuantumKeyNegotiatior() + let gatewayEndpoint = NWHostEndpoint(hostname: "10.64.0.1", port: "1337") + + pqTCPConnection = createTCPConnectionThroughTunnel( + to: gatewayEndpoint, + enableTLS: false, + tlsParameters: nil, + delegate: nil + ) + guard let pqTCPConnection else { return } + + // This will work as long as there is a detached, top-level task here. + // It might be due to the async runtime environment for `override func startTunnel(options: [String: NSObject]? = nil) async throws` + // There is a strong chance that the function's async availability was not properly declared by Apple. + Task.detached { + for await isViable in pqTCPConnection.viability where isViable == true { + negotiator.negotiateKey( + gatewayIP: IPv4Gateway, + devicePublicKey: privateKey.publicKey, + presharedKey: postQuantumSharedKey.publicKey, + packetTunnel: self, + tcpConnection: self.pqTCPConnection! + ) + break + } + } + } // MARK: - End testing Post Quantum key exchange diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift index 9181a73edfd5..ba2327ffe837 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift @@ -36,7 +36,11 @@ extension PacketTunnelActor { // Move currentKey into keyPolicy. stateData.keyPolicy = .usePrior(currentKey, startKeySwitchTask()) stateData.currentKey = nil + } + + _ = state.mutateConnectionState(connectionStateMutator) || + state.mutateBlockedState(blockedStateMutator) } /** @@ -82,6 +86,37 @@ extension PacketTunnelActor { // Prevent tunnel from reconnecting when in blocked state. guard case .error = state else { return state.keyPolicy != oldKeyPolicy } return false +// switch state { +// case var .connecting(connState): +// if setCurrentKeyPolicy(&connState.keyPolicy) { +// state = .connecting(connState) +// return true +// } +// +// case var .connected(connState): +// if setCurrentKeyPolicy(&connState.keyPolicy) { +// state = .connected(connState) +// return true +// } +// +// case var .reconnecting(connState): +// if setCurrentKeyPolicy(&connState.keyPolicy) { +// state = .reconnecting(connState) +// return true +// } +// +// case var .error(blockedState): +// if setCurrentKeyPolicy(&blockedState.keyPolicy) { +// state = .error(blockedState) +// +// // Prevent tunnel from reconnecting when in blocked state. +// return false +// } +// +// case .disconnected, .disconnecting, .initial: +// break +// } +// return false } /** diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift index 45b28a0c9c32..3ba1aea5f2e8 100644 --- a/ios/PacketTunnelCore/Actor/State+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift @@ -144,6 +144,7 @@ extension State { default: break + } } From 64baed2fb4df6cab9463bc4b2ae5591e0fada7fe Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Tue, 19 Mar 2024 17:36:11 +0100 Subject: [PATCH 178/214] Remove premature changes not related to this refactoring --- .../PacketTunnelProvider.swift | 121 +++++++++--------- 1 file changed, 59 insertions(+), 62 deletions(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index f87eb25ebfee..3531a7833a96 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -108,9 +108,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { if connectionState.connectionAttemptCount > 1 { return } - case let .negotiatingKey(connectionState): - try await startPostQuantumKeyExchange() - return default: break } @@ -126,65 +123,65 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // // try await startPostQuantumKeyExchange() // } - - func selectGothenburgRelay() throws -> MullvadEndpoint { - let constraints = RelayConstraints( - locations: .only(UserSelectedRelays(locations: [.city("se", "got")])) - ) - let relay = try relaySelector.selectRelay(with: constraints, connectionAttemptFailureCount: 0) - return relay.endpoint - } - - var pqTCPConnection: NWTCPConnection? - - func startPostQuantumKeyExchange() async throws { - let settingsReader = SettingsReader() - let settings: Settings = try settingsReader.read() - let privateKey = settings.privateKey - let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device - - let IPv4Gateway = IPv4Address("10.64.0.1")! - let gothenburgRelay = try selectGothenburgRelay() - - let configurationBuilder = ConfigurationBuilder( - privateKey: settings.privateKey, - interfaceAddresses: settings.interfaceAddresses, - dns: settings.dnsServers, - endpoint: gothenburgRelay, - allowedIPs: [ - IPAddressRange(from: "10.64.0.1/8")!, - ] - ) - - try await adapter.start(configuration: configurationBuilder.makeConfiguration()) - - let negotiator = PostQuantumKeyNegotiatior() - let gatewayEndpoint = NWHostEndpoint(hostname: "10.64.0.1", port: "1337") - - pqTCPConnection = createTCPConnectionThroughTunnel( - to: gatewayEndpoint, - enableTLS: false, - tlsParameters: nil, - delegate: nil - ) - guard let pqTCPConnection else { return } - - // This will work as long as there is a detached, top-level task here. - // It might be due to the async runtime environment for `override func startTunnel(options: [String: NSObject]? = nil) async throws` - // There is a strong chance that the function's async availability was not properly declared by Apple. - Task.detached { - for await isViable in pqTCPConnection.viability where isViable == true { - negotiator.negotiateKey( - gatewayIP: IPv4Gateway, - devicePublicKey: privateKey.publicKey, - presharedKey: postQuantumSharedKey.publicKey, - packetTunnel: self, - tcpConnection: self.pqTCPConnection! - ) - break - } - } - } +// +// func selectGothenburgRelay() throws -> MullvadEndpoint { +// let constraints = RelayConstraints( +// locations: .only(UserSelectedRelays(locations: [.city("se", "got")])) +// ) +// let relay = try relaySelector.selectRelay(with: constraints, connectionAttemptFailureCount: 0) +// return relay.endpoint +// } +// +// var pqTCPConnection: NWTCPConnection? +// +// func startPostQuantumKeyExchange() async throws { +// let settingsReader = SettingsReader() +// let settings: Settings = try settingsReader.read() +// let privateKey = settings.privateKey +// let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device +// +// let IPv4Gateway = IPv4Address("10.64.0.1")! +// let gothenburgRelay = try selectGothenburgRelay() +// +// let configurationBuilder = ConfigurationBuilder( +// privateKey: settings.privateKey, +// interfaceAddresses: settings.interfaceAddresses, +// dns: settings.dnsServers, +// endpoint: gothenburgRelay, +// allowedIPs: [ +// IPAddressRange(from: "10.64.0.1/8")!, +// ] +// ) +// +// try await adapter.start(configuration: configurationBuilder.makeConfiguration()) +// +// let negotiator = PostQuantumKeyNegotiatior() +// let gatewayEndpoint = NWHostEndpoint(hostname: "10.64.0.1", port: "1337") +// +// pqTCPConnection = createTCPConnectionThroughTunnel( +// to: gatewayEndpoint, +// enableTLS: false, +// tlsParameters: nil, +// delegate: nil +// ) +// guard let pqTCPConnection else { return } +// +// // This will work as long as there is a detached, top-level task here. +// // It might be due to the async runtime environment for `override func startTunnel(options: [String: NSObject]? = nil) async throws` +// // There is a strong chance that the function's async availability was not properly declared by Apple. +// Task.detached { +// for await isViable in pqTCPConnection.viability where isViable == true { +// negotiator.negotiateKey( +// gatewayIP: IPv4Gateway, +// devicePublicKey: privateKey.publicKey, +// presharedKey: postQuantumSharedKey.publicKey, +// packetTunnel: self, +// tcpConnection: self.pqTCPConnection! +// ) +// break +// } +// } +// } // MARK: - End testing Post Quantum key exchange From 2b1f0dcfbe08e9c5f6a591adec1f290a351c46fd Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Tue, 19 Mar 2024 17:37:28 +0100 Subject: [PATCH 179/214] Remove commented dead code --- .../Actor/PacketTunnelActor+KeyPolicy.swift | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift index ba2327ffe837..e0b49ada551b 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift @@ -86,37 +86,6 @@ extension PacketTunnelActor { // Prevent tunnel from reconnecting when in blocked state. guard case .error = state else { return state.keyPolicy != oldKeyPolicy } return false -// switch state { -// case var .connecting(connState): -// if setCurrentKeyPolicy(&connState.keyPolicy) { -// state = .connecting(connState) -// return true -// } -// -// case var .connected(connState): -// if setCurrentKeyPolicy(&connState.keyPolicy) { -// state = .connected(connState) -// return true -// } -// -// case var .reconnecting(connState): -// if setCurrentKeyPolicy(&connState.keyPolicy) { -// state = .reconnecting(connState) -// return true -// } -// -// case var .error(blockedState): -// if setCurrentKeyPolicy(&blockedState.keyPolicy) { -// state = .error(blockedState) -// -// // Prevent tunnel from reconnecting when in blocked state. -// return false -// } -// -// case .disconnected, .disconnecting, .initial: -// break -// } -// return false } /** From 15698a2aba25e9f015d685136fcc5004788d3cbf Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 20 Mar 2024 15:48:39 +0100 Subject: [PATCH 180/214] Make State Equatable, move change checks to didSet &c --- ios/PacketTunnelCore/Actor/State+Extensions.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift index 3ba1aea5f2e8..5451dc528c3d 100644 --- a/ios/PacketTunnelCore/Actor/State+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift @@ -92,6 +92,7 @@ extension State { let .disconnecting(connState): connState default: nil } + return modified } var blockedData: State.BlockingData? { From eb8a9515180dd5f71a9729815053ff10ac343e6f Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 20 Mar 2024 16:20:59 +0100 Subject: [PATCH 181/214] Add a new actor state for key negotiation --- .../TunnelManager/MapConnectionStatusOperation.swift | 6 +++--- .../PacketTunnelProvider/PacketTunnelProvider.swift | 2 +- .../Actor/ObservedState+Extensions.swift | 4 ++-- ios/PacketTunnelCore/Actor/ObservedState.swift | 6 +++--- .../Actor/PacketTunnelActor+ConnectionMonitoring.swift | 4 ++-- .../Actor/PacketTunnelActor+ErrorState.swift | 3 ++- ios/PacketTunnelCore/Actor/PacketTunnelActor.swift | 9 +++++++-- ios/PacketTunnelCore/Actor/State+Extensions.swift | 6 ++++-- ios/PacketTunnelCore/Actor/State.swift | 3 +++ 9 files changed, 27 insertions(+), 16 deletions(-) diff --git a/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift b/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift index bfa8b5d45e86..a6a604569e0a 100644 --- a/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift +++ b/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift @@ -57,9 +57,9 @@ class MapConnectionStatusOperation: AsyncOperation { return connectionState.isNetworkReachable ? .connecting(connectionState.selectedRelay) : .waitingForConnectivity(.noConnection) - case let .negotiatingKey(connectionState): - return connectionState.isNetworkReachable - ? .negotiatingKey(connectionState.selectedRelay) + case let .negotiatingPostQuantumKey(connectionState): + connectionState.isNetworkReachable + ? .negotiatingPostQuantumKey(connectionState.selectedRelay) : .waitingForConnectivity(.noConnection) case let .reconnecting(connectionState): return connectionState.isNetworkReachable diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 3531a7833a96..b7962f8d9615 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -305,7 +305,7 @@ extension PacketTunnelProvider { // Cache last connection attempt to filter out repeating calls. lastConnectionAttempt = connectionAttempt - case .negotiatingKey: + case .negotiatingPostQuantumKey: // TODO: Call the key negotiatior here ? break diff --git a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift index cc4a9c79a61a..c69103c9b9a0 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift @@ -13,7 +13,7 @@ extension ObservedState { public var relayConstraints: RelayConstraints? { switch self { case let .connecting(connState), let .connected(connState), let .reconnecting(connState), - let .negotiatingKey(connState): + let .negotiatingPostQuantumKey(connState): connState.relayConstraints case let .error(blockedState): @@ -30,7 +30,7 @@ extension ObservedState { "Connected" case .connecting: "Connecting" - case .negotiatingKey: + case .negotiatingPostQuantumKey: "Negotiating key" case .reconnecting: "Reconnecting" diff --git a/ios/PacketTunnelCore/Actor/ObservedState.swift b/ios/PacketTunnelCore/Actor/ObservedState.swift index a913894f0e0d..1643e765b9ae 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState.swift @@ -14,11 +14,9 @@ import Network /// A serializable representation of internal state. public enum ObservedState: Equatable, Codable { case initial - // TODO: Handle this maybe ? -// case exchangingPostQuantumKey(ObservedConnectionState) case connecting(ObservedConnectionState) case reconnecting(ObservedConnectionState) - case negotiatingKey(ObservedConnectionState) + case negotiatingPostQuantumKey(ObservedConnectionState) case connected(ObservedConnectionState) case disconnecting(ObservedConnectionState) case disconnected @@ -78,6 +76,8 @@ extension State { return .reconnecting(connState.observedConnectionState) case let .disconnecting(connState): return .disconnecting(connState.observedConnectionState) + case let .negotiatingPostQuantumKey(connState): + return .negotiatingPostQuantumKey(connState.observedConnectionState) case .disconnected: return .disconnected case let .error(blockedState): diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift index c2bc921b055e..84d34d2650cd 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift @@ -42,7 +42,7 @@ extension PacketTunnelActor { connState.connectionAttemptCount = 0 state = .connected(connState) - case .initial, .connected, .disconnecting, .disconnected, .error: + case .initial, .connected, .disconnecting, .disconnected, .error, .negotiatingPostQuantumKey: break } } @@ -53,7 +53,7 @@ extension PacketTunnelActor { case .connecting, .reconnecting, .connected: commandChannel.send(.reconnect(.random, reason: .connectionLoss)) - case .initial, .disconnected, .disconnecting, .error: + case .initial, .disconnected, .disconnecting, .error, .negotiatingPostQuantumKey: break } } diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift index b450f44819c3..fd88ea94e4ad 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift @@ -84,7 +84,8 @@ extension PacketTunnelActor { return nil } - case .disconnecting, .disconnected: + // Post quantum key exchange cannot enter the blocked state + case .disconnecting, .disconnected, .negotiatingPostQuantumKey: return nil } } diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index 3cb8a6c4d014..196d91af4882 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -164,7 +164,8 @@ extension PacketTunnelActor { /// Stop the tunnel. private func stop() async { switch state { - case let .connected(connState), let .connecting(connState), let .reconnecting(connState): + case let .connected(connState), let .connecting(connState), let .reconnecting(connState), + let .negotiatingPostQuantumKey(connState): state = .disconnecting(connState) tunnelMonitor.stop() @@ -199,6 +200,10 @@ extension PacketTunnelActor { private func reconnect(to nextRelay: NextRelay, reason: ReconnectReason) async { do { switch state { + case .negotiatingPostQuantumKey: + // There is no connection monitoring going on when exchanging keys. + // The procedure starts from scratch for each reconnection attempts. + try await tryStart(nextRelay: nextRelay, reason: reason) case .connecting, .connected, .reconnecting, .error: switch reason { case .connectionLoss: @@ -326,7 +331,7 @@ extension PacketTunnelActor { connectionState.incrementAttemptCount() } fallthrough - case var .connected(connectionState): + case var .connected(connectionState), var .negotiatingPostQuantumKey(connectionState): let selectedRelay = try callRelaySelector( connectionState.selectedRelay, connectionState.connectionAttemptCount diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift index 5451dc528c3d..5737aa413818 100644 --- a/ios/PacketTunnelCore/Actor/State+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift @@ -23,7 +23,7 @@ extension State { case .initial: return .connecting - case .connecting: + case .connecting, .negotiatingPostQuantumKey: return .connecting case .connected, .reconnecting: @@ -59,13 +59,15 @@ extension State { case let .error(blockedState): return "\(name): \(blockedState.reason)" - case .initial, .disconnecting, .disconnected: + case .initial, .disconnecting, .disconnected, .negotiatingPostQuantumKey: return name } } var name: String { switch self { + case .negotiatingPostQuantumKey: + "Negotiating Post Quantum Key" case .connected: "Connected" case .connecting: diff --git a/ios/PacketTunnelCore/Actor/State.swift b/ios/PacketTunnelCore/Actor/State.swift index ab3fe767c937..5ac9c1d8f7dd 100644 --- a/ios/PacketTunnelCore/Actor/State.swift +++ b/ios/PacketTunnelCore/Actor/State.swift @@ -58,6 +58,9 @@ enum State: Equatable { /// Initial state at the time when actor is initialized but before the first connection attempt. case initial + /// Establish a connection to the gateway, and exchange a post quantum key with the GRPC service that resides there. + case negotiatingPostQuantumKey(ConnectionData) + /// Tunnel is attempting to connect. /// The actor should remain in this state until the very first connection is established, i.e determined by tunnel monitor. case connecting(ConnectionData) From b528169f2a3b0909ee4ed8977337d1f55a037bb2 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Thu, 21 Mar 2024 17:27:13 +0100 Subject: [PATCH 182/214] Read settings to determine whether to exchange a PQ key --- .../PacketTunnelProvider+TCPConnection.swift | 9 +++ .../PostQuantumKeyNegotiatior.swift | 18 +++++- .../include/talpid_tunnel_config_client.h | 10 ++-- .../src/ios_ffi.rs | 17 ++++-- .../talpid-tunnel-config-client/src/lib.rs | 58 +++++++++++++------ .../xcschemes/MullvadVPN.xcscheme | 2 +- .../PacketTunnelProvider.swift | 56 +++++++++++++++++- .../Actor/ObservedState+Extensions.swift | 2 +- .../Actor/ObservedState.swift | 7 ++- .../Actor/PacketTunnelActor.swift | 41 ++++++++++--- ios/PacketTunnelCore/Actor/State.swift | 2 +- 11 files changed, 175 insertions(+), 47 deletions(-) diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift index 8796498cddbb..d9af21d38b10 100644 --- a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -19,6 +19,7 @@ func tcpConnectionSend( dataLength: UInt, sender: UnsafeMutableRawPointer ) { + NSLog("\(#function) receiving raw pointer \(connection)") let tcpConnection = Unmanaged.fromOpaque(connection).takeUnretainedValue() let rawData = Data(bytes: data, count: Int(dataLength)) @@ -38,6 +39,7 @@ func tcpConnectionReceive( connection: UnsafeMutableRawPointer, sender: UnsafeMutableRawPointer ) { + NSLog("\(#function) receiving raw pointer \(connection)") let tcpConnection = Unmanaged.fromOpaque(connection).takeUnretainedValue() tcpConnection.readMinimumLength(0, maximumLength: Int(UInt16.max)) { data, maybeError in if let data { @@ -54,6 +56,13 @@ func tcpConnectionReceive( func receivePostQuantumKey(rawPacketTunnel: UnsafeMutableRawPointer, rawPresharedKey: UnsafeMutableRawPointer) { let packetTunnel = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() // TODO: The `rawPresharedKey` pointer might be null, this means the key exchanged failed, and we should try from the start again + + guard rawPresharedKey.hashValue != 0.hashValue else { + // Fail here + print("no key for you") + return + } + let presharedKey = Data(bytes: rawPresharedKey, count: 32) if let postQuantumKeyReceiver = packetTunnel as? PostQuantumKeyReceiving, let key = PreSharedKey(rawValue: presharedKey) { diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift index 5908312506ba..679441f163c3 100644 --- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift @@ -11,7 +11,8 @@ import NetworkExtension import TalpidTunnelConfigClientProxy import WireGuardKitTypes -public struct PostQuantumKeyNegotiatior { +public class PostQuantumKeyNegotiatior { + private var cancellationToken: UnsafeRawPointer! public init() {} public func negotiateKey( @@ -23,13 +24,26 @@ public struct PostQuantumKeyNegotiatior { ) { let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel).toOpaque() let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque() + NSLog("\(#function) passing raw pointer \(opaqueConnection)") // TODO: Any non 0 return is considered a failure, and should be handled gracefully - negotiate_post_quantum_key( + let token = negotiate_post_quantum_key( devicePublicKey.rawValue.map { $0 }, presharedKey.rawValue.map { $0 }, packetTunnelPointer, opaqueConnection ) + guard token?.hashValue != 0.hashValue else { + // Handle failure here + return + } + + cancellationToken = token + } + + public func cancelKeyNegotiation() { + if let cancellationToken, cancellationToken.hashValue != 0.hashValue { + cancel_post_quantum_key_exchange(cancellationToken) + } } } diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h index 19c9cd159f33..80508a0f0a70 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h @@ -3,6 +3,8 @@ #include #include +void cancel_post_quantum_key_exchange(const void *sender); + /** * Callback to call when the TCP connection has written data. */ @@ -17,10 +19,10 @@ void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender); * Entry point for exchanging post quantum keys on iOS. * The TCP connection must be created to go through the tunnel. */ -int32_t negotiate_post_quantum_key(const uint8_t *public_key, - const uint8_t *ephemeral_public_key, - const void *packet_tunnel, - const void *tcp_connection); +const void *negotiate_post_quantum_key(const uint8_t *public_key, + const uint8_t *ephemeral_public_key, + const void *packet_tunnel, + const void *tcp_connection); /** * Called when there is data to send on the TCP connection. diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs index 32774c94d724..95c3b5dc754e 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs @@ -3,9 +3,18 @@ use tokio::sync::mpsc; use super::run_ios_runtime; -use std::sync::Once; +use std::{rc::Weak, sync::Once}; static INIT_LOGGING: Once = Once::new(); +#[no_mangle] +pub unsafe extern "C" fn cancel_post_quantum_key_exchange(sender: *const c_void) { + // Try to take the value, if there is a value, we can safely send the message, otherwise, assume it has been dropped and nothing happens + let send_tx: Weak> = unsafe { Weak::from_raw(sender as _) }; + match send_tx.upgrade() { + Some(tx) => _ = tx.send(()), + None => (), + } +} /// Callback to call when the TCP connection has written data. #[no_mangle] pub unsafe extern "C" fn handle_sent(bytes_sent: usize, sender: *const c_void) { @@ -30,9 +39,9 @@ pub unsafe extern "C" fn handle_recv(data: *const u8, data_len: usize, sender: * pub unsafe extern "C" fn negotiate_post_quantum_key( public_key: *const u8, ephemeral_public_key: *const u8, - packet_tunnel: *const libc::c_void, - tcp_connection: *const libc::c_void, -) -> i32 { + packet_tunnel: *const c_void, + tcp_connection: *const c_void, +) -> *const c_void { INIT_LOGGING.call_once(|| { let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.TTCC") .level_filter(log::LevelFilter::Trace) diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs index 5f972db13a51..d5730714c118 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -1,8 +1,11 @@ use std::ptr; +use std::rc::Weak; use libc::c_void; +use tokio::sync::mpsc; use std::io; +use std::sync::Arc; mod ios_ffi; pub use ios_ffi::negotiate_post_quantum_key; @@ -21,15 +24,17 @@ pub unsafe fn run_ios_runtime( ephemeral_pub_key: [u8; 32], packet_tunnel: *const c_void, tcp_connection: *const c_void, -) -> i32 { +) -> *const c_void { match IOSRuntime::new(pub_key, ephemeral_pub_key, packet_tunnel, tcp_connection) { Ok(runtime) => { + let weak_cancel_token = Arc::downgrade(&runtime.cancel_token_tx); + let token = weak_cancel_token.into_raw() as _; runtime.run(); - 0 + token } Err(err) => { log::error!("Failed to create runtime {}", err); - err.raw_os_error().unwrap_or(-1) + std::ptr::null() } } } @@ -48,6 +53,8 @@ struct IOSRuntime { pub_key: [u8; 32], ephemeral_public_key: [u8; 32], packet_tunnel: SwiftContext, + cancel_token_tx: Arc>, + cancel_token_rx: mpsc::UnboundedReceiver<()>, } impl IOSRuntime { @@ -67,11 +74,15 @@ impl IOSRuntime { tcp_connection, }; + let (tx, rx) = mpsc::unbounded_channel(); + Ok(Self { runtime, pub_key, ephemeral_public_key, packet_tunnel: context, + cancel_token_tx: Arc::new(tx), + cancel_token_rx: rx, }) } @@ -96,7 +107,11 @@ impl IOSRuntime { } fn run_service_inner(self) { - let Self { runtime, .. } = self; + let Self { + runtime, + mut cancel_token_rx, + .. + } = self; let packet_tunnel_ptr = self.packet_tunnel.packet_tunnel; runtime.block_on(async move { @@ -110,21 +125,26 @@ impl IOSRuntime { return; } }; - let preshared_key = talpid_tunnel_config_client::push_pq_inner( - &mut async_provider, - PublicKey::from(self.pub_key), - PublicKey::from(self.ephemeral_public_key), - ) - .await; - - match preshared_key { - Ok(key) => unsafe { - let bytes = key.as_bytes(); - swift_post_quantum_key_ready(packet_tunnel_ptr, bytes.as_ptr()); - }, - Err(_) => unsafe { - swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null_mut()); - }, + tokio::select! { + preshared_key = talpid_tunnel_config_client::push_pq_inner( + &mut async_provider, + PublicKey::from(self.pub_key), + PublicKey::from(self.ephemeral_public_key), + ) => { + match preshared_key { + Ok(key) => unsafe { + let bytes = key.as_bytes(); + swift_post_quantum_key_ready(packet_tunnel_ptr, bytes.as_ptr()); + }, + Err(_) => unsafe { + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null_mut()); + }, + } + } + + _ = cancel_token_rx.recv() => { + // The swift runtime pre emptively cancelled the key exchange, nothing to do here. + } } }); } diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme index 540a9b776de4..e96e9b561d23 100644 --- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme @@ -271,7 +271,7 @@ PrivateKey { + switch state.keyPolicy { + case .useCurrent: + settings.privateKey + case let .usePrior(priorKey, _): + priorKey + } + } + private func obfuscateConnection( nextRelay: NextRelay, settings: Settings, diff --git a/ios/PacketTunnelCore/Actor/State.swift b/ios/PacketTunnelCore/Actor/State.swift index 5ac9c1d8f7dd..259993a7f8e0 100644 --- a/ios/PacketTunnelCore/Actor/State.swift +++ b/ios/PacketTunnelCore/Actor/State.swift @@ -59,7 +59,7 @@ enum State: Equatable { case initial /// Establish a connection to the gateway, and exchange a post quantum key with the GRPC service that resides there. - case negotiatingPostQuantumKey(ConnectionData) + case negotiatingPostQuantumKey(ConnectionData, PrivateKey) /// Tunnel is attempting to connect. /// The actor should remain in this state until the very first connection is established, i.e determined by tunnel monitor. From f92a81e6de74b2209bd3d2c28ac465b1cc2cc321 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Tue, 26 Mar 2024 11:13:20 +0100 Subject: [PATCH 183/214] Try to reconnect when PQ key exchange fails --- .../PacketTunnelProvider+TCPConnection.swift | 8 +++----- ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift | 1 - .../talpid-tunnel-config-client/src/ios_ffi.rs | 10 +++++++--- .../talpid-tunnel-config-client/src/lib.rs | 3 ++- .../Protocols/PostQuantumKeyReceiving.swift | 2 +- .../PacketTunnelProvider/PacketTunnelProvider.swift | 10 ++++++++-- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift index d9af21d38b10..226e50a07960 100644 --- a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -55,17 +55,15 @@ func tcpConnectionReceive( @_cdecl("swift_post_quantum_key_ready") func receivePostQuantumKey(rawPacketTunnel: UnsafeMutableRawPointer, rawPresharedKey: UnsafeMutableRawPointer) { let packetTunnel = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() - // TODO: The `rawPresharedKey` pointer might be null, this means the key exchanged failed, and we should try from the start again + guard let postQuantumKeyReceiver = packetTunnel as? PostQuantumKeyReceiving else { return } guard rawPresharedKey.hashValue != 0.hashValue else { - // Fail here - print("no key for you") + postQuantumKeyReceiver.receivePostQuantumKey(.none) return } let presharedKey = Data(bytes: rawPresharedKey, count: 32) - if let postQuantumKeyReceiver = packetTunnel as? PostQuantumKeyReceiving, - let key = PreSharedKey(rawValue: presharedKey) { + if let key = PreSharedKey(rawValue: presharedKey) { postQuantumKeyReceiver.receivePostQuantumKey(key) } } diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift index 679441f163c3..e9d1b7e413e5 100644 --- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift @@ -24,7 +24,6 @@ public class PostQuantumKeyNegotiatior { ) { let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel).toOpaque() let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque() - NSLog("\(#function) passing raw pointer \(opaqueConnection)") // TODO: Any non 0 return is considered a failure, and should be handled gracefully let token = negotiate_post_quantum_key( diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs index 95c3b5dc754e..78db55283f06 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs @@ -6,13 +6,15 @@ use super::run_ios_runtime; use std::{rc::Weak, sync::Once}; static INIT_LOGGING: Once = Once::new(); +#[allow(clippy::let_underscore_future)] #[no_mangle] pub unsafe extern "C" fn cancel_post_quantum_key_exchange(sender: *const c_void) { // Try to take the value, if there is a value, we can safely send the message, otherwise, assume it has been dropped and nothing happens let send_tx: Weak> = unsafe { Weak::from_raw(sender as _) }; - match send_tx.upgrade() { - Some(tx) => _ = tx.send(()), - None => (), + if let Some(tx) = send_tx.upgrade() { + // # Safety + // Clippy warns of a non-binding let on a future, this future is being awaited on. + _ = tx.send(()); } } /// Callback to call when the TCP connection has written data. @@ -35,6 +37,8 @@ pub unsafe extern "C" fn handle_recv(data: *const u8, data_len: usize, sender: * /// Entry point for exchanging post quantum keys on iOS. /// The TCP connection must be created to go through the tunnel. +/// # Safety +/// This function is safe to call #[no_mangle] pub unsafe extern "C" fn negotiate_post_quantum_key( public_key: *const u8, diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs index d5730714c118..81c5d272dcfa 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -1,5 +1,4 @@ use std::ptr; -use std::rc::Weak; use libc::c_void; use tokio::sync::mpsc; @@ -19,6 +18,8 @@ use talpid_types::net::wireguard::PublicKey; use tonic::transport::Endpoint; use tower::service_fn; +/// # Safety +/// This function is safe to call pub unsafe fn run_ios_runtime( pub_key: [u8; 32], ephemeral_pub_key: [u8; 32], diff --git a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift index eb696fcca300..3abbfd6738e4 100644 --- a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift +++ b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift @@ -10,7 +10,7 @@ import Foundation import WireGuardKitTypes public protocol PostQuantumKeyReceiving { - func receivePostQuantumKey(_ key: PreSharedKey) + func receivePostQuantumKey(_ key: PreSharedKey?) } public enum PostQuantumKeyReceivingError: Error { diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 8d153d2eed73..5dbf0e915a2d 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -397,12 +397,18 @@ extension PacketTunnelProvider { } extension PacketTunnelProvider: PostQuantumKeyReceiving { - func receivePostQuantumKey(_ key: PreSharedKey) { + func receivePostQuantumKey(_ key: PreSharedKey?) { + quantumKeyNegotiatior?.cancelKeyNegotiation() tcpConnectionObserver?.invalidate() inTunnelTCPConnection.cancel() tcpConnectionObserver = nil inTunnelTCPConnection = nil + quantumKeyNegotiatior = nil - actor.replacePreSharedKey(key) + if let key { + actor.replacePreSharedKey(key) + } else { + actor.reconnect(to: .current) + } } } From 5b7a204f71ac1a76dce06016a93de154b3b27070 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Tue, 26 Mar 2024 14:43:46 +0100 Subject: [PATCH 184/214] Remove dead code --- .../PacketTunnelProvider+TCPConnection.swift | 2 +- .../include/talpid_tunnel_config_client.h | 2 + .../Protocols/PostQuantumKeyReceiving.swift | 9 --- .../PacketTunnelProvider.swift | 71 ------------------- 4 files changed, 3 insertions(+), 81 deletions(-) diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift index 226e50a07960..30a52e310358 100644 --- a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -63,7 +63,7 @@ func receivePostQuantumKey(rawPacketTunnel: UnsafeMutableRawPointer, rawPreshare } let presharedKey = Data(bytes: rawPresharedKey, count: 32) - if let key = PreSharedKey(rawValue: presharedKey) { + if let key = PreSharedKey(rawValue: presharedKey) { postQuantumKeyReceiver.receivePostQuantumKey(key) } } diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h index 80508a0f0a70..5d4604a59e39 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h @@ -18,6 +18,8 @@ void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender); /** * Entry point for exchanging post quantum keys on iOS. * The TCP connection must be created to go through the tunnel. + * # Safety + * This function is safe to call */ const void *negotiate_post_quantum_key(const uint8_t *public_key, const uint8_t *ephemeral_public_key, diff --git a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift index 3abbfd6738e4..714f4cd33e5b 100644 --- a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift +++ b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift @@ -16,12 +16,3 @@ public protocol PostQuantumKeyReceiving { public enum PostQuantumKeyReceivingError: Error { case invalidKey } - -public extension PostQuantumKeyReceiving { - func receivePostQuantumKey(_ keyData: Data) throws { - guard let key = PreSharedKey(rawValue: keyData) else { - throw PostQuantumKeyReceivingError.invalidKey - } - receivePostQuantumKey(key) - } -} diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 5dbf0e915a2d..009b26cda6e6 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -119,77 +119,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } - // MARK: - Uncomment the next three functions to test Post Quantum Key exchange - -// override func startTunnel(options: [String: NSObject]? = nil) async throws { -// let startOptions = parseStartOptions(options ?? [:]) -// -// startObservingActorState() -// -// try await startPostQuantumKeyExchange() -// } -// -// func selectGothenburgRelay() throws -> MullvadEndpoint { -// let constraints = RelayConstraints( -// locations: .only(UserSelectedRelays(locations: [.city("se", "got")])) -// ) -// let relay = try relaySelector.selectRelay(with: constraints, connectionAttemptFailureCount: 0) -// return relay.endpoint -// } -// -// var pqTCPConnection: NWTCPConnection? -// -// func startPostQuantumKeyExchange() async throws { -// let settingsReader = SettingsReader() -// let settings: Settings = try settingsReader.read() -// let privateKey = settings.privateKey -// let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device -// -// let IPv4Gateway = IPv4Address("10.64.0.1")! -// let gothenburgRelay = try selectGothenburgRelay() -// -// let configurationBuilder = ConfigurationBuilder( -// privateKey: settings.privateKey, -// interfaceAddresses: settings.interfaceAddresses, -// dns: settings.dnsServers, -// endpoint: gothenburgRelay, -// allowedIPs: [ -// IPAddressRange(from: "10.64.0.1/8")!, -// ] -// ) -// -// try await adapter.start(configuration: configurationBuilder.makeConfiguration()) -// -// let negotiator = PostQuantumKeyNegotiatior() -// let gatewayEndpoint = NWHostEndpoint(hostname: "10.64.0.1", port: "1337") -// -// pqTCPConnection = createTCPConnectionThroughTunnel( -// to: gatewayEndpoint, -// enableTLS: false, -// tlsParameters: nil, -// delegate: nil -// ) -// guard let pqTCPConnection else { return } -// -// // This will work as long as there is a detached, top-level task here. -// // It might be due to the async runtime environment for `override func startTunnel(options: [String: NSObject]? = nil) async throws` -// // There is a strong chance that the function's async availability was not properly declared by Apple. -// Task.detached { -// for await isViable in pqTCPConnection.viability where isViable == true { -// negotiator.negotiateKey( -// gatewayIP: IPv4Gateway, -// devicePublicKey: privateKey.publicKey, -// presharedKey: postQuantumSharedKey.publicKey, -// packetTunnel: self, -// tcpConnection: self.pqTCPConnection! -// ) -// break -// } -// } -// } - - // MARK: - End testing Post Quantum key exchange - override func stopTunnel(with reason: NEProviderStopReason) async { providerLogger.debug("stopTunnel: \(reason)") From 7b2e5e60d09942cd5fdb50b93835086246de09f5 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Tue, 26 Mar 2024 16:26:12 +0100 Subject: [PATCH 185/214] Enable exchanging PQ keys from an unconnected state --- .../PacketTunnelProvider/PacketTunnelProvider.swift | 6 ++++++ ios/PacketTunnelCore/Actor/PacketTunnelActor.swift | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 009b26cda6e6..e0f93c3eb2ce 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -113,6 +113,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider { if connectionState.connectionAttemptCount > 1 { return } + case .negotiatingPostQuantumKey: + // When negotiating post quantun keys, allow the connection to go through immediately. + // Otherwise, the in-tunnel TCP connection will never become ready as the OS doesn't let + // any traffic through until this function returns, which would prevent negotiating keys + // from an unconnected state. + return default: break } diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index 9c584ca0af08..887cff157de8 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -257,7 +257,7 @@ extension PacketTunnelActor { dns: settings.dnsServers, endpoint: connectionState.connectedEndpoint, allowedIPs: [ - IPAddressRange(from: "10.64.0.1/8")!, + IPAddressRange(from: "10.64.0.1/32")!, ] ) From 97946da232f575180c5970666f5b0081e022e6d3 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Tue, 26 Mar 2024 17:16:13 +0100 Subject: [PATCH 186/214] Add safety checks at the FFI boundary --- .../PacketTunnelProvider+TCPConnection.swift | 18 +++++++++--------- .../PostQuantumKeyNegotiatior.swift | 2 +- .../talpid-tunnel-config-client/src/lib.rs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift index 30a52e310358..48e350cec110 100644 --- a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -14,12 +14,12 @@ import WireGuardKitTypes @_cdecl("swift_nw_tcp_connection_send") func tcpConnectionSend( - connection: UnsafeMutableRawPointer, + connection: UnsafeMutableRawPointer?, data: UnsafeMutableRawPointer, dataLength: UInt, - sender: UnsafeMutableRawPointer + sender: UnsafeMutableRawPointer? ) { - NSLog("\(#function) receiving raw pointer \(connection)") + guard let connection, let sender else { return } let tcpConnection = Unmanaged.fromOpaque(connection).takeUnretainedValue() let rawData = Data(bytes: data, count: Int(dataLength)) @@ -36,10 +36,10 @@ func tcpConnectionSend( @_cdecl("swift_nw_tcp_connection_read") func tcpConnectionReceive( - connection: UnsafeMutableRawPointer, - sender: UnsafeMutableRawPointer + connection: UnsafeMutableRawPointer?, + sender: UnsafeMutableRawPointer? ) { - NSLog("\(#function) receiving raw pointer \(connection)") + guard let connection, let sender else { return } let tcpConnection = Unmanaged.fromOpaque(connection).takeUnretainedValue() tcpConnection.readMinimumLength(0, maximumLength: Int(UInt16.max)) { data, maybeError in if let data { @@ -53,11 +53,11 @@ func tcpConnectionReceive( } @_cdecl("swift_post_quantum_key_ready") -func receivePostQuantumKey(rawPacketTunnel: UnsafeMutableRawPointer, rawPresharedKey: UnsafeMutableRawPointer) { +func receivePostQuantumKey(rawPacketTunnel: UnsafeMutableRawPointer?, rawPresharedKey: UnsafeMutableRawPointer?) { + guard let rawPacketTunnel else { return } let packetTunnel = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() guard let postQuantumKeyReceiver = packetTunnel as? PostQuantumKeyReceiving else { return } - - guard rawPresharedKey.hashValue != 0.hashValue else { + guard let rawPresharedKey else { postQuantumKeyReceiver.receivePostQuantumKey(.none) return } diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift index e9d1b7e413e5..fd9fda167282 100644 --- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift @@ -32,7 +32,7 @@ public class PostQuantumKeyNegotiatior { packetTunnelPointer, opaqueConnection ) - guard token?.hashValue != 0.hashValue else { + guard let token else { // Handle failure here return } diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs index 81c5d272dcfa..17212a0a269f 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -121,7 +121,7 @@ impl IOSRuntime { Err(error) => { log::error!("Failed to create iOS TCP client: {error}"); unsafe { - swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null_mut()); + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null()); } return; } @@ -138,7 +138,7 @@ impl IOSRuntime { swift_post_quantum_key_ready(packet_tunnel_ptr, bytes.as_ptr()); }, Err(_) => unsafe { - swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null_mut()); + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null()); }, } } From 55f646b99f7851bdc03d902b3bf8f9600860273e Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 27 Mar 2024 08:20:42 +0100 Subject: [PATCH 187/214] Remove unneeded dependencies --- Cargo.lock | 2 -- ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml | 2 -- 2 files changed, 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9fbf51afb08..cbc85855aa8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4058,13 +4058,11 @@ version = "0.0.0" dependencies = [ "cbindgen", "classic-mceliece-rust", - "futures", "libc", "log", "oslog", "pqc_kyber", "prost", - "rand 0.8.5", "talpid-tunnel-config-client", "talpid-types", "tokio", diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml b/ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml index 902882201ab7..93ea14f99f15 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/Cargo.toml @@ -15,9 +15,7 @@ talpid-tunnel-config-client = { path = "../../../talpid-tunnel-config-client" } talpid-types = { path = "../../../talpid-types" } log = { workspace = true } -rand = "0.8" tonic = { workspace = true } -futures = "0.3" tower = { workspace = true } prost = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } From ceecd505de52ad10a62f8c35ea3174821ad7a574 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 27 Mar 2024 11:08:41 +0100 Subject: [PATCH 188/214] Tidy code around key negotiation and cancellation token flow --- .../PostQuantumKeyNegotiatior.swift | 7 +++---- .../PacketTunnelProvider.swift | 20 +++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift index fd9fda167282..861ecee3033b 100644 --- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift @@ -12,7 +12,7 @@ import TalpidTunnelConfigClientProxy import WireGuardKitTypes public class PostQuantumKeyNegotiatior { - private var cancellationToken: UnsafeRawPointer! + private var cancellationToken: UnsafeRawPointer? public init() {} public func negotiateKey( @@ -41,8 +41,7 @@ public class PostQuantumKeyNegotiatior { } public func cancelKeyNegotiation() { - if let cancellationToken, cancellationToken.hashValue != 0.hashValue { - cancel_post_quantum_key_exchange(cancellationToken) - } + guard let cancellationToken else { return } + cancel_post_quantum_key_exchange(cancellationToken) } } diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index e0f93c3eb2ce..95396be3640a 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -276,17 +276,15 @@ extension PacketTunnelProvider { .initial, .new, ]) { [weak self] observedConnection, _ in - guard let self else { return } - if observedConnection.isViable == true { - keyNegotiatior.negotiateKey( - gatewayIP: IPv4Gateway, - devicePublicKey: privateKey.publicKey, - presharedKey: postQuantumSharedKey.publicKey, - packetTunnel: self, - tcpConnection: tcpConnection - ) - self.tcpConnectionObserver.invalidate() - } + guard let self, observedConnection.isViable else { return } + keyNegotiatior.negotiateKey( + gatewayIP: IPv4Gateway, + devicePublicKey: privateKey.publicKey, + presharedKey: postQuantumSharedKey.publicKey, + packetTunnel: self, + tcpConnection: tcpConnection + ) + self.tcpConnectionObserver.invalidate() } inTunnelTCPConnection = tcpConnection From 95979bc6d503ad90723fc166e7755f4ae0aef164 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 27 Mar 2024 14:43:29 +0100 Subject: [PATCH 189/214] Add "tidy" as an imperative verb --- .github/workflows/git-commit-message-style.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/git-commit-message-style.yml b/.github/workflows/git-commit-message-style.yml index 3f57a51a160b..f18951f26d04 100644 --- a/.github/workflows/git-commit-message-style.yml +++ b/.github/workflows/git-commit-message-style.yml @@ -32,4 +32,8 @@ jobs: # This action defaults to 50 char subjects, but 72 is fine. max-subject-line-length: '72' # The action's wordlist is a bit short. Add more accepted verbs +<<<<<<< HEAD additional-verbs: 'tidy, wrap' +======= + additional-verbs: 'restart, coalesce, tidy' +>>>>>>> 52690ba8c (Add "tidy" as an imperative verb) From f8e2696b2e9642958bf5441ab66682daa79fe1b6 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 27 Mar 2024 19:33:46 +0100 Subject: [PATCH 190/214] Update git-commit-message-style to match main --- .github/workflows/git-commit-message-style.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/git-commit-message-style.yml b/.github/workflows/git-commit-message-style.yml index f18951f26d04..3f57a51a160b 100644 --- a/.github/workflows/git-commit-message-style.yml +++ b/.github/workflows/git-commit-message-style.yml @@ -32,8 +32,4 @@ jobs: # This action defaults to 50 char subjects, but 72 is fine. max-subject-line-length: '72' # The action's wordlist is a bit short. Add more accepted verbs -<<<<<<< HEAD additional-verbs: 'tidy, wrap' -======= - additional-verbs: 'restart, coalesce, tidy' ->>>>>>> 52690ba8c (Add "tidy" as an imperative verb) From 34bcefed8805edd5b6bb810e9a6bf8653d0d5042 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Thu, 28 Mar 2024 09:29:40 +0100 Subject: [PATCH 191/214] Fix a typo --- .../PacketTunnelProvider/PacketTunnelProvider.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 95396be3640a..1eeec56b72bc 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -114,7 +114,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return } case .negotiatingPostQuantumKey: - // When negotiating post quantun keys, allow the connection to go through immediately. + // When negotiating post quantum keys, allow the connection to go through immediately. // Otherwise, the in-tunnel TCP connection will never become ready as the OS doesn't let // any traffic through until this function returns, which would prevent negotiating keys // from an unconnected state. From 84c28b8ee7b5e6d0a314f065ca6c1cffb8f81d91 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Thu, 28 Mar 2024 14:47:10 +0100 Subject: [PATCH 192/214] Remove unneeded framework reference --- ios/MullvadVPN.xcodeproj/project.pbxproj | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 81500e5d4309..6e25ecdfc74b 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -1211,13 +1211,6 @@ remoteGlobalIDString = 5840231E2A406BF5007B27AC; remoteInfo = TunnelObfuscation; }; - F04F959E2B21D02700431E08 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 58CE5E58224146200008646E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 06799ABB28F98E1D00ACD94E; - remoteInfo = MullvadREST; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -4115,7 +4108,6 @@ buildRules = ( ); dependencies = ( - F04F959F2B21D02700431E08 /* PBXTargetDependency */, A91614D32B108F4D00F416EB /* PBXTargetDependency */, ); name = TunnelObfuscation; @@ -6067,11 +6059,6 @@ target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */; targetProxy = A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */; }; - F04F959F2B21D02700431E08 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 06799ABB28F98E1D00ACD94E /* MullvadREST */; - targetProxy = F04F959E2B21D02700431E08 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ From c74267473012d90c7e7de264da7d74dedc9df447 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Thu, 28 Mar 2024 16:22:42 +0100 Subject: [PATCH 193/214] Rewrite the PQ cancellation token in a safer way --- .../PostQuantumKeyNegotiatior.swift | 23 +++++---- .../include/talpid_tunnel_config_client.h | 17 +++++-- .../src/ios_ffi.rs | 47 ++++++++++++------- .../talpid-tunnel-config-client/src/lib.rs | 41 +++++++++++++--- .../Actor/State+Extensions.swift | 2 + 5 files changed, 93 insertions(+), 37 deletions(-) diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift index 861ecee3033b..9bdd8b41e838 100644 --- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift @@ -12,9 +12,10 @@ import TalpidTunnelConfigClientProxy import WireGuardKitTypes public class PostQuantumKeyNegotiatior { - private var cancellationToken: UnsafeRawPointer? public init() {} + var cancelToken: PostQuantumCancelToken? + public func negotiateKey( gatewayIP: IPv4Address, devicePublicKey: PublicKey, @@ -24,24 +25,30 @@ public class PostQuantumKeyNegotiatior { ) { let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel).toOpaque() let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque() + var cancelToken = PostQuantumCancelToken() // TODO: Any non 0 return is considered a failure, and should be handled gracefully - let token = negotiate_post_quantum_key( + let result = negotiate_post_quantum_key( devicePublicKey.rawValue.map { $0 }, presharedKey.rawValue.map { $0 }, packetTunnelPointer, - opaqueConnection + opaqueConnection, + &cancelToken ) - guard let token else { + guard result == 0 else { // Handle failure here return } - - cancellationToken = token + self.cancelToken = cancelToken } public func cancelKeyNegotiation() { - guard let cancellationToken else { return } - cancel_post_quantum_key_exchange(cancellationToken) + guard var cancelToken else { return } + cancel_post_quantum_key_exchange(&cancelToken) + } + + deinit { + guard var cancelToken else { return } + drop_post_quantum_key_exchange_token(&cancelToken) } } diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h index 5d4604a59e39..c14dda4e12ba 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h @@ -3,7 +3,13 @@ #include #include -void cancel_post_quantum_key_exchange(const void *sender); +typedef struct PostQuantumCancelToken { + void *context; +} PostQuantumCancelToken; + +void cancel_post_quantum_key_exchange(const struct PostQuantumCancelToken *sender); + +void drop_post_quantum_key_exchange_token(const struct PostQuantumCancelToken *sender); /** * Callback to call when the TCP connection has written data. @@ -21,10 +27,11 @@ void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender); * # Safety * This function is safe to call */ -const void *negotiate_post_quantum_key(const uint8_t *public_key, - const uint8_t *ephemeral_public_key, - const void *packet_tunnel, - const void *tcp_connection); +int32_t negotiate_post_quantum_key(const uint8_t *public_key, + const uint8_t *ephemeral_public_key, + const void *packet_tunnel, + const void *tcp_connection, + struct PostQuantumCancelToken *cancel_token); /** * Called when there is data to send on the TCP connection. diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs index 78db55283f06..b69a1abdf4ec 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs @@ -1,22 +1,26 @@ use libc::c_void; use tokio::sync::mpsc; +use crate::PostQuantumCancelToken; + use super::run_ios_runtime; -use std::{rc::Weak, sync::Once}; +use std::sync::Once; static INIT_LOGGING: Once = Once::new(); -#[allow(clippy::let_underscore_future)] #[no_mangle] -pub unsafe extern "C" fn cancel_post_quantum_key_exchange(sender: *const c_void) { - // Try to take the value, if there is a value, we can safely send the message, otherwise, assume it has been dropped and nothing happens - let send_tx: Weak> = unsafe { Weak::from_raw(sender as _) }; - if let Some(tx) = send_tx.upgrade() { - // # Safety - // Clippy warns of a non-binding let on a future, this future is being awaited on. - _ = tx.send(()); - } +pub unsafe extern "C" fn cancel_post_quantum_key_exchange(sender: *const PostQuantumCancelToken) { + let sender = unsafe { &*sender }; + sender.cancel(); } + +#[no_mangle] +pub unsafe extern "C" fn drop_post_quantum_key_exchange_token( + sender: *const PostQuantumCancelToken, +) { + let _sender = unsafe { std::ptr::read(sender) }; +} + /// Callback to call when the TCP connection has written data. #[no_mangle] pub unsafe extern "C" fn handle_sent(bytes_sent: usize, sender: *const c_void) { @@ -45,7 +49,8 @@ pub unsafe extern "C" fn negotiate_post_quantum_key( ephemeral_public_key: *const u8, packet_tunnel: *const c_void, tcp_connection: *const c_void, -) -> *const c_void { + cancel_token: *mut PostQuantumCancelToken, +) -> i32 { INIT_LOGGING.call_once(|| { let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.TTCC") .level_filter(log::LevelFilter::Trace) @@ -56,10 +61,18 @@ pub unsafe extern "C" fn negotiate_post_quantum_key( let eph_pub_key_copy: [u8; 32] = unsafe { std::ptr::read(ephemeral_public_key as *const [u8; 32]) }; - run_ios_runtime( - pub_key_copy, - eph_pub_key_copy, - packet_tunnel, - tcp_connection, - ) + match unsafe { + run_ios_runtime( + pub_key_copy, + eph_pub_key_copy, + packet_tunnel, + tcp_connection, + ) + } { + Ok(token) => { + unsafe { std::ptr::write(cancel_token, token) }; + 0 + } + Err(err) => err, + } } diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs index 17212a0a269f..079baf102708 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -18,24 +18,51 @@ use talpid_types::net::wireguard::PublicKey; use tonic::transport::Endpoint; use tower::service_fn; +#[repr(C)] +pub struct PostQuantumCancelToken { + // Must keep a pointer to a valid std::sync::Arc + pub context: *mut c_void, +} + +impl PostQuantumCancelToken { + /// #Safety + /// This function can only be called when the context pointer is valid. + unsafe fn cancel(&self) { + // Try to take the value, if there is a value, we can safely send the message, otherwise, assume it has been dropped and nothing happens + let send_tx: Arc> = unsafe { Arc::from_raw(self.context as _) }; + let _ = send_tx.send(()); + std::mem::forget(send_tx); + } +} + +impl Drop for PostQuantumCancelToken { + fn drop(&mut self) { + let _: Arc> = unsafe { Arc::from_raw(self.context as _) }; + } +} +unsafe impl Send for PostQuantumCancelToken {} + /// # Safety -/// This function is safe to call +/// packet_tunnel and tcp_connection must be valid pointers to a packet tunnel and a TCP connection instances. +/// pub unsafe fn run_ios_runtime( pub_key: [u8; 32], ephemeral_pub_key: [u8; 32], packet_tunnel: *const c_void, tcp_connection: *const c_void, -) -> *const c_void { - match IOSRuntime::new(pub_key, ephemeral_pub_key, packet_tunnel, tcp_connection) { +) -> Result { + match unsafe { IOSRuntime::new(pub_key, ephemeral_pub_key, packet_tunnel, tcp_connection) } { Ok(runtime) => { - let weak_cancel_token = Arc::downgrade(&runtime.cancel_token_tx); - let token = weak_cancel_token.into_raw() as _; + let token = runtime.cancel_token_tx.clone(); + runtime.run(); - token + Ok(PostQuantumCancelToken { + context: Arc::into_raw(token) as *mut _, + }) } Err(err) => { log::error!("Failed to create runtime {}", err); - std::ptr::null() + Err(-1) } } } diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift index 5737aa413818..93fe29f3b731 100644 --- a/ios/PacketTunnelCore/Actor/State+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift @@ -91,6 +91,7 @@ extension State { let .connecting(connState), let .connected(connState), let .reconnecting(connState), + let .negotiatingPostQuantumKey(connState, _), let .disconnecting(connState): connState default: nil } @@ -121,6 +122,7 @@ extension State { case .connected: .connected(newValue) case .reconnecting: .reconnecting(newValue) case .disconnecting: .disconnecting(newValue) + case let .negotiatingPostQuantumKey(_, privateKey): .negotiatingPostQuantumKey(newValue, privateKey) default: self } } From e3b54dde2b0c548a627ca0cb76d3f1bec4b2dae7 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Mon, 8 Apr 2024 17:17:37 +0200 Subject: [PATCH 194/214] Fix rebase issues --- .../Coordinators/ApplicationCoordinator.swift | 3 ++- .../MapConnectionStatusOperation.swift | 6 +++--- .../TunnelManager/StopTunnelOperation.swift | 3 ++- ios/MullvadVPN/TunnelManager/TunnelManager.swift | 2 +- ios/MullvadVPN/TunnelManager/TunnelState.swift | 9 +++++---- .../Tunnel/TunnelControlView.swift | 14 ++++++++------ .../Tunnel/TunnelViewController.swift | 2 +- .../Actor/PacketTunnelActor+KeyPolicy.swift | 4 ---- ios/PacketTunnelCore/Actor/State+Extensions.swift | 2 -- 9 files changed, 22 insertions(+), 23 deletions(-) diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift index 673f0602049e..80352cdda2c8 100644 --- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift @@ -980,7 +980,8 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo guard tunnelManager.deviceState.isLoggedIn else { return false } switch tunnelManager.tunnelStatus.state { - case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error, .negotiatingKey: + case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error, + .negotiatingPostQuantumKey: tunnelManager.reconnectTunnel(selectNewRelay: true) case .disconnecting, .disconnected: diff --git a/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift b/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift index a6a604569e0a..f92fd37e077b 100644 --- a/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift +++ b/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift @@ -57,9 +57,9 @@ class MapConnectionStatusOperation: AsyncOperation { return connectionState.isNetworkReachable ? .connecting(connectionState.selectedRelay) : .waitingForConnectivity(.noConnection) - case let .negotiatingPostQuantumKey(connectionState): - connectionState.isNetworkReachable - ? .negotiatingPostQuantumKey(connectionState.selectedRelay) + case let .negotiatingPostQuantumKey(connectionState, privateKey): + return connectionState.isNetworkReachable + ? .negotiatingPostQuantumKey(connectionState.selectedRelay, privateKey) : .waitingForConnectivity(.noConnection) case let .reconnecting(connectionState): return connectionState.isNetworkReachable diff --git a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift index 0448de22fd49..1bd7a4f9c783 100644 --- a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift @@ -35,7 +35,8 @@ class StopTunnelOperation: ResultOperation { finish(result: .success(())) - case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error, .negotiatingKey: + case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error, + .negotiatingPostQuantumKey: doShutDownTunnel() case .disconnected, .disconnecting, .pendingReconnect, .waitingForConnectivity(.noNetwork): diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift index 21e7bf546b84..eda2fa907e9e 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift @@ -675,7 +675,7 @@ final class TunnelManager: StorePaymentObserver { // while the tunnel process is trying to connect. startPollingTunnelStatus(interval: establishingTunnelStatusPollInterval) - case .negotiatingKey: + case .negotiatingPostQuantumKey: // No need to poll the tunnel while negotiating post quantum keys, assume the connection will work break diff --git a/ios/MullvadVPN/TunnelManager/TunnelState.swift b/ios/MullvadVPN/TunnelManager/TunnelState.swift index 7115c472d315..1d480fe3a53f 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelState.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelState.swift @@ -9,6 +9,7 @@ import Foundation import MullvadTypes import PacketTunnelCore +import WireGuardKitTypes /// A struct describing the tunnel status. struct TunnelStatus: Equatable, CustomStringConvertible { @@ -51,7 +52,7 @@ enum TunnelState: Equatable, CustomStringConvertible { case connecting(SelectedRelay?) /// Negotiating a key for post-quantum resistance - case negotiatingKey(SelectedRelay) + case negotiatingPostQuantumKey(SelectedRelay, PrivateKey) /// Connected the tunnel case connected(SelectedRelay) @@ -97,7 +98,7 @@ enum TunnelState: Equatable, CustomStringConvertible { "waiting for connectivity" case let .error(blockedStateReason): "error state: \(blockedStateReason)" - case let .negotiatingKey(tunnelRelay): + case let .negotiatingPostQuantumKey(tunnelRelay, _): "negotiating key with \(tunnelRelay.hostname)" } } @@ -109,14 +110,14 @@ enum TunnelState: Equatable, CustomStringConvertible { true case .pendingReconnect, .disconnecting, .disconnected, .waitingForConnectivity(.noNetwork), .error: false - case .negotiatingKey: + case .negotiatingPostQuantumKey: false } } var relay: SelectedRelay? { switch self { - case let .connected(relay), let .reconnecting(relay), let .negotiatingKey(relay): + case let .connected(relay), let .reconnecting(relay), let .negotiatingPostQuantumKey(relay, _): relay case let .connecting(relay): relay diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift index 84be14ae43d3..75f26547476a 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift @@ -454,7 +454,7 @@ final class TunnelControlView: UIView { private extension TunnelState { var textColorForSecureLabel: UIColor { switch self { - case .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .negotiatingKey: + case .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .negotiatingPostQuantumKey: .white case .connected: @@ -484,7 +484,7 @@ private extension TunnelState { ) // TODO: Is this the correct message here ? - case .negotiatingKey: + case .negotiatingPostQuantumKey: NSLocalizedString( "TUNNEL_STATE_NEGOTIATING_KEY", tableName: "Main", @@ -567,7 +567,8 @@ private extension TunnelState { comment: "" ) - case .negotiatingKey: + // TODO: Is this correct ? + case .negotiatingPostQuantumKey: NSLocalizedString( "SWITCH_LOCATION_BUTTON_TITLE", tableName: "Main", @@ -587,7 +588,8 @@ private extension TunnelState { comment: "" ) - case .negotiatingKey: + // TODO: Is this correct ? + case .negotiatingPostQuantumKey: NSLocalizedString( "TUNNEL_STATE_CONNECTING_ACCESSIBILITY_LABEL", tableName: "Main", @@ -672,7 +674,7 @@ private extension TunnelState { .waitingForConnectivity(.noConnection): [.selectLocation, .cancel] - case .negotiatingKey: + case .negotiatingPostQuantumKey: [.selectLocation, .cancel] case .connected, .reconnecting, .error: @@ -688,7 +690,7 @@ private extension TunnelState { .waitingForConnectivity(.noConnection): [.cancel] - case .negotiatingKey: + case .negotiatingPostQuantumKey: [.cancel] case .connected, .reconnecting, .error: [.disconnect] diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift index f257928386b6..ce7b72a9f357 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift @@ -152,7 +152,7 @@ class TunnelViewController: UIViewController, RootContainment { contentView.setAnimatingActivity(true) mapViewController.setCenter(tunnelRelay?.location.geoCoordinate, animated: animated) - case let .reconnecting(tunnelRelay), let .negotiatingKey(tunnelRelay): + case let .reconnecting(tunnelRelay), let .negotiatingPostQuantumKey(tunnelRelay, _): mapViewController.removeLocationMarker() contentView.setAnimatingActivity(true) mapViewController.setCenter(tunnelRelay.location.geoCoordinate, animated: animated) diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift index e0b49ada551b..9181a73edfd5 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift @@ -36,11 +36,7 @@ extension PacketTunnelActor { // Move currentKey into keyPolicy. stateData.keyPolicy = .usePrior(currentKey, startKeySwitchTask()) stateData.currentKey = nil - } - - _ = state.mutateConnectionState(connectionStateMutator) || - state.mutateBlockedState(blockedStateMutator) } /** diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift index 93fe29f3b731..01913b4e24ce 100644 --- a/ios/PacketTunnelCore/Actor/State+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift @@ -95,7 +95,6 @@ extension State { let .disconnecting(connState): connState default: nil } - return modified } var blockedData: State.BlockingData? { @@ -149,7 +148,6 @@ extension State { default: break - } } From 6ba2959b2e03e1e3505cf764fb8ba46ff0659ddf Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Wed, 10 Apr 2024 11:55:20 +0200 Subject: [PATCH 195/214] Add a method to receive the preshared key in the actor --- ios/PacketTunnelCore/Actor/PacketTunnelActor.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index 887cff157de8..ee1394de8f7a 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -113,8 +113,8 @@ public actor PacketTunnelActor { case let .networkReachability(defaultPath): await handleDefaultPathChange(defaultPath) - case .replaceDevicePrivateKey: - self.logger.warning("Not yet implemented") + case let .replaceDevicePrivateKey(preSharedKey): + await postQuantumConnect(with: preSharedKey) } } } @@ -226,6 +226,8 @@ extension PacketTunnelActor { } } + private func postQuantumConnect(with key: PreSharedKey) async {} + /** Attempt to start the tunnel by performing the following steps: From b85d4808dbc41bc235cb9800476b7c727feb0c1a Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 10 Apr 2024 14:19:17 +0200 Subject: [PATCH 196/214] Add preSharedKey to configurationBuilder --- ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift b/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift index b389a168bcc9..79833d16c2aa 100644 --- a/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift +++ b/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift @@ -27,19 +27,23 @@ public struct ConfigurationBuilder { var dns: SelectedDNSServers? var endpoint: MullvadEndpoint? var allowedIPs: [IPAddressRange] + // or should this go in MullvadEndpoint? + var preSharedKey: PreSharedKey? public init( privateKey: PrivateKey, interfaceAddresses: [IPAddressRange], dns: SelectedDNSServers? = nil, endpoint: MullvadEndpoint? = nil, - allowedIPs: [IPAddressRange] + allowedIPs: [IPAddressRange], + preSharedKey: PreSharedKey? = nil ) { self.privateKey = privateKey self.interfaceAddresses = interfaceAddresses self.dns = dns self.endpoint = endpoint self.allowedIPs = allowedIPs + self.preSharedKey = preSharedKey } public func makeConfiguration() throws -> TunnelAdapterConfiguration { @@ -62,7 +66,8 @@ public struct ConfigurationBuilder { return TunnelPeer( endpoint: .ipv4(endpoint.ipv4Relay), - publicKey: publicKey + publicKey: publicKey, + preSharedKey: preSharedKey ) } } From 31d3e1830b8f90c03549604af20e38056e1134a4 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 10 Apr 2024 15:34:10 +0200 Subject: [PATCH 197/214] Attempt at starting a tunnel with the PQ shared key --- .../Actor/PacketTunnelActor.swift | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index ee1394de8f7a..efccb030974a 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -226,7 +226,40 @@ extension PacketTunnelActor { } } - private func postQuantumConnect(with key: PreSharedKey) async {} + private func postQuantumConnect(with key: PreSharedKey) async { + guard + let settings: Settings = try? settingsReader.read(), + let connectionState = try? obfuscateConnection(nextRelay: .random, settings: settings, reason: .userInitiated), + let targetState = state.targetStateForReconnect + else { return } + let activeKey = activeKey(from: connectionState, in: settings) + let configurationBuilder = ConfigurationBuilder( + privateKey: activeKey, + interfaceAddresses: settings.interfaceAddresses, + dns: settings.dnsServers, + endpoint: connectionState.connectedEndpoint, + allowedIPs: [ + IPAddressRange(from: "0.0.0.0/0")!, + IPAddressRange(from: "::/0")!, + ], + preSharedKey: key + ) + stopDefaultPathObserver() + + defer { + // Restart default path observer and notify the observer with the current path that might have changed while + // path observer was paused. + startDefaultPathObserver(notifyObserverWithCurrentPath: true) + } + + guard let _ = try? await tunnelAdapter.start(configuration: configurationBuilder.makeConfiguration()) else { + return + } + + // Resume tunnel monitoring and use IPv4 gateway as a probe address. + tunnelMonitor.start(probeAddress: connectionState.selectedRelay.endpoint.ipv4Gateway) + + } /** Attempt to start the tunnel by performing the following steps: From 7864765abeaee9fb538a7130b2e17f479c4e6656 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Thu, 11 Apr 2024 10:06:37 +0200 Subject: [PATCH 198/214] Hack the new private key as a global to prove PQ PSK works --- .../Tunnel/TunnelControlView.swift | 2 +- .../PacketTunnelProvider.swift | 1 + .../Actor/ObservedState+Extensions.swift | 2 +- .../Actor/PacketTunnelActor.swift | 30 +++++++++++++------ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift index 75f26547476a..2d348b252835 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift @@ -488,7 +488,7 @@ private extension TunnelState { NSLocalizedString( "TUNNEL_STATE_NEGOTIATING_KEY", tableName: "Main", - value: "Negotiating key", + value: "Creating quantum secure connection", comment: "" ) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 1eeec56b72bc..b1b72b92c2fb 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -272,6 +272,7 @@ extension PacketTunnelProvider { ) let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device + PacketTunnelActor.newPQPrivateKey = postQuantumSharedKey let observer = tcpConnection.observe(\.isViable, options: [ .initial, .new, diff --git a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift index 2452ee0d1001..cf6d786bebe0 100644 --- a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift @@ -31,7 +31,7 @@ extension ObservedState { case .connecting: "Connecting" case .negotiatingPostQuantumKey: - "Negotiating key" + "Negotiating Post Quantum Secure Key" case .reconnecting: "Reconnecting" case .disconnecting: diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index efccb030974a..c0c4d1276006 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -36,6 +36,8 @@ public actor PacketTunnelActor { @Published internal(set) public var observedState: ObservedState = .initial + public static var newPQPrivateKey: PrivateKey! + let logger = Logger(label: "PacketTunnelActor") let timings: PacketTunnelActorTimings @@ -203,7 +205,8 @@ extension PacketTunnelActor { case .negotiatingPostQuantumKey: // There is no connection monitoring going on when exchanging keys. // The procedure starts from scratch for each reconnection attempts. - try await tryStart(nextRelay: nextRelay, reason: reason) +// try await tryStart(nextRelay: nextRelay, reason: reason) + break // DO nothing at all for the moment case .connecting, .connected, .reconnecting, .error: switch reason { case .connectionLoss: @@ -229,12 +232,18 @@ extension PacketTunnelActor { private func postQuantumConnect(with key: PreSharedKey) async { guard let settings: Settings = try? settingsReader.read(), - let connectionState = try? obfuscateConnection(nextRelay: .random, settings: settings, reason: .userInitiated), + let connectionState = try? obfuscateConnection( + nextRelay: .random, + settings: settings, + reason: .userInitiated + ), let targetState = state.targetStateForReconnect else { return } + let activeKey = activeKey(from: connectionState, in: settings) let configurationBuilder = ConfigurationBuilder( - privateKey: activeKey, + // privateKey: activeKey, + privateKey: Self.newPQPrivateKey, interfaceAddresses: settings.interfaceAddresses, dns: settings.dnsServers, endpoint: connectionState.connectedEndpoint, @@ -246,19 +255,22 @@ extension PacketTunnelActor { ) stopDefaultPathObserver() + switch targetState { + case .connecting: + state = .connecting(connectionState) + case .reconnecting: + state = .reconnecting(connectionState) + } + defer { // Restart default path observer and notify the observer with the current path that might have changed while // path observer was paused. - startDefaultPathObserver(notifyObserverWithCurrentPath: true) - } - - guard let _ = try? await tunnelAdapter.start(configuration: configurationBuilder.makeConfiguration()) else { - return + startDefaultPathObserver(notifyObserverWithCurrentPath: false) } + try? await tunnelAdapter.start(configuration: configurationBuilder.makeConfiguration()) // Resume tunnel monitoring and use IPv4 gateway as a probe address. tunnelMonitor.start(probeAddress: connectionState.selectedRelay.endpoint.ipv4Gateway) - } /** From c805115d3bfbc1b80b88ebee77a5dedcc7fcd92c Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Fri, 12 Apr 2024 11:31:59 +0200 Subject: [PATCH 199/214] Prototype a new actor to handle PQ PSK --- ios/MullvadVPN.xcodeproj/project.pbxproj | 4 +++ .../PacketTunnelProvider.swift | 27 ++++++++++++------- .../PostQuantumKeyExchangeActor.swift | 26 ++++++++++++++++++ .../Actor/PacketTunnelActorCommand.swift | 1 + 4 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 ios/PacketTunnel/PostQuantumKeyExchangeActor.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 6e25ecdfc74b..8eaaa363dd56 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -696,6 +696,7 @@ A944F2632B8DEFDB00473F4C /* MullvadPostQuantum.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiatior.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiatior.swift */; }; A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client_proxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */; }; + A948809B2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */; }; A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E22AA72AE9003D1918 /* WireGuardKitTypes */; }; A94D691B2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E72AA7399D003D1918 /* WireGuardKitTypes */; }; A95EEE362B722CD600A8A39B /* TunnelMonitorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */; }; @@ -2004,6 +2005,7 @@ A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadPostQuantum.h; sourceTree = ""; }; A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtalpid_tunnel_config_client_proxy.a; path = "../target/aarch64-apple-ios/debug/libtalpid_tunnel_config_client_proxy.a"; sourceTree = ""; }; A9467E7E2A29DEFE000DC21F /* RelayCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTests.swift; sourceTree = ""; }; + A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyExchangeActor.swift; sourceTree = ""; }; A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorState.swift; sourceTree = ""; }; A95EEE372B722DFC00A8A39B /* PingStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingStats.swift; sourceTree = ""; }; A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = talpid_tunnel_config_client.h; path = "talpid-tunnel-config-client/include/talpid_tunnel_config_client.h"; sourceTree = ""; }; @@ -3334,6 +3336,7 @@ 58F3F3682AA08E2200D3B0A4 /* PacketTunnelProvider */, 58915D662A25F9F20066445B /* DeviceCheck */, 588395612A9DF497008B63F6 /* WireGuardAdapter */, + A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */, ); path = PacketTunnel; sourceTree = ""; @@ -5655,6 +5658,7 @@ 58F3F36A2AA08E3C00D3B0A4 /* PacketTunnelProvider.swift in Sources */, 58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */, 58C7A45B2A8640030060C66F /* PacketTunnelPathObserver.swift in Sources */, + A948809B2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift in Sources */, 580D6B8E2AB33BBF00B2D6E0 /* BlockedStateErrorMapper.swift in Sources */, 06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */, 583FE02429C1ACB3006E85F9 /* RESTCreateApplePaymentResponse+Localization.swift in Sources */, diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index b1b72b92c2fb..77533c22b566 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -257,22 +257,29 @@ extension PacketTunnelProvider { } } - private func startPostQuantumKeyNegotation(with privateKey: PrivateKey) { - quantumKeyNegotiatior?.cancelKeyNegotiation() - - let keyNegotiatior = PostQuantumKeyNegotiatior() - let gatewayAddress = "10.64.0.1" - let IPv4Gateway = IPv4Address(gatewayAddress)! + func createTCPConnectionForPQPSK(_ gatewayAddress: String) -> NWTCPConnection { let gatewayEndpoint = NWHostEndpoint(hostname: gatewayAddress, port: "1337") - let tcpConnection = createTCPConnectionThroughTunnel( + return createTCPConnectionThroughTunnel( to: gatewayEndpoint, enableTLS: false, tlsParameters: nil, delegate: nil ) + } + + private func startPostQuantumKeyNegotation(with privateKey: PrivateKey) { + quantumKeyNegotiatior?.cancelKeyNegotiation() + + let keyNegotiatior = PostQuantumKeyNegotiatior() + + let gatewayAddress = "10.64.0.1" + let IPv4Gateway = IPv4Address(gatewayAddress)! + let tcpConnection = createTCPConnectionForPQPSK(gatewayAddress) - let postQuantumSharedKey = PrivateKey() // This will become the new private key of the device - PacketTunnelActor.newPQPrivateKey = postQuantumSharedKey + // TODO: Pass the private key to rust directly + // It will derive the public key, and give us back both the preshared key, and the ephemeral private key + let ephemeralSharedKey = PrivateKey() // This will become the new private key of the device + PacketTunnelActor.newPQPrivateKey = ephemeralSharedKey let observer = tcpConnection.observe(\.isViable, options: [ .initial, .new, @@ -281,7 +288,7 @@ extension PacketTunnelProvider { keyNegotiatior.negotiateKey( gatewayIP: IPv4Gateway, devicePublicKey: privateKey.publicKey, - presharedKey: postQuantumSharedKey.publicKey, + presharedKey: ephemeralSharedKey.publicKey, packetTunnel: self, tcpConnection: tcpConnection ) diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift new file mode 100644 index 000000000000..47a93c6663e4 --- /dev/null +++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift @@ -0,0 +1,26 @@ +// +// PostQuantumKeyExchangeActor.swift +// PacketTunnel +// +// Created by Marco Nikic on 2024-04-12. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import MullvadPostQuantum +import NetworkExtension + +typealias InTunnelTCPConnectionCreator = (NWHostEndpoint) -> NWTCPConnection + +actor PostQuantumKeyExchangeActor { + let createNetworkConnection: InTunnelTCPConnectionCreator + unowned let packetTunnel: PacketTunnelProvider + private var quantumKeyNegotiatior: PostQuantumKeyNegotiatior! + private var inTunnelTCPConnection: NWTCPConnection! + private var tcpConnectionObserver: NSKeyValueObservation! + + init(packetTunnel: PacketTunnelProvider, createNetworkConnection: @escaping InTunnelTCPConnectionCreator) { + self.packetTunnel = packetTunnel + self.createNetworkConnection = createNetworkConnection + } +} diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift index be325fc7b65a..58600c633170 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift @@ -36,6 +36,7 @@ extension PacketTunnelActor { /// Network reachability events. case networkReachability(NetworkPath) + // TODO: Add the device's ephemeral new private key here /// Update the device private key, as per post-quantum protocols case replaceDevicePrivateKey(PreSharedKey) From 07aaa666f107daa91998313ebf977976318a28a0 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Fri, 12 Apr 2024 14:46:02 +0200 Subject: [PATCH 200/214] Add the ephemeral private key to the negotiation data flow (Swift-side only; Rust needs work) --- .../PacketTunnelProvider+TCPConnection.swift | 17 +++++++++++++---- .../PostQuantumKeyNegotiatior.swift | 2 +- .../Protocols/PostQuantumKeyReceiving.swift | 2 +- .../PacketTunnelProvider.swift | 6 +++--- .../Actor/PacketTunnelActor+Public.swift | 4 ++-- .../Actor/PacketTunnelActor.swift | 2 +- .../Actor/PacketTunnelActorCommand.swift | 2 +- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift index 48e350cec110..875f6ca5cd8d 100644 --- a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -53,17 +53,26 @@ func tcpConnectionReceive( } @_cdecl("swift_post_quantum_key_ready") -func receivePostQuantumKey(rawPacketTunnel: UnsafeMutableRawPointer?, rawPresharedKey: UnsafeMutableRawPointer?) { - guard let rawPacketTunnel else { return } +func receivePostQuantumKey( + rawPacketTunnel: UnsafeMutableRawPointer?, + rawPresharedKey: UnsafeMutableRawPointer?, + rawEphemeralKey: UnsafeMutableRawPointer? +) { + guard + let rawPacketTunnel, + let rawEphemeralKey, + let ephemeralKey = PrivateKey(rawValue: Data(bytes: rawEphemeralKey, count: 32)) + else { return } let packetTunnel = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() + guard let postQuantumKeyReceiver = packetTunnel as? PostQuantumKeyReceiving else { return } guard let rawPresharedKey else { - postQuantumKeyReceiver.receivePostQuantumKey(.none) + postQuantumKeyReceiver.receivePostQuantumKey(.none, ephemeralKey: ephemeralKey) return } let presharedKey = Data(bytes: rawPresharedKey, count: 32) if let key = PreSharedKey(rawValue: presharedKey) { - postQuantumKeyReceiver.receivePostQuantumKey(key) + postQuantumKeyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) } } diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift index 9bdd8b41e838..377837bf178f 100644 --- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift @@ -19,7 +19,7 @@ public class PostQuantumKeyNegotiatior { public func negotiateKey( gatewayIP: IPv4Address, devicePublicKey: PublicKey, - presharedKey: PublicKey, + presharedKey: PrivateKey, packetTunnel: NEPacketTunnelProvider, tcpConnection: NWTCPConnection ) { diff --git a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift index 714f4cd33e5b..a91ab0872da6 100644 --- a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift +++ b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift @@ -10,7 +10,7 @@ import Foundation import WireGuardKitTypes public protocol PostQuantumKeyReceiving { - func receivePostQuantumKey(_ key: PreSharedKey?) + func receivePostQuantumKey(_ key: PreSharedKey?, ephemeralKey: PrivateKey) } public enum PostQuantumKeyReceivingError: Error { diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 77533c22b566..b077909fc194 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -288,7 +288,7 @@ extension PacketTunnelProvider { keyNegotiatior.negotiateKey( gatewayIP: IPv4Gateway, devicePublicKey: privateKey.publicKey, - presharedKey: ephemeralSharedKey.publicKey, + presharedKey: ephemeralSharedKey, packetTunnel: self, tcpConnection: tcpConnection ) @@ -338,7 +338,7 @@ extension PacketTunnelProvider { } extension PacketTunnelProvider: PostQuantumKeyReceiving { - func receivePostQuantumKey(_ key: PreSharedKey?) { + func receivePostQuantumKey(_ key: PreSharedKey?, ephemeralKey: PrivateKey) { quantumKeyNegotiatior?.cancelKeyNegotiation() tcpConnectionObserver?.invalidate() inTunnelTCPConnection.cancel() @@ -347,7 +347,7 @@ extension PacketTunnelProvider: PostQuantumKeyReceiving { quantumKeyNegotiatior = nil if let key { - actor.replacePreSharedKey(key) + actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey) } else { actor.reconnect(to: .current) } diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift index 0d80fb6d580a..209a6041d54c 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift @@ -56,8 +56,8 @@ extension PacketTunnelActor { - Parameter key: the new key */ - nonisolated public func replacePreSharedKey(_ key: PreSharedKey) { - commandChannel.send(.replaceDevicePrivateKey(key)) + nonisolated public func replacePreSharedKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) { + commandChannel.send(.replaceDevicePrivateKey(key, ephemeralKey: ephemeralKey)) } /** diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index c0c4d1276006..ccc17bbc988f 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -115,7 +115,7 @@ public actor PacketTunnelActor { case let .networkReachability(defaultPath): await handleDefaultPathChange(defaultPath) - case let .replaceDevicePrivateKey(preSharedKey): + case let .replaceDevicePrivateKey(preSharedKey, ephemeralKey): await postQuantumConnect(with: preSharedKey) } } diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift index 58600c633170..dc3735a9e77f 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift @@ -38,7 +38,7 @@ extension PacketTunnelActor { // TODO: Add the device's ephemeral new private key here /// Update the device private key, as per post-quantum protocols - case replaceDevicePrivateKey(PreSharedKey) + case replaceDevicePrivateKey(PreSharedKey, ephemeralKey: PrivateKey) /// Format command for log output. func logFormat() -> String { From 2af50f2b5a9e0e91dee4ea887e20096a9ea3b9df Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Fri, 12 Apr 2024 15:10:17 +0200 Subject: [PATCH 201/214] Pass the ephemeral key to postQuantumConnect --- .../PacketTunnelProvider/PacketTunnelProvider.swift | 1 - ios/PacketTunnelCore/Actor/PacketTunnelActor.swift | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index b077909fc194..08cadd6fed2c 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -279,7 +279,6 @@ extension PacketTunnelProvider { // TODO: Pass the private key to rust directly // It will derive the public key, and give us back both the preshared key, and the ephemeral private key let ephemeralSharedKey = PrivateKey() // This will become the new private key of the device - PacketTunnelActor.newPQPrivateKey = ephemeralSharedKey let observer = tcpConnection.observe(\.isViable, options: [ .initial, .new, diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index ccc17bbc988f..8076f3e01237 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -36,8 +36,6 @@ public actor PacketTunnelActor { @Published internal(set) public var observedState: ObservedState = .initial - public static var newPQPrivateKey: PrivateKey! - let logger = Logger(label: "PacketTunnelActor") let timings: PacketTunnelActorTimings @@ -116,7 +114,7 @@ public actor PacketTunnelActor { await handleDefaultPathChange(defaultPath) case let .replaceDevicePrivateKey(preSharedKey, ephemeralKey): - await postQuantumConnect(with: preSharedKey) + await postQuantumConnect(with: preSharedKey, privateKey: ephemeralKey) } } } @@ -229,7 +227,7 @@ extension PacketTunnelActor { } } - private func postQuantumConnect(with key: PreSharedKey) async { + private func postQuantumConnect(with key: PreSharedKey, privateKey: PrivateKey) async { guard let settings: Settings = try? settingsReader.read(), let connectionState = try? obfuscateConnection( @@ -243,7 +241,7 @@ extension PacketTunnelActor { let activeKey = activeKey(from: connectionState, in: settings) let configurationBuilder = ConfigurationBuilder( // privateKey: activeKey, - privateKey: Self.newPQPrivateKey, + privateKey: privateKey, interfaceAddresses: settings.interfaceAddresses, dns: settings.dnsServers, endpoint: connectionState.connectedEndpoint, From 71156e1d53d7c5d48d7b9004957f4e61e1351a77 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Mon, 15 Apr 2024 14:43:11 +0200 Subject: [PATCH 202/214] Modify (partially) Rust code to handle the ephemeral key as passed through --- .../src/ios_ffi.rs | 9 +++++---- .../talpid-tunnel-config-client/src/lib.rs | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs index b69a1abdf4ec..56906d1296a9 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs @@ -46,7 +46,8 @@ pub unsafe extern "C" fn handle_recv(data: *const u8, data_len: usize, sender: * #[no_mangle] pub unsafe extern "C" fn negotiate_post_quantum_key( public_key: *const u8, - ephemeral_public_key: *const u8, + /// ephemeral_key is now the private key that's being passed back + ephemeral_key: *const u8, packet_tunnel: *const c_void, tcp_connection: *const c_void, cancel_token: *mut PostQuantumCancelToken, @@ -58,13 +59,13 @@ pub unsafe extern "C" fn negotiate_post_quantum_key( }); let pub_key_copy: [u8; 32] = unsafe { std::ptr::read(public_key as *const [u8; 32]) }; - let eph_pub_key_copy: [u8; 32] = - unsafe { std::ptr::read(ephemeral_public_key as *const [u8; 32]) }; + let eph_key_copy: [u8; 32] = + unsafe { std::ptr::read(ephemeral_key as *const [u8; 32]) }; match unsafe { run_ios_runtime( pub_key_copy, - eph_pub_key_copy, + eph_key_copy, packet_tunnel, tcp_connection, ) diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs index 079baf102708..a940769c2ee1 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -47,11 +47,11 @@ unsafe impl Send for PostQuantumCancelToken {} /// pub unsafe fn run_ios_runtime( pub_key: [u8; 32], - ephemeral_pub_key: [u8; 32], + ephemeral_key: [u8; 32], packet_tunnel: *const c_void, tcp_connection: *const c_void, ) -> Result { - match unsafe { IOSRuntime::new(pub_key, ephemeral_pub_key, packet_tunnel, tcp_connection) } { + match unsafe { IOSRuntime::new(pub_key, ephemeral_key, packet_tunnel, tcp_connection) } { Ok(runtime) => { let token = runtime.cancel_token_tx.clone(); @@ -79,7 +79,7 @@ unsafe impl Sync for SwiftContext {} struct IOSRuntime { runtime: tokio::runtime::Runtime, pub_key: [u8; 32], - ephemeral_public_key: [u8; 32], + ephemeral_key: [u8; 32], packet_tunnel: SwiftContext, cancel_token_tx: Arc>, cancel_token_rx: mpsc::UnboundedReceiver<()>, @@ -88,7 +88,7 @@ struct IOSRuntime { impl IOSRuntime { pub unsafe fn new( pub_key: [u8; 32], - ephemeral_public_key: [u8; 32], + ephemeral_key: [u8; 32], packet_tunnel: *const libc::c_void, tcp_connection: *const c_void, ) -> io::Result { @@ -107,7 +107,7 @@ impl IOSRuntime { Ok(Self { runtime, pub_key, - ephemeral_public_key, + ephemeral_key, packet_tunnel: context, cancel_token_tx: Arc::new(tx), cancel_token_rx: rx, @@ -153,19 +153,22 @@ impl IOSRuntime { return; } }; + // TODO: derive the public key from the (private) ephemeral key for use here + // let ephemeral_key = ..... tokio::select! { preshared_key = talpid_tunnel_config_client::push_pq_inner( &mut async_provider, PublicKey::from(self.pub_key), - PublicKey::from(self.ephemeral_public_key), + PublicKey::from(ephemeral_key), ) => { match preshared_key { Ok(key) => unsafe { let bytes = key.as_bytes(); - swift_post_quantum_key_ready(packet_tunnel_ptr, bytes.as_ptr()); + let eph_bytes = self.ephemeral_public_key.as_bytes(); + swift_post_quantum_key_ready(packet_tunnel_ptr, bytes.as_ptr(), eph_bytes.as_ptr()); }, Err(_) => unsafe { - swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null()); + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); }, } } From 3f18654323776dd18bd22dc2a9ae9858c8b04d2c Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Mon, 15 Apr 2024 16:51:41 +0200 Subject: [PATCH 203/214] Derive the ephemeral public key from the private key in the FFI --- .../include/talpid_tunnel_config_client.h | 5 +++-- .../src/ios_ffi.rs | 13 ++----------- .../src/ios_tcp_connection.rs | 1 + .../talpid-tunnel-config-client/src/lib.rs | 19 +++++++++---------- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h index c14dda4e12ba..12603e7b402f 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h @@ -28,7 +28,7 @@ void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender); * This function is safe to call */ int32_t negotiate_post_quantum_key(const uint8_t *public_key, - const uint8_t *ephemeral_public_key, + const uint8_t *ephemeral_key, const void *packet_tunnel, const void *tcp_connection, struct PostQuantumCancelToken *cancel_token); @@ -53,4 +53,5 @@ extern void swift_nw_tcp_connection_read(const void *connection, const void *sen * `raw_preshared_key` might be NULL if the key negotiation failed. */ extern void swift_post_quantum_key_ready(const void *raw_packet_tunnel, - const uint8_t *raw_preshared_key); + const uint8_t *raw_preshared_key, + const uint8_t *raw_ephemeral_private_key); diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs index 56906d1296a9..a37106db8912 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_ffi.rs @@ -46,7 +46,6 @@ pub unsafe extern "C" fn handle_recv(data: *const u8, data_len: usize, sender: * #[no_mangle] pub unsafe extern "C" fn negotiate_post_quantum_key( public_key: *const u8, - /// ephemeral_key is now the private key that's being passed back ephemeral_key: *const u8, packet_tunnel: *const c_void, tcp_connection: *const c_void, @@ -59,17 +58,9 @@ pub unsafe extern "C" fn negotiate_post_quantum_key( }); let pub_key_copy: [u8; 32] = unsafe { std::ptr::read(public_key as *const [u8; 32]) }; - let eph_key_copy: [u8; 32] = - unsafe { std::ptr::read(ephemeral_key as *const [u8; 32]) }; + let eph_key_copy: [u8; 32] = unsafe { std::ptr::read(ephemeral_key as *const [u8; 32]) }; - match unsafe { - run_ios_runtime( - pub_key_copy, - eph_key_copy, - packet_tunnel, - tcp_connection, - ) - } { + match unsafe { run_ios_runtime(pub_key_copy, eph_key_copy, packet_tunnel, tcp_connection) } { Ok(token) => { unsafe { std::ptr::write(cancel_token, token) }; 0 diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs index a93eec0fa8a1..ac3b7e3cb326 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs @@ -27,6 +27,7 @@ extern "C" { pub fn swift_post_quantum_key_ready( raw_packet_tunnel: *const c_void, raw_preshared_key: *const u8, + raw_ephemeral_private_key: *const u8, ); } diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs index a940769c2ee1..77920e4ea580 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -1,6 +1,7 @@ use std::ptr; use libc::c_void; +use talpid_types::net::wireguard::PrivateKey; use tokio::sync::mpsc; use std::io; @@ -148,24 +149,22 @@ impl IOSRuntime { Err(error) => { log::error!("Failed to create iOS TCP client: {error}"); unsafe { - swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null()); + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); } return; } }; - // TODO: derive the public key from the (private) ephemeral key for use here - // let ephemeral_key = ..... + let ephemeral_pub_key = PrivateKey::from(self.ephemeral_key).public_key(); tokio::select! { - preshared_key = talpid_tunnel_config_client::push_pq_inner( + key_result = talpid_tunnel_config_client::push_pq_inner( &mut async_provider, PublicKey::from(self.pub_key), - PublicKey::from(ephemeral_key), + ephemeral_pub_key, ) => { - match preshared_key { - Ok(key) => unsafe { - let bytes = key.as_bytes(); - let eph_bytes = self.ephemeral_public_key.as_bytes(); - swift_post_quantum_key_ready(packet_tunnel_ptr, bytes.as_ptr(), eph_bytes.as_ptr()); + match key_result { + Ok(preshared_key) => unsafe { + let preshared_key_bytes = preshared_key.as_bytes(); + swift_post_quantum_key_ready(packet_tunnel_ptr, preshared_key_bytes.as_ptr(), self.ephemeral_key.as_ptr()); }, Err(_) => unsafe { swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); From e50866397e534cbc6d0f32ebdd3a10cb30abf1b4 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Tue, 23 Apr 2024 18:36:25 +0200 Subject: [PATCH 204/214] Correct spelling of PostQuantumKeyNegotiator --- ...eyNegotiatior.swift => PostQuantumKeyNegotiator.swift} | 4 ++-- ios/MullvadVPN.xcodeproj/project.pbxproj | 8 ++++---- .../PacketTunnelProvider/PacketTunnelProvider.swift | 4 ++-- ios/PacketTunnel/PostQuantumKeyExchangeActor.swift | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename ios/MullvadPostQuantum/{PostQuantumKeyNegotiatior.swift => PostQuantumKeyNegotiator.swift} (95%) diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift similarity index 95% rename from ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift rename to ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift index 377837bf178f..8e813f47df90 100644 --- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiatior.swift +++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift @@ -1,5 +1,5 @@ // -// PostQuantumKeyNegotiatior.swift +// PostQuantumKeyNegotiator.swift // PacketTunnel // // Created by Marco Nikic on 2024-02-16. @@ -11,7 +11,7 @@ import NetworkExtension import TalpidTunnelConfigClientProxy import WireGuardKitTypes -public class PostQuantumKeyNegotiatior { +public class PostQuantumKeyNegotiator { public init() {} var cancelToken: PostQuantumCancelToken? diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 8eaaa363dd56..442ccd2c46cf 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -694,7 +694,7 @@ A944F25F2B8DEFDB00473F4C /* MullvadPostQuantum.h in Headers */ = {isa = PBXBuildFile; fileRef = A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */; settings = {ATTRIBUTES = (Public, ); }; }; A944F2622B8DEFDB00473F4C /* MullvadPostQuantum.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; }; A944F2632B8DEFDB00473F4C /* MullvadPostQuantum.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiatior.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiatior.swift */; }; + A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */; }; A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client_proxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */; }; A948809B2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */; }; A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E22AA72AE9003D1918 /* WireGuardKitTypes */; }; @@ -2041,7 +2041,7 @@ A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelStore+Stubs.swift"; sourceTree = ""; }; A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusBlockObserver.swift; sourceTree = ""; }; A9E034632ABB302000E59A5A /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extensions.swift"; sourceTree = ""; }; - A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiatior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyNegotiatior.swift; sourceTree = ""; }; + A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyNegotiator.swift; sourceTree = ""; }; A9EC20E72A5D3A8C0040D56E /* CoordinatesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatesTests.swift; sourceTree = ""; }; A9F360332AAB626300F53531 /* VPNConnectionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConnectionProtocol.swift; sourceTree = ""; }; E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeViewController.swift; sourceTree = ""; }; @@ -3791,7 +3791,7 @@ children = ( A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */, A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */, - A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiatior.swift */, + A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */, A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */, ); path = MullvadPostQuantum; @@ -5844,7 +5844,7 @@ buildActionMask = 2147483647; files = ( A9630E492B921E6D00A65999 /* PacketTunnelProvider+TCPConnection.swift in Sources */, - A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiatior.swift in Sources */, + A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiator.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 08cadd6fed2c..13181740c43a 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -32,7 +32,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // Post Quantum Key required variables private var inTunnelTCPConnection: NWTCPConnection! private var tcpConnectionObserver: NSKeyValueObservation! - private var quantumKeyNegotiatior: PostQuantumKeyNegotiatior! + private var quantumKeyNegotiatior: PostQuantumKeyNegotiator! override init() { Self.configureLogging() @@ -270,7 +270,7 @@ extension PacketTunnelProvider { private func startPostQuantumKeyNegotation(with privateKey: PrivateKey) { quantumKeyNegotiatior?.cancelKeyNegotiation() - let keyNegotiatior = PostQuantumKeyNegotiatior() + let keyNegotiatior = PostQuantumKeyNegotiator() let gatewayAddress = "10.64.0.1" let IPv4Gateway = IPv4Address(gatewayAddress)! diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift index 47a93c6663e4..783ca123ca03 100644 --- a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift +++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift @@ -15,7 +15,7 @@ typealias InTunnelTCPConnectionCreator = (NWHostEndpoint) -> NWTCPConnection actor PostQuantumKeyExchangeActor { let createNetworkConnection: InTunnelTCPConnectionCreator unowned let packetTunnel: PacketTunnelProvider - private var quantumKeyNegotiatior: PostQuantumKeyNegotiatior! + private var quantumKeyNegotiatior: PostQuantumKeyNegotiator! private var inTunnelTCPConnection: NWTCPConnection! private var tcpConnectionObserver: NSKeyValueObservation! From 5c10737c6bc39857a0094b496f8b855e22cc3428 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 24 Apr 2024 10:39:52 +0200 Subject: [PATCH 205/214] Add functionality to the actor --- .../PacketTunnelProvider.swift | 3 ++ .../PostQuantumKeyExchangeActor.swift | 50 ++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 13181740c43a..79f6fd4e17ef 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -23,6 +23,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private let constraintsUpdater = RelayConstraintsUpdater() private var actor: PacketTunnelActor! + private var postQuantumActor: PostQuantumKeyExchangeActor! private var appMessageHandler: AppMessageHandler! private var stateObserverTask: AnyTask? private var deviceChecker: DeviceChecker! @@ -86,6 +87,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider { protocolObfuscator: ProtocolObfuscator() ) + postQuantumActor = PostQuantumKeyExchangeActor(packetTunnel: self) + let urlRequestProxy = URLRequestProxy(dispatchQueue: internalQueue, transportProvider: transportProvider) appMessageHandler = AppMessageHandler(packetTunnelActor: actor, urlRequestProxy: urlRequestProxy) diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift index 783ca123ca03..7f7fd8e06c74 100644 --- a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift +++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift @@ -9,18 +9,54 @@ import Foundation import MullvadPostQuantum import NetworkExtension +import WireGuardKitTypes -typealias InTunnelTCPConnectionCreator = (NWHostEndpoint) -> NWTCPConnection +// not needed? as we have the PacketTunnelProvider +// typealias InTunnelTCPConnectionCreator = (NWHostEndpoint) -> NWTCPConnection actor PostQuantumKeyExchangeActor { - let createNetworkConnection: InTunnelTCPConnectionCreator +// let createNetworkConnection: InTunnelTCPConnectionCreator unowned let packetTunnel: PacketTunnelProvider - private var quantumKeyNegotiatior: PostQuantumKeyNegotiator! - private var inTunnelTCPConnection: NWTCPConnection! - private var tcpConnectionObserver: NSKeyValueObservation! + private var negotiator: PostQuantumKeyNegotiator? + private var inTunnelTCPConnection: NWTCPConnection? + private var tcpConnectionObserver: NSKeyValueObservation? - init(packetTunnel: PacketTunnelProvider, createNetworkConnection: @escaping InTunnelTCPConnectionCreator) { + init(packetTunnel: PacketTunnelProvider /* , createNetworkConnection: @escaping InTunnelTCPConnectionCreator */ ) { self.packetTunnel = packetTunnel - self.createNetworkConnection = createNetworkConnection + } + + private func createTCPConnection(_ gatewayEndpoint: NWHostEndpoint) -> NWTCPConnection { + self.packetTunnel.createTCPConnectionThroughTunnel( + to: gatewayEndpoint, + enableTLS: false, + tlsParameters: nil, + delegate: nil + ) + } + + func startNegotiation(with privateKey: PrivateKey) { + negotiator = PostQuantumKeyNegotiator() + + let gatewayAddress = "10.64.0.1" + let IPv4Gateway = IPv4Address(gatewayAddress)! + let endpoint = NWHostEndpoint(hostname: gatewayAddress, port: "1337") + inTunnelTCPConnection = createTCPConnection(endpoint) + + let ephemeralSharedKey = PrivateKey() // This will become the new private key of the device + + tcpConnectionObserver = inTunnelTCPConnection!.observe(\.isViable, options: [ + .initial, + .new, + ]) { [weak self] observedConnection, _ in + guard let self, observedConnection.isViable else { return } + negotiator!.negotiateKey( + gatewayIP: IPv4Gateway, + devicePublicKey: privateKey.publicKey, + presharedKey: ephemeralSharedKey, + packetTunnel: packetTunnel, + tcpConnection: inTunnelTCPConnection! + ) + self.tcpConnectionObserver!.invalidate() + } } } From 1062fab8b0a1c94cf9ec33a1d26c96961faa6f15 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 24 Apr 2024 12:04:22 +0200 Subject: [PATCH 206/214] Move PQ negotiation to Actor which is now not an Actor --- .../PacketTunnelProvider.swift | 72 ++++++++++--------- .../PostQuantumKeyExchangeActor.swift | 11 ++- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 79f6fd4e17ef..21bfec36899e 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -271,35 +271,36 @@ extension PacketTunnelProvider { } private func startPostQuantumKeyNegotation(with privateKey: PrivateKey) { - quantumKeyNegotiatior?.cancelKeyNegotiation() - - let keyNegotiatior = PostQuantumKeyNegotiator() - - let gatewayAddress = "10.64.0.1" - let IPv4Gateway = IPv4Address(gatewayAddress)! - let tcpConnection = createTCPConnectionForPQPSK(gatewayAddress) - - // TODO: Pass the private key to rust directly - // It will derive the public key, and give us back both the preshared key, and the ephemeral private key - let ephemeralSharedKey = PrivateKey() // This will become the new private key of the device - let observer = tcpConnection.observe(\.isViable, options: [ - .initial, - .new, - ]) { [weak self] observedConnection, _ in - guard let self, observedConnection.isViable else { return } - keyNegotiatior.negotiateKey( - gatewayIP: IPv4Gateway, - devicePublicKey: privateKey.publicKey, - presharedKey: ephemeralSharedKey, - packetTunnel: self, - tcpConnection: tcpConnection - ) - self.tcpConnectionObserver.invalidate() - } - - inTunnelTCPConnection = tcpConnection - tcpConnectionObserver = observer - quantumKeyNegotiatior = keyNegotiatior + postQuantumActor.startNegotiation(with: privateKey) +// quantumKeyNegotiatior?.cancelKeyNegotiation() +// +// let keyNegotiatior = PostQuantumKeyNegotiator() +// +// let gatewayAddress = "10.64.0.1" +// let IPv4Gateway = IPv4Address(gatewayAddress)! +// let tcpConnection = createTCPConnectionForPQPSK(gatewayAddress) +// +// // TODO: Pass the private key to rust directly +// // It will derive the public key, and give us back both the preshared key, and the ephemeral private key +// let ephemeralSharedKey = PrivateKey() // This will become the new private key of the device +// let observer = tcpConnection.observe(\.isViable, options: [ +// .initial, +// .new, +// ]) { [weak self] observedConnection, _ in +// guard let self, observedConnection.isViable else { return } +// keyNegotiatior.negotiateKey( +// gatewayIP: IPv4Gateway, +// devicePublicKey: privateKey.publicKey, +// presharedKey: ephemeralSharedKey, +// packetTunnel: self, +// tcpConnection: tcpConnection +// ) +// self.tcpConnectionObserver.invalidate() +// } +// +// inTunnelTCPConnection = tcpConnection +// tcpConnectionObserver = observer +// quantumKeyNegotiatior = keyNegotiatior // Re-establish the tunnel connection to let the TCP connection flow through // reasserting = false } @@ -341,12 +342,13 @@ extension PacketTunnelProvider { extension PacketTunnelProvider: PostQuantumKeyReceiving { func receivePostQuantumKey(_ key: PreSharedKey?, ephemeralKey: PrivateKey) { - quantumKeyNegotiatior?.cancelKeyNegotiation() - tcpConnectionObserver?.invalidate() - inTunnelTCPConnection.cancel() - tcpConnectionObserver = nil - inTunnelTCPConnection = nil - quantumKeyNegotiatior = nil + postQuantumActor.acknowledgePostQuantumKey() +// quantumKeyNegotiatior?.cancelKeyNegotiation() +// tcpConnectionObserver?.invalidate() +// inTunnelTCPConnection.cancel() +// tcpConnectionObserver = nil +// inTunnelTCPConnection = nil +// quantumKeyNegotiatior = nil if let key { actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey) diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift index 7f7fd8e06c74..69ba1edadaa1 100644 --- a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift +++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift @@ -14,7 +14,7 @@ import WireGuardKitTypes // not needed? as we have the PacketTunnelProvider // typealias InTunnelTCPConnectionCreator = (NWHostEndpoint) -> NWTCPConnection -actor PostQuantumKeyExchangeActor { +class PostQuantumKeyExchangeActor { // let createNetworkConnection: InTunnelTCPConnectionCreator unowned let packetTunnel: PacketTunnelProvider private var negotiator: PostQuantumKeyNegotiator? @@ -59,4 +59,13 @@ actor PostQuantumKeyExchangeActor { self.tcpConnectionObserver!.invalidate() } } + + func acknowledgePostQuantumKey() { + negotiator?.cancelKeyNegotiation() + tcpConnectionObserver?.invalidate() + inTunnelTCPConnection?.cancel() + tcpConnectionObserver = nil + inTunnelTCPConnection = nil + negotiator = nil + } } From 1626e481645dea2c92a4ae3104426715158105a0 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 24 Apr 2024 14:37:32 +0200 Subject: [PATCH 207/214] Refactor PQ key receiving protocol --- .../PacketTunnelProvider+TCPConnection.swift | 24 +++++++++---------- .../Protocols/PostQuantumKeyReceiving.swift | 3 ++- .../PacketTunnelProvider.swift | 15 ++++++------ .../PostQuantumKeyExchangeActor.swift | 2 +- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift index 875f6ca5cd8d..5da6d0c9331a 100644 --- a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -58,21 +58,21 @@ func receivePostQuantumKey( rawPresharedKey: UnsafeMutableRawPointer?, rawEphemeralKey: UnsafeMutableRawPointer? ) { + guard let rawPacketTunnel, - let rawEphemeralKey, - let ephemeralKey = PrivateKey(rawValue: Data(bytes: rawEphemeralKey, count: 32)) + let postQuantumKeyReceiver = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() as? PostQuantumKeyReceiving else { return } - let packetTunnel = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() - - guard let postQuantumKeyReceiver = packetTunnel as? PostQuantumKeyReceiving else { return } - guard let rawPresharedKey else { - postQuantumKeyReceiver.receivePostQuantumKey(.none, ephemeralKey: ephemeralKey) + + guard + let rawPresharedKey, + let rawEphemeralKey, + let ephemeralKey = PrivateKey(rawValue: Data(bytes: rawEphemeralKey, count: 32)), + let key = PreSharedKey(rawValue: Data(bytes: rawPresharedKey, count: 32)) + else { + postQuantumKeyReceiver.keyExchangeFailed() return } - - let presharedKey = Data(bytes: rawPresharedKey, count: 32) - if let key = PreSharedKey(rawValue: presharedKey) { - postQuantumKeyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) - } + + postQuantumKeyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) } diff --git a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift index a91ab0872da6..50809c50b168 100644 --- a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift +++ b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift @@ -10,7 +10,8 @@ import Foundation import WireGuardKitTypes public protocol PostQuantumKeyReceiving { - func receivePostQuantumKey(_ key: PreSharedKey?, ephemeralKey: PrivateKey) + func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) + func keyExchangeFailed() } public enum PostQuantumKeyReceivingError: Error { diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 21bfec36899e..eb20e4a039df 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -341,8 +341,8 @@ extension PacketTunnelProvider { } extension PacketTunnelProvider: PostQuantumKeyReceiving { - func receivePostQuantumKey(_ key: PreSharedKey?, ephemeralKey: PrivateKey) { - postQuantumActor.acknowledgePostQuantumKey() + func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) { + postQuantumActor.acknowledgeNegotiationConcluded() // quantumKeyNegotiatior?.cancelKeyNegotiation() // tcpConnectionObserver?.invalidate() // inTunnelTCPConnection.cancel() @@ -350,10 +350,11 @@ extension PacketTunnelProvider: PostQuantumKeyReceiving { // inTunnelTCPConnection = nil // quantumKeyNegotiatior = nil - if let key { - actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey) - } else { - actor.reconnect(to: .current) - } + actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey) + } + + func keyExchangeFailed() { + postQuantumActor.acknowledgeNegotiationConcluded() + actor.reconnect(to: .current) } } diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift index 69ba1edadaa1..fe991040f6d5 100644 --- a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift +++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift @@ -60,7 +60,7 @@ class PostQuantumKeyExchangeActor { } } - func acknowledgePostQuantumKey() { + func acknowledgeNegotiationConcluded() { negotiator?.cancelKeyNegotiation() tcpConnectionObserver?.invalidate() inTunnelTCPConnection?.cancel() From 3829fef10a577f0d0bc0d90dfb70bab0d0fdf49e Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 24 Apr 2024 14:37:48 +0200 Subject: [PATCH 208/214] Fix whitespace --- .../PacketTunnelProvider+TCPConnection.swift | 8 ++++---- .../PacketTunnelProvider/PacketTunnelProvider.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift index 5da6d0c9331a..8b06c7b178c1 100644 --- a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift +++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift @@ -58,12 +58,12 @@ func receivePostQuantumKey( rawPresharedKey: UnsafeMutableRawPointer?, rawEphemeralKey: UnsafeMutableRawPointer? ) { - guard let rawPacketTunnel, - let postQuantumKeyReceiver = Unmanaged.fromOpaque(rawPacketTunnel).takeUnretainedValue() as? PostQuantumKeyReceiving + let postQuantumKeyReceiver = Unmanaged.fromOpaque(rawPacketTunnel) + .takeUnretainedValue() as? PostQuantumKeyReceiving else { return } - + guard let rawPresharedKey, let rawEphemeralKey, @@ -73,6 +73,6 @@ func receivePostQuantumKey( postQuantumKeyReceiver.keyExchangeFailed() return } - + postQuantumKeyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) } diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index eb20e4a039df..d66f0e5a4ef2 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -352,7 +352,7 @@ extension PacketTunnelProvider: PostQuantumKeyReceiving { actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey) } - + func keyExchangeFailed() { postQuantumActor.acknowledgeNegotiationConcluded() actor.reconnect(to: .current) From a4c39a60744d772b1e90b638debc75a2a181ed02 Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 24 Apr 2024 15:16:08 +0200 Subject: [PATCH 209/214] Combine all negotiation-lifecycle data in one optional struct --- .../PostQuantumKeyExchangeActor.swift | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift index fe991040f6d5..f46045169d0c 100644 --- a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift +++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift @@ -11,15 +11,21 @@ import MullvadPostQuantum import NetworkExtension import WireGuardKitTypes -// not needed? as we have the PacketTunnelProvider -// typealias InTunnelTCPConnectionCreator = (NWHostEndpoint) -> NWTCPConnection - class PostQuantumKeyExchangeActor { -// let createNetworkConnection: InTunnelTCPConnectionCreator + struct Negotiation { + var negotiator: PostQuantumKeyNegotiator + var inTunnelTCPConnection: NWTCPConnection + var tcpConnectionObserver: NSKeyValueObservation + + func cancel() { + negotiator.cancelKeyNegotiation() + tcpConnectionObserver.invalidate() + inTunnelTCPConnection.cancel() + } + } + unowned let packetTunnel: PacketTunnelProvider - private var negotiator: PostQuantumKeyNegotiator? - private var inTunnelTCPConnection: NWTCPConnection? - private var tcpConnectionObserver: NSKeyValueObservation? + private var negotiation: Negotiation? init(packetTunnel: PacketTunnelProvider /* , createNetworkConnection: @escaping InTunnelTCPConnectionCreator */ ) { self.packetTunnel = packetTunnel @@ -35,37 +41,38 @@ class PostQuantumKeyExchangeActor { } func startNegotiation(with privateKey: PrivateKey) { - negotiator = PostQuantumKeyNegotiator() + let negotiator = PostQuantumKeyNegotiator() let gatewayAddress = "10.64.0.1" let IPv4Gateway = IPv4Address(gatewayAddress)! let endpoint = NWHostEndpoint(hostname: gatewayAddress, port: "1337") - inTunnelTCPConnection = createTCPConnection(endpoint) + let inTunnelTCPConnection = createTCPConnection(endpoint) let ephemeralSharedKey = PrivateKey() // This will become the new private key of the device - tcpConnectionObserver = inTunnelTCPConnection!.observe(\.isViable, options: [ + let tcpConnectionObserver = inTunnelTCPConnection.observe(\.isViable, options: [ .initial, .new, ]) { [weak self] observedConnection, _ in guard let self, observedConnection.isViable else { return } - negotiator!.negotiateKey( + negotiator.negotiateKey( gatewayIP: IPv4Gateway, devicePublicKey: privateKey.publicKey, presharedKey: ephemeralSharedKey, packetTunnel: packetTunnel, - tcpConnection: inTunnelTCPConnection! + tcpConnection: inTunnelTCPConnection ) - self.tcpConnectionObserver!.invalidate() + self.negotiation?.tcpConnectionObserver.invalidate() } + negotiation = Negotiation( + negotiator: negotiator, + inTunnelTCPConnection: inTunnelTCPConnection, + tcpConnectionObserver: tcpConnectionObserver + ) } func acknowledgeNegotiationConcluded() { - negotiator?.cancelKeyNegotiation() - tcpConnectionObserver?.invalidate() - inTunnelTCPConnection?.cancel() - tcpConnectionObserver = nil - inTunnelTCPConnection = nil - negotiator = nil + negotiation?.cancel() + negotiation = nil } } From 5815d3aa821ef6466101d650f18cee87d3c9d19e Mon Sep 17 00:00:00 2001 From: Andrew Bulhak Date: Wed, 24 Apr 2024 16:23:38 +0200 Subject: [PATCH 210/214] Remove obsolete code from PacketTunnelProvider --- .../PacketTunnelProvider.swift | 41 ------------------- .../PostQuantumKeyExchangeActor.swift | 2 +- 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index d66f0e5a4ef2..9325fa6cf8b6 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -30,11 +30,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private var adapter: WgAdapter! private var relaySelector: RelaySelectorWrapper! - // Post Quantum Key required variables - private var inTunnelTCPConnection: NWTCPConnection! - private var tcpConnectionObserver: NSKeyValueObservation! - private var quantumKeyNegotiatior: PostQuantumKeyNegotiator! - override init() { Self.configureLogging() @@ -272,35 +267,6 @@ extension PacketTunnelProvider { private func startPostQuantumKeyNegotation(with privateKey: PrivateKey) { postQuantumActor.startNegotiation(with: privateKey) -// quantumKeyNegotiatior?.cancelKeyNegotiation() -// -// let keyNegotiatior = PostQuantumKeyNegotiator() -// -// let gatewayAddress = "10.64.0.1" -// let IPv4Gateway = IPv4Address(gatewayAddress)! -// let tcpConnection = createTCPConnectionForPQPSK(gatewayAddress) -// -// // TODO: Pass the private key to rust directly -// // It will derive the public key, and give us back both the preshared key, and the ephemeral private key -// let ephemeralSharedKey = PrivateKey() // This will become the new private key of the device -// let observer = tcpConnection.observe(\.isViable, options: [ -// .initial, -// .new, -// ]) { [weak self] observedConnection, _ in -// guard let self, observedConnection.isViable else { return } -// keyNegotiatior.negotiateKey( -// gatewayIP: IPv4Gateway, -// devicePublicKey: privateKey.publicKey, -// presharedKey: ephemeralSharedKey, -// packetTunnel: self, -// tcpConnection: tcpConnection -// ) -// self.tcpConnectionObserver.invalidate() -// } -// -// inTunnelTCPConnection = tcpConnection -// tcpConnectionObserver = observer -// quantumKeyNegotiatior = keyNegotiatior // Re-establish the tunnel connection to let the TCP connection flow through // reasserting = false } @@ -343,13 +309,6 @@ extension PacketTunnelProvider { extension PacketTunnelProvider: PostQuantumKeyReceiving { func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) { postQuantumActor.acknowledgeNegotiationConcluded() -// quantumKeyNegotiatior?.cancelKeyNegotiation() -// tcpConnectionObserver?.invalidate() -// inTunnelTCPConnection.cancel() -// tcpConnectionObserver = nil -// inTunnelTCPConnection = nil -// quantumKeyNegotiatior = nil - actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey) } diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift index f46045169d0c..e9189d947c65 100644 --- a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift +++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift @@ -27,7 +27,7 @@ class PostQuantumKeyExchangeActor { unowned let packetTunnel: PacketTunnelProvider private var negotiation: Negotiation? - init(packetTunnel: PacketTunnelProvider /* , createNetworkConnection: @escaping InTunnelTCPConnectionCreator */ ) { + init(packetTunnel: PacketTunnelProvider) { self.packetTunnel = packetTunnel } From 15d4f91e0137ca1d2ef86c78a709dad081eda391 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Fri, 26 Apr 2024 15:54:37 +0200 Subject: [PATCH 211/214] Do not schedule more than one read or writes at a time --- .../src/ios_tcp_connection.rs | 20 ++++++++++++++++++- .../PacketTunnelProvider.swift | 2 +- .../Actor/PacketTunnelActor.swift | 2 -- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs index ac3b7e3cb326..d11202b83164 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/ios_tcp_connection.rs @@ -39,6 +39,8 @@ pub struct IosTcpProvider { read_tx: mpsc::UnboundedSender>, read_rx: mpsc::UnboundedReceiver>, tcp_connection: *const c_void, + read_in_progress: bool, + write_in_progress: bool, } impl IosTcpProvider { @@ -51,6 +53,8 @@ impl IosTcpProvider { read_tx: recv_tx, read_rx: recv_rx, tcp_connection, + read_in_progress: false, + write_in_progress: false, } } } @@ -64,11 +68,19 @@ impl AsyncWrite for IosTcpProvider { let raw_sender = Box::into_raw(Box::new(self.write_tx.clone())); match self.write_rx.poll_recv(cx) { - std::task::Poll::Ready(Some(bytes_sent)) => Poll::Ready(Ok(bytes_sent)), + std::task::Poll::Ready(Some(bytes_sent)) => { + self.write_in_progress = false; + Poll::Ready(Ok(bytes_sent)) + } std::task::Poll::Ready(None) => { + self.write_in_progress = false; Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "sender dropped"))) } std::task::Poll::Pending => { + if self.write_in_progress { + return std::task::Poll::Pending; + } + self.write_in_progress = true; unsafe { swift_nw_tcp_connection_send( self.tcp_connection, @@ -107,12 +119,18 @@ impl AsyncRead for IosTcpProvider { match self.read_rx.poll_recv(cx) { std::task::Poll::Ready(Some(data)) => { buf.put_slice(&data); + self.read_in_progress = false; Poll::Ready(Ok(())) } std::task::Poll::Ready(None) => { + self.read_in_progress = false; Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "sender dropped"))) } std::task::Poll::Pending => { + if self.read_in_progress { + return std::task::Poll::Pending; + } + self.read_in_progress = true; unsafe { swift_nw_tcp_connection_read(self.tcp_connection, raw_sender as _); } diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 9325fa6cf8b6..0d6b7a1c38dd 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -308,8 +308,8 @@ extension PacketTunnelProvider { extension PacketTunnelProvider: PostQuantumKeyReceiving { func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) { - postQuantumActor.acknowledgeNegotiationConcluded() actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey) + postQuantumActor.acknowledgeNegotiationConcluded() } func keyExchangeFailed() { diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index 8076f3e01237..ae50069ee8e8 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -238,9 +238,7 @@ extension PacketTunnelActor { let targetState = state.targetStateForReconnect else { return } - let activeKey = activeKey(from: connectionState, in: settings) let configurationBuilder = ConfigurationBuilder( - // privateKey: activeKey, privateKey: privateKey, interfaceAddresses: settings.interfaceAddresses, dns: settings.dnsServers, From fb891f0a77da0b61b51b3195c181113117583f26 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Mon, 29 Apr 2024 11:29:39 +0200 Subject: [PATCH 212/214] Use the new proto file --- ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs index 3a4f92f421c5..54a77b1e1019 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/build.rs @@ -1,5 +1,5 @@ fn main() { - tonic_build::compile_protos("../../../talpid-tunnel-config-client/proto/tunnel_config.proto") + tonic_build::compile_protos("../../../talpid-tunnel-config-client/proto/ephemeralpeer.proto") .unwrap(); match std::env::var("TARGET").unwrap().as_str() { From eb4737370b63f40ee87edfb52ffe2724f99f8959 Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Mon, 29 Apr 2024 11:45:24 +0200 Subject: [PATCH 213/214] Readd missing line after rebase --- talpid-tunnel-config-client/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs index d287dc400431..e272e794c776 100644 --- a/talpid-tunnel-config-client/src/lib.rs +++ b/talpid-tunnel-config-client/src/lib.rs @@ -134,6 +134,7 @@ pub async fn request_ephemeral_peer( activate_daita: enable_daita, }); + let mut client = new_client(service_address).await?; let response = client .register_peer_v1(proto::EphemeralPeerRequestV1 { wg_parent_pubkey: parent_pubkey.as_bytes().to_vec(), From 4955b2231f7ba6ce147cc5d1dc071185b269825b Mon Sep 17 00:00:00 2001 From: Bug Magnet Date: Mon, 29 Apr 2024 12:02:30 +0200 Subject: [PATCH 214/214] Remove the iOS custom TTCC crate in favour of the original one --- Cargo.lock | 21 +-- Cargo.toml | 1 - .../module.private.modulemap | 2 +- .../include/talpid_tunnel_config_client.h | 5 + .../talpid-tunnel-config-client/src/lib.rs | 2 +- ios/MullvadVPN.xcodeproj/project.pbxproj | 10 +- talpid-tunnel-config-client/Cargo.toml | 11 +- talpid-tunnel-config-client/build.rs | 12 ++ .../src/ios_ffi/ios_runtime.rs | 169 ++++++++++++++++++ .../src/ios_ffi/ios_tcp_connection.rs | 142 +++++++++++++++ .../src/ios_ffi/mod.rs | 95 ++++++++++ talpid-tunnel-config-client/src/lib.rs | 94 ++++++---- 12 files changed, 500 insertions(+), 64 deletions(-) create mode 100644 talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs create mode 100644 talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs create mode 100644 talpid-tunnel-config-client/src/ios_ffi/mod.rs diff --git a/Cargo.lock b/Cargo.lock index cbc85855aa8c..b400704a1785 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4040,6 +4040,7 @@ dependencies = [ "classic-mceliece-rust", "libc", "log", + "oslog", "pqc_kyber", "prost", "rand 0.8.5", @@ -4052,26 +4053,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "talpid-tunnel-config-client-proxy" -version = "0.0.0" -dependencies = [ - "cbindgen", - "classic-mceliece-rust", - "libc", - "log", - "oslog", - "pqc_kyber", - "prost", - "talpid-tunnel-config-client", - "talpid-types", - "tokio", - "tonic", - "tonic-build", - "tower", - "zeroize", -] - [[package]] name = "talpid-types" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 173f8d8f6ebe..f7fcd70642b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ members = [ "android/translations-converter", "ios/MullvadREST/Transport/Shadowsocks/shadowsocks-proxy", "ios/TunnelObfuscation/tunnel-obfuscator-proxy", - "ios/MullvadPostQuantum/talpid-tunnel-config-client", "mullvad-api", "mullvad-cli", "mullvad-daemon", diff --git a/ios/MullvadPostQuantum/module.private.modulemap b/ios/MullvadPostQuantum/module.private.modulemap index ff5f63eee8d6..f831c7ca2e37 100644 --- a/ios/MullvadPostQuantum/module.private.modulemap +++ b/ios/MullvadPostQuantum/module.private.modulemap @@ -1,5 +1,5 @@ framework module TalpidTunnelConfigClientProxy { header "talpid_tunnel_config_client.h" - link "libtalpid_tunnel_config_client_proxy" + link "libtalpid_tunnel_config_client" export * } diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h index 12603e7b402f..e289ca4af895 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h @@ -3,6 +3,11 @@ #include #include +/** + * Port used by the tunnel config service. + */ +#define CONFIG_SERVICE_PORT 1337 + typedef struct PostQuantumCancelToken { void *context; } PostQuantumCancelToken; diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs index 77920e4ea580..5577aaf5863c 100644 --- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs +++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/src/lib.rs @@ -121,7 +121,7 @@ impl IOSRuntime { }); } - async fn ios_tcp_client(ctx: SwiftContext) -> Result { + pub async fn ios_tcp_client(ctx: SwiftContext) -> Result { let endpoint = Endpoint::from_static("tcp://0.0.0.0:0"); let conn = endpoint .connect_with_connector(service_fn(move |_| { diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 442ccd2c46cf..29ae0246c695 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -695,7 +695,7 @@ A944F2622B8DEFDB00473F4C /* MullvadPostQuantum.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; }; A944F2632B8DEFDB00473F4C /* MullvadPostQuantum.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */; }; - A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client_proxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */; }; + A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client.a */; }; A948809B2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */; }; A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E22AA72AE9003D1918 /* WireGuardKitTypes */; }; A94D691B2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E72AA7399D003D1918 /* WireGuardKitTypes */; }; @@ -2003,7 +2003,7 @@ A935594D2B4E919F00D5D524 /* Api.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Api.xcconfig; sourceTree = ""; }; A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadPostQuantum.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadPostQuantum.h; sourceTree = ""; }; - A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtalpid_tunnel_config_client_proxy.a; path = "../target/aarch64-apple-ios/debug/libtalpid_tunnel_config_client_proxy.a"; sourceTree = ""; }; + A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtalpid_tunnel_config_client.a; path = "../target/aarch64-apple-ios/debug/libtalpid_tunnel_config_client.a"; sourceTree = ""; }; A9467E7E2A29DEFE000DC21F /* RelayCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTests.swift; sourceTree = ""; }; A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyExchangeActor.swift; sourceTree = ""; }; A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorState.swift; sourceTree = ""; }; @@ -2298,7 +2298,7 @@ files = ( A9630E432B8E10FB00A65999 /* MullvadTypes.framework in Frameworks */, A906F94A2BA1E09A002BF22E /* WireGuardKitTypes in Frameworks */, - A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client_proxy.a in Frameworks */, + A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2822,7 +2822,7 @@ 584F991F2902CBDD001F858D /* Frameworks */ = { isa = PBXGroup; children = ( - A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client_proxy.a */, + A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client.a */, 01EF6F332B6A590700125696 /* libmullvad_api.a */, 01EF6F312B6A58F000125696 /* debug */, 01EF6F2F2B6A588300125696 /* aarch64-apple-ios */, @@ -4852,7 +4852,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "CARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh talpid-tunnel-config-client-proxy\n"; + shellScript = "CARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh talpid-tunnel-config-client\n"; }; F05F39962B21C704006E60A7 /* Prebuild relays */ = { isa = PBXShellScriptBuildPhase; diff --git a/talpid-tunnel-config-client/Cargo.toml b/talpid-tunnel-config-client/Cargo.toml index 6aa8e5e3fc79..18eb9342d014 100644 --- a/talpid-tunnel-config-client/Cargo.toml +++ b/talpid-tunnel-config-client/Cargo.toml @@ -17,7 +17,7 @@ talpid-types = { path = "../talpid-types" } tonic = { workspace = true } tower = { workspace = true } prost = { workspace = true } -tokio = { workspace = true, features = ["macros"] } +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } classic-mceliece-rust = { version = "2.0.0", features = [ "mceliece460896f", "zeroize", @@ -30,12 +30,15 @@ libc = "0.2" workspace = true features = ["Win32_Networking_WinSock"] -[dev-dependencies] -tokio = { workspace = true, features = ["rt-multi-thread"] } - [build-dependencies] tonic-build = { workspace = true, default-features = false, features = [ "transport", "prost", ] } cbindgen = { version = "0.24.3", default-features = false } + +[target.'cfg(target_os = "ios")'.dependencies] +oslog = "0.2" + +[lib] +crate-type = ["staticlib"] diff --git a/talpid-tunnel-config-client/build.rs b/talpid-tunnel-config-client/build.rs index aeb21fe009f2..50c9bfd1df6d 100644 --- a/talpid-tunnel-config-client/build.rs +++ b/talpid-tunnel-config-client/build.rs @@ -1,3 +1,15 @@ fn main() { tonic_build::compile_protos("proto/ephemeralpeer.proto").unwrap(); + match std::env::var("TARGET").unwrap().as_str() { + "aarch64-apple-ios" | "aarch64-apple-ios-sim" => { + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_language(cbindgen::Language::C) + .generate() + .expect("failed to generate bindings") + .write_to_file("../ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h"); + } + &_ => (), + } } diff --git a/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs b/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs new file mode 100644 index 000000000000..5a428d9ee143 --- /dev/null +++ b/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs @@ -0,0 +1,169 @@ +use super::ios_tcp_connection::*; +use super::PostQuantumCancelToken; +use crate::request_ephemeral_peer; +use crate::Error; +use crate::RelayConfigService; +use libc::c_void; +use std::sync::Arc; +use std::{io, ptr}; +use talpid_types::net::wireguard::{PrivateKey, PublicKey}; +use tokio::runtime::Builder; +use tokio::sync::mpsc; +use tonic::transport::channel::Endpoint; +use tower::util::service_fn; + +/// # Safety +/// packet_tunnel and tcp_connection must be valid pointers to a packet tunnel and a TCP connection instances. +/// +pub unsafe fn run_ios_runtime( + pub_key: [u8; 32], + ephemeral_key: [u8; 32], + packet_tunnel: *const c_void, + tcp_connection: *const c_void, +) -> Result { + match unsafe { IOSRuntime::new(pub_key, ephemeral_key, packet_tunnel, tcp_connection) } { + Ok(runtime) => { + let token = runtime.cancel_token_tx.clone(); + + runtime.run(); + Ok(PostQuantumCancelToken { + context: Arc::into_raw(token) as *mut _, + }) + } + Err(err) => { + log::error!("Failed to create runtime {}", err); + Err(-1) + } + } +} + +#[derive(Clone)] +pub struct SwiftContext { + pub packet_tunnel: *const c_void, + pub tcp_connection: *const c_void, +} + +unsafe impl Send for SwiftContext {} +unsafe impl Sync for SwiftContext {} + +struct IOSRuntime { + runtime: tokio::runtime::Runtime, + pub_key: [u8; 32], + ephemeral_key: [u8; 32], + packet_tunnel: SwiftContext, + cancel_token_tx: Arc>, + cancel_token_rx: mpsc::UnboundedReceiver<()>, +} + +impl IOSRuntime { + pub unsafe fn new( + pub_key: [u8; 32], + ephemeral_key: [u8; 32], + packet_tunnel: *const libc::c_void, + tcp_connection: *const c_void, + ) -> io::Result { + let runtime = Builder::new_multi_thread() + .enable_all() + .worker_threads(2) + .build()?; + + let context = SwiftContext { + packet_tunnel, + tcp_connection, + }; + + let (tx, rx) = mpsc::unbounded_channel(); + + Ok(Self { + runtime, + pub_key, + ephemeral_key, + packet_tunnel: context, + cancel_token_tx: Arc::new(tx), + cancel_token_rx: rx, + }) + } + + pub fn run(self) { + std::thread::spawn(move || { + self.run_service_inner(); + }); + } + + pub async fn ios_tcp_client(ctx: SwiftContext) -> Result { + let endpoint = Endpoint::from_static("tcp://0.0.0.0:0"); + let conn = endpoint + .connect_with_connector(service_fn(move |_| { + let ctx = ctx.clone(); + let tcp_provider = unsafe { IosTcpProvider::new(ctx.tcp_connection) }; + async move { Ok::<_, Error>(tcp_provider) } + })) + .await + .map_err(Error::GrpcConnectError)?; + + Ok(RelayConfigService::new(conn)) + } + + fn run_service_inner(self) { + let Self { + runtime, + mut cancel_token_rx, + .. + } = self; + + let packet_tunnel_ptr = self.packet_tunnel.packet_tunnel; + runtime.block_on(async move { + let async_provider = match Self::ios_tcp_client(self.packet_tunnel).await { + Ok(async_provider) => async_provider, + Err(error) => { + log::error!("Failed to create iOS TCP client: {error}"); + unsafe { + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); + } + return; + } + }; + let ephemeral_pub_key = PrivateKey::from(self.ephemeral_key).public_key(); + + tokio::select! { + ephemeral_peer = request_ephemeral_peer( + PublicKey::from(self.pub_key), + ephemeral_pub_key, + true, + false, + async_provider, + ) => { + match ephemeral_peer { + Ok(peer) => { + match peer.psk { + Some(preshared_key) => unsafe { + let preshared_key_bytes = preshared_key.as_bytes(); + swift_post_quantum_key_ready(packet_tunnel_ptr, preshared_key_bytes.as_ptr(), self.ephemeral_key.as_ptr()); + }, + None => unsafe { + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); + } + } + }, + Err(_) => unsafe { + swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); + } + } + // match key_result { + // Ok(preshared_key) => unsafe { + // let preshared_key_bytes = preshared_key.as_bytes(); + // swift_post_quantum_key_ready(packet_tunnel_ptr, preshared_key_bytes.as_ptr(), self.ephemeral_key.as_ptr()); + // }, + // Err(_) => unsafe { + // swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); + // }, + // } + } + + _ = cancel_token_rx.recv() => { + // The swift runtime pre emptively cancelled the key exchange, nothing to do here. + } + } + }); + } +} diff --git a/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs b/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs new file mode 100644 index 000000000000..d11202b83164 --- /dev/null +++ b/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs @@ -0,0 +1,142 @@ +use libc::c_void; +use std::io; +use std::io::Result; +use std::task::Poll; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::sync::mpsc; + +extern "C" { + /// Called when there is data to send on the TCP connection. + /// The TCP connection must write data on the wire, then call the `handle_sent` function. + pub fn swift_nw_tcp_connection_send( + connection: *const libc::c_void, + data: *const libc::c_void, + data_len: usize, + sender: *const libc::c_void, + ); + + /// Called when there is data to read on the TCP connection. + /// The TCP connection must read data from the wire, then call the `handle_read` function. + pub fn swift_nw_tcp_connection_read( + connection: *const libc::c_void, + sender: *const libc::c_void, + ); + + /// Called when the preshared post quantum key is ready. + /// `raw_preshared_key` might be NULL if the key negotiation failed. + pub fn swift_post_quantum_key_ready( + raw_packet_tunnel: *const c_void, + raw_preshared_key: *const u8, + raw_ephemeral_private_key: *const u8, + ); +} + +unsafe impl Send for IosTcpProvider {} + +pub struct IosTcpProvider { + write_tx: mpsc::UnboundedSender, + write_rx: mpsc::UnboundedReceiver, + read_tx: mpsc::UnboundedSender>, + read_rx: mpsc::UnboundedReceiver>, + tcp_connection: *const c_void, + read_in_progress: bool, + write_in_progress: bool, +} + +impl IosTcpProvider { + pub unsafe fn new(tcp_connection: *const c_void) -> Self { + let (tx, rx) = mpsc::unbounded_channel(); + let (recv_tx, recv_rx) = mpsc::unbounded_channel(); + Self { + write_tx: tx, + write_rx: rx, + read_tx: recv_tx, + read_rx: recv_rx, + tcp_connection, + read_in_progress: false, + write_in_progress: false, + } + } +} + +impl AsyncWrite for IosTcpProvider { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let raw_sender = Box::into_raw(Box::new(self.write_tx.clone())); + + match self.write_rx.poll_recv(cx) { + std::task::Poll::Ready(Some(bytes_sent)) => { + self.write_in_progress = false; + Poll::Ready(Ok(bytes_sent)) + } + std::task::Poll::Ready(None) => { + self.write_in_progress = false; + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "sender dropped"))) + } + std::task::Poll::Pending => { + if self.write_in_progress { + return std::task::Poll::Pending; + } + self.write_in_progress = true; + unsafe { + swift_nw_tcp_connection_send( + self.tcp_connection, + buf.as_ptr() as _, + buf.len(), + raw_sender as _, + ); + } + std::task::Poll::Pending + } + } + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } + + fn poll_shutdown( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } +} +impl AsyncRead for IosTcpProvider { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + let raw_sender = Box::into_raw(Box::new(self.read_tx.clone())); + + match self.read_rx.poll_recv(cx) { + std::task::Poll::Ready(Some(data)) => { + buf.put_slice(&data); + self.read_in_progress = false; + Poll::Ready(Ok(())) + } + std::task::Poll::Ready(None) => { + self.read_in_progress = false; + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "sender dropped"))) + } + std::task::Poll::Pending => { + if self.read_in_progress { + return std::task::Poll::Pending; + } + self.read_in_progress = true; + unsafe { + swift_nw_tcp_connection_read(self.tcp_connection, raw_sender as _); + } + + std::task::Poll::Pending + } + } + } +} diff --git a/talpid-tunnel-config-client/src/ios_ffi/mod.rs b/talpid-tunnel-config-client/src/ios_ffi/mod.rs new file mode 100644 index 000000000000..e6d65c959d96 --- /dev/null +++ b/talpid-tunnel-config-client/src/ios_ffi/mod.rs @@ -0,0 +1,95 @@ +pub mod ios_runtime; +pub mod ios_tcp_connection; + +use crate::ios_ffi::ios_runtime::run_ios_runtime; +use libc::c_void; +use std::sync::Arc; +use tokio::sync::mpsc; + +use std::sync::Once; +static INIT_LOGGING: Once = Once::new(); + +#[repr(C)] +pub struct PostQuantumCancelToken { + // Must keep a pointer to a valid std::sync::Arc + pub context: *mut c_void, +} + +impl PostQuantumCancelToken { + /// #Safety + /// This function can only be called when the context pointer is valid. + unsafe fn cancel(&self) { + // Try to take the value, if there is a value, we can safely send the message, otherwise, assume it has been dropped and nothing happens + let send_tx: Arc> = unsafe { Arc::from_raw(self.context as _) }; + let _ = send_tx.send(()); + std::mem::forget(send_tx); + } +} + +impl Drop for PostQuantumCancelToken { + fn drop(&mut self) { + let _: Arc> = unsafe { Arc::from_raw(self.context as _) }; + } +} +unsafe impl Send for PostQuantumCancelToken {} + +#[no_mangle] +pub unsafe extern "C" fn cancel_post_quantum_key_exchange(sender: *const PostQuantumCancelToken) { + let sender = unsafe { &*sender }; + sender.cancel(); +} + +#[no_mangle] +pub unsafe extern "C" fn drop_post_quantum_key_exchange_token( + sender: *const PostQuantumCancelToken, +) { + let _sender = unsafe { std::ptr::read(sender) }; +} + +/// Callback to call when the TCP connection has written data. +#[no_mangle] +pub unsafe extern "C" fn handle_sent(bytes_sent: usize, sender: *const c_void) { + let send_tx: Box> = unsafe { Box::from_raw(sender as _) }; + _ = send_tx.send(bytes_sent); +} + +/// Callback to call when the TCP connection has received data. +#[no_mangle] +pub unsafe extern "C" fn handle_recv(data: *const u8, data_len: usize, sender: *const c_void) { + let read_tx: Box>> = unsafe { Box::from_raw(sender as _) }; + let mut bytes = vec![0u8; data_len]; + if !data.is_null() { + std::ptr::copy_nonoverlapping(data, bytes.as_mut_ptr(), data_len); + } + _ = read_tx.send(bytes.into_boxed_slice()); +} + +/// Entry point for exchanging post quantum keys on iOS. +/// The TCP connection must be created to go through the tunnel. +/// # Safety +/// This function is safe to call +#[no_mangle] +pub unsafe extern "C" fn negotiate_post_quantum_key( + public_key: *const u8, + ephemeral_key: *const u8, + packet_tunnel: *const c_void, + tcp_connection: *const c_void, + cancel_token: *mut PostQuantumCancelToken, +) -> i32 { + INIT_LOGGING.call_once(|| { + let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.TTCC") + .level_filter(log::LevelFilter::Trace) + .init(); + }); + + let pub_key_copy: [u8; 32] = unsafe { std::ptr::read(public_key as *const [u8; 32]) }; + let eph_key_copy: [u8; 32] = unsafe { std::ptr::read(ephemeral_key as *const [u8; 32]) }; + + match unsafe { run_ios_runtime(pub_key_copy, eph_key_copy, packet_tunnel, tcp_connection) } { + Ok(token) => { + unsafe { std::ptr::write(cancel_token, token) }; + 0 + } + Err(err) => err, + } +} diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs index e272e794c776..458d9aa071c4 100644 --- a/talpid-tunnel-config-client/src/lib.rs +++ b/talpid-tunnel-config-client/src/lib.rs @@ -1,10 +1,16 @@ -use std::{ - fmt, - net::{IpAddr, SocketAddr}, -}; +use proto::PostQuantumRequestV1; +use std::fmt; +#[cfg(not(target_os = "ios"))] +use std::net::IpAddr; +#[cfg(not(target_os = "ios"))] +use std::net::SocketAddr; use talpid_types::net::wireguard::{PresharedKey, PublicKey}; +#[cfg(not(target_os = "ios"))] use tokio::net::TcpSocket; -use tonic::transport::{Channel, Endpoint}; +use tonic::transport::Channel; +#[cfg(not(target_os = "ios"))] +use tonic::transport::Endpoint; +#[cfg(not(target_os = "ios"))] use tower::service_fn; use zeroize::Zeroize; @@ -16,18 +22,27 @@ mod proto { tonic::include_proto!("ephemeralpeer"); } +#[cfg(target_os = "ios")] +pub mod ios_ffi; +#[cfg(target_os = "ios")] +use proto::ephemeral_peer_client::EphemeralPeerClient; + +#[cfg(not(target_os = "ios"))] use libc::setsockopt; -#[cfg(not(target_os = "windows"))] +#[cfg(not(any(target_os = "windows", target_os = "ios")))] mod sys { pub use libc::{socklen_t, IPPROTO_TCP, TCP_MAXSEG}; - pub use std::os::fd::{AsRawFd, RawFd}; + pub use std::os::fd::RawFd; } +#[cfg(not(target_os = "ios"))] +pub use std::os::fd::AsRawFd; #[cfg(target_os = "windows")] mod sys { pub use std::os::windows::io::{AsRawSocket, RawSocket}; pub use windows_sys::Win32::Networking::WinSock::{IPPROTO_IP, IP_USER_MTU}; } +#[cfg(not(target_os = "ios"))] use sys::*; #[derive(Debug)] @@ -79,7 +94,7 @@ impl std::error::Error for Error { } } -type RelayConfigService = proto::ephemeral_peer_client::EphemeralPeerClient; +pub type RelayConfigService = proto::ephemeral_peer_client::EphemeralPeerClient; /// Port used by the tunnel config service. pub const CONFIG_SERVICE_PORT: u16 = 1337; @@ -93,6 +108,7 @@ pub const CONFIG_SERVICE_PORT: u16 = 1337; /// 2. MH + PQ on macOS has connection issues during the handshake due to PF blocking packet /// fragments for not having a port. In the longer term this might be fixed by allowing the /// handshake to work even if there is fragmentation. +#[cfg(not(target_os = "ios"))] const CONFIG_CLIENT_MTU: u16 = 576; pub struct EphemeralPeer { @@ -101,40 +117,22 @@ pub struct EphemeralPeer { /// Negotiate a short-lived peer with a PQ-safe PSK or with DAITA enabled. pub async fn request_ephemeral_peer( - service_address: IpAddr, + #[cfg(not(target_os = "ios"))] service_address: IpAddr, parent_pubkey: PublicKey, ephemeral_pubkey: PublicKey, enable_post_quantum: bool, enable_daita: bool, + #[cfg(target_os = "ios")] mut client: EphemeralPeerClient, ) -> Result { - let (pq_request, kem_secrets) = if enable_post_quantum { - let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await; - let kyber_keypair = kyber::keypair(&mut rand::thread_rng()); - - ( - Some(proto::PostQuantumRequestV1 { - kem_pubkeys: vec![ - proto::KemPubkeyV1 { - algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(), - key_data: cme_kem_pubkey.as_array().to_vec(), - }, - proto::KemPubkeyV1 { - algorithm_name: kyber::ALGORITHM_NAME.to_owned(), - key_data: kyber_keypair.public.to_vec(), - }, - ], - }), - Some((cme_kem_secret, kyber_keypair.secret)), - ) - } else { - (None, None) - }; + let (pq_request, kem_secrets) = post_quantum_secrets(enable_post_quantum).await; let daita = Some(proto::DaitaRequestV1 { activate_daita: enable_daita, }); + #[cfg(not(target_os = "ios"))] let mut client = new_client(service_address).await?; + let response = client .register_peer_v1(proto::EphemeralPeerRequestV1 { wg_parent_pubkey: parent_pubkey.as_bytes().to_vec(), @@ -192,6 +190,37 @@ pub async fn request_ephemeral_peer( Ok(EphemeralPeer { psk }) } +async fn post_quantum_secrets( + enable_post_quantum: bool, +) -> ( + Option, + Option<(classic_mceliece_rust::SecretKey<'static>, [u8; 3168])>, +) { + let (pq_request, kem_secrets) = if enable_post_quantum { + let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await; + let kyber_keypair = kyber::keypair(&mut rand::thread_rng()); + + ( + Some(proto::PostQuantumRequestV1 { + kem_pubkeys: vec![ + proto::KemPubkeyV1 { + algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(), + key_data: cme_kem_pubkey.as_array().to_vec(), + }, + proto::KemPubkeyV1 { + algorithm_name: kyber::ALGORITHM_NAME.to_owned(), + key_data: kyber_keypair.public.to_vec(), + }, + ], + }), + Some((cme_kem_secret, kyber_keypair.secret)), + ) + } else { + (None, None) + }; + (pq_request, kem_secrets) +} + /// Performs `dst = dst ^ src`. fn xor_assign(dst: &mut [u8; 32], src: &[u8; 32]) { for (dst_byte, src_byte) in dst.iter_mut().zip(src.iter()) { @@ -199,6 +228,7 @@ fn xor_assign(dst: &mut [u8; 32], src: &[u8; 32]) { } } +#[cfg(not(target_os = "ios"))] async fn new_client(addr: IpAddr) -> Result { let endpoint = Endpoint::from_static("tcp://0.0.0.0:0"); @@ -245,7 +275,7 @@ fn try_set_tcp_sock_mtu(sock: RawSocket, mtu: u16) { } } -#[cfg(not(windows))] +#[cfg(not(any(target_os = "windows", target_os = "ios")))] fn try_set_tcp_sock_mtu(dest: &IpAddr, sock: RawFd, mut mtu: u16) { const IPV4_HEADER_SIZE: u16 = 20; const IPV6_HEADER_SIZE: u16 = 40;