tor-commits
Threads by month
- ----- 2026 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 215413 discussions
[Git][tpo/applications/tor-browser-build] Pushed new tag mb-13.0a2-build1
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed new tag mb-13.0a2-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/mb-…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag tbb-13.0a2-build1
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed new tag tbb-13.0a2-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/tbb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] 4 commits: Bug 40908: Enable the --enable-gpl config flag in tor to bring in PoW functionality
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
ec97c9d3 by Richard Pospesel at 2023-08-09T12:38:17+02:00
Bug 40908: Enable the --enable-gpl config flag in tor to bring in PoW functionality
- - - - -
9f038fb2 by Nicolas Vigier at 2023-08-09T12:38:19+02:00
Bug 40916: Pull Mullvad mar files from archive.torproject.org
In order to generate incrementals, get mar files from the previous
version from archive.tpo instead of the Mullvad mirrors, since that
release is not always there but should always be on archive.tpo.
- - - - -
da34986c by Pier Angelo Vendrame at 2023-08-09T17:14:21+02:00
MB 199 (fix): Use -L to copy Mullvad Browser's Fluent files.
We need to use the -L option when copying MB's Fluent files because on
macOS we symlink ja-JP-mac to ja.
- - - - -
c70c38d5 by Richard Pospesel at 2023-08-09T17:16:07+02:00
Bug 40901, 40902: Prepare Tor+Mullvad Browser Alpha 13.0a2
- - - - -
15 changed files:
- projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
- projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
- projects/browser/allowed_addons.json
- projects/browser/config
- projects/firefox-android/config
- projects/firefox/build
- projects/firefox/config
- projects/geckoview/config
- projects/go/config
- projects/openssl/config
- projects/release/update_responses_config.yml
- projects/tor/build
- projects/tor/config
- projects/translation/config
- rbm.conf
Changes:
=====================================
projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
=====================================
@@ -1,3 +1,37 @@
+Mullvad Browser 13.0a2 - August 08 2023
+ * All Platforms
+ * Updated NoScript to 11.4.26
+ * Upated uBlock Origin to 1.51.0
+ * Updated Firefox to 115.1.0esr
+ * Bug 195: Choose which locales to translate Mullvad Browser to [mullvad-browser]
+ * Bug 216: Rebase Mullvad Browser onto 115.1.0esr [mullvad-browser]
+ * Bug 30556: Re-evaluate letterboxing dimension choices [tor-browser]
+ * Bug 33282: Increase the max width of new windows [tor-browser]
+ * Bug 40916: Update updated_responses_config.yml to pull Mullvad incrementals from archive.torproject.org [tor-browser-build]
+ * Build System
+ * All Platforms
+ * Bug 198: Enable localization for Mullvad Browser builds [mullvad-browser]
+ * Bug 199: Mullvad Browser changes required to use Mullvad Browser-specific localization strings [mullvad-browser]
+ * Bug 40615: Consider adding a readme to the fonts directory [tor-browser-build]
+ * Bug 40880: The README doesn't include some dependencies needed for building incrementals [tor-browser-build]
+ * Bug 40907: Mar-tools aren't deterministic on 13.0a1 [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Bug 40913: add boklm back to list of taggers in relevant projects [tor-browser-build]
+
+Mullvad Browser 12.5.2 - July 31 2023
+ * All Platforms
+ * Updated NoScript to 11.4.26
+ * Upated uBlock Origin to 1.51.0
+ * Updated Firefox to 102.14.0esr
+ * Bug 217: Rebase Mullvad Browser 12.5 stable on top of 102.14esr [mullvad-browser]
+ * Build System
+ * All Platforms
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+
Mullvad Browser 13.0a1 - July 20 2023
* All Platforms
* Updated NoScript to 11.4.25
=====================================
projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
=====================================
@@ -1,3 +1,67 @@
+Tor Browser 13.0a2 - August 08 2023
+ * All Platforms
+ * Updated Translations
+ * Updated NoScript to 11.4.26
+ * Updated OpenSSL to 3.0.10
+ * Updated tor to 0.4.8.3-rc
+ * Bug 41909: Rebase 13.0 alpha to 115.1.0 esr [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 115.1.0esr
+ * Bug 30556: Re-evaluate letterboxing dimension choices [tor-browser]
+ * Bug 33282: Increase the max width of new windows [tor-browser]
+ * Bug 40982: Cleanup maps in tor-circuit-display [tor-browser]
+ * Bug 40983: Move not UI-related torbutton.js code to modules [tor-browser]
+ * Bug 41844: Stop using the control port directly [tor-browser]
+ * Bug 41907: The bootstrap is interrupted without any errors if the process becomes ready when already bootstrapping [tor-browser]
+ * Bug 41922: Unify the bridge line parsers [tor-browser]
+ * Bug 41923: The path normalization results in warnings [tor-browser]
+ * Bug 41924: Small refactors for TorProcess [tor-browser]
+ * Bug 41925: Remove the torbutton startup process [tor-browser]
+ * Bug 41926: Refactor the control port client implementation [tor-browser]
+ * Bug 41964: 'emojiAnnotations' not defined in time in connection preferences [tor-browser]
+ * Android
+ * Updated GeckoView to 115.1.0esr
+ * Bug 41928: Backport Android-specific security fixes from Firefox 116 to ESR 102.14 / 115.1 - based Tor Browser [tor-browser]
+ * Build System
+ * All Platforms
+ * Updated Go to 1.20.7
+ * Bug 31588: Be smarter about vendoring for Rust projects [tor-browser-build]
+ * Bug 40855: Update toolchains for Mozilla 115 [tor-browser-build]
+ * Bug 40880: The README doesn't include some dependencies needed for building incrementals [tor-browser-build]
+ * Bug 40905: Go vendor archives ignore the nightly version override on testbuilds [tor-browser-build]
+ * Bug 40908: Enable the --enable-gpl config flag in tor to bring in PoW functionality [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Bug 40913: add boklm back to list of taggers in relevant projects [tor-browser-build]
+ * Windows + macOS + Linux
+ * Bug 40615: Consider adding a readme to the fonts directory [tor-browser-build]
+ * Bug 40907: Mar-tools aren't deterministic on 13.0a1 [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+ * Android
+ * Bug 40867: Create a RBM project for the unified Android repository [tor-browser-build]
+ * Bug 41899: Use LLD for Android [tor-browser]
+
+Tor Browser 12.5.2 - July 31 2023
+ * All Platforms
+ * Updated Translations
+ * Updated NoScript to 11.4.26
+ * Bug 41908: Rebase stable 12.5 to 102.14esr [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 102.14.0esr
+ * Windows
+ * Bug 41761: xul.dll win crash tor-browser 12.5.1 (based on Mozilla Firefox 102.13.0esr) (64-Bit) [tor-browser]
+ * Android
+ * Updated GeckoView to 102.14.0esr
+ * Bug 41928: Backport Android-specific security fixes from Firefox 116 to ESR 102.14 / 115.1 - based Tor Browser [tor-browser]
+ * Build System
+ * All Platforms
+ * Updated Go to 1.20.6
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+
Tor Browser 13.0a1 - July 20 2023
* All Platforms
* Updated Translations
=====================================
projects/browser/allowed_addons.json
=====================================
@@ -17,7 +17,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/34/9734/13299734/13299734.pn…"
}
],
- "average_daily_users": 962293,
+ "average_daily_users": 963152,
"categories": {
"android": [
"experimental",
@@ -221,10 +221,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.5599,
- "bayesian_average": 4.558742718876036,
- "count": 5081,
- "text_count": 1597
+ "average": 4.5592,
+ "bayesian_average": 4.55804261202379,
+ "count": 5093,
+ "text_count": 1604
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/reviews/",
"requires_payment": false,
@@ -321,7 +321,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/versions/",
- "weekly_downloads": 23021
+ "weekly_downloads": 24659
},
"notes": null
},
@@ -337,7 +337,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/56/7656/6937656/6937656.png?…"
}
],
- "average_daily_users": 249664,
+ "average_daily_users": 249415,
"categories": {
"android": [
"security-privacy"
@@ -553,9 +553,9 @@
"category": "recommended"
},
"ratings": {
- "average": 4.8163,
- "bayesian_average": 4.811654069453744,
- "count": 1350,
+ "average": 4.816,
+ "bayesian_average": 4.811352368325867,
+ "count": 1353,
"text_count": 238
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/reviews/",
@@ -641,7 +641,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/versions/",
- "weekly_downloads": 3297
+ "weekly_downloads": 3670
},
"notes": null
},
@@ -657,7 +657,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/73/4073/5474073/5474073.png?…"
}
],
- "average_daily_users": 1077131,
+ "average_daily_users": 1076698,
"categories": {
"android": [
"security-privacy"
@@ -1180,9 +1180,9 @@
"category": "recommended"
},
"ratings": {
- "average": 4.8004,
- "bayesian_average": 4.797633164122742,
- "count": 2259,
+ "average": 4.8009,
+ "bayesian_average": 4.798135100629341,
+ "count": 2265,
"text_count": 431
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/reviews/",
@@ -1207,7 +1207,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/versions/",
- "weekly_downloads": 17147
+ "weekly_downloads": 18718
},
"notes": null
},
@@ -1223,7 +1223,7 @@
"picture_url": null
}
],
- "average_daily_users": 6213889,
+ "average_daily_users": 6229783,
"categories": {
"android": [
"security-privacy"
@@ -1388,7 +1388,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2023-07-25T13:50:33Z",
+ "last_updated": "2023-08-07T17:15:41Z",
"name": {
"ar": "uBlock Origin",
"bg": "uBlock Origin",
@@ -1533,10 +1533,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.782,
- "bayesian_average": 4.781604256114891,
- "count": 15731,
- "text_count": 4091
+ "average": 4.7828,
+ "bayesian_average": 4.782404807048353,
+ "count": 15798,
+ "text_count": 4101
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/reviews/",
"requires_payment": false,
@@ -1598,7 +1598,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/versions/",
- "weekly_downloads": 129273
+ "weekly_downloads": 143905
},
"notes": null
},
@@ -1614,7 +1614,7 @@
"picture_url": null
}
],
- "average_daily_users": 167699,
+ "average_daily_users": 167376,
"categories": {
"android": [
"photos-media"
@@ -1713,10 +1713,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4916,
- "bayesian_average": 4.486471885365025,
- "count": 1129,
- "text_count": 422
+ "average": 4.4929,
+ "bayesian_average": 4.487789225993283,
+ "count": 1132,
+ "text_count": 423
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/re…",
"requires_payment": false,
@@ -1738,7 +1738,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/ve…",
- "weekly_downloads": 367
+ "weekly_downloads": 328
},
"notes": null
},
@@ -1754,7 +1754,7 @@
"picture_url": null
}
],
- "average_daily_users": 85522,
+ "average_daily_users": 85334,
"categories": {
"android": [
"experimental",
@@ -1892,7 +1892,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/versions/",
- "weekly_downloads": 1659
+ "weekly_downloads": 1565
},
"notes": null
},
@@ -1908,7 +1908,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/64/9064/12929064/12929064.pn…"
}
],
- "average_daily_users": 257323,
+ "average_daily_users": 257984,
"categories": {
"android": [
"photos-media",
@@ -2127,10 +2127,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.652,
- "bayesian_average": 4.647356910324775,
- "count": 1296,
- "text_count": 249
+ "average": 4.6539,
+ "bayesian_average": 4.64926734511088,
+ "count": 1303,
+ "text_count": 250
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/reviews/",
"requires_payment": false,
@@ -2151,7 +2151,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/versions/",
- "weekly_downloads": 4065
+ "weekly_downloads": 4027
},
"notes": null
},
@@ -2174,7 +2174,7 @@
"picture_url": null
}
],
- "average_daily_users": 111166,
+ "average_daily_users": 111124,
"categories": {
"android": [
"other"
@@ -2457,10 +2457,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4202,
- "bayesian_average": 4.41559222474328,
- "count": 1228,
- "text_count": 332
+ "average": 4.4123,
+ "bayesian_average": 4.407708861807205,
+ "count": 1232,
+ "text_count": 334
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/reviews/",
"requires_payment": false,
@@ -2480,7 +2480,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/versions/",
- "weekly_downloads": 33
+ "weekly_downloads": 30
},
"notes": null
},
@@ -2496,7 +2496,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/43/0143/143/143.png?modified…"
}
],
- "average_daily_users": 301822,
+ "average_daily_users": 301669,
"categories": {
"android": [
"performance",
@@ -2686,10 +2686,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.404,
- "bayesian_average": 4.401301959464707,
- "count": 2089,
- "text_count": 810
+ "average": 4.4047,
+ "bayesian_average": 4.402001757843669,
+ "count": 2093,
+ "text_count": 811
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/reviews/",
"requires_payment": false,
@@ -2733,7 +2733,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/versions/",
- "weekly_downloads": 7807
+ "weekly_downloads": 7783
},
"notes": null
},
@@ -2749,7 +2749,7 @@
"picture_url": null
}
],
- "average_daily_users": 149721,
+ "average_daily_users": 150191,
"categories": {
"android": [
"performance",
@@ -2864,9 +2864,9 @@
"category": "recommended"
},
"ratings": {
- "average": 3.9005,
- "bayesian_average": 3.89624887816371,
- "count": 1146,
+ "average": 3.9015,
+ "bayesian_average": 3.8972613162211953,
+ "count": 1147,
"text_count": 406
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/revi…",
@@ -2886,7 +2886,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/vers…",
- "weekly_downloads": 1670
+ "weekly_downloads": 1838
},
"notes": null
}
=====================================
projects/browser/config
=====================================
@@ -104,9 +104,9 @@ input_files:
- URL: https://addons.mozilla.org/firefox/downloads/file/4141345/noscript-11.4.26.…
name: noscript
sha256sum: 283db0eaebbd2888c1a852f5acabaa8e0225ff1eb1a97a25bceaedfd14d9f44c
- - URL: https://addons.mozilla.org/firefox/downloads/file/4121906/ublock_origin-1.5…
+ - URL: https://addons.mozilla.org/firefox/downloads/file/4141256/ublock_origin-1.5…
name: ublock-origin
- sha256sum: 10618003e70b528c3f17996e373146d39e6b15f777ac4ca1f214da2ffdb7a5b3
+ sha256sum: 8b73468bc233a11dd2895219466381783d19123857dd0b6fd16a01820fca4834
enable: '[% c("var/mullvad-browser") %]'
- URL: https://github.com/mullvad/browser-extension/releases/download/v0.8.3-firef…
name: mullvad-extension
=====================================
projects/firefox-android/config
=====================================
@@ -16,7 +16,7 @@ container:
var:
fenix_version: 115.2.0
browser_branch: 13.0-1
- browser_build: 1
+ browser_build: 2
platform_version: 115.0
variant: Beta
# This should be updated when the list of gradle dependencies is changed.
=====================================
projects/firefox/build
=====================================
@@ -159,7 +159,7 @@ mkdir "$HOME/.mozbuild"
pushd "$rootdir/translation-mullvad-browser"
ln -s ja ja-JP-mac
for lang in $supported_locales; do
- cp -r $lang "$l10ncentral/"
+ cp -Lr $lang "$l10ncentral/"
done
popd
[% END -%]
=====================================
projects/firefox/config
=====================================
@@ -14,11 +14,11 @@ container:
use_container: 1
var:
- firefox_platform_version: 115.0.2
+ firefox_platform_version: 115.1.0
firefox_version: '[% c("var/firefox_platform_version") %]esr'
browser_series: '13.0'
browser_branch: '[% c("var/browser_series") %]-1'
- browser_build: 3
+ browser_build: 4
branding_directory_prefix: 'tb'
copyright_year: '[% exec("git show -s --format=%ci").remove("-.*") %]'
nightly_updates_publish_dir: '[% c("var/nightly_updates_publish_dir_prefix") %]nightly-[% c("var/osname") %]'
@@ -85,7 +85,7 @@ targets:
git_url: https://gitlab.torproject.org/tpo/applications/mullvad-browser.git
var:
branding_directory_prefix: 'mb'
- browser_build: 2
+ browser_build: 3
gitlab_project: https://gitlab.torproject.org/tpo/applications/mullvad-browser
linux-x86_64:
=====================================
projects/geckoview/config
=====================================
@@ -16,7 +16,7 @@ container:
var:
geckoview_version: 115.1.0esr
browser_branch: 13.0-1
- browser_build: 2
+ browser_build: 4
copyright_year: '[% exec("git show -s --format=%ci").remove("-.*") %]'
gitlab_project: https://gitlab.torproject.org/tpo/applications/tor-browser
git_commit: '[% exec("git rev-parse HEAD") %]'
=====================================
projects/go/config
=====================================
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 1.20.6
+version: 1.20.7
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
container:
use_container: 1
@@ -119,7 +119,7 @@ input_files:
enable: '[% ! c("var/linux") %]'
- URL: 'https://golang.org/dl/go[% c("version") %].src.tar.gz'
name: go
- sha256sum: 62ee5bc6fb55b8bae8f705e0cb8df86d6453626b4ecf93279e2867092e0b7f70
+ sha256sum: 2c5ee9c9ec1e733b0dbbc2bdfed3f62306e51d8172bf38f4f4e542b27520f597
- project: go-bootstrap
name: go-bootstrap
target_replace:
=====================================
projects/openssl/config
=====================================
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 3.0.9
+version: 3.0.10
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
container:
use_container: 1
@@ -33,4 +33,4 @@ input_files:
- name: '[% c("var/compiler") %]'
project: '[% c("var/compiler") %]'
- URL: 'https://www.openssl.org/source/openssl-[% c("version") %].tar.gz'
- sha256sum: eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90
+ sha256sum: 1761d4f5b13a1028b9b6f3d4b8e17feb0cedc9370f6afe61d7193d2cdce83323
=====================================
projects/release/update_responses_config.yml
=====================================
@@ -10,13 +10,12 @@ appname_bundle_win64: '[% c("var/projectname") %]-install-win64'
releases_dir: [% path(c('output_dir')) %][% IF ! c("var/nightly") %]/signed[% END %]
download:
gpg_keyring: ../../keyring/torbrowser.gpg
+ archive_url: 'https://archive.torproject.org/tor-package-archive/[% c("var/projectname") %]'
[% IF c("var/tor-browser") -%]
- archive_url: 'https://archive.torproject.org/tor-package-archive/torbrowser'
bundles_url: 'https://dist.torproject.org/torbrowser'
mars_url: 'https://cdn.torproject.org/aus1/torbrowser'
[% END -%]
[% IF c("var/mullvad-browser") -%]
- archive_url: 'https://cdn.mullvad.net/browser'
bundles_url: 'https://cdn.mullvad.net/browser'
mars_url: 'https://cdn.mullvad.net/browser'
[% END -%]
=====================================
projects/tor/build
=====================================
@@ -73,7 +73,7 @@ find -type f -print0 | xargs -0 [% c("touch") %]
[% IF c("var/windows") || c("var/android") %]--with-zlib-dir="$zlibdir"[% END %] \
[% IF c("var/macos") %]--enable-static-openssl[% END %] \
[% IF c("var/windows") %]--enable-static-libevent --enable-static-openssl --enable-static-zlib[% END %] \
- --prefix="$distdir" [% c("var/configure_opt") %]
+ --enable-gpl --prefix="$distdir" [% c("var/configure_opt") %]
[% IF c("var/macos") -%]
export LD_PRELOAD=[% c("var/faketime_path") %]
export FAKETIME="[% USE date; GET date.format(c('timestamp'), format = '%Y-%m-%d %H:%M:%S') %]"
=====================================
projects/tor/config
=====================================
@@ -1,6 +1,6 @@
# vim: filetype=yaml sw=2
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
-version: 0.4.8.2-alpha
+version: 0.4.8.3-rc
git_hash: 'tor-[% c("version") %]'
git_url: https://gitlab.torproject.org/tpo/core/tor.git
git_submodule: 1
=====================================
projects/translation/config
=====================================
@@ -12,19 +12,19 @@ compress_tar: 'gz'
steps:
base-browser:
base-browser: '[% INCLUDE build %]'
- git_hash: 060d9d04b3a4c3e0f09f3f5fa8ad3ab12e2f4e80
+ git_hash: f940956c425c199f622f53fef9a735fc303b11f1
targets:
nightly:
git_hash: 'base-browser'
base-browser-fluent:
base-browser-fluent: '[% INCLUDE build %]'
- git_hash: 6f64004400616a5956d1823a0e8176b60d212090
+ git_hash: 72c0d47f55994b6d68e0239d9edd02d7ae7708ab
targets:
nightly:
git_hash: 'basebrowser-newidentityftl'
tor-browser:
tor-browser: '[% INCLUDE build %]'
- git_hash: d8d2b8f3200aa309f44f2fa80625ff8e6a25bb78
+ git_hash: 2b885b2a6dfeaa046678133755639d6e26485754
targets:
nightly:
git_hash: 'tor-browser'
=====================================
rbm.conf
=====================================
@@ -81,12 +81,10 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '13.0a1'
+ torbrowser_version: '13.0a2'
torbrowser_build: 'build1'
torbrowser_incremental_from:
- - 12.5a5
- - 12.5a6
- - 12.5a7
+ - '13.0a1'
updater_enabled: 1
build_mar: 1
mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]'
@@ -302,9 +300,6 @@ targets:
- tr
- zh-CN
- zh-TW
- torbrowser_build: 'build2'
- torbrowser_incremental_from:
- - 12.5a7
torbrowser-testbuild:
- testbuild
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-115.1.0esr-13.0-1-build4
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed new tag tor-browser-115.1.0esr-13.0-1-build4 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] MB 198: Enable additional languages on Mullvad Browser.
by Pier Angelo Vendrame (@pierov) 08 Aug '23
by Pier Angelo Vendrame (@pierov) 08 Aug '23
08 Aug '23
Pier Angelo Vendrame pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
a93cb13f by Pier Angelo Vendrame at 2023-08-08T18:09:08+02:00
MB 198: Enable additional languages on Mullvad Browser.
Also MB 199: Inject Mullvad Browser-specific localized strings in
projects/firefox.
- - - - -
5 changed files:
- .gitlab/issue_templates/Release Prep - Mullvad Browser Alpha.md
- projects/firefox/build
- projects/firefox/config
- projects/translation/config
- rbm.conf
Changes:
=====================================
.gitlab/issue_templates/Release Prep - Mullvad Browser Alpha.md
=====================================
@@ -47,6 +47,7 @@ Mullvad Browser Alpha (and Nightly) are on the `main` branch
- [ ] run `make list_translation_updates-alpha` to get updated hashes
- [ ] `steps/base-browser/git_hash` : update with `HEAD` commit of project's `base-browser` branch
- [ ] `steps/base-browser-fluent/git_hash` : update with `HEAD` commit of project's `basebrowser-newidentityftl` branch
+ - [ ] `steps/mullvad-browser/git_hash` : update with `HEAD` commit of project's `mullvad-browser` branch
- [ ] Update common build configs
- [ ] Check for NoScript updates here : https://addons.mozilla.org/en-US/firefox/addon/noscript
- [ ] ***(Optional)*** If new version available, update `noscript` section of `input_files` in `projects/browser/config`
=====================================
projects/firefox/build
=====================================
@@ -154,6 +154,14 @@ mkdir "$HOME/.mozbuild"
echo "% locale torbutton $lang %locale/$lang/" >> "$torbutton_jar"
echo " locale/$lang (chrome/locale/$lang/*)" >> "$torbutton_jar"
done
+ [% ELSIF c("var/mullvad-browser") -%]
+ tar -C "$rootdir" -xf "$rootdir/[% c('input_files_by_name/translation-mullvad-browser') %]"
+ pushd "$rootdir/translation-mullvad-browser"
+ ln -s ja ja-JP-mac
+ for lang in $supported_locales; do
+ cp -r $lang "$l10ncentral/"
+ done
+ popd
[% END -%]
[% ELSE -%]
supported_locales=""
=====================================
projects/firefox/config
=====================================
@@ -199,6 +199,10 @@ input_files:
name: translation-tor-browser
pkg_type: tor-browser
enable: '[% c("var/tor-browser") && c("var/has_l10n") %]'
+ - project: translation
+ name: translation-mullvad-browser
+ pkg_type: mullvad-browser
+ enable: '[% c("var/mullvad-browser") && c("var/has_l10n") %]'
- filename: marsigner.der
enable: '[% c("var/override_updater_url") %]'
- filename: namecoin-torbutton.patch
=====================================
projects/translation/config
=====================================
@@ -28,6 +28,12 @@ steps:
targets:
nightly:
git_hash: 'tor-browser'
+ mullvad-browser:
+ mullvad-browser: '[% INCLUDE build %]'
+ git_hash: 1f736c5ae157f27df975c18bf3f2fa5f4bb5e33a
+ targets:
+ nightly:
+ git_hash: 'mullvad-browser'
fenix:
fenix: '[% INCLUDE build %]'
# We need to bump the commit before releasing but just pointing to a branch
@@ -40,7 +46,7 @@ steps:
list_updates:
list_updates: |
[%
- FOREACH component = [ 'base-browser', 'base-browser-fluent', 'tor-browser', 'fenix' ];
+ FOREACH component = [ 'base-browser', 'base-browser-fluent', 'tor-browser', 'mullvad-browser', 'fenix' ];
branch = pc(project, 'git_hash', { step => component, target => [ 'nightly' ] });
commit_hash = exec('git rev-parse ' _ branch, { git_hash => branch });
IF commit_hash == pc(project, "git_hash", { step => component });
=====================================
rbm.conf
=====================================
@@ -280,7 +280,28 @@ targets:
ProjectName: MullvadBrowser
exe_name: mullvadbrowser
mar_channel_id: '[% c("var/projectname") %]-mullvad-[% c("var/channel") %]'
- locales: []
+ locales:
+ - ar
+ - da
+ - de
+ - es-ES
+ - fa
+ - fi
+ - fr
+ - it
+ - '[% c("var/locale_ja") %]'
+ - ko
+ - my
+ - nb-NO
+ - nl
+ - pl
+ - pt-BR
+ - ru
+ - sv-SE
+ - th
+ - tr
+ - zh-CN
+ - zh-TW
torbrowser_build: 'build2'
torbrowser_incremental_from:
- 12.5a7
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-115.1.0esr-13.0-1] 2 commits: amend! Bug 32308: use direct browser sizing for letterboxing.
by ma1 (@ma1) 08 Aug '23
by ma1 (@ma1) 08 Aug '23
08 Aug '23
ma1 pushed to branch base-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
dc2274d0 by hackademix at 2023-08-08T09:18:01+02:00
amend! Bug 32308: use direct browser sizing for letterboxing.
Bug 32308: use direct browser sizing for letterboxing.
Bug 30556: align letterboxing with 200x100 new win width stepping
- - - - -
9ac98dab by hackademix at 2023-08-08T09:18:02+02:00
fixup! Firefox preference overrides.
Bug 33282: Redefine the dimensions of new RFP windows
- - - - -
2 changed files:
- browser/app/profile/001-base-profile.js
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -291,6 +291,9 @@ pref("dom.use_components_shim", false);
pref("privacy.resistFingerprinting.letterboxing", true);
// tor-browser#41695: how many warnings we show if user closes them without restoring the window size
pref("privacy.resistFingerprinting.resizeWarnings", 3);
+// tor-browser#33282: new windows start at 1400x900 when there's enough screen space, otherwise down by 200x100 blocks
+pref("privacy.window.maxInnerWidth", 1400);
+pref("privacy.window.maxInnerHeight", 900);
// Enforce Network Information API as disabled
pref("dom.netinfo.enabled", false);
pref("network.http.referer.defaultPolicy", 2); // Bug 32948: Make referer behavior consistent regardless of private browing mode status
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -475,14 +475,14 @@ class _RFPHelper {
/**
* Given a width or height, rounds it with the proper stepping.
*/
- steppedSize(aDimension) {
+ steppedSize(aDimension, isWidth = false) {
let stepping;
if (aDimension <= 50) {
return 0;
} else if (aDimension <= 500) {
stepping = 50;
} else if (aDimension <= 1600) {
- stepping = 100;
+ stepping = isWidth ? 200 : 100;
} else {
stepping = 200;
}
@@ -569,7 +569,7 @@ class _RFPHelper {
// If the set is empty, we will round the content with the default
// stepping size.
if (!this._letterboxingDimensions.length) {
- result = r(this.steppedSize(aWidth), this.steppedSize(aHeight));
+ result = r(this.steppedSize(aWidth, true), this.steppedSize(aHeight));
log(
`${logPrefix} roundDimensions(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}`
);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/65183e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/65183e…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] fixup! Firefox preference overrides.
by ma1 (@ma1) 08 Aug '23
by ma1 (@ma1) 08 Aug '23
08 Aug '23
ma1 pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
e0e763c0 by hackademix at 2023-08-08T09:11:30+02:00
fixup! Firefox preference overrides.
Bug 33282: Redefine the dimensions of new RFP windows
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -291,6 +291,9 @@ pref("dom.use_components_shim", false);
pref("privacy.resistFingerprinting.letterboxing", true);
// tor-browser#41695: how many warnings we show if user closes them without restoring the window size
pref("privacy.resistFingerprinting.resizeWarnings", 3);
+// tor-browser#33282: new windows start at 1400x900 when there's enough screen space, otherwise down by 200x100 blocks
+pref("privacy.window.maxInnerWidth", 1400);
+pref("privacy.window.maxInnerHeight", 900);
// Enforce Network Information API as disabled
pref("dom.netinfo.enabled", false);
pref("network.http.referer.defaultPolicy", 2); // Bug 32948: Make referer behavior consistent regardless of private browing mode status
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e0e763c…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e0e763c…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] amend! Bug 32308: use direct browser sizing for letterboxing.
by ma1 (@ma1) 08 Aug '23
by ma1 (@ma1) 08 Aug '23
08 Aug '23
ma1 pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
63eecb92 by hackademix at 2023-08-08T08:08:43+02:00
amend! Bug 32308: use direct browser sizing for letterboxing.
Bug 32308: use direct browser sizing for letterboxing.
Bug 30556: align letterboxing with 200x100 new win width stepping
- - - - -
1 changed file:
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
Changes:
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -475,14 +475,14 @@ class _RFPHelper {
/**
* Given a width or height, rounds it with the proper stepping.
*/
- steppedSize(aDimension) {
+ steppedSize(aDimension, isWidth = false) {
let stepping;
if (aDimension <= 50) {
return 0;
} else if (aDimension <= 500) {
stepping = 50;
} else if (aDimension <= 1600) {
- stepping = 100;
+ stepping = isWidth ? 200 : 100;
} else {
stepping = 200;
}
@@ -569,7 +569,7 @@ class _RFPHelper {
// If the set is empty, we will round the content with the default
// stepping size.
if (!this._letterboxingDimensions.length) {
- result = r(this.steppedSize(aWidth), this.steppedSize(aHeight));
+ result = r(this.steppedSize(aWidth, true), this.steppedSize(aHeight));
log(
`${logPrefix} roundDimensions(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}`
);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/63eecb9…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/63eecb9…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-115.1.0esr-13.0-1-build3
by richard (@richard) 07 Aug '23
by richard (@richard) 07 Aug '23
07 Aug '23
richard pushed new tag tor-browser-115.1.0esr-13.0-1-build3 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] 8 commits: fixup! Bug 40933: Add tor-launcher functionality
by richard (@richard) 07 Aug '23
by richard (@richard) 07 Aug '23
07 Aug '23
richard pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
d339153b by Pier Angelo Vendrame at 2023-08-07T18:36:05+02:00
fixup! Bug 40933: Add tor-launcher functionality
Use the new functions whenever possible, adjust some property names and
other minor fixes.
- - - - -
e8d3c0b8 by Pier Angelo Vendrame at 2023-08-07T18:36:14+02:00
fixup! Bug 40597: Implement TorSettings module
Changes needed for the new control port implementation.
Also, moved to ES modules and done some refactors on Moat.
- - - - -
c1b05a65 by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Changes for the new control port implementation and following
ESMification.
- - - - -
d23cb300 by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 30237: Add v3 onion services client authentication prompt
Changes for the new control port implementation.
- - - - -
c801d620 by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 7494: Create local home page for TBB.
Use the TorProvider in TorCheckService
- - - - -
fa105e4f by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.
Remove TorMonitorService and TorProtocolService references.
- - - - -
cd92f30e by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 41668: Tweaks to the Base Browser updater for Tor Browser
Removed TorMonitorService reference
- - - - -
384dff97 by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 40933: Add tor-launcher functionality
Remove the final references to TorMonitorService and TorProtocolService.
- - - - -
24 changed files:
- browser/components/abouttor/TorCheckService.sys.mjs
- browser/components/onionservices/content/authPrompt.js
- browser/components/onionservices/content/savedKeysDialog.js
- browser/components/torpreferences/content/builtinBridgeDialog.jsm
- browser/components/torpreferences/content/connectionPane.js
- browser/components/torpreferences/content/connectionSettingsDialog.jsm
- browser/components/torpreferences/content/provideBridgeDialog.jsm
- browser/components/torpreferences/content/requestBridgeDialog.jsm
- browser/components/torpreferences/content/torLogDialog.jsm
- browser/modules/BridgeDB.jsm → browser/modules/BridgeDB.sys.mjs
- browser/modules/Moat.jsm → browser/modules/Moat.sys.mjs
- browser/modules/TorConnect.jsm → browser/modules/TorConnect.sys.mjs
- browser/modules/TorSettings.jsm → browser/modules/TorSettings.sys.mjs
- browser/modules/moz.build
- toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
- − toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorParsers.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs → toolkit/components/tor-launcher/TorProvider.sys.mjs
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/components/tor-launcher/TorStartupService.sys.mjs
- toolkit/components/tor-launcher/moz.build
- toolkit/mozapps/update/UpdateService.sys.mjs
Changes:
=====================================
browser/components/abouttor/TorCheckService.sys.mjs
=====================================
@@ -11,14 +11,9 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ConsoleAPI: "resource://gre/modules/Console.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorProtocolService",
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
export const TorCheckService = {
kCheckNotInitiated: 0, // Possible values for status.
kCheckSuccessful: 1,
@@ -109,7 +104,7 @@ export const TorCheckService = {
let listeners;
try {
- listeners = await lazy.TorProtocolService.getSocksListeners();
+ listeners = await lazy.TorProviderBuilder.build().getSocksListeners();
} catch (e) {
this._logger.error("Failed to get the SOCKS listerner addresses.", e);
return false;
=====================================
browser/components/onionservices/content/authPrompt.js
=====================================
@@ -4,10 +4,13 @@
/* globals gBrowser, PopupNotifications, Services, XPCOMUtils */
+ChromeUtils.defineESModuleGetters(this, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
+
XPCOMUtils.defineLazyModuleGetters(this, {
OnionAuthUtil: "chrome://browser/content/onionservices/authUtil.jsm",
CommonUtils: "resource://services-common/utils.js",
- TorProtocolService: "resource://gre/modules/TorProtocolService.jsm",
TorStrings: "resource:///modules/TorStrings.jsm",
});
@@ -203,7 +206,8 @@ const OnionAuthPrompt = (function () {
let checkboxElem = this._getCheckboxElement();
let isPermanent = checkboxElem && checkboxElem.checked;
- TorProtocolService.onionAuthAdd(onionServiceId, base64key, isPermanent)
+ TorProviderBuilder.build()
+ .onionAuthAdd(onionServiceId, base64key, isPermanent)
.then(aResponse => {
// Success! Reload the page.
this._browser.sendMessageToActor(
=====================================
browser/components/onionservices/content/savedKeysDialog.js
=====================================
@@ -8,11 +8,9 @@ ChromeUtils.defineModuleGetter(
"resource:///modules/TorStrings.jsm"
);
-ChromeUtils.defineModuleGetter(
- this,
- "TorProtocolService",
- "resource://gre/modules/TorProtocolService.jsm"
-);
+ChromeUtils.defineESModuleGetters(this, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
var gOnionServicesSavedKeysDialog = {
selector: {
@@ -54,6 +52,7 @@ var gOnionServicesSavedKeysDialog = {
await this._deleteOneKey(indexesToDelete[i]);
}
} catch (e) {
+ console.error("Removing a saved key failed", e);
if (e.torMessage) {
this._showError(e.torMessage);
} else {
@@ -125,22 +124,16 @@ var gOnionServicesSavedKeysDialog = {
try {
this._tree.view = this;
- const keyInfoList = await TorProtocolService.onionAuthViewKeys();
+ const keyInfoList = await TorProviderBuilder.build().onionAuthViewKeys();
if (keyInfoList) {
// Filter out temporary keys.
- this._keyInfoList = keyInfoList.filter(aKeyInfo => {
- if (!aKeyInfo.Flags) {
- return false;
- }
-
- const flags = aKeyInfo.Flags.split(",");
- return flags.includes("Permanent");
- });
-
+ this._keyInfoList = keyInfoList.filter(aKeyInfo =>
+ aKeyInfo.flags?.includes("Permanent")
+ );
// Sort by the .onion address.
this._keyInfoList.sort((aObj1, aObj2) => {
- const hsAddr1 = aObj1.hsAddress.toLowerCase();
- const hsAddr2 = aObj2.hsAddress.toLowerCase();
+ const hsAddr1 = aObj1.address.toLowerCase();
+ const hsAddr2 = aObj2.address.toLowerCase();
if (hsAddr1 < hsAddr2) {
return -1;
}
@@ -164,7 +157,7 @@ var gOnionServicesSavedKeysDialog = {
// This method may throw; callers should catch errors.
async _deleteOneKey(aIndex) {
const keyInfoObj = this._keyInfoList[aIndex];
- await TorProtocolService.onionAuthRemove(keyInfoObj.hsAddress);
+ await TorProviderBuilder.build().onionAuthRemove(keyInfoObj.address);
this._tree.view.selection.clearRange(aIndex, aIndex);
this._keyInfoList.splice(aIndex, 1);
this._tree.rowCountChanged(aIndex + 1, -1);
@@ -193,26 +186,20 @@ var gOnionServicesSavedKeysDialog = {
// XUL tree widget view implementation.
get rowCount() {
- return this._keyInfoList ? this._keyInfoList.length : 0;
+ return this._keyInfoList?.length ?? 0;
},
getCellText(aRow, aCol) {
- let val = "";
if (this._keyInfoList && aRow < this._keyInfoList.length) {
const keyInfo = this._keyInfoList[aRow];
if (aCol.id.endsWith("-siteCol")) {
- val = keyInfo.hsAddress;
+ return keyInfo.address;
} else if (aCol.id.endsWith("-keyCol")) {
- val = keyInfo.typeAndKey;
- // Omit keyType because it is always "x25519".
- const idx = val.indexOf(":");
- if (idx > 0) {
- val = val.substring(idx + 1);
- }
+ // keyType is always "x25519", so do not show it.
+ return keyInfo.keyBlob;
}
}
-
- return val;
+ return "";
},
isSeparator(index) {
=====================================
browser/components/torpreferences/content/builtinBridgeDialog.jsm
=====================================
@@ -7,10 +7,10 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
const { TorSettings, TorBridgeSource, TorBuiltinBridgeTypes } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class BuiltinBridgeDialog {
=====================================
browser/components/torpreferences/content/connectionPane.js
=====================================
@@ -12,20 +12,17 @@ const { setTimeout, clearTimeout } = ChromeUtils.import(
);
const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
const { TorParsers } = ChromeUtils.importESModule(
"resource://gre/modules/TorParsers.sys.mjs"
);
-const { TorProtocolService } = ChromeUtils.importESModule(
- "resource://gre/modules/TorProtocolService.sys.mjs"
-);
-const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
+const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
);
const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } =
- ChromeUtils.import("resource:///modules/TorConnect.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorConnect.sys.mjs");
const { TorLogDialog } = ChromeUtils.import(
"chrome://browser/content/torpreferences/torLogDialog.jsm"
@@ -51,7 +48,9 @@ const { ProvideBridgeDialog } = ChromeUtils.import(
"chrome://browser/content/torpreferences/provideBridgeDialog.jsm"
);
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
+const { MoatRPC } = ChromeUtils.importESModule(
+ "resource:///modules/Moat.sys.mjs"
+);
const { QRCode } = ChromeUtils.import("resource://gre/modules/QRCode.jsm");
@@ -156,7 +155,7 @@ const gConnectionPane = (function () {
_populateXUL() {
// saves tor settings to disk when navigate away from about:preferences
window.addEventListener("blur", val => {
- TorProtocolService.flushSettings();
+ TorProviderBuilder.build().flushSettings();
});
document
@@ -751,7 +750,7 @@ const gConnectionPane = (function () {
// TODO: We could make sure TorSettings is in sync by monitoring also
// changes of settings. At that point, we could query it, instead of
// doing a query over the control port.
- const bridge = TorMonitorService.currentBridge;
+ const bridge = TorProviderBuilder.build().currentBridge;
if (bridge?.fingerprint !== this._currentBridgeId) {
this._currentBridgeId = bridge?.fingerprint ?? null;
this._updateConnectedBridges();
@@ -850,7 +849,7 @@ const gConnectionPane = (function () {
});
Services.obs.addObserver(this, TorConnectTopics.StateChange);
- Services.obs.addObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
Services.obs.addObserver(this, "intl:app-locales-changed");
},
@@ -875,7 +874,7 @@ const gConnectionPane = (function () {
// unregister our observer topics
Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged);
Services.obs.removeObserver(this, TorConnectTopics.StateChange);
- Services.obs.removeObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
Services.obs.removeObserver(this, "intl:app-locales-changed");
},
@@ -907,7 +906,7 @@ const gConnectionPane = (function () {
this.onStateChange();
break;
}
- case TorMonitorTopics.BridgeChanged: {
+ case TorProviderTopics.BridgeChanged: {
if (data?.fingerprint !== this._currentBridgeId) {
this._checkConnectedBridge();
}
=====================================
browser/components/torpreferences/content/connectionSettingsDialog.jsm
=====================================
@@ -2,8 +2,8 @@
var EXPORTED_SYMBOLS = ["ConnectionSettingsDialog"];
-const { TorSettings, TorProxyType } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
+const { TorSettings, TorProxyType } = ChromeUtils.importESModule(
+ "resource:///modules/TorSettings.sys.mjs"
);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
=====================================
browser/components/torpreferences/content/provideBridgeDialog.jsm
=====================================
@@ -6,12 +6,12 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
+const { TorSettings, TorBridgeSource } = ChromeUtils.importESModule(
+ "resource:///modules/TorSettings.sys.mjs"
);
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class ProvideBridgeDialog {
=====================================
browser/components/torpreferences/content/requestBridgeDialog.jsm
=====================================
@@ -4,11 +4,13 @@ var EXPORTED_SYMBOLS = ["RequestBridgeDialog"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm");
+const { BridgeDB } = ChromeUtils.importESModule(
+ "resource:///modules/BridgeDB.sys.mjs"
+);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class RequestBridgeDialog {
=====================================
browser/components/torpreferences/content/torLogDialog.jsm
=====================================
@@ -2,12 +2,12 @@
var EXPORTED_SYMBOLS = ["TorLogDialog"];
-const { setTimeout, clearTimeout } = ChromeUtils.import(
- "resource://gre/modules/Timer.jsm"
+const { setTimeout, clearTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
);
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
+const { TorProviderBuilder } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
@@ -56,7 +56,7 @@ class TorLogDialog {
}, RESTORE_TIME);
});
- this._logTextarea.value = TorMonitorService.getLog();
+ this._logTextarea.value = TorProviderBuilder.build().getLog();
}
init(window, aDialog) {
=====================================
browser/modules/BridgeDB.jsm → browser/modules/BridgeDB.sys.mjs
=====================================
@@ -1,10 +1,14 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = ["BridgeDB"];
+const lazy = {};
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
+ChromeUtils.defineESModuleGetters(lazy, {
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
+});
-var BridgeDB = {
+export var BridgeDB = {
_moatRPC: null,
_challenge: null,
_image: null,
@@ -20,7 +24,7 @@ var BridgeDB = {
async submitCaptchaGuess(solution) {
if (!this._moatRPC) {
- this._moatRPC = new MoatRPC();
+ this._moatRPC = new lazy.MoatRPC();
await this._moatRPC.init();
}
@@ -37,7 +41,7 @@ var BridgeDB = {
async requestNewCaptchaImage() {
try {
if (!this._moatRPC) {
- this._moatRPC = new MoatRPC();
+ this._moatRPC = new lazy.MoatRPC();
await this._moatRPC.init();
}
=====================================
browser/modules/Moat.jsm → browser/modules/Moat.sys.mjs
=====================================
@@ -1,24 +1,19 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = ["MoatRPC"];
+import {
+ TorSettings,
+ TorBridgeSource,
+} from "resource:///modules/TorSettings.sys.mjs";
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const lazy = {};
-const { Subprocess } = ChromeUtils.import(
- "resource://gre/modules/Subprocess.jsm"
-);
-
-const { TorLauncherUtil } = ChromeUtils.import(
- "resource://gre/modules/TorLauncherUtil.jsm"
-);
-
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
+ TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
const TorLauncherPrefs = Object.freeze({
bridgedb_front: "extensions.torlauncher.bridgedb_front",
@@ -26,73 +21,54 @@ const TorLauncherPrefs = Object.freeze({
moat_service: "extensions.torlauncher.moat_service",
});
-// Config keys used to query tor daemon properties
-const TorConfigKeys = Object.freeze({
- clientTransportPlugin: "ClientTransportPlugin",
-});
-
//
// Launches and controls the PT process lifetime
//
class MeekTransport {
- constructor() {
- this._inited = false;
- this._meekClientProcess = null;
- this._meekProxyType = null;
- this._meekProxyAddress = null;
- this._meekProxyPort = 0;
- this._meekProxyUsername = null;
- this._meekProxyPassword = null;
- }
+ // These members are used by consumers to setup the proxy to do requests over
+ // meek. They are passed to newProxyInfoWithAuth.
+ proxyType = null;
+ proxyAddress = null;
+ proxyPort = 0;
+ proxyUsername = null;
+ proxyPassword = null;
+
+ #inited = false;
+ #meekClientProcess = null;
// launches the meekprocess
async init() {
// ensure we haven't already init'd
- if (this._inited) {
+ if (this.#inited) {
throw new Error("MeekTransport: Already initialized");
}
- // cleanup function for killing orphaned pt process
- let onException = () => {};
try {
// figure out which pluggable transport to use
const supportedTransports = ["meek", "meek_lite"];
- let transportPlugins = await TorProtocolService.readStringArraySetting(
- TorConfigKeys.clientTransportPlugin
+ const proxy = (
+ await lazy.TorProviderBuilder.build().getPluggableTransports()
+ ).find(
+ pt =>
+ pt.type === "exec" &&
+ supportedTransports.some(t => pt.transports.includes(t))
);
+ if (!proxy) {
+ throw new Error("No supported transport found.");
+ }
- let { meekTransport, meekClientPath, meekClientArgs } = (() => {
- for (const line of transportPlugins) {
- let tokens = line.split(" ");
- if (tokens.length > 2 && tokens[1] == "exec") {
- let transportArray = tokens[0].split(",").map(aStr => aStr.trim());
- let transport = transportArray.find(aTransport =>
- supportedTransports.includes(aTransport)
- );
-
- if (transport != undefined) {
- return {
- meekTransport: transport,
- meekClientPath: tokens[2],
- meekClientArgs: tokens.slice(3),
- };
- }
- }
- }
-
- return {
- meekTransport: null,
- meekClientPath: null,
- meekClientArgs: null,
- };
- })();
-
+ const meekTransport = proxy.transports.find(t =>
+ supportedTransports.includes(t)
+ );
// Convert meek client path to absolute path if necessary
- let meekWorkDir = TorLauncherUtil.getTorFile("pt-startup-dir", false);
- if (TorLauncherUtil.isPathRelative(meekClientPath)) {
- let meekPath = meekWorkDir.clone();
- meekPath.appendRelativePath(meekClientPath);
- meekClientPath = meekPath.path;
+ const meekWorkDir = lazy.TorLauncherUtil.getTorFile(
+ "pt-startup-dir",
+ false
+ );
+ if (lazy.TorLauncherUtil.isPathRelative(proxy.pathToBinary)) {
+ const meekPath = meekWorkDir.clone();
+ meekPath.appendRelativePath(proxy.pathToBinary);
+ proxy.pathToBinary = meekPath.path;
}
// Construct the per-connection arguments.
@@ -105,16 +81,13 @@ class MeekTransport {
// First the "<Key>=<Value>" formatted arguments MUST be escaped,
// such that all backslash, equal sign, and semicolon characters
// are escaped with a backslash.
- let escapeArgValue = aValue => {
- if (!aValue) {
- return "";
- }
-
- let rv = aValue.replace(/\\/g, "\\\\");
- rv = rv.replace(/=/g, "\\=");
- rv = rv.replace(/;/g, "\\;");
- return rv;
- };
+ const escapeArgValue = aValue =>
+ aValue
+ ? aValue
+ .replaceAll("\\", "\\\\")
+ .replaceAll("=", "\\=")
+ .replaceAll(";", "\\;")
+ : "";
if (meekReflector) {
meekClientEscapedArgs += "url=";
@@ -132,10 +105,10 @@ class MeekTransport {
}
// Setup env and start meek process
- let ptStateDir = TorLauncherUtil.getTorFile("tordatadir", false);
+ const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
ptStateDir.append("pt_state"); // Match what tor uses.
- let envAdditions = {
+ const envAdditions = {
TOR_PT_MANAGED_TRANSPORT_VER: "1",
TOR_PT_STATE_LOCATION: ptStateDir.path,
TOR_PT_EXIT_ON_STDIN_CLOSE: "1",
@@ -145,9 +118,9 @@ class MeekTransport {
envAdditions.TOR_PT_PROXY = TorSettings.proxy.uri;
}
- let opts = {
- command: meekClientPath,
- arguments: meekClientArgs,
+ const opts = {
+ command: proxy.pathToBinary,
+ arguments: proxy.options.split(/s+/),
workdir: meekWorkDir.path,
environmentAppend: true,
environment: envAdditions,
@@ -155,27 +128,23 @@ class MeekTransport {
};
// Launch meek client
- let meekClientProcess = await Subprocess.call(opts);
- // kill our process if exception is thrown
- onException = () => {
- meekClientProcess.kill();
- };
+ this.#meekClientProcess = await lazy.Subprocess.call(opts);
// Callback chain for reading stderr
- let stderrLogger = async () => {
- if (this._meekClientProcess) {
- let errString = await this._meekClientProcess.stderr.readString();
- console.log(`MeekTransport: stderr => ${errString}`);
- await stderrLogger();
+ const stderrLogger = async () => {
+ while (this.#meekClientProcess) {
+ const errString = await this.#meekClientProcess.stderr.readString();
+ if (errString) {
+ console.log(`MeekTransport: stderr => ${errString}`);
+ }
}
};
stderrLogger();
// Read pt's stdout until terminal (CMETHODS DONE) is reached
// returns array of lines for parsing
- let getInitLines = async (stdout = "") => {
- let string = await meekClientProcess.stdout.readString();
- stdout += string;
+ const getInitLines = async (stdout = "") => {
+ stdout += await this.#meekClientProcess.stdout.readString();
// look for the final message
const CMETHODS_DONE = "CMETHODS DONE";
@@ -188,20 +157,16 @@ class MeekTransport {
};
// read our lines from pt's stdout
- let meekInitLines = await getInitLines();
+ const meekInitLines = await getInitLines();
// tokenize our pt lines
- let meekInitTokens = meekInitLines.map(line => {
- let tokens = line.split(" ");
+ const meekInitTokens = meekInitLines.map(line => {
+ const tokens = line.split(" ");
return {
keyword: tokens[0],
args: tokens.slice(1),
};
});
- let meekProxyType = null;
- let meekProxyAddr = null;
- let meekProxyPort = 0;
-
// parse our pt tokens
for (const { keyword, args } of meekInitTokens) {
const argsJoined = args.join(" ");
@@ -251,9 +216,9 @@ class MeekTransport {
}
// convert proxy type to strings used by protocol-proxy-servce
- meekProxyType = proxyType === "socks5" ? "socks" : "socks4";
- meekProxyAddr = addr;
- meekProxyPort = port;
+ this.proxyType = proxyType === "socks5" ? "socks" : "socks4";
+ this.proxyAddress = addr;
+ this.proxyPort = port;
break;
}
@@ -278,49 +243,47 @@ class MeekTransport {
}
}
- this._meekClientProcess = meekClientProcess;
// register callback to cleanup on process exit
- this._meekClientProcess.wait().then(exitObj => {
- this._meekClientProcess = null;
+ this.#meekClientProcess.wait().then(exitObj => {
+ this.#meekClientProcess = null;
this.uninit();
});
- this._meekProxyType = meekProxyType;
- this._meekProxyAddress = meekProxyAddr;
- this._meekProxyPort = meekProxyPort;
-
// socks5
- if (meekProxyType === "socks") {
+ if (this.proxyType === "socks") {
if (meekClientEscapedArgs.length <= 255) {
- this._meekProxyUsername = meekClientEscapedArgs;
- this._meekProxyPassword = "\x00";
+ this.proxyUsername = meekClientEscapedArgs;
+ this.proxyPassword = "\x00";
} else {
- this._meekProxyUsername = meekClientEscapedArgs.substring(0, 255);
- this._meekProxyPassword = meekClientEscapedArgs.substring(255);
+ this.proxyUsername = meekClientEscapedArgs.substring(0, 255);
+ this.proxyPassword = meekClientEscapedArgs.substring(255);
}
// socks4
} else {
- this._meekProxyUsername = meekClientEscapedArgs;
- this._meekProxyPassword = undefined;
+ this.proxyUsername = meekClientEscapedArgs;
+ this.proxyPassword = undefined;
}
- this._inited = true;
+ this.#inited = true;
} catch (ex) {
- onException();
+ if (this.#meekClientProcess) {
+ this.#meekClientProcess.kill();
+ this.#meekClientProcess = null;
+ }
throw ex;
}
}
async uninit() {
- this._inited = false;
-
- await this._meekClientProcess?.kill();
- this._meekClientProcess = null;
- this._meekProxyType = null;
- this._meekProxyAddress = null;
- this._meekProxyPort = 0;
- this._meekProxyUsername = null;
- this._meekProxyPassword = null;
+ this.#inited = false;
+
+ await this.#meekClientProcess?.kill();
+ this.#meekClientProcess = null;
+ this.proxyType = null;
+ this.proxyAddress = null;
+ this.proxyPort = 0;
+ this.proxyUsername = null;
+ this.proxyPassword = null;
}
}
@@ -328,21 +291,25 @@ class MeekTransport {
// Callback object with a cached promise for the returned Moat data
//
class MoatResponseListener {
+ #response = "";
+ #responsePromise;
+ #resolve;
+ #reject;
constructor() {
- this._response = "";
+ this.#response = "";
// we need this promise here because await nsIHttpChannel::asyncOpen does
// not return only once the request is complete, it seems to return
// after it begins, so we have to get the result from this listener object.
// This promise is only resolved once onStopRequest is called
- this._responsePromise = new Promise((resolve, reject) => {
- this._resolve = resolve;
- this._reject = reject;
+ this.#responsePromise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
});
}
// callers wait on this for final response
response() {
- return this._responsePromise;
+ return this.#responsePromise;
}
// noop
@@ -352,16 +319,17 @@ class MoatResponseListener {
onStopRequest(request, status) {
try {
if (!Components.isSuccessCode(status)) {
- const errorMessage = TorLauncherUtil.getLocalizedStringForError(status);
- this._reject(new Error(errorMessage));
+ const errorMessage =
+ lazy.TorLauncherUtil.getLocalizedStringForError(status);
+ this.#reject(new Error(errorMessage));
}
if (request.responseStatus != 200) {
- this._reject(new Error(request.responseStatusText));
+ this.#reject(new Error(request.responseStatusText));
}
} catch (err) {
- this._reject(err);
+ this.#reject(err);
}
- this._resolve(this._response);
+ this.#resolve(this.#response);
}
// read response data
@@ -370,30 +338,32 @@ class MoatResponseListener {
"@mozilla.org/scriptableinputstream;1"
].createInstance(Ci.nsIScriptableInputStream);
scriptableStream.init(stream);
- this._response += scriptableStream.read(length);
+ this.#response += scriptableStream.read(length);
}
}
class InternetTestResponseListener {
+ #promise;
+ #resolve;
+ #reject;
constructor() {
- this._promise = new Promise((resolve, reject) => {
- this._resolve = resolve;
- this._reject = reject;
+ this.#promise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
});
}
// callers wait on this for final response
get status() {
- return this._promise;
+ return this.#promise;
}
onStartRequest(request) {}
// resolve or reject our Promise
onStopRequest(request, status) {
- let statuses = {};
try {
- statuses = {
+ const statuses = {
components: status,
successful: Components.isSuccessCode(status),
};
@@ -408,56 +378,51 @@ class InternetTestResponseListener {
err
);
}
+ this.#resolve(statuses);
} catch (err) {
- this._reject(err);
+ this.#reject(err);
}
- this._resolve(statuses);
}
onDataAvailable(request, stream, offset, length) {
- // We do not care of the actual data, as long as we have a successful
+ // We do not care of the actual data, as long as we have a successful
// connection
}
}
// constructs the json objects and sends the request over moat
-class MoatRPC {
- constructor() {
- this._meekTransport = null;
- this._inited = false;
- }
+export class MoatRPC {
+ #inited = false;
+ #meekTransport = null;
get inited() {
- return this._inited;
+ return this.#inited;
}
async init() {
- if (this._inited) {
+ if (this.#inited) {
throw new Error("MoatRPC: Already initialized");
}
let meekTransport = new MeekTransport();
await meekTransport.init();
- this._meekTransport = meekTransport;
- this._inited = true;
+ this.#meekTransport = meekTransport;
+ this.#inited = true;
}
async uninit() {
- await this._meekTransport?.uninit();
- this._meekTransport = null;
- this._inited = false;
+ await this.#meekTransport?.uninit();
+ this.#meekTransport = null;
+ this.#inited = false;
}
- _makeHttpHandler(uriString) {
- if (!this._inited) {
+ #makeHttpHandler(uriString) {
+ if (!this.#inited) {
throw new Error("MoatRPC: Not initialized");
}
- const proxyType = this._meekTransport._meekProxyType;
- const proxyAddress = this._meekTransport._meekProxyAddress;
- const proxyPort = this._meekTransport._meekProxyPort;
- const proxyUsername = this._meekTransport._meekProxyUsername;
- const proxyPassword = this._meekTransport._meekProxyPassword;
+ const { proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword } =
+ this.#meekTransport;
const proxyPS = Cc[
"@mozilla.org/network/protocol-proxy-service;1"
@@ -511,11 +476,11 @@ class MoatRPC {
return ch;
}
- async _makeRequest(procedure, args) {
+ async #makeRequest(procedure, args) {
const procedureURIString = `${Services.prefs.getStringPref(
TorLauncherPrefs.moat_service
)}/${procedure}`;
- const ch = this._makeHttpHandler(procedureURIString);
+ const ch = this.#makeHttpHandler(procedureURIString);
// Arrange for the POST data to be sent.
const argsJson = JSON.stringify(args);
@@ -544,7 +509,7 @@ class MoatRPC {
const uri = `${Services.prefs.getStringPref(
TorLauncherPrefs.moat_service
)}/circumvention/countries`;
- const ch = this._makeHttpHandler(uri);
+ const ch = this.#makeHttpHandler(uri);
ch.requestMethod = "HEAD";
const listener = new InternetTestResponseListener();
@@ -582,7 +547,7 @@ class MoatRPC {
},
],
};
- const response = await this._makeRequest("fetch", args);
+ const response = await this.#makeRequest("fetch", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -623,7 +588,7 @@ class MoatRPC {
},
],
};
- const response = await this._makeRequest("check", args);
+ const response = await this.#makeRequest("check", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -642,7 +607,7 @@ class MoatRPC {
// Convert received settings object to format used by TorSettings module
// In the event of error, just return null
- _fixupSettings(settings) {
+ #fixupSettings(settings) {
try {
let retval = TorSettings.defaultSettings();
if ("bridges" in settings) {
@@ -691,11 +656,11 @@ class MoatRPC {
// Converts a list of settings objects received from BridgeDB to a list of settings objects
// understood by the TorSettings module
// In the event of error, returns and empty list
- _fixupSettingsList(settingsList) {
+ #fixupSettingsList(settingsList) {
try {
let retval = [];
for (let settings of settingsList) {
- settings = this._fixupSettings(settings);
+ settings = this.#fixupSettings(settings);
if (settings != null) {
retval.push(settings);
}
@@ -724,7 +689,7 @@ class MoatRPC {
transports: transports ? transports : [],
country,
};
- const response = await this._makeRequest("circumvention/settings", args);
+ const response = await this.#makeRequest("circumvention/settings", args);
let settings = {};
if ("errors" in response) {
const code = response.errors[0].code;
@@ -739,7 +704,7 @@ class MoatRPC {
throw new Error(`MoatRPC: ${detail} (${code})`);
} else if ("settings" in response) {
- settings.settings = this._fixupSettingsList(response.settings);
+ settings.settings = this.#fixupSettingsList(response.settings);
}
if ("country" in response) {
settings.country = response.country;
@@ -753,7 +718,7 @@ class MoatRPC {
// for
async circumvention_countries() {
const args = {};
- return this._makeRequest("circumvention/countries", args);
+ return this.#makeRequest("circumvention/countries", args);
}
// Request a copy of the builtin bridges, takes the following parameters:
@@ -766,7 +731,7 @@ class MoatRPC {
const args = {
transports: transports ? transports : [],
};
- const response = await this._makeRequest("circumvention/builtin", args);
+ const response = await this.#makeRequest("circumvention/builtin", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -791,13 +756,13 @@ class MoatRPC {
const args = {
transports: transports ? transports : [],
};
- const response = await this._makeRequest("circumvention/defaults", args);
+ const response = await this.#makeRequest("circumvention/defaults", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
throw new Error(`MoatRPC: ${detail} (${code})`);
} else if ("settings" in response) {
- return this._fixupSettingsList(response.settings);
+ return this.#fixupSettingsList(response.settings);
}
return [];
}
=====================================
browser/modules/TorConnect.jsm → browser/modules/TorConnect.sys.mjs
=====================================
@@ -1,36 +1,32 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = [
- "InternetStatus",
- "TorConnect",
- "TorConnectTopics",
- "TorConnectState",
-];
+import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const lazy = {};
-const { setTimeout, clearTimeout } = ChromeUtils.import(
- "resource://gre/modules/Timer.jsm"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
+ TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
-const { BrowserWindowTracker } = ChromeUtils.import(
+// TODO: Should we move this to the about:torconnect actor?
+ChromeUtils.defineModuleGetter(
+ lazy,
+ "BrowserWindowTracker",
"resource:///modules/BrowserWindowTracker.jsm"
);
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
-);
-const { TorBootstrapRequest } = ChromeUtils.import(
- "resource://gre/modules/TorBootstrapRequest.jsm"
-);
-
-const { TorSettings, TorSettingsTopics, TorBuiltinBridgeTypes } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+import {
+ TorSettings,
+ TorSettingsTopics,
+ TorBuiltinBridgeTypes,
+} from "resource:///modules/TorSettings.sys.mjs";
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
-
const TorTopics = Object.freeze({
LogHasWarnOrErr: "TorLogHasWarnOrErr",
ProcessExited: "TorProcessExited",
@@ -46,7 +42,7 @@ const TorConnectPrefs = Object.freeze({
allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
});
-const TorConnectState = Object.freeze({
+export const TorConnectState = Object.freeze({
/* Our initial state */
Initial: "Initial",
/* In-between initial boot and bootstrapping, users can change tor network settings during this state */
@@ -156,7 +152,7 @@ const TorConnectStateTransitions = Object.freeze(
);
/* Topics Notified by the TorConnect module */
-const TorConnectTopics = Object.freeze({
+export const TorConnectTopics = Object.freeze({
StateChange: "torconnect:state-change",
BootstrapProgress: "torconnect:bootstrap-progress",
BootstrapComplete: "torconnect:bootstrap-complete",
@@ -238,7 +234,7 @@ const debug_sleep = async ms => {
});
};
-const InternetStatus = Object.freeze({
+export const InternetStatus = Object.freeze({
Unknown: -1,
Offline: 0,
Online: 1,
@@ -302,7 +298,7 @@ class InternetTest {
// waiting both for the bootstrap, and for the Internet test.
// However, managing Moat with async/await is much easier as it avoids a
// callback hell, and it makes extra explicit that we are uniniting it.
- const mrpc = new MoatRPC();
+ const mrpc = new lazy.MoatRPC();
let status = null;
let error = null;
try {
@@ -340,7 +336,7 @@ class InternetTest {
}
}
-const TorConnect = (() => {
+export const TorConnect = (() => {
let retval = {
_state: TorConnectState.Initial,
_bootstrapProgress: 0,
@@ -459,7 +455,7 @@ const TorConnect = (() => {
return;
}
- const tbr = new TorBootstrapRequest();
+ const tbr = new lazy.TorBootstrapRequest();
const internetTest = new InternetTest();
let cancelled = false;
@@ -604,7 +600,7 @@ const TorConnect = (() => {
// lookup user's potential censorship circumvention settings from Moat service
try {
- this.mrpc = new MoatRPC();
+ this.mrpc = new lazy.MoatRPC();
await this.mrpc.init();
if (this.transitioning) {
@@ -678,7 +674,7 @@ const TorConnect = (() => {
await TorSettings.applySettings();
// build out our bootstrap request
- const tbr = new TorBootstrapRequest();
+ const tbr = new lazy.TorBootstrapRequest();
tbr.onbootstrapstatus = (progress, status) => {
TorConnect._updateBootstrapStatus(progress, status);
};
@@ -915,7 +911,7 @@ const TorConnect = (() => {
* @type {boolean}
*/
get enabled() {
- return TorMonitorService.ownsTorDaemon;
+ return lazy.TorProviderBuilder.build().ownsTorDaemon;
},
get shouldShowTorConnect() {
@@ -1053,7 +1049,7 @@ const TorConnect = (() => {
Further external commands and helper methods
*/
openTorPreferences() {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:preferences#connection", true);
},
@@ -1073,7 +1069,7 @@ const TorConnect = (() => {
* begin AutoBootstrapping, if possible.
*/
openTorConnect(options) {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:torconnect", true, {
ignoreQueryString: true,
});
@@ -1094,7 +1090,7 @@ const TorConnect = (() => {
},
viewTorLogs() {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:preferences#connection-viewlogs", true);
},
@@ -1104,7 +1100,7 @@ const TorConnect = (() => {
if (this._countryCodes.length) {
return this._countryCodes;
}
- const mrpc = new MoatRPC();
+ const mrpc = new lazy.MoatRPC();
try {
await mrpc.init();
this._countryCodes = await mrpc.circumvention_countries();
=====================================
browser/modules/TorSettings.jsm → browser/modules/TorSettings.sys.mjs
=====================================
@@ -1,36 +1,22 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = [
- "TorSettings",
- "TorSettingsTopics",
- "TorSettingsData",
- "TorBridgeSource",
- "TorBuiltinBridgeTypes",
- "TorProxyType",
-];
+const lazy = {};
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
-);
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
-/* tor-launcher observer topics */
-const TorTopics = Object.freeze({
- ProcessIsReady: "TorProcessIsReady",
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
/* TorSettings observer topics */
-const TorSettingsTopics = Object.freeze({
+export const TorSettingsTopics = Object.freeze({
Ready: "torsettings:ready",
SettingChanged: "torsettings:setting-changed",
});
/* TorSettings observer data (for SettingChanged topic) */
-const TorSettingsData = Object.freeze({
+export const TorSettingsData = Object.freeze({
QuickStartEnabled: "torsettings:quickstart_enabled",
});
@@ -98,21 +84,21 @@ const TorConfigKeys = Object.freeze({
clientTransportPlugin: "ClientTransportPlugin",
});
-const TorBridgeSource = Object.freeze({
+export const TorBridgeSource = Object.freeze({
Invalid: -1,
BuiltIn: 0,
BridgeDB: 1,
UserProvided: 2,
});
-const TorProxyType = Object.freeze({
+export const TorProxyType = Object.freeze({
Invalid: -1,
Socks4: 0,
Socks5: 1,
HTTPS: 2,
});
-const TorBuiltinBridgeTypes = Object.freeze(
+export const TorBuiltinBridgeTypes = Object.freeze(
(() => {
const bridgeListBranch = Services.prefs.getBranch(
TorLauncherPrefs.default_bridge
@@ -254,7 +240,7 @@ const arrayCopy = function (array) {
/* TorSettings module */
-const TorSettings = (() => {
+export const TorSettings = (() => {
const self = {
_settings: null,
@@ -288,7 +274,8 @@ const TorSettings = (() => {
/* load or init our settings, and register observers */
init() {
- if (TorMonitorService.ownsTorDaemon) {
+ const provider = lazy.TorProviderBuilder.build();
+ if (provider.ownsTorDaemon) {
// if the settings branch exists, load settings from prefs
if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
this.loadFromPrefs();
@@ -296,9 +283,9 @@ const TorSettings = (() => {
// otherwise load defaults
this._settings = this.defaultSettings();
}
- Services.obs.addObserver(this, TorTopics.ProcessIsReady);
+ Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
- if (TorMonitorService.isRunning) {
+ if (provider.isRunning) {
this.handleProcessReady();
}
}
@@ -309,8 +296,11 @@ const TorSettings = (() => {
console.log(`TorSettings: Observed ${topic}`);
switch (topic) {
- case TorTopics.ProcessIsReady:
- Services.obs.removeObserver(this, TorTopics.ProcessIsReady);
+ case lazy.TorProviderTopics.ProcessIsReady:
+ Services.obs.removeObserver(
+ this,
+ lazy.TorProviderTopics.ProcessIsReady
+ );
await this.handleProcessReady();
break;
}
@@ -569,7 +559,7 @@ const TorSettings = (() => {
}
/* Push to Tor */
- await TorProtocolService.writeSettings(settingsMap);
+ await lazy.TorProviderBuilder.build().writeSettings(settingsMap);
return this;
},
=====================================
browser/modules/moz.build
=====================================
@@ -123,7 +123,7 @@ XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]
EXTRA_JS_MODULES += [
"AboutNewTab.jsm",
"AsyncTabSwitcher.jsm",
- "BridgeDB.jsm",
+ "BridgeDB.sys.mjs",
"BrowserUIUtils.jsm",
"BrowserUsageTelemetry.jsm",
"BrowserWindowTracker.jsm",
@@ -135,7 +135,7 @@ EXTRA_JS_MODULES += [
"FeatureCallout.sys.mjs",
"HomePage.jsm",
"LaterRun.jsm",
- 'Moat.jsm',
+ "Moat.sys.mjs",
"NewTabPagePreloading.jsm",
"OpenInTabsUtils.jsm",
"PageActions.jsm",
@@ -149,8 +149,8 @@ EXTRA_JS_MODULES += [
"SitePermissions.sys.mjs",
"TabsList.jsm",
"TabUnloader.jsm",
- "TorConnect.jsm",
- "TorSettings.jsm",
+ "TorConnect.sys.mjs",
+ "TorSettings.sys.mjs",
"TorStrings.jsm",
"TransientPrefs.jsm",
"URILoadingHelper.sys.mjs",
=====================================
toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
=====================================
@@ -1,6 +1,6 @@
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
-import { TorProtocolService } from "resource://gre/modules/TorProtocolService.sys.mjs";
+import { TorProviderBuilder } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
/* tor-launcher observer topics */
@@ -13,19 +13,23 @@ export const TorTopics = Object.freeze({
// modeled after XMLHttpRequest
// nicely encapsulates the observer register/unregister logic
export class TorBootstrapRequest {
+ // number of ms to wait before we abandon the bootstrap attempt
+ // a value of 0 implies we never wait
+ timeout = 0;
+
+ // callbacks for bootstrap process status updates
+ onbootstrapstatus = (progress, status) => {};
+ onbootstrapcomplete = () => {};
+ onbootstraperror = (message, details) => {};
+
+ // internal resolve() method for bootstrap
+ #bootstrapPromiseResolve = null;
+ #bootstrapPromise = null;
+ #timeoutID = null;
+ #provider = null;
+
constructor() {
- // number of ms to wait before we abandon the bootstrap attempt
- // a value of 0 implies we never wait
- this.timeout = 0;
- // callbacks for bootstrap process status updates
- this.onbootstrapstatus = (progress, status) => {};
- this.onbootstrapcomplete = () => {};
- this.onbootstraperror = (message, details) => {};
-
- // internal resolve() method for bootstrap
- this._bootstrapPromiseResolve = null;
- this._bootstrapPromise = null;
- this._timeoutID = null;
+ this.#provider = TorProviderBuilder.build();
}
observe(subject, topic, data) {
@@ -41,15 +45,16 @@ export class TorBootstrapRequest {
if (this.onbootstrapcomplete) {
this.onbootstrapcomplete();
}
- this._bootstrapPromiseResolve(true);
- clearTimeout(this._timeoutID);
+ this.#bootstrapPromiseResolve(true);
+ clearTimeout(this.#timeoutID);
+ this.#timeoutID = null;
}
break;
}
case TorTopics.BootstrapError: {
console.info("TorBootstrapRequest: observerd TorBootstrapError", obj);
- this._stop(obj?.message, obj?.details);
+ this.#stop(obj?.message, obj?.details);
break;
}
}
@@ -57,12 +62,12 @@ export class TorBootstrapRequest {
// resolves 'true' if bootstrap succeeds, false otherwise
bootstrap() {
- if (this._bootstrapPromise) {
- return this._bootstrapPromise;
+ if (this.#bootstrapPromise) {
+ return this.#bootstrapPromise;
}
- this._bootstrapPromise = new Promise((resolve, reject) => {
- this._bootstrapPromiseResolve = resolve;
+ this.#bootstrapPromise = new Promise((resolve, reject) => {
+ this.#bootstrapPromiseResolve = resolve;
// register ourselves to listen for bootstrap events
Services.obs.addObserver(this, TorTopics.BootstrapStatus);
@@ -70,10 +75,10 @@ export class TorBootstrapRequest {
// optionally cancel bootstrap after a given timeout
if (this.timeout > 0) {
- this._timeoutID = setTimeout(async () => {
- this._timeoutID = null;
+ this.#timeoutID = setTimeout(async () => {
+ this.#timeoutID = null;
// TODO: Translate, if really used
- await this._stop(
+ await this.#stop(
"Tor Bootstrap process timed out",
`Bootstrap attempt abandoned after waiting ${this.timeout} ms`
);
@@ -81,38 +86,45 @@ export class TorBootstrapRequest {
}
// wait for bootstrapping to begin and maybe handle error
- TorProtocolService.connect().catch(err => {
- this._stop(err.message, "");
+ this.#provider.connect().catch(err => {
+ this.#stop(err.message, "");
});
}).finally(() => {
// and remove ourselves once bootstrap is resolved
Services.obs.removeObserver(this, TorTopics.BootstrapStatus);
Services.obs.removeObserver(this, TorTopics.BootstrapError);
- this._bootstrapPromise = null;
+ this.#bootstrapPromise = null;
});
- return this._bootstrapPromise;
+ return this.#bootstrapPromise;
}
async cancel() {
- await this._stop();
+ await this.#stop();
}
// Internal implementation. Do not use directly, but call cancel, instead.
- async _stop(message, details) {
+ async #stop(message, details) {
// first stop our bootstrap timeout before handling the error
- if (this._timeoutID !== null) {
- clearTimeout(this._timeoutID);
- this._timeoutID = null;
+ if (this.#timeoutID !== null) {
+ clearTimeout(this.#timeoutID);
+ this.#timeoutID = null;
}
- // stopBootstrap never throws
- await TorProtocolService.stopBootstrap();
+ try {
+ await this.#provider.stopBootstrap();
+ } catch (e) {
+ console.error("Failed to stop the bootstrap.", e);
+ if (!message) {
+ message = e.message;
+ details = "";
+ }
+ }
if (this.onbootstraperror && message) {
this.onbootstraperror(message, details);
}
- this._bootstrapPromiseResolve(false);
+ this.#bootstrapPromiseResolve(false);
}
}
=====================================
toolkit/components/tor-launcher/TorControlPort.sys.mjs
=====================================
@@ -274,6 +274,44 @@ class AsyncSocket {
* the command
*/
+/**
+ * @typedef {object} Bridge
+ * @property {string} transport The transport of the bridge, or vanilla if not
+ * specified.
+ * @property {string} addr The IP address and port of the bridge
+ * @property {string} id The fingerprint of the bridge
+ * @property {string} args Optional arguments passed to the bridge
+ */
+/**
+ * @typedef {object} PTInfo The information about a pluggable transport
+ * @property {string[]} transports An array with all the transports supported by
+ * this configuration.
+ * @property {string} type Either socks4, socks5 or exec
+ * @property {string} [ip] The IP address of the proxy (only for socks4 and
+ * socks5)
+ * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
+ * @property {string} [pathToBinary] Path to the binary that is run (only for
+ * exec)
+ * @property {string} [options] Optional options passed to the binary (only for
+ * exec)
+ */
+/**
+ * @typedef {object} OnionAuthKeyInfo
+ * @property {string} address The address of the onion service
+ * @property {string} typeAndKey Onion service key and type of key, as
+ * `type:base64-private-key`
+ * @property {string} Flags Additional flags, such as Permanent
+ */
+/**
+ * @callback EventFilterCallback
+ * @param {any} data Either a raw string, or already parsed data
+ * @returns {boolean}
+ */
+/**
+ * @callback EventCallback
+ * @param {any} data Either a raw string, or already parsed data
+ */
+
class TorError extends Error {
constructor(command, reply) {
super(`${command} -> ${reply}`);
@@ -584,319 +622,6 @@ class ControlSocket {
}
}
-// ## utils
-// A namespace for utility functions
-let utils = {};
-
-// __utils.identity(x)__.
-// Returns its argument unchanged.
-utils.identity = function (x) {
- return x;
-};
-
-// __utils.capture(string, regex)__.
-// Takes a string and returns an array of capture items, where regex must have a single
-// capturing group and use the suffix /.../g to specify a global search.
-utils.capture = function (string, regex) {
- let matches = [];
- // Special trick to use string.replace for capturing multiple matches.
- string.replace(regex, function (a, captured) {
- matches.push(captured);
- });
- return matches;
-};
-
-// __utils.extractor(regex)__.
-// Returns a function that takes a string and returns an array of regex matches. The
-// regex must use the suffix /.../g to specify a global search.
-utils.extractor = function (regex) {
- return function (text) {
- return utils.capture(text, regex);
- };
-};
-
-// __utils.splitLines(string)__.
-// Splits a string into an array of strings, each corresponding to a line.
-utils.splitLines = function (string) {
- return string.split(/\r?\n/);
-};
-
-// __utils.splitAtSpaces(string)__.
-// Splits a string into chunks between spaces. Does not split at spaces
-// inside pairs of quotation marks.
-utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g);
-
-// __utils.splitAtFirst(string, regex)__.
-// Splits a string at the first instance of regex match. If no match is
-// found, returns the whole string.
-utils.splitAtFirst = function (string, regex) {
- let match = string.match(regex);
- return match
- ? [
- string.substring(0, match.index),
- string.substring(match.index + match[0].length),
- ]
- : string;
-};
-
-// __utils.splitAtEquals(string)__.
-// Splits a string into chunks between equals. Does not split at equals
-// inside pairs of quotation marks.
-utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g);
-
-// __utils.mergeObjects(arrayOfObjects)__.
-// Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object.
-// Pure function.
-utils.mergeObjects = function (arrayOfObjects) {
- let result = {};
- for (let obj of arrayOfObjects) {
- for (let key in obj) {
- result[key] = obj[key];
- }
- }
- return result;
-};
-
-// __utils.listMapData(parameterString, listNames)__.
-// Takes a list of parameters separated by spaces, of which the first several are
-// unnamed, and the remainder are named, in the form `NAME=VALUE`. Apply listNames
-// to the unnamed parameters, and combine them in a map with the named parameters.
-// Example: `40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH`
-//
-// utils.listMapData("40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH",
-// ["streamID", "event", "circuitID", "IP"])
-// // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0",
-// // "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}"
-utils.listMapData = function (parameterString, listNames) {
- // Split out the space-delimited parameters.
- let parameters = utils.splitAtSpaces(parameterString),
- dataMap = {};
- // Assign listNames to the first n = listNames.length parameters.
- for (let i = 0; i < listNames.length; ++i) {
- dataMap[listNames[i]] = parameters[i];
- }
- // Read key-value pairs and copy these to the dataMap.
- for (let i = listNames.length; i < parameters.length; ++i) {
- let [key, value] = utils.splitAtEquals(parameters[i]);
- if (key && value) {
- dataMap[key] = value;
- }
- }
- return dataMap;
-};
-
-// ## info
-// A namespace for functions related to tor's GETINFO and GETCONF command.
-let info = {};
-
-// __info.keyValueStringsFromMessage(messageText)__.
-// Takes a message (text) response to GETINFO or GETCONF and provides
-// a series of key-value strings, which are either multiline (with a `250+` prefix):
-//
-// 250+config/defaults=
-// AccountingMax "0 bytes"
-// AllowDotExit "0"
-// .
-//
-// or single-line (with a `250-` or `250 ` prefix):
-//
-// 250-version=0.2.6.0-alpha-dev (git-b408125288ad6943)
-info.keyValueStringsFromMessage = utils.extractor(
- /^(250\+[\s\S]+?^\.|250[- ].+?)$/gim
-);
-
-// __info.applyPerLine(transformFunction)__.
-// Returns a function that splits text into lines,
-// and applies transformFunction to each line.
-info.applyPerLine = function (transformFunction) {
- return function (text) {
- return utils.splitLines(text.trim()).map(transformFunction);
- };
-};
-
-// __info.routerStatusParser(valueString)__.
-// Parses a router status entry as, described in
-// https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt
-// (search for "router status entry")
-info.routerStatusParser = function (valueString) {
- let lines = utils.splitLines(valueString),
- objects = [];
- for (let line of lines) {
- // Drop first character and grab data following it.
- let myData = line.substring(2),
- // Accumulate more maps with data, depending on the first character in the line.
- dataFun = {
- r: data =>
- utils.listMapData(data, [
- "nickname",
- "identity",
- "digest",
- "publicationDate",
- "publicationTime",
- "IP",
- "ORPort",
- "DirPort",
- ]),
- a: data => ({ IPv6: data }),
- s: data => ({ statusFlags: utils.splitAtSpaces(data) }),
- v: data => ({ version: data }),
- w: data => utils.listMapData(data, []),
- p: data => ({ portList: data.split(",") }),
- }[line.charAt(0)];
- if (dataFun !== undefined) {
- objects.push(dataFun(myData));
- }
- }
- return utils.mergeObjects(objects);
-};
-
-// __info.circuitStatusParser(line)__.
-// Parse the output of a circuit status line.
-info.circuitStatusParser = function (line) {
- let data = utils.listMapData(line, ["id", "status", "circuit"]),
- circuit = data.circuit;
- // Parse out the individual circuit IDs and names.
- if (circuit) {
- data.circuit = circuit.split(",").map(function (x) {
- return x.split(/~|=/);
- });
- }
- return data;
-};
-
-// __info.streamStatusParser(line)__.
-// Parse the output of a stream status line.
-info.streamStatusParser = function (text) {
- return utils.listMapData(text, [
- "StreamID",
- "StreamStatus",
- "CircuitID",
- "Target",
- ]);
-};
-
-// TODO: fix this parsing logic to handle bridgeLine correctly
-// fingerprint/id is an optional parameter
-// __info.bridgeParser(bridgeLine)__.
-// Takes a single line from a `getconf bridge` result and returns
-// a map containing the bridge's type, address, and ID.
-info.bridgeParser = function (bridgeLine) {
- let result = {},
- tokens = bridgeLine.split(/\s+/);
- // First check if we have a "vanilla" bridge:
- if (tokens[0].match(/^\d+\.\d+\.\d+\.\d+/)) {
- result.type = "vanilla";
- [result.address, result.ID] = tokens;
- // Several bridge types have a similar format:
- } else {
- result.type = tokens[0];
- if (
- [
- "flashproxy",
- "fte",
- "meek",
- "meek_lite",
- "obfs3",
- "obfs4",
- "scramblesuit",
- "snowflake",
- ].includes(result.type)
- ) {
- [result.address, result.ID] = tokens.slice(1);
- }
- }
- return result.type ? result : null;
-};
-
-// __info.parsers__.
-// A map of GETINFO and GETCONF keys to parsing function, which convert
-// result strings to JavaScript data.
-info.parsers = {
- "ns/id/": info.routerStatusParser,
- "ip-to-country/": utils.identity,
- "circuit-status": info.applyPerLine(info.circuitStatusParser),
- bridge: info.bridgeParser,
- // Currently unused parsers:
- // "ns/name/" : info.routerStatusParser,
- // "stream-status" : info.applyPerLine(info.streamStatusParser),
- // "version" : utils.identity,
- // "config-file" : utils.identity,
-};
-
-// __info.getParser(key)__.
-// Takes a key and determines the parser function that should be used to
-// convert its corresponding valueString to JavaScript data.
-info.getParser = function (key) {
- return (
- info.parsers[key] ||
- info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]
- );
-};
-
-// __info.stringToValue(string)__.
-// Converts a key-value string as from GETINFO or GETCONF to a value.
-info.stringToValue = function (string) {
- // key should look something like `250+circuit-status=` or `250-circuit-status=...`
- // or `250 circuit-status=...`
- let matchForKey = string.match(/^250[ +-](.+?)=/),
- key = matchForKey ? matchForKey[1] : null;
- if (key === null) {
- return null;
- }
- // matchResult finds a single-line result for `250-` or `250 `,
- // or a multi-line one for `250+`.
- let matchResult =
- string.match(/^250[ -].+?=(.*)$/) ||
- string.match(/^250\+.+?=([\s\S]*?)^\.$/m),
- // Retrieve the captured group (the text of the value in the key-value pair)
- valueString = matchResult ? matchResult[1] : null,
- // Get the parser function for the key found.
- parse = info.getParser(key.toLowerCase());
- if (parse === undefined) {
- throw new Error("No parser found for '" + key + "'");
- }
- // Return value produced by the parser.
- return parse(valueString);
-};
-
-/**
- * @typedef {object} Bridge
- * @property {string} transport The transport of the bridge, or vanilla if not
- * specified.
- * @property {string} addr The IP address and port of the bridge
- * @property {string} id The fingerprint of the bridge
- * @property {string} args Optional arguments passed to the bridge
- */
-/**
- * @typedef {object} PTInfo The information about a pluggable transport
- * @property {string[]} transports An array with all the transports supported by
- * this configuration.
- * @property {string} type Either socks4, socks5 or exec
- * @property {string} [ip] The IP address of the proxy (only for socks4 and
- * socks5)
- * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
- * @property {string} [pathToBinary] Path to the binary that is run (only for
- * exec)
- * @property {string} [options] Optional options passed to the binary (only for
- * exec)
- */
-/**
- * @typedef {object} OnionAuthKeyInfo
- * @property {string} address The address of the onion service
- * @property {string} typeAndKey Onion service key and type of key, as
- * `type:base64-private-key`
- * @property {string} Flags Additional flags, such as Permanent
- */
-/**
- * @callback EventFilterCallback
- * @param {any} data Either a raw string, or already parsed data
- * @returns {boolean}
- */
-/**
- * @callback EventCallback
- * @param {any} data Either a raw string, or already parsed data
- */
-
class TorController {
/**
* The control socket
@@ -905,16 +630,6 @@ class TorController {
*/
#socket;
- /**
- * A map of EVENT keys to parsing functions, which convert result strings to
- * JavaScript data.
- */
- #eventParsers = {
- stream: info.streamStatusParser,
- // Currently unused:
- // "circ" : info.circuitStatusParser,
- };
-
/**
* Builds a new TorController.
*
@@ -981,18 +696,6 @@ class TorController {
await this.#sendCommandSimple(`authenticate ${password || ""}`);
}
- /**
- * Sends a GETINFO for a single key.
- *
- * @param {string} key The key to get value for
- * @returns {any} The return value depends on the requested key
- */
- async getInfo(key) {
- this.#expectString(key, "key");
- const response = await this.sendCommand(`getinfo ${key}`);
- return this.#getMultipleResponseValues(response)[0];
- }
-
/**
* Sends a GETINFO for a single key.
* control-spec.txt says "one ReplyLine is sent for each requested value", so,
@@ -1054,9 +757,7 @@ class TorController {
const addresses = [v4[5]];
// a address:port
// dir-spec.txt also states only the first one should be taken
- // TODO: The consumers do not care about the port or the square brackets
- // either. Remove them when integrating this function with the rest
- const v6 = reply.match(/^a\s+(\[[0-9a-fA-F:]+\]:[0-9]{1,5})$/m);
+ const v6 = reply.match(/^a\s+\[([0-9a-fA-F:]+)\]:\d{1,5}$/m);
if (v6) {
addresses.push(v6[1]);
}
@@ -1091,23 +792,6 @@ class TorController {
// Configuration
- /**
- * Sends a GETCONF for a single key.
- * GETCONF with a single argument returns results with one or more lines that
- * look like `250[- ]key=value`.
- * Any GETCONF lines that contain a single keyword only are currently dropped.
- * So we can use similar parsing to that for getInfo.
- *
- * @param {string} key The key to get value for
- * @returns {any} A parsed config value (it depends if a parser is known)
- */
- async getConf(key) {
- this.#expectString(key, "key");
- return this.#getMultipleResponseValues(
- await this.sendCommand(`getconf ${key}`)
- );
- }
-
/**
* Sends a GETCONF for a single key.
* The function could be easily generalized to get multiple keys at once, but
@@ -1264,12 +948,14 @@ class TorController {
// TODO: Change the consumer and make the fields more consistent with what
// we get (e.g., separate key and type, and use a boolen for permanent).
const info = {
- hsAddress: match.groups.HSAddress,
- typeAndKey: `${match.groups.KeyType}:${match.groups.PrivateKeyBlob}`,
+ address: match.groups.HSAddress,
+ keyType: match.groups.KeyType,
+ keyBlob: match.groups.PrivateKeyBlob,
+ flags: [],
};
const maybeFlags = match.groups.other?.match(/Flags=(\S+)/);
if (maybeFlags) {
- info.Flags = maybeFlags[1];
+ info.flags = maybeFlags[1].split(",");
}
return info;
});
@@ -1369,28 +1055,12 @@ class TorController {
* first.
*
* @param {string} type The event type to catch
- * @param {EventFilterCallback?} filter An optional callback to filter
- * events for which the callback will be called. If null, all events will be
- * passed.
* @param {EventCallback} callback The callback that will handle the event
- * @param {boolean} raw Tell whether to ignore the data parser, even if
- * supported
*/
- watchEvent(type, filter, callback, raw = false) {
+ watchEvent(type, callback) {
this.#expectString(type, "type");
const start = `650 ${type}`;
- this.#socket.addNotificationCallback(new RegExp(`^${start}`), message => {
- // Remove also the initial text
- const dataText = message.substring(start.length + 1);
- const parser = this.#eventParsers[type.toLowerCase()];
- const data = dataText && parser ? parser(dataText) : null;
- // FIXME: This is the original code, but we risk of not filtering on the
- // data, if we ask for raw data (which we always do at the moment, but we
- // do not use a filter either...)
- if (filter === null || filter(data)) {
- callback(data && !raw ? data : message);
- }
- });
+ this.#socket.addNotificationCallback(new RegExp(`^${start}`), callback);
}
// Other helpers
@@ -1453,19 +1123,6 @@ class TorController {
)
);
}
-
- /**
- * Process multiple responses to a GETINFO or GETCONF request.
- *
- * @param {string} message The message to process
- * @returns {object[]} The keys depend on the message
- */
- #getMultipleResponseValues(message) {
- return info
- .keyValueStringsFromMessage(message)
- .map(info.stringToValue)
- .filter(x => x);
- }
}
const controlPortInfo = {};
=====================================
toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
=====================================
@@ -12,6 +12,11 @@ import {
const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
+
XPCOMUtils.defineLazyServiceGetters(lazy, {
ProtocolProxyService: [
"@mozilla.org/network/protocol-proxy-service;1",
@@ -19,11 +24,6 @@ XPCOMUtils.defineLazyServiceGetters(lazy, {
],
});
-ChromeUtils.defineESModuleGetters(lazy, {
- TorMonitorTopics: "resource://gre/modules/TorMonitorService.sys.mjs",
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
-});
-
const logger = new ConsoleAPI({
prefix: "TorDomainIsolator",
maxLogLevel: "warn",
@@ -143,7 +143,7 @@ class TorDomainIsolatorImpl {
Services.prefs.addObserver(NON_TOR_PROXY_PREF, this);
Services.obs.addObserver(this, NEW_IDENTITY_TOPIC);
- Services.obs.addObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
+ Services.obs.addObserver(this, lazy.TorProviderTopics.StreamSucceeded);
this.#cleanupIntervalId = setInterval(
this.#clearKnownCircuits.bind(this),
@@ -158,7 +158,7 @@ class TorDomainIsolatorImpl {
uninit() {
Services.prefs.removeObserver(NON_TOR_PROXY_PREF, this);
Services.obs.removeObserver(this, NEW_IDENTITY_TOPIC);
- Services.obs.removeObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
+ Services.obs.removeObserver(this, lazy.TorProviderTopics.StreamSucceeded);
clearInterval(this.#cleanupIntervalId);
this.#cleanupIntervalId = null;
this.clearIsolation();
@@ -257,12 +257,12 @@ class TorDomainIsolatorImpl {
);
this.clearIsolation();
try {
- await lazy.TorProtocolService.newnym();
+ await lazy.TorProviderBuilder.build().newnym();
} catch (e) {
logger.error("Could not send the newnym command", e);
// TODO: What UX to use here? See tor-browser#41708
}
- } else if (topic === lazy.TorMonitorTopics.StreamSucceeded) {
+ } else if (topic === lazy.TorProviderTopics.StreamSucceeded) {
const { username, password, circuit } = subject.wrappedJSObject;
this.#updateCircuit(username, password, circuit);
}
@@ -553,7 +553,7 @@ class TorDomainIsolatorImpl {
data = await Promise.all(
circuit.map(fingerprint =>
- lazy.TorProtocolService.getNodeInfo(fingerprint)
+ lazy.TorProviderBuilder.build().getNodeInfo(fingerprint)
)
);
this.#knownCircuits.set(id, data);
=====================================
toolkit/components/tor-launcher/TorMonitorService.sys.mjs deleted
=====================================
@@ -1,42 +0,0 @@
-// Copyright (c) 2022, The Tor Project, Inc.
-
-import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
-
-const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
-});
-
-export const TorMonitorTopics = Object.freeze({
- BridgeChanged: TorProviderTopics.BridgeChanged,
- StreamSucceeded: TorProviderTopics.StreamSucceeded,
-});
-
-/**
- * This service monitors an existing Tor instance, or starts one, if needed, and
- * then starts monitoring it.
- *
- * This is the service which should be queried to know information about the
- * status of the bootstrap, the logs, etc...
- */
-export const TorMonitorService = {
- get currentBridge() {
- return lazy.TorProtocolService.currentBridge;
- },
-
- get ownsTorDaemon() {
- return lazy.TorProtocolService.ownsTorDaemon;
- },
-
- get isRunning() {
- return lazy.TorProtocolService.isRunning;
- },
-
- get isBootstrapDone() {
- return lazy.TorProtocolService.isBootstrapDone;
- },
-
- getLog() {
- return lazy.TorProtocolService.getLog();
- },
-};
=====================================
toolkit/components/tor-launcher/TorParsers.sys.mjs
=====================================
@@ -269,11 +269,14 @@ export const TorParsers = Object.freeze({
},
parseBridgeLine(line) {
+ if (!line) {
+ return null;
+ }
const re =
/\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
const match = re.exec(line);
if (!match) {
- throw new Error("Invalid bridge line.");
+ throw new Error(`Invalid bridge line: ${line}.`);
}
const bridge = match.groups;
if (!bridge.transport) {
=====================================
toolkit/components/tor-launcher/TorProtocolService.sys.mjs → toolkit/components/tor-launcher/TorProvider.sys.mjs
=====================================
@@ -1,4 +1,6 @@
-// Copyright (c) 2021, The Tor Project, Inc.
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
@@ -11,7 +13,6 @@ import {
import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
const lazy = {};
-
ChromeUtils.defineESModuleGetters(lazy, {
controller: "resource://gre/modules/TorControlPort.sys.mjs",
configureControlPortModule: "resource://gre/modules/TorControlPort.sys.mjs",
@@ -21,7 +22,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
const logger = new ConsoleAPI({
maxLogLevel: "warn",
- prefix: "TorProtocolService",
+ maxLogLevelPref: "browser.tor_provider.log_level",
+ prefix: "TorProvider",
});
/**
@@ -70,7 +72,7 @@ const ControlConnTimings = Object.freeze({
* It can start a new tor instance, or connect to an existing one.
* In the former case, it also takes its ownership by default.
*/
-class TorProvider {
+export class TorProvider {
#inited = false;
// Maintain a map of tor settings set by Tor Browser so that we don't
@@ -85,7 +87,6 @@ class TorProvider {
#SOCKSPortInfo = null; // An object that contains ipcFile, host, port.
#controlConnection = null; // This is cached and reused.
- #connectionQueue = [];
// Public methods
@@ -123,39 +124,34 @@ class TorProvider {
// takes a Map containing tor settings
// throws on error
async writeSettings(aSettingsObj) {
+ const entries =
+ aSettingsObj instanceof Map
+ ? Array.from(aSettingsObj.entries())
+ : Object.entries(aSettingsObj);
// only write settings that have changed
- const newSettings = Array.from(aSettingsObj).filter(([setting, value]) => {
- // make sure we have valid data here
- this.#assertValidSetting(setting, value);
-
+ const newSettings = entries.filter(([setting, value]) => {
if (!this.#settingsCache.has(setting)) {
// no cached setting, so write
return true;
}
const cachedValue = this.#settingsCache.get(setting);
- if (value === cachedValue) {
- return false;
- } else if (Array.isArray(value) && Array.isArray(cachedValue)) {
- // compare arrays member-wise
- if (value.length !== cachedValue.length) {
- return true;
- }
- for (let i = 0; i < value.length; i++) {
- if (value[i] !== cachedValue[i]) {
- return true;
- }
- }
- return false;
+ // Arrays are the only special case for which === could fail.
+ // The other values we accept (strings, booleans, numbers, null and
+ // undefined) work correctly with ===.
+ if (Array.isArray(value) && Array.isArray(cachedValue)) {
+ return (
+ value.length !== cachedValue.length ||
+ value.some((val, idx) => val !== cachedValue[idx])
+ );
}
- // some other different values
- return true;
+ return value !== cachedValue;
});
// only write if new setting to save
if (newSettings.length) {
- const settingsObject = Object.fromEntries(newSettings);
- await this.setConfWithReply(settingsObject);
+ const conn = await this.#getConnection();
+ await conn.setConf(Object.fromEntries(newSettings));
// save settings to cache after successfully writing to Tor
for (const [setting, value] of newSettings) {
@@ -164,23 +160,15 @@ class TorProvider {
}
}
- async readStringArraySetting(aSetting) {
- const value = await this.#readSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
// writes current tor settings to disk
async flushSettings() {
- await this.sendCommand("SAVECONF");
+ const conn = await this.#getConnection();
+ await conn.flushSettings();
}
async connect() {
- const kTorConfKeyDisableNetwork = "DisableNetwork";
- const settings = {};
- settings[kTorConfKeyDisableNetwork] = false;
- await this.setConfWithReply(settings);
- await this.sendCommand("SAVECONF");
+ const conn = await this.#getConnection();
+ await conn.setNetworkEnabled(true);
this.clearBootstrapError();
this.retrieveBootstrapStatus();
}
@@ -188,12 +176,8 @@ class TorProvider {
async stopBootstrap() {
// Tell tor to disable use of the network; this should stop the bootstrap
// process.
- try {
- const settings = { DisableNetwork: true };
- await this.setConfWithReply(settings);
- } catch (e) {
- logger.error("Error stopping bootstrap", e);
- }
+ const conn = await this.#getConnection();
+ await conn.setNetworkEnabled(false);
// We are not interested in waiting for this, nor in **catching its error**,
// so we do not await this. We just want to be notified when the bootstrap
// status is actually updated through observers.
@@ -201,28 +185,31 @@ class TorProvider {
}
async newnym() {
- return this.sendCommand("SIGNAL NEWNYM");
+ const conn = await this.#getConnection();
+ await conn.newnym();
}
// Ask tor which ports it is listening to for SOCKS connections.
// At the moment this is used only in TorCheckService.
async getSocksListeners() {
- const cmd = "GETINFO";
- const keyword = "net/listeners/socks";
- const response = await this.sendCommand(cmd, keyword);
- return TorParsers.parseReply(cmd, keyword, response);
+ const conn = await this.#getConnection();
+ return conn.getSocksListeners();
}
async getBridges() {
+ const conn = await this.#getConnection();
// Ideally, we would not need this function, because we should be the one
// setting them with TorSettings. However, TorSettings is not notified of
// change of settings. So, asking tor directly with the control connection
// is the most reliable way of getting the configured bridges, at the
// moment. Also, we are using this for the circuit display, which should
// work also when we are not configuring the tor daemon, but just using it.
- return this.#withConnection(conn => {
- return conn.getConf("bridge");
- });
+ return conn.getBridges();
+ }
+
+ async getPluggableTransports() {
+ const conn = await this.#getConnection();
+ return conn.getPluggableTransports();
}
/**
@@ -232,68 +219,55 @@ class TorProvider {
* @returns {Promise<NodeData>}
*/
async getNodeInfo(id) {
- return this.#withConnection(async conn => {
- const node = {
- fingerprint: id,
- ipAddrs: [],
- bridgeType: null,
- regionCode: null,
- };
- const bridge = (await conn.getConf("bridge"))?.find(
- foundBridge => foundBridge.ID?.toUpperCase() === id.toUpperCase()
- );
- const addrRe = /^\[?([^\]]+)\]?:\d+$/;
- if (bridge) {
- node.bridgeType = bridge.type ?? "";
- // Attempt to get an IP address from bridge address string.
- const ip = bridge.address.match(addrRe)?.[1];
- if (ip && !ip.startsWith("0.")) {
- node.ipAddrs.push(ip);
- }
- } else {
- // Either dealing with a relay, or a bridge whose fingerprint is not
- // saved in torrc.
- const info = await conn.getInfo(`ns/id/${id}`);
- if (info.IP && !info.IP.startsWith("0.")) {
- node.ipAddrs.push(info.IP);
- }
- const ip6 = info.IPv6?.match(addrRe)?.[1];
- if (ip6) {
- node.ipAddrs.push(ip6);
- }
+ const conn = await this.#getConnection();
+ const node = {
+ fingerprint: id,
+ ipAddrs: [],
+ bridgeType: null,
+ regionCode: null,
+ };
+ const bridge = (await conn.getBridges())?.find(
+ foundBridge => foundBridge.id?.toUpperCase() === id.toUpperCase()
+ );
+ if (bridge) {
+ node.bridgeType = bridge.transport ?? "";
+ // Attempt to get an IP address from bridge address string.
+ const ip = bridge.addr.match(/^\[?([^\]]+)\]?:\d+$/)?.[1];
+ if (ip && !ip.startsWith("0.")) {
+ node.ipAddrs.push(ip);
}
- if (node.ipAddrs.length) {
- // Get the country code for the node's IP address.
- let regionCode;
- try {
- // Expect a 2-letter ISO3166-1 code, which should also be a valid
- // BCP47 Region subtag.
- regionCode = await conn.getInfo("ip-to-country/" + node.ipAddrs[0]);
- } catch {}
+ } else {
+ node.ipAddrs = await conn.getNodeAddresses(id);
+ }
+ if (node.ipAddrs.length) {
+ // Get the country code for the node's IP address.
+ try {
+ // Expect a 2-letter ISO3166-1 code, which should also be a valid
+ // BCP47 Region subtag.
+ const regionCode = await conn.getIPCountry(node.ipAddrs[0]);
if (regionCode && regionCode !== "??") {
node.regionCode = regionCode.toUpperCase();
}
+ } catch (e) {
+ logger.warn(`Cannot get a country for IP ${node.ipAddrs[0]}`, e);
}
- return node;
- });
+ }
+ return node;
}
- async onionAuthAdd(hsAddress, b64PrivateKey, isPermanent) {
- return this.#withConnection(conn => {
- return conn.onionAuthAdd(hsAddress, b64PrivateKey, isPermanent);
- });
+ async onionAuthAdd(address, b64PrivateKey, isPermanent) {
+ const conn = await this.#getConnection();
+ return conn.onionAuthAdd(address, b64PrivateKey, isPermanent);
}
- async onionAuthRemove(hsAddress) {
- return this.#withConnection(conn => {
- return conn.onionAuthRemove(hsAddress);
- });
+ async onionAuthRemove(address) {
+ const conn = await this.#getConnection();
+ return conn.onionAuthRemove(address);
}
async onionAuthViewKeys() {
- return this.#withConnection(conn => {
- return conn.onionAuthViewKeys();
- });
+ const conn = await this.#getConnection();
+ return conn.onionAuthViewKeys();
}
// TODO: transform the following 4 functions in getters.
@@ -333,106 +307,6 @@ class TorProvider {
return this.#SOCKSPortInfo;
}
- // Public, but called only internally
-
- // Executes a command on the control port.
- // Return a reply object or null if a fatal error occurs.
- async sendCommand(cmd, args) {
- const maxTimeout = 1000;
- let leftConnAttempts = 5;
- let timeout = 250;
- let reply;
- while (leftConnAttempts-- > 0) {
- const response = await this.#trySend(cmd, args, leftConnAttempts === 0);
- if (response.connected) {
- reply = response.reply;
- break;
- }
- // We failed to acquire the controller after multiple attempts.
- // Try again after some time.
- logger.warn(
- "sendCommand: Acquiring control connection failed, trying again later.",
- cmd,
- args
- );
- await new Promise(resolve => setTimeout(() => resolve(), timeout));
- timeout = Math.min(2 * timeout, maxTimeout);
- }
-
- // We sent the command, but we still got an empty response.
- // Something must be busted elsewhere.
- if (!reply) {
- throw new Error(`${cmd} sent an empty response`);
- }
-
- // TODO: Move the parsing of the reply to the controller, because anyone
- // calling sendCommand on it actually wants a parsed reply.
-
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- if (reply?.lineArray) {
- throw new Error(reply.lineArray.join("\n"));
- }
- throw new Error(`${cmd} failed with code ${reply.statusCode}`);
- }
-
- return reply;
- }
-
- // Perform a SETCONF command.
- // aSettingsObj should be a JavaScript object with keys (property values)
- // that correspond to tor config. keys. The value associated with each
- // key should be a simple string, a string array, or a Boolean value.
- // If an associated value is undefined or null, a key with no value is
- // passed in the SETCONF command.
- // Throws in case of error, or returns a reply object.
- async setConfWithReply(settings) {
- if (!settings) {
- throw new Error("Empty settings object");
- }
- const args = Object.entries(settings)
- .map(([key, val]) => {
- if (val === undefined || val === null) {
- return key;
- }
- const valType = typeof val;
- let rv = `${key}=`;
- if (valType === "boolean") {
- rv += val ? "1" : "0";
- } else if (Array.isArray(val)) {
- rv += val.map(TorParsers.escapeString).join(` ${key}=`);
- } else if (valType === "string") {
- rv += TorParsers.escapeString(val);
- } else {
- logger.error(`Got unsupported type for ${key}`, val);
- throw new Error(`Unsupported type ${valType} (key ${key})`);
- }
- return rv;
- })
- .filter(arg => arg);
- if (!args.length) {
- throw new Error("No settings to set");
- }
-
- await this.sendCommand("SETCONF", args.join(" "));
- }
-
- // Public, never called?
-
- async readBoolSetting(aSetting) {
- let value = await this.#readBoolSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
- async readStringSetting(aSetting) {
- let value = await this.#readStringSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
- // Private
-
async #setSockets() {
try {
const isWindows = TorLauncherUtil.isWindows;
@@ -511,167 +385,24 @@ class TorProvider {
}
}
- #assertValidSettingKey(aSetting) {
- // ensure the 'key' is a string
- if (typeof aSetting !== "string") {
- throw new Error(
- `Expected setting of type string but received ${typeof aSetting}`
- );
- }
- }
-
- #assertValidSetting(aSetting, aValue) {
- this.#assertValidSettingKey(aSetting);
- switch (typeof aValue) {
- case "boolean":
- case "string":
- return;
- case "object":
- if (aValue === null) {
- return;
- } else if (Array.isArray(aValue)) {
- for (const element of aValue) {
- if (typeof element !== "string") {
- throw new Error(
- `Setting '${aSetting}' array contains value of invalid type '${typeof element}'`
- );
- }
- }
- return;
- }
- // fall through
- default:
- throw new Error(
- `Invalid object type received for setting '${aSetting}'`
- );
- }
- }
-
- // Perform a GETCONF command.
- async #readSetting(aSetting) {
- this.#assertValidSettingKey(aSetting);
-
- const cmd = "GETCONF";
- let reply = await this.sendCommand(cmd, aSetting);
- return TorParsers.parseReply(cmd, aSetting, reply);
- }
-
- async #readStringSetting(aSetting) {
- let lineArray = await this.#readSetting(aSetting);
- if (lineArray.length !== 1) {
- throw new Error(
- `Expected an array with length 1 but received array of length ${lineArray.length}`
- );
- }
- return lineArray[0];
- }
-
- async #readBoolSetting(aSetting) {
- const value = this.#readStringSetting(aSetting);
- switch (value) {
- case "0":
- return false;
- case "1":
- return true;
- default:
- throw new Error(`Expected boolean (1 or 0) but received '${value}'`);
- }
- }
-
- async #trySend(cmd, args, rethrow) {
- let connected = false;
- let reply;
- let leftAttempts = 2;
- while (leftAttempts-- > 0) {
- let conn;
- try {
- conn = await this.#getConnection();
- } catch (e) {
- logger.error("Cannot get a connection to the control port", e);
- if (leftAttempts == 0 && rethrow) {
- throw e;
- }
- }
- if (!conn) {
- continue;
- }
- // If we _ever_ got a connection, the caller should not try again
- connected = true;
- try {
- reply = await conn.sendCommand(cmd + (args ? " " + args : ""));
- if (reply) {
- // Return for reuse.
- this.#returnConnection();
- } else {
- // Connection is bad.
- logger.warn(
- "sendCommand returned an empty response, taking the connection as broken and closing it."
- );
- this.#closeConnection();
- }
- } catch (e) {
- logger.error(`Cannot send the command ${cmd}`, e);
- this.#closeConnection();
- if (leftAttempts == 0 && rethrow) {
- throw e;
- }
- }
- }
- return { connected, reply };
- }
-
- // Opens an authenticated connection, sets it to this.#controlConnection, and
- // return it.
async #getConnection() {
- if (!this.#controlConnection) {
+ if (!this.#controlConnection?.isOpen) {
this.#controlConnection = await lazy.controller();
}
- if (this.#controlConnection.inUse) {
- await new Promise((resolve, reject) =>
- this.#connectionQueue.push({ resolve, reject })
- );
- } else {
- this.#controlConnection.inUse = true;
- }
return this.#controlConnection;
}
- #returnConnection() {
- if (this.#connectionQueue.length) {
- this.#connectionQueue.shift().resolve();
- } else {
- this.#controlConnection.inUse = false;
- }
- }
-
- async #withConnection(func) {
- // TODO: Make more robust?
- const conn = await this.#getConnection();
- try {
- return await func(conn);
- } finally {
- this.#returnConnection();
- }
- }
-
- // If aConn is omitted, the cached connection is closed.
#closeConnection() {
if (this.#controlConnection) {
logger.info("Closing the control connection");
this.#controlConnection.close();
this.#controlConnection = null;
}
- for (const promise of this.#connectionQueue) {
- promise.reject("Connection closed");
- }
- this.#connectionQueue = [];
}
async #reconnect() {
this.#closeConnection();
- const conn = await this.#getConnection();
- logger.debug("Reconnected to the control port.");
- this.#returnConnection(conn);
+ await this.#getConnection();
}
async #readAuthenticationCookie(aPath) {
@@ -777,8 +508,9 @@ class TorProvider {
if (this.ownsTorDaemon) {
// When we own the tor daemon, we listen to more events, that are used
// for about:torconnect or for showing the logs in the settings page.
- this._eventHandlers.set("STATUS_CLIENT", (_eventType, lines) =>
- this._processBootstrapStatus(lines[0], false)
+ this._eventHandlers.set(
+ "STATUS_CLIENT",
+ this._processStatusClient.bind(this)
);
this._eventHandlers.set("NOTICE", this._processLog.bind(this));
this._eventHandlers.set("WARN", this._processLog.bind(this));
@@ -809,23 +541,10 @@ class TorProvider {
throw new Error("Event monitor connection not available");
}
- // TODO: Unify with TorProtocolService.sendCommand and put everything in the
- // reviewed torbutton replacement.
- const cmd = "GETINFO";
- const key = "status/bootstrap-phase";
- let reply = await this._connection.sendCommand(`${cmd} ${key}`);
-
- // A typical reply looks like:
- // 250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
- // 250 OK
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- throw new Error(`${cmd} failed`);
- }
- reply = TorParsers.parseReply(cmd, key, reply);
- if (reply.length) {
- this._processBootstrapStatus(reply[0], true);
- }
+ this._processBootstrapStatus(
+ await this._connection.getBootstrapPhase(),
+ true
+ );
}
// Returns captured log message as a text string (one message per line).
@@ -1058,37 +777,32 @@ class TorProvider {
_monitorEvent(type, callback) {
logger.info(`Watching events of type ${type}.`);
let replyObj = {};
- this._connection.watchEvent(
- type,
- null,
- line => {
- if (!line) {
- return;
- }
- logger.debug("Event response: ", line);
- const isComplete = TorParsers.parseReplyLine(line, replyObj);
- if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
- return;
- }
- const reply = replyObj;
- replyObj = {};
- if (reply.statusCode !== TorStatuses.EventNotification) {
- logger.error("Unexpected event status code:", reply.statusCode);
- return;
- }
- if (!reply.lineArray[0].startsWith(`${type} `)) {
- logger.error("Wrong format for the first line:", reply.lineArray[0]);
- return;
- }
- reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
- try {
- callback(type, reply.lineArray);
- } catch (e) {
- logger.error("Exception while handling an event", reply, e);
- }
- },
- true
- );
+ this._connection.watchEvent(type, line => {
+ if (!line) {
+ return;
+ }
+ logger.debug("Event response: ", line);
+ const isComplete = TorParsers.parseReplyLine(line, replyObj);
+ if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
+ return;
+ }
+ const reply = replyObj;
+ replyObj = {};
+ if (reply.statusCode !== TorStatuses.EventNotification) {
+ logger.error("Unexpected event status code:", reply.statusCode);
+ return;
+ }
+ if (!reply.lineArray[0].startsWith(`${type} `)) {
+ logger.error("Wrong format for the first line:", reply.lineArray[0]);
+ return;
+ }
+ reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
+ try {
+ callback(type, reply.lineArray);
+ } catch (e) {
+ logger.error("Exception while handling an event", reply, e);
+ }
+ });
}
_processLog(type, lines) {
@@ -1116,15 +830,12 @@ class TorProvider {
// to TorBootstrapStatus observers.
// If aSuppressErrors is true, errors are ignored. This is used when we
// are handling the response to a "GETINFO status/bootstrap-phase" command.
- _processBootstrapStatus(aStatusMsg, aSuppressErrors) {
- const statusObj = TorParsers.parseBootstrapStatus(aStatusMsg);
- if (!statusObj) {
- return;
- }
-
+ _processBootstrapStatus(statusObj, suppressErrors) {
// Notify observers
- statusObj.wrappedJSObject = statusObj;
- Services.obs.notifyObservers(statusObj, "TorBootstrapStatus");
+ Services.obs.notifyObservers(
+ { wrappedJSObject: statusObj },
+ "TorBootstrapStatus"
+ );
if (statusObj.PROGRESS === 100) {
this._isBootstrapDone = true;
@@ -1141,7 +852,7 @@ class TorProvider {
if (
statusObj.TYPE === "WARN" &&
statusObj.RECOMMENDATION !== "ignore" &&
- !aSuppressErrors
+ !suppressErrors
) {
this._notifyBootstrapError(statusObj);
}
@@ -1184,6 +895,15 @@ class TorProvider {
}
}
+ _processStatusClient(_type, lines) {
+ const statusObj = TorParsers.parseBootstrapStatus(lines[0]);
+ if (!statusObj) {
+ // No `BOOTSTRAP` in the line
+ return;
+ }
+ this._processBootstrapStatus(statusObj, false);
+ }
+
async _processCircEvent(_type, lines) {
const builtEvent =
/^(?<CircuitID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(?:,?\$[0-9a-fA-F]{40}(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
@@ -1295,7 +1015,3 @@ class TorProvider {
this.clearBootstrapError();
}
}
-
-// TODO: Stop defining TorProtocolService, make the builder instance the
-// TorProvider.
-export const TorProtocolService = new TorProvider();
=====================================
toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
=====================================
@@ -4,7 +4,7 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
+ TorProvider: "resource://gre/modules/TorProvider.sys.mjs",
});
export const TorProviderTopics = Object.freeze({
@@ -19,16 +19,25 @@ export const TorProviderTopics = Object.freeze({
});
export class TorProviderBuilder {
+ static #provider = null;
+
static async init() {
- await lazy.TorProtocolService.init();
+ const provider = new lazy.TorProvider();
+ await provider.init();
+ // Assign it only when initialization succeeds.
+ TorProviderBuilder.#provider = provider;
}
static uninit() {
- lazy.TorProtocolService.uninit();
+ TorProviderBuilder.#provider.uninit();
+ TorProviderBuilder.#provider = null;
}
// TODO: Switch to an async build?
static build() {
- return lazy.TorProtocolService;
+ if (!TorProviderBuilder.#provider) {
+ throw new Error("TorProviderBuilder has not been initialized yet.");
+ }
+ return TorProviderBuilder.#provider;
}
}
=====================================
toolkit/components/tor-launcher/TorStartupService.sys.mjs
=====================================
@@ -3,22 +3,13 @@ const lazy = {};
// We will use the modules only when the profile is loaded, so prefer lazy
// loading
ChromeUtils.defineESModuleGetters(lazy, {
+ TorConnect: "resource:///modules/TorConnect.sys.mjs",
TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorSettings: "resource:///modules/TorSettings.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorConnect",
- "resource:///modules/TorConnect.jsm"
-);
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorSettings",
- "resource:///modules/TorSettings.jsm"
-);
-
/* Browser observer topis */
const BrowserTopics = Object.freeze({
ProfileAfterChange: "profile-after-change",
=====================================
toolkit/components/tor-launcher/moz.build
=====================================
@@ -3,10 +3,9 @@ EXTRA_JS_MODULES += [
"TorControlPort.sys.mjs",
"TorDomainIsolator.sys.mjs",
"TorLauncherUtil.sys.mjs",
- "TorMonitorService.sys.mjs",
"TorParsers.sys.mjs",
"TorProcess.sys.mjs",
- "TorProtocolService.sys.mjs",
+ "TorProvider.sys.mjs",
"TorProviderBuilder.sys.mjs",
"TorStartupService.sys.mjs",
]
=====================================
toolkit/mozapps/update/UpdateService.sys.mjs
=====================================
@@ -23,18 +23,13 @@ ChromeUtils.defineESModuleGetters(lazy, {
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
CertUtils: "resource://gre/modules/CertUtils.sys.mjs",
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
ctypes: "resource://gre/modules/ctypes.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorMonitorService",
- "resource://gre/modules/TorMonitorService.jsm"
-);
-
XPCOMUtils.defineLazyServiceGetter(
lazy,
"AUS",
@@ -394,10 +389,11 @@ XPCOMUtils.defineLazyGetter(
);
function _shouldRegisterBootstrapObserver(errorCode) {
+ const provider = lazy.TorProviderBuilder.build();
return (
errorCode == PROXY_SERVER_CONNECTION_REFUSED &&
- !lazy.TorMonitorService.isBootstrapDone &&
- lazy.TorMonitorService.ownsTorDaemon
+ !provider.isBootstrapDone &&
+ provider.ownsTorDaemon
);
}
@@ -5833,10 +5829,7 @@ Downloader.prototype = {
// we choose to compute these hashes.
hash = hash.finish(false);
digest = Array.from(hash, (c, i) =>
- hash
- .charCodeAt(i)
- .toString(16)
- .padStart(2, "0")
+ hash.charCodeAt(i).toString(16).padStart(2, "0")
).join("");
} catch (e) {
LOG(
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/ab8a15…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/ab8a15…
You're receiving this email because of your account on gitlab.torproject.org.
1
0