Files

518 lines
15 KiB
Go
Raw Permalink Normal View History

package main
2022-01-16 14:45:43 -05:00
import (
2022-01-23 12:00:37 -05:00
"embed"
2022-02-07 21:38:54 -05:00
"fmt"
2022-07-08 13:19:41 -04:00
"io/ioutil"
2022-01-16 14:45:43 -05:00
"log"
"net"
"os"
2022-03-04 01:22:39 -05:00
"os/signal"
"path/filepath"
"runtime"
"strings"
2022-01-16 14:45:43 -05:00
flag "github.com/spf13/pflag"
2022-01-16 15:33:47 -05:00
"github.com/cloudfoundry/jibber_jabber"
2022-07-12 02:34:26 -04:00
"github.com/eyedeekay/go-I2P-jpackage"
2022-02-08 18:31:32 -05:00
"github.com/itchio/damage"
"github.com/itchio/damage/hdiutil"
"github.com/itchio/headway/state"
"github.com/ncruces/zenity"
tbget "i2pgit.org/idk/i2p.plugins.tor-manager/get"
tbserve "i2pgit.org/idk/i2p.plugins.tor-manager/serve"
tbsupervise "i2pgit.org/idk/i2p.plugins.tor-manager/supervise"
tinymce "github.com/eyedeekay/go-htmleditor"
2022-01-16 14:45:43 -05:00
)
2022-02-07 21:09:43 -05:00
/*
TODO: A "Default" config file which uses hardened Tor Browser for clearnet
(or default-route) browsing.
*/
2022-01-23 12:00:37 -05:00
//go:embed tor-browser/unpack/i2p.firefox/*
2022-02-06 16:57:30 -05:00
//go:embed tor-browser/unpack/i2p.firefox.config/*
//go:embed tor-browser/unpack/awo@eyedeekay.github.io.xpi
//go:embed tor-browser/TPO-signing-key.pub
//go:embed tor-browser/NOT-TPO-signing-key.pub
2022-01-28 00:09:18 -05:00
//go:embed garliconion.png
//go:embed onion.png
//go:embed www.png
//go:embed offline.png
//go:embed torbrowser.desktop
//go:embed i2ptorbrowser.desktop
2022-07-08 13:19:41 -04:00
//go:embed LICENSE
2022-01-23 12:00:37 -05:00
var content embed.FS
2022-07-08 13:19:41 -04:00
// print license, then exit 0
func LICENSE() {
//get LICENSE from embedded content
license, err := content.Open("LICENSE")
if err != nil {
log.Fatal(err)
}
license_bytes, err := ioutil.ReadAll(license)
if err != nil {
log.Fatal(err)
}
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, string(license_bytes))
2022-07-12 02:34:26 -04:00
I2P.PrintLicenses()
2022-07-08 13:19:41 -04:00
os.Exit(0)
}
func OS() string {
switch runtime.GOOS {
case "darwin":
2022-02-08 01:23:30 -05:00
return "osx"
case "linux":
return "linux"
case "windows":
return "win"
default:
return "unknown"
}
}
func ARCH() string {
switch runtime.GOARCH {
case "386":
return "32"
case "amd64":
return "64"
case "arm64":
if OS() == "osx" {
return "64"
}
return ""
default:
return "unknown"
}
}
2022-01-16 21:22:04 -05:00
2022-03-22 23:13:07 -04:00
var theLang = os.Getenv("TBLANG")
func defaultLang() string {
if theLang != "" {
return theLang
}
lang, _ := jibber_jabber.DetectIETF()
return lang
}
func defaultTor() bool {
_, err := net.Listen("TCP", "127.0.0.1:9050")
if err != nil {
return true
}
return false
}
2022-01-16 14:45:43 -05:00
var (
lang = flag.String("lang", defaultLang(), "Language to download")
system = flag.String("os", OS(), "OS/arch to download")
arch = flag.String("arch", ARCH(), "OS/arch to download")
2022-01-23 11:42:23 -05:00
i2pbrowser = flag.Bool("i2pbrowser", false, "Open I2P in Tor Browser")
2022-02-06 16:57:30 -05:00
i2pconfig = flag.Bool("i2pconfig", false, "Open I2P routerconsole in Tor Browser with javscript enabled and non-routerconsole sites disabled")
2022-01-23 11:42:23 -05:00
torbrowser = flag.Bool("torbrowser", false, "Open Tor Browser")
2022-03-22 22:07:12 -04:00
i2peditor = flag.Bool("i2peditor", false, "Open I2P Site Editor in Tor Browser")
2022-01-27 22:41:30 -05:00
verbose = flag.Bool("verbose", false, "Verbose output")
directory = flag.String("directory", DefaultDir(), "Directory operate in")
host = flag.String("host", "127.0.0.1", "Host to serve on")
port = flag.Int("port", 7695, "Port to serve on")
bemirror = flag.Bool("bemirror", false, "Act as an in-I2P mirror when you're done downloading")
shortcuts = flag.Bool("shortcuts", false, "Create desktop shortcuts")
apparmor = flag.Bool("apparmor", false, "Generate apparmor rules")
2022-02-10 23:39:31 -05:00
offline = flag.Bool("offline", false, "Work offline. Differs from Firefox's offline mode in that cannot be disabled until the browser is closed.")
2022-03-15 17:06:44 -04:00
clearnet = flag.Bool("clearnet", Clearnet(), "Use clearnet (no Tor or I2P) in Tor Browser")
profile = flag.String("profile", "", "use a custom profile path, normally blank")
2022-03-15 17:06:44 -04:00
help = flag.Bool("help", false, "Print help and quit")
mirror = flag.String("mirror", Mirror(), "Mirror to use. I2P will be used if an I2P proxy is present, if system Tor is available, it will be downloaded over the Tor proxy.")
solidarity = flag.Bool("onion", defaultTor(), "Serve an onion site which shows some I2P propaganda")
2022-02-25 00:30:41 -05:00
torrent = flag.Bool("torrent", tbget.TorrentReady(), "Create a torrent of the downloaded files and seed it over I2P using an Open Tracker")
2022-03-03 23:48:32 -05:00
destruct = flag.Bool("destruct", false, "Destructively delete the working directory when finished")
2022-03-04 12:40:25 -05:00
password = flag.String("password", Password(), "Password to encrypt the working directory with. Implies -destruct, only the encrypted container will be saved.")
chat = flag.Bool("chat", false, "Open a WebChat client")
notor = flag.Bool("notor", false, "Do not automatically start Tor")
nounpack = flag.Bool("nounpack", false, "Do not unpack the Tor Browser")
ptop = flag.Bool("p2p", tbget.TorrentDownloaded(defaultLang(), OS()+ARCH()), "Use bittorrent over I2P to download the initial copy of Tor Browser")
2022-03-23 00:57:20 -04:00
torversion = flag.Bool("torversion", false, "Print the version of Tor Browser that will be downloaded and exit")
mirrorall = flag.Bool("mirrorall", false, "Download and mirror every language and OS/arch combination")
nevertor = flag.Bool("nevertor", false, "Never use Tor for downloading Tor Browser")
2022-07-08 13:19:41 -04:00
license = flag.Bool("license", false, "Print the license and exit")
2022-07-15 17:33:13 -04:00
rsystray = flag.Bool("systray", false, "Create a systray icon")
2022-01-16 14:45:43 -05:00
)
2022-03-15 17:04:24 -04:00
func Clearnet() bool {
if tmc := os.Getenv("TOR_MANAGER_CLEARNET"); tmc != "" {
switch tmc {
case "1", "true", "yes", "on":
return true
}
}
return false
}
func Password() string {
require_password := os.Getenv("TOR_MANAGER_REQUIRE_PASSWORD")
if require_password == "" && !PluginStat() {
require_password = "true"
}
2022-07-08 13:19:41 -04:00
// search os.Args for -license flag and if it's present, set to false
for _, arg := range os.Args {
if arg == "-license" {
require_password = "false"
}
}
2022-03-15 17:04:24 -04:00
switch require_password {
case "true", "1", "yes", "on":
passwd, err := zenity.Entry(
"Enter a password if you want to encrypt the working directory",
zenity.Title("Work Directory Encryption"),
zenity.CancelLabel("Don't encrypt"),
zenity.OKLabel("Encrypt"),
zenity.Width(400),
zenity.EntryText("password"),
)
if err != nil {
if !strings.Contains(err.Error(), "canceled") {
log.Panicln(err)
}
log.Println("Password dialog canceled")
return ""
}
return passwd
}
return ""
}
func Mirror() string {
2022-03-15 17:04:24 -04:00
if mir := os.Getenv("TOR_MANAGER_MIRROR"); mir != "" {
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, "Using environment mirror %s", mir)
2022-03-15 17:04:24 -04:00
return mir
}
if runtime.GOOS == "linux" && runtime.GOARCH == "arm64" {
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, "Using arm64 mirror")
2022-03-15 17:04:24 -04:00
return "https://sourceforge.net/projects/tor-browser-ports/files"
}
clear := os.Getenv("TOR_MANAGER_CLEARNET")
switch clear {
case "1", "true", "yes", "on":
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, "Using clearnet mirror")
2022-03-15 17:04:24 -04:00
return "https://dist.torproject.org/torbrowser/"
}
clearmirror := os.Getenv("TOR_MANAGER_CLEARNET_MIRROR")
switch clearmirror {
case "1", "true", "yes", "on":
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, "Using clearnet mirror")
return "https://dist.torproject.org/torbrowser/"
}
2022-03-22 23:54:10 -04:00
if tbget.Torrent(*lang, OS()+ARCH()) {
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, "Using torrent mirror")
return "http://localhost:7657/i2psnark/"
}
2022-03-15 17:04:24 -04:00
if tbget.TestHTTPDefaultProxy() {
2022-07-15 18:19:53 -04:00
//fmt.Fprintf(os.Stderr,"Using I2P mirror")
//return "http://dist.torproject.i2p/torbrowser/"
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, "Using clearnet mirror instead of I2P mirror due to hash sum mismatch issue")
return "https://dist.torproject.org/torbrowser/"
}
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, "Using clearnet mirror")
return "https://dist.torproject.org/torbrowser/"
}
var snowflake *bool
var client *tbserve.Client
func main() {
2022-07-08 13:19:41 -04:00
for _, arg := range os.Args {
if arg == "-license" {
LICENSE()
}
}
2022-03-25 13:56:28 -04:00
if err := NSISCompat(); err != nil {
log.Println("NSIS compat mode failure", err)
os.Exit(0)
}
filename := filepath.Base(os.Args[0])
SnowflakeFlag()
2022-02-07 21:09:43 -05:00
usage := flag.Usage
flag.Usage = func() {
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stdout, "Usage: %s %s\n", filename, "[options]")
fmt.Fprintf(os.Stdout, "\n")
2022-07-15 19:07:26 -04:00
printversion()
fmt.Fprintf(os.Stdout, "\n")
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stdout, "Downloads, verifies and unpacks Tor Browser. Manages the Tor Browser\n")
fmt.Fprintf(os.Stdout, "system in environments where Tor is not in use. Monitors a long-running\n")
fmt.Fprintf(os.Stdout, "Tor process and downloads updates when Tor is not available.\n")
fmt.Fprintf(os.Stdout, "\n")
fmt.Fprintf(os.Stdout, "Options:\n")
fmt.Fprintf(os.Stdout, "\n")
// redirect stderr to stdout
flag.CommandLine.SetOutput(os.Stdout)
2022-02-07 21:09:43 -05:00
usage()
2022-07-15 18:19:53 -04:00
// redirect stderr back to stderr
flag.CommandLine.SetOutput(os.Stderr)
fmt.Fprintf(os.Stdout, "\nAvailable Languages:\n\n")
for _, l := range tbget.Languages() {
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stdout, " - %s\n", l)
}
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stdout, "\n")
}
args, trailers := CleanupArgs()
log.Printf("Args: %v\n", args)
log.Printf("Trailers: %v\n", trailers)
2022-07-15 01:09:09 -04:00
if len(args) > 0 {
os.Args = args
}
2022-01-16 14:45:43 -05:00
flag.Parse()
if *nevertor {
err := os.Setenv("TOR_MANAGER_NEVER_USE_TOR", "true")
if err != nil {
log.Panicln(err)
}
}
if *mirrorall {
2022-06-28 00:51:49 -04:00
err := os.Setenv("TOR_MANAGER_NEVER_USE_TOR", "true")
if err != nil {
log.Panicln(err)
}
err = mirrorAll()
if err != nil {
log.Fatal(err)
}
2022-06-28 11:24:05 -04:00
os.Exit(0)
}
2022-03-23 00:57:20 -04:00
if *torversion {
torbrowserversion, err := tbget.GetTorBrowserVersionFromUpdateURL()
if err != nil {
log.Panicln(err)
}
2022-07-15 18:19:53 -04:00
fmt.Fprintf(os.Stderr, torbrowserversion)
2022-03-23 00:57:20 -04:00
os.Exit(0)
}
if *ptop {
log.Println("Using p2p")
*mirror = "http://localhost:7657/i2psnark/"
}
if *password != "" {
log.Println("Looking for directory with password")
DecryptTarXZifThere(*directory, *password)
2022-03-04 01:22:39 -05:00
// capture sigint
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
log.Println("Caught interrupt, exiting")
2022-03-04 12:40:25 -05:00
err := EncryptTarXZip(*directory, *password)
if err != nil {
log.Println(err)
}
os.Exit(0)
2022-03-04 01:22:39 -05:00
}
}()
}
2022-03-05 00:08:13 -05:00
if *clearnet {
*mirror = "http://dist.torproject.org/torbrowser/"
}
if *snowflake {
go Snowflake()
}
if *destruct {
defer OverwriteDirectoryContents(*directory)
}
tbget.WORKING_DIR = *directory
if filename == "i2pbrowser" {
log.Println("Starting I2P in Tor Browser")
*i2pbrowser = true
} else if filename == "torbrowser" {
log.Println("Starting Tor Browser")
*torbrowser = true
2022-02-07 21:09:43 -05:00
} else if filename == "i2pconfig" {
log.Println("Starting I2P routerconsole in Tor Browser")
*i2pconfig = true
} else if filename == "firefox" || *clearnet || *offline {
*clearnet = true
2022-02-14 23:42:08 -05:00
}
if *profile == "" {
if *offline {
*profile = filepath.Join(tbget.WORKING_DIR, "profile.firefox.offline")
} else if *clearnet {
*profile = filepath.Join(tbget.WORKING_DIR, "profile.firefox")
} else {
2022-02-14 23:42:08 -05:00
*profile = filepath.Join(tbget.WORKING_DIR, "profile.firefox.default")
2022-02-07 21:09:43 -05:00
}
2022-02-14 23:42:08 -05:00
} else {
*profile = filepath.Join(tbget.WORKING_DIR, *profile)
}
if *i2pbrowser && *torbrowser {
2022-01-23 11:42:23 -05:00
log.Fatal("Please don't open I2P and Tor Browser at the same time when running from the terminal.")
}
2022-01-16 15:33:47 -05:00
if *lang == "" {
var err error
*lang, err = jibber_jabber.DetectIETF()
if err != nil {
log.Fatal("Please specify a language", err)
}
log.Println("Using auto-detected language", *lang)
}
2022-03-19 19:17:05 -04:00
if I2PDaemon, err := StartI2P(*directory); err != nil {
2022-03-19 18:55:51 -04:00
log.Fatal(err)
2022-03-19 19:17:05 -04:00
} else {
if I2PDaemon != nil {
defer I2PDaemon.Stop()
}
}
2022-02-20 00:05:57 -05:00
var err error
client, err = tbserve.NewClient(*verbose, *lang, *system, *arch, *mirror, &content, *nounpack)
2022-02-20 00:05:57 -05:00
if err != nil {
log.Fatal("Couldn't create client", err)
}
if *apparmor {
err := GenerateAppArmor()
if err != nil {
log.Fatal("Couldn't generate apparmor rules", err)
}
log.Println("################################################################")
log.Println("# AppArmor rules generated successfully #")
log.Println("################################################################")
log.Println("!IMPORTANT! You must now run the following commands:")
log.Println("sudo mkdir -p /etc/apparmor.d/tunables/")
log.Println("sudo cp tunables.torbrowser.apparmor /etc/apparmor.d/tunables/torbrowser")
log.Println("sudo cp torbrowser.Tor.tor.apparmor /etc/apparmor.d/torbrowser.Tor.tor")
log.Println("sudo cp torbrowser.Browser.firefox.apparmor /etc/apparmor.d/torbrowser.Browser.firefox")
log.Println("sudo apparmor_parser -r /etc/apparmor.d/tunables/torbrowser")
log.Println("sudo apparmor_parser -r /etc/apparmor.d/torbrowser.Tor.tor")
log.Println("sudo apparmor_parser -r /etc/apparmor.d/torbrowser.Browser.firefox")
log.Println("To copy them to apparmor profiles directory and reload AppArmor")
return
}
if *shortcuts {
err := CreateShortcuts()
if err != nil {
log.Fatal("Couldn't create desktop shortcuts", err)
}
}
client.Host = *host
client.Port = *port
2022-01-23 12:00:37 -05:00
client.TBS.Profile = &content
client.TBS.PassThroughArgs = trailers
2022-03-01 00:17:59 -05:00
if runtime.GOOS == "darwin" {
2022-02-21 18:08:49 -05:00
consumer := &state.Consumer{
OnMessage: func(lvl string, msg string) {
log.Printf("[%s] %s", lvl, msg)
},
}
host := hdiutil.NewHost(consumer)
defer damage.Unmount(host, client.TBD.BrowserDir())
2022-02-08 18:31:32 -05:00
}
2022-02-06 16:57:30 -05:00
// log.Fatalf("%s", client.TBS.PassThroughArgs)
if *help {
2022-07-15 18:19:53 -04:00
log.Println("Usage:")
flag.Usage()
2022-07-15 18:19:53 -04:00
if err := client.TBS.RunTBHelpWithLang(); err != nil {
log.Fatal(err)
}
return
}
if *torrent {
log.Println("Generating I2P torrents of Tor packages")
if err := client.TBD.GenerateMissingTorrents(); err != nil {
log.Fatal(err)
}
2022-03-16 15:01:49 -04:00
log.Println("I2P torrents generated")
}
client.TBS.UnpackI2PAppData()
client.TBS.UnpackI2PData()
if *nounpack {
log.Println("not unpacking, cannot continue")
os.Exit(0)
}
if !(*clearnet || *notor) {
log.Println("CLEARNET", *clearnet)
log.Println("NOTOR", *notor)
2022-03-15 17:04:24 -04:00
client.TBS.RunTorWithLang()
}
if *chat {
log.Println("Starting I2P chat")
go BRBClient(*directory, "brb")
}
2022-03-23 10:45:41 -04:00
go ServeEditor()
2022-02-14 23:42:08 -05:00
if *i2pbrowser {
if err := client.TBS.RunI2PBWithLang(); err != nil {
2022-02-08 18:31:32 -05:00
log.Fatal(err)
}
2022-02-06 16:57:30 -05:00
} else if *i2pconfig {
if err := client.TBS.RunI2PBAppWithLang(); err != nil {
2022-02-08 18:31:32 -05:00
log.Fatal(err)
}
2022-03-22 22:07:12 -04:00
} else if *i2peditor {
if err := client.TBS.RunI2PSiteEditorWithOfflineClearnetProfile(filepath.Join(client.TBD.UnpackPath, "i2p.firefox.editor")); err != nil {
log.Fatal(err)
}
2022-01-23 11:42:23 -05:00
} else if *torbrowser {
2022-02-08 18:31:32 -05:00
if err := client.TBS.RunTBWithLang(); err != nil {
log.Fatal(err)
}
2022-02-14 23:42:08 -05:00
} else if *offline {
log.Println("Working offline")
if err := client.TBS.RunTBBWithOfflineClearnetProfile(*profile, *offline, *clearnet); err != nil {
log.Fatal(err)
}
} else if *clearnet {
log.Println("Using a custom profile")
if err := client.TBS.RunTBBWithOfflineClearnetProfile(*profile, *offline, *clearnet); err != nil {
log.Fatal(err)
}
2022-01-23 11:42:23 -05:00
} else {
if *bemirror {
go client.TBD.Serve()
}
2022-02-28 19:59:47 -05:00
if *solidarity {
2022-02-28 21:15:35 -05:00
client.Onion.UnpackSite()
go ServeOnion()
2022-02-28 19:59:47 -05:00
}
2022-07-15 17:33:13 -04:00
if *rsystray {
go runSysTray(false)
}
2022-01-23 11:42:23 -05:00
if err := client.Serve(); err != nil {
log.Fatal(err)
}
2022-01-16 21:22:04 -05:00
}
}
func ServeOnion() error {
if err := client.Onion.ListenAndServe(); err != nil {
log.Println("Onion error:", err)
}
return nil
}
func ServeEditor() error {
docroot, err := tbsupervise.FindEepsiteDocroot()
if err != nil {
return err
}
if err := tinymce.Serve("127.0.0.1", docroot, "index.html", 7685); err != nil {
log.Println("Couldn't serve editor", err)
}
return nil
}
func pathToMe() (string, error) {
ex, err := os.Executable()
if err != nil {
return "", err
}
exPath, err := filepath.Abs(ex)
if err != nil {
return "", err
}
return exPath, nil
}