2022-01-16 14:31:31 -05:00
package main
2022-01-16 14:45:43 -05:00
import (
2022-01-23 12:00:37 -05:00
"embed"
2022-01-16 14:45:43 -05:00
"flag"
2022-02-07 21:38:54 -05:00
"fmt"
2022-01-16 14:45:43 -05:00
"log"
2022-01-24 17:09:50 -05:00
"os"
2022-03-04 01:22:39 -05:00
"os/signal"
2022-01-24 17:09:50 -05:00
"path/filepath"
"runtime"
2022-01-16 14:45:43 -05:00
2022-01-16 15:33:47 -05:00
"github.com/cloudfoundry/jibber_jabber"
2022-01-31 16:14:30 -05:00
i2cpcheck "github.com/eyedeekay/checki2cp"
2022-02-08 18:31:32 -05:00
"github.com/itchio/damage"
"github.com/itchio/damage/hdiutil"
"github.com/itchio/headway/state"
2022-01-29 01:18:37 -05:00
tbget "i2pgit.org/idk/i2p.plugins.tor-manager/get"
2022-01-21 22:47:18 -05:00
tbserve "i2pgit.org/idk/i2p.plugins.tor-manager/serve"
2022-02-19 00:46:47 -05:00
"github.com/eyedeekay/go-I2P-jpackage"
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/*
2022-02-07 16:57:13 -05:00
//go:embed tor-browser/unpack/awo@eyedeekay.github.io.xpi
2022-01-23 13:40:14 -05:00
//go:embed tor-browser/TPO-signing-key.pub
2022-01-28 00:09:18 -05:00
//go:embed garliconion.png
//go:embed onion.png
2022-01-30 16:45:50 -05:00
//go:embed torbrowser.desktop
//go:embed i2ptorbrowser.desktop
2022-01-23 12:00:37 -05:00
var content embed . FS
2022-01-24 17:09:50 -05:00
func OS ( ) string {
switch runtime . GOOS {
case "darwin" :
2022-02-08 01:23:30 -05:00
return "osx"
2022-01-24 17:09:50 -05:00
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"
2022-02-10 20:20:02 -05:00
case "arm64" :
if OS ( ) == "osx" {
return "64"
}
return ""
2022-01-24 17:09:50 -05:00
default :
return "unknown"
}
}
2022-01-16 21:22:04 -05:00
2022-01-16 14:45:43 -05:00
var (
2022-01-23 11:42:23 -05:00
lang = flag . String ( "lang" , "" , "Language to download" )
2022-01-24 17:09:50 -05:00
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-01-27 22:41:30 -05:00
verbose = flag . Bool ( "verbose" , false , "Verbose output" )
2022-03-03 23:23:15 -05:00
directory = flag . String ( "directory" , DefaultDir ( ) , "Directory operate in" )
2022-01-29 01:18:37 -05:00
host = flag . String ( "host" , "127.0.0.1" , "Host to serve on" )
port = flag . Int ( "port" , 7695 , "Port to serve on" )
2022-01-30 16:45:50 -05:00
bemirror = flag . Bool ( "bemirror" , false , "Act as an in-I2P mirror when you're done downloading" )
shortcuts = flag . Bool ( "shortcuts" , false , "Create desktop shortcuts" )
2022-01-31 02:09:02 -05:00
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-02-11 22:48:28 -05:00
clearnet = flag . Bool ( "clearnet" , false , "Use clearnet (no Tor or I2P)" )
2022-02-07 16:18:55 -05:00
profile = flag . String ( "profile" , "" , "use a custom profile path, normally blank" )
help = flag . Bool ( "help" , false , "Print help" )
2022-02-22 22:12:25 -05:00
/ * TODO : Hash sometimes doesn ' t match when downloading from this mirror . Probably due to latency
between updates . Know for sure why before enabling it . * /
2022-02-28 23:54:41 -05:00
//mirror = flag.String("mirror", "http://dist.torproject.i2p/torbrowser/", "Mirror to use(I2P)")
2022-02-22 22:12:25 -05:00
/ * TODO : This is a clearnet mirror which was originally used by default . Since this is intended as
an I2P browser configurer , and we guarantee the availability of the HTTP proxy by starting our own
if one is unavailable , we download over I2P instead . This is pretty fast these days really , but for
77 or so MB it ' s noticably delayed still . In "clearnet" modes , it might make sense to default to
this mirror instead of the I2P one , or maybe offer a convenience option for just the download . * /
2022-02-28 23:54:41 -05:00
mirror = flag . String ( "mirror" , "https://dist.torproject.org/torbrowser/" , "Mirror to use" )
2022-02-28 23:58:21 -05:00
solidarity = flag . Bool ( "onion" , false , "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." )
2022-03-07 18:56:24 -05:00
chat = flag . Bool ( "chat" , false , "Open a WebChat client" )
2022-02-10 23:39:31 -05:00
/*ptop = flag.Bool("p2p", false, "Use bittorrent over I2P to download the initial copy of Tor Browser")*/
2022-01-16 14:45:43 -05:00
)
2022-01-16 14:34:46 -05:00
2022-03-03 23:23:15 -05:00
var snowflake * bool
2022-01-31 02:09:02 -05:00
var client * tbserve . Client
2022-01-16 14:31:31 -05:00
func main ( ) {
2022-01-24 17:09:50 -05:00
filename := filepath . Base ( os . Args [ 0 ] )
2022-03-03 23:23:15 -05:00
SnowflakeFlag ( )
2022-02-07 21:09:43 -05:00
usage := flag . Usage
2022-02-07 16:18:55 -05:00
flag . Usage = func ( ) {
2022-02-07 21:38:54 -05:00
fmt . Printf ( "Usage: %s %s\n" , filename , "[options]" )
fmt . Printf ( "\n" )
fmt . Printf ( "Downloads, verifies and unpacks Tor Browser. Manages the Tor Browser\n" )
fmt . Printf ( "system in environments where Tor is not in use.\n" )
fmt . Printf ( "\n" )
fmt . Printf ( "Options:\n" )
fmt . Printf ( "\n" )
2022-02-07 21:09:43 -05:00
usage ( )
2022-02-07 16:18:55 -05:00
}
2022-01-16 14:45:43 -05:00
flag . Parse ( )
2022-03-04 00:16:02 -05:00
if * password != "" {
2022-03-04 01:02:03 -05:00
log . Println ( "Looking for directory with password" )
2022-03-04 00:16:02 -05:00
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-04 00:16:02 -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 )
}
2022-01-29 01:18:37 -05:00
tbget . WORKING_DIR = * directory
2022-01-24 17:09:50 -05:00
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
2022-02-11 22:48:28 -05:00
} 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" )
2022-02-11 22:48:28 -05:00
} 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 )
2022-01-24 17:09:50 -05:00
}
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-02-20 00:05:57 -05:00
if tbget . TestHTTPDefaultProxy ( ) {
log . Println ( "I2P HTTP proxy OK" )
} else {
log . Println ( "I2P HTTP proxy not OK" )
run , err := i2cpcheck . ConditionallyLaunchI2P ( )
if err != nil {
log . Println ( "Couldn't launch I2P" , err )
}
if run {
if tbget . TestHTTPDefaultProxy ( ) {
log . Println ( "I2P HTTP proxy OK after launching I2P" )
} else {
go proxy ( )
if ! tbget . TestHTTPBackupProxy ( ) {
log . Fatal ( "Please set the I2P HTTP proxy on localhost:4444" , err )
}
}
2022-02-07 15:53:16 -05:00
} else {
2022-02-20 00:05:57 -05:00
I2Pdaemon , err := I2P . NewDaemon ( * directory , false )
2022-02-07 15:53:16 -05:00
if err != nil {
2022-02-20 00:05:57 -05:00
log . Println ( err )
return
2022-02-07 15:53:16 -05:00
}
2022-02-20 00:05:57 -05:00
if err = I2Pdaemon . Start ( ) ; err != nil {
log . Fatal ( err )
}
shutdown = true
defer I2Pdaemon . Stop ( )
go runSysTray ( true )
if tbget . TestHTTPDefaultProxy ( ) {
log . Println ( "I2P HTTP proxy OK" )
2022-02-07 15:53:16 -05:00
} else {
2022-02-20 00:05:57 -05:00
log . Fatal ( "Embedded i2pd unable to start" )
2022-01-31 16:14:30 -05:00
}
}
}
2022-02-20 00:05:57 -05:00
var err error
client , err = tbserve . NewClient ( * verbose , * lang , * system , * arch , * mirror , & content )
if err != nil {
log . Fatal ( "Couldn't create client" , err )
}
2022-01-31 02:09:02 -05:00
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
}
2022-01-30 16:45:50 -05:00
if * shortcuts {
err := CreateShortcuts ( )
if err != nil {
log . Fatal ( "Couldn't create desktop shortcuts" , err )
}
}
2022-01-29 01:18:37 -05:00
client . Host = * host
client . Port = * port
2022-01-23 12:00:37 -05:00
client . TBS . Profile = & content
2022-02-06 16:57:30 -05:00
client . TBS . PassThroughArgs = flag . Args ( )
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)
2022-02-07 16:18:55 -05:00
if * help {
flag . Usage ( )
2022-02-08 20:30:33 -05:00
if err := client . TBS . RunTBHelpWithLang ( ) ; err != nil {
log . Fatal ( err )
}
2022-02-07 16:18:55 -05:00
return
}
2022-02-11 22:48:28 -05:00
client . TBS . UnpackI2PAppData ( )
client . TBS . UnpackI2PData ( )
2022-02-22 20:34:48 -05:00
if * torrent {
log . Println ( "Generating I2P torrents of Tor packages" )
if err := client . TBD . GenerateMissingTorrents ( ) ; err != nil {
log . Fatal ( err )
}
}
2022-02-28 19:59:47 -05:00
client . TBS . RunTorWithLang ( )
2022-03-07 18:56:24 -05:00
if * chat {
log . Println ( "Starting I2P chat" )
go BRBClient ( * directory , "brb" )
}
2022-02-14 23:42:08 -05:00
if * i2pbrowser {
2022-02-08 20:30:33 -05:00
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 {
2022-02-08 20:30:33 -05:00
if err := client . TBS . RunI2PBAppWithLang ( ) ; err != nil {
2022-02-08 18:31:32 -05:00
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 {
2022-01-29 01:18:37 -05:00
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 func ( ) {
if err := client . Onion . ListenAndServe ( ) ; err != nil {
log . Println ( "Onion error:" , err )
}
} ( )
2022-02-28 19:59:47 -05:00
}
2022-02-14 23:42:08 -05:00
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
}
2022-01-16 14:31:31 -05:00
}
2022-01-30 16:45:50 -05:00
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
}