mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 21:12:04 -08:00
Load default addons through launcher
- Default addons are now downloaded through the launcher to avoid redownloading when new profiles are created - Updated README
This commit is contained in:
parent
ad87cec317
commit
8aadd3e1f2
9 changed files with 247 additions and 37 deletions
43
README.md
43
README.md
|
|
@ -17,8 +17,8 @@ Camoufox aims to be a minimalistic browser for robust fingerprint injection & an
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Fingerprint injection (override properties of `navigator`, `window`, `screen`, etc) ✅
|
- Fingerprint injection (override properties of `navigator`, `window`, `screen`, etc) ✅
|
||||||
- Patches to avoid Playwright detection ✅
|
- Patches to avoid bot detection ✅
|
||||||
- Custom Playwright Juggler implementation with minimal leaks ✅
|
- Custom Playwright Juggler implementation for the latest Firefox ✅
|
||||||
- Font spoofing & anti-fingerprinting ✅
|
- Font spoofing & anti-fingerprinting ✅
|
||||||
- Patches from LibreWolf & Ghostery to remove Mozilla services ✅
|
- Patches from LibreWolf & Ghostery to remove Mozilla services ✅
|
||||||
- Optimized for memory and speed ✅
|
- Optimized for memory and speed ✅
|
||||||
|
|
@ -34,6 +34,7 @@ Camoufox is built on top of Firefox/Juggler instead of Chromium because:
|
||||||
|
|
||||||
### What's planned?
|
### What's planned?
|
||||||
|
|
||||||
|
- Continue research on potential leaks
|
||||||
- Built in TLS fingerprinting protection using [Hazetunnel](https://github.com/daijro/hazetunnel)
|
- Built in TLS fingerprinting protection using [Hazetunnel](https://github.com/daijro/hazetunnel)
|
||||||
- Create integration tests
|
- Create integration tests
|
||||||
- Chromium port (long-term)
|
- Chromium port (long-term)
|
||||||
|
|
@ -198,6 +199,25 @@ Spoofing document.body has been implemented, but it is more advicable to set `wi
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
HTTP Headers
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
Camoufox can override the following network headers:
|
||||||
|
|
||||||
|
| Property | Status |
|
||||||
|
| ----------------------- | ------ |
|
||||||
|
| headers.User-Agent | ✅ |
|
||||||
|
| headers.Accept-Language | ✅ |
|
||||||
|
| headers.Accept-Encoding | ✅ |
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
|
||||||
|
- If `headers.User-Agent` is not set, it will fall back to `navigator.userAgent`.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>
|
<summary>
|
||||||
|
|
@ -214,6 +234,14 @@ Example:
|
||||||
|
|
||||||
Camoufox will automatically download and use the latest uBlock Origin with custom privacy/adblock filters, and B.P.C. by default to help with scraping.
|
Camoufox will automatically download and use the latest uBlock Origin with custom privacy/adblock filters, and B.P.C. by default to help with scraping.
|
||||||
|
|
||||||
|
You can also exclude default addons with the `--exclude-addons` flag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./launcher --exclude-addons '["uBO", "BPC"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
@ -285,6 +313,7 @@ Miscellaneous (WebGl spoofing, battery status, etc)
|
||||||
- Added B.P.C.
|
- Added B.P.C.
|
||||||
- Addons are not allowed to open tabs
|
- Addons are not allowed to open tabs
|
||||||
- Addons are automatically enabled in Private Browsing mode
|
- Addons are automatically enabled in Private Browsing mode
|
||||||
|
- Addons are automatically pinned to the toolbar
|
||||||
|
|
||||||
## Stealth Performance
|
## Stealth Performance
|
||||||
|
|
||||||
|
|
@ -319,7 +348,7 @@ Camoufox performs well against every major WAF I've tested. (Test sites from [Bo
|
||||||
| [**BrowserScan**](https://browserscan.net/) | ✔️ |
|
| [**BrowserScan**](https://browserscan.net/) | ✔️ |
|
||||||
| [**Bet365**](https://www.bet365.com/#/AC/B1/C1/D1002/E79147586/G40/) | ✔️ |
|
| [**Bet365**](https://www.bet365.com/#/AC/B1/C1/D1002/E79147586/G40/) | ✔️ |
|
||||||
|
|
||||||
Camoufox does **not** fully support injecting Chromium fingerprints. Some websites (such as Cloudflare [Interstitial](https://nopecha.com/demo/cloudflare)) look for the Gecko webdriver underneath.
|
Camoufox does **not** fully support injecting Chromium fingerprints. Some WAFs (such as [Interstitial](https://nopecha.com/demo/cloudflare)) look for the Gecko webdriver underneath.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -334,11 +363,11 @@ graph TD
|
||||||
FFSRC[Firefox Source] -->|make fetch| REPO
|
FFSRC[Firefox Source] -->|make fetch| REPO
|
||||||
|
|
||||||
subgraph REPO[Camoufox Repository]
|
subgraph REPO[Camoufox Repository]
|
||||||
MASKING[Camoufox masking module]
|
PATCHES[Fingerprint masking patches]
|
||||||
PATCHES[Debloat/optimizations]
|
|
||||||
ADDONS[uBlock & B.P.C.]
|
ADDONS[uBlock & B.P.C.]
|
||||||
SYSTEM_FONTS[Win, Mac, Linux System fonts]
|
DEBLOAT[Debloat/optimizations]
|
||||||
JUGGLER[Playwright Juggler]
|
SYSTEM_FONTS[Win, Mac, Linux fonts]
|
||||||
|
JUGGLER[Patched Juggler]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Local
|
subgraph Local
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Default addons to extract to /addons
|
||||||
|
var DefaultAddons = map[string]string{
|
||||||
|
"uBO": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi",
|
||||||
|
"BPC": "https://gitflic.ru/project/magnolia1234/bpc_uploads/blob/raw?file=bypass_paywalls_clean-latest.xpi",
|
||||||
|
}
|
||||||
|
|
||||||
// Exclude lines from output
|
// Exclude lines from output
|
||||||
var ExclusionRules = []string{
|
var ExclusionRules = []string{
|
||||||
// Ignore search related warnings
|
// Ignore search related warnings
|
||||||
|
|
@ -17,6 +23,7 @@ var ExclusionRules = []string{
|
||||||
"^console\\.error:\\ \\(\\{\\}\\)$",
|
"^console\\.error:\\ \\(\\{\\}\\)$",
|
||||||
"^console\\.error:\\ \"Could\\ not\\ record\\ event:\\ \"\\ \\(\\{\\}\\)$",
|
"^console\\.error:\\ \"Could\\ not\\ record\\ event:\\ \"\\ \\(\\{\\}\\)$",
|
||||||
"^\\s*?$",
|
"^\\s*?$",
|
||||||
|
"Rejected by Camoufox\\.$",
|
||||||
// Ignore missing urlbar provider errors
|
// Ignore missing urlbar provider errors
|
||||||
"^JavaScript\\ error:\\ resource:///modules/UrlbarProvider",
|
"^JavaScript\\ error:\\ resource:///modules/UrlbarProvider",
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,12 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getExecutableName() string {
|
func getExecutableName() string {
|
||||||
|
|
@ -68,23 +66,6 @@ func filterOutput(r io.Reader, w io.Writer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryLoadAddons(debugPortInt int, addonsList []string) {
|
|
||||||
// Wait for the server to be open
|
|
||||||
for {
|
|
||||||
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", debugPortInt))
|
|
||||||
if err == nil {
|
|
||||||
conn.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load addons
|
|
||||||
for _, addon := range addonsList {
|
|
||||||
loadFirefoxAddon(debugPortInt, addon)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run Camoufox
|
// Run Camoufox
|
||||||
func runCamoufox(execName string, args []string, addonsList []string) {
|
func runCamoufox(execName string, args []string, addonsList []string) {
|
||||||
// If addons are specified, get the debug port
|
// If addons are specified, get the debug port
|
||||||
|
|
@ -8,8 +8,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Gets the debug port from the args, or creates a new one if not provided
|
||||||
func getDebugPort(args *[]string) int {
|
func getDebugPort(args *[]string) int {
|
||||||
debugPort := parseArgs("-start-debugger-server", "", args, false)
|
debugPort := parseArgs("-start-debugger-server", "", args, false)
|
||||||
|
|
||||||
|
|
@ -30,8 +32,8 @@ func getDebugPort(args *[]string) int {
|
||||||
return debugPortInt
|
return debugPortInt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Confirm paths are valid
|
||||||
func confirmPaths(paths []string) {
|
func confirmPaths(paths []string) {
|
||||||
// Confirm paths are valid
|
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
if _, err := os.Stat(path); err != nil {
|
if _, err := os.Stat(path); err != nil {
|
||||||
fmt.Printf("Error: %s is not a valid addon path.\n", path)
|
fmt.Printf("Error: %s is not a valid addon path.\n", path)
|
||||||
|
|
@ -40,8 +42,8 @@ func confirmPaths(paths []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate an open port
|
||||||
func getOpenPort() int {
|
func getOpenPort() int {
|
||||||
// Generate an open port
|
|
||||||
ln, err := net.Listen("tcp", ":0") // listen on a random port
|
ln, err := net.Listen("tcp", ":0") // listen on a random port
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -52,6 +54,24 @@ func getOpenPort() int {
|
||||||
return addr.Port
|
return addr.Port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Waits for the server to start, then loads the addons
|
||||||
|
func tryLoadAddons(debugPortInt int, addonsList []string) {
|
||||||
|
// Wait for the server to be open
|
||||||
|
for {
|
||||||
|
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", debugPortInt))
|
||||||
|
if err == nil {
|
||||||
|
conn.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load addons
|
||||||
|
for _, addon := range addonsList {
|
||||||
|
go loadFirefoxAddon(debugPortInt, addon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Firefox addon loader
|
// Firefox addon loader
|
||||||
// Ported from this Nodejs implementation:
|
// Ported from this Nodejs implementation:
|
||||||
// https://github.com/microsoft/playwright/issues/7297#issuecomment-1211763085
|
// https://github.com/microsoft/playwright/issues/7297#issuecomment-1211763085
|
||||||
|
|
@ -17,11 +17,16 @@ func main() {
|
||||||
|
|
||||||
configPath := parseArgs("--config", "{}", &args, true)
|
configPath := parseArgs("--config", "{}", &args, true)
|
||||||
addons := parseArgs("--addons", "[]", &args, true)
|
addons := parseArgs("--addons", "[]", &args, true)
|
||||||
|
excludeAddons := parseArgs("--exclude-addons", "[]", &args, true)
|
||||||
|
|
||||||
|
//*** PARSE CONFIG ***//
|
||||||
|
|
||||||
// Read and parse the config file
|
// Read and parse the config file
|
||||||
var configMap map[string]interface{}
|
var configMap map[string]interface{}
|
||||||
parseJson(configPath, &configMap)
|
parseJson(configPath, &configMap)
|
||||||
|
|
||||||
|
//*** PARSE ADDONS ***//
|
||||||
|
|
||||||
// If addons are passed, handle them
|
// If addons are passed, handle them
|
||||||
var addonsList []string
|
var addonsList []string
|
||||||
parseJson(addons, &addonsList)
|
parseJson(addons, &addonsList)
|
||||||
|
|
@ -29,10 +34,21 @@ func main() {
|
||||||
// Confirm addon paths are valid
|
// Confirm addon paths are valid
|
||||||
confirmPaths(addonsList)
|
confirmPaths(addonsList)
|
||||||
|
|
||||||
userAgentOS := determineUserAgentOS(configMap) // Determine the user agent OS
|
// Add the default addons, excluding the ones specified in --exclude-addons
|
||||||
|
var excludeAddonsList []string
|
||||||
|
parseJson(excludeAddons, &excludeAddonsList)
|
||||||
|
|
||||||
// OS specific font config
|
addDefaultAddons(excludeAddonsList, &addonsList)
|
||||||
|
|
||||||
|
//*** FONTS ***//
|
||||||
|
|
||||||
|
// Determine the target OS
|
||||||
|
userAgentOS := determineUserAgentOS(configMap)
|
||||||
|
// Add OS specific fonts
|
||||||
updateFonts(configMap, userAgentOS)
|
updateFonts(configMap, userAgentOS)
|
||||||
|
|
||||||
|
//*** LAUNCH ***//
|
||||||
|
|
||||||
setEnvironmentVariables(configMap, userAgentOS)
|
setEnvironmentVariables(configMap, userAgentOS)
|
||||||
|
|
||||||
// Run the Camoufox executable
|
// Run the Camoufox executable
|
||||||
|
|
@ -44,6 +60,7 @@ func main() {
|
||||||
runCamoufox(execName, args, addonsList)
|
runCamoufox(execName, args, addonsList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses & removes an argument from the args list
|
||||||
func parseArgs(param string, defaultValue string, args *[]string, removeFromArgs bool) string {
|
func parseArgs(param string, defaultValue string, args *[]string, removeFromArgs bool) string {
|
||||||
for i := 0; i < len(*args); i++ {
|
for i := 0; i < len(*args); i++ {
|
||||||
if (*args)[i] != param {
|
if (*args)[i] != param {
|
||||||
|
|
@ -62,6 +79,7 @@ func parseArgs(param string, defaultValue string, args *[]string, removeFromArgs
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses a JSON string or file into a map
|
||||||
func parseJson(argv string, target interface{}) {
|
func parseJson(argv string, target interface{}) {
|
||||||
// Unmarshal the config input into a map
|
// Unmarshal the config input into a map
|
||||||
var data []byte
|
var data []byte
|
||||||
|
|
@ -84,6 +102,7 @@ func parseJson(argv string, target interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determines the target OS from the user agent string if provided
|
||||||
func determineUserAgentOS(configMap map[string]interface{}) string {
|
func determineUserAgentOS(configMap map[string]interface{}) string {
|
||||||
// Determine the OS from the user agent string if provided
|
// Determine the OS from the user agent string if provided
|
||||||
defaultOS := normalizeOS(runtime.GOOS)
|
defaultOS := normalizeOS(runtime.GOOS)
|
||||||
|
|
@ -96,8 +115,8 @@ func determineUserAgentOS(configMap map[string]interface{}) string {
|
||||||
return defaultOS
|
return defaultOS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the OS name as {macos, windows, linux}
|
||||||
func normalizeOS(osName string) string {
|
func normalizeOS(osName string) string {
|
||||||
// Get the OS name as {macos, windows, linux}
|
|
||||||
osName = strings.ToLower(osName)
|
osName = strings.ToLower(osName)
|
||||||
switch {
|
switch {
|
||||||
case osName == "darwin" || strings.Contains(osName, "mac"):
|
case osName == "darwin" || strings.Contains(osName, "mac"):
|
||||||
|
|
@ -109,8 +128,8 @@ func normalizeOS(osName string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add fonts associated with the OS to the config map
|
||||||
func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
||||||
// Add fonts associated with the OS to the config map
|
|
||||||
fonts, ok := configMap["fonts"].([]interface{})
|
fonts, ok := configMap["fonts"].([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
fonts = []interface{}{}
|
fonts = []interface{}{}
|
||||||
|
|
@ -129,8 +148,8 @@ func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
||||||
configMap["fonts"] = fonts
|
configMap["fonts"] = fonts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the config map with the fonts and environment variables
|
||||||
func setEnvironmentVariables(configMap map[string]interface{}, userAgentOS string) {
|
func setEnvironmentVariables(configMap map[string]interface{}, userAgentOS string) {
|
||||||
// Update the config map with the fonts and environment variables
|
|
||||||
updatedConfigData, err := json.Marshal(configMap)
|
updatedConfigData, err := json.Marshal(configMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error updating config: %v\n", err)
|
fmt.Printf("Error updating config: %v\n", err)
|
||||||
|
|
|
||||||
158
launcher/xpi-dl.go
Normal file
158
launcher/xpi-dl.go
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Downloads and extracts the default addons
|
||||||
|
func addDefaultAddons(excludeList []string, addonsList *[]string) {
|
||||||
|
// Build a map from DefaultAddons, excluding keys found in excludeAddonsList
|
||||||
|
addonsMap := make(map[string]string)
|
||||||
|
for name, url := range DefaultAddons {
|
||||||
|
if len(excludeList) == 0 || !contains(excludeList, name) {
|
||||||
|
addonsMap[name] = url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download if not already downloaded
|
||||||
|
maybeDownloadAddons(addonsMap, addonsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downloads and extracts the addon
|
||||||
|
func downloadAndExtract(url, extractPath string) error {
|
||||||
|
// Create a temporary file to store the downloaded zip
|
||||||
|
tempFile, err := os.CreateTemp("", "camoufox-addon-*.zip")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create temp file: %w", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tempFile.Name()) // Clean up the temp file when done
|
||||||
|
|
||||||
|
// Download the zip file
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download addon: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Write the body to the temp file
|
||||||
|
_, err = io.Copy(tempFile, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write addon to temp file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file before unzipping
|
||||||
|
tempFile.Close()
|
||||||
|
|
||||||
|
// Extract the zip file
|
||||||
|
err = unzip(tempFile.Name(), extractPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to extract addon: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts the zip file
|
||||||
|
func unzip(src, dest string) error {
|
||||||
|
r, err := zip.OpenReader(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
for _, f := range r.File {
|
||||||
|
fpath := filepath.Join(dest, f.Name)
|
||||||
|
|
||||||
|
if f.FileInfo().IsDir() {
|
||||||
|
os.MkdirAll(fpath, os.ModePerm)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rc, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
outFile.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(outFile, rc)
|
||||||
|
outFile.Close()
|
||||||
|
rc.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a slice contains a string
|
||||||
|
func contains(slice []string, item string) bool {
|
||||||
|
for _, s := range slice {
|
||||||
|
if s == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the absolute path to the target addon location
|
||||||
|
func getAddonPath(addonName string) (string, error) {
|
||||||
|
execPath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error getting executable path: %v\n", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
execDir := filepath.Dir(execPath)
|
||||||
|
|
||||||
|
addonPath := filepath.Join(execDir, "addons", addonName)
|
||||||
|
return addonPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downloads and extracts the addons
|
||||||
|
func maybeDownloadAddons(addons map[string]string, addonsList *[]string) {
|
||||||
|
for addonName, url := range addons {
|
||||||
|
// Get the addon path
|
||||||
|
addonPath, err := getAddonPath(addonName)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error getting addon path: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the addon is already extracted
|
||||||
|
if _, err := os.Stat(addonPath); !os.IsNotExist(err) {
|
||||||
|
// Add the existing addon path to addonsList
|
||||||
|
*addonsList = append(*addonsList, addonPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addon doesn't exist, create directory and download
|
||||||
|
err = os.MkdirAll(addonPath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create directory for %s: %v\n", addonName, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = downloadAndExtract(url, addonPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to download and extract %s: %v\n", addonName, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Successfully downloaded and extracted %s\n", addonName)
|
||||||
|
// Add the new addon directory path to addonsList
|
||||||
|
*addonsList = append(*addonsList, addonPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,10 +35,6 @@
|
||||||
"Exceptions": ["https://localhost/*"]
|
"Exceptions": ["https://localhost/*"]
|
||||||
},
|
},
|
||||||
"Extensions": {
|
"Extensions": {
|
||||||
"Install": [
|
|
||||||
"https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi",
|
|
||||||
"https://github.com/bpc-clone/bpc_updates/releases/download/latest/bypass_paywalls_clean-latest.xpi"
|
|
||||||
],
|
|
||||||
"Uninstall": [
|
"Uninstall": [
|
||||||
"google@search.mozilla.org",
|
"google@search.mozilla.org",
|
||||||
"bing@search.mozilla.org",
|
"bing@search.mozilla.org",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue